blob: 789cd4f27c38465cf905ce9ce2e75cbf5100dbe0 [file] [log] [blame]
(* 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.