143/143 tests green (137 + 3 run + 3 rejection), mc64 boot + example green, both Showcases unchanged (157, 83).
Complete single-file local modules: optional BEGIN..END init bodies run
at startup in declaration order, including calls to the module's own
procedures (plain and M.-qualified).
MGen: module inits compile as parameterless proper procedures
(ModInitBegin allocates a proc-table number + ENTER, ModInitEnd
emits LEAVE); BeginBody calls them in order after const inits,
before program statements. Max 8 inits (module limit), sharing the 64
proc numbers (print helper still takes maxNum+1 at EndModule).M2c.atg ModuleDecl: optional BEGIN StatSeq, jumped over inline
(Jmp/DefLabel like procedures), skipped entirely for
nested/duplicate modules. RETURN in a body is already 232 (not in a
proc); header updated (no bodies dropped).SymTab: modLevs[] records each module's body scope level;
SelfKind(mod, exp) resolves M.x against the live body scope (the
export table only fills at END), guarded to body level so nested
scopes can't hijack; SelfQual builds M.x for codegen.M2c.atg call sites use it: DesignTail (vars/consts/procs),
CallTail (by-number via ProcNum), Fact (return types),
QualIdent (M.T inside M).3 run: b_init 42, b_order 111 (two inits, order + cross-module read),
b_call 15 (plain Inc() and M.Inc() from init). 3 rejections, each
exactly one error: b_bad_ret 232, b_bad_nest 230, b_bad_dup 200.
M.Inc() inside M gave 201: the export table is empty until END.
Self-scope fallback fixes vars, consts, procs, and types uniformly.x = -1 & (y = -1) in generated M2cP.mod — minimized to
IF a = -1 & (b = -1) THEN END in 20 lines. Workaround: nested IFs
with < 0 (single F() = -1 and (x < 0) & ... shapes compile).
New rule: never compare against a negative literal inside an & chain.DEFINITION/IMPLEMENTATION split (the remaining big item), WITH-field
VAR actuals, CHAR/BOOLEAN tails as VAR actuals, open-to-open.
Showcase3 (A.x*100 + B.y + C.cnt + D.val = 168) exposed a real bug:
with an early plain init plus a later procedure-owning module, the run
printed 72 (A.x = 0, C.cnt = 15). Root cause: ModInitBegin took
maxNum+1 from emission order while SymTab numbers procedures from a
separate declaration-order pool — so module A's init stole C.Inc's
number 1, and ProcEntry(1) for Inc overwrote the init's table cell
(startup then called Inc instead of A's init: 5 + 10 = 15 in cnt,
x never assigned). Single-module tests passed only because each
procedure body was emitted before its module's init. Fix: init numbers
now come from SymTab.AllocInitNum(), sharing the nProcs pool
(procs[] entry initialized like a parameterless proper procedure).
Regression b_mix (110) locks the shape; suite 144/144, Showcases
unchanged (157, 83), Showcase3 prints 168.