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.
Notation
Section titled “Notation”The grammar is written in Extended Backus–Naur Form:
| Form | Meaning |
|---|---|
= | 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 B | A followed by B (concatenation) |
A | B | A 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.
Lexical grammar
Section titled “Lexical grammar”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 byteschar_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.
Compilation unit
Section titled “Compilation unit”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 placeDeclarationPrefix = Attribute | CompilerDirective | DerivePrefix .Attribute = "[" QualifiedName [ "(" [ ArgList ] ")" ] "]" .AssemblyAttribute = "[" "assembly" ":" QualifiedName [ "(" [ ArgList ] ")" ] "]" . // unit scope only; attaches to the output, not to a declarationCompilerDirective = "@" 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.
Declarations
Section titled “Declarations”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 keywordRegionTarget = { any-token-but-unbalanced-paren } . // reaches the template as text, not as syntaxRegionBody = "{" { any-char | RegionBody } "}" . // brace-balanced raw text; one token
Generics = "<" TypeParam { "," TypeParam } ">" .TypeParam = identifier [ ":" Bound ] . // an optional capability boundBound = "unmanaged" . // the CLR unmanaged constraintBaseList = TypeName { "," TypeName } . // base class, if any, is firstCase = identifier [ "(" ParamList ")" ] .EnumCase = identifier [ "=" int_lit ] .Members
Section titled “Members”FieldList = Field { Sep Field } [ Sep ] . // Sep is "," or newline; trailing Sep allowedField = [ "pub" | "priv" ] [ "required" ] [ "let" | "var" ] identifier ":" Type [ "=" Expr ] | [ "pub" ] [ "*" ] TypeName . // embedded (anonymous) fieldMember = Field | Property | Init | Method | EventDecl | ConstDecl | ReturnsClause | TypeDecl . // nested type declarationProperty = [ "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 typeInit = [ "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 itReturnsClause = "returns" Type . // class-level default returnInterfaceMember = "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 propertyEventDecl = "event" identifier ":" Type . // Type shall be a delegate
ParamList = Param { Sep Param } [ Sep ] . // Sep is "," or newline; trailing Sep allowedSep = "," | newline .Param = ( [ "out" ] [ "readonly" ] identifier ":" Type | identifier ":" "*" Type ) [ "=" Expr ] . // optional default valueReturnType = ( "->" | "returns" ) Type .Statements
Section titled “Statements”Block = "{" { Statement } "}" .Statement = Binding | Assignment | If | While | For | Match | Defer | Return | Break | Continue | Try | Raise | TaskFunc | ExprStmt | Region . // spliced into the enclosing block, not nestedBinding = ( "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 destructureAssignment = 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.
Patterns
Section titled “Patterns”Match = "match" ( Expr | "(" Expr ":" Type ")" ) "{" { Arm } "}" .Arm = Pattern [ "if" Expr ] ( Block | "=>" Expr ) . // optional guard; block or expression bodyPattern = 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.
Expressions
Section titled “Expressions”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 ] . // ternaryCoalesce = 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 updateIndexArg = Expr | RangeSlice . // a[i] , a[a..b] , a[..b] , a[a..] , a[..]RangeSlice = [ SliceEnd ] ".." [ SliceEnd ] . // either endpoint optionalSliceEnd = [ "^" ] Expr . // ^k counts from the endPrimary = 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 allowedFieldInit = identifier ":" Expr .ArgList = Arg { Sep Arg } [ Sep ] . // positional args first, then named, in any orderArg = [ "out" | "&" | "*" ] Expr // positional | identifier ":" Expr . // named — binds by parameter nameMatchExpr = 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.
Concurrency forms
Section titled “Concurrency forms”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.