blob: ff2cd9303113208c199b761f7619c453fdf396ab [file] [log] [blame]
(* M2Swig.mod generates a swig interface file for the main module.
Copyright (C) 2008-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 M2Swig ;
FROM Storage IMPORT ALLOCATE ;
FROM M2Options IMPORT GenerateSwig ;
FROM SFIO IMPORT OpenToWrite ;
FROM FIO IMPORT File, Close ;
FROM NameKey IMPORT Name, KeyToCharStar ;
FROM M2Error IMPORT InternalError ;
FROM M2Printf IMPORT fprintf0, fprintf1, fprintf2, fprintf3, fprintf4 ;
FROM M2AsmUtil IMPORT GetFullScopeAsmName ;
FROM SYSTEM IMPORT WORD ;
FROM DynamicStrings IMPORT String, InitString, InitStringCharStar, ConCat, Mark,
KillString ;
FROM Lists IMPORT List, InitList, KillList, IsItemInList,
IncludeItemIntoList, RemoveItemFromList,
ForeachItemInListDo, NoOfItemsInList,
GetItemFromList ;
FROM M2Quads IMPORT IsProcedureScope ;
FROM M2System IMPORT IsSystemType, Address, Byte, Loc, Word ;
FROM M2Bitset IMPORT Bitset ;
FROM Indexing IMPORT Index, InitIndex, KillIndex, HighIndice, PutIndice, GetIndice ;
FROM M2Scope IMPORT ScopeBlock, InitScopeBlock, KillScopeBlock ;
FROM M2Base IMPORT IsBaseType, Char, Cardinal, Integer, Real, LongReal, ShortReal,
LongCard, ShortCard, LongInt, ShortInt, Boolean ;
FROM SymbolTable IMPORT GetSymName, IsType, IsProcedure, IsConst, IsVar,
GetType, GetNthParamAny, IsUnbounded, GetMode, ModeOfAddr,
NoOfParamAny, IsConstString, IsConstLit, IsPointer,
IsExported, ForeachExportedDo, IsUnboundedParamAny,
IsParameter, IsParameterUnbounded, IsParameterVar,
GetParameterShadowVar, GetReadQuads, GetWriteQuads,
NulSym ;
FROM M2BasicBlock IMPORT BasicBlock, InitBasicBlocks, KillBasicBlocks,
ForeachBasicBlockDo,
GetBasicBlockStart, GetBasicBlockEnd ;
TYPE
UnboundedSig = POINTER TO RECORD
type: CARDINAL ;
name: Name ;
END ;
VAR
includedArray: BOOLEAN ;
uKey : Index ;
mainModule : CARDINAL ;
Done,
ToDo : List ;
f : File ;
name : String ;
(*
DoExported - includes, sym, into the, ToDo, list.
*)
PROCEDURE DoExported (sym: CARDINAL) ;
BEGIN
IncludeItemIntoList(ToDo, sym)
END DoExported ;
(*
MoveToDone - moves a sym to the, Done, list,
providing that it is not already on it.
It returns TRUE if the lists were modified.
*)
PROCEDURE MoveToDone (sym: CARDINAL) : BOOLEAN ;
BEGIN
IF IsItemInList(Done, sym)
THEN
RETURN( FALSE )
ELSIF IsItemInList(ToDo, sym)
THEN
RemoveItemFromList(ToDo, sym) ;
IncludeItemIntoList(Done, sym) ;
RETURN( TRUE )
END ;
IncludeItemIntoList(Done, sym) ;
RETURN( TRUE )
END MoveToDone ;
(*
MoveToToDo - moves a sym to the, ToDo, list,
providing that it is not already on it.
It returns TRUE if the lists were modified.
*)
PROCEDURE MoveToToDo (sym: CARDINAL) : BOOLEAN ;
BEGIN
IF IsItemInList(Done, sym)
THEN
InternalError ('not expecting to get here')
ELSIF IsItemInList(ToDo, sym)
THEN
RETURN( FALSE )
ELSE
IncludeItemIntoList(ToDo, sym) ;
RETURN( TRUE )
END
END MoveToToDo ;
(*
Trybase - returns TRUE
*)
PROCEDURE TryBase (sym: CARDINAL) : BOOLEAN ;
BEGIN
IF (sym=Cardinal) OR (sym=Integer) OR (sym=LongInt) OR
(sym=LongCard) OR (sym=Char) OR (sym=ShortCard) OR
(sym=ShortInt) OR (sym=Real) OR (sym=LongReal) OR
(sym=ShortReal) OR (sym=Boolean)
THEN
RETURN( MoveToDone(sym) )
ELSE
RETURN( FALSE )
END
END TryBase ;
(*
TrySystem - returns TRUE if sym can be moved to the done list.
*)
PROCEDURE TrySystem (sym: CARDINAL) : BOOLEAN ;
BEGIN
IF (sym=Bitset) OR (sym=Address) OR (sym=Byte) OR (sym=Loc) OR
(sym=Word)
THEN
RETURN( MoveToDone(sym) )
ELSE
RETURN( FALSE )
END
END TrySystem ;
(*
TryMove - tries to move sym to the done queue as long
as type is known.
*)
PROCEDURE TryMove (sym, type: CARDINAL) : BOOLEAN ;
BEGIN
IF IsItemInList(Done, type)
THEN
IF MoveToDone(sym)
THEN
RETURN( TRUE )
END
ELSE
IF MoveToToDo(sym)
THEN
RETURN( TRUE )
END
END ;
RETURN( FALSE )
END TryMove ;
(*
TryType -
*)
PROCEDURE TryType (sym: CARDINAL) : BOOLEAN ;
VAR
type : CARDINAL ;
result: BOOLEAN ;
BEGIN
type := GetType(sym) ;
result := TryDependents(type) ;
IF TryMove(sym, type)
THEN
RETURN( TRUE )
ELSE
RETURN( result )
END
END TryType ;
(*
TryVar -
*)
PROCEDURE TryVar (sym: CARDINAL) : BOOLEAN ;
VAR
type : CARDINAL ;
result: BOOLEAN ;
BEGIN
type := GetType(sym) ;
result := TryDependents(type) ;
IF TryMove(sym, type)
THEN
RETURN( TRUE )
ELSE
RETURN( result )
END
END TryVar ;
(*
TryProcedure -
*)
PROCEDURE TryProcedure (sym: CARDINAL) : BOOLEAN ;
VAR
son,
p, i,
type : CARDINAL ;
solved,
result: BOOLEAN ;
BEGIN
type := GetType(sym) ;
result := FALSE ;
solved := TRUE ;
IF type#NulSym
THEN
IF TryDependents(type)
THEN
result := TRUE
END ;
IF NOT IsItemInList(Done, type)
THEN
solved := FALSE
END
END ;
p := NoOfParamAny (sym) ;
i := 1 ;
WHILE i<=p DO
son := GetNthParamAny(sym, i) ;
IF TryDependents(son)
THEN
result := TRUE
END ;
IF NOT IsItemInList(Done, son)
THEN
solved := FALSE
END ;
INC(i)
END ;
IF solved
THEN
IF MoveToDone(sym)
THEN
RETURN( TRUE )
END
ELSE
IF MoveToToDo(sym)
THEN
RETURN( TRUE )
END
END ;
RETURN( result )
END TryProcedure ;
(*
TryUnbounded -
*)
PROCEDURE TryUnbounded (sym: CARDINAL) : BOOLEAN ;
VAR
type : CARDINAL ;
result: BOOLEAN ;
BEGIN
type := GetType(sym) ;
result := TryDependents(type) ;
IF TryMove(sym, type)
THEN
RETURN( TRUE )
ELSE
RETURN( result )
END
END TryUnbounded ;
(*
TryParameter -
*)
PROCEDURE TryParameter (sym: CARDINAL) : BOOLEAN ;
VAR
type : CARDINAL ;
result: BOOLEAN ;
BEGIN
type := GetType(sym) ;
result := TryDependents(type) ;
IF TryMove(sym, type)
THEN
RETURN( TRUE )
ELSE
RETURN( result )
END
END TryParameter ;
(*
TryDependents - returns TRUE if any alteration occurred to any
of the lists.
*)
PROCEDURE TryDependents (sym: CARDINAL) : BOOLEAN ;
BEGIN
IF IsBaseType(sym)
THEN
RETURN( TryBase(sym) )
ELSIF IsSystemType(sym)
THEN
RETURN( TrySystem(sym) )
ELSIF IsType(sym)
THEN
RETURN( TryType(sym) )
ELSIF IsParameter(sym)
THEN
RETURN( TryParameter(sym) )
ELSIF IsProcedure(sym)
THEN
RETURN( TryProcedure(sym) )
ELSIF IsConstString(sym)
THEN
RETURN( MoveToDone(sym) )
ELSIF IsConstLit(sym)
THEN
RETURN( MoveToDone(sym) )
ELSIF IsVar(sym) AND (GetMode(sym)=ImmediateValue)
THEN
RETURN( MoveToDone(sym) )
ELSIF IsVar(sym)
THEN
RETURN( TryVar(sym) )
ELSIF IsUnbounded(sym)
THEN
RETURN( TryUnbounded(sym) )
ELSE
RETURN( FALSE )
END
END TryDependents ;
(*
DoResolveOrder - resolves the declaration order for swig (C).
*)
PROCEDURE DoResolveOrder ;
VAR
sym,
i, n : CARDINAL ;
movement: BOOLEAN ;
BEGIN
REPEAT
n := NoOfItemsInList(ToDo) ;
movement := FALSE ;
i := 1 ;
WHILE (i<=n) AND (NOT movement) DO
sym := GetItemFromList(ToDo, i) ;
movement := TryDependents(sym) ;
INC(i)
END
UNTIL NOT movement
END DoResolveOrder ;
(*
DoName -
*)
PROCEDURE DoName (sym: CARDINAL) ;
VAR
n: Name ;
BEGIN
n := GetFullScopeAsmName(sym) ;
fprintf1(f, "%a", n)
END DoName ;
(*
DoParamName -
*)
PROCEDURE DoParamName (sym: CARDINAL) ;
VAR
n: Name ;
BEGIN
n := GetSymName(sym) ;
fprintf1(f, "%a", n)
END DoParamName ;
(*
DoVar -
*)
PROCEDURE DoVar (sym: CARDINAL) ;
BEGIN
fprintf0(f, 'extern "C" ') ;
DoType(GetType(sym)) ;
fprintf0(f, ' ') ;
DoName(sym) ;
fprintf0(f, ';\n')
END DoVar ;
(*
DoType -
*)
PROCEDURE DoType (sym: CARDINAL) ;
BEGIN
IF IsPointer(sym)
THEN
DoType(GetType(sym)) ;
fprintf0(f, ' *')
ELSIF sym=Cardinal
THEN
fprintf0(f, "unsigned int")
ELSIF sym=Integer
THEN
fprintf0(f, "int")
ELSIF sym=Boolean
THEN
fprintf0(f, "unsigned int")
ELSIF sym=LongInt
THEN
fprintf0(f, "long long int")
ELSIF sym=LongCard
THEN
fprintf0(f, "long long unsigned int")
ELSIF sym=Char
THEN
fprintf0(f, "char")
ELSIF sym=ShortCard
THEN
fprintf0(f, "short unsigned int")
ELSIF sym=ShortInt
THEN
fprintf0(f, "short int")
ELSIF sym=Real
THEN
fprintf0(f, "double")
ELSIF sym=LongReal
THEN
fprintf0(f, "long double")
ELSIF sym=ShortReal
THEN
fprintf0(f, "float")
ELSIF sym=Bitset
THEN
fprintf0(f, "unsigned int")
ELSIF sym=Address
THEN
fprintf0(f, "void *")
ELSIF sym=Byte
THEN
fprintf0(f, "unsigned char")
ELSIF sym=Loc
THEN
fprintf0(f, "unsigned char")
ELSIF sym=Word
THEN
fprintf0(f, "unsigned int")
END
END DoType ;
(*
DoUnbounded -
*)
PROCEDURE DoUnbounded (sym: CARDINAL) ;
VAR
n : Name ;
type: CARDINAL ;
BEGIN
type := GetType(sym) ;
DoType(GetType(type)) ;
n := GetSymName(sym) ;
fprintf2(f, ' *_m2_address_%a, int _m2_high_%a', n, n)
END DoUnbounded ;
VAR
FirstBasicBlock,
Input,
Output,
InOut,
CanGuess,
IsKnown : BOOLEAN ;
rs, ws : CARDINAL ;
(*
DoBasicBlock -
*)
PROCEDURE DoBasicBlock (bb: BasicBlock) ;
VAR
start, end: CARDINAL ;
BEGIN
start := GetBasicBlockStart (bb) ;
end := GetBasicBlockEnd (bb) ;
IF IsProcedureScope(start)
THEN
(* skip this basic block, as this will not modify the parameter *)
RETURN
ELSIF IsKnown OR CanGuess
THEN
(* already resolved *)
RETURN
ELSE
IF (ws=0) AND (rs=0)
THEN
FirstBasicBlock := FALSE
ELSIF rs=0
THEN
(* only written *)
IF ws<=end
THEN
Output := TRUE ;
IF FirstBasicBlock
THEN
IsKnown := TRUE
ELSE
CanGuess := TRUE
END ;
FirstBasicBlock := FALSE
END
ELSIF ws=0
THEN
(* only read *)
Input := TRUE ;
IF (rs<=end) AND FirstBasicBlock
THEN
IsKnown := TRUE
ELSE
CanGuess := TRUE
END ;
FirstBasicBlock := FALSE
ELSIF rs<=ws
THEN
(* read before write *)
InOut := TRUE ;
IF (rs<=end) AND (ws<=end) AND FirstBasicBlock
THEN
IsKnown := TRUE
ELSE
CanGuess := TRUE
END ;
FirstBasicBlock := FALSE
ELSE
(* must be written before read *)
Output := TRUE ;
IF (rs<=end) AND (ws<=end) AND FirstBasicBlock
THEN
IsKnown := TRUE
ELSE
CanGuess := TRUE
END ;
FirstBasicBlock := FALSE
END
END
END DoBasicBlock ;
(*
DetermineParameter -
*)
PROCEDURE DetermineParameter (procedure, param: CARDINAL) ;
VAR
sb: ScopeBlock ;
bb: BasicBlock ;
we,
re: CARDINAL ;
BEGIN
sb := InitScopeBlock(procedure) ;
bb := InitBasicBlocks(sb) ;
Input := FALSE ;
Output := FALSE ;
InOut := FALSE ;
CanGuess := FALSE ;
IsKnown := FALSE ;
FirstBasicBlock := TRUE ;
GetReadQuads(param, RightValue, rs, re) ;
GetWriteQuads(param, RightValue, ws, we) ;
ForeachBasicBlockDo(bb, DoBasicBlock) ;
KillBasicBlocks(bb) ;
KillScopeBlock(sb)
END DetermineParameter ;
(*
PrintDirection -
*)
PROCEDURE PrintDirection ;
BEGIN
IF Input
THEN
fprintf0(f, 'INPUT')
ELSIF Output
THEN
fprintf0(f, 'OUTPUT')
ELSE
fprintf0(f, 'INOUT')
END
END PrintDirection ;
(*
CalculateVarDirective -
*)
PROCEDURE CalculateVarDirective (procedure, param: CARDINAL; annotate: BOOLEAN) ;
VAR
sym: CARDINAL ;
BEGIN
sym := GetParameterShadowVar(param) ;
IF sym=NulSym
THEN
InternalError ('why did we get here')
ELSE
DetermineParameter(procedure, sym) ;
IF annotate
THEN
DoParamName(sym) ;
IF IsKnown
THEN
fprintf0(f, ' is known to be an ') ;
PrintDirection
ELSIF CanGuess
THEN
fprintf0(f, ' is guessed to be an ') ;
PrintDirection
ELSE
fprintf0(f, ' is unknown')
END
ELSE
fprintf0(f, '*') ;
IF IsKnown OR CanGuess
THEN
PrintDirection
ELSE
DoParamName(sym)
END
END
END
END CalculateVarDirective ;
(*
AnnotateProcedure -
*)
PROCEDURE AnnotateProcedure (sym: CARDINAL) ;
VAR
son, p, i: CARDINAL ;
needComma: BOOLEAN ;
BEGIN
fprintf0(f, '/* Parameter: ') ;
p := NoOfParamAny (sym) ;
i := 1 ;
needComma := FALSE ;
WHILE i<=p DO
son := GetNthParamAny(sym, i) ;
IF IsParameterVar(son)
THEN
IF needComma
THEN
fprintf0(f, ',\n ')
END ;
CalculateVarDirective(sym, son, TRUE) ;
needComma := TRUE
END ;
INC(i)
END ;
fprintf0(f, '. */\n\n')
END AnnotateProcedure ;
(*
DoProcedure -
*)
PROCEDURE DoProcedure (sym: CARDINAL) : BOOLEAN ;
VAR
son,
p, i : CARDINAL ;
found: BOOLEAN ;
BEGIN
found := FALSE ;
fprintf0(f, 'extern "C" ') ;
IF GetType(sym)=NulSym
THEN
fprintf0(f, 'void') ;
ELSE
DoType(GetType(sym))
END ;
fprintf0(f, ' ') ;
DoName(sym) ;
fprintf0(f, ' (') ;
p := NoOfParamAny (sym) ;
IF p=0
THEN
fprintf0(f, 'void') ;
ELSE
i := 1 ;
WHILE i<=p DO
son := GetNthParamAny(sym, i) ;
IF IsUnboundedParamAny (sym, i)
THEN
DoUnbounded(son)
ELSE
DoType(GetType(son)) ;
fprintf0(f, ' ') ;
IF IsParameterVar(son)
THEN
found := TRUE ;
CalculateVarDirective(sym, son, FALSE)
ELSE
DoParamName(son)
END
END ;
IF i<p
THEN
fprintf0(f, ', ')
END ;
INC(i)
END
END ;
fprintf0(f, ');\n') ;
RETURN( found )
END DoProcedure ;
(*
DoWriteSymbol -
*)
PROCEDURE DoWriteSymbol (sym: CARDINAL) ;
BEGIN
IF IsBaseType(sym)
THEN
ELSIF IsSystemType(sym)
THEN
ELSIF IsType(sym)
THEN
ELSIF IsProcedure(sym)
THEN
IF DoProcedure(sym)
THEN
AnnotateProcedure(sym)
END
ELSIF IsConstString(sym)
THEN
ELSIF IsConstLit(sym)
THEN
ELSIF IsVar(sym) AND (GetMode(sym)=ImmediateValue)
THEN
ELSIF IsVar(sym)
THEN
DoVar(sym)
END
END DoWriteSymbol ;
(*
DoCheckExported -
*)
PROCEDURE DoCheckExported (sym: WORD) ;
BEGIN
IF IsExported(mainModule, sym)
THEN
DoWriteSymbol(sym)
END
END DoCheckExported ;
(*
IsUnique - returns TRUE if the combination of, n, and, t,
is unique.
*)
PROCEDURE IsUnique (n: Name; t: CARDINAL) : BOOLEAN ;
VAR
p : UnboundedSig ;
h, i: CARDINAL ;
BEGIN
i := 1 ;
h := HighIndice(uKey) ;
WHILE i<=h DO
p := GetIndice(uKey, i) ;
IF (p^.type=t) AND (p^.name=n)
THEN
RETURN( FALSE )
END ;
INC(i)
END ;
INC(h) ;
NEW(p) ;
WITH p^ DO
type := t ;
name := n
END ;
PutIndice(uKey, h, p) ;
RETURN( TRUE )
END IsUnique ;
(*
IsTypeUnique - returns TRUE if type, t, has not been entered yet.
*)
PROCEDURE IsTypeUnique (t: CARDINAL) : BOOLEAN ;
VAR
p : UnboundedSig ;
h, i: CARDINAL ;
BEGIN
i := 1 ;
h := HighIndice(uKey) ;
WHILE i<=h DO
p := GetIndice(uKey, i) ;
IF p^.type=t
THEN
RETURN( FALSE )
END ;
INC(i)
END ;
RETURN( TRUE )
END IsTypeUnique ;
(*
DoCheckUnbounded -
*)
PROCEDURE DoCheckUnbounded (sym: WORD) ;
VAR
name : Name ;
type : CARDINAL ;
typeUnique: BOOLEAN ;
BEGIN
IF IsParameter(sym) AND IsParameterUnbounded(sym)
THEN
name := GetSymName(sym) ;
type := GetType(GetType(sym)) ;
typeUnique := IsTypeUnique(type) ;
IF IsUnique(name, type)
THEN
IF NOT includedArray
THEN
includedArray := TRUE ;
fprintf0(f, '%%include "carrays.i"\n')
END ;
fprintf0(f, '%%') ;
fprintf0(f, 'apply (char *STRING, int LENGTH) { (') ;
DoUnbounded(sym) ;
fprintf0(f, ') };\n') ;
IF typeUnique
THEN
fprintf0(f, '%%array_functions(') ;
DoType(type) ;
fprintf0(f, ', ') ;
DoType(type) ;
fprintf0(f, 'Array);\n')
END
END
END
END DoCheckUnbounded ;
(*
DoWriteFile -
*)
PROCEDURE DoWriteFile (sym: CARDINAL) ;
VAR
n: Name ;
BEGIN
mainModule := sym ;
n := GetSymName(sym) ;
fprintf0(f, '/* Automatically generated by gm2 -fswig. */\n') ;
fprintf0(f, '%%') ;
fprintf1(f, 'module %a\n\n', n) ;
fprintf0(f, '%%') ;
fprintf1(f, 'include exception.i\n\n', n) ;
fprintf0(f, '%%') ;
fprintf0(f, 'exception {\n') ;
fprintf0(f, ' try {\n') ;
fprintf0(f, ' $action\n') ;
fprintf0(f, ' } catch (int i) {\n') ;
fprintf0(f, ' return NULL;\n') ;
fprintf0(f, ' }\n') ;
fprintf0(f, '}\n\n') ;
ForeachItemInListDo(Done, DoCheckUnbounded) ;
fprintf0(f, '\n%%{\n') ;
ForeachItemInListDo(Done, DoCheckExported) ;
fprintf0(f, '%%}\n\n') ;
ForeachItemInListDo(Done, DoCheckExported)
END DoWriteFile ;
(*
DoGenerateSwig -
*)
PROCEDURE DoGenerateSwig (sym: CARDINAL) ;
BEGIN
Init ;
name := ConCat (InitStringCharStar (KeyToCharStar (GetSymName (sym))),
Mark (InitString ('.i'))) ;
f := OpenToWrite (name) ;
ForeachExportedDo (sym, DoExported) ;
DoResolveOrder ;
DoWriteFile (sym) ;
Close (f) ;
name := KillString (name) ;
Kill
END DoGenerateSwig ;
(*
GenerateSwigFile - if the -fswig option was specified then generate
a swig interface file for the main module.
*)
PROCEDURE GenerateSwigFile (sym: CARDINAL) ;
BEGIN
IF GenerateSwig
THEN
DoGenerateSwig(sym)
END
END GenerateSwigFile ;
(*
Init -
*)
PROCEDURE Init ;
BEGIN
InitList(Done) ;
InitList(ToDo) ;
uKey := InitIndex(1) ;
includedArray := FALSE
END Init ;
(*
Kill -
*)
PROCEDURE Kill ;
BEGIN
KillList(Done) ;
KillList(ToDo) ;
uKey := KillIndex(uKey)
END Kill ;
END M2Swig.