home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d3xx
/
d339
/
pcq.lha
/
PCQ
/
Source.lzh
/
Source
/
Utilities.p
< prev
Wrap
Text File
|
1989-12-02
|
15KB
|
724 lines
external;
{
Utilities.p (of PCQ Pascal)
Copyright (c) 1989 Patrick Quaid.
This module handles the various tables and whatever
run-time business the compiler might have.
}
{$O-}
{$I "Pascal.i"}
{$I "Include/StringLib.i"}
{$I "Include/Exec.i"}
Procedure Error(s : string);
external;
Procedure NextSymbol;
external;
Procedure Abort;
external;
Procedure PushLongD0;
external;
Procedure PushLongD1;
External;
Procedure PopLongD1;
external;
Procedure PopLongD0;
External;
Procedure NewSpell;
var
TempPtr : SpellRecPtr;
begin
New(TempPtr);
TempPtr^.Previous := CurrentSpellRec;
CurrentSpellRec := TempPtr;
CurrentSpellRec^.First := SpellPtr;
end;
Procedure BackUpSpell(Position : Integer);
var
TempPtr : SpellRecPtr;
begin
while Position < CurrentSpellRec^.First do begin
TempPtr := CurrentSpellRec^.Previous;
Dispose(CurrentSpellRec);
CurrentSpellRec := TempPtr;
end;
SpellPtr := Position;
end;
Function EnterSpell(S : String) : String;
var
Length : Integer;
Result : String;
begin
Length := strlen(S) + 1;
if (Length + SpellPtr) - CurrentSpellRec^.First > Spell_Max then
NewSpell;
Result := Adr(CurrentSpellRec^.Data[SpellPtr - CurrentSpellRec^.First]);
strcpy(Result, S);
SpellPtr := SpellPtr + Length;
EnterSpell := Result;
end;
Function SimpleType(testtype : TypePtr) : Boolean;
{
If a variable passes this test, it is held in a register
during processing. If not, the address of the variable is held in
the register. This is the main reason why type conversions don't
work across all types of the same size.
}
begin
SimpleType := (TestType^.Size <= 4) and
(TestType^.Size <> 3) and
(TestType^.Object <> ob_record) and
(TestType^.Object <> ob_array);
end;
Function BaseType(orgtype : TypePtr): TypePtr;
{
This routine returns the base type of type. If this
routine is used consistently, ranges and subtypes will work with
some consistency.
}
begin
while (orgtype^.Object = ob_subrange) or (orgtype^.Object = ob_synonym) do
orgtype := orgtype^.SubType;
basetype := orgtype;
end;
Function HigherType(typea, typeb : TypePtr): TypePtr;
{
This routine returns the more complex type of the two
numeric types passed to it. In other words a 32 bit integer is
'higher' than a 16 bit one.
}
begin
if (TypeA = RealType) or (TypeB = RealType) then
HigherType := RealType;
if (typea = inttype) or (typeb = inttype) then
highertype := inttype;
if (typea = shorttype) or (typeb = shorttype) then
highertype := shorttype;
highertype := typea;
end;
Procedure PromoteType(var from : TypePtr; other : TypePtr; reg : Short);
{
This routine extends reg as necessary to make the 'from'
type equivalent to 'other'.
}
var
totype : TypePtr;
begin
from := basetype(from);
other := basetype(other);
totype := highertype(from, other);
if from = totype then
return;
if totype = realtype then begin
if from = bytetype then
writeln(OutFile, "\tand.l\t#255,d",reg)
else if from = shorttype then
writeln(OutFile, "\text.l\td",reg);
if reg = 0 then
PushLongD1
else begin
PushLongD0;
writeln(OutFile, "\tmove.l\td1,d0");
end;
writeln(OutFile, "\tmove.l\t_p%MathBase,a6");
writeln(OutFile, "\tjsr\t-36(a6)"); { _LVOSPFlt }
if reg = 0 then
PopLongD1
else begin
writeln(OutFile, "\tmove.l\td0,d1");
PopLongD0;
end;
from := RealType;
end else if totype = inttype then begin
if from = shorttype then
writeln(OutFile, "\text.l\td", reg)
else if from = bytetype then
writeln(OutFile, "\tand.l\t#255,d", reg);
from := inttype;
end else if totype = shorttype then begin
if from = bytetype then
writeln(OutFile, "\tand.w\t#255,d", reg);
from := shorttype;
end;
end;
Procedure NewBlock;
var
CB : BlockPtr;
i : Short;
begin
New(CB);
CB^.FirstType := Nil;
for i := 0 to Hash_Size do
CB^.Table[i] := Nil;
if CurrentBlock = Nil then
CB^.Level := 0
else
CB^.Level := Succ(CurrentBlock^.Level);
CB^.Previous := CurrentBlock;
CurrentBlock := CB;
end;
Procedure KillIDList(ID : IDPtr);
var
TempID : IDPtr;
begin
while ID <> Nil do begin
if (ID^.Object = proc) or (ID^.Object = func) then
KillIDList(ID^.Param);
TempID := ID^.Next;
Dispose(ID);
ID := TempID;
end;
end;
Procedure KillBlock;
var
CB : BlockPtr;
ID : IDPtr;
TP : TypePtr;
i : Integer;
Procedure KillTypeList(TP : TypePtr);
var
TempType : TypePtr;
begin
while TP <> nil do begin
if TP^.Object = ob_record then
KillIDList(TP^.Ref);
TempType := TP^.Next;
Dispose(TP);
TP := TempType;
end;
end;
begin
CB := CurrentBlock;
CurrentBlock := CurrentBlock^.Previous;
for i := 0 to Hash_Size do
KillIDList(CB^.Table[i]);
KillTypeList(CB^.FirstType);
end;
Function Match(sym : Symbols): Boolean;
{
If the current symbol is sym, return true and get the
next one.
}
begin
if CurrSym = Sym then begin
NextSymbol;
Match := True;
end else
Match := False;
end;
{
The following routines just print out common error messages
and make some common tests.
}
procedure Mismatch;
begin
error("Mismatched types");
end;
procedure NeedNumber;
begin
error("Need a numeric type");
end;
procedure NoLeftParent;
begin
error("No left parenthesis");
end;
procedure NoRightParent;
begin
error("No right parenthesis");
end;
procedure NeedLeftParent;
begin
if not match(leftparent1) then
noleftparent;
end;
procedure NeedRightParent;
begin
if not match(rightparent1) then
norightparent;
end;
{
Function Hash(s : String) : Short;
var
c : Char;
i : Short;
result : Integer;
begin
result := strlen(s);
i := 0;
while s[i] <> Chr(0) do begin
c := toupper(s[i]);
result := ((result * 13 + Ord(c)) and $07ff);
i := Succ(i);
end;
Hash := Result and Hash_Size;
end;
}
Procedure EnterID(EntryBlock : BlockPtr; ID : IDPtr);
var
HVal : Short;
begin
ID^.Level := EntryBlock^.Level;
HVal := Hash(ID^.Name) and Hash_Size;
ID^.Next := EntryBlock^.Table[HVal];
EntryBlock^.Table[HVal] := ID;
end;
Function EnterStandard( st_Name : String;
st_Object : IDObject;
st_Type : TypePtr;
st_Storage : IDStorage;
st_Offset : Integer) : IDPtr;
var
ID : IDPtr;
begin
new(ID);
ID^.Next := Nil;
ID^.Name := EnterSpell(st_Name);
ID^.Object := st_Object;
ID^.VType := st_Type;
ID^.Param := Nil;
ID^.Storage := st_Storage;
ID^.Offset := st_Offset;
EnterID(CurrentBlock, ID);
EnterStandard := ID;
end;
Procedure ns;
{
This routine just tests for a semicolon.
}
begin
if not match(semicolon1) then begin
if (currsym <> end1) and (currsym <> else1) and (currsym <> until1) then
error("missing semicolon");
end else
while match(semicolon1) do;
end;
Function TypeCmp(TypeA, TypeB : TypePtr) : Boolean;
{
This routine just compares two types to see if they're
equivalent. Subranges of the same type are considered equivalent.
Note that 'badtype' is actually a universal type used when there
are errors, in order to avoid streams of errors.
}
var
t1ptr,
t2ptr : IDPtr;
begin
TypeA := BaseType(TypeA);
TypeB := BaseType(TypeB);
if TypeA = TypeB then
TypeCmp := True;
if (TypeA = BadType) or (TypeB = BadType) then
TypeCmp := True;
if TypeA^.Object <> TypeB^.Object then
typecmp := false;
if TypeA^.Object = ob_array then begin
if (TypeA^.Upper - TypeA^.Lower) <>
(TypeB^.Upper - TypeB^.Lower) then
typecmp := false;
TypeCmp := TypeCmp(TypeA^.Subtype, TypeB^.SubType);
end;
if TypeA^.Object = ob_pointer then
TypeCmp := TypeCmp(TypeA^.SubType, TypeB^.SubType);
if TypeA^.Object = ob_file then
TypeCmp := TypeCmp(TypeA^.SubType, TypeB^.Subtype);
TypeCmp := false;
end;
Function NumberType(testtype : TypePtr) : Boolean;
{
Return true if this is a numeric type.
}
begin
TestType := BaseType(TestType);
if TestType = IntType then
NumberType := true
else if TestType = ShortType then
NumberType := True
else if TestType = ByteType then
NumberType := True;
NumberType := False;
end;
Function TypeCheck(TypeA, TypeB : TypePtr) : Boolean;
{
This is similar to typecmp, but considers numeric types
equivalent.
}
begin
TypeA := BaseType(TypeA);
TypeB := BaseType(TypeB);
if TypeA = TypeB then
TypeCheck := True;
if NumberType(TypeA) and NumberType(TypeB) then
TypeCheck := True;
TypeCheck := TypeCmp(TypeA, TypeB);
end;
Function AddType(at_Object : TypeObject;
at_SubType: TypePtr;
at_Ref : Address;
at_Upper,
at_Lower,
at_Size : Integer) : TypePtr;
{
Adds a type to the id array.
}
var
TP : TypePtr;
begin
New(TP);
TP^.Object := at_Object;
TP^.SubType := at_SubType;
TP^.Ref := at_Ref;
TP^.Upper := at_Upper;
TP^.Lower := at_Lower;
TP^.Size := at_Size;
TP^.Next := CurrentBlock^.FirstType;
CurrentBlock^.FirstType := TP;
AddType := TP;
end;
Function FindID(idname : string): IDPtr;
{ Find the most local reference to a variable }
var
ID : IDPtr;
CB : BlockPtr;
HVal : Short;
begin
CB := CurrentBlock;
HVal := Hash(idname) and Hash_Size;
while CB <> nil do begin
ID := CB^.Table[HVal];
while ID <> nil do begin
if strieq(idname, ID^.Name) then
FindID := ID;
ID := ID^.Next;
end;
CB := CB^.Previous;
end;
FindID := Nil;
end;
Function CheckID(idname : string): IDPtr;
{
This is like the above, but only checks the current block.
}
var
ID : IDPtr;
begin
ID := CurrentBlock^.Table[Hash(idname) and Hash_Size];
while ID <> nil do begin
if strieq(idname, ID^.Name) then
CheckID := ID;
ID := ID^.Next;
end;
CheckID := Nil;
end;
Function CheckIDList(S : String; ID : IDPtr) : Boolean;
begin
while ID <> nil do begin
if strieq(S, ID^.Name) then
CheckIDList := True;
ID := ID^.Next;
end;
CheckIDList := False;
end;
Function FindField(idname : string; RecType : TypePtr) : IDPtr;
{
This just finds the appropriate field, given the index of
the record type.
}
var
ID : IDPtr;
begin
ID := RecType^.Ref;
while ID <> Nil do begin
if strieq(idname, ID^.Name) then
FindField := ID;
ID := ID^.Next;
end;
FindField := Nil;
end;
Function FindWithField(Str : String) : IDPtr;
var
CurrentWith : WithRecPtr;
ID : IDPtr;
begin
CurrentWith := FirstWith;
while CurrentWith <> Nil do begin
ID := FindField(Str, CurrentWith^.RecType);
if ID <> Nil then begin
LastWith := CurrentWith;
FindWithField := ID;
end;
CurrentWith := CurrentWith^.Previous;
end;
FindWithField := Nil;
end;
Function IsVariable(ID : IDPtr) : Boolean;
{
Returns true if index is a variable.
}
begin
case ID^.Object of
local,
refarg,
valarg,
global,
typed_const,
field : IsVariable := True;
else
IsVariable := False;
end;
end;
Function Suffix(size : integer): char;
{
Returns the proper assembly language suffix for the various
operations.
}
begin
if size = 1 then
suffix := 'b'
else if size = 2 then
suffix := 'w'
else if size = 4 then
suffix := 'l'
else {must be a bug!}
suffix := '!';
end;
{
Procedure WriteTabs(Tabs : Short);
var
I : Short;
begin
I := 0;
while I < Tabs do begin
Write(' ');
I := Succ(I);
end;
end;
Procedure WriteID(ID : IDPtr; Tabs : Short; Primary : Boolean);
forward;
Procedure WriteType(TP : TypePtr; Tabs : Short; Primary : Boolean);
var
ID : IDPtr;
begin
if CheckBreak() then
Abort;
case TP^.Object of
ob_array : begin
write('Array [', TP^.lower, '..', TP^.upper, '] of ');
WriteType(TP^.SubType, Tabs, True);
end;
ob_record : begin
Write('Record');
if not Primary then
return
else
Writeln;
ID := TP^.Ref;
while ID <> Nil do begin
WriteID(ID, Tabs + 4, False);
ID := ID^.Next;
end;
WriteTabs(Tabs);
Write('END');
end;
ob_ordinal : begin
if TP = IntType then
Write('Integer')
else if TP = ShortType then
Write('Short')
else if TP = BoolType then
Write('Boolean')
else if TP = CharType then
Write('Char')
else if TP = ByteType then
Write('Byte')
else if TP = BadType then
Write('Universal')
else
Write('Enumerated');
end;
ob_pointer : begin
write('^');
WriteType(TP^.SubType, Tabs, Primary);
end;
ob_file : begin
if TP = TextType then
Write('Text')
else begin
write('File of ');
WriteType(TP^.SubType,Tabs, True);
end;
end;
ob_real : Write('Real');
ob_subrange : begin
Write(TP^.Lower, ' .. ', TP^.Upper, ' of ');
WriteType(TP^.SubType, Tabs, True);
end;
end;
end;
procedure WriteID(ID : IDPtr; Tabs : Short; Primary : Boolean);
var
TempID : IDPtr;
begin
if CheckBreak() then
Abort;
WriteTabs(Tabs);
case ID^.Object of
global,
local : write('VAR ');
refarg : write('REF ');
valarg : write('VAL ');
typed_const : write('IVAR ');
proc,
stanproc: write('Procedure ');
stanfunc,
func : write('Function ');
obtype : write('TYPE ');
constant : write('CONST ');
end;
if ID^.Name = nil then
Write('""')
else
Write(ID^.Name);
if (ID^.Object = proc) or (ID^.Object = func) then begin
TempID := ID^.Param;
write('(');
while TempID <> nil do begin
WriteID(TempID, Tabs + 4, True);
TempID := TempID^.Next;
end;
write(')');
end;
if (ID^.Object <> proc) and (ID^.Object <> stanproc) then begin
if (ID^.Object = constant) or (ID^.Object = refarg) or
(ID^.Object = valarg) or (ID^.Object = local) or
(ID^.Object = field) then
Write(' (', ID^.Offset, ') ');
Write(' : ');
WriteType(ID^.VType, Tabs, Primary)
end;
writeln(';');
end;
Procedure Decompose;
var
CB : BlockPtr;
ID : IDPtr;
TP : TypePtr;
i : Integer;
begin
Writeln("\nCurrent contents of the symbol table:");
CB := CurrentBlock;
while CB <> nil do begin
Writeln("\nLevel ", CB^.Level, "\n");
Writeln("Identifiers\n");
for i := 0 to Hash_Size do begin
ID := CB^.Table[i];
Writeln('Hash ', i);
while ID <> nil do begin
WriteID(ID, 0, True);
ID := ID^.Next;
end;
end;
Writeln("\nTypes\n");
TP := CB^.FirstType;
while TP <> nil do begin
WriteType(TP, 0, True);
Writeln;
TP := TP^.Next;
end;
CB := CB^.Previous;
end;
end;
}
Function CompareProcs(Proc1, Proc2 : IDPtr) : Boolean;
var
ID1, ID2 : IDPtr;
begin
if Proc1^.Object <> Proc2^.Object then
CompareProcs := False;
if Proc1^.Object = func then
if not TypeCmp(Proc1^.VType, Proc2^.VType) then
CompareProcs := False;
ID1 := Proc1^.Param;
ID2 := Proc2^.Param;
while (ID1 <> Nil) and (ID2 <> Nil) do begin
if not TypeCmp(ID1^.VType, ID2^.VType) then
CompareProcs := False;
ID1 := ID1^.Next;
ID2 := ID2^.Next;
end;
CompareProcs := ID1 = ID2;
end;