This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.
JSON
import std/jsonstd/json - JSON encoding and decoding
json.kz · 11 tors
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.
JsonObject 1 state building!string 2 states emitted!// Object Builder - Create JSON objects
~pub tor object.new { allocator: ?std.mem.Allocator } -> *JsonObject<building!>~pub tor object.set {
obj: *JsonObject<building>,
key: string,
value: JsonValue
} -> *JsonObject<building>~pub tor object.finish { obj: *JsonObject<!building> } -> string// Array Builder - Create JSON arrays
~pub tor array.new { allocator: ?std.mem.Allocator } -> *JsonObject<building!>~pub tor array.push {
arr: *JsonObject<building>,
value: JsonValue
} -> *JsonObject<building>~pub tor array.finish { arr: *JsonObject<!building> } -> string// Decoding - Extract values from JSON strings
~pub tor get.string { json: string<*>, key: string }
| ok string
| not-foundget.int
koru_std/json.kz:212~pub tor get.int { json: string, key: string }
| ok i64
| not-found~part parse koru_std/json.parse.kz
Koru Standard Library: the `parse` part of json.kz — schema-directed compile-time JSON parsing as control flow. Joined via `~part parse` in json.kz; merged into the json module at load. The `ok` branch's destructure IS the schema — the same license std/regex:match takes with named groups (the destructure names the payload; a type on a field is a conversion at the splice, and `sub` — meaningless for a text span — is a nested object here): std/json:parse(input: body) | ok { name: string, age: i64, addr: { city: string } } |> greet(name) | malformed { message, offset } |> report(message, offset) Field grammar: a bare leaf (`name`) delivers the value's raw JSON span; `string` delivers the decoded inner text (escapes resolved); `bool`, the int types, and the float types convert at the splice; `?T` admits a missing key or a literal null; `[]T` is an array of T (T may itself be `?`/`[]`/`{...}` — `{...}` inside type_text is a nested object shape); `name: { ... }` is a nested object. Undeclared keys are skipped (JSON convention — strict both-ways is a pinned follow-up); duplicate keys take the last; every declared required key must appear or the parse fails. Ownership is SCOPED: allocations (decoded strings, arrays) land on a per-site arena that is deinit'd when the branch's chain exits — field values are valid inside the branch and die with it (the same contract yyjson's `close` draws, enforced by scope instead of a phantom). A produced value that retains a field slice is dangling — hand long-lived data an owned copy before it leaves the branch. The transform compiles that schema into a bespoke recursive-descent parser: one generated fn per object shape, typed readers per field, a generic value-skipper for undeclared keys, and a dispatch that hands the assembled payload struct to the `ok` body or routes failure to `malformed`. No DOM is materialized — fields land as typed values in the branch. This is the direct competitor of a navigation API (yyjson's parse→root→get→as.* chain): schema known at compile time means the tree is never built. Keys are compared raw — an escape inside a JSON key does not match its identifier yet (cut 2). A destructure-less `| ok v` is recognizer mode: the input must be exactly one JSON value, and the binding carries the consumed span.
~[comptime|transform] pub tor parse {
input: string<*>,
reporter: std/compiler:*ErrorReporter
}
| ok *
| ?malformed { message: string, offset: usize }~part emit koru_std/json.emit.kz
Koru Standard Library: the `emit` part of json.kz — schema-directed compile-time JSON emission as control flow. Joined via `~part emit` in json.kz; merged into the json module at load. `std/json:emit` is the write-side twin of `std/json:parse`: the record literal at the `value:` position IS the output schema — keys are the JSON key names, and each value expression's type is resolved at compile time from the bindings in scope at the site: std/json:emit(value: { service: service, port: port, tls: tls }) | ok body |> send(body) |> std/json:emit.free(text: body) A bare identifier `emit(value: cfg)` emits every field of a branch-payload record bound upstream (`| ok cfg` on a tor whose payload declares named fields). Value grammar: a binding name resolved through scopeAtSite (destructure fields, `| branch name` payload binds, `: name` return binds, impl input fields), a `binding.field` path into a record payload, a nested `{...}` record literal, or a string/number/bool/null literal. Supported field types: `string` (escaped at the splice), `bool`, the int types, the float types (non-finite emits `null` — JSON cannot spell inf/nan), `?T` (null or inner), `[]T`. A bare destructure leaf has no type — it cannot be resolved and is refused with a teaching error. Ownership: `ok` mints `string<emitted!>`, discharged ONLY by `std/json:emit.free` — the same contract as yyjson's `render`/`render.free`, one allocation, koru_allocator-owned, no arena. The output is independent of every input binding's lifetime. The transform emits no DOM and no builder tree: a per-site escaping writer, a number writer, and an inline byte-append sequence specialized to the resolved schema. The tor decl declares the USER surface: `emit(...): body` binds a string carrying <emitted!>, discharged by `emit.free`. The transform proc returns SiteResult internally — the decl's return is what the call site produces.
// The tor decl declares the USER surface: `emit(...): body` binds a string
// carrying <emitted!>, discharged by `emit.free`. The transform proc returns
// SiteResult internally — the decl's return is what the call site produces.
~[comptime|transform] pub tor emit {
value: string,
reporter: std/compiler:*ErrorReporter
} -> string<emitted!>// Discharge the `<emitted!>` obligation — the emitted text is owned by
// koru_allocator(), so free is the matching call. Same contract as
// yyjson's render.free.
~pub tor emit.free { text: string<!emitted> }~part parsehelpers koru_std/json.parsehelpers.kz
Koru Standard Library: JSON — parse helpers. Joined via `~part helpers` in json.parse.kz; merged into the json.parse module at load. Holds the transform's helper container so the driver stays under the line wall.
~part parsesub koru_std/json.parsesub.kz
Koru Standard Library: JSON parse — sub-entry expansion. Joined via `~part sub` in json.parse.kz; merged into the json.parse module at load. A `{...}` spec written inline in a plain-brace schema arrives as spec text, not a sub-destructure; expanding it here binds the object's LEAVES (`addr:{ city }` binds `city`, not `addr`) — the same shape the sub-destructure form produces.