DEFINITION MODULE SymTab; (* Symbol table with static type checking for the M2c compiler (Modula-2 program modules, procedures arrive in a later phase). - flat scopes with levels: globals at level 0, WITH statement bodies push the record's fields as an inner scope (shadowing allowed, duplicates within one level rejected) - every symbol carries a type descriptor index (InvalidType if unknown, e.g. imported names); unknown types suppress follow-on errors to avoid cascades - type descriptors: aliases (one per TYPE declaration, so self-references such as POINTER TO Person resolve), subranges, enumerations, arrays, records (with field lists), sets, pointers, predefined types, string literals - INTEGER, CARDINAL and subranges thereof form one lenient "integer family"; mixed INTEGER/REAL arithmetic is rejected *) 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 for RelCheck *) OpEq = 0; OpNeq1 = 1; OpNeq2 = 2; OpLt = 3; OpLe = 4; OpGt = 5; OpGe = 6; OpIn = 7; (* operator codes for AddOp / MulOp *) OpAdd = 0; OpSub = 1; OpOr = 2; OpTimes = 0; OpSlash = 1; OpDiv = 2; OpMod = 3; OpAnd = 4; TYPE Name = ARRAY [0 .. 63] OF CHAR; TypeIndex = INTEGER; (* ---------------- 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 PendCount (): CARDINAL; (* Number of entries currently waiting for FixPending. *) PROCEDURE PendName (i: CARDINAL; VAR n: Name); (* Name of the i-th pending entry (empty string if out of range). Used by the backend to emit storage for VAR lists whose type is parsed after the identifiers. *) 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 SymLev (name: ARRAY OF CHAR): INTEGER; (* Scope level at declaration (-1 if absent). *) (* ---------------- local modules (single-file, step 6) *) (* Local MODULEs live at program level (no nesting, no bodies). EXPORT names are resolved at END (before PopScope) into an export table keyed by (module, name); MGen stores module vars qualified ("M.x") so FindVar has no collisions. *) PROCEDURE EnterModule (name: ARRAY OF CHAR): BOOLEAN; (* Enters KindModule + pushes body scope + module context. FALSE on duplicate. *) PROCEDURE ModuleAddExp (name: ARRAY OF CHAR): BOOLEAN; (* Records an export name for the current module (checked for duplicates in the export list). FALSE if not in a module. *) PROCEDURE ExitModule(): BOOLEAN; (* Resolves exports against the body scope, pops scope + context. TRUE if every EXPORT name was declared (FALSE = ghost export). *) PROCEDURE InModule (): BOOLEAN; (* TRUE inside a local MODULE body (including its procedures). *) PROCEDURE CurModName (VAR m: Name); (* Top 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 type-ish export). *) PROCEDURE ExpProc (mod, exp: ARRAY OF CHAR): INTEGER; (* Exported proc number, -1 if absent / not a procedure. *) PROCEDURE ExpQual (mod, exp: ARRAY OF CHAR; VAR qual: Name); (* Qualified MGen name ("M.x"), empty if absent / not a variable or constant. *) PROCEDURE SelfKind (mod, exp: ARRAY OF CHAR): INTEGER; (* Kind of exp as M.exp from inside M itself (live body scope, since exports resolve only at END). -1 if not applicable. *) PROCEDURE SelfQual (exp: ARRAY OF CHAR; VAR qual: Name); (* CurMod.exp for a self reference (call only when SelfKind # -1). *) (* ---------------- separate compilation units (step 11) *) (* DEFINITION modules publish every declared name into the export tables and record an in-memory interface (side table: kind, type, proc number); the body scope is popped. IMPLEMENTATION modules re-enter that interface into a fresh scope (no new MGen slots), match procedure headings via the forward machinery (231 on mismatch), and must supply every body (231). All units share one image: globals/procs accumulate, library inits run first. FROM-imported names materialize as real alias symbols (procs share the original number, so call checking is unchanged); VAR/CONST aliases map to the definition's global via GlobAlias. *) PROCEDURE ExitDefinition (): BOOLEAN; (* Publishes the current module scope, records the interface, pops scope + context. FALSE when a phase cap breaks (too many definitions, names, or exports). *) PROCEDURE OpenImplementation (name: ARRAY OF CHAR): BOOLEAN; (* Re-enters a recorded interface for its implementation body. FALSE when no definition is known (stale state untouched). *) PROCEDURE CloseImplementation (): BOOLEAN; (* 231-check (every definition proc got a body), pops scope + context. State is popped even on FALSE. *) PROCEDURE IsDefMod (name: ARRAY OF CHAR): BOOLEAN; (* TRUE for a module with a recorded definition interface. *) PROCEDURE ImpBind (mod, exp: ARRAY OF CHAR): BOOLEAN; (* Records a FROM-import alias target ("mod.exp"). FALSE when the export is unknown or the alias table is full. *) PROCEDURE ImpUnbind (name: ARRAY OF CHAR); (* Drops an alias binding (failed materialization cleanup). *) PROCEDURE EnterImpProc (name: ARRAY OF CHAR; pnum: INTEGER): BOOLEAN; (* Enters an imported procedure alias sharing pnum (signature and codegen resolve through the shared procs[] entry; the current procedure context is untouched). FALSE on duplicate or full. *) PROCEDURE GlobAlias (name: ARRAY OF CHAR; VAR q: Name): BOOLEAN; (* TRUE + qualified definition global ("M.x") for a VAR/CONST FROM-import alias (MGen slot lookup hook). *) PROCEDURE NoteProgram (): BOOLEAN; (* Claims the single program unit of the session (FALSE when one already exists). *) 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. *) PROCEDURE PrintTable; (* ---------------- type descriptors ---------------- *) PROCEDURE NewAlias (): TypeIndex; PROCEDURE NewSub (base: TypeIndex): TypeIndex; PROCEDURE NewSubB (base: TypeIndex; lo, hi: INTEGER): TypeIndex; PROCEDURE NewEnum (): TypeIndex; PROCEDURE EnumAdd (t: TypeIndex); PROCEDURE NewArray (elem: TypeIndex): TypeIndex; PROCEDURE NewArrayB (elem: TypeIndex; lo, hi: INTEGER): TypeIndex; PROCEDURE NewOpen (elem: TypeIndex): TypeIndex; PROCEDURE IsOpen (t: TypeIndex): BOOLEAN; 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 FieldOffset (rec: TypeIndex; name: ARRAY OF CHAR): INTEGER; PROCEDURE ArrayElem (t: TypeIndex): TypeIndex; PROCEDURE ArrayLo (t: TypeIndex): INTEGER; PROCEDURE ArrayHi (t: TypeIndex): INTEGER; PROCEDURE ArrayLen (t: TypeIndex): CARDINAL; PROCEDURE TypeSlots (t: TypeIndex): CARDINAL; PROCEDURE TypeLo (t: TypeIndex): INTEGER; PROCEDURE TypeHi (t: TypeIndex): INTEGER; PROCEDURE TypeLen (t: TypeIndex): CARDINAL; 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; (* ---------------- procedures ---------------- *) (* Step 3: nested procedures with value and VAR parameters, forward declarations, static scoping. Proc numbers start at 1 (0 is the module body); at most MaxProcs user procedures. *) PROCEDURE EnterProc (name: ARRAY OF CHAR): BOOLEAN; (* Enters a procedure name (KindProc, InvalidType) and assigns the next proc number. FALSE on duplicate or table full. Sets the current-proc context for following EnterParam/FwdParam calls. *) PROCEDURE AllocInitNum (): INTEGER; (* Reserves the next proc number for a module BEGIN init body (step 10). Init bodies share the procedure number pool so an init emitted before a later procedure's body can never steal that procedure's number. The slot has no symbol entry and no signature; -1 when the pool is full. *) PROCEDURE IsForward (name: ARRAY OF CHAR): BOOLEAN; (* TRUE if the visible entry is a forward-declared procedure whose body is still pending. *) PROCEDURE ReuseProc (name: ARRAY OF CHAR); (* Starts the full definition of a forward-declared procedure: keeps its number, switches signature recording to define mode. *) PROCEDURE OpenProcScope; (* Pushes a scope for a procedure body, enters proc depth + 1, resets slot counters, pushes return-type context. *) PROCEDURE CloseProc; (* Pops a procedure body scope, restores the enclosing context. *) PROCEDURE EnterParam (name: ARRAY OF CHAR; isVar: BOOLEAN; t: TypeIndex): BOOLEAN; (* Enters a value (KindParam) or VAR (KindVarPar) parameter in the current procedure scope with its frame slot, appends it to the define signature. FALSE on duplicate. *) PROCEDURE SetProcRet (t: TypeIndex): BOOLEAN; (* Sets the return type (InvalidType = proper procedure). TRUE unless it mismatches a forward heading (error 231). *) PROCEDURE VerifyProc (): BOOLEAN; (* TRUE unless the define signature mismatches the forward heading (error 231). *) PROCEDURE SetForward; (* Marks the current procedure forward-declared (body pending). *) PROCEDURE AnyForward (): BOOLEAN; (* TRUE if some procedure body is still missing (error 231). *) PROCEDURE ProcNum (name: ARRAY OF CHAR): INTEGER; (* Proc number, or -1 if absent / not a procedure. *) PROCEDURE ProcRet (name: ARRAY OF CHAR): TypeIndex; (* Return type (InvalidType = proper procedure or unknown). *) PROCEDURE ProcNPar (name: ARRAY OF CHAR): CARDINAL; (* Arity from the define signature, else the forward one. *) PROCEDURE ParamType (name: ARRAY OF CHAR; i: CARDINAL): TypeIndex; PROCEDURE ParamIsVar (name: ARRAY OF CHAR; i: CARDINAL): BOOLEAN; (* i-th formal parameter (0-based), InvalidType/FALSE if absent. *) PROCEDURE ProcValid (num: INTEGER): BOOLEAN; (* TRUE if num is a declared procedure number (survives scope pops, so module procs stay callable after ExitModule). *) PROCEDURE ProcNParByNum (num: INTEGER): CARDINAL; PROCEDURE ParamTypeByNum (num: INTEGER; i: CARDINAL): TypeIndex; PROCEDURE ParamIsVarByNum (num: INTEGER; i: CARDINAL): BOOLEAN; PROCEDURE ProcRetByNum (num: INTEGER): TypeIndex; (* Same signature queries as above, keyed by procedure number. *) PROCEDURE SymSlot (name: ARRAY OF CHAR): INTEGER; (* Frame slot (params >= 3, locals negative). Only meaningful for variables at proc depth > 0. *) PROCEDURE SymDepth (name: ARRAY OF CHAR): INTEGER; (* Proc depth at declaration (0 = module level). *) PROCEDURE CurDepth (): INTEGER; (* Current proc depth (0 = module body). *) PROCEDURE CurProc (): INTEGER; (* Current proc number (0 = module body). *) PROCEDURE CurNPar (): CARDINAL; (* Arity of the current procedure (0 outside). *) PROCEDURE CurRet (): TypeIndex; (* Current return type (InvalidType = proper or outside). *) PROCEDURE InProc (): BOOLEAN; (* TRUE inside a procedure body. *) PROCEDURE InFunction (): BOOLEAN; (* TRUE inside a function (non-InvalidType return) body. *) PROCEDURE ProcNLocals (): CARDINAL; (* Local slot count of the current procedure (0 outside). *) END SymTab.