# Two-phase refactor — slice 5 (statements → AST) Branch `ast-stage-c`. Suite **178/178**; self-hosting fixpoint **OK** (**3,000,988 bytes**; slice 4 was 2,987,484). ## What landed Statements now build AST nodes. A module-level `astStmt` carries each statement's node; `StatSeq` folds them into an `NkBlock`. | Statement | AST | | --- | --- | | `StatSeq` | `NkBlock` of the contained statements | | `x := e` | `NkAssign` (child 0 = designator, 1 = value) | | `P(…)` / `P` statement | `NkCall` (shared `AstCallNode` helper) | | `IF … THEN … ELSIF … ELSE … END` | `NkIf` chain: each `ELSIF` becomes the `else` child of the previous `NkIf` | | `WHILE c DO … END` | `NkWhile(cond, body)` | | `REPEAT … UNTIL c` | `NkRepeat(body, cond)` | | `LOOP … END` | `NkLoop(body)` | | `EXIT` | `NkExit` | | `FOR v := lo TO hi [BY s] DO … END` | `NkFor(var, lo, hi, by, body)` | | `RETURN [e]` | `NkReturn([e])` | | `HALT [(e)]` | `NkHalt([e])` | The `IF`/`ELSIF` chain is built incrementally (one `NkIf` per clause, linked through the `else` child) rather than with a local array — see the finding below. ## Finding — local arrays are miscompiled (pre-existing) While self-hosting this slice, the `IfStat` `VAR … ARRAY [0..7] OF AST.Node` locals exposed a **pre-existing V3 codegen bug**: a local `ARRAY` inside a `PROCEDURE` is miscompiled. ```modula2 PROCEDURE P; VAR a : ARRAY [0..3] OF INTEGER; i : INTEGER; BEGIN i := 0; WHILE i < 4 DO a[i] := i * 10; INC(i) END; ExitCode := a[0] + a[1] + a[2] + a[3] (* want 60, got 1 *) END P; ``` Module-level arrays are fine; **local** ones are not. Repro parked in `docs/wip/local-array-bug.mod`. It is **not** caused by this refactor (the code is inert and does not touch local-array lowering); it was merely *triggered* by using local arrays in the grammar. The grammar now uses scalars/globals. This should be a dedicated fix (like the array-of-record stride bug from Stage A). ## Verified (temporary `AST.Dump` of the body, then removed) A program with assignment, `IF`/`ELSE`, `WHILE`, `FOR` (no `BY`) and a statement call produced the expected `NkBlock`: - `NkAssign(NkDesignator x, 1)` - `NkIf(Lt(x,2), Block[Assign(x,3)], Block[Assign(x,4)])` - `NkWhile(Lt(x,5), Block[Assign(x, x+1)])` - `NkFor(Ident i, 1, 3, nil, Block[Assign(x, x+i)])` - `NkCall(NkDesignator P, NkDesignator x)` ## Deliberately deferred - `CASE` and `WITH` statements: explicit `astStmt := NoNode`. - Builtin statements `INCL`/`EXCL`/`INC`/`DEC`/`NEW`/`DISPOSE`: they do not set `astStmt` (the `Statement` leading reset leaves `NoNode`). - Brace/set literals and `ResultComp` call suffixes (from slice 4). - `Lower` + `.ssa` byte-compare (not started). ## Next Declarations (`CONST`/`TYPE`/`VAR`/`PROC` bodies → AST), then `CASE`/ `WITH`/builtin statements and brace/set literals, then the `Lower` walk.