Skip to content

Phase -1 / Etch / Tree-walking interpreter#7

Merged
guysenpai merged 11 commits into
mainfrom
phase-pre-0/etch/tree-walking-interpreter
May 16, 2026
Merged

Phase -1 / Etch / Tree-walking interpreter#7
guysenpai merged 11 commits into
mainfrom
phase-pre-0/etch/tree-walking-interpreter

Conversation

@guysenpai
Copy link
Copy Markdown
Contributor

@guysenpai guysenpai commented May 15, 2026

Brief

briefs/S4-etch-tree-walking-interpreter.md

Résumé

S4 validates the tree-walking interpreter hypothesis (cf. engine-spec.md §22.3 sub-section S4). Tier 0 gains an additive runtime ECS surface — runtime component registry, dynamic SoA archetype, resource store, runtime query — without touching the S1 comptime (Transform, Velocity) path. The Etch interpreter walks the S3 AST directly, compiles each component / resource / rule into runtime descriptors (registry ids, predicate pool, field filter, resource deps), and executes rule bodies tick by tick. A 20-program differential corpus parameterised by a Runner interface exercises every supported when form, in-place mutation, resource gating, and multi-rule ordering. The interface is the contract S5's codegen runner will plug into without modifying the harness.

Critères d'acceptation

  • Tier 0 ECS extensions (registry, archetype_dynamic, resources, query_runtime, world additions, root re-exports).
  • Etch interpreter (value, ecs_bridge, interp) + extended public surface (Interpreter, Value, RuntimeReport, runProgram, evalConst).
  • 20-program differential corpus + generic Runner-parameterised driver + interpreter Runner.
  • Bench harness (bench-etch-interp) — two sweeps (1 000 × 5 × 1 000 and 10 000 × 5 × 100). Apple Silicon dev primary, ReleaseSafe: median 0.603 ms / tick at 1 000 (gate 10 ms, target 5 ms — beaten 16× and 8×), median 6.593 ms / tick at 10 000 (gate 100 ms, target 50 ms — beaten 15× and 7.5×). Report archived under bench/results/.
  • Demo binary (run-demo-etch-interp) — produces Demo S4 OK | mode=ReleaseSafe | entities=1000 | rules=5 | ticks=60 | rules_matched=300 | errors=0 | total=37.566ms.
  • Zero leaks under std.testing.allocator on the full test suite.
  • zig build, zig build test (Debug + ReleaseSafe), zig fmt --check all green locally.
  • All previously existing build steps (run, bench-ecs, bench-etch, bindgen-vk, bindgen-wayland) still pass.
  • Diff-list vs. brief's "Files to create or modify" verified — 3 additions outside the explicit list each justified under « Acknowledged deviations ».

Notes de review

Three points to flag explicitly:

  1. Tier 0 surface is strictly additive. The S1 comptime (Transform, Velocity) archetype and its bench are untouched. World.init() stays zero-arg — Registry, ResourceStore, DynamicArchetype are unmanaged per engine-zig-conventions.md §3.
  2. or predicates compile to a PredicateNode pool and the runtime walks every dynamic archetype to evaluate it. Adequate for S4 corpus volume; a bitset short-cut may be needed in Phase 0.5 when archetype counts grow.
  3. The runner heap-boxes the Ast. The Interpreter holds a *const Ast. The parser's value-returning parse(gpa, source) would otherwise leave a dangling pointer once the Ast value moves into the Runner struct. The Runner allocates a stable *Ast, hands its address to the Interpreter, and frees in finalize. The "borrowed Ast" API on Interpreter.run(gpa, &ast, world, ticks) is preserved for short-lived callers; the long-lived Runner path is the new convention.

See also the Closure notes section of the brief for the full GO verdict, final measurements, and the residual debt list — including three S4-specific debts formalised post-bench (RuntimeQuery unused in hot path, RuntimeReport.last_error never assigned, ecs_bridge.writeValueAsBytes panics on type mismatch).

Changelog

Code

  • src/core/ecs/registry.zig, archetype_dynamic.zig, resources.zig, query_runtime.zig — new Tier 0 surface.
  • src/core/ecs/world.zig, src/core/root.zig — additive edits.
  • src/etch/value.zig, ecs_bridge.zig, interp.zig — interpreter pieces.
  • src/etch/root.zig — public surface extension.
  • src/demo_etch_interp.zig — demo binary.
  • bench/etch_interp.zig, bench/fixtures/demo_5_rules.etch, bench/fixture_facade.zig — bench harness + shared fixture.
  • bench/results/s4-etch-interp-20260515-1946.md — initial bench report (Apple Silicon dev primary, ReleaseSafe, verdict GO).
  • tests/etch_interp/diff_runner.zig, runner_interp.zig, corpus_facade.zig, corpus_test.zig — generic Runner-parameterised driver + interpreter Runner + facade + test entry.
  • tests/etch_interp/programs/*.etch × 20 + *.expected.zig × 20 — differential corpus.
  • build.zig — registers bench-etch-interp, run-demo-etch-interp, fixture facade, S4 test modules.

Documentation

  • README.md — bumped status to S4, added bench + demo build steps, mentioned tests/etch_interp/ in the project layout. Aligned tags table with actual git state (S2 / S3 are posted, dropped "pending merge"); fixed the bench layout line to point at the "Basic commands" block instead of naming a single bench.
  • CLAUDE.md — flipped current milestone to S4 (CLOSED, PR pending), added S4 row to the Tags table, marked the S4 hypothesis as validated. Aligned tags table with actual git state (S1 → 2026-05-09, S2 → 2026-05-11, S3 → 2026-05-15, "Last released tag" bumped to v0.0.4-S3-etch-parser-subset). Date stamp updated to 2026-05-16.
  • briefs/S4-etch-tree-walking-interpreter.md — milestone brief; copied verbatim at branch creation, ticked Specs read, kept execution journal, filled Closure notes at status → CLOSED. Three S4-specific residual debts formalised in Closure notes (RuntimeQuery unused on hot path, RuntimeReport.last_error never assigned, ecs_bridge.writeValueAsBytes panics on type mismatch).

guysenpai added 11 commits May 15, 2026 20:11
…esources

Additive S4 ECS surface (S1 comptime path untouched):
- registry.zig: ComponentId, FieldKind/FieldDesc/ComponentDesc, register
  by comptime T or by raw descriptor (Etch bridge path), find field by name.
- archetype_dynamic.zig: 16 KiB SoA chunks computed at runtime from
  component sizes/aligns, spawnDefault memcpy's registry defaults.
- resources.zig: HashMap<ComponentId, []u8> + dirty bit.
- query_runtime.zig: includes/excludes set test + chunk iterator.
- world.zig: additive surface, new DynamicLocation map keyed by EntityId.
- value.zig: Value tagged union (int/float/bool/string_id/entity_id/component_ref),
  RuntimeError + RuntimeErrorKind, checked int helpers.
- ecs_bridge.zig: Bridge owns Etch-name → ComponentId maps for components and
  resources; read/write component fields and resource fields by name through
  the registry's FieldDesc offsets.
- interp.zig: Interpreter.compile / runFor / stepOnce. Registers components
  and resources, collects rule include/exclude/filter/resource-deps from the
  when tree, evaluates rule bodies in source order, single tick = step + tick
  boundary (resets resource dirty bits).
- root.zig: extends the weld_etch public surface with Value, Interpreter,
  RuntimeReport, runProgram, runWithAst, evalConst.
- build.zig: wires weld_core into the weld_etch module.
- diff_runner.zig: generic driver (FieldValue/FieldSpec/ComponentSpec/
  EntitySpec/ResourceInit/WorldSpec/ExpectedWorld + runProgram(Runner, ...)).
- runner_interp.zig: interpreter Runner (setup/step/finalize). Heap-boxes
  the AST so the Interpreter's borrowed pointer survives the Runner move.
- 20 .etch programs + sidecars under tests/etch_interp/programs/ covering
  arithmetic, mutation, when single, and/or/not composition, has-with-filter
  (int/bool/float), resource gate, resource changed dirty/clean, multi-rule
  ordering.
- corpus_facade.zig + corpus_test.zig wired into zig build test through
  four new modules in build.zig.
- bench/fixtures/demo_5_rules.etch: shared 5-rule fixture covering arithmetic,
  in-place mutation, field-equality filter, resource gate, and rule ordering.
- bench/fixture_facade.zig: tiny module that @embedFiles the fixture so both
  the bench and demo binary can reach it (their root modules differ).
- bench/etch_interp.zig: two sweeps (1 000 × 5 × 1 000 and 10 000 × 5 × 100),
  per-tick samples, Markdown report under bench/results/.
- src/demo_etch_interp.zig: spawns 1 000 entities, runs 60 ticks, prints the
  mandated summary line.
- build.zig: registers fixture_facade module, S4 bench step bench-etch-interp,
  S4 demo step run-demo-etch-interp.
@guysenpai guysenpai merged commit 483062b into main May 16, 2026
6 checks passed
@guysenpai guysenpai deleted the phase-pre-0/etch/tree-walking-interpreter branch May 16, 2026 04:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant