iter loop-recur.1: additive Term::Loop / Term::Recur / LoopBinder foundation
First of three iterations of the standalone loop/recur milestone
(spec 97c1ed1). loop/recur become real parseable / printable /
round-trippable / hash-stable strictly-additive AST nodes across
every no-wildcard exhaustive Term match in all six crates, plus
parse/print, prose arms, DESIGN.md + form_a.md schema blocks,
drift/coverage/hash anchors, and the spec's worked sum_to program
as a green round-trip fixture. NO typecheck semantics and NO real
codegen this iter by design: synth and lower_term are stubbed
CheckError::Internal / CodegenError::Internal exactly as mut.1
(real typecheck = iter 2, real loop-header/phi/back-edge = iter 3).
Two binding Boss design calls recorded in the plan header and the
journal: (1) verify_tail_positions "byte-unchanged" = its tail-app
verification role is unchanged, not its source — it is an
exhaustive no-wildcard match so two descent-only arms are
mandatory; acceptance evidence is the tail-app non-regression test.
(2) codegen is in iter-1 scope as stubs/pass-throughs because the
workspace must compile for cargo test --workspace.
Three plan-pseudo-vs-reality substitutions (serde Type::Con
literal, bare Int -> (con Int), unqualified LoopBinder) and one
recon-undercount integration-test site, all intent-preserving and
journalled. Boss forward-fix folded in: the committed specs
themselves wrote bare Int in the loop-binder snippets (parses as
Type::Var) — corrected the three headline snippets to (con Int) for
doc-honesty (no design/schema change).
cargo test --workspace 600 -> 608 / 0 red (Boss-reran
independently); iter13a + new loop_recur hash pins green.
This commit is contained in:
@@ -2420,6 +2420,25 @@ are real surface forms.
|
||||
{ "t": "assign",
|
||||
"name": "<id>",
|
||||
"value": Term }
|
||||
|
||||
// loop-recur iter 1: strict iteration block. `binders` declares
|
||||
// one or more loop parameters (name, type, init), evaluated in
|
||||
// order on loop entry; `body` is in scope of all binders. The
|
||||
// loop's value is `body`'s value on the iteration that exits via a
|
||||
// non-`recur` branch. Strictly additive (no `skip_serializing_if`;
|
||||
// pre-existing fixtures hash bit-identically — none carry the tag).
|
||||
// No totality claim — an infinite loop is legal. See
|
||||
// `docs/specs/2026-05-17-loop-recur.md`.
|
||||
{ "t": "loop",
|
||||
"binders": [ { "name": "<id>", "type": Type, "init": Term }, ... ],
|
||||
"body": Term }
|
||||
|
||||
// loop-recur iter 1: re-enter the lexically innermost enclosing
|
||||
// `loop`, rebinding its binders positionally to `args`. Transfers
|
||||
// control (no fall-through); valid only in tail position of its
|
||||
// enclosing loop (enforced at typecheck, `recur-not-in-tail-position`).
|
||||
{ "t": "recur",
|
||||
"args": [ Term, ... ] }
|
||||
```
|
||||
|
||||
In the MVP, `do` is only a direct call to a built-in effect op (no
|
||||
|
||||
Reference in New Issue
Block a user