summary_step1.9.md 2.8 KB

V3 step 1.9 — statements + boolean/relational core (done 2026-09-19)

Interactive grammar §4 (last big section before composites). Suite 28/28 (13 run + 15 reject), LL(1)-clean throughout.

Grammar (compiler/src/M2.atg)

  • Expr = SimExpr [Rel SimExpr]: = # < <= > >= (no <>, no IN yet), RelCheck → 213, result BOOLEAN via new QbeGen.Cmp (ceqw/cnew/csltw/cslew/csgtw/csgew, eager 0/1).
  • OR/AND (BoolCheck → 212) eager via or/and; NOT via xor-1.
  • BOOLEAN/CHAR variables load like integers now (still w); named CHAR consts load too; other named-const use stays 230. VAR x: BOOLEAN/CHAR accepted (enums/subranges still 230).
  • Statements: IF/ELSIF/ELSE, WHILE, REPEAT/UNTIL, LOOP/EXIT (230 outside LOOP via loop-label stack), FOR/TO/static-sign-BY, full CASE (labels, a..b ranges, ELSE), RETURN with 232 checks (outside proc / value-vs-proper mismatch / missing value).
  • FOR: integer var + integer bounds (220), literal non-zero BY (ConstInt; non-literal → 230, zero → 220), sign picks cslew/csgew.
  • WITH waits for records (step 3).

Backend (QbeGen: labels, jumps, dead positions)

New: deterministic @LN labels, EmitLabel/Jmp/Jnz, Cmp, NotQ, LOOP-exit stack. Plus the section's hard lesson, handled once in the backend instead of every action:

  • QBE forbids instructions after a terminator with no intervening label. Two rules fix all shapes: (1) value-emitters auto-open an unreachable block past a terminator (keeps every temporary defined — a skipped definition would dangle later uses); (2) Jmp/Jnz past a terminator are skipped (provably unreachable).
  • CASE needed a real layout fix on top: each alternative now ends its tests with jmp lAfter, so no-match fallthrough skips the body (cmp; jnz(lBody,lF); lF: ...; jmp lAfter; lBody: S; ... lAfter:). The first attempt (fallthrough into the body) ran the wrong branch and is recorded here so it isn't reinvented.

SymTab (small)

Proc-stack: EnterProc/ReenterProc push, new CloseProc pops both scope and stack (all three heading sites converted); InProc/CurRes drive the RETURN 232s. Frame gains 232 "bad RETURN".

Bugs the tests caught

  1. Missing LOOP back-edge — the exact bug Test2's own summary records ("body ran once"); t_loop proved it again.
  2. CASE no-match fallthrough executed the first body (100 vs 103).
  3. Double-terminator QBE rejections (EXIT inside IF bodies).

Tests

Run: t_if/while/repeat/loop/for (incl. BY -2 and empty-range skip)/case (labels+ranges+ELSE)/bool (vars, chars, #, NOT) + showcase3 tour → 183. Reject: 214 non-boolean WHILE, stray EXIT, module-level RETURN 1, BY 0. Total 28 (13 run + 15 reject).

Open (step 2/3/4): REAL lowering, WITH, IN, CHAR arithmetic policy (currently 211), unmatched-FORWARD 231, call emission.