Grammar
The Nazm grammar in EBNF, as committed in docs/grammar.ebnf.
(* The Nazm grammar.
*
* This file is an artefact, not a description: `crates/nazm-syntax/tests/grammar.rs`
* reads it and checks three things against the real implementation.
*
* 1. Every terminal written here is a real token, and every token the lexer has is
* written here. A keyword added to the lexer and not to the grammar fails the test.
* 2. Every nonterminal referenced is defined, and every nonterminal defined is
* reachable from `program`. A rule nobody uses is a rule nobody maintains.
* 3. Every rule carries at least one `@example`, and every example is fed to the real
* parser and must parse with no diagnostics. A grammar whose examples the parser
* rejects is describing a language that does not exist.
*
* What the test does *not* do is prove the parser accepts exactly this language. That
* would need a generated parser, and this one is written by hand — deliberately, for the
* diagnostics. So: the terminals are exact, the examples are executable, and the
* structure is a claim checked by review. `docs/guide.md` is generated from this file.
*
* Notation: `=` defines, `|` alternates, `{ x }` is zero or more, `[ x ]` is optional,
* `( )` groups, quoted text is a terminal, UPPERCASE is a token class.
*)
(* @example fn main() -> Int { 0 } *)
(* @example use "other.nz"; fn main() -> Int { 0 } *)
(* @example struct Point { x: Int, } fn main() -> Int { Point(x: 1).x } *)
(* @example enum State { Ready, } fn main() -> Int { match State.Ready() { State.Ready() => 0, } } *)
(* @example fn identity[T](value: T) -> T { value } fn main() -> Int { identity(7) } *)
(* @example trait Show { fn show(self: Self) -> Str; } impl Show for Int { fn show(self: Int) -> Str { int_to_str(self) } } fn main() -> Int { str_len(7.show()) } *)
(* @example const SIZE: Int = 4; fn main() -> Int { SIZE } *)
(* @example @cfg(os = "linux") fn f() -> Int { 1 } @cfg(not(os = "linux")) fn f() -> Int { 0 } fn main() -> Int { f() } *)
program = { use_item } , { { attribute } , ( function | foreign_function | record | foreign_struct | enumeration | trait_item | impl_item | constant ) } ;
(* An attribute (Gate 2): `@` and a name from a closed vocabulary — `cfg`, `test`, `bench`,
* `fuzz`, `deprecated` — before a top-level declaration, never before a `use`. Which names
* exist and what each takes is the checker's (`N0619`), not the grammar's. See
* `docs/spec.md`, *Attributes* and *Conditional compilation*.
*)
(* @example @test fn checks() -> Int { 0 } fn main() -> Int { 0 } *)
(* @example @cfg(all(os = "macos", any(arch = "aarch64", arch = "x86_64"))) fn f() -> Int { 0 } fn main() -> Int { 0 } *)
attribute = "@" , IDENT , [ "(" , [ attribute_arg , { "," , attribute_arg } , [ "," ] ] , ")" ] ;
(* @example @deprecated("use g") fn f() -> Int { 0 } fn main() -> Int { 0 } *)
(* @example @test(fails) fn f() -> Int { 0 } fn main() -> Int { 0 } *)
attribute_arg = STRING
| IDENT , [ "=" , STRING | "(" , [ attribute_arg , { "," , attribute_arg } , [ "," ] ] , ")" ] ;
(* A constant (Gate 2): a value the checker computes from literals, other constants,
* operators and numeric conversions. `const` is a word only here, before a name and a `:`,
* so it remains an ordinary name everywhere else. See `docs/spec.md`, *Constants*.
*
* A static (Gate 3) is written the same way with `static`: storage with one address, a
* number or a `Bool` read-only, or an `Atomic` started by `atomic_new` of a constant. `static`
* is a word only where `const` is. See `docs/spec.md`, *Statics*.
*)
(* @example const MASK: UInt8 = 0xF0; fn main() -> Int { Int(MASK) } *)
(* @example static TICKS: Atomic = atomic_new(0); fn main() -> Int { atomic_add(TICKS, 1) } *)
constant = [ visibility ] , ( "const" | "static" ) , IDENT , ":" , type , "=" , expression , ";" ;
(* A trait (N78): a named set of methods a type may implement. Each method is a signature
* ended by `;` — there are no default bodies — whose first parameter is `self: Self`. A trait
* is in the type namespace and is not a type: it is written as a type parameter's bound,
* `[T: Show]`, and never as the type of a value. See `docs/spec.md`, *Traits and methods*.
*)
(* @example pub trait Show { fn show(self: Self) -> Str; fn shout(self: Self, n: Int) -> Str ! {}; } fn main() -> Int { 0 } *)
trait_item = [ visibility ] , "trait" , IDENT , "{" , { trait_method } , "}" ;
(* @example trait Size { fn size(self: Self) -> Int; } fn main() -> Int { 0 } *)
trait_method = "fn" , IDENT , "(" , [ parameters ] , ")" , "->" , type , [ effect_set ] , ";" ;
(* `impl Trait for Type`: every method of the trait, once, with `Self` written as the type.
* `for` is a word here and nowhere else. At most one impl of a trait for a type, in the
* trait's module or the type's. *)
(* @example trait Size { fn size(self: Self) -> Int; } struct P { x: Int, } impl Size for P { fn size(self: P) -> Int { 1 } } fn main() -> Int { 0 } *)
impl_item = "impl" , type , IDENT , type , "{" , { function } , "}" ;
(* @example use "lib/helpers.nz"; fn main() -> Int { 0 } *)
(* `as NAME` imports the module under a name (N49): its exports are written `NAME::item`
* and are not brought into scope. `as` is a word here and nowhere else. See `docs/spec.md`,
* *Qualified names*.
*)
(* @example use "other.nz" as other; fn main() -> Int { 0 } *)
(* `pub use` re-exports (N103): every name the module offers, or the names in braces, each
* optionally offered under another name with the word `as`. A re-export is also an ordinary
* import, and is never under a name: `pub use "PATH" as m;` is refused. See `docs/spec.md`,
* *`pub use` offers another module's definitions*.
*)
(* @example pub use "shapes.nz"; fn main() -> Int { 0 } *)
(* @example pub use "shapes.nz" { double, Sq as Square }; fn main() -> Int { 0 } *)
use_item = "use" , STRING , [ IDENT , IDENT ] , ";"
| visibility , "use" , STRING , [ "{" , [ reexport_name , { "," , reexport_name } , [ "," ] ] , "}" ] , ";" ;
(* @example pub use "shapes.nz" { Sq as Square }; fn main() -> Int { 0 } *)
reexport_name = IDENT , [ IDENT , IDENT ] ;
(* `NAME ::` before the name of a definition: a function, a record, an enum. Nazm has no
* module-level values, so a qualified name is never a variable.
*)
(* @example use "geometry.nz" as g; fn main() -> Int { g::area(g::Point(x: 1, y: 2)) } *)
(* @example use "shapes.nz" as s; fn f(x: s::Shape) -> Int { match x { s::Shape.Dot() => 0, } } fn main() -> Int { 0 } *)
qualifier = IDENT , "::" ;
(* @example fn main() -> Int { 0 } *)
(* @example fn add(a: Int, b: Int) -> Int { a + b } fn main() -> Int { add(1, 2) } *)
(* @example pub fn add(a: Int, b: Int) -> Int { a + b } fn main() -> Int { add(1, 2) } *)
(* @example fn first[A, B](a: A, b: B) -> A { a } fn main() -> Int { first(1, "x") } *)
function = [ visibility ] , "fn" , IDENT , [ type_parameters ] , "(" , [ parameters ] , ")" ,
"->" , type , [ effect_set ] , { contract } , block ;
(* A contract clause (N87): `requires` is checked as the function is entered, `ensures` as it
* returns, on every path, with `result` the value returned. Both are IDENTs recognised only
* here, between the signature and the body; every `requires` comes before every `ensures`. A
* clause is a `Bool` of literals, names, operators, field reads and calls of pure functions.
* See `docs/spec.md`, *Contracts*.
*)
(* @example fn half(n: Int) -> Int ! {} requires n >= 0 ensures result * 2 <= n { n / 2 } fn main() -> Int { half(8) } *)
contract = ( "requires" | "ensures" ) , expression ;
(* A foreign function (N42): a C function this program calls, declared by its signature and
* its C symbol, with no body. `extern` is not a keyword — it is recognised only where a
* declaration starts, before a string — so it is an IDENT here, and the string before `fn`
* is the ABI, which must be `"C"`. Only `Int` and `Bool` cross; no type parameters and no
* written effect set: calling one is always the effect `foreign`, and needs a `ForeignCap`.
* `nazm run` refuses a program that calls one; `nazm build --link FILE` links it. See
* `docs/spec.md`, *Foreign functions*.
*)
(* @example extern "C" fn c_add(a: Int, b: Int) -> Int = "c_add"; fn main() -> Int { 0 } *)
(* @example pub extern "C" fn is_even(n: Int) -> Bool = "c_is_even"; fn main() -> Int { 0 } *)
foreign_function = [ visibility ] , IDENT , STRING , "fn" , IDENT , "(" , [ parameters ] , ")" ,
"->" , type , "=" , STRING , ";" ;
(* The effects a function declares it may exercise: after the return type, a `!` and a
* set in braces. `! {}` is the empty set — the function is pure, and its body may call
* nothing that is not. With no set written the checker infers one from the body, and
* another module calling the function must assume every effect — so `fn f() -> Int` and
* `fn f() -> Int ! {}` are not the same: only the second promises purity. A declared set
* says what a function does, not what allows it: its body holds only the authority of
* the capability values in its scope (`IoCap`, `SpawnCap`). The names live in their
* own namespace: `io`, `spawn` and `foreign` are the only effects, a name that is not one is refused,
* and so is one written twice. Order means nothing. See `docs/spec.md`, *Effects*.
*)
(* @example fn show(io: IoCap, s: Str) -> Int ! { io } { print(s) } fn main(io: IoCap) -> Int { show(io, "hi") } *)
(* @example fn add(a: Int, b: Int) -> Int ! {} { a + b } fn main() -> Int { add(1, 2) } *)
(* @example fn w(c: Chan) -> Int { 0 } fn run(t: SpawnCap) -> Int ! { spawn } { let c = chan_new(1); scope { spawn w(c); } 0 } fn main(t: SpawnCap) -> Int { run(t) } *)
effect_set = "!" , "{" , [ effect_name , { "," , effect_name } ] , "}" ;
(* `spawn` is a keyword, and the effect of starting a task is named by it. *)
(* @example fn w(c: Chan) -> Int { 0 } fn run(io: IoCap, t: SpawnCap) -> Int ! { io, spawn } { let c = chan_new(1); scope { spawn w(c); } print("done") } fn main(io: IoCap, t: SpawnCap) -> Int { run(io, t) } *)
effect_name = IDENT | "spawn" ;
(* A generic declaration's parameters: at least one, no trailing comma. A parameter
* is a **position** — renaming one changes nothing — and it is a type and nothing else.
* Which names may be one (not a built-in type, not `Vec`, not twice) is the checker's
* question. See `docs/spec.md`, *Generics*.
*)
(* @example struct Pair[A, B] { first: A, second: B, } fn main() -> Int { 0 } *)
(* @example enum Maybe[T] { None, Some(value: T), } fn main() -> Int { 0 } *)
type_parameters = "[" , type_parameter , { "," , type_parameter } , "]" ;
(* `effects E` declares the function's effect parameter (N51): `effects` is a word only
* here, before a name. At most one, only on a function that declares its effect set, and
* never on a record or an enum (`N0388`); its name may then be written in the function's
* effect sets. See `docs/spec.md`, *Effect parameters and captured authority*.
*
* A parameter may require a capability of every type it stands for: one name after a
* `:`, and no list: `Equality`, which gives the parameter `==`, or a trait (N78), which
* gives it the trait's methods. Only a function's parameter may require one. `Equality` is
* not a type and nothing declares it. See `docs/spec.md`, *A type parameter may require
* equality* and *Traits and methods*.
*)
(* @example fn same[T: Equality](a: T, b: T) -> Bool { a == b } fn main() -> Int { if same(1, 1) { 1 } else { 0 } } *)
(* @example fn apply[effects E](f: fn() -> Int ! { E }) -> Int ! { E } { f() } fn main() -> Int { apply(fn() -> Int { 1 }) } *)
type_parameter = IDENT , [ ":" , IDENT ] | "effects" , IDENT ;
(* One bit, and one spelling for it. Absent means private to this module; `pub` exports
* it to the modules that directly import this one. There is no `private` keyword,
* because the default needs no word, and no package or friend visibility: a package offers
* what its modules export, and nothing sits between. See `docs/spec.md`.
*)
(* @example pub fn helper() -> Int { 1 } fn main() -> Int { helper() } *)
visibility = "pub" ;
(* A record: a nominal type whose fields are named. `struct` is the keyword; *record* is
* what the semantics call the category. One concept, one spelling in each place — see
* `docs/spec.md`, *Records*.
*
* Declaration order is written down and means nothing: construction names every field,
* and reordering two fields changes neither the type, nor its interface fingerprint, nor
* its native layout.
*)
(* @example struct Point { x: Int, y: Int, } fn main() -> Int { Point(x: 1, y: 2).x } *)
(* @example pub struct User { name: Str, } fn main() -> Int { str_len(User(name: "a").name) } *)
(* @example struct Box[T] { value: T, } fn main() -> Int { Box[Int](value: 1).value } *)
record = [ visibility ] , "struct" , IDENT , [ type_parameters ] , "{" , [ fields ] , "}" ;
(* A struct C sees (N85): `extern "C" struct Name;` is an opaque handle — a C pointer only a
* foreign function returns, never built, read or compared — and `extern "C" struct Name { … }`
* a record laid out as C's struct, its fields in the order written, which here is meaning.
* `extern` is an IDENT recognised before a string, as for a foreign function. See `docs/spec.md`,
* *Foreign functions v3*.
*)
(* @example extern "C" struct Db; fn main() -> Int { 0 } *)
(* @example extern "C" struct Point { x: Int, flag: Bool, y: Int } fn main() -> Int { Point(x: 1, flag: true, y: 2).y } *)
foreign_struct = [ visibility ] , IDENT , STRING , "struct" , IDENT , ( ";" | "{" , [ fields ] , "}" ) ;
(* @example struct P { x: Int, y: Int, } fn main() -> Int { P(x: 1, y: 2).y } *)
fields = field , { "," , field } , [ "," ] ;
(* @example struct P { x: Int, } fn main() -> Int { P(x: 7).x } *)
field = IDENT , ":" , type ;
(* An enum: a closed set of named variants, exactly one of which is active in any value.
* `enum` is the keyword and *enum* is what the semantics call the category — unlike
* `struct`/record there is no second word, because none was needed.
*
* Variant order is written down and means nothing: the discriminant is derived from the
* names, so reordering two variants changes neither the type, nor its interface
* fingerprint, nor its native representation. See `docs/spec.md`, *Enums*.
*)
(* @example enum State { Ready, Done, } fn main() -> Int { match State.Ready() { State.Ready() => 1, State.Done() => 0, } } *)
(* @example pub enum E { V(x: Int), } fn main() -> Int { match E.V(x: 1) { E.V(x: n) => n, } } *)
(* @example enum Maybe[T] { None, Some(value: T), } fn main() -> Int { match Maybe[Int].Some(value: 1) { Maybe.None() => 0, Maybe.Some(value: v) => v, } } *)
enumeration = [ visibility ] , "enum" , IDENT , [ type_parameters ] , "{" , [ variants ] , "}" ;
(* @example enum E { A, B, } fn main() -> Int { match E.A() { E.A() => 1, E.B() => 0, } } *)
variants = variant , { "," , variant } , [ "," ] ;
(* A variant with no payload is written with **no parentheses**: there is no field list,
* exactly as a record with no fields has none. Construction and patterns always write
* them, because there the parentheses are what separates a variant from a projection.
*)
(* @example enum E { A, } fn main() -> Int { match E.A() { E.A() => 0, } } *)
(* @example enum E { V(x: Int, y: Int), } fn main() -> Int { match E.V(x: 1, y: 2) { E.V(x: a, y: b) => a + b, } } *)
variant = IDENT , [ "(" , payload , ")" ] ;
(* @example enum E { V(x: Int,), } fn main() -> Int { match E.V(x: 1) { E.V(x: n) => n, } } *)
payload = field , { "," , field } , [ "," ] ;
(* @example fn f(a: Int, b: Str) -> Int { str_len(b) + a } fn main() -> Int { f(1, "x") } *)
parameters = parameter , { "," , parameter } ;
(* @example fn f(n: Int) -> Int { n } fn main() -> Int { f(1) } *)
parameter = IDENT , ":" , type ;
(* A name, applied to type arguments or not. Which names are types — `Int`, `Bool`,
* `Str`, `Ints`, `Strs`, `Chan` (and `Chan[T]`, N53), the seven capabilities (`IoCap`, `OutCap`, `SpawnCap`, `ForeignCap`, `VouchCap`, `TimeCap`, `MmioCap`), `Vec`, a
* declared record or enum, a type parameter in scope — is a *checking* question, not a grammatical one: the parser accepts any
* identifier here and the checker reports `N0305` for one that is not a type. A grammar
* that listed them would be claiming the parser rejects `fn f(x: Widget)`, which it does
* not — it parses it and then says what is wrong with it, which is how one mistake stays
* one error. How many arguments a name takes is the checker's too (`N0351` – `N0353`).
*)
(* @example fn f(c: Chan) -> Int { 0 } fn main() -> Int { f(chan_new(1)) } *)
(* @example fn f(c: Chan[Str]) -> Bool { chan_send_of(c, "hi") } fn main() -> Int { if f(chan_new_of[Str](1)) { 0 } else { 1 } } *)
(* @example fn f(xs: Vec[Vec[Int]]) -> Int { vec_len(xs) } fn main() -> Int { f(vec_new[Vec[Int]]()) } *)
(* @example fn say(io: IoCap, s: Str) -> Int ! { io } { print(s) } fn main(io: IoCap) -> Int { say(io, "hi") } *)
(* @example fn f(xs: [Int; 4]) -> Int { xs[0] } fn main() -> Int { f([1, 2, 3, 4]) } *)
type = function_type | array_type | [ qualifier ] , IDENT , [ type_arguments ] ;
(* A fixed-size array type (Gate 2): its element type and its length, an integer literal or a
* constant's name. See `docs/spec.md`, *Arrays*.
*)
(* @example const N: Int = 3; fn f(xs: [UInt8; N]) -> Int { Int(xs[2]) } fn main() -> Int { f([UInt8(1), 2, 3]) } *)
array_type = "[" , type , ";" , ( INTEGER | IDENT ) , "]" ;
(* A function type (N50): its parameters' types, its result's, and the effects a call
* performs — none when unwritten. Structural: two are one type exactly when all three are
* equal. See `docs/spec.md`, *Function values and closures*.
*)
(* @example fn twice(f: fn(Int) -> Int, x: Int) -> Int { f(f(x)) } fn main() -> Int { 0 } *)
(* @example fn run(c: IoCap, f: fn(IoCap) -> Int ! { io }) -> Int ! { io } { f(c) } fn main() -> Int { 0 } *)
function_type = "fn" , "(" , [ type , { "," , type } ] , ")" , "->" , type , [ effect_set ] ;
(* Square brackets, and one spelling: there are no angle brackets and no indexing, so
* `name[` can only begin type arguments. Empty brackets parse — `Vec[]` is a type written
* with the wrong number of arguments, which is `N0353` rather than a syntax error.
*)
(* @example struct Pair[A, B] { a: A, b: B, } fn f(p: Pair[Int, Str]) -> Int { p.a } fn main() -> Int { f(Pair[Int, Str](a: 1, b: "x")) } *)
type_arguments = "[" , [ type , { "," , type } ] , "]" ;
(* @example fn main() -> Int { let a = 1; a } *)
(* @example fn main() -> Int { 1 } *)
block = "{" , { statement } , [ expression ] , "}" ;
(* @example fn main() -> Int { let a = 1; a } *)
statement = let_statement
| assignment
| while_statement
| return_statement
| break_statement
| continue_statement
| scope_statement
| spawn_statement
| expression_statement ;
(* @example fn main() -> Int { let a = 1; a } *)
(* @example fn main() -> Int { let mut a = 1; a = 2; a } *)
let_statement = "let" , [ "mut" ] , IDENT , "=" , expression , ";" ;
(* The left-hand side is a **place**: a binding, then a path of fields through it.
* Mutability is checked at the binding, so `p.x = 2` needs `let mut p` — see
* `docs/spec.md`, *Binding mutability reaches every field*.
*)
(* @example fn main() -> Int { let mut a = 1; a = 2; a } *)
(* @example struct P { x: Int, } fn main() -> Int { let mut p = P(x: 1); p.x = 2; p.x } *)
assignment = place , "=" , expression , ";" ;
(* @example struct I { n: Int, } struct O { i: I, } fn main() -> Int { let mut o = O(i: I(n: 1)); o.i.n = 2; o.i.n } *)
(* @example fn main() -> Int { let mut g = [[0; 2]; 2]; g[1][0] = 5; g[1][0] } *)
place = IDENT , { "." , IDENT | "[" , expression , "]" } ;
(* @example fn main() -> Int { let mut i = 0; while i < 3 { i = i + 1; } i } *)
while_statement = "while" , expression , block ;
(* @example fn f() -> Int { return 1; } fn main() -> Int { f() } *)
return_statement = "return" , expression , ";" ;
(* @example fn main() -> Int { let mut i = 0; while true { break; } i } *)
break_statement = "break" , ";" ;
(* @example fn main() -> Int { let mut i = 0; while i < 3 { i = i + 1; continue; } i } *)
continue_statement = "continue" , ";" ;
(* @example fn w(c: Chan) -> Int { 0 } fn main(t: SpawnCap) -> Int { let c = chan_new(1); scope { spawn w(c); } 0 } *)
scope_statement = "scope" , block ;
(* @example fn w(c: Chan, n: Int) -> Int { 0 } fn main(t: SpawnCap) -> Int { let c = chan_new(1); scope { spawn w(c, 1); } 0 } *)
spawn_statement = "spawn" , IDENT , "(" , [ arguments ] , ")" , ";" ;
(* @example fn main(out: OutCap) -> Int { print("hi"); 0 } *)
expression_statement = expression , ";" ;
(* @example fn main() -> Int { 1 + 2 * 3 } *)
expression = disjunction ;
(* `||` and `&&` (N49): `Bool` operands, left-associative, and short-circuit — the right
* operand is evaluated only when the left does not decide. `||` binds loosest. Each is the
* `if` it lowers to; see `docs/spec.md`, *Logical operators*.
*)
(* @example fn main() -> Int { if 1 < 2 || 2 < 1 { 1 } else { 0 } } *)
disjunction = conjunction , { "||" , conjunction } ;
(* @example fn main() -> Int { if 1 < 2 && 2 < 3 { 1 } else { 0 } } *)
conjunction = comparison , { "&&" , comparison } ;
(* Each operator gets an example. Listing six and demonstrating one would be a grammar
* that is right about the alternatives and silent about five of them, which the
* examples exist to prevent.
*)
(* @example fn main() -> Int { if 1 < 2 { 1 } else { 0 } } *)
(* @example fn main() -> Int { if 1 == 1 { 1 } else { 0 } } *)
(* @example fn main() -> Int { if 1 != 2 { 1 } else { 0 } } *)
(* @example fn main() -> Int { if 1 <= 2 { 1 } else { 0 } } *)
(* @example fn main() -> Int { if 2 > 1 { 1 } else { 0 } } *)
(* @example fn main() -> Int { if 2 >= 2 { 1 } else { 0 } } *)
comparison = bit_or , { ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) , bit_or } ;
(* The bit operators (Gate 2): two integers of one type, or for a shift an integer and an
* `Int` amount. They bind tighter than comparison — `a & m == 0` is `(a & m) == 0`, not C's
* reading — and looser than arithmetic. See `docs/general-purpose.md` §3.
*)
(* @example fn main() -> Int { 6 | 1 } *)
bit_or = bit_xor , { "|" , bit_xor } ;
(* @example fn main() -> Int { 6 ^ 3 } *)
bit_xor = bit_and , { "^" , bit_and } ;
(* @example fn main() -> Int { 6 & 3 } *)
bit_and = shift , { "&" , shift } ;
(* @example fn main() -> Int { 1 << 4 >> 2 } *)
shift = sum , { ( "<<" | ">>" ) , sum } ;
(* @example fn main() -> Int { 1 + 2 - 3 } *)
sum = product , { ( "+" | "-" ) , product } ;
(* @example fn main() -> Int { 2 * 3 / 4 % 5 } *)
product = unary , { ( "*" | "/" | "%" ) , unary } ;
(* `!` is logical negation, as tight as `-`: `!a && b` is `(!a) && b`. It is never the `!` of
* an effect set, which follows a result type, where no expression is.
*)
(* `~` is the bit complement of an integer (Gate 2). *)
(* @example fn main() -> Int { -1 } *)
(* @example fn main() -> Int { if !false { 1 } else { 0 } } *)
(* @example fn main() -> Int { ~0 } *)
unary = [ "-" | "!" | "~" ] , primary ;
(* @example fn main() -> Int { (1 + 2) * 3 } *)
(* @example fn main() -> Int { if true { 1 } else { 0 } } *)
(* @example fn main() -> Int { if false { 1 } else { 0 } } *)
(* @example fn main() -> Int { str_len("a name") } *)
(* @example struct P { x: Int, } fn main() -> Int { P(x: 1).x } *)
(* `?` is a postfix beside projection, in any order and any number: `r?`, `make()?.x`,
* `r??`. It binds tighter than negation, so `-r?` is `-(r?)`. Whether the operand is a
* `Result` — and the core prelude's one — is the checker's question, not the grammar's.
* See `docs/spec.md`, *Propagation*.
*)
(* @example fn f(r: Result[Int, Str]) -> Result[Int, Str] { Result[Int, Str].Ok(value: r? + 1) } fn main() -> Int { 0 } *)
(* @example struct P { x: Int, } fn f(r: Result[P, Str]) -> Result[Int, Str] { Result[Int, Str].Ok(value: -r?.x) } fn main() -> Int { 0 } *)
(* `.name(…)` after an operand is a method call (N78): `p.show()`, `make().size()`. With a
* trait's name as the operand, `Show.show(p)`, the trait chooses the method and the first
* argument is the receiver. *)
(* @example trait Size { fn size(self: Self) -> Int; } fn f[T: Size](x: T) -> Int { x.size() + Size.size(x) } fn main() -> Int { 0 } *)
(* `[index]` after an operand reads an array's element (Gate 2): `xs[i]`, `grid[r][c]`,
* `points[i].x`. After a name, a `[` followed by types and then `(` is type arguments instead,
* as it always was: `vec_new[Int]()`; a method call on an element is written `(xs[i]).m()`.
*)
(* @example fn main() -> Int { let xs = [1, 2, 3]; xs[1] } *)
primary = atom , { "." , IDENT , [ "(" , [ arguments ] , ")" ] | "?" | "[" , expression , "]" } ;
(* A `FLOAT` is digits, `.` and digits, and/or an exponent: `1.5`, `2e9` (Gate 2). An
* `INTEGER` may be written `0x`, `0o` or `0b`, with `_` between digits.
*)
(* @example fn main() -> Int { 1 } *)
(* @example fn main() -> Int { Int(2.5e1) + 0xff + 0b1_0 + 0o7 } *)
atom = INTEGER
| FLOAT
| STRING
| "true"
| "false"
| IDENT
| call
| construction
| variant_construction
| if_expression
| match_expression
| closure
| array
| "(" , expression , ")" ;
(* An array (Gate 2): its elements, or one value and a count. *)
(* @example fn main() -> Int { let a = [1, 2, 3]; let b = [0; 4]; a[2] + b[3] } *)
array = "[" , expression , ( { "," , expression } , [ "," ] | ";" , expression ) , "]" ;
(* A closure (N50): a function written where a value is, its parameters and result in
* full, its effects declared or none. Its body may name the bindings around it, each
* copied in when the closure is made; a `let mut` binding may not be (`N0386`). A
* capability may be (N51): the closure holds it, and its type says what it does.
*)
(* @example fn main() -> Int { let k = 2; let f = fn(x: Int) -> Int { x * k }; f(3) } *)
closure = "fn" , "(" , [ parameters ] , ")" , "->" , type , [ effect_set ] , block ;
(* Type arguments are written where inference cannot find them from the arguments —
* `vec_new[Int]()` — or where a reader wants them visible. The expected result type
* never supplies one.
*)
(* @example fn main() -> Int { str_len("abc") } *)
(* @example fn identity[T](v: T) -> T { v } fn main() -> Int { identity[Int](7) } *)
call = [ qualifier ] , IDENT , [ type_arguments ] , "(" , [ arguments ] , ")" ;
(* Named, never positional, so field order is never an implicit API. Parenthesised rather
* than `Point { x: 1 }` because `IDENT "{"` is exactly what an `if` whose condition is a
* bare name looks like, and no amount of parser lookahead makes that a decision rather
* than a guess. See `docs/spec.md`, *Why construction is parenthesised*.
*
* Told apart from `call` by two tokens: `IDENT "(" IDENT ":"`. No argument expression can
* begin with a name followed by a colon, so the two forms cannot be confused.
*)
(* @example struct P { x: Int, y: Int, } fn main() -> Int { P(x: 1, y: 2).x } *)
(* @example struct Box[T] { value: T, } fn main() -> Int { Box[Int](value: 7).value } *)
construction = [ qualifier ] , IDENT , [ type_arguments ] , "(" , initialisers , ")" ;
(* @example struct P { x: Int, } fn main() -> Int { P(x: 1,).x } *)
initialisers = initialiser , { "," , initialiser } , [ "," ] ;
(* @example struct P { x: Int, } fn main() -> Int { P(x: 1 + 1).x } *)
initialiser = IDENT , ":" , expression ;
(* Always qualified by the enum, and always parenthesised — including for a variant with
* no payload, because `E.V` alone is exactly the shape of a field projection. Told apart
* from one by three tokens, `IDENT "." IDENT "("`. Since N78 a method call has that shape
* too: `x.m(…)` with an unlabelled argument is always a call, and `x.m()` is a call when the
* checker finds `x` names a value rather than an enum — an enum keeps every meaning it had.
* See `docs/spec.md`, *Variants are named by their enum* and *Traits and methods*.
*)
(* @example enum E { A, } fn main() -> Int { match E.A() { E.A() => 0, } } *)
(* @example enum E { V(x: Int), } fn main() -> Int { match E.V(x: 1 + 1) { E.V(x: n) => n, } } *)
(* @example enum Maybe[T] { None, Some(value: T), } fn main() -> Int { match Maybe[Int].None() { Maybe.None() => 0, Maybe.Some(value: v) => v, } } *)
variant_construction = [ qualifier ] , IDENT , [ type_arguments ] , "." , IDENT , "(" , [ initialisers ] , ")" ;
(* @example fn main() -> Int { ints_set(ints_new(), 0, 1) } *)
arguments = expression , { "," , expression } ;
(* @example fn main() -> Int { if true { 1 } else { 2 } } *)
(* @example fn main() -> Int { let mut n = 0; if true { n = 1; } n } *)
if_expression = "if" , expression , block , [ "else" , ( block | if_expression ) ] ;
(* The one elimination form for an enum. An expression, so it composes wherever one may
* appear, and its arms reuse the existing block and completion rules rather than
* introducing a second set.
*
* Every variant gets exactly one arm — there is no `_` arm, deliberately, so that adding
* a variant breaks the matches that believed they had handled the whole enum. See
* `docs/spec.md`, *Exhaustiveness*.
*)
(* @example enum E { A, B(x: Int), } fn main() -> Int { match E.A() { E.A() => 1, E.B(x: n) => n, } } *)
(* @example enum E { A(text: Str), } fn main() -> Int { match E.A(text: "hi") { E.A(text: s) => { let n = str_len(s); n }, } } *)
match_expression = "match" , expression , "{" , [ match_arms ] , "}" ;
(* @example enum E { A, B, } fn main() -> Int { match E.A() { E.A() => 1, E.B() => 0, } } *)
match_arms = match_arm , { "," , match_arm } , [ "," ] ;
(* A pattern names the enum and never its type arguments: the value being matched already
* has one concrete type, and `Maybe.Some(value: v)` matches a `Maybe[Int]` and a
* `Maybe[Str]` alike.
*)
(* @example enum E { V(x: Int), } fn main() -> Int { match E.V(x: 1) { E.V(x: n) => n, } } *)
match_arm = [ qualifier ] , IDENT , "." , IDENT , "(" , [ pattern_fields ] , ")" , "=>" , expression ;
(* @example enum E { V(x: Int, y: Int), } fn main() -> Int { match E.V(x: 1, y: 2) { E.V(x: a, y: _) => a, } } *)
pattern_fields = pattern_field , { "," , pattern_field } , [ "," ] ;
(* A payload field is bound to a name, or discarded with `_`. `_` declines to name the
* value; it does not mean the field is absent and does not mean it is not released.
*)
(* @example enum E { V(x: Int), } fn main() -> Int { match E.V(x: 1) { E.V(x: _) => 0, } } *)
pattern_field = IDENT , ":" , IDENT ;