grammar = {(fn_decl | import_stmt) (stmt_end | EOF)} ;
import_stmt = "import" STRING ;
fn_decl = "let" IDENT '(' [arg {',' arg}] ')' ':' IDENT '=' (expression | block_expr) ;
arg = IDENT ':' IDENT ;
stmt = let_decl
| var_decl
| fn_decl
| reassign
| reassign_by
| fn_call
| control_keyword
| block
| for_loop
| if_statement ;
stmt_end = NEWLINE | ';' ;
let_decl = "let" IDENT [':' IDENT] '=' expression ;
var_decl = "var" IDENT [':' IDENT] '=' expression ;
reassign = IDENT '=' expression ;
reassign_by = reassign_plus
| reassign_minus
| reassign_mul
| reassign_div
| reassign_mod ;
reassign_plus = IDENT '+=' expression ;
reassign_minus = IDENT '-=' expression ;
reassign_mul = IDENT '*=' expression ;
reassign_div = IDENT '/=' expression ;
reassign_mod = IDENT '%=' expression ;
if_statement = "if" expression block {'else' 'if' expression block} ['else' block ] ;
for_loop = "for" [loop_init {',' loop_init}] ';' block ;
loop_init = let_decl
| var_decl
| reassign
| reassign_by
| expression ;
control_keyword = "break"
| "continue"
| "return" [expression]
;
block = '{' {stmt stmt_end} '}' ;
block_expr = '{' expression '}' ;
fn_call = IDENT '(' [expression {',' expression}] ')' ;
expression = logical_or ;
logical_or = logical_and {"or" logical_and} ;
logical_and = logical_not {"and" logical_not} ;
logical_not = ["not"] (logical_not | comparison) ;
comparison = additive {("==" | "!=" | "<" | ">" | "<=" | ">=") additive} ;
additive = multiplicative {('+' | '-') multiplicative} ;
multiplicative = unary {('*' | '/' | '%') unary} ;
unary = '-' unary | term ;
term = NUMBER
| BOOLEAN
| fn_call
| IDENT
| if_expr
| cast
| '(' expression ')' ;
if_expr = "if" expression block_expr {'else' 'if' expression block_expr} 'else' block_expr ;
cast = "cast" '(' IDENT ',' expression ')' ;
(* LEXER RULES *)
(* ? ... ? means regex is inside *)
(* ? skip ? means if you encounter this, skip it *)
IDENT = (? [a-zA-Z_] ?){? [a-zA-Z_0-9] ?} ;
STRING = '"' {? . ?} '"' ;
NUMBER = ? [0-9] ? ['.' {? [0-9] ?}] ;
BOOLEAN = "true" | 'false' ;
NEWLINE = ['\r'] '\n' ;
WS = {'\t'} ? skip ? ;
COMMENT = '--' {? . ?} NEWLINE ? skip ? ;
(* vim: ft=ebnf
*)