Extended BNF
On this page
The syntax is specified below using XML's Extended BNF Notation. The diagrams do not attempt to convey operator precedence within a rule (see Operator and Precedence for that information).
Statements#
program ::= dcl_stmt*
dcl_stmt ::= mod_stmt | modtrait_stmt | var_stmt | fn_stmt | use_stmt | import_stmt
mod_stmt ::= 'pub'? 'mod' ident_token ('extends' ident_token)? ('is' ident_token)? defaultfold? ';'
/* the first statement of a module's designated file, or of a one-file module's file; 'extends' names one module already in reach, a sister or one this module imports;
'is' names the one module trait this module conforms to, by one name it can reach */
modtrait_stmt ::= 'pub'? 'mod' 'trait' ident_token ('{' modtraitmember* '}' | ';')
modtraitmember ::= 'pub'? (fn_type block? | perm_ident ident_token type? ('=' literal)? ';')
/* a function or a global: with a body or an initial value a default, without one a requirement.
Not generic, and no overload name. Anywhere among a module's statements */
defaultfold ::= 'use' ('*' ('but' ident_token (',' ident_token)*)? | namelist | '{' namelist ','? '}')
namelist ::= ident_token (',' ident_token)*
/* what a bare import of this module folds by default; each name a public name of the module.
Folds into importers, not into this module; an importer's own clause replaces it. No 'as', no 'pub' */
import_stmt ::= 'pub'? 'import' (ident_token | text_token) ('as' ident_token)? foldclause? ';'
/* 'as' binds the module under that name instead of its own, which is then not bound here.
A module imports another once: a second import is refused, identical or not, whatever name each binds it under */
use_stmt ::= 'pub'? 'use' genqualname (folditems | '{' folditems ','? '}')? ';'
/* folds in a namespace the module reaches without an import: an enum's variants, the enum named alone, never an instance of a generic one;
or a submodule's public names, the submodule named by its name or a path through submodules. Never an imported module */
var_stmt ::= perm_ident ident_token type? (',' ident_token type?)* ('=' append_exp (',' append_exp)*)? foldclause? ';'
/* a fold clause only on a module's global, an 'extern' one included: a local, a parameter and a type's static have no namespace to fold into */
fn_stmt ::= fn_type block
block ::= '{' stmt* '}'
stmt ::= if_stmt | letelse_stmt | match_stmt | block | while_stmt | break_stmt | cont_stmt | each_stmt | par_each_stmt | return_stmt | yield_stmt | exp_stmt | ';'
if_stmt ::= 'if' logic_exp block ( 'elif' logic_exp block )* ( 'else' block )?
letelse_stmt ::= ('imm' | 'mut') ident_token type '=' exp 'else' block ';'
/* binds a variant of the value (the type names only the variant) or leaves: the block must end in 'return', 'break', 'continue' or a call that does not return */
match_stmt ::= 'match' exp ('using' exp)? ('into' var_list)? ('with' exp ('into' var_list)? block)+ ('else' block)?
while_stmt ::= (lifetime_token ':')? 'while' logic_exp? block ('else' block)?
/* with no condition the loop never runs out, and takes no 'else'; with a condition, the loop is an expression only when it has an 'else',
whose block (outside the loop) gives the value when the condition fails */
each_stmt ::= (lifetime_token ':')? 'each' ident_token (',' ident_token)* 'in' exp ('by' exp)? ('if' logic_exp)? block ('else' block)?
/* 'by' follows a numeric range, an exp that is a range_exp ('a .. b' through b, 'a ..< b' before b); a range gives one variable, and the other sources
(an array, a slice, a list) one as well; a cursor, or something that gives one, one or several that unpack a tuple item;
'if' skips the items for which its condition is false; the loop is an expression only when it has an 'else' */
yield_stmt ::= 'yield' 'each'? exp ';'
/* only in the body of a function declared 'yields' (a generator), and a statement of its own, never part of an expression;
'yield exp' hands the caller a value, 'yield each src' hands on every value of another generator or of anything with a 'next' */
par_each_stmt ::= 'parallel' 'each' ident_token 'in' exp ('if' logic_exp)? block
/* 'parallel' is a word only directly before 'each', and a name anywhere else; the exp is a number range counting up by one,
an exp that is a range_exp (no 'by'), or an array, a slice or a list; one variable; no 'else', and a statement only */
break_stmt ::= 'break' lifetime_token? this_exp? ('if' logic_exp)? ';'
cont_stmt ::= 'continue' lifetime_token? ('if' logic_exp)? ';'
return_stmt ::= 'return' this_exp? ('if' logic_exp)? ';'
/* a trailing 'if' makes the statement conditional; only these three take one. An 'if' expression as the value
is told from it by the block that follows its condition */
exp_stmt ::= this_exp ';'
this_exp ::= exp (('using' exp)? block)?
var_list ::= perm_ident? ident_token type (',' perm_ident? ident_token type)*
Type declarations#
type ::= genqualname | nbrtype | arraytype | optiontype | ptrtype | reftype | ownreftype | fntype | structtype
nbrtype ::= 'bool' | 'i8' | 'i16' | 'i32' | 'i64' | 'isize' | 'u8' | 'u16' | 'u32' | 'u64' | 'usize' | 'f32' | 'f64'
arraytype ::= 'Array' '[' type (',' integer_token)+ ']'
/* the element type, then one size for each dimension, the first the outermost:
'Array[T, n, m]' is 'Array[Array[T, m], n]'. 'Array' is a name every module
reaches unless it declares the name itself */
optiontype ::= '?' type
ptrtype ::= '*' type
reftype ::= ('&' | '&<') lifetime_token? perm_type? type ('+' lifetime_token)?
/* a borrowed reference names no region: '&mut Point', a virtual one '&=' | '==') lifetime_token
/* ''a >= 'b': ''a' lasts at least as long as ''b'; '==' both ways. On any function, of lifetimes its
signature names, and on a struct, of lifetimes it declares */
baseclause ::= isaclause | extendsclause /* either order, each at most once; 'extends' not on a trait */
isaclause ::= 'is' genqualname (',' genqualname)*
/* every name is an open trait: an enum anywhere in the list is refused.
On a struct or a trait the first is its base; an enum and a variant have none of their own */
extendsclause ::= 'extends' genqualname /* the concrete base this type enriches with methods */
structstmt ::= var_stmt | fn_stmt | usestmt
/* 'mixin', retired, is refused: a type names what it complies with in its isaclause */
usestmt ::= 'use' genqualname folditems? ';' /* folds in a sibling: a type that declared the same 'extends' base */
foldclause ::= 'pub'? 'use' ('*' ('but' ident_token (',' ident_token)*)? | folditems | '{' folditems ','? '}')
/* written after the type of a struct's field or of a module's global, or after an import's module; 'pub' not on a field's.
'pub' comes first, as on every declaration: 'use pub' is refused */
folditems ::= ident_token ('as' ident_token)? (',' ident_token ('as' ident_token)?)*
| 'but' ident_token (',' ident_token)* /* omitted admits every member of the sibling named */
traittype ::= 'trait' ident_token? isaclause? ('{' structstmt* '}')? /* a synonym for 'struct trait' */
actortype ::= 'actor' ident_token '{' (structstmt | behaviourstmt)* '}'
/* its fields are its private state, never 'pub'; each behaviour is a message, whose parameters
and returned value are Sendable; each 'fn' method is synchronous and never 'pub' ('init' and
'final' as a struct's); no static, macro, 'extern' or 'use'. 'new Name(args)' runs its 'init'
and answers its handle. Needs 'import actors' */
behaviourstmt ::= 'pub'? 'async' 'do' ident_token '(' fnparm (',' fnparm)* ')' type? block
/* an actor's behaviour: its first parameter 'self'; 'async' and 'do' mean nothing apart */
unbuiltkind ::= 'actor' 'trait' ident_token? ('{' structstmt* '}')? /* accepted, not implemented */
enumtype ::= 'enum' '@unsized'? ident_token genparms? nbrtype? (('extends' genqualname) | isaclause)? whereclause? '{' enumstmt* '}'
/* 'extends' names another enum, a generic one with its type arguments, whose variants
this one holds copies of; such an enum declares only variants in its body, writes
no integer type of its own, and names no isaclause: its base's comes with the copies.
An isaclause gives every variant the traits' requirements and defaults */
enumstmt ::= structstmt | variantlist | 'struct' ident_token ('=' integer_token)? isaclause? structbody?
/* a variant's isaclause names open traits beyond its enum, which is its base unwritten */
variantlist ::= variant (',' variant)* ';'
variant ::= ident_token ('=' integer_token)?
tagtype ::= 'tag'
General use expressions (with possible assignments)#
exp ::= assgn_exp
assgn_exp ::= tuple_exp ((('=' | '+='|'-='|'*='|'/='|'%='|'&='|'|='|'^='|'<<='|'>>=') exp) | ('<-' entries))
tuple_exp ::= simple_exp (',' simple_exp)*
entries ::= entry (',' entry)*
/* the list after '<-', running to the statement's end, across lines; each entry is appended in turn */
entry ::= ('fill' contents_exp) | (contents_exp (('of' | ':') contents_exp)?) | ('(' entries ')') | each_entry
each_entry ::= 'each' (simple_exp | ident (',' ident)* 'in' simple_exp ('by' simple_exp)? ('if' simple_exp)? 'yield' simple_exp (':' simple_exp)?)
/* 'each src' appends every item of src; with variables, the loop's own header, and the 'yield' says
what each pass appends ('yield k: v' a pair) */
/* 'of' and 'fill' are words only here, names elsewhere; 'fill' is the word when a space and a value
follow it. 'n of x': n values, x evaluated for each; 'fill x': x until the collection's len()
reaches its capacity(); 'k: v': a key and its value, given to a '<-' that takes both */
contents_exp ::= simple_exp ('<-' entry)?
/* inside a comma list, a construction's contents take one entry: several are parenthesized */
Expressions without any assignments#
simple_exp ::= if_exp | match_exp | while_exp | each_exp | loop_exp | or_exp
or_exp ::= and_exp ('or' and_exp)?
and_exp ::= not_exp ('and' not_exp)?
not_exp ::= '!'* compare_exp
compare_exp ::= range_exp (('=='|'!='|'==='|'!=='|'~~'|'<=>'|'<'|'<='|'>'|'>=') range_exp)?
range_exp ::= bit_exp (('..' | '..<') bit_exp)?
bit_exp ::= shift_exp (('|'|'&'|'^') shift_exp)*
shift_exp ::= arith_exp (('<<' | '>>') arith_exp)?
arith_exp ::= cast_exp (('+'|'-'|'*'|'/'|'%') cast_exp)*
as_exp ::= prefix_exp ('as' type)*
/* 'into' is retired: a value converts with 'f32.from(x)', and a reference narrows to a variant by a match or a bound 'if' */
prefix_exp ::= ('.'|'<-'|'-'|'~'|'*'|'++'|'--'|'await')* term | addr | plus_alloc
/* 'await' only in an actor's behaviour; built on a behaviour of an actor that returns a value */
addr ::= ('&'|'&<'|'&[]') perm_type? (fn_stmt | (term suffix*))
/* '&[]' only borrows: there is no owning array reference, and '+[]' is refused */
plus_alloc ::= '?'? '+' ident_token ('-' perm_type)? prefix_exp
/* the retired allocation spelling, refused: every allocation is a new_exp, 'new Rc[i32](5)'.
It stays only as a match pattern's owning reference type, '+Rc-mut Circle', whose spelling
is an open design question */
suffix_exp ::= term suffix*
suffix ::= '++'|'--'| ('.' property) | ('(' (contents_exp (',' contents_exp)*)? ')') | ('[' (contents_exp (',' contents_exp)*)? ']')
/* square brackets hold type arguments or an index, and never a value's contents: an enum variant is
constructed with parentheses, 'Some(x)', and an empty one by its name alone, 'None'. A struct's value
is built with 'new' */
property ::= ident_token | symbol_token | integer_token
term ::= literal | ident_token | pseudo | '(' exp ')' | new_exp
new_exp ::= ('new' | 'trynew') ident_token ('.' ident_token)* ('[' type (',' type)* ']')* ('(' (contents_exp (',' contents_exp)*)? ')')?
/* a construction, its type then its initializer's arguments: 'new Point(1, 2)', and its contents
after '<-' (entries): 'new List[i32](100) <- fill 0'; an array takes contents only. A managed reference
type, or an alias of one, allocates: 'new Rc[mut, Node](1)', or, given one finished value of the
value type, moves it in: 'new Rc[i32](5)'. 'trynew' takes only a managed reference
type, and gives an Option of it, None when memory runs out */
genqualname ::= ident_token ('.' ident_token)* ('[' typearg (',' typearg)* ']')?
typearg ::= type | lifetime_token
/* a lifetime only in a type of a function's signature or of a struct's field: a struct's lifetimes,
named positionally ('Cursor['a]', 'Parser['s, 'r]', 'Cursor['a, T]') or not at all; never in a
construction's type, a variable's or a function's brackets */
pseudo ::= 'self' | 'this' | 'selfactor'
/* 'selfactor' only in an actor's method: the actor's own handle */
Literals and other tokens#
literal ::= float_token | integer_token | text_token | symbol_token | 'true' | 'false' | 'null' | array_lit
/* 'null' is the null raw pointer, of the pointer type it is wanted as */
array_lit ::= '[' contents_exp (',' contents_exp)* ']'
/* '[n; x]', the fill literal, is retired: 'new Array[T, n] <- fill x' */
float_token ::= (('0'-'9') ('0'-'9')*) '.' ('0'-'9')* (('e'|'E') ('-'|'+')? ('0'-'9')+)?
integer_token ::= (('0'-'9') ('0'-'9')*) | '0x' (('0'-'9')|('a'-'f')|('A'-'F'))+
lifetime_token ::= '\'' ident_token
text_token ::= '"' any_char '"'
symbol_token ::= "'" any_char "'"
ident_token ::= '`' any_char* '`' | ('_' | alpha_char) (alpha_char | ('0'-'9') | '$' | '_')*
Railroad diagrams courtesy of the Railroad Diagram Generator.