summary_m2comp_step5.md 4.9 KB

m2comp step 5 — separate compilation (tag: m2comp-step5)

Single-image separate compilation, v1 step-11 style: M2comp lib.def impl.mod prog.mod compiles definitions, then implementations, then exactly one program into one <Prog>.MC4 (depCount 0). One .LST per input, fail-fast. Suite 54/54 (41 inherited + 6 run + 7 reject); all single-file images byte-identical to step 4 (no drift).

Session model

  • Grammar: M2comp = Unit ".", Unit = "DEFINITION" DefUnit | "IMPLEMENTATION" ImplUnit | Module. New keywords DEFINITION / IMPLEMENTATION (token numbers shifted; Msg regenerated from .err automatically, no test depends on numbers).
  • DEFINITION MODULE L — CONST (integer-family only, else 230) / TYPE / VAR + procedure HEADINGS (no bodies). All top-level names auto-exported at END (parameters excluded).
  • IMPLEMENTATION MODULE L — needs L defined (201) and not yet implemented (200); private CONST/TYPE/VAR + bodies + optional BEGIN init body. Every heading needs its body at END (231).
  • Program MODULE — IMPORT L / FROM L IMPORT x need L defined AND implemented (201). Unknown-module imports stay unchecked stubs (legacy behavior, unchanged).
  • Order inside a session is free except program-last (grammar checks enforce per-library order); a second program file fails the session with Incorrect source (no .LST mark). A session without a program emits nothing (Incorrect source).

What was built

  • src/SymTab: session state (no per-file Init; driver inits once) — SetUnit/UnitKind, BeginDef/EndDef (auto-export + def registry), DefExists/ImplDone, OpenImpl/EndImpl (fresh scope + interface materialization), EnterHeading, BeginBody/EndBodyHeader (body headers re-parse into the heading entry: signature snapshot compare, 231 on mismatch/double-body), EnterAlias/SetProcNum (FROM-import materialization), ExpConstVal + const-value export snapshot (integer family). 231 message already existed in the driver (compiler.frm).
  • Grammar: DefDecl/ProcHeading, ImplUnit/ImplDecl/ImplProcBody, rewritten Import (second ax attribute carries alias decl nodes) + ImpName (201 unknown / 200 duplicate / 230 unsupported), InProc guard (session FROM-import inside a PROCEDURE is 230), InDef guard (REAL/STRING CONST in a definition is 230). ConstFold accepts qualified Lib.C (bounds across files).
  • Driver (compiler.frm): SymTab.Init once, per-file ResetErrors (the firstErr list never reset — stale errors leaked into later files' listings), unit-root collection, impl→def merge by module name, ONE MGen.EmitModule over the session root (program decls chained after the library units; zero units reuses the program root verbatim).
  • src/MGen (3 small changes): EmitDecls shares the library global slot for tag-stamped alias nkVars (no double slots); ConstFindMod + tag-aware PushConstName/ConstTextOf with an export-table fallback (plain IMPORT + Lib.C needs no materialization); EmitExpr routes qualified const designators (aux = KindConst) to the const path.
  • run_tests.sh: expect_run_files / expect_fail_files / expect_fail_multi (MC4-aware, timeout-guarded like the rest).

Tests — 54/54

New tests/d_* (hand-computed ExitCodes): DBasic 60 (FROM + qualified, var/proc), DFrom 35 (two libs, const/var/proc, cross-def import), DMulti 68 (qualified VAR actual), DInit 7 (impl BEGIN runs: preset 2 vs zeroed 0), DFunc 81 (nested function calls), DType 40 (FROM array type + qualified Lib.Point record). Rejections: unknown definition (201), signature mismatch (231), missing body (231), unimplemented import (201), duplicate definition (200), private access (201), second program file (driver-level).

Bugs found (all fixed, all covered)

  • Unit double-consumed MODULE (every file rejected) — Module keeps its own keyword.
  • ProcHeading ate the ; its caller also expected (def files rejected with cascade ';' expected).
  • Driver stage machine rejected def-after-impl; relaxed to program-last (v1 interleaves per lib too).
  • Test-only: CONST dbl = step + step doesn't fold (ConstFold is literal/negation/name-only, pre-existing single-file limit) — test lib uses a literal + a bound-via-imported-const instead.

Limits (documented, next steps)

  • One program per session; def/impl file order must respect dependencies (imports need completed implementations).
  • Interface consts are integer-family only (CHAR/BOOLEAN/ordinal included); REAL/STRING consts in definitions are 230.
  • Bodies are define-before-use inside an implementation (V2 has no FORWARD, same discipline as everywhere else).
  • Unknown-module imports unchecked (legacy); proc/type/var/const exports only (no opaque or procedure types yet).
  • Next candidates: opaque + procedure types, WITH/CASE, LONGINT/LONGREAL quads, multi-.MC4 depCount emission.