summary_two-phase-slice2.md 3.0 KB

Two-phase refactor — slice 2 (expression spine → AST)

Branch ast-stage-c. Suite 178/178; self-hosting fixpoint OK (2,966,245 bytes; slice 1 was 2,957,466, +8,779 for the builder).

What landed

The scalar expression hierarchy now builds an AST alongside the legacy emit path. The nodes are still inert (nothing lowers them), so the generated .ssa is unchanged (fixpoint byte-identical).

Production Now builds
Expr NkBinExpr for a relation (= # < <= > >= IN)
SimExpr NkUnary (-, OpSub) for a leading minus; NkBinExpr for + - OR
Term NkBinExpr for * / DIV MOD AND
Fact NkIntLit / NkCharLit / NkRealLit / NkStrLit; ( Expr ) passes the inner node through

Design: one result slot + a per-invocation literal flag

The productions keep their existing t/q attributes (no signature changes, so no call sites change). A single module-level astCur : AST.Node carries the result of the most recently completed Expr/SimExpr/Term/Fact; the combination productions save it into locals before parsing the next operand.

The one hazard is Fact: alternatives such as CHR(x) or a[i] call Expr internally (and Design calls Expr for indices), which would leave an inner node in astCur. The fix is a per-invocation astIsLit : BOOLEAN local in Fact: it is set only by the literal alternatives, and a trailing action does

IF NOT astIsLit THEN astCur := AST.NoNode END

Because astIsLit is a local (stack) variable, a nested Fact has its own copy and cannot make an outer non-literal Fact look literal. ( Expr ) is the single pure-grouping alternative and explicitly sets astIsLit := TRUE so the inner node survives.

Verified (temporary AST.Dump, then removed)

AST.Dump(astCur, 0) was wired into the M2 root for one build and three shapes checked; the dump was then removed (the committed grammar has no debug output):

  • 2 + 3 * 4 - 5 → Sub(Add(2, Mul(3,4)), 5) — precedence + left assoc.
  • -2 + 3 → Add(Unary(Sub, 2), 3).
  • -(2 + 3) * 4 → Unary(Sub, Mul(Add(2,3), 4)) — unary binds to the whole Term, as the grammar specifies.
  • 2 * (3 + 4) → Mul(2, Add(3,4)); 2 < 3 → BinExpr(op=OpLt)(2,3).

Two builder bugs were caught this way and fixed (unary minus not fed back into the left accumulator; parentheses discarding the inner node).

Deliberately deferred

Still astCur := NoNode (to be converted in later slices):

  • Design (identifiers, qualified names, selectors .f [i] ^) — the largest piece; NkIdent/NkDesignator/NkSelector.
  • Calls (ArgList, result suffixes) and NkBraceLit / NkSetLit.
  • Builtins CHR/ORD/ABS/HIGH/LEN/… and NOT.
  • Lower + .ssa byte-compare (plan §3 phase 2) — not started; the inert builder is validated by the fixpoint and the manual dump above.

Next

Slice 3: convert Design and the Fact designator/call alternatives, which turns the spine into a useful AST for real expressions.