# OriLang > Ori is a statically-typed, expression-based compiled programming language with Hindley-Milner type inference, value semantics, ARC memory management (no garbage collector, no borrow checker), capability-based effects, and first-class testing. It compiles to native binaries via LLVM. Ori is not in LLM training data — this file is the self-contained writing kit: the maintained syntax reference, the full grammar (EBNF), and the operator rules, in one fetch. --- # Ori Syntax Quick Reference > Condensed syntax and prelude reference for the Ori programming language, generated from the compiler team's maintained cheat sheet. The specification (https://raw.githubusercontent.com/upstat-io/ori-lang/master/docs/ori_lang/v2026/spec/) and grammar.ebnf are authoritative. **Syntax family: Rust-like.** Statements terminate with `;` (including every `let` in a block); the closing `}` of an expression-bodied declaration or block-ending-statement is the only place `;` is omitted. Default to `;` — ML/Haskell/Gleam newline-as-terminator instincts do NOT apply. ## Declarations **Functions**: `@name (p: T) -> R = expr;` | `@name (p: T) -> R = { ... }` (no `;`) | `pub @name` | `@name` | `@name` | `@name` | `where T: Clone` | `uses Capability` | `(x: int = 10)` defaults **Variadics**: `@sum (nums: ...int) -> int` | receives as `[T]` | call: `sum(1, 2, 3)` | spread: `sum(...list)` | trait objects: `...Printable` | empty calls need explicit type for generics **Clauses**: `@f (0: int) -> int = 1` then `@f (n) = n * f(n-1)` | `if guard` | exhaustive, top-to-bottom **Constants**: `let $name = value;` | `pub let $name` | module-level must be `$` **Const Functions**: `$name (p: T) -> R = expr` — pure, comptime, limits: 1M steps/1000 depth/100MB/10s **Types**: `type N = { f: T }` struct | `A | B | C(f: T)` sum | `type N = Existing` newtype | `type N` | `#derive(Eq)` | `pub type` **Newtypes**: `type UserId = int` | construct: `UserId(42)` | `.inner` (always public) | no trait/method inheritance | `#derive(Eq, Clone)` required | zero cost **Traits**: `trait N { @m (self) -> T }` | `@m (self) -> T = default` | `type Item` assoc | `type Output = Self` default | `trait C: P` | `@m () -> Self` assoc fn | `trait N` default type param **Impls**: `impl T { @m }` inherent | `impl T: Trait` | `impl C: Trait` | `self` (mutable in methods — mutations propagate to caller) / `Self` **Method generics**: `impl Box { @map (self, f: T -> U) -> Box = ... }` | `` bounded | `where U: Trait` clause | duplicate binder `` rejected (`E1002`) | def-impl methods may not declare `uses` (stateless contract) | non-object-safe traits cannot include methods with `<...>` (object-safety §Object Safety) **Associated Functions**: `impl T { @new () -> T }` — no `self` | call: `Type.method()` | `Self` in return | generics: `Option.some(v:)` **Default Impls**: `pub def impl Trait { @m }` — stateless, one per trait/module, auto-bound, override with `with` **Extensions**: `extend Type { @m (self) -> T }` | `extend [T]` | `extend T where T: Bound` | `pub extend` | no statics/fields/override **Resolution**: Diamond=single impl; Inherent>Trait>Extension; qualified: `Trait.method(v)`, `Type::Trait::Assoc`; extensions: `module.Type.method(v)` **Object Safety**: No `Self` return/param (except receiver), no generic methods; safe: `Printable`, `Debug`, `Hashable`; unsafe: `Clone`, `Eq`, `Iterator` **Tests**: `@t tests @fn () -> void` | `@name tests _ () -> void` floating | `tests @a tests @b` multi | `#skip("r")` | `#compile_fail("e")` | `#fail("e")` ## Conditional Compilation **Target**: `#target(os: "linux")` | `arch:` | `family:` | `any_os:` | `not_os:` | file-level: `#!target(...)` **Config**: `#cfg(debug)` | `release` | `feature:` | `any_feature:` | `not_debug` | `not_feature:` **Constants**: `$target_os`, `$target_arch`, `$target_family`, `$debug`, `$release` — false branch not type-checked ## Types **Primitives**: `int` (i64), `float` (f64), `bool`, `str` (UTF-8), `char`, `byte`, `void`, `Never` **Special**: `Duration` (`100ns`/`us`/`ms`/`s`/`m`/`h`), `Size` (`100b`/`kb`/`mb`/`gb`/`tb`) **Collections**: `[T]` list, `[T, max N]` fixed-capacity, `{K: V}` map, `Set` **Compound**: `(T, U)` tuple (access: `.0`, `.1`), `()` unit, `(T) -> U` fn, `Trait` object, `impl Trait` existential **Generic**: `Option`, `Result`, `Range`, `Ordering` **Const Generics**: `$N: int` | `@f` | `$B: bool` | `where N > 0` | `where N > 0 && N <= 100` **Const Bounds**: comparison (`==`/`!=`/`<`/`<=`/`>`/`>=`), logical (`&&`/`||`/`!`), arithmetic (`+`/`-`/`*`/`/`/`%`), bitwise (`&`/`|`/`^`/`<<`/`>>`) | multiple `where` = AND **Channels**: `Producer`, `Consumer`, `CloneableProducer`, `CloneableConsumer` (`T: Sendable`) **Concurrency**: `Nursery`, `NurseryErrorMode` (`CancelRemaining | CollectAll | FailFast`) **FFI**: `CPtr` (C opaque), `JsValue` (JS handle), `JsPromise` (JS async) **Rules**: No implicit conversions; overflow panics; `str[i]` → single-codepoint `str` ### Duration & Size **Duration**: 64-bit nanoseconds; suffixes `ns`/`us`/`ms`/`s`/`m`/`h`; decimal syntax (`0.5s`=500ms, `1.5s`=1500ms) **Size**: 64-bit bytes (non-negative); suffixes `b`/`kb`/`mb`/`gb`/`tb`; SI units (1000-based); decimal syntax (`1.5kb`=1500 bytes) **Decimal literals**: Compile-time sugar using integer arithmetic (no floats); must result in whole base unit; `1.5ns`/`0.5b` = error **Arithmetic**: `+`/`-`/`*`/`/`/`%`, unary `-` (Duration only; Size `-` panics if negative, unary `-` = compile error) **Methods**: `.nanoseconds()`/`.microseconds()`/`.milliseconds()`/`.seconds()`/`.minutes()`/`.hours()` | `.bytes()`/`.kilobytes()`/`.megabytes()`/`.gigabytes()`/`.terabytes()` → `int` **Factory**: `Duration.from_nanoseconds(ns:)`... | `Size.from_bytes(b:)`... **Traits**: `Eq`, `Comparable`, `Hashable`, `Clone`, `Debug`, `Printable`, `Default` (`0ns`/`0b`), `Sendable` ### Never Bottom type (uninhabited); coerces to any `T` **Producers**: `panic(msg:)`, `todo()`, `unreachable()`, `break`, `continue`, `expr?` on Err/None, infinite `loop` **Generics**: `Result` = always Err | `Result` = always Ok | `Option` = always None **Restrictions**: Cannot be struct field; may be sum variant payload (unconstructable) ### Fixed-Capacity Lists `[T, max N]` — inline-allocated, compile-time max N, dynamic length 0..N | `[T, max N] <: [T]` **Methods**: `.capacity()`, `.is_full()`, `.remaining()`, `.push()` (panics), `.try_push()` → `bool`, `.push_or_drop()`, `.push_or_oldest()`, `.to_dynamic()` **Conversion**: `.to_fixed<$N>()` panics | `.try_to_fixed<$N>()` → `Option` ### Existential Types (`impl Trait`) `impl Trait where Assoc == Type` — opaque return type; concrete type hidden from callers **Position**: return only | argument position: use generics instead **Syntax**: `@f () -> impl Iterator where Item == int` | `impl A + B` multi-trait **Where clause**: type-local (constraints on associated types, not type params) **Dispatch**: static (monomorphized) — no vtable overhead **Rules**: all return paths must yield same concrete type **vs Trait objects**: `impl Trait` (static/single type) vs `Trait` (dynamic/any type at runtime) ## Literals `42`, `1_000_000`, `0xFF`, `0b1010` | `3.14`, `2.5e-8` | `"hello"` (escapes: `\\\"\n\t\r\0\xHH\u{H}`) | `` `{name}` `` | `'a'`, `'\x41'` (escapes: `\\\'\n\t\r\0\xHH\u{H}`, `\xHH` restricted to `\x00`–`\x7F`) | `b'x'`, `b'\xFF'` (byte literal, escapes: `\\\'\n\t\r\0\xHH`, full `\x00`–`\xFF`, no `\u{}` or `\"`) | `true`/`false` | duration/size literals | `[1, 2]`, `[...a, ...b]` | `{key: v}`, `{"key": v}`, `{[expr]: v}`, `{...a, ...b}` | `Point { x, y }`, `{ ...p, x: 10 }` ## Operators (precedence high→low) 1. `.` `[]` `()` `?` `as` `as?` — 2. `**` (right) — 3. `!` `-` `~` — 4. `*` `/` `%` `div` `@` — 5. `+` `-` — 6. `<<` `>>` — 7. `..` `..=` (with optional `by` step modifier) — 8. `<` `>` `<=` `>=` — 9. `==` `!=` — 10. `&` — 11. `^` — 12. `|` — 13. `&&` — 14. `||` — 15. `??` (right) — 16. `|>` (pipe) **Unary**: `!` (Not), `-` (Neg), `~` (BitNot) | **Bitwise**: `&`/`|`/`^` (BitAnd/Or/Xor), `<<`/`>>` (Shl/Shr) **Shift overflow**: negative count panics; count ≥ bit width panics; `1 << 63` panics **Operator traits**: desugar to trait methods; user types implement for operator support **Compound assignment**: `x op= y` desugars to `x = x op y` (parser-level) | `+=` `-=` `*=` `/=` `%=` `**=` `@=` `&=` `|=` `^=` `<<=` `>>=` `&&=` `||=` | statement, not expression | target must be mutable (no `$`) | `&&=`/`||=` preserve short-circuit **Pipe**: `x |> f(a: v)` fills single unspecified param | `x |> .method()` calls method on piped value | `x |> (a -> expr)` lambda fallback | prec 16 (lowest) | left-assoc | desugars to let-binding + call in type checker | "unspecified" = no value AND no default ## Expressions **Conditionals**: `if c then e else e` | `if c then e` (void) **Bindings**: `let x = v` mutable | `let $x` immutable | `let x: T` | shadowing OK | `let { x, y }` | `let { x: px }` | `let (a, b)` | `let [..t]` / `let [..$t]` rest-only (irrefutable) — refutable list shapes (`[a, b, c]`, `[head, ..tail]`, `[only]`) are illegal in `let` (E2001), bind via `match` (per 15-patterns.md) **Indexing**: `list[0]`, `list[# - 1]` (`#`=length, panics OOB) | `map["k"]` → `Option` **Index/Field Assignment**: `list[i] = x` → `list = list.updated(key: i, value: x)` | `state.field = x` → `state = { ...state, field: x }` | mixed chains: `state.items[i] = x`, `list[i].name = x` | compound: `list[i] += 1` | root must be mutable (non-`$`) **Access**: `v.field`, `v.0` (tuple), `v.method(arg: v)` — named args required except: fn variables, single-param with inline lambda **Argument Punning**: `f(x:)` = `f(x: x)` when variable matches param name | `f(x:, y: 42)` mixed | trailing `:` distinguishes from positional `f(x)` **Lambdas**: `x -> x + 1` | `(a, b) -> a + b` | `() -> 42` | `(x: int) -> x * 2` (typed param, inferred return) | `(x: int) -> int = x * 2` (explicit return) — capture by value **Ranges**: `0..10` excl | `0..=10` incl | `0..10 by 2` | descending: `10..0 by -1` | infinite: `0..`, `0.. by -1` | int only **Blocks**: `{ let $x = 1; x + 2 }` — `;` terminates statements, last expression (no `;`) is value | all `;` = void block | `ori fmt` enforces blank line before result | empty `{ }` = empty map **Loops**: `while c do e` | `for i in items do e` | `for x in items yield x * 2` | `for x in items if g yield x` | nested `for` | `loop { body }` + `break`/`continue` | `break value` | `continue value` **While**: `while condition do body` — sugar for `loop { if !condition then break; body }` | type: `void` | no `while...yield` | `break value` error (E0860) | `continue value` error (E0861) **Loop body**: block expression; `loop { a \n b \n c }` for sequences | type: `void` (break no value), inferred (break value), `Never` (no break) | `continue value` error (E0861) **Yield control**: `continue` skips | `continue value` substitutes | `break` stops | `break value` adds final | `{K: V}` from `(K, V)` tuples **Labels**: `loop:name` | `for:name` | `while:name` | `block:name` | `break:name` | `continue:name` | no shadowing | `continue:name value` in yield → outer **Labeled blocks**: `block:name { body }` — early exit via `break:name value` | type = unified exit paths | bare `break` is loop-only (not block) | `continue:block_label` = error | transparent to `break:loop_label`/`continue:loop_label` **Spread**: `[...a, ...b]` | `{...a, ...b}` | `P { ...orig, x: 10 }` — later wins, literal contexts only | `fn(...list)` into variadic only ## Block expressions **Semicolons**: Rust-style — `;` terminates statements; last expression (no `;`) is block value; all `;` = void block **Semicolon rule**: Ends with `}`? No `;`. Everything else: `;`. Applies to `use`, `let $`, functions, types, methods. **Blocks**: `{ let $x = 1; let $y = 2; x + y }` — `;` on statements, no `;` on result **Match**: `match expr { P1 -> e1, P2 -> e2 }` — scrutinee before block, comma-separated arms (trailing comma optional) **Try**: `try { let $x = f()?; Ok(x) }` — error-propagating block **Contracts**: `pre(condition)` | `pre(condition | "message")` | `post(r -> condition)` — on function declaration, between signature and `=` **function_exp**: `recurse(condition:, base:, step:, memo:, parallel:)` | `parallel(tasks:, max_concurrent:, timeout:)` → `[Result]` | `spawn(tasks:, max_concurrent:)` → `void` | `timeout(op:, after:)` | `cache(key:, op:, ttl:)` | `with(acquire:, action:, release:)` | `for(over:, match:, default:)` | `catch(expr:)` → `Result` | `nursery(body:, on_error:, timeout:)` **Channels**: `channel(buffer:)` → `(Producer, Consumer)` | `channel_in` | `channel_out` | `channel_all` **Conversions**: `42 as float` infallible | `"42" as? int` fallible → `Option` **Match patterns**: literal | `x` | `_` | `Some(x)` | `{ x, y }` | `[a, ..rest]` | `1..10` | `A | B` | `x @ pat` | `x if guard` | variant punning: `Circle(radius:)` = `Circle(radius: radius)`, `Some(value:)` = `Some(value: value)` **Exhaustiveness**: match exhaustive; guards need `_`; `let` patterns irrefutable ## Imports **Relative**: `use "./math" { add };` | `"../utils"` | `"./http/client"` **Module**: `use std.math { sqrt };` | `use std.net.http as http;` **Private**: `use "./m" { ::internal };` | **Alias**: `{ add as plus }` | **Re-export**: `pub use` **Without default**: `use "m" { Trait without def };` — import without `def impl` **Extensions**: `extension std.iter.extensions { Iterator.count }` — method-level, no wildcards | `pub extension` ## FFI **Native (C)**: `extern "c" from "lib" { @_sin (x: float) -> float as "sin" }` | `from` specifies library | `as` maps name **JavaScript**: `extern "js" { @_sin (x: float) -> float as "Math.sin" }` | `extern "js" from "./utils.js"` **C Variadics**: `extern "c" { @printf (fmt: CPtr, ...) -> c_int }` — untyped, requires `unsafe`, platform va_list ABI **Types**: `CPtr` opaque | `Option` nullable | `JsValue` handle | `JsPromise` async **C Types**: `c_char`, `c_short`, `c_int`, `c_long`, `c_longlong`, `c_float`, `c_double`, `c_size` **Layout**: `#repr("c")` C-compatible | `#repr("packed")` no padding | `#repr("transparent")` same as single field | `#repr("aligned", N)` minimum alignment (power of two) | struct types only; newtypes implicitly transparent **Unsafe**: `unsafe { ptr_read(...) }` | **Capability**: `uses Unsafe` (marker, like `Suspend` — cannot be bound via `with...in`) **Async WASM**: `JsPromise` implicitly resolved at binding sites | **Compile Error**: `compile_error("msg")` ### Deep FFI (opt-in annotations on extern blocks) **Error Protocols**: `extern "c" from "lib" #error(errno) { ... }` — block-level; `#error(none)` per-function opt-out **Error Variants**: `#error(errno | nonzero | null | negative | success: N | none)` — auto-generates `Result` **FfiError**: `use std.ffi { FfiError }` — `{ code: int, message: str, source: str }` **Out Params**: `@f (name: str, db: out CPtr) -> c_int` — `out` params folded into return type **Ownership**: `owned` / `borrowed` on params/returns | str returns default to `borrowed` (copy, don't free) **Free**: `#free(fn)` on block or per-function — auto-generates `Drop` impl for `owned CPtr` **[byte] Elision**: `[byte]` in extern generates adjacent `(ptr, len)` C args | `mut [byte]` generates `(ptr, &len)` **Parametric FFI**: `uses FFI("sqlite3")` per-library | `uses FFI` shorthand for all | each `from "lib"` is distinct capability **Mocking**: `with FFI("lib") = handler { fn: (...) -> T = ..., } in { ... }` — handler-based mock; stateless is sugar for `handler(state: ())` ## Capabilities **Declare**: `@f (...) -> T uses Http = ...` | `uses FileSystem, Suspend` **Provide**: `with Http = RealHttp { } in expr` | `with Http = mock, Cache = mock in expr` **Stateful Handlers**: `with Cap = handler(state: init) { op: (s) -> (s', val), ... } in expr` — state replaces `self`; returns `(S, R)` tuple; frame-local mutable state; `with...in` returns body type only **Handler Rules**: context-sensitive keyword; single state value (compose via structs); all trait methods required (defaults used if omitted); no `self`; errors E1204-E1207 **Resolution**: with...in > imported `def impl` > module-local `def impl` **Suspend**: `uses Suspend` = may suspend; no `uses` = sync; concurrency via `parallel(...)` **Standard**: `Http`, `FileSystem`, `Clock`, `Random`, `Crypto`, `Cache`, `Print` (default), `Logger`, `Env`, `Intrinsics`, `Suspend`, `FFI` **Intrinsics**: Generic SIMD/bit ops; `Intrinsics.simd_add(a:, b:)` (monomorphized by `[T, max N]`), `count_ones(value:)`, `cpu_has_feature(feature:)`; comparisons return `Mask<$N>` (methods: `bits`, `any`, `all`, `count`, `first_set`; operators: `&`, `|`, `~`) **Capsets**: `capset Net = Http, Dns, Tls` — transparent alias, expanded in `uses` before type checking; `@f uses Net` expands to `@f uses Http, Dns, Tls`; capsets can include other capsets; not a trait (no `impl`, no `with`, no `def impl`) ## Comments `// comment` — own line only | Doc: `// Desc` | `// * name:` | `// ! Error:` | `// > expr -> result` ## Formatting 4 spaces, 100 char limit, trailing commas multi-line only | `;` terminates statements in blocks, `use`, `let $`, expression-bodied declarations; block body `}` = no `;` | Space around: binary ops, arrows, colons/commas, `pub`, all braces `{ }`, `as`/`by`/`|`/`with`/`+`, `=` in ``, `??`, compound `+=` | No space: parens/brackets, `.`/`..`/`?`/`...`, empty delimiters, before `;`, labels `:`, punning `name:` | Break at 100; blocks 4-space indent; blank line before result in setup+result blocks; `match`/`try`/`recurse`/`parallel`/`spawn`/`nursery` always stacked; `timeout`/`cache`/`catch` width-based | Params/args/generics/where/fields/variants one-per-line; chains break at `.method()` (all-or-nothing); binary break before op; `if...then` together, `else` newline; chained `else if` each on own line; `for...yield`/`do` inline if fits | File order: file attrs → imports (stdlib→relative, sorted alpha) → constants → user-ordered rest | Attrs canonical order: `#target`/`#cfg` → `#repr` → `#derive` → `#skip`/`#compile_fail`/`#fail` | Traits: assoc types → required methods → defaults | Impls: assoc types → methods in trait order | Parens always preserved; never removed by formatter ## Keywords **Reserved (36)**: `as break continue def div do else extend extension extern false for if impl in let loop match Never pub self Self suspend tests then trait true type unsafe use uses void where while with yield` **Reserved (future)**: `asm inline static union view` (reserved for future low-level features) **Context-sensitive (type names, 5)**: `bool byte float int str` **Context-sensitive (patterns, 9)**: `cache catch handler nursery parallel recurse spawn timeout try` **Context-sensitive (blocks, 1)**: `block` (before `:` only — labeled block early exit) **Context-sensitive (pattern args, 10)**: `body buffer default expr map on_error over pre post state` **Context-sensitive (imports, 2)**: `from` (extern blocks), `without` (import items, before `def`) **Context-sensitive (other, 3)**: `args` (@main params), `by` (after range), `max` (fixed-capacity lists) **Context-sensitive (embed, 2)**: `embed` (file embedding), `has_embed` (file existence check) **Built-in constructors (4)**: `channel channel_all channel_in channel_out` **Built-in names**: `len is_empty is_some is_none is_ok is_err compare min max print panic todo unreachable dbg compile_error hash_combine repeat is_cancelled drop_early` ## Prelude **Types**: `Option` (`Some`/`None`), `Result` (`Ok`/`Err`), `Error`, `TraceEntry`, `Ordering`, `PanicInfo`, `CancellationError`, `CancellationReason`, `FormatSpec`, `Alignment`, `Sign`, `FormatType` **Traits**: `Eq`, `Comparable`, `Hashable`, `Printable`, `Formattable`, `Debug`, `Clone`, `Default`, `Drop`, `Len`, `IsEmpty`, `Iterator`, `DoubleEndedIterator`, `Iterable`, `Collect`, `Into`, `Traceable`, `Index`, `Sendable`, `Value` **Built-ins**: `print(msg:)`, `len(collection:)`, `is_empty(collection:)`, `is_some/is_none(option:)`, `is_ok/is_err(result:)`, `panic(msg:)`→`Never`, `todo()`/`todo(reason:)`→`Never`, `unreachable()`/`unreachable(reason:)`→`Never`, `dbg(value:)`/`dbg(value:, label:)`→`T`, `compare(left:, right:)`→`Ordering`, `min/max(left:, right:)`, `hash_combine(seed:, value:)`→`int`, `repeat(value:)`→iter (`T: Clone`), `is_cancelled()`→`bool`, `compile_error(msg:)`, `drop_early(value:)`, `embed(path)`→type-driven (`str`/`[byte]`), `has_embed(path)`→`bool` **Testing (`std.testing`, import required)**: `assert(cond:)`, `assert_eq(actual:, expected:)`, `assert_ne(actual:, unexpected:)`, `assert_some/none(opt:)`, `assert_ok/err(result:)`, `assert_panics(f:)`, `assert_panics_with(f:, msg:)`. Import with `use std.testing { assert, assert_eq }`. **Option**: `.map(transform:)`, `.unwrap()`, `.unwrap_or(default:)`, `.expect(msg:)`, `.ok_or(err:)`, `.and_then(then:)`, `.flat_map(f:)`, `.or(alt:)`, `.filter(predicate:)` **Result**: `.map(transform:)`, `.map_err(transform:)`, `.unwrap()`, `.unwrap_err()`, `.unwrap_or(default:)`, `.expect(msg:)`, `.expect_err(msg:)`, `.ok()`, `.err()`, `.and_then(then:)`, `.or_else(f:)`, `.trace()`→`str`, `.trace_entries()`→`[TraceEntry]`, `.has_trace()` **Error**: `.trace()`, `.trace_entries()`, `.has_trace()` **Ordering**: `Less | Equal | Greater` — `.is_less/equal/greater()`, `.is_less_or_equal/greater_or_equal()`, `.reverse()`, `.then(other:)`, `.then_with(f:)`; default `Equal`; order `Less < Equal < Greater`; impls Eq, Comparable, Clone, Debug, Printable, Hashable, Default **Printable**: `@to_str (self) -> str` — required for `` `{x}` ``; all primitives impl **Formattable**: `@format (self, spec: FormatSpec) -> str` — blanket for Printable; spec: `[[fill]align][sign][#][0][width][.precision][type]`; align `<>^`; sign `+ - `; types `bxXoeEf%`; `#` prefix; `0` pads **Debug**: `@debug (self) -> str` — escaped strings, derivable | **Clone**: `@clone (self) -> Self` — all primitives/collections, derivable **Iterator**: `type Item; @next (self) -> (Option, Self)` — fused, copy elision, lazy **DoubleEndedIterator**: `trait: Iterator { @next_back (self) -> (Option, Self) }` **Iterable**: `type Item; @iter (self) -> impl Iterator` | **Collect**: `@from_iter (iter: impl Iterator) -> Self` **Iterator methods**: `.map`, `.filter`, `.fold`, `.find`, `.for_each`, `.collect`, `.count`, `.any`, `.all`, `.take`, `.skip`, `.enumerate`, `.zip`, `.chain`, `.flatten`, `.flat_map`, `.cycle`, `.join` **DoubleEnded methods**: `.rev`, `.last`, `.rfind`, `.rfold` **Infinite**: `repeat(value:)`, `(0..).iter()` — bound with `.take(count:)` before `.collect()` **Into**: `@into (self) -> T` — lossless, explicit `.into()`, standard: str→Error, int→float, Set→[T]; no identity/chaining **Traceable**: `@with_trace`, `@trace`→`str`, `@trace_entries`→`[TraceEntry]`, `@has_trace` **TraceEntry**: `{ function, file, line, column: int }` — `@` prefix; most recent first **PanicInfo**: `{ message, location: TraceEntry, stack_trace: [TraceEntry], thread_id: Option }` **Drop**: `@drop (self) -> void` — refcount zero; not async; panic during unwind aborts **Index**: `@index (self, key: Key) -> Value` — `x[k]`→`x.index(key: k)`; return `T`/`Option`/`Result`; `#` built-in only; multiple impls per type OK: `impl Index` + `impl Index` disambiguated by key type at compile time **Eq**: `@equals (self, other: Self) -> bool` — reflexive/symmetric/transitive; derives `==`/`!=` **Comparable**: `trait: Eq { @compare (self, other: Self) -> Ordering }` — total order; derives `<`/`<=`/`>`/`>=`; NaN > all; `None < Some`; `Ok < Err` **Hashable**: `trait: Eq { @hash (self) -> int }` — `a == b` ⇒ same hash; +0.0/-0.0 same; use `hash_combine` **Operator traits**: `Add`/`Sub`/`Mul`/`Div`/`FloorDiv`/`Rem`/`Pow` — binary; `MatMul` — matrix multiply (`@`); `Neg`/`Not`/`BitNot` — unary; `BitAnd`/`BitOr`/`BitXor`, `Shl`/`Shr` — bitwise; `As`/`TryAs` — conversion (`as`/`as?`); all default `type Output = Self` **Operator methods**: `add`/`subtract`/`multiply`/`divide`/`floor_divide`/`remainder`/`power` — arithmetic; `matrix_multiply` — matmul (`@`); `negate`/`not`/`bit_not` — unary; `bit_and`/`bit_or`/`bit_xor`/`shift_left`/`shift_right` — bitwise; `as`/`try_as` — conversion **Sendable**: marker trait, auto-derived by compiler; all fields must be `Sendable`, no interior mutability, no non-Sendable captures; required for channel types `T: Sendable`; cannot be implemented manually **Value**: `trait Value: Clone, Eq` — marker trait; inline storage, bitwise copy, no ARC, no Drop; all fields must be `Value`; cannot be implemented manually; auto-satisfies `Clone` + `Sendable`; warning >256 bytes, error >512 bytes; primitives (`int`/`float`/`bool`/`char`/`byte`/`void`/`Duration`/`Size`/`Ordering`) implicitly `Value`; `str`/`[T]`/`{K:V}`/`Set` never `Value`; syntax: `type Point: Value, Eq = { x: float, y: float }` **List methods**: `.map`/`.filter`/`.fold(initial:, op:)`/`.reduce(op:)`/`.find(where:)`/`.any(pred:)`/`.all(pred:)`/`.for_each(f:)`/`.flat_map(f:)`/`.flatten()`/`.first()`/`.last()`/`.get(index:)`/`.take(count:)`/`.take_while(pred:)`/`.skip(count:)`/`.skip_while(pred:)`/`.slice(start:, end:)`/`.chunk(size:)`/`.window(size:)`/`.enumerate()`/`.zip(other:)`/`.concat(other:)`/`.append(value:)`/`.prepend(value:)`/`.push(value:)`/`.pop()`/`.insert(index:, value:)`/`.remove(index:)`/`.set(index:, value:)`/`.reverse()`/`.sort()`/`.sort_by(cmp:)`/`.sort_stable()`/`.sorted()`/`.unique()`/`.partition(pred:)`/`.group_by(key:)`/`.count()`/`.min()`/`.max()`/`.min_by(key:)`/`.max_by(key:)`/`.sum()`/`.product()`/`.join(sep:)`/`.iter()`/`.contains(value:)`/`.len()`/`.length()`/`.is_empty()` (sort/min/max require `T: Comparable`; contains requires `T: Eq`) **String methods**: `.split(sep:)`, `.trim()`/`.trim_start()`, `.substring(start:, end:)`/`.slice(start:, end:)`, `.to_lowercase()`, `.starts_with(prefix:)`, `.ends_with(suffix:)`, `.contains(substr:)`, `.last_index_of(substr:)`, `.replace(old:, new:)`, `.repeat(count:)`, `.pad_start(width:, fill:)`/`.pad_end(width:, fill:)`, `.concat(other:)`, `.chars()`/`.iter()`/`.lines()`, `.parse_int()`/`.parse_float()`→`Option`, `.escape()`, `.len()`/`.length()`, `.is_empty()`, `.as_bytes()`→`[byte]` (zero-copy), `.to_bytes()`→`[byte]` (copy), `.byte_len()`→`int` **Char methods**: `.is_alpha()`, `.is_digit()`, `.is_whitespace()`, `.is_uppercase()`, `.is_lowercase()`, `.is_ascii()`, `.to_lowercase()`, `.to_uppercase()`, `.to_byte()`→`byte`, `.to_int()`→`int`, `.to_str()`→`str` **Byte methods**: `.is_ascii()`, `.is_ascii_alpha()`, `.is_ascii_digit()`, `.is_ascii_whitespace()`, `.to_char()`→`char`, `.to_int()`→`int`, `.to_str()`→`str`; arithmetic: `+`/`-`/`*`/`/`/`%` via `add`/`sub`/`mul`/`div`/`rem`; bitwise: `&`/`|`/`^`/`~`/`<<`/`>>` via `bit_and`/`bit_or`/`bit_xor`/`bit_not`/`shl`/`shr` **Compile-Time Reflection**: `fields_of(T)`→`[$FieldMeta]`, `variants_of(T)`→`[$VariantMeta]`, `name_of(T)`→`str` — zero-cost intrinsics, no opt-in; `$FieldMeta { name: str, index: int }`, `$VariantMeta { name: str, index: int, fields: [$FieldMeta] }` — compiler-internal types; `$for field in fields_of(T) yield/do body` — compile-time expansion; `$if const_cond then expr else expr` — dead-branch elimination; `value.[field]` — splice access; `is_struct(T)`, `is_enum(T)`, `is_primitive(T)`, `is_collection(T)`, `is_option(T)`, `is_result(T)`, `is_tuple(T)` — type classification (accept type param or expression) --- # Grammar (EBNF) — syntax single source of truth ```ebnf // ============================================================================ // Ori Language Grammar // Version: 2026 // // This is the unified formal grammar for the Ori programming language. // All productions are authoritative and take precedence over prose descriptions. // // Notation: // production = expression . Production definition // "keyword" Literal token // | Alternation // [ ] Optional (0 or 1) // { } Repetition (0 or more) // ( ) Grouping // /* comment */ Informative note // // Cross-references to detailed explanations are provided in comments. // ============================================================================ // ============================================================================ // LEXICAL GRAMMAR // See: 06-source-code.md, 07-lexical-elements.md // ============================================================================ // --- Characters --- // See: 06-source-code.md § 6.1 Characters, § 6.1.2 Letters and digits unicode_char = /* any Unicode code point except NUL (U+0000) */ . letter = 'A' … 'Z' | 'a' … 'z' . digit = '0' … '9' . hex_digit = digit | 'A' … 'F' | 'a' … 'f' . bin_digit = '0' | '1' . newline = /* U+000A */ . whitespace = ' ' | '\t' | '\r' | newline . // --- Tokens --- token = identifier | keyword | literal | operator | delimiter . // --- Comments --- // See: 07-lexical-elements.md § 7.1 Comments comment = "//" { unicode_char - newline } newline . doc_comment = "//" [ " " ] [ doc_marker ] { unicode_char - newline } newline . doc_marker = "*" | "!" | ">" . member_doc = "//" " " "*" " " identifier ":" [ " " { unicode_char - newline } ] . warning_doc = "//" " " "!" " " { unicode_char - newline } . example_doc = "//" " " ">" " " { unicode_char - newline } . // --- Identifiers --- // See: 07-lexical-elements.md § 7.2 Identifiers identifier = ( letter | "_" ) { letter | digit | "_" } . // --- Keywords --- // See: 07-lexical-elements.md § 7.3 Keywords // // Reserved (36): as, break, continue, def, div, do, else, extend, extension, extern, // false, for, if, impl, in, let, loop, match, Never, pub, self, Self, suspend, // tests, then, trait, true, type, unsafe, use, uses, void, where, while, with, yield // Reserved (future, 5): asm, inline, static, union, view // Context-sensitive (patterns, 9): cache, catch, handler, nursery, parallel, recurse, spawn, // timeout, try // Context-sensitive (blocks, 1): block (before ":" only — labeled block early exit) // Context-sensitive (pattern args, 10): body, buffer, default, expr, map, on_error, over, // pre, post, state // Context-sensitive (imports, 2): from (extern blocks), without (import items, before "def") // Context-sensitive (other, 3): args (@main params), by (after range), max (fixed-capacity lists) // Context-sensitive (embed, 2): embed (file embedding), has_embed (file existence check) // Context-sensitive (type names, 5): bool, byte, float, int, str // Built-in constructors (4): channel, channel_all, channel_in, channel_out // --- Operators --- // See: 07-lexical-elements.md § 7.4 Operators arith_op = "+" | "-" | "*" | "/" | "%" | "div" | "**" . comp_op = "==" | "!=" | "<" | ">" | "<=" | ">=" . logic_op = "&&" | "||" | "!" . bit_op = "&" | "|" | "^" | "~" | "<<" | ">>" . unary_op = "!" | "-" | "~" . other_op = ".." | "..=" | "??" | "?" | "->" | "=>" | "|>" . // --- Delimiters --- delimiter = "(" | ")" | "[" | "]" | "{" | "}" | "," | ":" | "." | "@" | "$" | ";" . // --- Literals --- // See: 07-lexical-elements.md § 7.7 Literals literal = int_literal | float_literal | string_literal | template_literal | char_literal | byte_literal | bool_literal | duration_literal | size_literal . // Integer literals // See: 07-lexical-elements.md § 7.7.1 Integer literals int_literal = decimal_lit | hex_lit | bin_lit . decimal_lit = "0" | non_zero_digit { [ "_" ] digit } . non_zero_digit = "1" … "9" . hex_lit = "0" ( "x" | "X" ) hex_digit { [ "_" ] hex_digit } . bin_lit = "0" ( "b" | "B" ) bin_digit { [ "_" ] bin_digit } . // Float literals // See: 07-lexical-elements.md § 7.7.2 Float literals float_literal = decimal_digits "." decimal_digits [ exponent ] | decimal_digits exponent . decimal_digits = digit { [ "_" ] digit } . exponent = ( "e" | "E" ) [ "+" | "-" ] decimal_digits . // String literals // See: 07-lexical-elements.md § 7.7.3 String literals string_literal = '"' { string_char | escape_seq } '"' . string_char = unicode_char - ( '"' | '\' | newline ) . escape_seq = '\' ( '"' | '\' | 'n' | 't' | 'r' | '0' ) | unicode_escape | hex_escape . unicode_escape = '\' 'u' '{' hex_digit { hex_digit } '}' . /* 1-6 hex digits */ hex_escape = '\' 'x' hex_digit hex_digit . /* \x00-\xFF */ // Template string literals (with interpolation) // See: 07-lexical-elements.md § 7.7.4 Template strings template_literal = '`' { template_char | template_escape | unicode_escape | template_brace | interpolation } '`' . template_char = unicode_char - ( '`' | '\' | '{' | '}' ) . template_escape = '\' ( '`' | '\' | 'n' | 't' | 'r' | '0' ) . template_brace = "{{" | "}}" . interpolation = '{' expression [ ':' format_spec ] '}' . // Format specifiers for template strings // See: 07-properties-of-types.md § Format Spec Syntax // Syntax: [[fill]align][sign][#][0][width][.precision][type] format_spec = [ [ fill ] align ] [ sign ] [ alt_form ] [ zero_pad ] [ width ] [ '.' precision ] [ format_type ] . fill = unicode_char - ( align | sign ) . align = '<' | '>' | '^' . sign = '+' | '-' | ' ' . alt_form = '#' . zero_pad = '0' . width = decimal_lit . precision = decimal_lit . format_type = 'b' | 'o' | 'x' | 'X' | 'e' | 'E' | 'f' | '%' . // Character literals // See: 07-lexical-elements.md § 7.7.5 Character literals char_literal = "'" ( char_char | char_escape ) "'" . char_char = unicode_char - ( "'" | '\' | newline ) . char_escape = '\' ( "'" | '\' | 'n' | 't' | 'r' | '0' ) | unicode_escape | char_hex_escape . char_hex_escape = '\' 'x' hex_digit hex_digit . /* \x00-\x7F only; \x80-\xFF is an error */ // Byte literals // See: 07-lexical-elements.md § 7.7.6 Byte literals byte_literal = "b'" ( byte_char | byte_escape ) "'" . byte_char = ascii_char - ( "'" | '\' ) . /* U+0020-U+007E, excluding ' and \ */ byte_escape = '\' ( "'" | '\' | 'n' | 't' | 'r' | '0' ) | hex_escape . /* \x00-\xFF; no \u{} or \" */ ascii_char = '\x20' … '\x7E' . /* printable ASCII */ // Boolean literals bool_literal = "true" | "false" . // Duration literals // See: 07-lexical-elements.md § 7.7.8 Duration literals // Decimal syntax (e.g., 0.5s) is compile-time sugar computed via integer arithmetic duration_literal = duration_number duration_unit . duration_number = decimal_lit | decimal_lit "." digit { digit } . duration_unit = "ns" | "us" | "ms" | "s" | "m" | "h" . // Size literals // See: 07-lexical-elements.md § 7.7.9 Size literals // Decimal syntax (e.g., 1.5kb) is compile-time sugar computed via integer arithmetic size_literal = size_number size_unit . size_number = decimal_lit | decimal_lit "." digit { digit } . size_unit = "b" | "kb" | "mb" | "gb" | "tb" . // ============================================================================ // SOURCE STRUCTURE // See: 06-source-code.md, 18-modules.md // ============================================================================ source_file = [ file_attribute ] { import | reexport | extension_import } { declaration } . // File-level attribute (conditional compilation) // See: 24-conditional-compilation.md § File-Level Conditions file_attribute = "#!" identifier "(" [ attribute_arg { "," attribute_arg } ] ")" . // --- Imports --- // See: 12-modules.md § Imports import = "use" import_path [ import_list | "as" identifier ] ";" . import_path = string_literal | identifier { "." identifier } . import_list = "{" import_item { "," import_item } "}" . import_item = [ "::" ] identifier [ "without" "def" ] [ "as" identifier ] | "$" identifier . // --- Re-exports --- reexport = "pub" "use" import_path import_list ";" . // --- Extensions --- // See: 12-modules.md § Extensions extension_def = "extend" [ generics ] type [ where_clause ] "{" { method } "}" . extension_import = [ "pub" ] "extension" import_path "{" extension_item { "," extension_item } "}" . extension_item = identifier "." identifier . // --- FFI (Foreign Function Interface) --- // See: spec/24-ffi.md extern_block = [ "pub" ] "extern" string_literal [ "from" string_literal ] { block_attribute } "{" { extern_item } "}" . extern_item = "@" identifier extern_params "->" [ ownership ] type [ "as" string_literal ] { item_attribute } . extern_params = "(" [ extern_param { "," extern_param } ] [ c_variadic ] ")" . extern_param = [ param_modifier ] identifier ":" [ ownership ] type . c_variadic = "," "..." . /* C-style variadic - only valid in extern "c" blocks */ // Deep FFI annotations (see: proposals/approved/deep-ffi-proposal.md) param_modifier = "out" | "mut" . ownership = "owned" | "borrowed" . block_attribute = "#" identifier "(" { attribute_arg } ")" . /* #error(...), #free(...) */ item_attribute = "#" identifier "(" { attribute_arg } ")" . /* per-function overrides */ // Parametric FFI capability ffi_capability = "FFI" [ "(" string_literal ")" ] . // ============================================================================ // DECLARATIONS // See: 08-declarations.md // ============================================================================ declaration = { attribute } [ "pub" ] ( function | type_def | trait_def | impl_block | extension_def | test | constant_decl | extern_block | capset_decl ) . // --- Attributes --- // See: 08-declarations.md § Attributes, 24-conditional-compilation.md // Item-level: #derive(...), #skip(...), #target(...), #cfg(...) attribute = "#" identifier [ "(" [ attribute_arg { "," attribute_arg } ] ")" ] . attribute_arg = expression | identifier ":" expression | array_literal . array_literal = "[" [ string_literal { "," string_literal } ] "]" . // --- Functions --- // See: 08-declarations.md § Functions function = "@" identifier [ generics ] clause_params "->" type [ uses_clause ] [ where_clause ] [ guard_clause ] { contract } "=" expression [ ";" ] . /* Semicolon rule: ";" is required when the body expression does not end with "}". When the body is a block_expr (ending with "}"), ";" is omitted — same as Rust's fn. */ clause_params = "(" [ clause_param { "," clause_param } ] ")" . clause_param = match_pattern [ ":" type ] [ "=" expression ] . /* pattern with optional type and default */ guard_clause = "if" expression . // --- Generics --- // See: 06-types.md § Const Generic Parameters generics = "<" generic_param { "," generic_param } ">" . generic_param = type_param | const_param . type_param = identifier [ ":" bounds ] [ "=" type ] . const_param = "$" identifier ":" const_type [ "=" const_expr ] . const_type = "int" | "bool" . bounds = type_path { "+" type_path } . where_clause = "where" constraint { "," constraint } . constraint = type_constraint | const_constraint . type_constraint = identifier ":" bounds . const_constraint = const_bound_expr . // Const bound expressions (for where clauses) // See: 06-types.md § Const Bounds const_bound_expr = const_or_expr . const_or_expr = const_and_expr { "||" const_and_expr } . const_and_expr = const_not_expr { "&&" const_not_expr } . const_not_expr = "!" const_not_expr | const_cmp_expr . const_cmp_expr = const_expr comparison_op const_expr | "(" const_bound_expr ")" . comparison_op = ">" | "<" | ">=" | "<=" | "==" | "!=" . // --- Capabilities --- // See: 14-capabilities.md uses_clause = "uses" identifier { "," identifier } . capset_decl = "capset" identifier "=" identifier { "," identifier } ";" . // --- Type Definitions --- // See: 08-declarations.md § Types, 06-types.md § User-Defined Types type_def = "type" identifier [ generics ] [ where_clause ] "=" type_body [ ";" ] . /* Semicolon rule: ";" required for sum types and newtypes (no "}"); omitted for struct types (end with "}") */ type_body = struct_body | sum_body | type . struct_body = "{" [ field { "," field } ] "}" . sum_body = variant { "|" variant } . variant = identifier [ "(" [ field { "," field } ] ")" ] . field = identifier ":" type . // --- Traits --- // See: 08-declarations.md § Traits trait_def = "trait" identifier [ generics ] [ ":" bounds ] "{" { trait_item } "}" . trait_item = method_sig | default_method | assoc_type . method_sig = "@" identifier params "->" type ";" . default_method = "@" identifier params "->" type "=" expression [ ";" ] . /* Semicolon rule: same as function — ";" required unless body ends with "}" */ assoc_type = "type" identifier [ ":" bounds ] [ "=" type ] . params = "(" [ param { "," param } ] ")" . param = identifier ":" type | variadic_param . variadic_param = identifier ":" "..." type . // --- Implementations --- // See: 08-declarations.md § Implementations impl_block = inherent_impl | trait_impl | def_impl . inherent_impl = "impl" [ generics ] type_path [ where_clause ] "{" { method } "}" . trait_impl = "impl" [ generics ] type ":" type_path [ where_clause ] "{" { method } "}" . def_impl = "def" "impl" identifier "{" { def_impl_method } "}" . method = "@" identifier params "->" type [ uses_clause ] "=" expression [ ";" ] . def_impl_method = "@" identifier params "->" type "=" expression [ ";" ] . /* no self, no uses */ /* Semicolon rule: same as function — ";" required unless body ends with "}" */ // --- Tests --- // See: 13-testing.md test = "@" identifier "tests" test_targets "()" "->" "void" "=" expression [ ";" ] . /* Semicolon rule: same as function — ";" required unless body ends with "}" */ test_targets = "_" | test_target { "tests" test_target } . test_target = "@" identifier . // --- Constants (Immutable Bindings) --- // See: 04-constants.md constant_decl = "let" "$" identifier [ ":" type ] "=" expression ";" . // ============================================================================ // TYPES // See: 06-types.md // ============================================================================ type = type_path [ type_args ] | trait_object_bounds /* Printable + Hashable */ | list_type | fixed_list_type | map_type | tuple_type | function_type | impl_trait_type . type_path = identifier { "." identifier } . // --- Bounded Trait Objects --- // See: 06-types.md § Bounded Trait Objects // Multiple trait bounds as a type: `@store (item: Printable + Hashable) -> void` trait_object_bounds = type_path "+" type_path { "+" type_path } . // --- Existential Types (impl Trait) --- // See: 06-types.md § Existential Types impl_trait_type = "impl" trait_bounds [ impl_where_clause ] . trait_bounds = type_path { "+" type_path } . impl_where_clause = "where" assoc_constraint { "," assoc_constraint } . assoc_constraint = identifier "==" type . type_args = "<" type_or_const { "," type_or_const } ">" . type_or_const = type | const_expr . list_type = "[" type "]" . fixed_list_type = "[" type "," "max" const_expr "]" . map_type = "{" type ":" type "}" . tuple_type = "(" type { "," type } ")" | "()" . function_type = "(" [ type { "," type } ] ")" "->" type . /* const_expr is defined in § CONSTANT EXPRESSIONS below */ // ============================================================================ // EXPRESSIONS // See: 09-expressions.md // ============================================================================ expression = with_expr | let_expr | if_expr | for_expr | while_expr | loop_expr | labeled_block | lambda | break_expr | continue_expr | unsafe_expr | block_expr | match_expr | try_expr | binary_expr . // --- Primary Expressions --- primary = literal | identifier | "self" | "Self" | "(" expression ")" | "#" /* length in index context */ | list_literal | map_literal | struct_literal | block_expr /* { expr \n expr \n result } */ | match_expr /* match expr { arms } */ | try_expr /* try { block_body } */ | pattern_expr /* parallel, spawn, etc. */ | unsafe_expr | embed_expr | has_embed_expr . // --- Block Expression --- // See: proposals/approved/block-expression-syntax.md // Amended by: proposals/approved/optional-semicolon-after-block-expressions-proposal.md // Semicolons terminate statements inside blocks. The last expression without ";" // is the block's value. A block where every expression has ";" returns void. // Semicolons are optional after expression statements whose last token is "}". block_expr = "{" { statement } [ expression ] "}" . statement = let_expr ";" | assignment ";" | block_ending_expression [ ";" ] | expression ";" . // An expression whose last token is "}" — semicolon is optional when used as a statement. // The rule is purely syntactic: check whether the last token is "}". block_ending_expression = for_do_expr | while_do_expr | loop_expr | if_expr | match_expr | unsafe_expr | labeled_block | block_expr . // --- Labeled Block --- // See: 16-control-flow.md § Labeled Blocks // "block" is a context-sensitive keyword, recognized only before ":". // break:label exits the block with a value; continue:label targeting a block is an error. labeled_block = "block" label block_expr . // --- Unsafe Expression --- // See: spec/24-ffi.md § Unsafe Blocks unsafe_expr = "unsafe" block_expr . // --- Embed Expressions --- // See: 11-built-in-functions.md § embed, § has_embed // See: proposals/approved/embed-expression-proposal.md embed_expr = "embed" "(" expression ")" . has_embed_expr = "has_embed" "(" expression ")" . // List literals with spread support // See: 09-expressions.md § Spread Operator list_literal = "[" [ list_element { "," list_element } ] "]" . list_element = "..." expression | expression . // Map literals with spread support // See: computed-map-keys-proposal.md for key semantics map_literal = "{" [ map_element { "," map_element } ] "}" . map_element = "..." expression | map_entry . map_entry = map_key ":" expression . map_key = "[" expression "]" /* computed key: evaluates expression */ | identifier /* literal string key: "foo" from foo */ | string_literal . /* literal string key */ // Struct literals with spread support struct_literal = type_path "{" [ struct_element { "," struct_element } ] "}" . struct_element = "..." expression | field_init . field_init = identifier [ ":" expression ] . // --- Postfix Expressions --- // See: 09-expressions.md § Postfix Expressions postfix_expr = primary { postfix_op } . postfix_op = "." member_name [ call_args ] /* field/method access */ | "." match_method /* method-style match */ | "[" expression "]" /* index access */ | call_args /* function call */ | "?" /* error propagation */ | "as" type /* infallible type conversion */ | "as?" type . /* fallible type conversion */ member_name = identifier | keyword | int_literal . /* keywords/ints valid after "." (tuple: t.0) */ match_method = "match" "(" match_arm { "," match_arm } ")" . call_args = "(" [ call_arg { "," call_arg } ] ")" . call_arg = named_arg | positional_arg | spread_arg . named_arg = identifier ":" [ expression ] . /* punned when expression omitted: f(x:) = f(x: x) */ positional_arg = expression . spread_arg = "..." expression . /* spread into variadic position only */ // --- Unary Expressions --- // See: 09-expressions.md § Unary Expressions unary_expr = [ "!" | "-" | "~" ] power_expr . // --- Power Expression --- // See: operator-rules.md § Power // ** binds tighter than unary: -x ** 2 = -(x ** 2) power_expr = postfix_expr [ "**" power_expr ] . /* right-associative */ // --- Binary Expressions --- // See: 09-expressions.md § Binary Expressions // Precedence (lowest to highest): |>, ??, ||, &&, |, ^, &, ==, cmp, range, shift, add, mul, unary, power binary_expr = pipe_expr . pipe_expr = coalesce_expr { "|>" pipe_step } . pipe_step = "." member_name [ call_args ] /* method on piped value */ | postfix_expr [ call_args ] /* function call with implicit fill */ | lambda . /* expression-level operation */ coalesce_expr = or_expr [ "??" coalesce_expr ] . /* right-associative */ or_expr = and_expr { "||" and_expr } . and_expr = bit_or_expr { "&&" bit_or_expr } . bit_or_expr = bit_xor_expr { "|" bit_xor_expr } . bit_xor_expr = bit_and_expr { "^" bit_and_expr } . bit_and_expr = eq_expr { "&" eq_expr } . eq_expr = cmp_expr { ( "==" | "!=" ) cmp_expr } . cmp_expr = range_expr { ( "<" | ">" | "<=" | ">=" ) range_expr } . range_expr = shift_expr [ ( ".." | "..=" ) [ shift_expr ] [ "by" shift_expr ] ] . shift_expr = add_expr { ( "<<" | ">>" ) add_expr } . add_expr = mul_expr { ( "+" | "-" ) mul_expr } . mul_expr = unary_expr { ( "*" | "/" | "%" | "div" | "@" ) unary_expr } . // --- With Expression --- // See: 09-expressions.md § With Expression, 14-capabilities.md § Providing Capabilities with_expr = "with" capability_binding { "," capability_binding } "in" expression . capability_binding = identifier "=" ( handler_expr | expression ) . // --- Handler Expression (Stateful Effect Handlers) --- // See: 14-capabilities.md § Stateful Handlers, proposals/approved/stateful-mock-testing-proposal.md // `handler` is context-sensitive: valid only in the expression position of a capability_binding. handler_expr = "handler" "(" "state" ":" expression ")" "{" handler_operations "}" . handler_operations = handler_operation { "," handler_operation } . handler_operation = identifier ":" expression . // --- Let Binding --- // See: 09-expressions.md § Let Binding, 05-variables.md let_expr = "let" binding_pattern [ ":" type ] "=" expression . assignment = assignment_target "=" expression | assignment_target compound_op expression . assignment_target = identifier { "[" expression "]" | "." identifier } . compound_op = "+=" | "-=" | "*=" | "/=" | "%=" | "**=" | "@=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "&&=" | "||=" . binding = let_expr | assignment . /* convenience grouping; not currently referenced */ // --- Conditional --- // See: 09-expressions.md § Conditional if_expr = "if" expression "then" expression { "else" "if" expression "then" expression } [ "else" expression ] . // --- For Expression --- // See: 09-expressions.md § For Expression, 19-control-flow.md § Labeled Loops for_expr = "for" [ label ] binding_pattern "in" expression [ "if" expression ] { for_clause } ( "do" | "yield" ) expression . for_clause = "for" binding_pattern "in" expression [ "if" expression ] . // --- Loop Expression --- // See: 09-expressions.md § Loop Expression, 19-control-flow.md § Labeled Loops loop_expr = "loop" [ label ] block_expr . // --- While Expression --- // See: 16-control-flow.md § While Loop // Desugars to: loop { if !condition then break; body } while_expr = "while" [ label ] expression "do" expression . // --- Labels --- // See: 19-control-flow.md § Labeled Loops label = ":" identifier . /* no space around colon */ // --- Lambda --- // See: 09-expressions.md § Lambda lambda = simple_lambda | typed_lambda . simple_lambda = lambda_params "->" expression . typed_lambda = "(" [ typed_param { "," typed_param } ] ")" "->" type "=" expression . lambda_params = identifier | "(" [ identifier { "," identifier } ] ")" . typed_param = identifier ":" type . // --- Control Flow --- // See: 19-control-flow.md break_expr = "break" [ label ] [ expression ] . continue_expr = "continue" [ label ] [ expression ] . // ============================================================================ // PATTERNS (COMPILER CONSTRUCTS) // See: 10-patterns.md // ============================================================================ // --- Pattern Categories --- pattern_expr = function_exp | function_val | channel_expr | for_pattern | catch_expr | nursery_expr . function_exp = pattern_name "(" pattern_arg { "," pattern_arg } ")" . pattern_name = "recurse" | "parallel" | "spawn" | "timeout" | "cache" | "with" . /* nursery has explicit nursery_expr */ pattern_arg = identifier ":" expression . // Type conversion patterns (call position only) function_val = ( "int" | "float" | "str" | "byte" ) "(" expression ")" . // --- Block-Based Expressions --- // See: proposals/approved/block-expression-syntax.md // match: Pattern matching with scrutinee before block match_expr = "match" expression "{" match_arms "}" . match_arms = [ match_arm { "," match_arm } [ "," ] ] . match_arm = match_pattern [ "if" expression ] "->" expression . // try: Error-propagating block (returns early on Err) try_expr = "try" block_expr . // --- Function-Level Contracts --- // See: proposals/approved/block-expression-syntax.md § Function-Level Contracts // Contracts sit between the return type and the "=" in function declarations. contract = pre_contract | post_contract . pre_contract = "pre" "(" check_expr ")" . post_contract = "post" "(" postcheck_expr ")" . check_expr = expression [ "|" string_literal ] . postcheck_expr = lambda_params "->" check_expr . // for pattern: First-match iteration // See: 10-patterns.md § for Pattern for_pattern = "for" "(" for_pattern_args ")" . for_pattern_args = "over" ":" expression "," "match" ":" match_pattern "->" expression "," "default" ":" expression | "over" ":" expression "," "map" ":" expression "," "match" ":" match_pattern "->" expression "," "default" ":" expression . // catch: Panic recovery // See: 20-errors-and-panics.md § Catching Panics catch_expr = "catch" "(" "expr" ":" expression ")" . // nursery: Structured concurrency // See: 10-patterns.md § Concurrency nursery_expr = "nursery" "(" nursery_args ")" . nursery_args = "body" ":" lambda "," "on_error" ":" expression [ "," "timeout" ":" expression ] . // --- Channel Constructors --- // See: 06-types.md § Channel Types channel_expr = channel_constructor "(" "buffer" ":" expression ")" . channel_constructor = "channel" [ type_args ] | "channel_in" [ type_args ] | "channel_out" [ type_args ] | "channel_all" [ type_args ] . // --- Match Patterns --- // See: 10-patterns.md § Match Patterns match_pattern = literal_pattern | identifier_pattern | wildcard_pattern | variant_pattern | struct_pattern | tuple_pattern | list_pattern | range_pattern | or_pattern | at_pattern . literal_pattern = [ "-" ] int_literal | string_literal | char_literal | byte_literal | bool_literal . identifier_pattern = identifier . wildcard_pattern = "_" . variant_pattern = type_path [ "(" [ variant_field { "," variant_field } ] ")" ] . variant_field = identifier ":" [ match_pattern ] /* named; punned if pattern omitted */ | match_pattern . /* positional */ struct_pattern = [ type_path ] "{" [ field_pattern { "," field_pattern } ] [ ".." ] "}" . field_pattern = identifier [ ":" match_pattern ] . tuple_pattern = "(" [ match_pattern { "," match_pattern } ] ")" . list_pattern = "[" [ list_pattern_elems ] "]" . list_pattern_elems = match_pattern { "," match_pattern } [ "," ".." [ identifier ] ] | ".." [ identifier ] . range_pattern = const_pattern ( ".." | "..=" ) const_pattern . const_pattern = literal_pattern | "$" identifier . or_pattern = match_pattern "|" match_pattern . at_pattern = identifier "@" match_pattern . // --- Binding Patterns --- // See: 05-variables.md § Destructuring // The "$" prefix marks immutable bindings; without "$", bindings are mutable. binding_pattern = [ "$" ] identifier | "_" | "{" [ field_binding { "," field_binding } ] "}" | "(" [ binding_pattern { "," binding_pattern } ] ")" | "[" [ binding_pattern { "," binding_pattern } [ ".." [ "$" ] identifier ] ] "]" . field_binding = [ "$" ] identifier [ ":" binding_pattern ] . // ============================================================================ // CONSTANT EXPRESSIONS // See: 04-constants.md, 21-constant-expressions.md // ============================================================================ const_expr = literal | "$" identifier | const_expr ( arith_op | comp_op | "&&" | "||" | "&" | "|" | "^" | "<<" | ">>" ) const_expr | unary_op const_expr | "(" const_expr ")" . // ============================================================================ // PROGRAM ENTRY // See: 18-program-execution.md // ============================================================================ main_function = "@main" main_params "->" main_return "=" expression [ ";" ] . main_params = "()" | "(" "args" ":" "[" "str" "]" ")" . main_return = "void" | "int" | "Never" . // ============================================================================ // END OF GRAMMAR // ============================================================================ ``` --- --- title: "Operator Rules" description: "Ori Language Specification — Operator typing and evaluation rules" order: 101 section: "Annexes" --- # Operator Rules Formal typing and evaluation rules for Ori operators. ## Legend ``` NOTATION ──────── T, U, E type variables v, v1, v2 values e, e1, e2 expressions env type environment |- "entails" (type judgment) -> type-level transformation => evaluation (reduces to) ───── inference rule separator (premises above, conclusion below) [cond] side condition x type product (left x right) TYPE RULES ────────── premise1 premise2 ──────────────────── RULE-NAME conclusion READ AS: "if premise1 and premise2 hold, then conclusion holds" EVALUATION RULES ──────────────── pattern => result [condition] READ AS: "pattern evaluates to result when condition holds" ASSOCIATIVITY ───────────── assoc=left left-to-right grouping: a op b op c = (a op b) op c assoc=right right-to-left grouping: a op b op c = a op (b op c) MAINTENANCE ─────────── - Add new operators: copy existing block, modify rules - Modify behavior: update relevant => rules - Type changes: update -> rules and inference rules - Keep in sync with: grammar.ebnf, ori_types/src/infer/expr/operators.rs, ori_eval/src/interpreter/mod.rs ``` --- ## Pipe `|>` ``` assoc=left prec=16 (lowest binary) TYPE RULES ────────── e1 : T f : (P: T, ...) -> U [P is single unspecified param of f] ───────────────────────────────────────────────────────────────────── PIPE-FILL e1 |> f(...) : U e1 : T method : (self: T, ...) -> U ────────────────────────────────────── PIPE-METHOD e1 |> .method(...) : U e1 : T g : (T) -> U ──────────────────────── PIPE-LAMBDA e1 |> g : U UNSPECIFIED PARAMETER ───────────────────── A parameter is "unspecified" when: (a) not provided in the call arguments, AND (b) has no default value Parameters with defaults are treated as filled for pipe resolution. COMPILE ERRORS ────────────── Zero unspecified params => "all parameters already specified; nothing for pipe to fill" 2+ unspecified params => "ambiguous pipe target; specify all parameters except one" DESUGARING ────────── e1 |> f(a: v) => { let $__pipe = e1; f(: __pipe, a: v) } e1 |> .method(a: v) => { let $__pipe = e1; __pipe.method(a: v) } e1 |> (x -> expr) => { let $__pipe = e1; (x -> expr)(__pipe) } e1 |> f(a: v)? => { let $__pipe = e1; f(: __pipe, a: v)? } LEFT-TO-RIGHT: a |> f |> g |> h = h(g(f(a))) ``` --- ## Coalesce `??` ``` assoc=right prec=15 TYPE RULES ────────── e1 : Option e2 : T ──────────────────────── COALESCE-UNWRAP e1 ?? e2 : T e1 : Option e2 : Option ──────────────────────────────── COALESCE-CHAIN e1 ?? e2 : Option e1 : Result e2 : T ────────────────────────── COALESCE-RESULT-UNWRAP e1 ?? e2 : T e1 : Result e2 : Result ──────────────────────────────────── COALESCE-RESULT-CHAIN e1 ?? e2 : Result e1 : Never e2 : T [Never unifies with Option] ──────────────────────────────────────────────────── COALESCE-NEVER-LEFT e1 ?? e2 : T e1 : Option e2 : Never ──────────────────────────── COALESCE-NEVER-RIGHT e1 ?? e2 : T EVALUATION ────────── Some(v) ?? e2 => v [type(e1) != type(e1 ?? e2)] Some(v) ?? e2 => Some(v) [type(e1) = type(e1 ?? e2)] None ?? e2 => eval(e2) Ok(v) ?? e2 => v [type(e1) != type(e1 ?? e2)] Ok(v) ?? e2 => Ok(v) [type(e1) = type(e1 ?? e2)] Err(_) ?? e2 => eval(e2) SHORT-CIRCUIT: e2 not evaluated when e1 is Some/Ok ``` --- ## Arithmetic `+` `-` `*` `/` `%` `div` ``` assoc=left prec=4 (* / % div @), prec=5 (+ -) TYPE RULES ────────── e1 : int e2 : int ──────────────────── ARITH-INT e1 op e2 : int e1 : float e2 : float ──────────────────────── ARITH-FLOAT e1 op e2 : float e1 : str e2 : str ──────────────────── CONCAT e1 + e2 : str e1 : Duration e2 : Duration ────────────────────────────── DURATION-ADD-SUB e1 +|- e2 : Duration e1 : Duration e2 : int ───────────────────────── DURATION-MUL-DIV e1 *|/ e2 : Duration e1 : int e2 : Duration ───────────────────────── DURATION-MUL-REV e1 * e2 : Duration e1 : Duration e2 : Duration ────────────────────────────── DURATION-MOD e1 % e2 : Duration e1 : Size e2 : Size ────────────────────── SIZE-ADD-SUB e1 +|- e2 : Size e1 : Size e2 : int ───────────────────── SIZE-MUL-DIV e1 *|/ e2 : Size e1 : int e2 : Size ───────────────────── SIZE-MUL-REV e1 * e2 : Size e1 : Size e2 : Size ────────────────────── SIZE-MOD e1 % e2 : Size EVALUATION ────────── n1 + n2 => sum [overflow -> panic] n1 - n2 => diff [overflow -> panic] n1 * n2 => product [overflow -> panic] n1 / n2 => quotient [n2 = 0 -> panic, truncates toward zero] n1 % n2 => remainder [n2 = 0 -> panic] n1 div n2 => floor_quot [n2 = 0 -> panic, floor toward -inf] s1 + s2 => concat ``` --- ## Power `**` ``` assoc=right prec=2 (tighter than unary, looser than postfix) TYPE RULES ────────── e1 : int e2 : int ──────────────────── POW-INT e1 ** e2 : int e1 : float e2 : float ────────────────────────── POW-FLOAT e1 ** e2 : float e1 : float e2 : int ───────────────────────── POW-FLOAT-INT e1 ** e2 : float e1 : int e2 : float ───────────────────────── POW-INT-FLOAT e1 ** e2 : float EVALUATION ────────── n1 ** n2 => int_power [n1 : int, n2 : int, n2 >= 0] n1 ** n2 => panic [n1 : int, n2 : int, n2 < 0, "negative exponent on integer"] n1 ** 0 => 1 [for all n1, including 0 ** 0] f1 ** f2 => libm_pow [delegates to libm pow()] OVERFLOW ──────── int ** int follows standard overflow behavior (panic in debug) TRAIT DISPATCH ────────────── ** -> Pow -> power(self, rhs:) ``` --- ## Comparison `==` `!=` `<` `<=` `>` `>=` ``` assoc=left prec=8 (< <= > >=), prec=9 (== !=) TYPE RULES ────────── e1 : T e2 : T T : Eq ────────────────────────── EQ e1 ==|!= e2 : bool e1 : T e2 : T T : Comparable ────────────────────────────────── ORD e1 <|<=|>|>= e2 : bool EVALUATION ────────── v == v => true v1 == v2 => false [v1 != v2] v != v => false v1 != v2 => true [v1 != v2] v1 < v2 => compare(v1, v2) = Less v1 <= v2 => compare(v1, v2) != Greater v1 > v2 => compare(v1, v2) = Greater v1 >= v2 => compare(v1, v2) != Less ``` --- ## Logical `&&` `||` ``` assoc=left prec=13 (&&), prec=14 (||) TYPE RULES ────────── e1 : bool e2 : bool ────────────────────── AND e1 && e2 : bool e1 : bool e2 : bool ────────────────────── OR e1 || e2 : bool EVALUATION ────────── false && e2 => false [e2 not evaluated] true && e2 => eval(e2) true || e2 => true [e2 not evaluated] false || e2 => eval(e2) ``` --- ## Bitwise `&` `|` `^` `<<` `>>` ``` assoc=left prec=10 (&), prec=11 (^), prec=12 (|), prec=6 (<< >>) TYPE RULES ────────── e1 : int e2 : int ──────────────────────── BITWISE-INT e1 &|^|<<|>> e2 : int e1 : byte e2 : byte ────────────────────────── BITWISE-BYTE e1 &|^ e2 : byte e1 : byte e2 : int ───────────────────────── SHIFT-BYTE e1 <<|>> e2 : byte EVALUATION ────────── n1 & n2 => bitwise_and n1 | n2 => bitwise_or n1 ^ n2 => bitwise_xor n1 << n2 => shift_left [n2 < 0 -> panic, n2 >= width -> panic] n1 >> n2 => shift_right [n2 < 0 -> panic, n2 >= width -> panic] CONSTRAINTS ─────────── int width = 64 bits (valid shift: 0..63) byte width = 8 bits (valid shift: 0..7) ``` --- ## Range `..` `..=` ``` assoc=left prec=7 TYPE RULES ────────── e1 : int e2 : int ──────────────────────── RANGE e1 ..|..= e2 : Range e1 : int ──────────────────── RANGE-OPEN e1 .. : Range e1 : int e2 : int e3 : int ──────────────────────────────── RANGE-STEP e1 ..|..= e2 by e3 : Range ``` --- ## Unary `-` `!` `~` ``` prec=3 (between power and multiplicative) TYPE RULES ────────── e : int ─────────── NEG-INT -e : int e : float ───────────── NEG-FLOAT -e : float e : Duration ──────────────── NEG-DURATION -e : Duration e : bool ──────────── NOT !e : bool e : int ─────────── BITNOT-INT ~e : int e : byte ──────────── BITNOT-BYTE ~e : byte CONSTRAINTS ─────────── -Size -> compile error (Size cannot be negative) ``` --- ## Type Conversion `as` `as?` ``` prec=1 (postfix) TYPE RULES ────────── e : T T converts to U (infallible) ────────────────────────────────────── AS-INFALLIBLE e as U : U e : T T converts to U (fallible) ──────────────────────────────────── AS-FALLIBLE e as? U : Option INFALLIBLE CONVERSIONS ────────────────────── int -> float byte -> int char -> int FALLIBLE CONVERSIONS ──────────────────── str -> int (parse) str -> float (parse) int -> byte (range check) float -> int (truncate, range check) ``` --- ## Try `?` ``` prec=1 (postfix) TYPE RULES ────────── e : Option enclosing returns Option ──────────────────────────────────────────── TRY-OPTION e? : T e : Result enclosing returns Result ──────────────────────────────────────────────── TRY-RESULT e? : T EVALUATION ────────── Some(v)? => v None? => return None Ok(v)? => v Err(e)? => return Err(e) ``` --- ## Never ``` UNIFICATION ─────────── unify(Never, T) = Ok for all T unify(T, Never) = Ok for all T COERCION ──────── e : Never ───────── NEVER-COERCE e : T for all T SOURCES ─────── panic(msg:) : Never todo() : Never todo(reason:) : Never unreachable() : Never unreachable(reason:) : Never break : Never [in loop] continue : Never [in loop] loop { e } : Never [no break in e] e? : Never [early return path when e is None/Err] ``` --- ## Precedence Table ``` PREC OPERATORS ASSOC DESCRIPTION ──── ───────── ───── ─────────── 1 . [] () ? as as? left postfix 2 ** right power 3 ! - ~ right unary 4 * / % div @ left multiplicative 5 + - left additive 6 << >> left shift 7 .. ..= [by] left range (by is step modifier) 8 < > <= >= left comparison 9 == != left equality 10 & left bitwise and 11 ^ left bitwise xor 12 | left bitwise or 13 && left logical and 14 || left logical or 15 ?? RIGHT coalesce ``` --- ## Trait Dispatch ``` OPERATOR -> TRAIT -> METHOD ─────────────────────────── + -> Add -> add(self, other:) - -> Sub -> subtract(self, other:) * -> Mul -> multiply(self, other:) / -> Div -> divide(self, other:) div -> FloorDiv -> floor_divide(self, other:) % -> Rem -> remainder(self, other:) ** -> Pow -> power(self, rhs:) @ -> MatMul -> matrix_multiply(self, rhs:) - -> Neg -> negate(self) ! -> Not -> not(self) ~ -> BitNot -> bit_not(self) & -> BitAnd -> bit_and(self, other:) | -> BitOr -> bit_or(self, other:) ^ -> BitXor -> bit_xor(self, other:) << -> Shl -> shift_left(self, rhs:) >> -> Shr -> shift_right(self, rhs:) == -> Eq -> equals(self, other:) < -> Comparable -> compare(self, other:) RESOLUTION ORDER ──────────────── 1. Primitive type -> direct evaluation 2. User type -> trait method lookup ``` --- ## Compound Assignment ``` DESUGARING ────────── x op= y => x = x op y [parser-level rewrite] SUPPORTED OPERATORS (TRAIT-BASED) ───────────────────────────────── += desugars via Add -= desugars via Sub *= desugars via Mul /= desugars via Div %= desugars via Rem **= desugars via Pow @= desugars via MatMul &= desugars via BitAnd |= desugars via BitOr ^= desugars via BitXor <<= desugars via Shl >>= desugars via Shr SUPPORTED OPERATORS (LOGICAL) ───────────────────────────── &&= desugars to x = x && y [bool-only, short-circuit preserved] ||= desugars to x = x || y [bool-only, short-circuit preserved] CONSTRAINTS ─────────── - Left-hand side shall be a mutable binding (no `$` prefix) - Compound assignment is a statement, not an expression - Target expression is duplicated in AST (pure: no side effects) ``` --- ## Deeper material - [llms-reference.txt](https://ori-lang.com/llms-reference.txt): link catalog — every spec clause, guide chapter, curated example program, and standard-library source - [llms-full.txt](https://ori-lang.com/llms-full.txt): the complete specification, grammar, and example programs inlined into a single document - [GitHub repository](https://github.com/upstat-io/ori-lang) | [Playground](https://ori-lang.com/playground) | [Roadmap](https://ori-lang.com/roadmap/) - Specification source: https://raw.githubusercontent.com/upstat-io/ori-lang/master/docs/ori_lang/v2026/spec/