title: Grammar description: Formal grammar reference for the Chasm language. keywords: ["chasm", "grammar", "syntax", "BNF", "EBNF", "reference"]

This page describes the complete syntax of Chasm using an informal BNF-style notation.

  • item*, zero or more
  • item+, one or more
  • item?, optional
  • ( A | B ), either A or B
  • Terminals are shown in "double quotes" or italic
  • Non-terminals are shown in bold

Top Level

program       = top_decl*

top_decl      = import_decl
              | defstruct_decl
              | enum_decl
              | extern_decl
              | attr_decl
              | fn_decl

Declarations

Import

import_decl   = "import" IDENT

Struct

defstruct_decl = "defstruct" IDENT "do"
                   field_decl*
                 "end"

field_decl     = IDENT "::" type ("=" expr)?

Enum

enum_decl      = "enum" IDENT "{"
                   enum_variant ("," enum_variant)* ","?
                 "}"

enum_variant   = IDENT ("(" type ")")?

Extern Function

extern_decl    = "extern" "fn" IDENT "(" param_list? ")" "->" type
               | "extern" "fn" IDENT "(" param_list? ")" "->" type "as" STRING

Module Attribute

attr_decl      = "@" IDENT "::" lifetime ("=" expr)?

lifetime       = "frame" | "script" | "persistent"

Function

fn_decl        = ("def" | "defp") IDENT "(" param_list? ")" ("::" type)? "do"
                   stmt*
                 "end"

param_list     = param ("," param)*
param          = IDENT "::" type

Statements

stmt           = var_decl
               | assign
               | attr_assign
               | if_stmt
               | while_stmt
               | for_stmt
               | return_stmt
               | expr_stmt

var_decl       = IDENT ("::" type)? "=" expr
               | IDENT "::" lifetime "=" expr

assign         = IDENT "=" expr

attr_assign    = "@" IDENT "=" expr

if_stmt        = "if" expr "do" stmt* "end"
               | "if" expr "do" stmt* ("else" "do" stmt*)? "end"

while_stmt     = "while" expr "do"
                   stmt*
                 "end"

for_stmt       = "for" IDENT "in" expr "do"
                   stmt*
                 "end"

return_stmt    = "return" expr?

expr_stmt      = expr

Expressions

Expressions are listed from lowest to highest precedence.

expr           = pipe_expr

pipe_expr      = compare_expr ("|>" call_expr)*

compare_expr   = logic_expr (("==" | "!=" | "<" | "<=" | ">" | ">=") logic_expr)?

logic_expr     = add_expr (("and" | "or") add_expr)*

add_expr       = mul_expr (("+" | "-") mul_expr)*

mul_expr       = unary_expr (("*" | "/" | "%") unary_expr)*

unary_expr     = ("!" | "-") unary_expr
               | postfix_expr

postfix_expr   = primary_expr (field_access | index_access | call_suffix)*

field_access   = "." IDENT
index_access   = "[" expr "]"
call_suffix    = "." IDENT "(" arg_list? ")"

primary_expr   = INT
               | FLOAT
               | STRING
               | ATOM
               | "true" | "false"
               | IDENT
               | "@" IDENT
               | struct_lit
               | with_expr
               | case_expr
               | "(" expr ")"
               | call_expr

call_expr      = IDENT "(" arg_list? ")"
               | IDENT "." IDENT "(" arg_list? ")"   # module call

arg_list       = expr ("," expr)*

struct_lit     = IDENT "{" field_init* "}"
               | IDENT "{}"

field_init     = IDENT ":" expr ("," field_init)?

with_expr      = expr "with" "{" field_init* "}"

case_expr      = "case" expr "do"
                   case_arm*
                   ("_" "->" expr)?
                 "end"

case_arm       = "when" expr "->" expr

Types

type           = "int"
               | "float"
               | "bool"
               | "string"
               | "atom"
               | "void"
               | IDENT            # struct or enum name
               | "[" "]" IDENT    # typed array: []Player
               | "[" "]" type     # typed array: []int

Literals

Integer

INT            = [0-9]+
               | "0x" [0-9a-fA-F]+

Integers are 64-bit signed values. Hex literals are commonly used for RGBA colors: 0xff0000ff.

Float

FLOAT          = [0-9]+ "." [0-9]*
               | [0-9]* "." [0-9]+

Floats are 64-bit IEEE 754 doubles.

String

STRING         = '"' (char | interpolation)* '"'
interpolation  = "#{" expr "}"

Strings are UTF-8. Interpolation embeds any expression: "x = #{@x}".

Atom

ATOM           = ":" IDENT

Atoms are compile-time constant identifiers compared by identity: :menu, :playing, :dead.

Comments

comment        = "#" .* newline

Comments run from # to the end of the line. There are no block comments.

Identifiers

IDENT          = [a-zA-Z_] [a-zA-Z0-9_]*

Identifiers are case-sensitive. By convention:

  • Local variables and function names use snake_case
  • Struct and enum names use PascalCase
  • Module attributes use @snake_case

Keywords

The following words are reserved and cannot be used as identifiers:

and        as         atom       bool       break
case       continue   def        defp       defstruct
do         else       end        enum       extern
false      float      fn         for        if
import     in         int        or         persistent
return     script     string     frame      true
void       when       while      with