20. Appendix: grammar sketch
Informal EBNF. TERM is a terminator (§2.2). This is a sketch for orientation, not the
normative grammar — the parser is the normative grammar, and this appendix is updated to
match it.
file = { import | declaration } ;
import = "import" ( module_path "as" ident | module_path "::" "{" ident { "," ident } "}" ) TERM ;module_path = ident { "::" ident } ;
declaration = { attribute } [ visibility ] ( fn_decl | struct_decl | union_decl | enum_decl | trait_decl | impl_decl | const_decl | static_decl | macro_decl | macro_invoke ) ;static_decl = "static" ident ":" type "=" expr TERM ; (* §7.7 *)cfg_if<T> = "#" "if" cfg_cond "{" { T } "}" (* §12.10: T is a declaration, *) [ "#" "else" ( cfg_if<T> | "{" { T } "}" ) ] ; (* a member or a statement *)cfg_cond = cfg_and { "||" cfg_and } ;cfg_and = cfg_unary { "&&" cfg_unary } ;cfg_unary = "!" cfg_unary | "(" cfg_cond ")" | ident [ "::" ident ] ;visibility = "pub" [ "(" "module" ")" ] ;attribute = "#" "[" ident [ "(" token_tree ")" ] "]" ;
fn_decl = [ "unsafe" ] [ "mut" ] "fn" ident [ generics ] "(" [ params ] ")" [ ( "->" | "!->" ) type [ "in" in_list ] ] [ "kills" kill_list ] block ; (* §6.7 *)kill_list = tie_path { "," tie_path } | "(" tie_path { "," tie_path } ")" ;in_list = tie_path | "(" tie_path { "," tie_path } ")" ;params = param { "," param } ;param = [ "&" | "&mut" ] "self" | ident ":" type [ "in" tie_path ] [ "=" expr ] ;tie_path = ( ident | "self" ) { "." ident | "[" "*" "]" | "@" ident } ;generics = "<" gparam { "," gparam } ">" ;gparam = [ "view" ] ident [ ":" bound { "+" bound } ] ; (* `view` is a keyword only here, before `struct` and before `type`, §6.7 *)
struct_decl = [ "view" ] "struct" ident [ generics ] [ ":" path ] "{" { member } "}" ; (* one base *)member = field | fn_decl | ctor | tie_decl ;field = { attribute } [ visibility ] [ "own" ] ident ":" type [ "=" expr ] [ raw_string ] TERM ; (* the tag, §13.1 *)tie_decl = "tie" ( "@" ident | "[" "*" "]" ) "=" kill_list TERM ; (* paths from fields, §6.7 *)ctor = ident "(" [ params ] ")" block ;impl_decl = "impl" [ generics ] path [ "<" type_list ">" ] "for" type "{" { impl_item } "}" ;impl_item = "type" ident "=" type TERM | fn_decl ;
union_decl = "union" ident [ generics ] "{" { variant | fn_decl } "}" ; (* methods, §7.5 *)variant = ident [ "{" field_list "}" ] TERM ; (* a bare ident carries that struct, §7.5 *)enum_decl = "enum" ident [ ":" type ] "{" { ident [ "=" expr ] TERM } "}" ;trait_decl = "trait" ident [ generics ] [ ":" trait_list ] "{" { trait_item } "}" ;trait_list = path [ "<" type_list ">" ] { "+" path [ "<" type_list ">" ] } ;trait_item = [ "view" ] "type" ident TERM | fn_decl_sig | fn_decl ;
type = access_type [ "?" ] ;access_type = "^" type | "&" [ "mut" ] type | "[" [ expr ] "]" type | "own" "^" type | "const" type (* only `const &str`, `const &cstr` and `const &[]T`, §5.3 *) | "fn" "(" [ fn_type_params ] ")" [ ( "->" | "!->" ) type [ "in" ( tie_path | "(" tie_path { "," tie_path } ")" ) ] ] [ "kills" ( tie_path | "(" tie_path { "," tie_path } ")" ) ] (* names needed only by a clause, §6.7; a comma after a bare path begins the next parameter, so more than one path is parenthesised here; an `in` after the result is the type's, not an enclosing parameter's *) | "(" type_list ")" | path [ "<" type_list ">" ] ;path = ident { "::" ident } ;fn_type_params = fn_type_param { "," fn_type_param } ;fn_type_param = [ ident ":" ] type ;? binds to the innermost complete type to its left, so ^T? parses as ^(T?). Inside a
function type, a trailing ? belongs to the return type; (fn(i32) -> bool)? is how you
spell an optional function pointer.
statement = let_stmt | assign | expr_stmt | if_stmt | while_stmt | for_stmt | switch_stmt | return_stmt | defer_stmt | block | "unsafe" block | macro_invoke | "break" [ ident ] | "continue" [ ident ] ; (* no value after `break` *)
let_stmt = "let" [ "mut" ] pattern [ ":" type ] [ "=" expr ] TERM ;assign = place ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" ) expr TERM ;if_stmt = "if" if_head block { "else" "if" if_head block } [ "else" block ] ;if_head = expr | "let" [ "mut" ] ident "=" expr ;while_stmt = [ ident ":" ] "while" expr block ;for_stmt = [ ident ":" ] "for" for_binding { "," for_binding } "in" expr block ;for_binding = [ "&" [ "mut" ] ] ( ident | "_" ) ;switch_stmt = "switch" expr "{" { case_arm } [ "default" block ] "}" ;case_arm = "case" case_label { "," case_label } block ;case_label = ident [ "(" [ "&" [ "mut" ] ] ident ")" ] (* a variant, or a constant's name *) | expr ; (* a constant: `1`, `"idle"`, `lib::MAX` *)or_expr = expr "or" or_clause ;or_clause = "{" [ ident "->" ] { statement } "}" | "return" [ expr ] | "break" [ ident ] | "continue" [ ident ] | "ignore" | "unreachable" | expr ;return_stmt = "return" [ expr ] TERM ;defer_stmt = "defer" ( statement | block ) ;
expr = assign_free_expr ; (* precedence per §11.1; includes `or` *)primary = literal | "none" | path | struct_lit | call | index | field_access | "(" expr ")" | "move" "(" expr ")" | "error" "(" [ expr { "," expr } ] ")" | "&" expr | "&mut" expr | unary | intrinsic | macro_invoke | "." ident [ struct_lit_body ] | "unsafe" block | array_lit ;array_lit = "[" [ expr { "," expr } [ "," ] ] "]" ;struct_lit = path struct_lit_body ;struct_lit_body = "{" [ field_init { "," field_init } [ "," ] ] "}" ;field_init = ident ":" expr | expr ;intrinsic = "#" ident [ "(" [ expr ] ")" ] ; (* a built-in macro, §12.7 *)
(* Macros, §12. A `token` is any token but an unbalanced bracket; a `token_tree` is a bracket-balanced run of them. *)macro_decl = "macro" ident "->" macro_kind "{" { syntax_rule } pattern_rule expand_rule "}" ;macro_kind = "decl" | "stmt" | "expr" ; (* `type` is reserved *)syntax_rule = "syntax" ident "{" pattern "}" ;pattern_rule = "pattern" "{" pattern "}" ;pattern = { capture | "$" "(" pattern ")" "?" | token } ;capture = "$" ident ":" [ "[" [ token ] "]" ] frag_kind [ "?" ] ; (* §12.3 *)frag_kind = "ident" | "literal" | "type" | "expr" | "stmt" | "block" | "code" | "decl" | ident ; (* a `syntax` rule *)expand_rule = "expand" "{" template "}" ;template = { "$" ident { "." ident } | "#" "for" ident "in" "$" ident { "." ident } [ "[" token "]" ] "{" template "}" | "#" "if" [ "!" ] "$" ident { "." ident } "{" template "}" [ "#" "else" "{" template "}" ] | "#" "error" "(" [ "$" ident { "." ident } "," ] string ")" | token } ;macro_invoke = "#" path ( token_tree_to_line_end (* declaration, statement position *) | bracket_group ) ; (* expression position *)