home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
300-399
/
ff339.lzh
/
PCQ
/
Source.lzh
/
Source
/
IO.p
< prev
next >
Wrap
Text File
|
1990-03-04
|
20KB
|
960 lines
External;
{
IO.p (of PCQ Pascal)
Copyright (c) 1989 Patrick Quaid
This module handles the IO of the compiler. The actual
compilation of the io statements is handled in stanprocs.p
}
{$O-}
{$I "Pascal.i"}
{$I "Include/DOS.i" }
{$I "Include/StringLib.i"}
{$I "Include/Exec.i"}
Function EndOfFile() : Boolean;
{
This just determines when the end of all input has occured.
}
begin
EndOfFile := (InFile = nil) and (not CharBuffed);
end;
Procedure AnnounceFile;
begin
Write('\r\cK', LineNo:5, ' ', InFile^.Name, '\r');
end;
Procedure WriteLineNo;
begin
Write(Chr(13), LineNo:5);
end;
Procedure CountLines;
{ Does the bookkeeping for errors }
begin
if CurrentChar = Chr(10) then begin
LineNo := Succ(LineNo);
if Inform then
if (LineNo and 15) = 0 then
WriteLineNo;
end;
end;
Procedure EndComment;
forward; { It's in this module }
Procedure CloseInputFile;
{ This closes the current input file and restores the saved stuff }
var
TempPtr : FileRecPtr;
begin
if Inform then begin
WriteLineNo;
Writeln;
end;
Close(InFile^.PCQFile);
TempPtr := InFile^.Previous;
FreeString(InFile^.Name);
Dispose(InFile);
InFile := TempPtr;
if InFile <> nil then begin
LineNo := InFile^.SaveLine;
FNStart := InFile^.SaveStart;
CurrentChar := InFile^.SaveChar;
if Inform then
AnnounceFile;
EndComment;
end else
CurrentChar := Chr(0);
end;
Procedure Abort;
{
This routine cuts out cleanly. If you are debugging the
compiler, this is a likely place to put post mortem dumps, like the
one commented out.
}
begin
While InFile <> nil do
CloseInputFile;
Close(OutFile);
Writeln('Compilation Aborted');
Exit(20);
end;
Function OpenInputFile(name : String) : Boolean;
{ This routine opens a new file record, and a new file. It also
saves the state of the File-dependant variables, like LineNo. }
var
TempPtr : FileRecPtr;
OpenError : Integer;
begin
New(TempPtr);
if not ReOpen(name, TempPtr^.PCQFile, 2048) then begin
Dispose(TempPtr);
OpenError := IOResult;
OpenInputFile := False;
end;
TempPtr^.Previous := InFile;
if InFile <> nil then begin
InFile^.SaveLine := LineNo;
InFile^.SaveStart := FNStart;
InFile^.SaveChar := CurrentChar;
end;
LineNo := 1;
FNStart := 1;
TempPtr^.Name := AllocString(strlen(name));
strcpy(TempPtr^.Name, name);
InFile := TempPtr;
if EOF(InFile^.PCQFile) then
CloseInputFile
else
Read(Infile^.PCQFile, CurrentChar);
if Inform then
AnnounceFile;
OpenInputFile := True;
end;
Function EQFix(x : integer): integer;
{
This helps implement a queue. In this case it's for the
error queue.
}
begin
if x = -1 then
EQFix := EQSize
else
EQFix := x mod (Succ(EQSize));
end;
Procedure Error(ptr : string);
{
This just writes out at most the previous 128 characters or
two lines, then writes the error message passed to it. If there
are five errors, it aborts.
}
var
index : integer;
newlines : integer;
begin
index := EQEnd;
newlines := 0;
while (index <> EQStart) and (newlines < 2) do begin
index := EQFix(index - 1);
if ErrorQ[EQFix(index - 1)] = chr(10) then
newlines := newlines + 1;
end;
if Inform then begin
write('\n\cK'); { newline, ClrEOL }
while index <> EQEnd do begin
if index = ErrorPtr then
write('\c0;33;40m'); { start highlight for ANSI }
write(ErrorQ[index]);
index := EQFix(index + 1);
end;
write('\c0;31;40m'); { end highlight }
writeln;
write('Line ', lineno, ' ');
if currfn <> nil then
write('(', CurrFn^.Name, ')');
writeln(': ', ptr, '\n');
end else
Writeln('Line ', LineNo, ' : ', ptr); { Quiet mode, no surprises }
Inc(errorcount);
if errorcount > 4 then
Abort;
if CheckBreak() then
Abort;
if Inform then
AnnounceFile;
end;
Procedure ReadChar;
{ This is the main link between the lexical analysis stuff and the
IO stuff. It sets up CurrentChar and keeps the line count. }
var
IOError : Integer;
begin
if CheckBreak() then
Abort;
if CharBuffed then begin
CurrentChar := BuffedChar;
CharBuffed := False;
return;
end;
if EOF(InFile^.PCQFile) then
CloseInputFile
else begin
Read(InFile^.PCQFile, CurrentChar);
IOError := IOResult;
CountLines;
end;
EQEnd := EQFix(Succ(EQEnd));
ErrorQ[EQEnd] := CurrentChar;
if EQStart = EQEnd then
EQStart := EQFix(Succ(EQStart));
end;
Function NextChar() : Char;
var
c : Char;
begin
if not CharBuffed then begin
c := CurrentChar;
ReadChar;
BuffedChar := CurrentChar;
CurrentChar := c;
CharBuffed := True;
end;
NextChar := BuffedChar;
end;
Procedure EndComment;
{
This just eats characters up to the end of a comment. If
you want nested comments, this is probably the place to do it.
}
begin
while (Currentchar <> '}') and (not EndOfFile()) do
ReadChar;
if not EndOfFile() then
ReadChar;
end;
Function GetLabel() : integer;
{
As in all compilers, this just returns a unique serial
number.
}
begin
Inc(NxtLab);
getlabel := nxtlab;
end;
Procedure PrintLabel(lab : integer);
{
This routine prints a label based on a number from the
above procedure. The prefix for the label can be anything the
assembler accepts - in this case I wanted it similar to the prefix
of the run time library routines. I didn't realize how ugly it
would look.
}
begin
write(OutFile, '_p%', lab);
end;
Function JustFileName(S : String) : String;
{ returns a string that is the file name part of a path. It does
NOT allocate space. }
var
Ptr : String;
begin
if S^ = Chr(0) then
JustFileName := S;
Ptr := String(Integer(S) + strlen(s) - 1);
while (Ptr^ <> ':') and (Ptr^ <> '/') do begin
if Ptr = S then
JustFileName := S;
Dec(Ptr);
end;
Inc(Ptr);
JustFileName := Ptr;
end;
Procedure AddIncludeName(S : String);
{ Adds the name of an include file to the list, so it won't be
included again. }
var
Ptr : IncludeRecPtr;
begin
Ptr := IncludeRecPtr(AllocString(strlen(S) + 5));
if Ptr = nil then
Abort;
strcpy(Adr(Ptr^.Name), S);
Ptr^.Next := IncludeList;
IncludeList := Ptr;
end;
Function AlreadyIncluded(S : String) : Boolean;
{ Determines whether a file has been included already }
var
Ptr : IncludeRecPtr;
begin
Ptr := IncludeList;
while Ptr <> nil do begin
if streq(Adr(Ptr^.Name), S) then
AlreadyIncluded := True;
Ptr := Ptr^.Next;
end;
AlreadyIncluded := False;
end;
Procedure DoInclude;
{
The name says it all. The mechanics of the include
directive are all handled here.
}
var
Ptr : String;
begin
ReadChar;
While (CurrentChar <= ' ') and (not EndOfFile()) do
ReadChar;
if CurrentChar <> '"' then begin
Error("Missing Quote");
EndComment;
Return;
end;
ReadChar;
Ptr := SymText;
while CurrentChar <> '"' do begin
Ptr^ := CurrentChar;
Inc(Ptr);
if EndOfFile() then
Return;
ReadChar;
end;
Ptr^ := Chr(0); { mark then end of the file name }
ReadChar; { read the end quote }
if not AlreadyIncluded(JustFileName(SymText)) then begin
if OpenInputFile(SymText) then
AddIncludeName(JustFileName(SymText))
else begin
Error("Could not open input file");
EndComment;
end;
end else
EndComment;
end;
Procedure DoComment;
{
This routine implements compiler directives.
}
Procedure DoASM;
begin
ReadChar;
while CurrentChar <> '}' do begin
Write(OutFile, currentchar);
if EndOfFile() then begin
Error("File ended in a comment");
return;
end;
ReadChar;
end;
ReadChar;
Writeln(OutFile);
end;
Procedure DoOnOff(var Flag : Boolean);
begin
ReadChar;
if CurrentChar = '+' then
Flag := True
else if CurrentChar = '-' then
Flag := False;
end;
Procedure DoStorage;
var
KillChar : Boolean;
begin
ReadChar;
KillChar := True;
case CurrentChar of
'X' : StandardStorage := st_external;
'P' : StandardStorage := st_private;
'N' : StandardStorage := st_internal;
else begin
Error("Unknown storage class");
KillChar := False;
end;
end;
if KillChar then
ReadChar;
end;
begin
readchar;
if currentchar = '$' then begin
repeat
readchar; { either $ or , }
Case CurrentChar of
'I' : begin
DoInclude;
return;
end;
'A' : begin
DoASM;
return;
end;
'R' : DoOnOff(RangeCheck);
'O' : DoOnOff(IOCheck);
'S' : DoStorage;
else begin
Error("Unknown Directive");
EndComment;
return;
end;
end;
if (CurrentChar <> ',') or EndOfFile then begin
EndComment;
return;
end;
until false;
end else
EndComment;
end;
Function Alpha(c : char): boolean;
{
This function answers the eternal question "is this
character an alphabetic character?" Note that _ is.
}
begin
c := toupper(c);
Alpha := ((c >= 'A') and (c <= 'Z')) or (c = '_');
end;
Function AlphaNumeric(c : char): boolean;
{
Is the character a letter or digit?
}
begin
AlphaNumeric := Alpha(c) or isdigit(c);
end;
Procedure Header;
{
This routine references all the run time library routines.
One thing I like about A68k is that the only routines that are
used in the assembly code end up in the object file. Maybe all
assemblers do it, but I don't know.
}
begin
writeln(OutFile, "* Pascal compiler intermediate assembly program.\n\n");
writeln(OutFile, "\tSECTION\tPCQMain\n");
writeln(OutFile, "\tXREF\t_Input");
writeln(OutFile, "\tXREF\t_Output");
writeln(OutFile, "\tXREF\t_p%WriteInt");
writeln(OutFile, "\tXREF\t_p%WriteReal");
writeln(OutFile, "\tXREF\t_p%WriteChar");
writeln(OutFile, "\tXREF\t_p%WriteBool");
writeln(OutFile, "\tXREF\t_p%WriteCharray");
writeln(OutFile, "\tXREF\t_p%WriteString");
writeln(OutFile, "\tXREF\t_p%WriteLn");
writeln(OutFile, "\tXREF\t_p%ReadInt");
writeln(OutFile, "\tXREF\t_p%ReadReal");
writeln(OutFile, "\tXREF\t_p%ReadCharray");
writeln(OutFile, "\tXREF\t_p%ReadChar");
writeln(OutFile, "\tXREF\t_p%ReadString");
writeln(OutFile, "\tXREF\t_p%ReadLn");
writeln(OutFile, "\tXREF\t_p%ReadArb");
writeln(OutFile, '\tXREF\t_p%FilePtr');
writeln(OutFile, '\tXREF\t_p%Get');
writeln(OutFile, '\tXREF\t_p%Put');
writeln(OutFile, "\tXREF\t_p%dispose");
writeln(OutFile, "\tXREF\t_p%new");
writeln(OutFile, "\tXREF\t_p%Open");
writeln(OutFile, "\tXREF\t_p%WriteArb");
writeln(OutFile, "\tXREF\t_p%Close");
writeln(OutFile, "\tXREF\t_p%exit");
writeln(OutFile, "\tXREF\t_p%lmul");
writeln(OutFile, "\tXREF\t_p%ldiv");
writeln(OutFile, "\tXREF\t_p%lrem");
writeln(OutFile, "\tXREF\t_p%MathBase");
writeln(OutFile, '\tXREF\t_p%sin');
writeln(OutFile, '\tXREF\t_p%cos');
writeln(OutFile, '\tXREF\t_p%sqrt');
Writeln(OutFile, '\tXREF\t_p%tan');
Writeln(OutFile, '\tXREF\t_p%atn');
Writeln(OutFile, '\tXREF\t_p%CheckIO');
Writeln(OutFile, '\tXREF\t_p%CheckRange\n');
if mainmode then begin
writeln(OutFile, "\tXREF\t_p%initialize");
writeln(OutFile, "\tjsr\t_p%initialize");
writeln(OutFile, "\tjsr\t_MAIN");
writeln(OutFile, '\tmoveq.l\t#0,d0');
writeln(OutFile, "\tjsr\t_p%exit");
writeln(OutFile, "\trts");
end
end;
Procedure Trailer;
{
This routine is the most important in the compiler
}
begin
writeln(OutFile, "\tEND");
end;
Procedure Blanks;
{
blanks() skips spaces, tabs and eoln's. It handles
comments if it comes across one.
}
var
done : boolean;
begin
while ((CurrentChar <= ' ') or (CurrentChar = '{')) and
(not EndOfFile()) do begin
if CurrentChar = '{' then
DoComment
else
ReadChar;
end;
end;
Procedure DumpLitQ(k : Integer);
{
This procedure dumps the literal table at the end of the
compilation. Individual components are referenced as offsets to
the literal label.
}
var
j : integer;
quotemode : boolean;
begin
while k < litptr do begin
write(OutFile, "\tdc.b\t");
j := 0;
quotemode := false;
while j < 40 do begin
if (ord(litq[k]) > 31) and (ord(litq[k]) <> 39) then begin
if quotemode then
write(OutFile, litq[k])
else begin
if j > 0 then
write(OutFile, ',');
write(OutFile, chr(39), litq[k]);
quotemode := true;
end;
end else begin
if quotemode then begin
write(OutFile, chr(39));
quotemode := false;
end;
if j > 0 then
write(OutFile, ',');
write(OutFile, ord(litq[k]));
if j > 32 then
j := 40
else
j := j + 3;
end;
j := j + 1;
k := k + 1;
if k >= litptr then
j := 40;
end;
if quotemode then
write(OutFile, chr(39));
writeln(OutFile);
end
end;
Procedure DumpLits;
begin
if LitPtr = 0 then
return;
writeln(OutFile, '\n\tSECTION\tLITS,DATA');
PrintLabel(LitLab);
DumpLitQ(0);
end;
Procedure DumpIds;
{
This routine does whatever is appropriate with the various
identifers. If it's a global, it either references it or allocates
space. Similar stuff for the other ids. When the modularity of
PCQ is better defined, this routine will have to do more work.
}
var
CB : BlockPtr;
ID : IDPtr;
TP : TypePtr;
i : Integer;
isodd : boolean;
begin
if mainmode then
writeln(OutFile, "\n\tSECTION\tTHREE,BSS\n");
isodd := false;
CB := CurrentBlock;
while CB <> nil do begin
for i := 0 to Hash_Size do begin
ID := CB^.Table[i];
while ID <> nil do begin
case ID^.Object of
global : case ID^.Storage of
st_internal,
st_private : begin
TP := ID^.VType;
if isodd and (TP^.Size > 1) then begin
Writeln(OutFile, "\tCNOP\t0,2");
isodd := False;
end;
if ID^.Storage <> st_private then
Writeln(OutFile,"\tXDEF\t_", ID^.Name);
Write(OutFile, '_', ID^.Name);
Writeln(OutFile, "\tds.b\t", TP^.Size);
if odd(TP^.Size) then
isodd := not isodd;
end;
end;
proc,
func : if ID^.Storage = st_forward then
Writeln(ID^.Name, ' was never defined.');
end;
ID := ID^.Next;
end;
end;
CB := CB^.Previous;
end;
end;
Procedure DumpRefs;
{
This routine makes all the external references necessary.
}
var
CB : BlockPtr;
ID : IDPtr;
i : Integer;
begin
writeln(OutFile);
CB := CurrentBlock;
while CB <> nil do begin
for i := 0 to Hash_Size do begin
ID := CB^.Table[i];
while ID <> nil do begin
if ID^.Storage = st_external then
writeln(OutFile, "\tXREF\t_", ID^.Name);
ID := ID^.Next;
end;
end;
CB := CB^.Previous;
end
end;
Procedure SearchReserved;
{
This just does a binary chop search of the list of reserved
words.
}
var
top,
middle,
bottom : Symbols;
compare : Short;
begin
Bottom := And1;
Top := LastReserved;
while Bottom <= Top do begin
middle := Symbols((Short(bottom) + Short(top)) div 2);
Compare := stricmp(Reserved[Middle], SymText);
if Compare = 0 then begin
CurrSym := Middle;
Return;
end else if Compare < 0 then
Bottom := Succ(Middle)
else
Top := Pred(Middle);
end;
CurrSym := Ident1;
end;
Procedure ReadWord;
{
This reads a Pascal identifier into symtext.
}
var
ptr : string;
begin
ptr := symtext;
repeat
Ptr^ := CurrentChar;
Ptr := String(Integer(Ptr) + 1);
ReadChar;
until not AlphaNumeric(CurrentChar);
Ptr^ := chr(0);
SearchReserved;
end;
Function DigVal(c : Char) : Integer;
begin
DigVal := Ord(c) - Ord('0');
end;
Procedure ReadNumber;
{
This routine reads a literal integer. Note that _ can be used.
}
var
Divider : Real;
Fraction : Real;
begin
SymLoc := 0;
While isdigit(CurrentChar) do begin
SymLoc := (SymLoc * 10) + DigVal(CurrentChar);
ReadChar;
if CurrentChar = '_' then
ReadChar;
end;
CurrSym := Numeral1;
if (CurrentChar = '.') and isdigit(NextChar()) then begin { It's real! }
ReadChar; { skip the . }
RealValue := Float(SymLoc);
Divider := 1.0;
Fraction := 0.0;
while isdigit(CurrentChar) do begin
Fraction := Fraction * 10.0 + Float(DigVal(CurrentChar));
Divider := Divider * 10.0;
ReadChar;
end;
RealValue := RealValue + Fraction / Divider;
CurrSym := RealNumeral1;
end;
end;
Procedure ReadHex;
{
readhex() reads a hexadecimal number.
}
var
rc : integer;
begin
ReadChar;
symloc := 0;
rc := ord(toupper(currentchar));
while isdigit(currentchar) or
((rc >= ord('A')) and (rc <= ord('F'))) do begin
SymLoc := SymLoc shl 4;
if isdigit(currentchar) then
symloc := symloc + ord(currentchar) - ord('0')
else
symloc := symloc + rc - ord('A') + 10;
ReadChar;
rc := ord(toupper(currentchar));
end;
currsym := numeral1;
end;
Procedure WriteHex(num : integer);
{
This writes full 32 bit hexadecimal numbers.
}
var
numary : array [1..8] of char;
pos : integer;
ch : Short;
begin
pos := 8;
while (num <> 0) and (pos > 0) do begin
ch := num and 15;
if ch < 10 then
numary[pos] := chr(ch + ord('0'))
else
numary[pos] := chr(ch + ord('A') - 10);
pos := pos - 1;
num := num shr 4;
end;
if pos = 8 then begin
pos := 7;
numary[8] := '0';
end;
write(OutFile, '$');
for num := pos + 1 to 8 do
write(OutFile, numary[num]);
end;
Procedure NextSymbol;
{
This is the workhorse lexical analysis routine. It sets
currsym to the appropriate symbol number, sets symtext equal to
whatever identifier is read, and symloc to the value of a literal
integer.
}
begin
ErrorPtr := EQEnd;
Blanks;
if EndOfFile then begin
CurrentChar := Chr(0);
CurrSym := EndText1; { I don't think this routine is ever hit }
Return;
end;
if Alpha(CurrentChar) then
readword
else if isdigit(currentchar) then
readnumber
else begin
case CurrentChar of
'[' : begin
CurrSym:= leftbrack1;
ReadChar;
end;
']' : begin
CurrSym:= rightbrack1;
ReadChar;
end;
'(' : begin
CurrSym:= leftparent1;
ReadChar;
end;
')' : begin
CurrSym:= rightparent1;
ReadChar;
end;
'+' : begin
CurrSym := plus1;
ReadChar;
end;
'-' : begin
CurrSym := minus1;
ReadChar;
end;
'*' : begin
CurrSym:= asterisk1;
ReadChar;
end;
'/' : begin
CurrSym := RealDiv1;
ReadChar;
end;
'<' : begin
ReadChar;
if CurrentChar = '=' then begin
CurrSym := notgreater1;
ReadChar;
end else if currentchar = '>' then begin
CurrSym := notequal1;
ReadChar;
end else
CurrSym:= less1;
end;
'=' : begin
CurrSym:= equal1;
ReadChar;
end;
'>' : begin
ReadChar;
if CurrentChar = '=' then begin
CurrSym:= notless1;
ReadChar;
end else
CurrSym:= greater1;
end;
':' : begin
ReadChar;
if CurrentChar = '=' then begin
CurrSym:= Becomes1;
ReadChar;
end else
CurrSym:= colon1;
end;
',' : begin
CurrSym:= comma1;
ReadChar;
end;
'.' : begin
ReadChar;
if CurrentChar = '.' then begin
CurrSym:= DotDot1;
ReadChar;
end else
CurrSym:= period1;
end;
';' : begin
CurrSym:= semicolon1;
ReadChar;
end;
'\'': begin
CurrSym:= apostrophe1;
ReadChar;
end;
'"' : begin
CurrSym:= quote1;
ReadChar;
end;
'^' : begin
CurrSym:= carat1;
ReadChar;
end;
'@' : begin
CurrSym := At1;
ReadChar;
end;
'$' : ReadHex;
'\0' : CurrSym := EndText1;
else begin
Error("Unknown symbol.");
ReadChar;
end;
end; { Case }
end { Else }
end;