summary_m2comp_step2.md 3.5 KB

m2comp step 2 — SymTab semantic analysis (tag: m2comp-step2)

Coco/R lexer/parser (step 1) + static type checking, still no codegen. Follows Blaise Phase 2 (type system) with the semantic pass folded into grammar actions, Wirth single-pass style.

What was built

  • src/SymTab.def/.mod — ported from CocoGm2 Test1 (proven under gm2 -fiso) and extended for procedures and local modules:
    • procedures: nested, value/VAR params (pending-list entry), function results; signatures persist in procs[]/params[] after body scopes pop (no FORWARD headings, so no 231);
    • local modules (Wirth form): body scope + (module, name) export table; M.x resolves via exports, or the live body scope from inside M itself (SelfKind/SelfType/SelfProc);
    • call checking with an 8-deep frame stack (nested calls safe); invalid callees get a dead frame to suppress cascades;
    • open arrays (formal-only): NewOpen/WrapArray/IsOpen; VAR/value rules enforced at declaration (230 otherwise);
    • loop-depth tracking for EXIT validation (230).
  • src/M2comp.atg — full attribution: every Type/Expr/Stat production carries SymTab.TypeIndex attributes; Designator returns type + kind + proc number with record/field (215/216), array (217/218, per-index stepping), pointer (219) checks; AssignOrCall checks storability + Assignable (210) and call arity/formals (233, incl. proper-proc-in-expression); RETURN checked against function context (232); conditions (214); FOR control/bounds (220); sets via 222; cycles (223); ordinals (224); MODULE/END match (202); dup/undeclared (200/201).
  • Checks NOT yet done (no codegen info): WITH (no grammar support), enum/set-literal construction (kept in SymTab, grammar later), HIGH, range checks, NIL dereference.

Tests — 19/19 (run_tests.sh)

Accept: ok_minimal, ok_proc, showcase (extended with real y/z declarations — calls to merely-imported names are now rightly 233 — plus Chars/sa for a VAR open-formal call), bad_mismatch still rejected. Reject (one per code): t_dup 200, t_undecl 201, t_assign 210, t_arith 211, t_bool 212, t_rel 213, t_cond 214, t_field 216, t_index 217, t_ptr 219, t_for 220, t_call/t_call2 233, t_return 232, t_exit 230.

Bugs found by the tests (both fixed, both covered)

  • EBNF grouping trap: in A | B | C {loop} Coco/R binds the loop to the LAST alternative only, so -v + 1 broke after step-2 rewrite. Fixed with a parenthesized group + shared loop.
  • Leaked call frames: Fact pushed a dead frame for every bare designator but popped only on the call path, silently shifting all later arity checks (e.g. P(i) vs 2 formals passed). Fixed with balanced push/pop (pushed flag); verified P()/P(i)/P(i,TRUE)/ P(i,TRUE,i) behave correctly.

Docs added (reference for next steps)

  • docs/AST.pdf (Mössenböck: ASTs via plain Coco/R output attributes — recipe for the step-3 AST layer),
  • docs/oberon-master/ (freeoberon Oberon compiler: hand-written recursive descent + Item-technique generator; pointer-linked SymTable studied but NOT mimicked — flat TypeIndex design stays, since the future MC64 MGen needs it),
  • docs/Document 1 sans titre renamed to BenjaminKowarsh.txt.

Next

Step 3: AST layer per AST.pdf (node modules, output attributes; pointer-record tree à la Oberon dsc/next), then MC64 MGen backend behind it. SymTab API is already backend-ready (ProcNum, ByNum queries, export tables).