This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.

Interpreter

import std/interpreter

Koru Interpreter Standard Library

interpreter.kz · 6 tors · ~[runtime]

Koru Interpreter Standard Library · 21 more lines
Koru Interpreter Standard Library Enables runtime evaluation of Koru source code — DESIGN: - Parses Koru source at runtime using the same parser as the compiler - Walks the AST and executes tors via registered dispatchers - Tracks bindings in an environment; validates flows before execution Usage: import std/interpreter import std/runtime // Register tors you want callable at runtime std/runtime:register(scope: "api") { greet process_order } // Interpret Koru source std/interpreter:run(source: user_input, scope: "api") | result r |> handle_result(value: r.value) | parse-error e |> respond(status: 400, body: e.message) | validation-error e |> respond(status: 400, body: e.message) | dispatch-error e |> respond(status: 500, body: e.tor)

Phantom lifecycles

Derived from the phantom labels in the declarations below — state! issues an obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.

string 1 state allocated!

value.stringify

retain <std.io:allocated!> koru_std/interpreter.kz:328

~part args koru_std/interpreter.args.kz

Koru Standard Library: the `args` part of interpreter.kz — the arg judgment: field membership and bound references, one walk over the flow before it runs. Joined via `~part args` in interpreter.kz; merged into the interpreter module at load. The walk itself moved to the `flowchecks` part — the field-membership and bound-reference rationale moved with it.

~part argref koru_std/interpreter.argref.kz

Koru Standard Library: the `argref` part of interpreter.kz — the bare-reference judgment used by the arg check: which raw arg values are references to bound names. Joined via `~part argref` in interpreter.kz; merged into the interpreter module at load. `std` arrives at module scope from the other parts — a local import here would shadow it. The name a bare arg value references, or null when the value is a literal. A quoted value is a string literal; `true`/`false`/`null` are literals. Text that is not a lone identifier is expression text — `wireExprRefName` names the first unbound ident inside it.

~part arith koru_std/interpreter.arith.kz

Koru Standard Library: the `arith` part of interpreter.kz — the numeric binary-op kernels behind evaluateExpression's arithmetic. Joined via `~part arith` in interpreter.kz; merged into the interpreter module at load. `ExprValue`/`ExprArithOp`/`ExprEvalError` arrive at module scope from the root file.

~part arms koru_std/interpreter.arms.kz

Koru Standard Library: the `arms` part of interpreter.kz — the arm judgment: which siblings can ever fire. Joined via `~part arms` in interpreter.kz; merged into the interpreter module at load. ARM REACHABILITY — a second unguarded handler for a branch an earlier sibling already answers is dead text Selection is first-match (the `select` part): on an outcome the first unguarded arm naming it fires, and every later same-name sibling can never run — dead text a writer believes executed. Compiled flows refuse the shape as SHAPE002 (branch_checker's `firstDuplicateSibling` is the single source of truth this mirrors); an interpreted flow now meets the same judge. `when` narrows rather than duplicates, `!` links compose (every link fires during the tor), `|?` is the fallback — none of them shadow. A transformed subtree's synthesized children are exempt — the same carve-out the compiled checkers take. The message teaches the spelling the writer usually meant: an arm that follows a STEP is indented under that step's line — flat `|` lines are siblings of the head call.

~part checks koru_std/interpreter.checks.kz

Koru Standard Library: the `checks` part of interpreter — the flow-check internals. Joined via `~part checks` in interpreter.kz; merged into the interpreter module at load. checkInvocationArgs moved to the `invargs` part — the argument-vocabulary walk beside the seed helpers below. `name` enters the bound set unless an outer scope already bound it — then it stays bound (and the duplicate is the shadowing check's call).

~part contracts koru_std/interpreter.contracts.kz

Koru Standard Library: the `contracts` part of interpreter — the dispatcher and obligation-spec type surface. Joined via `~part contracts` in interpreter.kz; merged into the interpreter module at load. Everything here is declaration-level types: the host-dispatch contract (HostView / HostDispatchFn / the dispatch-detail channel), the fn-ptr vocabulary the runtime generates dispatchers against, and the obligation CreateSpec/DischargeSpec specs. DISPATCHER TYPE - Function signature for scope dispatchers

~part define koru_std/interpreter.define.kz

Koru Standard Library: the `define` part of interpreter.kz. Joined via `~part define` in interpreter.kz; merged into the interpreter module at load. The REPL define: install a subflow declaration into the session's defined-flows table, making it a verb callable on subsequent runs. A definition is `tor name { args }` followed by the impl flow `name = <body>`. The body is stored as SOURCE (durable) and re-parsed per dispatch; every call it makes runs through the same possession machinery, so a defined flow can never reach beyond what the session holds. The definition-time analyses (declared obligations must match the body's real behavior; every call must be within the session's scopes) are the richer guarantee and are rungs of the stance, not this slice; call-time possession enforcement is the floor and is already structural.

~part expr koru_std/interpreter.expr.kz

Koru Standard Library: the `expr` part of interpreter — the runtime expression evaluator: ExprValue and its comparison/arithmetic ops (guards evaluate through this, distinct from the dispatch Value union) plus evaluateExpr, the env-facing entry the `eval` verb calls. Joined via `~part expr` in interpreter.kz; merged into the interpreter module at load. EXPRESSION VALUE - For runtime expression evaluation (different from Value)

~part fieldvalue koru_std/interpreter.fieldvalue.kz

Koru Standard Library: the `fieldvalue` part of interpreter.kz — the composite FieldValue helpers. Joined via `~part fieldvalue` in interpreter.kz; merged into the interpreter module at load. FieldValue's first composite is `string_array`: these are its three verbs — JSON render (Value serialization + interpolation), JSON render for expr bindings, and the deep copy that carries a []const []const u8 out of a dispatcher frame into interpreter/session memory. Scalars need none of this; the array pays the composite tax here, once. JSON-escape one string into a writer — shared by string_val and the per-element render of string_array.

~part guards koru_std/interpreter.guards.kz

Koru Standard Library: the `guards` part of interpreter.kz — `when` clauses on dispatch arms. Joined via `~part guards` in interpreter.kz; merged into the interpreter module at load. Compiled emission lowers same-branch armed continuations into an if/else-if/else chain: arms try in source order, the first truthy guard wins, an unguarded arm is the else. The interpreter mirrors that — a guard is an expression over the arm's OWN binding, which does not exist until selection, so `armGuardPasses` evaluates against a scratch scope provisionally holding the payload. A false guard moves selection to the next name-matching candidate; it never fires the arm anyway. A guard that cannot be judged (won't parse, names nothing bound) is GuardUnjudgable — surfaced as a dispatch-error, never silently true or silently false. (440_024; kopium bridge-mirror 2026-09-13-arm-when-guards)

~part interp koru_std/interpreter.interp.kz

Koru Standard Library: the `interp` part of interpreter — `{{ name }}` / `{{ name.field }}` splicing inside wire string literals, resolved against the session's bindings. Joined via `~part interp` in interpreter.kz; merged into the interpreter module at load. `{{ name }}` / `{{ name.field }}` inside a wire string splices a bound value into the text — the composition channel that lets one reply carry a call's result into prose ("booked {{ b.reservation_id }}") instead of spending a second turn to quote it. A reference that resolves substitutes; a name nobody bound refuses loudly (an empty placeholder shipped as data is the dishonest version of that error). `{{` that does not open a dotted identifier stays literal, so prose may carry braces.

~part names koru_std/interpreter.names.kz

Koru Standard Library: the `names` part of interpreter.kz — name identity across the authored/mangled boundary. Joined via `~part names`. Koru names compare in normalized space: `-` and `_` are the same character (ast_mangle folds both to `_` at parse). Interpreter-parsed source keeps the author's spelling (`| bad-amount`), while a dispatch outcome or registry spec carries the mangled form (`bad_amount`) — raw eql can never join them, and every comparison across that boundary owes this fold.

~part parse koru_std/interpreter.parse.kz

Koru Standard Library: the `parse` part of interpreter.kz. Joined via `~part parse` in interpreter.kz; merged into the interpreter module at load. Parse WITHOUT executing — the other half of `eval`. Interpreter source is pure Koru: it has no file, so it has no host half and no ``. That is why this takes no file name. The full compiler parser derives its host/pure mode from the filename extension (`src/parser.zig:707`), so handing it a synthetic name silently answers a question interpreted source does not have; `flow_parser` has no such notion and is the only correct parser here. The returned Flow and every slice it points at are allocated from `allocator` — the caller owns the AST and may `eval` it as many times as it likes.

~part pool koru_std/interpreter.pool.kz

Koru Standard Library: the `pool` part of interpreter.kz — the handle record. Joined via `~part pool` in interpreter.kz; merged into the interpreter module at load. HandlePool is the custody organ: every `acquire` mints, every discharge releases. When a world-mounted bridge installs `on_custody`, each of those events is witnessed into the world's holds.log — the record a successor session reads before it acts. Pool callbacks are null by default: a bare interpreter run records nothing, and nothing here knows a world exists. Handle, CustodyKind, custodySubject and renderHeldHandles moved to the `custody` part — the record types and witnesses beside the pool that mints them.

~part mint koru_std/interpreter.mint.kz

Koru Standard Library: the `mint` part of interpreter.kz — the opaque resource-token mint for HandlePool. Joined via `~part mint` in interpreter.kz; merged into the interpreter module at load. Split from the pool part under the line wall. The ruling (OPAQUE MINT — Lars 2026-09-07/08, the notes mint's rule carried to resource tokens 2026-10-20): token text must not be predictable, because a countable suffix lets a program spell custody it was never handed — `h:Res:1` used to be a resolvable guess. `h:Type:` stays for legibility; the suffix is seeded six-hex. Mint the token a `*T` return rides home on — opaque and pool-unique, carrying the resource's type name for legibility ("h:Connection:a3f9c2"). Called only from `HandlePool.mintResourceToken`. Token bytes live on the page allocator — the same never-freed tolerance __koru_own already documents — because a result field aliasing the token may still be rendered after its handle discharges.

~part run koru_std/interpreter.run.kz

Koru Standard Library: the `run` part of interpreter.kz — the main interpreter tors (run / run-cached / eval) and the flow-execution core. Joined via `~part run` in interpreter.kz; merged into the interpreter module at load. MAIN INTERPRETER EVENTS Parse and execute in one step - the main interpreter entry point Use get-scope to get the dispatcher first, then pass it here

~part refuse koru_std/interpreter.refuse.kz

Koru Standard Library: the `refuse` part of interpreter.run — the post-parse gates that reject source which parsed but must never dispatch. Joined via `~part refuse` in interpreter.run.kz; merged into the run part at load. Two refusals share one position — after either parse path, before the scope lookup, before dispatch: prose-shaped invocation names (430_055) and `|*` residual arms (440_020). Both answer `parse-error` from `run` and `validation-error` from `eval`. Event-name prose check: an tor name must lex as identifier segments with `.`/`-`/`:` between them, never spaces. The fast parser already rejects prose, but the full-parser fallback re-accepts an English sentence whose first `(` makes it look like an invocation.

~part runcached koru_std/interpreter.runcached.kz

Koru Standard Library: the `runcached` part of interpreter — the cached fast path. Joined via `~part runcached` in interpreter.kz; merged into the interpreter module at load. `run-cached` reuses a parsed flow for identical source input — the same contract as `run`, minus the parse.

~part select koru_std/interpreter.select.kz

Koru Standard Library: the `select` part of interpreter.kz — picking the arm a dispatched outcome runs. Joined via `~part select` in interpreter.kz. There is no catch-all: an arm names a declared branch or the outcome goes unhandled. A same-branch `when` guard gates the arm and is judged in source order — the machinery lives in the `guards` part. (kopium bridge-mirror 2026-09-12-branch-arms, 2026-09-13-arm-when-guards)

~part literal koru_std/interpreter.literal.kz

Koru Standard Library: the `literal` part of interpreter.kz — literal materialization on the wire path: what the quoted text between the quotes MEANS when it becomes a value. Joined via `~part literal` in interpreter.kz; merged into the interpreter module at load. Unescape a string literal's inner text (between the quotes). The wire is line-oriented — an agent cannot put a raw newline inside a quoted arg, so escapes are the ONLY way to carry one; before this, `\n` in a wire string materialized as backslash-n (measured live: a model's multi-line edit() wrote literal "\n" into suite.k, kopium libs-workbench 2026-09-25). Codegen never sees this — its strings are re-emitted into Zig literals, which unescape downstream. Unknown escapes pass through untouched: \q stays backslash-q, not q.

~part validate koru_std/interpreter.validate.kz

Koru Standard Library: the `validate` part of interpreter.kz — pre-execution flow validation. Joined via `~part validate` in interpreter.kz; merged into the interpreter module at load. ValidationError and validateFlow moved to the `flowchecks` part — the pre-execution judgment sits beside the arg walk it calls; the recursive shadow walk it consults stays in the `checks` part.

~part trace koru_std/interpreter.trace.kz

Koru Standard Library: the `trace` part of interpreter.kz — the run's dispatch trace and the durable naming a failure report needs. Joined via `~part trace` in interpreter.kz; merged into the interpreter module at load. One dispatched call's outcome in a run's trace — a chain's middle is invisible to the model unless each link's issued value is named and shown. `bound` is the durable session name (rN) when the host supplied a binder; `arm` is the `|` arm's binding name when one armed it.

~part handles koru_std/interpreter.handles.kz

Koru Standard Library: the `handles` part of interpreter.kz — the handle-pool custody cluster: the discharge sweeps and the held-handles report. Joined via `~part handles` in interpreter.kz; merged into the interpreter module at load. renderHeldHandles moved to the `custody` part — the record's witnesses live beside Handle/CustodyKind now. assertHandlesHeld moved to the `mounted` part — the held gate consults the verb's owner, and owners are a scope-set question now.

~part flowsrc koru_std/interpreter.flowsrc.kz

Koru Standard Library: the `flowsrc` part of interpreter.kz — how a run gets a parsed Flow: the per-thread parse cache, and the full-parser fallback for source the fast path declines. Joined via `~part flowsrc` in interpreter.kz; merged into the interpreter module at load.

~part deftable koru_std/interpreter.deftable.kz

Koru Standard Library: the `deftable` part of interpreter.kz — the defined-flows table itself: the record, the durable map that owns it, and the install path `define` calls. Joined via `~part deftable` in interpreter.kz; merged into the interpreter module at load. A flow the session defined at runtime (std/runtime:define): its name, its signature parameters, its declared outcome contract, and its body SOURCE. The body is re-parsed per dispatch — cheap, and the definition stays durable text rather than a cloned AST that dies with the turn's arena. Every call the body makes runs through the same possession/obligation machinery as any other dispatch, so a defined flow can never reach beyond what the session holds.

~part defined koru_std/interpreter.defined.kz

Koru Standard Library: the `defined` part of interpreter.kz. Joined via `~part defined` in interpreter.kz; merged into the interpreter module at load. The defined-flow dispatch machinery — MAX_DEFINED_DEPTH, findDefinedFlow, dispatchDefinedFlow — moved to the `defflow` part; its recursion-cap rationale moved with it.

~part wireexpr koru_std/interpreter.wireexpr.kz

Koru Standard Library: the `wireexpr` part of interpreter.kz — the whole-expression fallback for arg values. Joined via `~part wireexpr` in interpreter.kz; merged into the interpreter module at load. evaluateExpr resolves quoted strings, bound names, and lone field paths first; what remains is judged here as one Koru expression — `2 + 3`, `n * 2`, `r3.total > 50` — evaluated against expr_bindings, the same scope `when` guards see. Text that is not an expression at all returns null so the caller keeps its verbatim fallback (non-wire callers feed evaluateExpr raw shapes the wire never admits); text that parses as an expression but cannot evaluate — a name nothing bound — refuses loudly.

~part resdispatch koru_std/interpreter.resdispatch.kz

Koru Standard Library: the `resdispatch` part of interpreter — the thread-local channels a generated dispatcher speaks through. Joined via `~part resdispatch` in interpreter.kz; merged into the interpreter module at load. A generated dispatcher lives in the user module and cannot reach InterpreterContext or the HandlePool — these slots are the channel: dispatch-detail carries the typed-arg failure it detects, dispatch-pool carries the pool that `*T` handle args resolve against and `*T` returns mint into. Dispatch-detail channel: a typed-arg failure the dispatcher detects (e.g. an i64 field handed 'abc') reports through this slot instead of degrading to a silent default. Cleared at the top of every run; read when a dispatch actually throws, folded into error_detail.

~part custody koru_std/interpreter.custody.kz

Koru Standard Library: the `custody` part of interpreter.kz — the handle record's types and witnesses. Joined via `~part custody` in interpreter.kz; merged into the interpreter module at load. The Handle is what a HandlePool mints and discharges; CustodyKind is what the custody log calls those acts; custodySubject renders WHAT the minting verb took, renderHeldHandles WHO still owes.

~part invargs koru_std/interpreter.invargs.kz

Koru Standard Library: the `invargs` part of interpreter — the argument-vocabulary walk. Joined via `~part invargs` in interpreter.kz; merged into the interpreter module at load. checkInvocationArgs judges each invocation's named fields against the verb's declared input vocabulary, and each arm word against its declared outcome vocabulary — the checks called by checkFlowArgs in the args part. Owner resolution (bound scope, then the run's mounted set) lives in the `mounted` part.

~part mounted koru_std/interpreter.mounted.kz

Koru Standard Library: the `mounted` part of interpreter — the runtime-installed scope lane. Joined via `~part mounted` in interpreter.kz; merged into the interpreter module at load. A scope mounted by `std/bridge:mount` is a `.dylib` whose `koru_module_init` handed the host a ScopeRuntime — the same vtable a register block emits at compile time. Every check a run performs on a verb then answers to the installed scope SET (bound first, then mounted), not the bound scope alone: this part holds the channel the set rides in on, the host-service ABI a module stores at init, the owner resolution every check consults, and the checks themselves — held-possession, the dispatch fallback, the arm-word walk, obligation bookkeeping, and the sweep's discharge-owner search — each moved here because each now resolves an owner the bound scope may not be.

~part flowchecks koru_std/interpreter.flowchecks.kz

Koru Standard Library: the `flowchecks` part of interpreter — the pre-execution judgment: field membership, bound references, shadowing, unreachable arms — one traversal before the flow runs. Joined via `~part flowchecks` in interpreter.kz; merged into the module at load. FIELD MEMBERSHIP — a named field the verb's signature does not declare is refused, never ignored at dispatch. The declared vocabulary comes from the scope's own `get_event_input` table — the check is derived from the same registry data the prompt and dispatcher are emitted from, so it cannot drift from what dispatch enforces. `input_fn` answers null for a name outside the scope: vocabulary membership is a different question (event-denied at dispatch), and an empty list is a real answer — a zero-field verb refuses any named field. A session-defined flow's params are its own surface and are not judged against the scope manifest; calls nested in its arms still are. BOUND REFERENCES — a bare identifier in arg position is a reference, never a literal. Unbound, it must not degrade to its own text (`append(handle: h)` dispatching `handle="h"` fails `HandleNotHeld`, which blames the handle instead of the missing binding). Bindings are per item along an arm path: `| ok h |>` scopes `h` to that arm's subtree and to every later item of the turn — a sibling arm still sees it unbound.

~part defflow koru_std/interpreter.defflow.kz

Koru Standard Library: the `defflow` part of interpreter.kz — the runtime-defined-flow dispatch machinery. Joined via `~part defflow` in interpreter.kz; merged into the interpreter module at load. Runtime-defined-flow DISPATCH — the call half of the REPL define (interpreter.define.kz is the install half). A defined flow's body is stored source, re-parsed and executed inline per dispatch; the table is a live namespace, so a body naming a defined flow — itself included — resolves to the CURRENT body. That makes self-referencing recursion; `run` forwards no budget, so depth is the only bound: MAX_DEFINED_DEPTH answers before nested frames kill the host stack. Pin: 440_042.

~part eval koru_std/interpreter.eval.kz

Koru Standard Library: the `eval` part of interpreter.kz — the fast-path tor: a pre-parsed flow, no parse step. Joined via `~part eval` in interpreter.kz; merged into the interpreter module at load. This is the fast path: no parsing overhead, just execution!

~part env koru_std/interpreter.env.kz

Koru Standard Library: the `env` part of interpreter.kz — the binding table and its seeding. Joined via `~part env` in interpreter.kz; merged into the interpreter module at load. ENVIRONMENT - Tracks bindings during interpretation