Part 5: Formal Grammar
source_file ::= prelude_directive* program
prelude_directive ::= import_directive
| supported_targets
| target_if
| meta_diagnostic
import_directive ::= 'import' path_string
path_string ::= 'p' STRING
supported_targets ::= '$supported_targets' '=' '[' STRING* ']'
target_if ::= 'if' target_condition '{' prelude_directive* '}'
target_condition ::= '$target' ('=?' | 'not=?') STRING
meta_diagnostic ::= ('$warning' | '$error') '(' STRING ')'
program ::= top_level_stmt*
top_level_stmt ::= fn_decl
| const_decl
| ignored_type_decl
fn_decl ::= ('let' | 'const') IDENT '=' '(' param_list ')' fn_type_annot '=>' (block | 'extern')
const_decl ::= ('let' | 'const') IDENT type_annot '=' (const_expr | 'extern')
param_list ::= (IDENT type_annot)*
fn_type_annot ::= ':>' IDENT type_param?
| ':>' type_param
type_annot ::= ':' IDENT type_param?
| type_param
type_param ::= '<' type_content '>'
block ::= '{' statement* '}'
statement ::= var_decl
| ignored_type_decl
| assign_stmt
| if_stmt
| loop_stmt
| 'break'
| 'continue'
| return_stmt
| expr
var_decl ::= ('let' | 'const') IDENT type_annot '=' expr
ignored_type_decl ::= IDENT ':type' '=' ignored_expr
| ('let' | 'const') IDENT ':type' '=' ignored_expr
assign_stmt ::= IDENT '=' expr
| IDENT compound_op expr
compound_op ::= '+=' | '-=' | '*=' | '//=' | '%='
| '<<=' | '>>=' | 'and=' | 'or=' | 'xor='
if_stmt ::= 'if' expr block else_clause?
else_clause ::= 'else' 'if' expr block else_clause?
| 'else' block
loop_stmt ::= 'loop' expr block
return_stmt ::= 'return' expr
expr ::= prefix_expr (binop prefix_expr)* cast_annot?
prefix_expr ::= '-' prefix_expr
| 'not' prefix_expr
| atom
atom ::= NUMBER
| STRING
| BASED_STRING
| 'true'
| 'false'
| 'void'
| IDENT
| IDENT '(' arg_list ')'
| '(' expr ')' ('(' arg_list ')')?
arg_list ::= expr*
cast_annot ::= 'transmute' (IDENT type_param? | type_param)
ignored_expr ::= a Dewy-compatible type expression or struct/type literal
# udewy consumes this syntax but does not evaluate it
binop ::= '+' | '-' | '*' | '//' | '%'
| '<<' | '>>'
| '=?' | 'not=?' | '>?' | '<?' | '>=?' | '<=?'
| 'and' | 'or' | 'xor'
const_expr ::= NUMBER | STRING | BASED_STRING | IDENT
# Lexical elements
IDENT ::= [a-zA-Z_][a-zA-Z0-9_]*
NUMBER ::= decimal | hex | binary
decimal ::= [0-9][0-9_]*
hex ::= '0x' [0-9a-fA-F_]+
binary ::= '0b' [01_]+
STRING ::= '"' string_char* '"'
string_char ::= <any char except '"' or '\'>
| '\' <any char>
BASED_STRING ::= ('0b' binary_string_body | '0x' hex_string_body)
binary_string_body ::= '"' ([01_] | whitespace | line_comment)* '"'
hex_string_body ::= '"' ([0-9a-fA-F_] | whitespace | line_comment)* '"'
line_comment ::= '#' <characters through end of line>
NOTE:
prelude_directiveforms are consumed during preprocessing and do not appear in the token stream seen by the parser. The wordimportremains reserved, so any survivingimportis rejected during tokenization.