Просмотр исходного кода

two-phase slice 8: CASE and WITH statement AST

CASE builds NkCase(selector, arms...) with NkCaseArm(label..., body)
(range labels as NkSubrange; ELSE arm op=1).  WITH builds
NkWith(designators..., body).  Both build incrementally via a node
passed to CaseAlt/CaseLabel/WithItem, avoiding local arrays and keeping
nested forms correct.  Nodes are inert.

Deferred: imports/CLASS/nested modules, brace/set literals, ResultComp,
Lower.

Gates: suite 178/178; fixpoint OK (stage2 == stage3, 3021285 bytes).

See docs/summary_two-phase-slice8.md.
Eric Streit 1 неделя назад
Родитель
Сommit
c916ece9a9
5 измененных файлов с 2423 добавлено и 2309 удалено
  1. 54 22
      compiler/src/M2.atg
  2. 2313 2281
      compiler/src/M2.lst
  3. 3 1
      docs/plan-two-phase.md
  4. 4 5
      docs/session-handoff-2026-10-03.md
  5. 49 0
      docs/summary_two-phase-slice8.md

+ 54 - 22
compiler/src/M2.atg

@@ -1292,22 +1292,31 @@ PRODUCTIONS
                                            END; .) .
                                            END; .) .
   (* WITH pushes each record's fields (inner wins) plus its base
   (* WITH pushes each record's fields (inner wins) plus its base
      address; field designators resolve through both stacks. *)
      address; field designators resolve through both stacks. *)
-  WithStat                              (. VAR nW: CARDINAL; .)
-    = "WITH"                            (. nW := 0; .)
-      WithItem<nW> { "," WithItem<nW> }
-      "DO" [ StatSeq ] "END"              (. astStmt := AST.NoNode;
+  WithStat                              (. VAR nW: CARDINAL;
+                                             astNode: AST.Node; .)
+    = "WITH"                            (. nW := 0;
+                                           astNode := AST.MakeNode(AST.NkWith); .)
+      WithItem<nW, astNode> { "," WithItem<nW, astNode> }
+      "DO"                              (. astStmt := AST.NoNode; .)
+      [ StatSeq ] "END"                 (. AST.SetChild(astNode,
+                                           AST.NChild(astNode), astStmt);
+                                           astStmt := astNode;
                                            WHILE nW > 0 DO
                                            WHILE nW > 0 DO
                                              SymTab.PopScope;
                                              SymTab.PopScope;
                                              QbeGen.PopWith;
                                              QbeGen.PopWith;
                                              DEC(nW)
                                              DEC(nW)
                                            END; .) .
                                            END; .) .
-  WithItem<VAR nW: CARDINAL>            (. VAR dt: SymTab.TypeIndex;
+  WithItem<VAR nW: CARDINAL; wnode: AST.Node>
+                                        (. VAR dt: SymTab.TypeIndex;
                                              dk: INTEGER;
                                              dk: INTEGER;
                                              qd, qe: QbeGen.QVal;
                                              qd, qe: QbeGen.QVal;
                                              qn: SymTab.Name;
                                              qn: SymTab.Name;
                                              sfx: BOOLEAN; .)
                                              sfx: BOOLEAN; .)
     = Design<dt, dk, qd, qn, sfx>
     = Design<dt, dk, qd, qn, sfx>
-                                        (. IF dt = SymTab.InvalidType THEN
+                                        (. IF astCur # AST.NoNode THEN
+                                             AST.SetChild(wnode,
+                                               AST.NChild(wnode), astCur) END;
+                                           IF dt = SymTab.InvalidType THEN
                                            ELSIF (SymTab.ClassOf(dt) #
                                            ELSIF (SymTab.ClassOf(dt) #
                                                   SymTab.ClRecord)
                                                   SymTab.ClRecord)
  AND (SymTab.ClassOf(dt) #
  AND (SymTab.ClassOf(dt) #
@@ -1849,33 +1858,52 @@ PRODUCTIONS
                                            QbeGen.Jmp(lTop);
                                            QbeGen.Jmp(lTop);
                                            QbeGen.EmitLabel(lEnd); .) .
                                            QbeGen.EmitLabel(lEnd); .) .
   CaseStat                              (. VAR tsel: SymTab.TypeIndex;
   CaseStat                              (. VAR tsel: SymTab.TypeIndex;
-                                             qsel, lEnd: QbeGen.QVal; .)
-    = "CASE" Expr<tsel, qsel>           (. QbeGen.NewLabel(lEnd); .)
-      "OF" CaseAlt<tsel, qsel, lEnd>
-      { "|" CaseAlt<tsel, qsel, lEnd> }
-      [ "ELSE" [ StatSeq ] ]
-      "END"                             (. astStmt := AST.NoNode;
+                                             qsel, lEnd: QbeGen.QVal;
+                                             arm, astNode: AST.Node; .)
+    = "CASE" Expr<tsel, qsel>           (. astNode := AST.MakeNode(AST.NkCase);
+                                           AST.SetChild(astNode, 0, astCur);
+                                           QbeGen.NewLabel(lEnd); .)
+      "OF" CaseAlt<tsel, qsel, lEnd, astNode>
+      { "|" CaseAlt<tsel, qsel, lEnd, astNode> }
+      [ "ELSE"                          (. astStmt := AST.NoNode; .)
+        [ StatSeq ]                     (. arm := AST.MakeNode(AST.NkCaseArm);
+                                           AST.SetOp(arm, 1);
+                                           AST.SetChild(arm, 0, astStmt);
+                                           AST.SetChild(astNode,
+                                             AST.NChild(astNode), arm); .) ]
+      "END"                             (. astStmt := astNode;
                                            QbeGen.EmitLabel(lEnd); .) .
                                            QbeGen.EmitLabel(lEnd); .) .
   (* Compare-chain lowering: each alternative ends its match-tests
   (* Compare-chain lowering: each alternative ends its match-tests
      with "jmp lAfter", so the no-match fallthrough skips the body:
      with "jmp lAfter", so the no-match fallthrough skips the body:
      "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
      "cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
      lAfter:". Falls into the next alternative, ELSE, or END. *)
      lAfter:". Falls into the next alternative, ELSE, or END. *)
   CaseAlt<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
   CaseAlt<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
-          lEnd: QbeGen.QVal>            (. VAR lBody, lAfter: QbeGen.QVal; .)
-    =                                   (. QbeGen.NewLabel(lBody);
+          lEnd: QbeGen.QVal; cnode: AST.Node>
+                                        (. VAR lBody, lAfter: QbeGen.QVal;
+                                             arm: AST.Node; .)
+    =                                   (. arm := AST.MakeNode(AST.NkCaseArm);
+                                           QbeGen.NewLabel(lBody);
                                            QbeGen.NewLabel(lAfter); .)
                                            QbeGen.NewLabel(lAfter); .)
-      CaseLabel<tsel, qsel, lBody>
-      { "," CaseLabel<tsel, qsel, lBody> }
-      ":"                               (. QbeGen.Jmp(lAfter);
+      CaseLabel<tsel, qsel, lBody, arm>
+      { "," CaseLabel<tsel, qsel, lBody, arm> }
+      ":"                               (. astStmt := AST.NoNode;
+                                           QbeGen.Jmp(lAfter);
                                            QbeGen.EmitLabel(lBody); .)
                                            QbeGen.EmitLabel(lBody); .)
-      [ StatSeq ]                       (. QbeGen.Jmp(lEnd);
+      [ StatSeq ]                       (. AST.SetChild(arm, AST.NChild(arm),
+                                           astStmt);
+                                           AST.SetChild(cnode,
+                                             AST.NChild(cnode), arm);
+                                           QbeGen.Jmp(lEnd);
                                            QbeGen.EmitLabel(lAfter); .) .
                                            QbeGen.EmitLabel(lAfter); .) .
   CaseLabel<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
   CaseLabel<tsel: SymTab.TypeIndex; qsel: QbeGen.QVal;
-            lBody: QbeGen.QVal>         (. VAR t2, t3: SymTab.TypeIndex;
+            lBody: QbeGen.QVal; arm: AST.Node>
+                                        (. VAR t2, t3: SymTab.TypeIndex;
                                              q2, q3, qc, qd, qe:
                                              q2, q3, qc, qd, qe:
                                                QbeGen.QVal;
                                                QbeGen.QVal;
+                                             astLab: AST.Node;
                                              lNext: QbeGen.QVal; .)
                                              lNext: QbeGen.QVal; .)
-    = Expr<t2, q2>                      (. IF (t2 #
+    = Expr<t2, q2> (. astLab := astCur;
+                                           IF (t2 #
                                              SymTab.InvalidType)
                                              SymTab.InvalidType)
  AND (tsel #
  AND (tsel #
                                                SymTab.InvalidType)
                                                SymTab.InvalidType)
@@ -1900,7 +1928,9 @@ PRODUCTIONS
                                              qsel, q2, qc, FALSE);
                                              qsel, q2, qc, FALSE);
                                            QbeGen.Jnz(qc, lBody, lNext);
                                            QbeGen.Jnz(qc, lBody, lNext);
                                            QbeGen.EmitLabel(lNext); .)
                                            QbeGen.EmitLabel(lNext); .)
-      [ ".." Expr<t3, q3>               (. IF (t3 #
+      [ ".." Expr<t3, q3>               (. astLab := AST.MakeBin(
+                                             AST.NkSubrange, 0, astLab, astCur);
+                                           IF (t3 #
                                              SymTab.InvalidType)
                                              SymTab.InvalidType)
  AND (tsel #
  AND (tsel #
                                                SymTab.InvalidType)
                                                SymTab.InvalidType)
@@ -1920,7 +1950,9 @@ PRODUCTIONS
                                              FALSE);
                                              FALSE);
                                            QbeGen.NewLabel(lNext);
                                            QbeGen.NewLabel(lNext);
                                            QbeGen.Jnz(qe, lBody, lNext);
                                            QbeGen.Jnz(qe, lBody, lNext);
-                                           QbeGen.EmitLabel(lNext); .) ] .
+                                           QbeGen.EmitLabel(lNext); .) ]
+                                        (. AST.SetChild(arm,
+                                             AST.NChild(arm), astLab); .) .
   ReturnStat                            (. VAR t: SymTab.TypeIndex;
   ReturnStat                            (. VAR t: SymTab.TypeIndex;
                                              q, qt: QbeGen.QVal;
                                              q, qt: QbeGen.QVal;
                                              res: SymTab.TypeIndex;
                                              res: SymTab.TypeIndex;

Разница между файлами не показана из-за своего большого размера
+ 2313 - 2281
compiler/src/M2.lst


+ 3 - 1
docs/plan-two-phase.md

@@ -92,7 +92,9 @@ Suggested slices, each ending in `build.sh` + `run_tests.sh` +
    `NkConstDecl`/`NkTypeDecl`/`NkVarDecl`/`NkProcDecl` (types referenced
    `NkConstDecl`/`NkTypeDecl`/`NkVarDecl`/`NkProcDecl` (types referenced
    by the node `ty` index); `astUnit` holds `NkUnit(name, decls, body)`
    by the node `ty` index); `astUnit` holds `NkUnit(name, decls, body)`
    for all three unit kinds; builtin statements build `NkCall`s.
    for all three unit kinds; builtin statements build `NkCall`s.
-   Deferred: imports, `CLASS`, nested modules in the unit AST.
+   Deferred: imports, `CLASS`, nested modules in the unit AST; brace/set
+   literals and `ResultComp`; `Lower`.  Also
+   `docs/summary_two-phase-slice8.md` (`CASE`/`WITH`).
 5. **Flip**: bodies lower from the AST; delete `FwdPatchAll` /
 5. **Flip**: bodies lower from the AST; delete `FwdPatchAll` /
    `FixLoadClass` / `FixStoreClass`, the `Design` forward-variable
    `FixLoadClass` / `FixStoreClass`, the `Design` forward-variable
    placeholder, and `pendVar*` if superseded.
    placeholder, and `pendVar*` if superseded.

+ 4 - 5
docs/session-handoff-2026-10-03.md

@@ -1,7 +1,7 @@
 # Session handoff — 2026-10-03 (two-phase frontend, slices 1–5)
 # Session handoff — 2026-10-03 (two-phase frontend, slices 1–5)
 
 
 State: branch **`ast-stage-c`** (off `1c93f19`, not merged to `master`).
 State: branch **`ast-stage-c`** (off `1c93f19`, not merged to `master`).
-Suite **178/178**; self-hosting fixpoint **OK** (**3,016,802 bytes**).
+Suite **178/178**; self-hosting fixpoint **OK** (**3,021,285 bytes**).
 Working tree clean except the user's `compiler/toto.mod`.
 Working tree clean except the user's `compiler/toto.mod`.
 
 
 Plan of record: `docs/plan-two-phase.md`.  Start: §4 slice 3
 Plan of record: `docs/plan-two-phase.md`.  Start: §4 slice 3
@@ -18,6 +18,7 @@ Plan of record: `docs/plan-two-phase.md`.  Start: §4 slice 3
 | `v3-ast-slice5` | `summary_two-phase-slice5.md` | statements: `NkBlock`, assign, `IF`/`WHILE`/`REPEAT`/`LOOP`/`EXIT`/`FOR`/`RETURN`/`HALT`, calls |
 | `v3-ast-slice5` | `summary_two-phase-slice5.md` | statements: `NkBlock`, assign, `IF`/`WHILE`/`REPEAT`/`LOOP`/`EXIT`/`FOR`/`RETURN`/`HALT`, calls |
 | `v3-ast-slice6` | `summary_two-phase-slice6.md` | declarations: `NkDeclSeq`, `NkConstDecl`, `NkTypeDecl`, `NkVarDecl`, `NkProcDecl` (types by `ty` index) |
 | `v3-ast-slice6` | `summary_two-phase-slice6.md` | declarations: `NkDeclSeq`, `NkConstDecl`, `NkTypeDecl`, `NkVarDecl`, `NkProcDecl` (types by `ty` index) |
 | `v3-ast-slice7` | `summary_two-phase-slice7.md` | unit root `NkUnit(name, decls, body)`; builtin statements `INCL`/`INC`/`NEW`/… as `NkCall`s |
 | `v3-ast-slice7` | `summary_two-phase-slice7.md` | unit root `NkUnit(name, decls, body)`; builtin statements `INCL`/`INC`/`NEW`/… as `NkCall`s |
+| `v3-ast-slice8` | `summary_two-phase-slice8.md` | `CASE`/`WITH` statements (incremental nodes, no local arrays) |
 
 
 The AST is **built alongside** the existing lowering; nothing consumes it
 The AST is **built alongside** the existing lowering; nothing consumes it
 yet.  Each slice was gated by `build.sh` + `run_tests.sh` + `fixpoint.sh`,
 yet.  Each slice was gated by `build.sh` + `run_tests.sh` + `fixpoint.sh`,
@@ -26,11 +27,9 @@ production (removed before each commit).
 
 
 ## Not converted yet (still `NoNode`)
 ## Not converted yet (still `NoNode`)
 
 
-- Statements: `CASE`, `WITH`.
+- Statements: (all core statements done; `CASE`/`WITH` as of slice 8).
 - Expressions: brace/set literals, `ResultComp` call suffixes.
 - Expressions: brace/set literals, `ResultComp` call suffixes.
-- Declarations: imports, `CLASS`, nested modules in the unit AST
-  (`CONST`/`TYPE`/`VAR`/`PROC`, the `NkUnit` root and builtin statements
-  are done as of slices 6–7).
+- Declarations: imports, `CLASS`, nested modules in the unit AST.
 - **`Lower` + `.ssa` byte-compare** (the point where the old emit path
 - **`Lower` + `.ssa` byte-compare** (the point where the old emit path
   gets deleted) — not started.
   gets deleted) — not started.
 
 

+ 49 - 0
docs/summary_two-phase-slice8.md

@@ -0,0 +1,49 @@
+# Two-phase refactor — slice 8 (CASE + WITH statements)
+
+Branch `ast-stage-c`.  Suite **178/178**; self-hosting fixpoint **OK**
+(**3,021,285 bytes**; slice 7 was 3,016,802).
+
+## What landed
+
+| Statement | AST |
+| --- | --- |
+| `CASE e OF … END` | `NkCase(selector, arm…)` |
+| case alternative | `NkCaseArm(label…, body)` |
+| range label `a..b` | `NkSubrange(a, b)` |
+| `ELSE` arm | `NkCaseArm` with `op = 1`, child 0 = body |
+| `WITH d, … DO … END` | `NkWith(designator…, body)` |
+
+`CASE` and `WITH` build their node **incrementally**, so no local arrays
+are needed (and per-invocation state is preserved across nested forms):
+`CaseAlt`, `CaseLabel` and `WithItem` take the enclosing node as a value
+parameter and append to it.  A body-less alternative resets `astStmt`
+first so it never appends a stale node.
+
+## Verified (temporary `AST.Dump` of `astUnit`, then removed)
+
+```modula2
+CASE x OF
+  1 : x := 1
+| 2, 3..5 : x := 2
+ELSE x := 9
+END;
+WITH r DO x := f END
+```
+
+produced
+
+- `NkCase(x, [ NkCaseArm(1, Block[x:=1]),
+  NkCaseArm(2, Subrange(3,5), Block[x:=2]),
+  NkCaseArm(op=1, Block[x:=9]) ])`
+- `NkWith(NkDesignator r, Block[ NkAssign(x, f) ])`
+
+## Deliberately deferred
+
+- Imports, `CLASS`, nested modules in the unit AST.
+- Brace/set literals and `ResultComp` call suffixes.
+- `Lower` + `.ssa` byte-compare.
+
+## Next
+
+Brace/set literals and `ResultComp`; imports/`CLASS`; then the `Lower`
+walk.

Некоторые файлы не были показаны из-за большого количества измененных файлов