(* M2StateCheck.mod provide state check tracking for declarations. Copyright (C) 2024-2025 Free Software Foundation, Inc. Contributed by Gaius Mulley <gaiusmod2@gmail.com>. This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Modula-2; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. *) IMPLEMENTATION MODULE M2StateCheck ; FROM Storage IMPORT ALLOCATE ; FROM M2MetaError IMPORT MetaErrorStringT1 ; FROM DynamicStrings IMPORT String, InitString, ConCat, Mark ; FROM SymbolTable IMPORT NulSym, IsType, IsVar, IsConst ; TYPE StateCheck = POINTER TO RECORD state: StateSet ; stack, next : StateCheck ; END ; State = (const, var, type, constfunc, varparam, constructor) ; StateSet = SET OF State ; VAR FreeList: StateCheck ; (* InitState - returns a new initialized StateCheck. *) PROCEDURE InitState () : StateCheck ; VAR s: StateCheck ; BEGIN s := New () ; WITH s^ DO state := StateSet {} ; stack := NIL ; next := NIL END ; RETURN s END InitState ; (* New - returns an uninitialized StateCheck. *) PROCEDURE New () : StateCheck ; VAR s: StateCheck ; BEGIN IF FreeList = NIL THEN NEW (s) ELSE s := FreeList ; FreeList := FreeList^.next END ; RETURN s END New ; (* PushState - duplicates the StateCheck s and chains the new copy to s. Return the copy. *) PROCEDURE PushState (VAR s: StateCheck) ; VAR copy: StateCheck ; BEGIN copy := InitState () ; copy^.state := s^.state ; copy^.stack := s ; s := copy END PushState ; (* KillState - destructor for StateCheck. *) PROCEDURE KillState (VAR s: StateCheck) ; VAR t: StateCheck ; BEGIN WHILE s^.stack # NIL DO t := s^.stack ; s^.stack := t^.stack ; Dispose (t) END ; Dispose (s) END KillState ; (* Dispose - place s onto the FreeList and set s to NIL. *) PROCEDURE Dispose (VAR s: StateCheck) ; BEGIN s^.next := FreeList ; FreeList := s END Dispose ; (* InclVar - s := s + {var}. *) PROCEDURE InclVar (s: StateCheck) ; BEGIN INCL (s^.state, var) END InclVar ; (* InclConst - s := s + {const}. *) PROCEDURE InclConst (s: StateCheck) ; BEGIN INCL (s^.state, const) END InclConst ; (* InclType - s := s + {type}. *) PROCEDURE InclType (s: StateCheck) ; BEGIN INCL (s^.state, type) END InclType ; (* InclConstFunc - s := s + {constfunc}. *) PROCEDURE InclConstFunc (s: StateCheck) ; BEGIN INCL (s^.state, constfunc) END InclConstFunc ; (* InclVarParam - s := s + {varparam}. *) PROCEDURE InclVarParam (s: StateCheck) ; BEGIN INCL (s^.state, varparam) END InclVarParam ; (* InclConstructor - s := s + {constructor}. *) PROCEDURE InclConstructor (s: StateCheck) ; BEGIN INCL (s^.state, constructor) END InclConstructor ; (* ExclVar - s := s - {var}. *) PROCEDURE ExclVar (s: StateCheck) ; BEGIN EXCL (s^.state, var) END ExclVar ; (* ExclConst - s := s - {const}. *) PROCEDURE ExclConst (s: StateCheck) ; BEGIN EXCL (s^.state, const) END ExclConst ; (* ExclType - s := s - {type}. *) PROCEDURE ExclType (s: StateCheck) ; BEGIN EXCL (s^.state, type) END ExclType ; (* ExclConstFunc - s := s - {constfunc}. *) PROCEDURE ExclConstFunc (s: StateCheck) ; BEGIN EXCL (s^.state, constfunc) END ExclConstFunc ; (* ExclVarParam - s := s - {varparam}. *) PROCEDURE ExclVarParam (s: StateCheck) ; BEGIN EXCL (s^.state, varparam) END ExclVarParam ; (* ExclConstructor - s := s - {varparam}. *) PROCEDURE ExclConstructor (s: StateCheck) ; BEGIN EXCL (s^.state, constructor) END ExclConstructor ; (* PopState - pops the current state. *) PROCEDURE PopState (VAR s: StateCheck) ; VAR t: StateCheck ; BEGIN t := s ; s := s^.stack ; t^.stack := NIL ; Dispose (t) END PopState ; (* CheckQualident - checks to see that qualident sym is allowed in the state s. *) PROCEDURE CheckQualident (tok: CARDINAL; s: StateCheck; sym: CARDINAL) ; BEGIN IF sym = NulSym THEN (* Ignore. *) ELSIF IsType (sym) THEN IF (constfunc IN s^.state) OR (constructor IN s^.state) THEN (* Ok. *) ELSIF const IN s^.state THEN GenerateError (tok, s, sym) END ELSIF IsConst (sym) THEN IF (constfunc IN s^.state) OR (constructor IN s^.state) THEN (* Ok. *) ELSIF (var IN s^.state) OR (type IN s^.state) THEN GenerateError (tok, s, sym) END ELSIF IsVar (sym) THEN IF constfunc IN s^.state THEN (* Ok. *) ELSIF (const IN s^.state) OR (type IN s^.state) OR (var IN s^.state) THEN GenerateError (tok, s, sym) END END END CheckQualident ; (* GenerateError - generates an unrecoverable error string based on the state and sym. *) PROCEDURE GenerateError (tok: CARDINAL; s: StateCheck; sym: CARDINAL) ; VAR str: String ; BEGIN str := InitString ('not expecting the {%1Ad} {%1a} in a ') ; IF const IN s^.state THEN str := ConCat (str, Mark (InitString ('{%kCONST} block'))) ELSIF type IN s^.state THEN str := ConCat (str, Mark (InitString ('{%kTYPE} block'))) ELSIF var IN s^.state THEN str := ConCat (str, Mark (InitString ('{%kVAR} block'))) END ; IF constfunc IN s^.state THEN str := ConCat (str, Mark (InitString (' and within a constant procedure function actual parameter'))) END ; IF constructor IN s^.state THEN str := ConCat (str, Mark (InitString (' and within a constructor'))) END ; MetaErrorStringT1 (tok, str, sym) END GenerateError ; (* init - initialize the global variables in the module. *) PROCEDURE init ; BEGIN FreeList := NIL END init ; BEGIN init END M2StateCheck.