DEFINITION MODULE AST; (* Abstract syntax tree for m2compiler-V3 (Stage A: node store only). This is the foundation for a two-phase frontend: productions build nodes here while parsing, and a later walk resolves names and lowers to QBE. Until the grammar is converted, nothing calls it. Design notes ------------ - Nodes live in a flat, pre-allocated arena and are referenced by an integer index (`Node`), exactly like `SymTab.TypeIndex`. This avoids pointers in the interface and keeps the structure self-hosting-friendly (no GC, bounded memory). - Every node has a `kind` (NKind) and a `ty` (a SymTab.TypeIndex set by the checker), plus up to `MaxChild` children. This mirrors the `armornick/CocoR` AST.java pattern (a Node base with per-construct subclasses) but flattened into one record array so Modula-2 can store it without variant-class machinery. - Text (identifiers, literals, operator spellings) is kept in a pooled string table; `TxtIndex` is an index into it. - `NoNode` is the null node. *) CONST NoNode = -1; NoTxt = -1; (* how many children a node may carry. Statements/expressions in Modula-2 nest shallowly (a body is a list, an expression is a spine); 8 covers every construct in the current grammar with room to spare. *) MaxChild = 8; (* ---- node kinds ---- Grouped by role; the numeric values are stable and may be stored in listings. *) (* module / unit structure *) NkUnit = 0; (* [0]=name txt, [1]=decls, [2]=body *) NkDefUnit = 1; NkImplUnit = 2; NkDeclSeq = 3; (* [0..n)=declarations *) NkImport = 4; (* [0]=module txt, [1..n)=names (or FROM list) *) (* declarations *) NkConstDecl = 10; (* [0]=name txt, [1]=value expr *) NkTypeDecl = 11; (* [0]=name txt, [1]=type *) NkVarDecl = 12; (* [0..n)=name txt, [1]=type (names then type) *) NkProcDecl = 13; (* [0]=name, [1]=formals, [2]=result type, [3]=body, [4]=forward flag node *) NkClassDecl = 14; NkFieldDecl = 15; (* type constructors *) NkTypeIdent = 20; (* [0]=name txt (possibly qualified: a.b) *) NkSubrange = 21; (* [0]=lo expr, [1]=hi expr *) NkEnum = 22; (* [0..n)=literal name txts *) NkArray = 23; (* [0..n-2)=index types, [last]=element type *) NkRecord = 24; (* [0..n)=fields *) NkSet = 25; (* [0]=base type *) NkPointer = 26; (* [0]=base type *) NkProcType = 27; (* [0]=formals, [1]=result type *) (* statements *) NkAssign = 40; (* [0]=designator, [1]=value *) NkCall = 41; (* [0]=callee, [1..n)=actuals *) NkIf = 42; (* [0]=cond, [1]=then, [2]=else (or NoNode) *) NkWhile = 43; (* [0]=cond, [1]=body *) NkRepeat = 44; (* [0]=body, [1]=until cond *) NkLoop = 45; (* [0]=body *) NkExit = 46; NkFor = 47; (* [0]=control var, [1]=lo, [2]=hi, [3]=by, [4]=body *) NkCase = 48; (* [0]=selector, [1..n)=case arms *) NkCaseArm = 49; (* [0..n-1)=labels, [n]=body *) NkWith = 50; (* [0..n-1)=designators, [n]=body *) NkReturn = 51; (* [0]=expr (or NoNode) *) NkBlock = 52; (* [0..n)=statements *) NkHalt = 53; NkGoto = 54; (* [0]=label ident *) NkLabel = 55; (* [0]=label ident *) (* expressions *) NkBinExpr = 70; (* [0]=left, [1]=right; `op` holds the operator *) NkUnary = 71; (* [0]=operand; `op` holds the operator *) NkDesignator = 72; (* [0]=base, [1..n)=selectors *) NkSelector = 73; (* .field / [idx] / ^ ; `op` distinguishes *) NkIdent = 74; (* [0]=name txt, [1]=qualifier txt (or NoTxt) *) NkIntLit = 75; (* [0]=digits txt *) NkRealLit = 76; NkStrLit = 77; NkCharLit = 78; NkBraceLit = 79; (* [0]=type, [1..n)=elements *) NkSetLit = 80; (* ---- relational / arithmetic / selector operator codes ---- *) OpAdd = 0; OpSub = 1; OpOr = 2; OpMul = 3; OpDiv = 4; OpMod = 5; OpAnd = 6; OpEq = 7; OpNe = 8; OpLt = 9; OpLe = 10; OpGt = 11; OpGe = 12; OpIn = 13; OpNot = 14; (* selector codes (NkSelector.op) *) SelField = 0; SelIndex = 1; SelDeref = 2; TYPE Node = INTEGER; (* index into the arena, or NoNode *) TxtIndex = INTEGER; (* index into the text pool, or NoTxt *) (* ---------------- lifecycle ---------------- *) PROCEDURE Init; (* Clears the arena and text pool. *) (* ---------------- text pool ---------------- *) PROCEDURE AddTxt (s : ARRAY OF CHAR) : TxtIndex; (* Interns s, returning its index (deduplicated). *) PROCEDURE Txt (i : TxtIndex; VAR s : ARRAY OF CHAR); (* Copies the i-th text into s (empty when out of range). *) (* ---------------- node construction ---------------- *) PROCEDURE MakeNode (kind : INTEGER) : Node; (* A fresh node of the given kind, no children, ty = SymTab.InvalidType (the caller sets it). NoNode when the arena is full. *) PROCEDURE MakeLeaf (kind : INTEGER; name : ARRAY OF CHAR) : Node; (* A fresh node with child[0] set to the interned text of name. *) PROCEDURE MakeBin (kind, op : INTEGER; l, r : Node) : Node; (* A fresh node with op and two children (binary expr, assign, …). *) PROCEDURE MakeUn (kind, op : INTEGER; e : Node) : Node; (* A fresh node with op and one child (unary expr, selector, …). *) (* ---------------- node accessors ---------------- *) PROCEDURE Kind (n : Node) : INTEGER; PROCEDURE Op (n : Node) : INTEGER; PROCEDURE SetOp (n : Node; op : INTEGER); PROCEDURE Ty (n : Node) : INTEGER; (* The SymTab.TypeIndex attached by the checker (InvalidType until then). Declared as INTEGER to avoid a circular import with SymTab. *) PROCEDURE SetTy (n : Node; t : INTEGER); PROCEDURE NChild (n : Node) : CARDINAL; PROCEDURE Child (n : Node; i : CARDINAL) : Node; PROCEDURE AddChild (n : Node; c : Node) : BOOLEAN; PROCEDURE SetChild (n : Node; i : CARDINAL; c : Node); (* Replaces child i, extending the node when i = NChild. *) (* ---------------- listing (debug) ---------------- *) PROCEDURE Dump (n : Node; depth : CARDINAL); (* Writes a one-line-per-node indented tree to StdOut (via FileIO). Used by the Stage-A self-test and future debugging. *) END AST.