Skip to content

Grammar

This page collects the whole grammar of E# as a single reference, in the same EBNF used throughout the specification. Each section links to the prose page that gives the static semantics; this page is syntax only — what parses, not what it means.

The grammar is written in Extended Backus–Naur Form:

FormMeaning
=production: the name on the left is defined by the expression on the right
"x"the literal terminal x (a keyword, punctuation, or operator)
A BA followed by B (concatenation)
A | BA or B (alternation)
[ A ]zero or one A (optional)
{ A }zero or more A (repetition)
( A )grouping
εthe empty production (nothing)
an elided inclusive range ("A" … "Z")

Terminals named in lower-case italics (identifier, int_lit, string_lit, newline) are produced by the lexical grammar below. The grammar is LL-parseable with finite lookahead; the two places that need it — postfix ? vs. the ternary, and the contextual keywords — are noted where they arise and in Lexical structure.

A source file is UTF-8 text. Tokens are formed greedily (longest match). Whitespace separates tokens and is otherwise insignificant except that, inside any ( … ) parameter or argument list and any { … } field-initializer or field list, a newline separates exactly like a comma (a trailing separator is allowed), and a newline can precede a leading-dot method-chain continuation; see Functions → method chaining.

comment = "//" { any-char-but-newline } .
identifier = ( letter | "_" ) { letter | digit | "_" } .
letter = "A" … "Z" | "a" … "z" .
digit = "0" … "9" .
int_lit = dec_lit | hex_lit | bin_lit .
dec_lit = digit { digit | "_" } .
hex_lit = ( "0x" | "0X" ) hex_digit { hex_digit | "_" } .
bin_lit = ( "0b" | "0B" ) ( "0" | "1" ) { "0" | "1" | "_" } .
hex_digit = digit | "a" … "f" | "A" … "F" .
float_lit = digit { digit | "_" } "." digit { digit | "_" } [ exponent ]
| digit { digit | "_" } exponent .
exponent = ( "e" | "E" ) [ "+" | "-" ] digit { digit } .
string_lit = `"` { string_char | interpolation } `"` .
byte_string_lit = "b" `"` { string_char | escape } `"` . // b"MFL1" → byte[] of the UTF-8 bytes
char_lit = "'" ( char_char | escape ) "'" .
bool_lit = "true" | "false" .
nil_lit = "nil" .
interpolation = "{" Expr "}" . // a `{` opens a hole only before letter/_/(/!

Casing carries no meaning in the grammar. Type names are PascalCase and free functions camelCase by convention only (Lexical → identifiers). The reserved and contextual keyword tables are in Lexical → keywords.

SourceFile = NamespaceDecl { Using } { AssemblyAttribute | Declaration } .
NamespaceDecl = "namespace" QualifiedName .
QualifiedName = identifier { "." identifier } .
Using = "using" string_lit
| "using" "static" string_lit
| "using" identifier "=" string_lit .
Declaration = { DeclarationPrefix } [ "pub" ] ( StructDecl | ClassDecl | UnionDecl | RefUnionDecl
| EnumDecl | InterfaceDecl | DelegateFuncDecl
| StaticFacetDecl | FuncDecl | StateDecl | NamespaceInitDecl
| ConstDecl )
| ExtBlockDecl // namespace-hosted extensions; takes no "pub"
| Region . // a template's declarations, spliced in place
DeclarationPrefix = Attribute | CompilerDirective | DerivePrefix .
Attribute = "[" QualifiedName [ "(" [ ArgList ] ")" ] "]" .
AssemblyAttribute = "[" "assembly" ":" QualifiedName [ "(" [ ArgList ] ")" ] "]" .
// unit scope only; attaches to the output, not to a declaration
CompilerDirective = "@" identifier [ "(" NamedBoolArg { "," NamedBoolArg } ")" ] .
NamedBoolArg = identifier ":" bool_lit .
StateDecl = ( "let" | "var" ) identifier
( [ ":" Type ] "=" Expr
| ":" Type ( "=>" Expr | AccessorBlock ) ) .
NamespaceInitDecl = "init" Block .

See Declarations and Names & resolution.

Type = QualifiedName [ TypeArgs ] [ "?" ] // named type, generic args, optional nullable
| "*" Type // pointer
| "readonly" "*" Type // read-only by-ref (parameter position)
| "(" TupleElem { "," TupleElem } ")" // tuple, elements optionally labeled
| FuncPtrType .
TupleElem = [ identifier ":" ] Type . // (x: int, y: int) , (count: int, int)
FuncPtrType = "&" "(" [ TypeList "->" ] Type ")" . // &(int, int -> int) , &(-> bool)
TypeArgs = "<" TypeList ">" .
TypeList = Type { "," Type } .

See Type system and Pointers & by-ref.

StructDecl = [ "readonly" ] "struct" TypeName [ Generics ]
( "{" [ FieldList ] "}" | "(" [ ParamList ] ")" ) .
ClassDecl = [ "open" | "abstract" ] "class" TypeName [ Generics ]
[ "(" [ ParamList ] ")" ] // primary-constructor (capture) header
[ ":" BaseList ] "{" { Member } "}" .
UnionDecl = "union" TypeName [ Generics ] "{" { Case } "}" .
RefUnionDecl = "ref" "union" TypeName [ Generics ] "{" { Case } "}" .
EnumDecl = "enum" TypeName [ ":" IntegralType ] "{" { EnumCase } "}" .
IntegralType = "byte" | "sbyte" | "short" | "ushort" | "int" | "uint" | "long" | "ulong" .
InterfaceDecl = "interface" TypeName [ Generics ] "{" { InterfaceMember } "}" .
DelegateFuncDecl = "delegate" "func" TypeName "(" [ ParamList ] ")" [ ReturnType ] .
StaticFacetDecl = "static" TypeName [ Generics ] "{" { StaticMember } "}" .
FuncDecl = [ "readonly" ] "func" [ Receiver ] ( identifier [ Generics ] | OperatorName ) "(" [ ParamList ] ")" [ ReturnType ]
( Block | "=" Expr ) .
Receiver = "(" [ "ext" ] identifier ":" [ "static" ] Type ")" . // func (c: *Circle) scale(...) · func (ext s: string) shout()
OperatorName = "+" | "-" | "!" | "~" | "*" | "/" | "%" | "&" | "|" | "^"
| "<<" | ">>" | ">>>" | "==" | "!=" | "<" | ">" | "<=" | ">=" .
ConstDecl = "const" identifier [ ":" Type ] "=" Expr .
DerivePrefix = "@" "derive" "(" DeriveInvocation { "," DeriveInvocation } ")" .
DeriveInvocation = identifier [ "(" [ DeriveArg { "," DeriveArg } ] ")" ] .
DeriveArg = identifier ":" ConstExpr . // the outer list selects, the inner configures
Region = "@" sigil [ "(" RegionTarget ")" ] RegionBody .
sigil = identifier . // claimed by a loaded template, not a keyword
RegionTarget = { any-token-but-unbalanced-paren } . // reaches the template as text, not as syntax
RegionBody = "{" { any-char | RegionBody } "}" . // brace-balanced raw text; one token
Generics = "<" TypeParam { "," TypeParam } ">" .
TypeParam = identifier [ ":" Bound ] . // an optional capability bound
Bound = "unmanaged" . // the CLR unmanaged constraint
BaseList = TypeName { "," TypeName } . // base class, if any, is first
Case = identifier [ "(" ParamList ")" ] .
EnumCase = identifier [ "=" int_lit ] .
FieldList = Field { Sep Field } [ Sep ] . // Sep is "," or newline; trailing Sep allowed
Field = [ "pub" | "priv" ] [ "required" ] [ "let" | "var" ] identifier ":" Type [ "=" Expr ]
| [ "pub" ] [ "*" ] TypeName . // embedded (anonymous) field
Member = Field | Property | Init | Method | EventDecl | ConstDecl | ReturnsClause
| TypeDecl . // nested type declaration
Property = [ "pub" | "priv" ] [ "required" ] ( "let" | "var" ) identifier ":" Type
( "=>" Expr | AccessorBlock ) .
AccessorBlock = "{" { Accessor } "}" .
Accessor = [ "pub" | "priv" ] ( "get" | "set" | "init" ) // bare accessor, optional per-accessor visibility
| [ "pub" | "priv" ] "get" "=>" Expr // custom getter
| [ "pub" | "priv" ] "set" "(" identifier ")" "=>" Expr // custom setter
| "loca" "=>" "&self." identifier
| "mut" ( "=>" "&self." identifier | Block ) .
TypeDecl = StructDecl | ClassDecl | UnionDecl | RefUnionDecl
| EnumDecl | InterfaceDecl | DelegateFuncDecl . // emits as a CLR nested type
Init = [ "priv" | "protected" ] "init" "(" [ ParamList ] ")"
[ ":" ( "base" | "this" ) "(" [ ArgList ] ")" ] Block .
Method = ( ":" [ Vis ] | [ Vis ] [ "virtual" | "abstract" ] ) "func" identifier
"(" [ ParamList ] ")" [ ReturnType ] ( Block | "=" Expr | ε ) .
Vis = "pub" | "priv" | "protected" . // the ':' override marker leads it
ReturnsClause = "returns" Type . // class-level default return
InterfaceMember = "func" identifier "(" [ ParamList ] ")" [ ReturnType ]
| EventDecl
| [ "let" | "var" ] identifier ":" Type "{" InterfaceAccessors "}" .
InterfaceAccessors = ( "get" | "set" | "init" | "loca" )
{ "get" | "set" | "init" | "loca" } .
StaticMember = ConstDecl | "let" identifier [ ":" Type ] "=" Expr
| "var" identifier [ ":" Type ] "=" Expr | FuncDecl | ReturnsClause
| TypeDecl // nested type declaration
| ExtBlockDecl . // a FuncDecl here carries a Receiver only when marked "ext"
ExtBlockDecl = [ "readonly" ] "ext" "(" identifier ":" Type ")" "{" { ExtBlockMember } "}" .
ExtBlockMember = FuncDecl // no receiver — the block header names it
| [ Vis ] "let" identifier ":" Type "=>" Expr . // computed get-only extension property
EventDecl = "event" identifier ":" Type . // Type shall be a delegate
ParamList = Param { Sep Param } [ Sep ] . // Sep is "," or newline; trailing Sep allowed
Sep = "," | newline .
Param = ( [ "out" ] [ "readonly" ] identifier ":" Type
| identifier ":" "*" Type ) [ "=" Expr ] . // optional default value
ReturnType = ( "->" | "returns" ) Type .
Block = "{" { Statement } "}" .
Statement = Binding | Assignment | If | While | For | Match
| Defer | Return | Break | Continue | Try | Raise | TaskFunc | ExprStmt
| Region . // spliced into the enclosing block, not nested
Binding = ( "let" | "var" ) BindTarget [ ":" Type ] "=" Expr
| identifier ":" Type "=" Expr // typed mutable local
| "let" BindTarget "=" Expr "else" Block // let-else
| "async" "let" identifier [ ":" Type ] "=" Expr // concurrent binding
| ConstDecl .
BindTarget = identifier | "(" identifier { "," identifier } ")" . // tuple destructure
Assignment = Lvalue ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^="
| "<<=" | ">>=" | ">>>=" ) Expr .
Lvalue = identifier { "." identifier | "[" Expr "]" } .
If = "if" Expr Block { "else" "if" Expr Block } [ "else" Block ] .
While = "while" Expr Block .
For = "for" BindTarget "in" Expr Block .
Defer = "defer" Block .
Return = "return" [ Expr ] .
Try = "try" Block CatchClause { CatchClause } .
CatchClause = "catch" [ "(" identifier [ ":" Type ] ")" ] Block .
Raise = "raise" identifier "(" [ ArgList ] ")" .
TaskFunc = "task" "func" identifier "(" [ ParamList ] ")" [ ReturnType ] Block .
ExprStmt = Expr .

break and continue are bare keywords. See Statements and Concurrency.

Match = "match" ( Expr | "(" Expr ":" Type ")" ) "{" { Arm } "}" .
Arm = Pattern [ "if" Expr ] ( Block | "=>" Expr ) . // optional guard; block or expression body
Pattern = DotCase [ "(" [ Binding { "," Binding } ] ")" ] // union / enum case + payload bindings
| "(" identifier ":" Type ")" // type pattern — `is T` + bind the narrowed value
| literal // int / string / bool literal pattern
| "nil" // the absent-value arm
| "default" .

match is both a statement and an expression. Exhaustiveness and payload-view projection are in Statements → match.

Precedence runs tightest (top) to loosest (bottom). All binary operators are left-associative except ?? (right) and the ternary ? :. The full precedence table is in Expressions.

Expr = Coalesce [ "?" Expr ":" Expr ] . // ternary
Coalesce = OrExpr { "??" OrExpr } .
OrExpr = AndExpr { ( "||" | "or" ) AndExpr } .
AndExpr = BitwiseOr { ( "&&" | "and" ) BitwiseOr } .
BitwiseOr = BitwiseXor { "|" BitwiseXor } .
BitwiseXor = BitwiseAnd { "^" BitwiseAnd } .
BitwiseAnd = Equality { "&" Equality } .
Equality = Comparison { ( "==" | "!=" ) Comparison } .
Comparison = TypeOp { ( "<" | "<=" | ">" | ">=" ) TypeOp } .
TypeOp = Range { ( "is" [ "not" ] | "as" [ "!" ] ) Type } . // type test / cast (narrowing)
Range = Shift [ ".." Shift ] . // expression range (both endpoints)
Shift = Additive { ( "<<" | ">>" | ">>>" ) Additive } .
Additive = Multiplicative { ( "+" | "-" ) Multiplicative } .
Multiplicative = Unary { ( "*" | "/" | "%" ) Unary } .
Unary = ( "!" | "not" | "+" | "-" | "~" | "&" | "*" ) Unary | Postfix .
Postfix = Primary { Selector } .
Selector = "." identifier // member access
| "(" [ ArgList ] ")" // call
| "<" TypeList ">" "(" [ ArgList ] ")" // generic call
| "[" IndexArg "]" // index or range-slice
| "?" // try-unwrap (postfix)
| "?." identifier // null-conditional
| "with" "{" FieldInitList "}" . // non-destructive update
IndexArg = Expr | RangeSlice . // a[i] , a[a..b] , a[..b] , a[a..] , a[..]
RangeSlice = [ SliceEnd ] ".." [ SliceEnd ] . // either endpoint optional
SliceEnd = [ "^" ] Expr . // ^k counts from the end
Primary = literal | identifier
| "(" TupleArg { "," TupleArg } ")" // parenthesised / tuple
| CompositeLit | NewExpr | ListLit | StackAlloc
| DotCase | Lambda | MatchExpr
| "ok" "(" Expr ")" | "error" "(" Expr ")"
| "typeof" "(" Type ")" // the System.Type for a named type
| "await" Unary | "spawn" Block
| Region . // exactly one expression; see /spec/comptime/
TupleArg = [ identifier ":" ] Expr . // (q: a / b, r: a % b) , (count: 2, "hi")
StackAlloc = "stackalloc" Type "[" "]" "(" Expr ")" . // stackalloc byte[](8) → Span<byte>
CompositeLit = TypeName [ TypeArgs ] "{" [ FieldInitList ] "}" .
NewExpr = "new" TypeName ( "{" [ FieldInitList ] "}" | "(" [ ArgList ] ")" ) .
ListLit = "[" [ Expr { "," Expr } ] "]" .
DotCase = "." identifier [ "(" [ ArgList ] ")" ] .
Lambda = "func" "(" [ ParamList ] ")" [ ReturnType ] ( Block | "=" Expr )
| "(" [ identifier { "," identifier } ] ")" "=>" Expr .
FieldInitList = FieldInit { Sep FieldInit } [ Sep ] . // Sep is "," or newline; trailing Sep allowed
FieldInit = identifier ":" Expr .
ArgList = Arg { Sep Arg } [ Sep ] . // positional args first, then named, in any order
Arg = [ "out" | "&" | "*" ] Expr // positional
| identifier ":" Expr . // named — binds by parameter name
MatchExpr = Match .

The postfix ? (try-unwrap) and the ternary ? share a token; ? is the ternary when the following token can begin an expression, otherwise postfix try-unwrap. expr?.member parses as the null-conditional selector — to unwrap then access, write (expr?).member.

The concurrency surface reuses the productions above (await and spawn are Primary alternatives; async let is a Binding; task func is a Statement/Declaration). select is its own form:

Select = "select" "{" { SelectArm } "}" .
SelectArm = ".recv" "(" identifier "," Expr ")" Block
| ".send" "(" Expr "," Expr ")" Block
| ".timeout" "(" Expr ")" Block
| "default" Block .

See Concurrency.