This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.
Grid
import std/gridKoru Standard Library: Grid — a fixed, positionally-addressed table
grid.kz · 9 tors
Koru Standard Library: Grid — a fixed, positionally-addressed table · 40 more lines
~part read koru_std/grid.read.kz
Koru Standard Library: the read path of grid.kz — the grid_read namespace (refs, parseTarget, lower/lowerCont/lowerItems, trusted index). Joined via `~part read` in grid.kz; merged into the grid module at load.
~part slice koru_std/grid.slice.kz
Koru Standard Library: the `slice` part of grid.kz. Joined via `~part slice` in grid.kz; merged into the grid module at load. std/grid:slice(name) { grid.field } Borrows a column-layout grid's field array as a raw slice: `const name = __koru_grid_<g>.<f>[0..]` lands next to the grid in the module, so `name[i]` reads splice verbatim through generic indexing and `stored { name[i]: v }` writes lower to a direct `name[i] = v` — the write unit threads the slice through as an argument, the way every write threads its target. THE BORROW IS THE CONTRACT: the grid's per-access __koru_at floor is the thing the program is declining — a raw view buys vectorization-shaped code with the program's own index obligation, and Zig's own slice bounds check still runs in Debug. Pools are refused: a pool column is a heap slice whose extent arrives at alloc — the borrow would only shadow what is already raw.
// Koru Standard Library: the `slice` part of grid.kz.
// Joined via `~part slice` in grid.kz; merged into the grid module at load.
//
// std/grid:slice(name) { grid.field }
//
// Borrows a column-layout grid's field array as a raw slice:
// `const name = __koru_grid_<g>.<f>[0..]` lands next to the grid in the
// module, so `name[i]` reads splice verbatim through generic indexing and
// `stored { name[i]: v }` writes lower to a direct `name[i] = v` — the
// write unit threads the slice through as an argument, the way every
// write threads its target. THE BORROW IS THE CONTRACT: the grid's
// per-access __koru_at floor is the thing the program is declining — a
// raw view buys vectorization-shaped code with the program's own index
// obligation, and Zig's own slice bounds check still runs in Debug.
// Pools are refused: a pool column is a heap slice whose extent arrives
// at alloc — the borrow would only shadow what is already raw.
~[keyword|declaration] pub tor slice { expr: Expression, source: Source }~part readwalk koru_std/grid.readwalk.kz
Koru Standard Library: the continuation-tree walk of grid.kz's read path — lowerCont/lowerContTrusted/lowerItems. Joined via `~part readwalk` in grid.kz; merged into the grid module at load. Split out of grid.read.kz at the read-arm-expr x sweep-trusted-cursor mergeback: the union of both sides' work pushed the file over its line baseline, and the wall does not widen.
~part new koru_std/grid.new.kz
Koru Standard Library: the `new` part of grid.kz. Joined via `~part new` in grid.kz; merged into the grid module at load. GRID.NEW — declare a fixed, positionally-addressed table
// Koru Standard Library: the `new` part of grid.kz.
// Joined via `~part new` in grid.kz; merged into the grid module at load.
//
// GRID.NEW — declare a fixed, positionally-addressed table
~[keyword|comptime|transform] pub tor new {
expr: Expression,
source: Source,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult~part stored koru_std/grid.stored.kz
Koru Standard Library: the `stored` part of grid.kz. Joined via `~part stored` in grid.kz; merged into the grid module at load. There is no write EVENT and no envelope. The store routes every write through one centralizing subflow because that is where reactivity splices; a grid has nothing to splice, so a write is a write and lowers to an assignment.
// Koru Standard Library: the `stored` part of grid.kz.
// Joined via `~part stored` in grid.kz; merged into the grid module at load.
//
// There is no write EVENT and no envelope. The store routes every write through
// one centralizing subflow because that is where reactivity splices; a grid has
// nothing to splice, so a write is a write and lowers to an assignment.
~[keyword|comptime|transform] pub tor stored {
source: Source,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult~part idx koru_std/grid.idx.kz
Koru Standard Library: the `idx` part of grid.kz — index-typed cells. Joined via `~part idx` in grid.kz; merged into the grid module at load. `0[<grid>]` — a field typed by a grid's own name is AN INDEX INTO THAT GRID: `nd: 0[tn]` is "a cell holding an index into tn". The declaration buys three things the bare `[i64]` spelling cannot express: WIDTH — the cell is the narrowest unsigned int covering the target's extent (grid.new emits `__koru_idx_<target>` = IntFittingRange(0, target.__koru_size)), so a `size:` bump re-derives the storage rather than silently overflowing it. CERTIFICATION — the WRITE pays the bounds check once (stored wraps the value in the target's `__koru_at`), so the contents are `< size` by construction. A read of the target through the cell — `tn[que[i].nd]` — is then certified and re-collects no floor. PROVENANCE THAT SURVIVES EMISSION — the const's NAME carries the target (`__koru_idx_tn`), so re-collection (scanLoweredFields) and the decl-site parse (scanFlowFields) recover the same fact from two different surfaces. One encoding, no drift. The arithmetic rule: an index value leaves its type the moment it feeds `+`/`-`/`*` — the emitted cell is a plain int, so `nd + 1` is an int and `tn[nd + 1]` floors again, as it must: the proof was about `nd`, not about arithmetic on it. Storing the result back re-earns the proof through the checked write.
~part sweep koru_std/grid.sweep.kz
Koru Standard Library: the `sweep` part of grid.kz. Joined via `~part sweep` in grid.kz; merged into the grid module at load. keeps the body Koru all the way down — a nested `std/store:stored`, a `print.ln`, a guard, all behave exactly as they do anywhere else, because nothing about them has been moved.
// Koru Standard Library: the `sweep` part of grid.kz.
// Joined via `~part sweep` in grid.kz; merged into the grid module at load.
//
// keeps the body Koru all the way down — a nested `std/store:stored`, a
// `print.ln`, a guard, all behave exactly as they do anywhere else, because
// nothing about them has been moved.
~[keyword|comptime|transform|claims_descendants] pub tor sweep {
expr: Expression,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult~part cursor koru_std/grid.cursor.kz
Koru Standard Library: the minted-cursor convention of grid.kz — the name a grid-owned loop gives the index it has already proven. Joined via `~part cursor` in grid.kz; merged into the grid module at load.
~part for koru_std/grid.for.kz
Koru Standard Library: the `for` part of grid.kz — a bounded loop the GRID owns, so the index's proof is established once, at the door. Joined via `~part for` in grid.kz; merged into the grid module at load. std/grid:for(0..n, cells) ! each i |> ... cells[i] ... An ordinary `for(0..n)` hands the body an index whose bound lives in a VALUE `n`; nothing connects it to the grid it is about to index, so every `cells[i]` pays the `__koru_at` floor — and, more expensively, the panic edge that keeps the vectorizer out of the loop. This construct takes the range AND the grid, checks `lo..hi` against `__koru_size` ONCE before the loop begins, and then mints the binding as a trusted cursor: the same provenance mechanism `sweep` built for its `0..__koru_size` cursor. The panic the check carries is real — a range that outruns the grid fails loud, never clamped, because truncation is the program's to write. The trust is exactly as wide as the proof: the minted name on the named grid. `cells[i+1]` keeps the floor, `other[i]` keeps the floor, and an `i` spelled by a different scope keeps it too — the match is on the raw index text, which is the thing nobody else can spell. The binding is RENAMED to the minted cursor at source level (`i` -> `__koru_gi_<grid>_L<line>`) before the body lowers. That is what admits `stored { cells[i].v: ... }` to the unchecked write path: the write unit names an index trusted when its text is a cursor minted for THAT grid — a name only a grid-owned loop can mint, which is the whole reason for the rename. A body that leaves the binding spelled `i` would read trusted and write checked, an asymmetry with no principle under it.
// Koru Standard Library: the `for` part of grid.kz — a bounded loop the
// GRID owns, so the index's proof is established once, at the door.
// Joined via `~part for` in grid.kz; merged into the grid module at load.
//
// std/grid:for(0..n, cells)
// ! each i |> ... cells[i] ...
//
// An ordinary `for(0..n)` hands the body an index whose bound lives in a
// VALUE `n`; nothing connects it to the grid it is about to index, so every
// `cells[i]` pays the `__koru_at` floor — and, more expensively, the panic
// edge that keeps the vectorizer out of the loop. This construct takes the
// range AND the grid, checks `lo..hi` against `__koru_size` ONCE before the
// loop begins, and then mints the binding as a trusted cursor: the same
// provenance mechanism `sweep` built for its `0..__koru_size` cursor. The
// panic the check carries is real — a range that outruns the grid fails
// loud, never clamped, because truncation is the program's to write.
//
// The trust is exactly as wide as the proof: the minted name on the named
// grid. `cells[i+1]` keeps the floor, `other[i]` keeps the floor, and an
// `i` spelled by a different scope keeps it too — the match is on the raw
// index text, which is the thing nobody else can spell.
//
// The binding is RENAMED to the minted cursor at source level
// (`i` -> `__koru_gi_<grid>_L<line>`) before the body lowers. That is what
// admits `stored { cells[i].v: ... }` to the unchecked write path: the write
// unit names an index trusted when its text is a cursor minted for THAT grid
// — a name only a grid-owned loop can mint, which is the whole reason for
// the rename. A body that leaves the binding spelled `i` would read trusted
// and write checked, an asymmetry with no principle under it.
~[comptime|transform|claims_descendants] pub tor for {
expr: Expression,
grid: Expression,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult~part walk koru_std/grid.walk.kz
Koru Standard Library: the `walk` part of grid.kz. Joined via `~part walk` in grid.kz; merged into the grid module at load. std/grid:walk { s: inp.b, 2, d: enc.c, 3 } ! each |> ... s[k] ... stored { d[k]: v } ... | tail |> ... leftover window ... | done |> ... A RESLICING window walk: each labeled arg names a cursor (borrowed off `grid.field` like a `slice` decl) and the positional after it is its stride. The driver emits moving-base cursors and ONE guard at the loop head — `while (s.len >= 2 and d.len >= 3)` — so every `s[k]`/`d[k]` in the body is a raw slice access whose bound was already proven. This is the shape LLVM auto-vectorizes (fixed-offset reads/writes on advancing bases) — the same shape Goose's `s = s[3..]` loops emit — where an indexed `for (0..n) |q| g[4*q]` keeps per-element structure the vectorizer cannot see through. The borrow contract carries over from `std/grid:slice`: the program asked for the raw view, so the per-access __koru_at floor is declined and Zig's own slice checks still run in Debug. `| tail` fires once with whatever remains (`s.len < stride`); the remainder is the program's to write — a codec's partial group lives there, never silently dropped. `| done` chains after the walk, unlowered. Zig only this rung: a JS reslice is `s.slice(n)` — a COPY — and writes through it never reach the grid, which is a silently-wrong lowering no honest surface may ship.
~[comptime|transform|claims_descendants] pub tor walk {
source: Source,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult~part pool koru_std/grid.pool.kz
Koru Standard Library: the `pool` part of grid.kz — a positionally- addressed table whose EXTENT is a run-time value. Joined via `~part pool` in grid.kz; merged into the grid module at load. std/grid:pool(ops) { o: 0[u8], j: 0[i64] } -- declares the SHAPE std/grid:pool-alloc(ops, src.len) -- decides the extent ... ops[i].j ... stored { ops[i].o: 1 } ... -- reads/writes as usual std/grid:pool-free(ops) -- hands the slices back Why a sibling and not a flag on `new`: a grid's every cell exists from declaration, so `size:` is comptime-only and its storage is static BSS. A kernel that learns its table size from its input over-declares to the worst case and pays the bytes on every run — brainfuck's `ops` grid was four megabytes of i64 for programs a few kilocells long. A pool declares the shape comptime and the extent at the point the program knows it: one heap slice per column and a `var __koru_size` the alloc step sets. Everything else is the grid's own ABI, deliberately: the emitted struct wears the same `__KoruGridT_<name>`/`__koru_grid_<name>` names, so `collect`/`scanLowered` register it in the same namespace and reads, `stored` writes, `sweep`/`for` cursors and `[unsafe(bounds)]` all ride the existing path unchanged. The ONE difference the lowerers see is the `.pool` flag on GridRef: slice columns cannot be `(&x)[i]`-indexed, so pool access names the column without the `&`. The bounds floor is unchanged in spirit and sharper in message: an index outruns `__koru_size`, which is `0` until `pool-alloc` runs — so touching a pool before its extent arrives is the same loud trap as any other index error, never a silent neighbour. Declared before first use, same as `new`. No `size:`/row/`dimensions` facets — the extent is the alloc call's business and the layout is column for now.
~[keyword|comptime|transform] pub tor pool {
expr: Expression,
source: Source,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult// GRID.POOL-ALLOC — decide the extent, once the program knows it
//
// std/grid:pool-alloc(ops, src.len)
//
// A flow step, not a declaration: the bound is an ordinary caller-scope
// expression (`src.len`, `n * 4`), evaluated at the site and threaded into
// the generated unit as an event input — the same de-bundled-operand shape
// `stored` and `for` already use. One heap slice per column through
// `koru_allocator`, each seeded with its declared default, then the
// runtime `__koru_size` is set — last, so a column never says it has a
// cell it does not.
~[comptime|transform] pub tor pool-alloc {
expr: Expression,
size: Expression,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult// GRID.POOL-FREE — hand the slices back, seal the extent to zero
//
// std/grid:pool-free(ops)
//
// Discharges the allocation through the same allocator that made it and
// resets `__koru_size` to 0 — a read after free traps exactly as a read
// before alloc does. No phantom obligation this rung: the pool is a
// module-level table, not a bound value, so there is nothing to carry the
// `<pool!>` state on. That gap is named rather than hidden — freeing is
// the program's call to make.
~[comptime|transform] pub tor pool-free {
expr: Expression,
invocation: *const Invocation,
item: std/compiler:*const Item,
program: std/compiler:*const Program,
reporter: std/compiler:*ErrorReporter,
allocator: std.mem.Allocator
} -> SiteResult