DEFINITION MODULE SymTab; IMPORT AST; (* Symbol table with static type checking for the M2comp compiler (Modula-2 program modules, step 2: semantic analysis, no codegen). Base: Test1 SimpleMod2 table (flat scopes with levels, type descriptors with aliases, integer-family leniency, InvalidType cascade suppression). Extensions for step 2: - procedures: nested, value/VAR params, function results; signatures persist in procs[] after body scopes pop (no FORWARD headings) - local modules (Wirth form): body scope + export table for M.x - call checking with an 8-deep frame stack (nested calls safe); invalid callees get a dead frame to suppress cascades - open arrays (formal-only) + WrapArray/IsOpen helpers - loop-depth tracking for EXIT validation (230) Error codes follow m2c/Test1: 200 duplicate, 201 undeclared, 202 name mismatch, 210 bad assignment, 211 arithmetic, 212 boolean operand, 213 comparison, 214 condition, 215 not a record, 216 unknown field, 217 not an array, 218 bad index, 219 not a pointer, 220 FOR misuse, 221 not a type, 222 set mismatch, 223 cyclical type, 224 ordinal required, 230 unsupported construct (EXIT outside LOOP, open array outside formal), 232 bad RETURN, 233 invalid call. 231 (forward mismatch) unused: no FORWARD. - flat scopes with levels: globals at level 0 (duplicates within one level rejected, shadowing allowed in nested scopes) - every symbol carries a type descriptor index (InvalidType if unknown, e.g. imported names); unknown types suppress follow-on errors to avoid cascades *) CONST MaxSyms = 256; InvalidType = -1; (* symbol kinds *) KindConst = 0; KindType = 1; KindVar = 2; KindImport = 3; KindModule = 4; KindPredef = 5; KindField = 6; KindProc = 7; KindParam = 8; KindVarPar = 9; (* type classes returned by ClassOf *) ClInvalid = 0; ClInt = 1; ClReal = 2; ClChar = 3; ClBool = 4; ClEnum = 5; ClArray = 6; ClRecord = 7; ClSet = 8; ClPtr = 9; ClStr = 10; (* operator codes; all groups use distinct ranges so a tree-walking backend can dispatch on the code alone *) OpEq = 20; OpNeq1 = 21; OpNeq2 = 22; OpLt = 23; OpLe = 24; OpGt = 25; OpGe = 26; OpIn = 27; (* operator codes for AddOp / MulOp; MulOp codes are offset *) OpAdd = 0; OpSub = 1; OpOr = 2; OpTimes = 10; OpSlash = 11; OpDiv = 12; OpMod = 13; OpAnd = 14; TYPE Name = ARRAY [0 .. 63] OF CHAR; TypeIndex = INTEGER; BoundsArr = ARRAY [0 .. 7] OF INTEGER; (* Per-dimension bounds for WrapArrayB (Coco/R attribute type). *) (* ---------------- symbols and scopes ---------------- *) PROCEDURE Init; (* Clears the table and enters predefined identifiers (INTEGER, CARDINAL, SHORTINT, LONGINT, REAL, LONGREAL, CHAR, BOOLEAN, TRUE, FALSE, NIL). *) PROCEDURE Enter (name: ARRAY OF CHAR; kind: INTEGER): BOOLEAN; (* Enters name at the current scope level (type InvalidType). Returns FALSE on duplicate within the same level. *) PROCEDURE EnterPending (name: ARRAY OF CHAR; kind: INTEGER): BOOLEAN; (* Like Enter, but remembers the entry for a later FixPending call (used for VAR identifier lists whose type is parsed afterwards). Returns FALSE on duplicate (entry not recorded). *) PROCEDURE FixPending (t: TypeIndex); (* Assigns type t to all pending entries, clears the buffer. *) PROCEDURE FieldPending (rec: TypeIndex; name: ARRAY OF CHAR): BOOLEAN; (* Records a field name for record descriptor rec (type fixed later with FixPendingF). Returns FALSE on duplicate field. *) PROCEDURE FixPendingF (rec: TypeIndex; t: TypeIndex); (* Assigns type t to pending fields owned by rec. *) PROCEDURE Lookup (name: ARRAY OF CHAR): BOOLEAN; (* TRUE if name is visible (innermost scope wins). *) PROCEDURE SymType (name: ARRAY OF CHAR): TypeIndex; (* Type of innermost visible entry, InvalidType if absent. *) PROCEDURE SetSymType (name: ARRAY OF CHAR; t: TypeIndex); (* Sets type of innermost visible entry. *) PROCEDURE SymKind (name: ARRAY OF CHAR): INTEGER; (* Kind of innermost visible entry, -1 if absent. *) PROCEDURE Equal (a, b: ARRAY OF CHAR): BOOLEAN; PROCEDURE PushScope; PROCEDURE PopScope; (* WITH statement support: PushRecord pushes a record's fields. *) PROCEDURE PushRecord (t: TypeIndex): BOOLEAN; (* Pushes a scope containing t's fields (as KindField). FALSE if t is not a record type (or invalid). Caller must PopScope after. Kept for a future WITH statement; unused by the step-2 grammar. *) PROCEDURE PrintTable; (* ---------------- procedures ---------------- *) (* Signatures live in procs[]/params[] keyed by a per-procedure number stored on the symbol, so they survive PopScope (nested and exported procedures stay callable). No FORWARD headings in step 2. *) PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN; (* Enters a KindProc name in the current scope, assigns the next proc number with an empty signature. FALSE on duplicate or table full. Sets the current-proc context for EnterParamPending calls. *) PROCEDURE OpenProcScope; (* Pushes a body scope for the current procedure (depth + 1, fresh return-type context). *) PROCEDURE CloseProc; (* Pops the procedure body scope, restores the enclosing context. *) PROCEDURE EnterParamPending (name: ARRAY OF CHAR; isVar: BOOLEAN): BOOLEAN; (* Like EnterPending for FPSection identifier lists whose type is parsed afterwards. FALSE on duplicate (not recorded). *) PROCEDURE FixParamPending (t: TypeIndex): BOOLEAN; (* Enters all pending parameters of the current procedure with type t (KindParam or KindVarPar), appends them to its signature. FALSE if any entry failed (duplicate or table full). *) PROCEDURE SetProcRet (t: TypeIndex); (* Sets the return type of the current procedure (InvalidType = proper procedure) and of the current return-type context. *) PROCEDURE ProcNum (name: ARRAY OF CHAR): INTEGER; (* Proc number of the visible procedure, or -1 if absent / not one. *) PROCEDURE ProcRet (name: ARRAY OF CHAR): TypeIndex; PROCEDURE ProcNPar (name: ARRAY OF CHAR): CARDINAL; PROCEDURE ParamType (name: ARRAY OF CHAR; i: CARDINAL): TypeIndex; PROCEDURE ParamIsVar (name: ARRAY OF CHAR; i: CARDINAL): BOOLEAN; (* Signature queries by name (InvalidType/0/FALSE if absent). *) PROCEDURE ProcRetByNum (num: INTEGER): TypeIndex; PROCEDURE ProcNParByNum (num: INTEGER): CARDINAL; PROCEDURE ParamTypeByNum (num: INTEGER; i: CARDINAL): TypeIndex; PROCEDURE ParamIsVarByNum (num: INTEGER; i: CARDINAL): BOOLEAN; (* Same queries keyed by procedure number (exported module procs). *) PROCEDURE InProc (): BOOLEAN; (* TRUE inside a procedure body. *) PROCEDURE InFunction (): BOOLEAN; (* TRUE inside a function (non-InvalidType return) body. *) PROCEDURE CurRet (): TypeIndex; (* Current return type (InvalidType = proper procedure or outside). *) (* ---------------- local modules (Wirth form) ---------------- *) (* Local MODULEs nest at any level. EXPORT names resolve at END into the export table keyed by (module, name); M.x qualified access consults it, or the live body scope from inside M itself. *) PROCEDURE EnterModule (name: ARRAY OF CHAR): BOOLEAN; (* Enters KindModule + pushes the body scope + module context. FALSE on duplicate. *) PROCEDURE ModuleAddExp (name: ARRAY OF CHAR): BOOLEAN; (* Records an export name for the current module. FALSE if not in a module, on duplicate, or when the export list is full. *) PROCEDURE ExitModule (): BOOLEAN; (* Resolves exports against the body scope into the export table, pops scope + context. FALSE when an EXPORT name was not declared (ghost export). *) PROCEDURE InModule (): BOOLEAN; (* TRUE inside a local MODULE body (including its procedures). *) PROCEDURE CurModName (VAR m: Name); (* Innermost module name, empty if none. *) PROCEDURE ExpKind (mod, exp: ARRAY OF CHAR): INTEGER; (* Exported kind, -1 if absent. *) PROCEDURE ExpType (mod, exp: ARRAY OF CHAR): TypeIndex; (* Exported type (InvalidType if absent / not a typed export). *) PROCEDURE ExpProc (mod, exp: ARRAY OF CHAR): INTEGER; (* Exported proc number, -1 if absent / not a procedure. *) PROCEDURE SelfKind (mod, exp: ARRAY OF CHAR): INTEGER; (* Kind of exp as M.exp from inside M itself (live body scope at body level, since exports resolve only at END). -1 if absent. *) PROCEDURE SelfType (mod, exp: ARRAY OF CHAR): TypeIndex; (* Type of an M.exp self reference (InvalidType if absent). *) PROCEDURE SelfProc (mod, exp: ARRAY OF CHAR): INTEGER; (* Proc number of an M.exp self reference (-1 if absent). *) (* ---------------- call checking ---------------- *) (* An 8-deep frame stack keeps nested calls (f(g(x))) safe. A dead frame (proc -1) accepts everything: the grammar pushes one for invalid callees so actuals parse without cascade errors. *) PROCEDURE CallBeginNum (num: INTEGER); (* Opens a call frame for procedure num (-1 = dead frame). *) PROCEDURE CallActual (t: TypeIndex; isVar: BOOLEAN): BOOLEAN; (* Checks one actual against the current formal (VAR formals need a storable designator of identical type, value formals need assignment compatibility) and advances. FALSE on mismatch. *) PROCEDURE CallEndNum (n: CARDINAL): BOOLEAN; (* Closes the frame; FALSE on arity mismatch (n # formals). *) (* ---------------- loops ---------------- *) PROCEDURE LoopEnter; PROCEDURE LoopExit; PROCEDURE InLoop (): BOOLEAN; (* Innermost LOOP tracking for EXIT validation (error 230). *) (* ---------------- type descriptors ---------------- *) PROCEDURE NewAlias (): TypeIndex; PROCEDURE NewSub (base: TypeIndex): TypeIndex; PROCEDURE NewEnum (): TypeIndex; PROCEDURE NewArray (elem: TypeIndex): TypeIndex; PROCEDURE NewOpen (elem: TypeIndex): TypeIndex; PROCEDURE WrapArray (elem: TypeIndex; dims: CARDINAL): TypeIndex; (* WrapArray nests elem in dims ARRAY levels (dims = 0 gives an open array). Open arrays are formal-only (grammar reports 230). *) PROCEDURE IsOpen (t: TypeIndex): BOOLEAN; PROCEDURE NewSubB (base: TypeIndex; lo, hi: INTEGER): TypeIndex; PROCEDURE NewArrayB (elem: TypeIndex; lo, hi: INTEGER): TypeIndex; PROCEDURE WrapArrayB (elem: TypeIndex; los, his: BoundsArr; dims: CARDINAL): TypeIndex; (* Bounded nesting for ARRAY index lists (dims = 0 gives an open array). Bounds come from folded index expressions (grammar). *) PROCEDURE IndexBounds (t: TypeIndex; VAR lo, hi: INTEGER): BOOLEAN; (* Finite bounds of an index type: subrange (stored), CHAR (0..255), BOOLEAN (0..1). FALSE for INTEGER/CARDINAL (unbounded) and anything else (grammar reports 230). *) PROCEDURE ArrayLo (t: TypeIndex): INTEGER; PROCEDURE ArrayHi (t: TypeIndex): INTEGER; PROCEDURE ArrayLen (t: TypeIndex): CARDINAL; PROCEDURE TypeSlots (t: TypeIndex): CARDINAL; (* Stack slots for a value: 1 per scalar/pointer/set, len*elem for arrays, summed members for records. Cycle-guarded. *) PROCEDURE FieldOffset (rec: TypeIndex; name: ARRAY OF CHAR): INTEGER; (* Slot offset of a record field in declaration order. *) PROCEDURE NewRecord (): TypeIndex; PROCEDURE NewSet (base: TypeIndex): TypeIndex; PROCEDURE NewPtr (base: TypeIndex): TypeIndex; PROCEDURE NewStr (): TypeIndex; (* Fresh descriptors; base/elem may be InvalidType. InvalidType is returned when the table is full. *) PROCEDURE SetTarget (t, base: TypeIndex); (* Sets an alias target (TYPE declaration completion). *) PROCEDURE IntType (): TypeIndex; PROCEDURE RealType (): TypeIndex; PROCEDURE CharType (): TypeIndex; PROCEDURE BoolType (): TypeIndex; PROCEDURE ClassOf (t: TypeIndex): INTEGER; (* Resolves aliases; InvalidType maps to ClInvalid. *) PROCEDURE IsIntFamily (t: TypeIndex): BOOLEAN; (* INTEGER, CARDINAL or subrange thereof (Invalid suppresses). *) PROCEDURE SameType (a, b: TypeIndex): BOOLEAN; (* Same resolved descriptor (Invalid suppresses). *) PROCEDURE FieldExists (rec: TypeIndex; name: ARRAY OF CHAR): BOOLEAN; PROCEDURE FieldType (rec: TypeIndex; name: ARRAY OF CHAR): TypeIndex; PROCEDURE ArrayElem (t: TypeIndex): TypeIndex; PROCEDURE PtrBase (t: TypeIndex): TypeIndex; (* ---------------- predicates used by grammar checks ---------------- *) (* All return TRUE if either operand is InvalidType (no cascades). *) PROCEDURE Assignable (src, dst: TypeIndex): BOOLEAN; (* Assignment compatibility (210). *) PROCEDURE ArithCheck (l, r: TypeIndex; divmod: BOOLEAN; VAR res: TypeIndex): BOOLEAN; (* + - * / (divmod FALSE) or DIV MOD (TRUE); res is result type (211). *) PROCEDURE UnaryCheck (t: TypeIndex; VAR res: TypeIndex): BOOLEAN; (* Unary + - (part of 211). *) PROCEDURE BoolCheck (t: TypeIndex): BOOLEAN; (* BOOLEAN required: NOT/AND/OR operands (212), conditions (214). *) PROCEDURE RelCheck (l, r: TypeIndex; op: INTEGER): BOOLEAN; (* = # <> < <= > >= IN (213/222 chosen by caller via op). *) PROCEDURE EqCheck (l, r: TypeIndex): BOOLEAN; (* = # compatibility, also reused for CASE label matching. *) PROCEDURE InCheck (l, set: TypeIndex): BOOLEAN; PROCEDURE SetElemCheck (first, elem: TypeIndex): BOOLEAN; PROCEDURE SetFor (elem: TypeIndex): TypeIndex; (* Fresh SET OF elem descriptor for set literals. *) PROCEDURE StrLen (s: ARRAY OF CHAR): CARDINAL; (* ---------------- compile-time constants ---------------- *) (* Integer values of CONST declarations, recorded at parse time for array/subrange bound folding (grammar) and MGen lookups. Chained consts fold (Neg = -N + 2 with N = 10 gives -8); anything else stays undefined (bounds then report 230). *) PROCEDURE NoteConst (name: ARRAY OF CHAR; v: INTEGER; ok: BOOLEAN); (* Records the folded value of a just-declared CONST (ok = foldable). *) PROCEDURE ConstVal (name: ARRAY OF CHAR; VAR v: INTEGER): BOOLEAN; (* Value of the visible integer CONST (TRUE/FALSE predefs included); FALSE if absent, non-const, or undefined. *) PROCEDURE ConstFold (n: AST.Node; VAR v: INTEGER): BOOLEAN; (* Folds nkInt / nkUn(+/-) / const nkName nodes. Needs AST import. *) END SymTab.