Skip to content

Grammar (subset)

EBNF of the slice the lexer/parser must accept. This is not ISO C++. The running grammar is Pest (src/parser/grammar.pest).

flowchart LR
  src["Source"] --> pest["Pest EBNF"]
  pest --> ast["AST"]
  ast --> luau["Luau"]
translation-unit = { include | pragma | using-skip | declaration } ;
include = "#include" ( "<" path ">" | '"' path '"' ) ;
pragma = "#pragma" ( "once" | "strict" | "nostrict" | "nstrict" | "nonstrict" | "native" | "optimize" [ 0 | 1 | 2 ] ) ;
declaration =
struct-decl
| function-def
| method-def
| var-decl
| const-decl
;
struct-decl = ( "struct" | "class" ) ident "{" { struct-member } "}" ";" ;
struct-member =
access-label
| "static" "constexpr" type ident "=" expr ";"
| ident ":" type [ "=" expr ] ";"
| type ident [ "=" expr ] ";"
| type ident "(" [ param-list ] ")" ";"
;
function-def = type ident "(" [ param-list ] ")" block ;
method-def = type ident "::" ident "(" [ param-list ] ")" block ;
param-list = param { "," param } ;
param = type ident | ident ":" type ;
type =
"void" | "int" | "float" | "double" | "bool" | "string" | "func" | "auto"
| ident
| ( "LuaArray" | "vector" | "array" | "span" | "optional" ) "<" type ">"
| "dictionary" "<" type "," type ">"
| "const" type
;
block = "{" { statement } "}" ;
statement =
var-decl
| expr ";"
| "return" [ expr ] ";"
| "break" ";"
| if-stmt
| while-stmt
| c-for
| range-for
| spawn-stmt
| parallel-stmt
| guard-stmt
| match-stmt
| switch-stmt
| destructure
| block
;
guard-stmt = "guard" "(" expr ")" "else" statement ;
spawn-stmt = "spawn" block [ ";" ] ;
parallel-stmt = "parallel" block [ ";" ] ;
match-stmt = "match" "(" expr ")" "{" { match-arm } "}" [ ";" ] ;
match-arm = ( "_" | type ident ) "=>" ( block | expr ) [ "," ] ;
destructure = "auto" "[" ident { "," ident } "]" "=" expr ";" ;
if-stmt = "if" "(" expr ")" statement [ "else" statement ] ;
while-stmt = "while" "(" expr ")" statement ;
c-for = "for" "(" [ var-init ] ";" [ expr ] ";" [ expr ] ")" statement ;
range-for = "for" "(" type ident ":" expr ")" statement ;
switch-stmt = "switch" "(" expr ")" "{" { case-clause } "}" ;
case-clause = ( "case" expr | "default" ) ":" { statement } ;
expr =
ident | number | string | "true" | "false" | "null"
| expr ( "+" | "-" | "*" | "/" | "==" | "!=" | "<" | ">" | "<=" | ">=" | "&&" | "||" | ".:" ) expr
| "!" expr | "-" expr
| expr ( "=" | "+=" | "-=" | "*=" | "/=" ) expr
| expr "++" | expr "--"
| expr "::" ident [ "(" arg-list ")" ]
| expr ":" ident [ "(" arg-list ")" ]
| expr "." ident [ "(" arg-list ")" ]
| ident "(" arg-list ")"
| "@this" | "@" ident
| "new" ident "(" [ arg-list ] ")"
| "GetService" "<" ident ">" "(" ")"
| "static_cast" "<" type ">" "(" expr ")"
| ident "{" { "." ident "=" expr "," } "}"
| "{" expr { "," expr } "}"
| "{" "{" expr "," expr "}" { "," "{" expr "," expr "}" } "}"
| lambda
| "(" expr ")"
;
lambda = "func" "(" [ param-list ] ")" block ;

Concatenation uses .: (not ..). . is property / instance method; : is type / protected call; :: is static / manual Connect. @this / @ident is the receiver inside Class::Method (not an attribute; [[server]] is unchanged).

Extra mapped tokens, not pure grammar: string_concat, to_string, to_number, to_bool, cout, cerr, endl, post, warn, report.