# 2025-02-03 # 2025-02-13 # https://smlweb.cpsc.ucalgary.ca/start.html # https://smlweb.cpsc.ucalgary.ca/example-grammars/ # pan grammar # dotlr ######################################## # module ############################### ######################################## _ =| whitespace* ; whitespace = | " " | "\t" | "\n" ; # TODO `#!/usr/bin/...` program =| _ declaration* ; declaration = | ( "pub" _ )? "var" _ ( "mut" _ )? ( "#" _ )? identifier compile_type? "=" _ expr ";" # TODO compile_var mutability.. # TODO "ref", "err", "out" ; compile_expr = | literal | "#" atom ; compile_type = | ":" type | ":" "#" atom ; assignment =| left_hand_side "=" expr ";" ; # TODO should the semicolon be in the statement rule ? statement = | declaration | assignment | expr ";" | defer_identifier ( "|" identifier "|" )? block ";" | loop_control_identifier label? expr? ";" ; # keyword "defer" and "errdefer" defer_identifier =| identifier ; # keyword "break" and "continue" loop_control_identifier =| identifier ; ######################################## # types ################################ ######################################## type = | simple_type | "*" expr | "[" expr "]" expr | "[" "]" expr | "?" expr | errorset "!" expr # TODO record # TODO class # TODO enum # TODO union # TODO variant ; simple_type = | "void" | "bool" | "u" digit+ # "u1", "u8", "u16", "u32", "u64" | "s" digit+ # "s1", "s8", "s16", "s32", "s64" | "usize" | "isize" | "f32" | "f64" ; ######################################## # functions ############################ ######################################## function =| fn_lambda parameter_list? block ; fn_lambda = | "fn" compile_type # fn, stand-alone types | "." # lambda, type signature inferred # TODO '.' prefix is large gramatic allocation ; # TODO generalize argument lists parameter_list =| "|" ( parameter "," )* ( parameter ","? )? "|" ; parameter_list_inner = | parameter "," parameter_list_inner | parameter ","? | ; parameter_one =| "|" parameter "|" ; # TODO consider replace parameter_list parameter =| "ref"? "#"? identifier comp_type? ; ######################################## # control flow ######################### ######################################## block =| "{" statement* expr? "}" ; if =| "if" "(" expr ")" parameter_one? block ( "elif" block )* ( "else" parameter_one? block )? ; for =| "for" "(" for_list ")" parameter_list? block ( "else" block )? ; for_list =| ( for_arg "," )* for_arg ","? ; for_list_inner = | for_arg "," for_list_inner | for_arg ","? ; for_arg = | expr | range ; range =| expr ".." expr ; # TODO optional exprs ? # TODO update_expr, after parameter, before block ## normal `while( it.next() ) |val| { ... }` ## zig `while( i < 10 ) :( i += 1 ) { ... }` ## pan `while( i < 10 ) { defer { i += 1; }; ... }` while =| "while" "(" expr ")" parameter_one? block ( "else" block )? ; # TODO switch =| "switch" "(" expr ")" switch_case* ( "else" parameter_one? block )? ; # TODO should case arguments be required comptime, like types ? switch_case =| "case" "(" arg_list? ")" parameter_one? block ; arg_list =| ( expr "," )* expr ","? ; arg_list = | expr "," arg_list | expr ","? ; ######################################## # expr ################################# ######################################## expr = | binary # atom # thru the binary_exprs ; # equality, comparison, addition, multiplication, unary binary =| comparison ; bool_combo =| comparison ( bool_operator comparison )* ; bool_operator = | "and" | "or" ; comparison =| addition ( comparison_operator addition )* ; comparison_operator = | "==" | "<>" | "<" | "<=" | ">" | "=>" ; addition = | multiplication ( addition_operator multiplication )* ; # sum addition_operator = | "+" | "-" ; multiplication =| atom ; # product multiplication_operator = | "*" | "/" | "%" ; bitwise = # TODO | expr ( ( "`|`" | "`&`" | "<<" | ">>" ) expr )* # TODO maybe `$&` and `$|` # TODO sign extend shift right.. # TODO rotate left, rotate right.. ; atom = | left_hand_side | atom "." _ "!" _ # `atom iferr |e| { return e; }` | atom "." _ "?" _ # TODO trap or error or panic ? fallible function, # `if( atom ) |a| { a } else { return error.nil; }` # fallible function # `if( atom ) |a| { a } else { trap; }` # XXX hidden trap, like assert.. | atom "." _ "&" _ | atom "." _ "~" _ | atom "." _ "+" _ | atom "." _ "-" _ | literal | struct_val # TODO | type | call | slice | block | expr "ifnil" _ parameter_one? block | expr "iferr" _ parameter_one? block | expr "catch" _ parameter_one? switch_block # TODO | if | for | while | switch # TODO ; struct_val = | "." _ "(" _ struct_val_fields? ")" | expr "(" _ struct_val_fields? ")" ; struct_val_fields = | ( "." _ identifier "=" _ expr "," )* "." _ identifier "=" _ expr ","?_ | ( expr "," _ )* expr ","?_ ; # .( .k1 = v1, .k2 = "v2" ) // shorthand # .( val1, val2, ) // tuple # List ( .allocator = c_allocator ) // explicit call =| expr "(" argument_list ")" ; index =| expr "[" expr "]" ; slice =| expr "[" expr? ".." expr? "]" ; argument_list =| ( expr "," )* ( expr ","? )? ; ######################################## # identifier and literals ############## ######################################## identifier =| letter alphanumeric* ; letter = | "a".."z" | "A".."Z" | "_" ; alphanumeric = | letter | "0".."9" ; literal = | "undefined" | "nil" | boolean | number | string | function ; boolean = | "true" | "false" ; number = | integer | float ; integer = | sign? digit+ | sign? "0x" hex+ | sign? "0b" bin+ ; sign = | "+" | "-" ; digit =| "0".."9" ; digit_ = | digit | "_" ; hex = | digit_ | "a".."f" | "A".."F" ; bin = | "0".."1" | "_" ; float = | sign? digit digit_* "." digit_+ ( "p" sign? digit_+ )? | sign? "0x" hex+ "." hex+ ( "p" sign? hex+ )? | sign? "0b" bin+ "." bin+ ( "p" sign? bin+ )? ; string = | '"' not_quote_not_newline* '"' | "/" '"' not_newline* "\n" ; not_quote_not_newline = TODO ; not_newline = TODO ; left_hand_side = # resident, assignable | identifier | atom "." identifier | atom "." digit+ | atom "." "*" | atom "[" expr "]" ; ######################################## # remaining issues ##################### ######################################## # expr + + 7 # bad, invalid # expr + +7 # good, definitive # expr ++ 7 # odd, definitive, but wrong # expr ++7 # valid, but difficult; ( expr + signed_integer ) or ( expr ++ expr )