grammar = {(fn_decl | import_stmt) (stmt_end | EOF)} ; import_stmt = "import" STRING ; fn_decl = "let" IDENT '(' [{'var'} arg {',' {'var'} arg}] ')' ':' IDENT '=' (expression | block_expr) ; arg = IDENT ':' IDENT ; stmt = let_decl | var_decl | fn_decl | reassign | 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 ; 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 | 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 | given_expr | cast | '(' expression ')' ; if_expr = "if" expression block_expr {'else' 'if' expression block_expr} 'else' block_expr ; given_expr = "given" block '->' expression ; 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 *)