Skip to content

Expressions

From tightest to loosest binding. All binary operators are left-associative except ?? (right) and the ternary ? :.

LevelOperators
primaryliterals · names · (…) · f(…) · .member · [index] · T { … } · new T { … } · .case(…)
postfixexpr? (try-unwrap) · ?.member (null-conditional) · with { … }
unary! not · unary - · & (address-of) · * (deref / by-ref)
multiplicative* / %
additive+ -
range..
comparison< <= > >=
equality== !=
logical and&& and
logical or|| or
null-coalescing??
ternary? :

(Assignment and compound assignment are statements, not expressions.)

Expr = Coalesce [ "?" Expr ":" Expr ] . // ternary
Coalesce = OrExpr { "??" OrExpr } .
OrExpr = AndExpr { ( "||" | "or" ) AndExpr } .
AndExpr = Equality { ( "&&" | "and" ) Equality } .
Equality = Comparison { ( "==" | "!=" ) Comparison } .
Comparison = Range { ( "<" | "<=" | ">" | ">=" ) Range } .
Range = 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
| "[" Expr "]" // index
| "?" // try-unwrap
| "?." identifier // null-conditional
| "with" "{" FieldInitList "}" . // non-destructive update
Primary = literal | identifier
| "(" Expr { "," Expr } ")" // parenthesised / tuple
| CompositeLit | NewExpr | ListLit
| DotCase | Lambda | MatchExpr
| "ok" "(" Expr ")" | "error" "(" Expr ")"
| "await" Unary | "spawn" Block .
CompositeLit = TypeName [ TypeArgs ] "{" [ FieldInitList ] "}" .
FieldInitList = FieldInit { ( "," | newline ) FieldInit } .
FieldInit = identifier ":" Expr .
NewExpr = "new" TypeName ( "{" [ FieldInitList ] "}" | "(" [ ArgList ] ")" ) .
ListLit = "[" [ Expr { "," Expr } ] "]" .
DotCase = "." identifier [ "(" [ ArgList ] ")" ] .
Lambda = "func" "(" [ ParamList ] ")" [ ReturnType ] ( Block | "=" Expr )
| "(" [ identifier { "," identifier } ] ")" "=>" Expr .
ArgList = Arg { "," Arg } .
Arg = [ "out" | "&" | "*" ] Expr .

Try-unwrap expr?. If expr is a Result, ? yields the ok value or returns the error from the enclosing function. It is an expression operator — valid anywhere an expression appears (a let, a return, a call argument, a bare statement). The enclosing function’s error type shall be compatible. The spelling expr?.member parses as the null-conditional operator; to unwrap-then-access, parenthesise ((expr?).member) or bind with a let. See Errors.

Ternary vs. try-unwrap. ? is disambiguated by lookahead: if the next token can begin an expression, ? is the ternary operator; otherwise it is postfix try-unwrap.

Null-coalescing a ?? b yields a if non-null, else b. Null-conditional a?.m yields a.m if a is non-null, else null/default; it chains (a?.b?.c).

Composite literal T { f: v } constructs a value of type T; field initializers are comma- or newline-separated. new T { … } / new T(…) heap-allocates a value data and yields *T — the only allocation expression; new on a non-data type is ES2144, on a ref data is ES2003.

Address-of &x yields a managed pointer to a variable, or a function pointer for a function name; *x / &x at a call site pass by reference. See Pointers & by-ref.

.case / ok / error construct a choice variant or a Result side when the target type is known from context.

Collection literal [a, b] constructs a List<T> with T inferred from the elements; [] is List<object>. A parenthesised comma list (a, b) constructs a ValueTuple.

Lambdas. func(…) -> T { … } and the arrow form (x) => expr are function literals; arrow-parameter types are inferred when a delegate type is expected. Both close over the enclosing scope; captures are mutable, and a closure over a let may read but not write it.

match as an expression appears in any expression position; every arm produces a value of one common type. Its grammar is given with the statement form.