21. Grammar
Repetition is {...}, optional syntax is [...], quoted text is a token. NEWLINE, INDENT, and DEDENT come from the layout lexer; RAW_LINE is a physical line captured without tokenisation after removal of the suite's indentation baseline. Semantic restrictions in the earlier chapters remain normative over this shape.
module = { import_decl }, { top_decl }, EOF ;
import_decl = "import", module_path, [ "as", IDENT ], NEWLINE
| "from", module_path, "import", IDENT, { ",", IDENT }, NEWLINE ;
module_path = IDENT, { ".", IDENT } ;
top_decl = [ "pub" ], ( constant_decl | global_decl | type_alias
| function_decl | struct_decl | enum_decl
| union_decl | interface_decl | extern_decl )
| "export", function_decl
| test_decl
| asm_module_decl ;
constant_decl = "let", IDENT, [ ":", type ], "=", expression, NEWLINE ;
global_decl = [ "thread_local" ], "var", IDENT, ":", type, NEWLINE ;
type_alias = "type", TYPE_IDENT, "=", type, NEWLINE ;
function_decl = [ "inline" ], [ "mutating" ], "func", [ TYPE_IDENT, "." ], IDENT,
[ generic_params ], parameter_list, result_clause, ":", suite ;
function_sig = [ "mutating" ], "func", IDENT, [ generic_params ],
parameter_list, result_clause, NEWLINE ;
generic_params = "[", generic_param, { ",", generic_param }, [ "," ], "]" ;
generic_param = TYPE_IDENT, [ ":", interface_type, { "&", interface_type } ] ;
parameter_list = "(", [ parameter, { ",", parameter }, [ "," ] ], ")" ;
parameter = "self" | IDENT, ":", type, [ "=", constant_expression ] ;
result_clause = [ "->", type ] ;
implements_clause
= [ "implements", interface_type, { ",", interface_type } ] ;
struct_decl = [ "packed" | "align", "(", INTEGER_LITERAL, ")" ],
"struct", TYPE_IDENT, [ generic_params ], implements_clause, ":",
NEWLINE, INDENT, type_member, { type_member }, DEDENT ;
type_member = [ "pub" ], ( field_decl | function_decl ) ;
field_decl = ( "let" | "var" ), IDENT, ":", type, [ "=", constant_expression ], NEWLINE ;
enum_decl = "enum", TYPE_IDENT, [ generic_params ], [ "as", integer_type ],
implements_clause, ":", NEWLINE, INDENT,
enum_case, { enum_case }, { [ "pub" ], function_decl }, DEDENT ;
enum_case = IDENT, [ payload_list | "=", INTEGER_LITERAL ], NEWLINE ;
payload_list = "(", payload, { ",", payload }, [ "," ], ")" ;
payload = IDENT, ":", type ;
union_decl = "union", TYPE_IDENT, ":", NEWLINE, INDENT,
union_member, { union_member }, { [ "pub" ], function_decl }, DEDENT ;
union_member = IDENT, ":", type, NEWLINE ;
interface_decl = "interface", TYPE_IDENT, [ generic_params ], ":", NEWLINE,
INDENT, function_sig, { function_sig }, DEDENT ;
test_decl = "test", STRING_LITERAL, ":", suite ;
asm_module_decl = "asm", IDENT, ":", NEWLINE, INDENT, RAW_LINE, { RAW_LINE }, DEDENT ;
extern_decl = "extern", ( extern_type | extern_func | extern_var
| extern_struct | extern_union ) ;
extern_type = "type", TYPE_IDENT, [ "=", ( "u32" | "i32" | "u64" | "i64" ) ], NEWLINE ;
extern_func = [ "blocking" ], "func", IDENT, [ "as", STRING_LITERAL ],
"(", [ extern_parameter, { ",", extern_parameter } ], [ ",", "..." ], ")",
result_clause, NEWLINE ;
extern_parameter
= [ "out" ], IDENT, ":", type ;
extern_var = "var", IDENT, ":", type, NEWLINE ;
extern_struct = "struct", TYPE_IDENT, ":", NEWLINE, INDENT, union_member, { union_member }, DEDENT ;
extern_union = "union", TYPE_IDENT, ":", NEWLINE, INDENT, union_member, { union_member }, DEDENT ;
suite = simple_stmt
| NEWLINE, INDENT, statement, { statement }, DEDENT ;
statement = simple_stmt | if_stmt | while_stmt | for_stmt | match_stmt
| labeled_loop | asm_stmt ;
simple_stmt = binding_stmt | assignment_stmt
| "break", [ IDENT ], NEWLINE
| "continue", [ IDENT ], NEWLINE
| "return", [ expression ], NEWLINE
| "defer", call_expression, NEWLINE
| "errdefer", call_expression, NEWLINE
| "recover", expression, NEWLINE
| expression, NEWLINE ;
binding_stmt = "let", binding_pattern, [ ":", type ], "=", expression, NEWLINE
| "var", binding_pattern, ":", type, [ "=", ( expression | "---" ) ], NEWLINE
| "var", binding_pattern, "=", expression, NEWLINE ;
binding_pattern = IDENT | "(", IDENT, ",", IDENT, { ",", IDENT }, [ "," ], ")" ;
assignment_stmt = lvalue, ASSIGN_OP, expression, NEWLINE ;
lvalue = IDENT, { lvalue_part }
| "*", unary_expr
| "(", lvalue, ")", { lvalue_part } ;
lvalue_part = ".", IDENT | "[", expression, "]" ;
ASSIGN_OP = "=" | "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "+%=" | "-%=" | "*%="
| "&=" | "|=" | "^=" | "<<=" | ">>=" ;
if_stmt = "if", expression, ":", suite, { "elif", expression, ":", suite },
[ "else", ":", suite ]
| "if", "let", IDENT, "=", expression, ":", suite, [ "else", ":", suite ] ;
while_stmt = "while", expression, ":", suite
| "while", "let", IDENT, "=", expression, ":", suite ;
for_stmt = "for", IDENT, "in", expression, ":", suite ;
labeled_loop = IDENT, ":", ( while_stmt | for_stmt ) ;
match_stmt = "match", expression, ":", NEWLINE, INDENT, match_arm, { match_arm }, DEDENT ;
match_arm = pattern, { ",", pattern }, ":", suite ;
pattern = "_" | literal_pattern | case_pattern ;
literal_pattern = pattern_literal, [ ( "..<" | "..=" ), pattern_literal ] ;
pattern_literal = literal | "-", ( INTEGER_LITERAL | FLOAT_LITERAL ) ;
case_pattern = ".", IDENT, [ "(", [ IDENT, { ",", IDENT } ], ")" ] ;
asm_stmt = "asm", IDENT, [ "(", asm_operand, { ",", asm_operand }, ")" ],
":", NEWLINE, INDENT, RAW_LINE, { RAW_LINE }, DEDENT ;
asm_operand = ( "in" | "out" | "inout" ), ( "reg" | "mem" ), IDENT
| "clobber", ( IDENT | "memory" ), { ",", ( IDENT | "memory" ) } ;
expression = else_expr, [ "catch", IDENT, ":", suite ] ;
else_expr = conditional_expr, [ "else", else_expr ] ;
conditional_expr
= or_expr, [ "if", or_expr, "else", conditional_expr ] ;
or_expr = and_expr, { "or", and_expr } ;
and_expr = comparison_expr, { "and", comparison_expr } ;
comparison_expr = range_expr, [ COMPARE_OP, range_expr ] ;
range_expr = bit_or_expr, [ ( "..<" | "..=" ), bit_or_expr ] ;
bit_or_expr = bit_xor_expr, { "|", bit_xor_expr } ;
bit_xor_expr = bit_and_expr, { "^", bit_and_expr } ;
bit_and_expr = shift_expr, { "&", shift_expr } ;
shift_expr = additive_expr, { ( "<<" | ">>" ), additive_expr } ;
additive_expr = multiply_expr, { ( "+" | "-" | "+%" | "-%" ), multiply_expr } ;
multiply_expr = unary_expr, { ( "*" | "/" | "//" | "%" | "*%" ), unary_expr } ;
unary_expr = { "try" | "not" | "+" | "-" | "~" | "*" | "&" | cast_prefix }, postfix_expr ;
cast_prefix = "(", type, ")" ; (* only when the parenthesised text is cast-shaped, §7.5 *)
postfix_expr = primary_expr, { ".", IDENT | argument_list | type_arguments, argument_list
| "[", index_or_slice, "]" } ;
call_expression = postfix_expr ;
argument_list = "(", [ argument, { ",", argument }, [ "," ] ], ")" ;
argument = [ IDENT, "=" ], expression ;
index_or_slice = expression | expression, "..<", [ expression ] | "..<", expression | expression, ".." ;
primary_expr = literal | IDENT | ".", IDENT, [ argument_list ]
| "(", expression, [ ",", expression, { ",", expression }, [ "," ] ], ")"
| "(", ")"
| "[", [ expression, { ",", expression }, [ "," ] ], "]"
| lambda_expr | match_expr | span_constructor ;
lambda_expr = "(", [ IDENT, [ ":", type ], { ",", IDENT, [ ":", type ] } ], ")", "=>", expression ;
match_expr = "match", expression, ":", NEWLINE, INDENT,
pattern, { ",", pattern }, "=>", expression, NEWLINE,
{ pattern, { ",", pattern }, "=>", expression, NEWLINE }, DEDENT ;
span_constructor
= type, "[", "]", argument_list ; (* u8[](pointer, count) *)
type = [ "@" ], [ "const" ], [ "volatile" ], type_core,
{ "*" | "[", INTEGER_LITERAL, "]" | "[", "]" }, [ "?" ], [ "!" ] ;
type_core = type_name, [ type_arguments ]
| "func", "(", [ type, { ",", type } ], ")", "->", type
| "void"
| "(", type, ")"
| "(", type, ",", type, { ",", type }, [ "," ], ")" ;
type_name = TYPE_PATH | CORE_TYPE ;
type_arguments = "[", type, { ",", type }, [ "," ], "]" ;
interface_type = type_name, [ type_arguments ] ;
integer_type = "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" ;
literal = INTEGER_LITERAL | FLOAT_LITERAL | CHAR_LITERAL | STRING_LITERAL
| formatted_string | "true" | "false" | "none" ;
formatted_string
= FORMAT_START, { FORMAT_TEXT | "{", expression, "}" }, FORMAT_END ;
Notes on the shape. Qualifiers bind to the innermost type_core, so const (T*)* is C's T *const * and void must be followed by at least one *. A type_arguments bracket contains types; a bracket after a complete type containing only an integer literal or nothing is an array or span suffix. *% is recognised only between two operands, so it cannot be confused with a dereference. The new tokens @, ---, ..., +%, -%, *%, +%=, -%=, *%= are matched longest-first and admitted by the parser in Base modules only, so the formatter stays single. A statement beginning IDENT ":" is a labeled loop and nothing else, because no other statement begins with a bare identifier and a colon.