# m2comp step 5 — separate compilation (tag: `m2comp-step5`) Single-image separate compilation, v1 step-11 style: `M2comp lib.def lib.mod prog.mod` compiles definitions, then implementations, then exactly one program into one `.MC4` (depCount 0). Definition and implementation share their basename (`toto.def` / `toto.mod`) and hence one `.LST` (the implementation listing overwrites the definition's); one `.LST` per basename otherwise, fail-fast. Suite 55/55 (41 inherited + 7 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 `nkVar`s (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 — 55/55 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). Showcase `tests/showcase5.mod` (+ `s5lib.def` / `s5lib.mod`) tours every interface kind in one program and prints 46. 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.