summary_step6.md 3.2 KB

Step 6 — Local modules hardened (uncommitted)

112/112 tests green (99 regressions + 5 run + 8 rejection), mc64 boot + example green.

Goal

Harden the step-6 WIP (single-file local MODULEs, no nesting, no bodies): exported VARs, CONSTs and parameterless proper procedures, used as M.x / M.P(). Fix the bugs found by probing, lock behavior in with rejection tests, keep every old suite green.

What was built

  • SymTab: ExpQual now qualifies KindConst as well as KindVar ("M.x"); ExitModule became a BOOLEAN function — TRUE iff every EXPORT name was declared (ghost exports report FALSE).
  • M2c.atg: ModuleDecl gained an enterOk flag (failed EnterModule skips export recording and ExitModule, killing the duplicate-module cascade); END reports SemError(201) on ghost exports; header comment documents local-module support and limits.
  • run_tests.sh: new Step-6 sections (5 run + 8 rejection).

Tests — 112/112 (99 + 5 + 8)

5 run: m_mod 12 (exported var + Inc() twice), m_multi 7 (two modules), m_arr 42 (exported array + M.arr[2]), m_outer 11 (module proc touches outer global), m_const 7 (exported CONST). 8 rejections, each exactly one error: m_bad_priv 201 (private M.secret), m_bad_nest 230 (module in procedure), m_bad_dup 200 (duplicate module), m_bad_exp 200 (duplicate export), m_bad_ghost 201 (EXPORT ghost), m_bad_param 233 (M.Add(5)), m_bad_func 233 (M.Get() in expression), m_bad_end 202 (END N mismatch).

Bugs found and fixed

  1. Exported CONST read back 0: ExpQual returned an empty name for non-Var kinds, so GlobalAddr("") hit a temp slot. Qualifying KindConst fixes the load (M.C → 7) with no ATG change (address + LoadIndir path is shared with vars).
  2. Ghost EXPORT silently accepted: ExitModule dropped undeclared names. Now returns FALSE and the ATG reports 201 at END.
  3. Duplicate module gave 2 errors (200 at name + 200 at its EXPORT, since ModuleAddExp ran with no module context). enterOk guard → 1 error.

Known limits (each with a rejection test or documented)

Module procedures take no parameters (M.Add(5) → 233), module functions are statement-only (M.Get() in an expression → 233), no type exports (M.T → 221 + 230 via QualIdent), no DEFINITION/IMPLEMENTATION split, no module BEGIN bodies, NoteSfx/NoteIdx VAR-tail helpers still unwired (a[i]/p^/fields as VAR actuals → 233), max 8 modules / 32 exports per module / 128 exports total.

Text output (I/O builtins, verified this session)

No new machinery needed for simple output — step-5 builtins cover it: WriteString("...") / WriteString(charArrayVar) emits raw bytes via SYSTEM service 1 (no newline); WriteInt(n) emits unsigned decimal + CRLF via the embedded EmitPrint helper; a declared ExitCode global is printed the same way at program end. Demo verified under mcint: WriteString("Hello"); WriteString(" world"); WriteInt(42); → Hello world42\r\n. Limits: no WriteLn, single-CHAR literals rejected (210 — "A" lexes as CHAR, not a string), WriteInt wraps negatives, one value per call. A WriteLn/WriteChar would be a small isolated addition (new ATG builtin + MGen emitter + tests).