Statements
Grammar
Section titled “Grammar”Block = "{" { Statement } "}" .Statement = Binding | Assignment | If | While | For | Match | Defer | Return | Break | Continue | Try | Raise | ExprStmt .Binding = ( "let" | "var" ) BindTarget [ ":" Type ] "=" Expr | "let" BindTarget "=" Expr "else" Block // let-else | "async" "let" identifier [ ":" Type ] "=" Expr | 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" [ "(" Type [ identifier ] ")" ] Block .Raise = "raise" identifier "(" [ ArgList ] ")" .ExprStmt = Expr .Conditions take no parentheses; braces are mandatory on every block.
Bindings & mutation
Section titled “Bindings & mutation”let binds an immutable name; var a mutable one; const a compile-time literal (folding rules and
ES1011 in Declarations). Immutability
of let is enforced by the compiler, not the CLR. A binding may destructure a tuple:
let (a, b) = pair. async let starts its initializer concurrently and is awaited at first reference; its
initializer shall be a call or awaitable (ES3005), and a sync user-function
initializer is auto-wrapped in Task.Run with ES3004. Assignment targets are
locals, parameters, member chains, and index expressions.
let-else
Section titled “let-else”let user = db.find(id) else { return error(.notFound) }If the initializer is nil, the else block runs and shall leave the scope (return, break, …); after
the statement the bound name is non-nil.
Control flow
Section titled “Control flow”for … in iterates collections, ranges (0..n), and channels. A for target may destructure a tuple
(for (k, v) in entries). defer runs its block on scope exit, LIFO; it lowers to try/finally and is
the cleanup mechanism (there is no finally keyword).
Match = "match" ( Expr | "(" Expr ":" Type ")" ) "{" { Arm } "}" .Arm = Pattern Block | Pattern Expr . // statement body or expression bodyPattern = DotCase [ "(" [ Binding { "," Binding } ] ")" ] // choice / enum case | literal // int / string / bool literal pattern | "default" .match dispatches over a choice, ref choice, enum, or literal value. The type annotation
(expr: T) is required only when the scrutinee’s static type is ambiguous (e.g. a value flowing through
object, or an enum reached via dot-case); otherwise the bare form suffices.
- Destructuring. A case arm binds payloads positionally (
.message(lvl, txt)); a value-choicearm may instead bind a single case-view name whose members are the payloads (.pair(p)→p.a,p.b). For a single-payload case the view is transparent — the binding doubles as the payload value. Aref choicearm matches with anisinsttype pattern. - Literal patterns (
int,string,bool) match by value; exhaustiveness does not apply to them, sodefaulthandles the open set. - Exhaustiveness. A
matchover achoice/ref choice/enumthat omits a variant is warned;defaultsuppresses it. - As an expression.
matchin expression position requires every arm to yield one common type (see Expressions).
return and definite return
Section titled “return and definite return”A non-void function shall return on every path; a fall-through is ES2140. The
analysis is exhaustive-match-aware: an if/else whose arms all return, or an exhaustive match whose
arms all terminate, counts as returning — no redundant trailing return is required. return, throw,
and an infinite loop with no break are terminators.
try / catch / throw
Section titled “try / catch / throw”try/catch is the BCL boundary mechanism; in-language flow uses Result. Three catch
shapes are accepted:
catch (FormatException e) { ... } // typed + boundcatch (Exception e) { ... } // base type + boundcatch { ... } // bare — any exception, no bindingThere is no finally (use defer inside the try). throw with no operand rethrows, and is valid only
inside a catch.
raise Name(args) fires the field-style event Name declared on the enclosing ref data; it is null-safe
(a capture-then-invoke lowering) and a no-op with no subscribers. raise on an undeclared event is
ES2142. See Delegates & events.