# m2comp step 3 — AST layer (tag: `m2comp-step3`) Coco/R frontend + SymTab checks (steps 1–2) + an abstract syntax tree built alongside parsing, per H. Mössenböck, "How to Build Abstract Syntax Trees with Coco/R" (`docs/AST.pdf`): every nonterminal carries an output attribute returning its subtree, built with plain semantic actions. No codegen yet (step 4: MC64 `MGen` walking this tree). ## What was built - `src/AST.def/.mod` — 30 node kinds in three families: decls (`Module/Block/Import/ImpName/Export/Const/Type/Var/ Member/Param/Proc`), statements (`Assign/Call/If/While/Repeat/ Loop/For/Return/Exit`), expressions (`Bin/Un/Name/Int/Real/Char/ Str/Field/Index/Deref/CallExpr`). Nodes are heap pointer records (`NEW`) with child links (`left/right/extra/more`) and a sibling link (`next`) — the `dsc`/`next` shape borrowed from the Oberon `SymTable` (`docs/oberon-master`), while `SymTab` itself stays flat. Types are stored as descriptor indices (no type subtree); unary ops use `opNot/opNeg/opPos`, binary ops reuse `SymTab` codes. Error-tolerant: nodes build even with `InvalidType`, so rejected sources still dump. Includes `Append`, `SetChainType` (identifier lists typed after the fact), `ParseInt` (decimal + `H` hex), and a NIL-safe depth-capped `DumpTree`. - `src/M2comp.atg` — full attribution with step-2 checks untouched: `MkModule/MkBlock` roots dumped by the `Module` action; `ELSIF` chains attach at the deepest else (standard `If(a,A,If(b,B,C))` nesting); `MkFor` carries optional BY; calls share the step-2 frame machinery (`MkCall`/`MkCallExpr` with pnum); designators become `Name/Field/Index/Deref` chains (indices appended per bracket); literals folded (`0FFH`→255, `'q'`→113, reals/strings kept as text). - `run_tests.sh` — new `expect_dump` assertions on tree content (`Module Showcase`, `Proc Work`, `CallExpr`, `Field q`, `Int 255`, `Proc P`). ## Tests — 25/25 19/19 accept/reject from step 2 unchanged + 6 AST content checks. ## Bugs found (both fixed, both covered) - `ParseInt` detected hex only when the digit scan stopped at `H`, so `0FFH` dumped as `Int 0`. Fixed by checking the trailing `H` first, then hex-parsing the prefix. - `NEW` on `POINTER TO RECORD` needs `FROM Storage IMPORT ALLOCATE` under `gm2 -fiso` (ISO rule). ## Notes - Dump verified by hand on `showcase`: nested procs, recursion, `p^`, `m[1,2]`, `Local.q`, `ELSIF` nesting, `LOOP/EXIT`, `FOR` with/without `BY`, precedence (`Min(3,4)+(2*3-4/2)`). - Known simplifications for step 4: string/real literals kept as text (truncated at 63 chars); record members build nodes that the `Type` alternative discards (types resolve via `SymTab`). ## Next Step 4: MC64 `MGen` backend — tree-walking emitter (`mc64-spec.md` §11 opcodes, frames §5.2, `.MC4` images runnable under `mcint`), reusing the `m2c` lowering as reference. `SymTab` (`ProcNum`, `ByNum` queries, export tables) and `AST` (pnum/typ on nodes) are already backend-ready.