blob: ebab5e7ed39f7fdeb3322b554cd50252e2b5fc89 [file] [log] [blame]
(* P0SymBuild.mod pass 0 symbol creation.
Copyright (C) 2011-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
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 P0SymBuild ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM M2Printf IMPORT printf0, printf1, printf2 ;
FROM Lists IMPORT List, InitList, KillList, IncludeItemIntoList, RemoveItemFromList, NoOfItemsInList, GetItemFromList, IsItemInList ;
FROM Indexing IMPORT Index, InitIndex, HighIndice, LowIndice, GetIndice, RemoveIndiceFromIndex, IncludeIndiceIntoIndex ;
FROM M2Batch IMPORT MakeDefinitionSource, MakeProgramSource, MakeImplementationSource ;
FROM SymbolTable IMPORT NulSym, MakeInnerModule, SetCurrentModule, SetFileModule, MakeError, PutDefinitionForC ;
FROM NameKey IMPORT Name, NulName ;
FROM M2Quads IMPORT PushT, PushTF, PopT, PopTF, PopN, OperandT, PopTtok, PushTtok, OperandTok ;
FROM M2Reserved IMPORT ImportTok ;
FROM M2Debug IMPORT Assert ;
FROM M2MetaError IMPORT MetaErrorT1, MetaErrorT2, MetaError1, MetaError2 ;
FROM M2LexBuf IMPORT GetTokenNo, UnknownTokenNo ;
IMPORT M2Error ;
CONST
Debugging = FALSE ;
TYPE
Kind = (module, program, defimp, inner, procedure, universe, unknown) ;
BlockInfoPtr = POINTER TO RECORD
name : Name ;
kind : Kind ;
sym : CARDINAL ;
level : INTEGER ;
token : CARDINAL ; (* where the block starts. *)
LocalModules : List ; (* locally declared modules at the current level *)
ImportedModules: Index ; (* current list of imports for the scanned module *)
toPC,
toReturn,
toNext, (* next in same level *)
toUp, (* return to outer level *)
toDown : BlockInfoPtr ; (* first of the inner level *)
END ;
ModuleDesc = POINTER TO RECORD
name: Name ; (* Name of the module. *)
tok : CARDINAL ; (* Location where the module ident was first seen. *)
END ;
VAR
headBP,
curBP : BlockInfoPtr ;
Level : INTEGER ;
(*
nSpaces -
*)
PROCEDURE nSpaces (n: CARDINAL) ;
BEGIN
WHILE n > 0 DO
printf0 (" ") ;
DEC (n)
END
END nSpaces ;
(*
DisplayB -
*)
PROCEDURE DisplayB (b: BlockInfoPtr) ;
BEGIN
CASE b^.kind OF
program : printf1 ("MODULE %a ;\n", b^.name) |
defimp : printf1 ("DEFIMP %a ;\n", b^.name) |
inner : printf1 ("INNER MODULE %a ;\n", b^.name) |
procedure: printf1 ("PROCEDURE %a ;\n", b^.name)
ELSE
HALT
END
END DisplayB ;
(*
DisplayBlock -
*)
PROCEDURE DisplayBlock (b: BlockInfoPtr; l: CARDINAL) ;
VAR
a: BlockInfoPtr ;
BEGIN
nSpaces (l) ;
DisplayB (b) ;
a := b^.toDown ;
INC (l, 3) ;
WHILE a # NIL DO
DisplayBlock (a, l) ;
a := a^.toNext
END ;
DEC (l, 3) ;
nSpaces (l) ;
printf1 ("END %a\n", b^.name)
END DisplayBlock ;
(*
pc - an interactive debugging aid callable from gdb.
*)
(*
PROCEDURE pc ;
BEGIN
DisplayB (curBP)
END pc ;
*)
(*
Display -
*)
PROCEDURE Display ;
VAR
b: BlockInfoPtr ;
BEGIN
printf0 ("Universe of Modula-2 modules\n") ;
IF headBP # NIL
THEN
b := headBP^.toDown ;
WHILE b # NIL DO
DisplayBlock (b, 0) ;
b := b^.toNext
END
END
END Display ;
(*
addDown - adds, b, to the down link of, a.
*)
PROCEDURE addDown (a, b: BlockInfoPtr) ;
BEGIN
IF a^.toDown = NIL
THEN
a^.toDown := b
ELSE
a := a^.toDown ;
WHILE a^.toNext # NIL DO
a := a^.toNext
END ;
a^.toNext := b
END
END addDown ;
(*
GraftBlock - add a new block, b, into the tree in the correct order.
*)
PROCEDURE GraftBlock (b: BlockInfoPtr) ;
BEGIN
Assert (curBP # NIL) ;
Assert (ABS (Level-curBP^.level) <= 1) ;
CASE Level-curBP^.level OF
-1: (* returning up to the outer scope *)
curBP := curBP^.toUp ;
Assert (curBP^.toNext = NIL) ;
curBP^.toNext := b |
0: (* add toNext *)
Assert (curBP^.toNext = NIL) ;
curBP^.toNext := b ;
b^.toUp := curBP^.toUp |
+1: (* insert down a level *)
b^.toUp := curBP ; (* save return value *)
addDown (curBP, b)
ELSE
HALT
END ;
curBP := b
END GraftBlock ;
(*
BeginBlock - denotes the start of the next block. We remember all imports and
local modules and procedures created in this block.
*)
PROCEDURE BeginBlock (n: Name; k: Kind; s: CARDINAL; tok: CARDINAL) ;
VAR
b: BlockInfoPtr ;
BEGIN
NEW (b) ;
WITH b^ DO
name := n ;
kind := k ;
sym := s ;
InitList (LocalModules) ;
ImportedModules := InitIndex (1) ;
toPC := NIL ;
toReturn := NIL ;
toNext := NIL ;
toDown := NIL ;
toUp := NIL ;
level := Level ;
token := tok
END ;
GraftBlock(b)
END BeginBlock ;
(*
InitUniverse -
*)
PROCEDURE InitUniverse ;
BEGIN
NEW (curBP) ;
WITH curBP^ DO
name := NulName ;
kind := universe ;
sym := NulSym ;
InitList (LocalModules) ;
ImportedModules := InitIndex (1) ;
toNext := NIL ;
toDown := NIL ;
toUp := curBP ;
level := Level
END ;
headBP := curBP
END InitUniverse ;
(*
FlushImports -
*)
PROCEDURE FlushImports (b: BlockInfoPtr) ;
VAR
i, n: CARDINAL ;
desc: ModuleDesc ;
BEGIN
WITH b^ DO
i := LowIndice (ImportedModules) ;
n := HighIndice (ImportedModules) ;
WHILE i <= n DO
desc := GetIndice (ImportedModules, i) ;
sym := MakeDefinitionSource (desc^.tok, desc^.name) ;
Assert (sym # NulSym) ;
INC (i)
END
END
END FlushImports ;
(*
EndBlock - shutdown the module and create definition symbols for all imported
modules.
*)
PROCEDURE EndBlock ;
BEGIN
FlushImports (curBP) ;
curBP := curBP^.toUp ;
DEC (Level) ;
IF Level = 0
THEN
FlushImports (curBP)
END
END EndBlock ;
(*
RegisterLocalModule - register, n, as a local module.
*)
PROCEDURE RegisterLocalModule (modname: Name) ;
VAR
i, n: CARDINAL ;
desc: ModuleDesc ;
BEGIN
(* printf1('seen local module %a\n', n) ; *)
WITH curBP^ DO
IncludeItemIntoList (LocalModules, modname) ;
i := LowIndice (ImportedModules) ;
n := HighIndice (ImportedModules) ;
WHILE i <= n DO
desc := GetIndice (ImportedModules, i) ;
IF desc^.name = modname
THEN
RemoveIndiceFromIndex (ImportedModules, desc) ;
DISPOSE (desc) ;
DEC (n)
(* Continue checking in case a user imported the same module again. *)
ELSE
INC (i)
END
END
END
END RegisterLocalModule ;
(*
RegisterImport - register, n, as a module imported from either a local scope or definition module.
*)
PROCEDURE RegisterImport (tok: CARDINAL; modname: Name) ;
VAR
bp : BlockInfoPtr ;
desc: ModuleDesc ;
BEGIN
(* printf1('register import from module %a\n', n) ; *)
Assert (curBP # NIL) ;
Assert (curBP^.toUp # NIL) ;
bp := curBP^.toUp ; (* skip over current module *)
WITH bp^ DO
IF NOT IsItemInList (LocalModules, modname)
THEN
NEW (desc) ;
desc^.name := modname ;
desc^.tok := tok ;
IncludeIndiceIntoIndex (ImportedModules, desc)
END
END
END RegisterImport ;
(*
RegisterImports -
*)
PROCEDURE RegisterImports ;
VAR
index,
i, n : CARDINAL ;
BEGIN
PopT (n) ; (* n = # of the Ident List *)
IF OperandT (n+1) = ImportTok
THEN
(* Ident list contains Module Names *)
i := 1 ;
WHILE i<=n DO
index := n+1-i ;
RegisterImport (OperandTok (index), OperandT (index)) ;
INC (i)
END
ELSE
(* Ident List contains list of objects *)
RegisterImport (OperandTok (n+1), OperandT (n+1))
END ;
PopN (n+1) (* clear stack *)
END RegisterImports ;
(*
RegisterInnerImports -
*)
PROCEDURE RegisterInnerImports ;
VAR
n: CARDINAL ;
BEGIN
PopT (n) ; (* n = # of the Ident List *)
IF OperandT (n+1) = ImportTok
THEN
(* Ident list contains list of objects, which will be seen outside the scope of this module. *)
ELSE
(* Ident List contains list of objects, but we are importing directly from a module OperandT(n+1) *)
RegisterImport (OperandTok (n+1), OperandT (n+1))
END ;
PopN (n+1) (* clear stack *)
END RegisterInnerImports ;
(*
RegisterProgramModule - register the top of stack as a program module.
*)
PROCEDURE RegisterProgramModule ;
VAR
n : Name ;
sym: CARDINAL ;
tok: CARDINAL ;
BEGIN
Assert (Level = 0) ;
INC (Level) ;
PopTtok (n, tok) ;
PushTtok (n, tok) ;
sym := MakeProgramSource (tok, n) ;
SetCurrentModule (sym) ;
SetFileModule (sym) ;
BeginBlock (n, program, sym, tok) ;
M2Error.EnterProgramScope (n)
END RegisterProgramModule ;
(*
RegisterImplementationModule - register the top of stack as an implementation module.
*)
PROCEDURE RegisterImplementationModule ;
VAR
n : Name ;
sym: CARDINAL ;
tok: CARDINAL ;
BEGIN
Assert (Level = 0) ;
INC (Level) ;
PopTtok (n, tok) ;
PushTtok (n, tok) ;
sym := MakeImplementationSource (tok, n) ;
SetCurrentModule (sym) ;
SetFileModule (sym) ;
BeginBlock (n, defimp, sym, tok) ;
M2Error.EnterImplementationScope (n)
END RegisterImplementationModule ;
(*
RegisterDefinitionModule - register the top of stack as a definition module.
*)
PROCEDURE RegisterDefinitionModule (forC: BOOLEAN) ;
VAR
n : Name ;
sym: CARDINAL ;
tok: CARDINAL ;
BEGIN
Assert (Level=0) ;
INC (Level) ;
PopTtok (n, tok) ;
PushTtok (n, tok) ;
sym := MakeDefinitionSource (tok, n) ;
SetCurrentModule (sym) ;
SetFileModule (sym) ;
IF forC
THEN
PutDefinitionForC (sym)
END ;
BeginBlock (n, defimp, sym, tok) ;
M2Error.EnterDefinitionScope (n)
END RegisterDefinitionModule ;
(*
RegisterInnerModule - register the top of stack as an inner module, this module name
will be removed from the list of outstanding imports in the
current module block.
*)
PROCEDURE RegisterInnerModule ;
VAR
n : Name ;
tok: CARDINAL ;
BEGIN
INC (Level) ;
PopTtok (n, tok) ;
PushTtok (n, tok) ;
RegisterLocalModule (n) ;
BeginBlock (n, inner, NulSym, tok) ;
M2Error.EnterModuleScope (n)
END RegisterInnerModule ;
(*
RegisterProcedure - register the top of stack as a procedure.
*)
PROCEDURE RegisterProcedure ;
VAR
n : Name ;
tok: CARDINAL ;
BEGIN
INC (Level) ;
PopTtok (n, tok) ;
PushTtok (n, tok) ;
BeginBlock (n, procedure, NulSym, tok) ;
M2Error.EnterProcedureScope (n)
END RegisterProcedure ;
(*
EndBuildProcedure - ends building a Procedure.
*)
PROCEDURE EndProcedure ;
VAR
NameEnd, NameStart: Name ;
end, start : CARDINAL ;
BEGIN
PopTtok (NameEnd, end) ;
PopTtok (NameStart, start) ;
Assert (start # UnknownTokenNo) ;
Assert (end # UnknownTokenNo) ;
IF NameEnd # NameStart
THEN
IF NameEnd = NulName
THEN
MetaErrorT1 (start,
'procedure name at beginning {%1Ea} does not match the name at end',
MakeError (start, NameStart)) ;
MetaError1 ('procedure name at end does not match the name at beginning {%1Ea}',
MakeError (start, NameStart))
ELSE
MetaErrorT2 (start,
'procedure name at beginning {%1Ea} does not match the name at end {%2a}',
MakeError (start, curBP^.name), MakeError (end, NameEnd)) ;
MetaErrorT2 (end,
'procedure name at end {%1Ea} does not match the name at beginning {%2Ea}',
MakeError (end, NameEnd), MakeError (start, curBP^.name))
END
END ;
EndBlock ;
M2Error.LeaveErrorScope
END EndProcedure ;
(*
EndForward - ends building a forward procedure.
*)
PROCEDURE EndForward ;
BEGIN
PopN (1) ;
EndBlock ;
M2Error.LeaveErrorScope
END EndForward ;
(*
EndModule -
*)
PROCEDURE EndModule ;
VAR
NameEnd, NameStart: Name ;
end, start : CARDINAL ;
BEGIN
PopTtok (NameEnd, end) ;
PopTtok (NameStart, start) ;
Assert (start # UnknownTokenNo) ;
Assert (end # UnknownTokenNo) ;
IF NameEnd # NameStart
THEN
IF NameEnd = NulName
THEN
MetaErrorT1 (start,
'module name at beginning {%1Ea} does not match the name at end',
MakeError (start, NameStart)) ;
MetaError1 ('module name at end does not match the name at beginning {%1Ea}',
MakeError (start, NameStart))
ELSE
MetaErrorT2 (start,
'module name at beginning {%1Ea} does not match the name at end {%2a}',
MakeError (start, curBP^.name), MakeError (end, NameEnd)) ;
MetaErrorT2 (end,
'module name at end {%1Ea} does not match the name at beginning {%2Ea}',
MakeError (end, NameEnd), MakeError (start, curBP^.name))
END
END ;
EndBlock ;
M2Error.LeaveErrorScope
END EndModule ;
(*
DeclareModules - declare all inner modules seen at the current block level.
*)
PROCEDURE DeclareModules ;
VAR
b: BlockInfoPtr ;
s: CARDINAL ;
BEGIN
b := curBP^.toDown ;
WHILE b # NIL DO
IF b^.kind = inner
THEN
IF Debugging
THEN
printf1 ("*** declaring inner module %a\n", b^.name)
END ;
s := MakeInnerModule (curBP^.token, b^.name) ;
Assert (s # NulSym)
END ;
b := b^.toNext
END
END DeclareModules ;
(****
(*
MoveNext -
*)
PROCEDURE MoveNext ;
VAR
b: BlockInfoPtr ;
BEGIN
IF curBP^.toNext#NIL
THEN
b := curBP^.toUp ;
(* moving to next *)
curBP := curBP^.toNext ;
(* remember our return *)
curBP^.toUp := b
END
END MoveNext ;
(*
MoveDown -
*)
PROCEDURE MoveDown ;
VAR
b: BlockInfoPtr ;
BEGIN
(* move down a level *)
(* remember where we came from *)
b := curBP ;
curBP := curBP^.toDown ;
curBP^.toUp := b
END MoveDown ;
(*
MoveUp -
*)
PROCEDURE MoveUp ;
BEGIN
(* move up to the outer scope *)
curBP := curBP^.toUp ;
END MoveUp ;
***** *)
(*
Move -
*)
PROCEDURE Move ;
VAR
b: BlockInfoPtr ;
BEGIN
IF Level = curBP^.level
THEN
b := curBP^.toReturn ;
(* moving to next *)
curBP := curBP^.toNext ;
(* remember our return *)
curBP^.toReturn := b
ELSE
WHILE Level # curBP^.level DO
IF Level < curBP^.level
THEN
(* move up to the outer scope *)
b := curBP ;
curBP := curBP^.toReturn ;
curBP^.toPC := b^.toNext (* remember where we reached *)
ELSE
(* move down a level *)
(* remember where we came from *)
b := curBP ;
IF curBP^.toPC = NIL
THEN
Assert (curBP^.toDown#NIL) ;
curBP^.toPC := curBP^.toDown
END ;
Assert (curBP^.toPC#NIL) ;
curBP := curBP^.toPC ;
curBP^.toReturn := b
END
END
END
END Move ;
(*
EnterBlock -
*)
PROCEDURE EnterBlock (n: Name) ;
BEGIN
Assert (curBP#NIL) ;
INC (Level) ;
Move ;
IF Debugging
THEN
nSpaces (Level*3) ;
IF n = curBP^.name
THEN
printf1 ('block %a\n', n)
ELSE
printf2 ('seen block %a but tree has recorded %a\n', n, curBP^.name)
END
END ;
Assert ((n = curBP^.name) OR (curBP^.name = NulName)) ;
DeclareModules
END EnterBlock ;
(*
LeaveBlock -
*)
PROCEDURE LeaveBlock ;
BEGIN
IF Debugging
THEN
printf1 ('leaving block %a ', curBP^.name)
END ;
DEC (Level) ;
Move
END LeaveBlock ;
(*
P0Init -
*)
PROCEDURE P0Init ;
BEGIN
headBP := NIL ;
curBP := NIL ;
Level := 0 ;
InitUniverse
END P0Init ;
(*
P1Init -
*)
PROCEDURE P1Init ;
BEGIN
IF Debugging
THEN
Display
END ;
(* curBP := headBP^.toDown ; *)
curBP := headBP ;
Assert(curBP#NIL) ;
curBP^.toPC := curBP^.toDown ;
curBP^.toReturn := curBP ;
Level := 0
END P1Init ;
END P0SymBuild.