Lexical structure
Comments
Section titled “Comments”Line comments only — // to end of line. There is no block-comment form.
// this is a commentIdentifiers
Section titled “Identifiers”identifier = ( letter | "_" ) { letter | digit | "_" } .letter = "A" … "Z" | "a" … "z" .digit = "0" … "9" .A letter or underscore, followed by letters, digits, or underscores. Case-sensitive.
Initial case is significant by kind — it carries the value/type distinction so the grammar stays
unambiguous (a bare uppercase name is always a type, so Foo { ... } and NS.Foo { ... } always read
as construction, never as a value’s member-then-block):
- Types are PascalCase.
data,ref data,choice,ref choice,enum,interface,static func, anddelegate funcnames must start uppercase. A lower-case type name is a hard error (ES2160) — it would collide with value/local syntax and could not be constructed withName { ... }. - Free functions are camelCase. A receiverless free function must start lowercase
(ES2161). Two exceptions may be PascalCase to match .NET convention: a
method (first parameter is a
datareceiver, value or*T) and astatic funcmember — neither is ever a bare name, so neither can be mistaken for a type.
Keywords
Section titled “Keywords”Always reserved — never usable as identifiers:
| Group | Keywords |
|---|---|
| Control flow | if else return while for in match default defer select break continue |
| Declarations | namespace data func const let var enum choice interface using derive ref pub readonly with |
| Concurrency | spawn chan await |
| Errors / boundary | try catch throw out params |
| Operator words | and or not |
| Constants | true false nil |
Contextual keywords — matched by text in a specific position only; valid identifiers everywhere
else (so let static = 1 is legal):
| Keyword | Reserved position |
|---|---|
static abstract open virtual task base returns | declaration position |
delegate | before func at member scope — mints a nominal delegate type |
event | before name : in a ref data / interface body |
raise | before EventName( in statement position |
new | expression position, immediately before an upper-case type name followed by {, (, or < |
init | member position in a ref data body — a constructor |
yield | statement start inside an IAsyncEnumerable<T> function |
new is the sharpest case: it is recognized only before Type{ / Type( / Type< in expression
position (new Point { x: 1 }, new Vec2(3, 4)), and is an ordinary identifier everywhere else
(let new = 1 compiles). There is no async keyword — await alone makes a function asynchronous
(see Concurrency).
Operators
Section titled “Operators”| Category | Operators |
|---|---|
| Arithmetic | + - * / % |
| Comparison | == != < > <= >= |
| Logical | && || ! (or and or not) |
| Compound assignment | += -= *= /= |
| Error propagation | ? (postfix) |
| Ternary | ? : |
| Null-coalescing | ?? |
| Null-conditional | ?. |
| Range | .. |
| Index-from-end | ^ |
| Heap construction | new (contextual keyword) |
| Address-of | & |
| By-ref pass (call site) | * / & |
| Return type | -> or returns |
| Expression body | = (after the signature) |
| Arrow lambda | => |
| Assignment | = |
The postfix ? (try-unwrap) is disambiguated from the ternary ? by lookahead: if the next token can
start an expression, it is a ternary.
Literals
Section titled “Literals”int_lit = digit { digit | "_" } .float_lit = digit { digit | "_" } "." digit { digit | "_" } [ exponent ] | digit { digit | "_" } exponent .exponent = ( "e" | "E" ) [ "+" | "-" ] digit { digit } .string_lit = `"` { string_char | interpolation } `"` .char_lit = "'" ( char_char | escape ) "'" .bool_lit = "true" | "false" .nil_lit = "nil" .| Type | Examples |
|---|---|
| Integer | 42 · 0 · 1_000_000 (underscore separators) |
| Float | 3.14 · 0.5 · 1.0e10 |
| String | "hello" · "hello {name}" (interpolated) |
| Char | 'a' · '\n' |
| Boolean | true · false |
| Null | nil |
A leading - is the unary negation operator, not part of the literal.
String interpolation
Section titled “String interpolation”interpolation = "{" Expr "}" .No prefix required. { expr } inside a string inserts the value of any expression — a variable, a
member chain ({x.field}), an operator expression ({a + b}), a call ({f(x)}), a ternary
({x > 0 ? "+" : "-"}), an index ({xs[0]}). Each hole is handed to the expression parser and
type-checked like any other expression; value types are boxed into the underlying string.Concat call.
Braces nest, so object/collection literals inside a hole balance.
The hole rule: a { opens a hole only when the next character can start an expression — a letter,
_, (, or !. A leading digit is excluded, so {0} and {0:d} stay literal and BCL format strings
pass through to string.Format unchanged.
let msg = "user {u.name} has {u.count} items" // holeslet fmt = "progress: {0:p1}" // literal — passes to string.Format