c9355d7d58
The Term::Loop arm of write_term rendered binders as bare
`name = init;` statements inside the `loop { … }` body block, which
reads as C/Rust re-init-every-iteration semantics — a projection
lying about a body the prose surface exists to let the reader trust.
Rewrite to a parenthesised init-list on the keyword line,
`loop(acc = 0, i = 1) { … }`, positionally isomorphic to the
unchanged Term::Recur arm. Projection-only: loop/recur AST, Form-A,
JSON-AST, typecheck, codegen, and the Form-A↔JSON round-trip
invariant untouched. RED-first via two new committed
examples/*.prose.txt byte-equality snapshots + two snapshot_loop_*
tests; full ailang-prose suite 10/0; both ail-prose CLI byte-matches
green. Single-iteration milestone, brainstorm→plan→implement.
Spec: docs/specs/2026-05-18-prose-loop-binders.md
Plan: docs/plans/prose-loop-binders.1.md
73 lines
2.9 KiB
Markdown
73 lines
2.9 KiB
Markdown
# iter prose-loop-binders.1 — Form-B `loop` renders binders as a parenthesised init-list on the keyword
|
|
|
|
**Date:** 2026-05-18
|
|
**Started from:** 6533134dab888605c24fb2e50a65fcbf99c1897e
|
|
**Status:** DONE
|
|
**Tasks completed:** 2 of 2
|
|
|
|
## Summary
|
|
|
|
Projection-only single-iteration milestone in `ailang-prose`. The
|
|
`Term::Loop` arm of `write_term` (`crates/ailang-prose/src/lib.rs`)
|
|
now renders loop binders as a parenthesised init-list on the keyword
|
|
line — `loop(acc = 0, i = 1) { … }` — instead of bare
|
|
`name = init;` statements inside the body block. The old shape
|
|
implied C/Rust re-init-every-iteration semantics; the new shape
|
|
mirrors the adjacent (untouched) `Term::Recur` arm's
|
|
`enumerate()` + `", "`-separator idiom and renders init expressions
|
|
at `level` (they sit on the keyword line, not the indented block).
|
|
RED-first: two committed `examples/*.prose.txt` snapshot fixtures
|
|
plus two `check_snapshot`-calling `#[test]` fns were added and
|
|
verified failing against the old renderer before the arm rewrite
|
|
made them pass. Loop/recur AST, Form-A, JSON-AST, typecheck,
|
|
codegen and the Form-A↔JSON round-trip invariant are untouched by
|
|
construction (single render-arm body rewrite, no signature change,
|
|
no caller touched).
|
|
|
|
## Per-task notes
|
|
|
|
- iter prose-loop-binders.1.1: RED — created
|
|
`examples/loop_sum_to_run.prose.txt` (multi-binder + non-recur
|
|
exit) and `examples/loop_forever_build.prose.txt` (single-binder
|
|
recur-only) with the exact Option-A bytes from the plan; appended
|
|
`snapshot_loop_sum_to_run` + `snapshot_loop_forever_build` after
|
|
`snapshot_ordering_match` in `tests/snapshot.rs`. `cargo test -p
|
|
ailang-prose snapshot_loop` → `0 passed; 2 failed; 8 filtered
|
|
out` (filter resolved to exactly the two new tests; both panic
|
|
with the expected `prose snapshot mismatch` message). RED
|
|
confirmed.
|
|
- iter prose-loop-binders.1.2: GREEN — rewrote the `Term::Loop`
|
|
arm at `lib.rs:938-955` verbatim per the plan (parenthesised
|
|
init-list, init at `level`, stale "not yet designed" comment
|
|
removed). `cargo test -p ailang-prose snapshot_loop` → `2
|
|
passed; 0 failed`; full `ailang-prose` suite → `10 passed; 0
|
|
failed` (no pre-existing snapshot regressed); both `ail prose |
|
|
diff` CLI byte-matches → `MATCH`; post-cleanup `git status
|
|
--porcelain` is EXACTLY the four expected paths.
|
|
|
|
## Concerns
|
|
|
|
(none)
|
|
|
|
## Known debt
|
|
|
|
(none — projection-only single-arm rewrite, fully gated by
|
|
whole-file byte-equality snapshots + live-CLI diff)
|
|
|
|
## Files touched
|
|
|
|
- `crates/ailang-prose/src/lib.rs` — `Term::Loop` arm of
|
|
`write_term` rewritten (single hunk, lines 938-953)
|
|
- `crates/ailang-prose/tests/snapshot.rs` — two `#[test]` fns
|
|
appended after `snapshot_ordering_match`
|
|
- `examples/loop_sum_to_run.prose.txt` — new committed snapshot
|
|
- `examples/loop_forever_build.prose.txt` — new committed snapshot
|
|
|
|
## Blocked detail
|
|
|
|
(n/a — Status DONE)
|
|
|
|
## Stats
|
|
|
|
bench/orchestrator-stats/2026-05-18-iter-prose-loop-binders.1.json
|