home *** CD-ROM | disk | FTP | other *** search
- 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
- }
-
- const
- {$I "pasconst.i"}
-
- type
- {$I "pastype.i"}
-
- var
- {$I "pasvar.i"}
-
- procedure doinclude;
- forward;
- function AllocString(i : integer): string;
- forward;
- procedure FreeString(s : string);
- forward;
- function searchreserved(): integer;
- forward;
- function raise(c : Char): Char;
- forward;
-
- procedure readchar;
- forward;
- procedure endinclude;
- forward;
-
-
- { This routine lists the contents of the identifier table for
- debugging purposes.
-
- procedure dumptypes;
- var
- index : integer;
- begin
- for index := 1 to identptr - 1 do begin
- write(index, chr(9));
- if idents[index].name = string(adr(spelling)) then
- writeln('no name')
- else
- writeln(idents[index].name);
- writeln('object ', idents[index].object);
- writeln('offset ', idents[index].offset);
- writeln('vtype ', idents[index].vtype);
- writeln('upper ', idents[index].upper);
- writeln('lower ', idents[index].lower);
- writeln('size ', idents[index].size);
- writeln('indtype ', idents[index].indtype);
- writeln;
- end;
- 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
- if including then begin
- close(input2);
- close(input);
- end else
- close(input);
- writeln('Compilation aborted');
- { writeln('IdentPtr = ', identptr, '. SpellPtr = ', spellptr,
- '. LitPtr = ', litptr);
- dumptypes; }
- exit(20);
- 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 (eqsize + 1);
- 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 more than 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;
-
- while index <> eqend do begin
- if index = errorptr then
- write(chr($9b), '0;33;40m'); { start highlight for ANSI }
- write(errorq[index]);
- index := eqfix(index + 1);
- end;
- write(chr($9b), '0;31;40m'); { end highlight }
- writeln;
-
- if including then
- write('"', includename, '", ')
- else
- write('"', mainname, '", ');
-
- write('Line ', lineno, ' ');
- if currfn <> 0 then
- write('(', idents[currfn].name, ')');
- writeln(': ', ptr);
- writeln;
-
- { writeln('Identptr = ', identptr, '. SpellPtr = ', spellptr); }
-
- errorcount := errorcount + 1;
- if errorcount > 5 then
- abort;
- end;
-
- function endoffile(): boolean;
-
- {
- This is the modified eof() function. This is necessary
- because of include files.
- }
-
- begin
- if including then
- if eof(input2) then begin
- endinclude;
- endoffile := eof(input);
- end else
- endoffile := false;
- else
- endoffile := eof(input);
- 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 <> '}' do begin
- if endoffile() then begin
- error("The file ended in a comment!");
- return;
- end;
- readchar;
- end;
- readchar;
- end;
-
- procedure endinclude;
-
- {
- This switches the input back to the main file.
- }
-
- begin
- close(input2);
- including := false;
- lineno := saveline;
- fnstart := savestart;
- currentchar := savechar;
- endcomment;
- end;
-
- procedure readchar;
-
- {
- This just reads a character from wherever it's appropriate.
- In the next version, the options might include an ARexx port.
- }
-
- begin
- if including then begin
- if eof(input2) then begin
- endinclude;
- end else
- read(input2, currentchar)
- end else
- read(input, currentchar);
-
- { At this point the character is read. The following code just
- inserts the character into a queue, which will be printed if
- we hit an error. }
-
- if currentchar = chr(10) then
- lineno := lineno + 1;
- eqend := eqfix(eqend + 1);
- errorq[eqend] := currentchar;
- if eqstart = eqend then
- eqstart := eqfix(eqend + 1);
- end;
-
- procedure gch;
-
- {
- This reads a character from the same line, for situations
- where a symbol cannot be spread over two lines.
- }
-
- begin
- if currentchar <> chr(10) then
- readchar;
- end;
-
- function getlabel() : integer;
-
- {
- As in all compilers, this just returns a unique serial
- number.
- }
-
- begin
- nxtlab := nxtlab + 1;
- 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(output, '_p%', lab);
- end;
-
- function nch(): char;
-
- {
- This stands for next character, and just returns the
- buffered character from the appropriate file. It looks ahead.
- }
-
- begin
- if including then
- nch := input2^
- else
- nch := input^;
- end;
-
- procedure doinclude;
-
- {
- The name says it all. The mechanics of the include
- directive are all handled here. If you want to nest includes,
- you'll have to implement a list or something here, then adjust
- endoffile(), readchar(), nextchar(), etc. Not too hard, I suppose.
- }
-
- var
- c : string;
- begin
- if including then
- error("Cannot nest include files")
- else begin
- while (currentchar = ' ') or (currentchar = chr(9)) or
- (currentchar = chr(10)) do
- readchar;
- if currentchar = '"' then
- gch
- else
- error("missing open quote");
- c := includename;
- while (currentchar <> '"') and (currentchar <> chr(10)) do begin
- c^ := currentchar;
- readchar;
- c := string(integer(c) + 1); { sorry. }
- end;
-
- if currentchar = '"' then
- readchar
- else
- error("missing close quote");
-
- c^ := chr(0);
-
- if reopen(includename, input2) then begin
- saveline := lineno;
- savestart := fnstart;
- savechar := currentchar;
- including := true;
- readchar;
- end else
- error("Could not open include file");
- end
- end;
-
- procedure docomment;
-
- {
- This routine implements compiler directives. When I get a
- few more directives I'll probably split these up a bit. I'd also
- like to make the directives themselves full words.
- }
-
- begin
- readchar;
- if currentchar = '$' then begin
- readchar;
- if currentchar = 'I' then begin
- readchar;
- doinclude;
- return;
- end else if currentchar = 'A' then begin
- readchar;
- while currentchar <> '}' do begin
- write(output, currentchar);
- if endoffile() then begin
- error("File ended in a comment");
- return;
- end;
- readchar;
- end;
- readchar;
- writeln(output);
- return;
- end else if currentchar = 'R' then begin
- readchar;
- if currentchar = '+' then
- rangecheck := true
- else if currentchar = '-' then
- rangecheck := false;
- end;
- end;
- endcomment;
- end;
-
- function alpha(c : char): boolean;
-
- {
- This function answers the eternal question "is this
- character an alphabetic character?" Note that _ is.
- }
-
- begin
- if (ord(c) >= ord('a')) and (ord(c) <= ord('z')) then
- alpha := true
- else if (ord(c) >= ord('A')) and (ord(c) <= ord('Z')) then
- alpha := true
- else if c = '_' then
- alpha := true
- else
- alpha := false;
- end;
-
- function numeric(c : char): boolean;
-
- {
- Is the character a digit?
- }
-
- begin
- numeric := (ord(c) >= ord('0')) and (ord(c) <= ord('9'));
- end;
-
- function an(c : char): boolean;
-
- {
- Is the character a letter or digit?
- }
-
- begin
- an := alpha(c) or numeric(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 will
- actually be referenced are those that are used in the code. Maybe
- all assemblers do this, but I don't know.
- }
-
- begin
- writeln(output, "* Pascal compiler intermediate assembly program.\n\n");
- writeln(output, "\tSECTION\tONE\n");
- writeln(output, "\tXREF\t_stdout");
- writeln(output, "\tXREF\t_p%writeint");
- writeln(output, "\tXREF\t_p%writechar");
- writeln(output, "\tXREF\t_p%writebool");
- writeln(output, "\tXREF\t_p%writecharray");
- writeln(output, "\tXREF\t_p%writestring");
- writeln(output, "\tXREF\t_p%writeln");
- writeln(output, "\tXREF\t_p%readint");
- writeln(output, "\tXREF\t_p%readcharray");
- writeln(output, "\tXREF\t_p%readchar");
- writeln(output, "\tXREF\t_p%readarbbuf");
- writeln(output, "\tXREF\t_p%readstring");
- writeln(output, "\tXREF\t_p%readln");
- writeln(output, "\tXREF\t_p%readarb");
- writeln(output, "\tXREF\t_p%dispose");
- writeln(output, "\tXREF\t_p%new");
- writeln(output, "\tXREF\t_p%open");
- writeln(output, "\tXREF\t_p%writearb");
- writeln(output, "\tXREF\t_p%close");
- writeln(output, "\tXREF\t_p%case");
- writeln(output, "\tXREF\t_p%exit\n");
- if mainmode then begin
- writeln(output, "\tXREF\t_p%initialize");
- writeln(output, "\tXREF\t_p%wrapitup");
- writeln(output, "\tjsr\t_p%initialize");
- writeln(output, "\tjsr\t_MAIN");
- writeln(output, "\tjsr\t_p%wrapitup");
- writeln(output, "\trts");
- end
- end;
-
- procedure trailer;
-
- {
- This routine is the most important in the compiler
- }
-
- begin
- writeln(output, "\tEND");
- end;
-
- procedure blanks;
-
- {
- blanks() skips spaces, tabs and eoln's. It handles
- comments if it comes across one.
- }
-
- var
- done : boolean;
- begin
- if currentchar = '{' then
- docomment;
- done := false;
- while not done do begin
- if endoffile() then
- done := true
- else if (currentchar = ' ') or (currentchar = chr(9)) or
- (currentchar = chr(10)) then
- readchar
- else if currentchar = '{' then
- docomment;
- else
- done := true;
- end;
- end;
-
- procedure dumplits;
-
- {
- This procedure dumps the literal table at the end of the
- compilation. Individual components are referenced as offsets to
- the literal label.
- }
-
- var
- j, k : integer;
- quotemode : boolean;
- begin
- if litptr = 0 then
- return;
- writeln(output, "\n\tSECTION\tTWO,DATA\n");
- printlabel(litlab);
- k := 1;
- while k < litptr do begin
- write(output, "\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(output, litq[k])
- else begin
- if j > 0 then
- write(output, ',');
- write(output, chr(39), litq[k]);
- quotemode := true;
- end;
- end else begin
- if quotemode then begin
- write(output, chr(39));
- quotemode := false;
- end;
- if j > 0 then
- write(output, ',');
- write(output, 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(output, chr(39));
- writeln(output);
- end
- 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
- vartype : integer;
- index : integer;
- isodd : boolean;
- begin
- if mainmode then
- writeln(output, "\n\tSECTION\tTHREE,BSS\n");
- index:= 1;
- isodd := false;
- while index < identptr do begin
- if idents[index].object = global then begin
- if mainmode then begin
- vartype := idents[index].vtype;
- if isodd and (idents[vartype].size > 1) then begin
- writeln(output, "\tCNOP\t0,2");
- isodd := false;
- end;
- writeln(output, "\tXDEF\t_", idents[index].name);
- write(output, '_', idents[index].name);
- writeln(output, "\tds.b\t", idents[vartype].size);
- if odd(idents[vartype].size) then
- isodd := not isodd;
- end else
- writeln(output, "\tXREF\t_", idents[index].name);
- end else if (idents[index].object = proc) or
- (idents[index].object = func) then
- if idents[index].upper = 0 then
- writeln(output, "\tXREF\t_", idents[index].name);
- index := index + 1;
- end
- end;
-
- procedure readword;
-
- {
- This reads a Pascal identifier into symtext.
- }
-
- var
- index : integer;
- ptr : string;
- begin
- index := 0;
- ptr := symtext;
- while an(currentchar) do begin
- ptr^ := currentchar;
- gch;
- ptr := string(integer(ptr) + 1); { here's that thing again...}
- end;
- ptr^ := chr(0);
- currsym := searchreserved();
- if currsym = 0 then
- currsym := ident1;
- symloc := 0;
- end;
-
- procedure readnumber;
-
- {
- This routine reads a literal integer. Since it uses *, it
- will not properly handle numbers whose magnitude is greater than
- about 200,000 or 300,000. Note that _ can be used.
- }
-
- var
- negative : boolean;
- begin
- if currentchar = '-' then begin
- negative := true;
- gch();
- end else
- negative := false;
- symloc:= 0;
- while numeric(currentchar) do begin
- symloc := symloc * 10 + ord(currentchar) - ord('0');
- gch();
- if currentchar = '_' then
- gch();
- end;
- if negative then
- symloc := -symloc;
- currsym := numeral1;
- end;
-
- procedure readhex;
-
- {
- readhex() reads a hexadecimal number. Since it uses the
- assembly instructions it is able to read full 32 bit values.
- }
-
- var
- rc : integer;
- begin
- gch;
- symloc := 0;
- rc := ord(raise(currentchar));
- while numeric(currentchar) or
- ((rc >= ord('A')) and (rc <= ord('F'))) do begin
-
- {$A move.l _symloc,d0
- asl.l #4,d0
- move.l d0,_symloc ; symloc := symloc * 16;
- }
- if numeric(currentchar) then
- symloc := symloc + ord(currentchar) - ord('0')
- else
- symloc := symloc + rc - ord('A') + 10;
- gch;
- rc := ord(raise(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 : char;
- begin
- pos := 8;
- while (num <> 0) and (pos > 0) do begin
- {$A move.l 8(a5),d0
- and.b #15,d0
- move.b d0,-13(a5) ; ch := num AND $0f;
- }
- if ord(ch) < 10 then
- numary[pos] := chr(ord(ch) + ord('0'))
- else
- numary[pos] := chr(ord(ch) + ord('A') - 10);
- pos := pos - 1;
-
- {$A move.l 8(a5),d0
- lsr.l #4,d0
- move.l d0,8(a5) ; num := num div 16;
- }
- end;
- if pos = 8 then begin
- pos := 7;
- numary[8] := '0';
- end;
- write(output, '$');
- for num := pos + 1 to 8 do
- write(output, 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.
- Soon this will be a big case statement.
- }
-
- begin
- errorptr := eqend;
- blanks;
- if endoffile() then begin
- currentchar := chr(0);
- currsym := endtext1; { I don't think this routine is ever hit }
- return;
- end;
- while currentchar = '{' do begin
- docomment; { I think this is unused }
- blanks;
- end;
- if alpha(currentchar) then
- readword
- else if numeric(currentchar) then
- readnumber
- else if currentchar = '[' then begin
- currsym:= leftbrack1;
- readchar;
- end else if currentchar = ']' then begin
- currsym:= rightbrack1;
- readchar;
- end else if currentchar = '(' then begin
- currsym:= leftparent1;
- readchar;
- end else if currentchar = ')' then begin
- currsym:= rightparent1;
- readchar;
- end else if currentchar = '+' then begin
- currsym := plus1;
- readchar;
- end else if currentchar = '-' then begin
- currsym := minus1;
- readchar;
- end else if currentchar = '*' then begin
- currsym:= asterisk1;
- readchar;
- end else if currentchar = '<' then begin
- gch;
- if currentchar = '=' then begin
- currsym := notgreater1;
- readchar;
- end else if currentchar = '>' then begin
- currsym := notequal1;
- readchar;
- end else
- currsym:= less1;
- end else if currentchar = '=' then begin
- currsym:= equal1;
- readchar;
- end else if currentchar = '>' then begin
- gch;
- if currentchar = '=' then begin
- currsym:= notless1;
- readchar;
- end else
- currsym:= greater1;
- end else if currentchar = ':' then begin
- gch;
- if currentchar = '=' then begin
- currsym:= becomes1;
- readchar;
- end else
- currsym:= colon1;
- end else if currentchar = ',' then begin
- currsym:= comma1;
- readchar;
- end else if currentchar = '.' then begin
- gch;
- if currentchar = '.' then begin
- currsym:= dotdot1;
- readchar;
- end else
- currsym:= period1;
- end else if currentchar = ';' then begin
- currsym:= semicolon1;
- readchar;
- end else if currentchar = chr(39) then begin
- currsym:= apostrophe1;
- readchar;
- end else if currentchar = '"' then begin
- currsym:= quote1;
- readchar;
- end else if currentchar = '^' then begin
- currsym:= carat1;
- readchar;
- end else if currentchar = '$' then
- readhex;
- else if currentchar = chr(0) then
- currsym:= endtext1;
- else begin
- error("Unknown symbol.");
- readchar;
- end
- end;
-