home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d183 / pcq.lha / PCQ / Source / IO.p < prev    next >
Text File  |  1989-02-26  |  17KB  |  799 lines

  1. external;
  2.  
  3. {
  4.     IO.p (of PCQ Pascal)
  5.     Copyright (c) 1989 Patrick Quaid
  6.  
  7.     This module handles the IO of the compiler.  The actual
  8. compilation of the io statements is handled in stanprocs.p
  9. }
  10.  
  11. const
  12. {$I "pasconst.i"}
  13.  
  14. type
  15. {$I "pastype.i"}
  16.  
  17. var
  18. {$I "pasvar.i"}
  19.  
  20.     procedure doinclude;
  21.         forward;
  22.     function AllocString(i : integer): string;
  23.         forward;
  24.     procedure FreeString(s : string);
  25.         forward;
  26.     function searchreserved(): integer;
  27.         forward;
  28.     function raise(c : Char): Char;
  29.         forward;
  30.  
  31. procedure readchar;
  32.     forward;
  33. procedure endinclude;
  34.     forward;
  35.  
  36.  
  37. {    This routine lists the contents of the identifier table for
  38. debugging purposes.
  39.  
  40. procedure dumptypes;
  41. var
  42.     index : integer;
  43. begin
  44.     for index := 1 to identptr - 1 do begin
  45.     write(index, chr(9));
  46.     if idents[index].name = string(adr(spelling)) then
  47.         writeln('no name')
  48.     else
  49.         writeln(idents[index].name);
  50.     writeln('object  ', idents[index].object);
  51.     writeln('offset  ', idents[index].offset);
  52.     writeln('vtype   ', idents[index].vtype);
  53.     writeln('upper   ', idents[index].upper);
  54.     writeln('lower   ', idents[index].lower);
  55.     writeln('size    ', idents[index].size);
  56.     writeln('indtype ', idents[index].indtype);
  57.     writeln;
  58.     end;
  59. end;
  60. }
  61.  
  62. procedure abort;
  63.  
  64. {
  65.     This routine cuts out cleanly.  If you are debugging the
  66. compiler, this is a likely place to put post mortem dumps, like the
  67. one commented out.
  68. }
  69.  
  70. begin
  71.     if including then begin
  72.     close(input2);
  73.     close(input);
  74.     end else
  75.     close(input);
  76.     writeln('Compilation aborted');
  77.  {   writeln('IdentPtr = ', identptr, '.  SpellPtr = ', spellptr,
  78.         '.  LitPtr = ', litptr);
  79.     dumptypes; }
  80.     exit(20);
  81. end;
  82.  
  83. function eqfix(x : integer): integer;
  84.  
  85. {
  86.     This helps implement a queue.  In this case it's for the
  87. error queue.
  88. }
  89.  
  90. begin
  91.     if x = -1 then
  92.     eqfix := eqsize
  93.     else
  94.     eqfix := x mod (eqsize + 1);
  95. end;
  96.  
  97. procedure error(ptr : string);
  98.  
  99. {
  100.     This just writes out at most the previous 128 characters or
  101. two lines, then writes the error message passed to it.  If there
  102. are more than five errors, it aborts.
  103. }
  104.  
  105. var
  106.     index : integer;
  107.     newlines : integer;
  108. begin
  109.     index := eqend;
  110.     newlines := 0;
  111.     while (index <> eqstart) and (newlines < 2) do begin
  112.     index := eqfix(index - 1);
  113.     if errorq[eqfix(index - 1)] = chr(10) then
  114.         newlines := newlines + 1;
  115.     end;
  116.  
  117.     while index <> eqend do begin
  118.     if index = errorptr then
  119.         write(chr($9b), '0;33;40m');  { start highlight for ANSI }
  120.     write(errorq[index]);
  121.     index := eqfix(index + 1);
  122.     end;
  123.     write(chr($9b), '0;31;40m');  { end highlight }
  124.     writeln;
  125.  
  126.     if including then
  127.     write('"', includename, '", ')
  128.     else
  129.     write('"', mainname, '", ');
  130.  
  131.     write('Line ', lineno, ' ');
  132.     if currfn <> 0 then
  133.     write('(', idents[currfn].name, ')');
  134.     writeln(': ', ptr);
  135.     writeln;
  136.  
  137. {    writeln('Identptr = ', identptr, '.  SpellPtr = ', spellptr); }
  138.  
  139.     errorcount := errorcount + 1;
  140.     if errorcount > 5 then
  141.     abort;
  142. end;
  143.  
  144. function endoffile(): boolean;
  145.  
  146. {
  147.     This is the modified eof() function.  This is necessary
  148. because of include files.
  149. }
  150.  
  151. begin
  152.     if including then
  153.     if eof(input2) then begin
  154.         endinclude;
  155.         endoffile := eof(input);
  156.     end else
  157.         endoffile := false;
  158.     else
  159.     endoffile := eof(input);
  160. end;
  161.  
  162. procedure endcomment;
  163.  
  164. {
  165.     This just eats characters up to the end of a comment.  If
  166. you want nested comments, this is probably the place to do it.
  167. }
  168.  
  169. begin
  170.     while currentchar <> '}' do begin
  171.     if endoffile() then begin
  172.         error("The file ended in a comment!");
  173.         return;
  174.     end;
  175.     readchar;
  176.     end;
  177.     readchar;
  178. end;
  179.  
  180. procedure endinclude;
  181.  
  182. {
  183.     This switches the input back to the main file.
  184. }
  185.  
  186. begin
  187.     close(input2);
  188.     including := false;
  189.     lineno  := saveline;
  190.     fnstart := savestart;
  191.     currentchar := savechar;
  192.     endcomment;
  193. end;
  194.  
  195. procedure readchar;
  196.  
  197. {
  198.     This just reads a character from wherever it's appropriate.
  199. In the next version, the options might include an ARexx port.
  200. }
  201.  
  202. begin
  203.     if including then begin
  204.     if eof(input2) then begin
  205.         endinclude;
  206.     end else
  207.         read(input2, currentchar)
  208.     end else
  209.     read(input, currentchar);
  210.  
  211.       { At this point the character is read.  The following code just
  212.     inserts the character into a queue, which will be printed if
  213.     we hit an error. }
  214.  
  215.     if currentchar = chr(10) then
  216.     lineno := lineno + 1;
  217.     eqend := eqfix(eqend + 1);
  218.     errorq[eqend] := currentchar;
  219.     if eqstart = eqend then
  220.     eqstart := eqfix(eqend + 1);
  221. end;
  222.  
  223. procedure gch;
  224.  
  225. {
  226.     This reads a character from the same line, for situations
  227. where a symbol cannot be spread over two lines.
  228. }
  229.  
  230. begin
  231.     if currentchar <> chr(10) then
  232.     readchar;
  233. end;
  234.  
  235. function getlabel() : integer;
  236.  
  237. {
  238.     As in all compilers, this just returns a unique serial
  239. number.
  240. }
  241.  
  242. begin
  243.     nxtlab := nxtlab + 1;
  244.     getlabel := nxtlab;
  245. end;
  246.  
  247. procedure printlabel(lab : integer);
  248.  
  249. {
  250.     This routine prints a label based on a number from the
  251. above procedure.  The prefix for the label can be anything the
  252. assembler accepts - in this case I wanted it similar to the prefix
  253. of the run time library routines.  I didn't realize how ugly it
  254. would look.
  255. }
  256.  
  257. begin
  258.     write(output, '_p%', lab);
  259. end;
  260.  
  261. function nch(): char;
  262.  
  263. {
  264.     This stands for next character, and just returns the
  265. buffered character from the appropriate file.  It looks ahead.
  266. }
  267.  
  268. begin
  269.     if including then
  270.     nch := input2^
  271.      else
  272.     nch := input^;
  273. end;
  274.  
  275. procedure doinclude;
  276.  
  277. {
  278.     The name says it all.  The mechanics of the include
  279. directive are all handled here.  If you want to nest includes,
  280. you'll have to implement a list or something here, then adjust
  281. endoffile(), readchar(), nextchar(), etc.  Not too hard, I suppose.
  282. }
  283.  
  284. var
  285.     c        : string;
  286. begin
  287.     if including then
  288.     error("Cannot nest include files")
  289.     else begin
  290.     while (currentchar = ' ') or (currentchar = chr(9)) or
  291.         (currentchar = chr(10)) do
  292.         readchar;
  293.     if currentchar = '"' then
  294.         gch
  295.     else
  296.         error("missing open quote");
  297.     c := includename;
  298.     while (currentchar <> '"') and (currentchar <> chr(10)) do begin
  299.         c^ := currentchar;
  300.         readchar;
  301.         c := string(integer(c) + 1); { sorry. }
  302.     end;
  303.  
  304.     if currentchar = '"' then
  305.         readchar
  306.     else
  307.         error("missing close quote");
  308.  
  309.     c^ := chr(0);
  310.  
  311.     if reopen(includename, input2) then begin
  312.         saveline  := lineno;
  313.         savestart := fnstart;
  314.         savechar  := currentchar;
  315.         including := true;
  316.         readchar;
  317.     end else
  318.         error("Could not open include file");
  319.     end
  320. end;
  321.  
  322. procedure docomment;
  323.  
  324. {
  325.     This routine implements compiler directives.  When I get a
  326. few more directives I'll probably split these up a bit.  I'd also
  327. like to make the directives themselves full words.
  328. }
  329.  
  330. begin
  331.     readchar;
  332.     if currentchar = '$' then begin
  333.     readchar;
  334.     if currentchar = 'I' then begin
  335.         readchar;
  336.         doinclude;
  337.         return;
  338.     end else if currentchar = 'A' then begin
  339.         readchar;
  340.         while currentchar <> '}' do begin
  341.         write(output, currentchar);
  342.         if endoffile() then begin
  343.             error("File ended in a comment");
  344.             return;
  345.         end;
  346.         readchar;
  347.         end;
  348.         readchar;
  349.         writeln(output);
  350.         return;
  351.     end else if currentchar = 'R' then begin
  352.         readchar;
  353.         if currentchar = '+' then
  354.         rangecheck := true
  355.         else if currentchar = '-' then
  356.         rangecheck := false;
  357.     end;
  358.     end;
  359.     endcomment;
  360. end;
  361.  
  362. function alpha(c : char): boolean;
  363.  
  364. {
  365.     This function answers the eternal question "is this
  366. character an alphabetic character?"  Note that _ is.
  367. }
  368.  
  369. begin
  370.     if (ord(c) >= ord('a')) and (ord(c) <= ord('z')) then
  371.     alpha := true
  372.     else if (ord(c) >= ord('A')) and (ord(c) <= ord('Z')) then
  373.     alpha := true
  374.     else if c = '_' then
  375.     alpha := true
  376.     else
  377.     alpha := false;
  378. end;
  379.  
  380. function numeric(c : char): boolean;
  381.  
  382. {
  383.     Is the character a digit?
  384. }
  385.  
  386. begin
  387.     numeric := (ord(c) >= ord('0')) and (ord(c) <= ord('9'));
  388. end;
  389.  
  390. function an(c : char): boolean;
  391.  
  392. {
  393.     Is the character a letter or digit?
  394. }
  395.  
  396. begin
  397.     an := alpha(c) or numeric(c);
  398. end;
  399.  
  400. procedure header;
  401.  
  402. {
  403.     This routine references all the run time library routines.
  404. One thing I like about A68k is that the only routines that will
  405. actually be referenced are those that are used in the code.  Maybe
  406. all assemblers do this, but I don't know.
  407. }
  408.  
  409. begin
  410.     writeln(output, "* Pascal compiler intermediate assembly program.\n\n");
  411.     writeln(output, "\tSECTION\tONE\n");
  412.     writeln(output, "\tXREF\t_stdout");
  413.     writeln(output, "\tXREF\t_p%writeint");
  414.     writeln(output, "\tXREF\t_p%writechar");
  415.     writeln(output, "\tXREF\t_p%writebool");
  416.     writeln(output, "\tXREF\t_p%writecharray");
  417.     writeln(output, "\tXREF\t_p%writestring");
  418.     writeln(output, "\tXREF\t_p%writeln");
  419.     writeln(output, "\tXREF\t_p%readint");
  420.     writeln(output, "\tXREF\t_p%readcharray");
  421.     writeln(output, "\tXREF\t_p%readchar");
  422.     writeln(output, "\tXREF\t_p%readarbbuf");
  423.     writeln(output, "\tXREF\t_p%readstring");
  424.     writeln(output, "\tXREF\t_p%readln");
  425.     writeln(output, "\tXREF\t_p%readarb");
  426.     writeln(output, "\tXREF\t_p%dispose");
  427.     writeln(output, "\tXREF\t_p%new");
  428.     writeln(output, "\tXREF\t_p%open");
  429.     writeln(output, "\tXREF\t_p%writearb");
  430.     writeln(output, "\tXREF\t_p%close");
  431.     writeln(output, "\tXREF\t_p%case");
  432.     writeln(output, "\tXREF\t_p%exit\n");
  433.     if mainmode then begin
  434.     writeln(output, "\tXREF\t_p%initialize");
  435.     writeln(output, "\tXREF\t_p%wrapitup");
  436.     writeln(output, "\tjsr\t_p%initialize");
  437.     writeln(output, "\tjsr\t_MAIN");
  438.     writeln(output, "\tjsr\t_p%wrapitup");
  439.     writeln(output, "\trts");
  440.     end
  441. end;
  442.  
  443. procedure trailer;
  444.  
  445. {
  446.     This routine is the most important in the compiler
  447. }
  448.  
  449. begin
  450.     writeln(output, "\tEND");
  451. end;
  452.  
  453. procedure blanks;
  454.  
  455. {
  456.     blanks() skips spaces, tabs and eoln's.  It handles
  457. comments if it comes across one.
  458. }
  459.  
  460. var
  461.     done : boolean;
  462. begin
  463.     if currentchar = '{' then
  464.     docomment;
  465.     done := false;
  466.     while not done do begin
  467.     if endoffile() then
  468.         done := true
  469.     else if (currentchar = ' ') or (currentchar = chr(9)) or
  470.         (currentchar = chr(10)) then
  471.         readchar
  472.     else if currentchar = '{' then
  473.         docomment;
  474.     else
  475.         done := true;
  476.     end;
  477. end;
  478.  
  479. procedure dumplits;
  480.  
  481. {
  482.     This procedure dumps the literal table at the end of the
  483. compilation.  Individual components are referenced as offsets to
  484. the literal label.
  485. }
  486.  
  487. var
  488.     j, k    : integer;
  489.     quotemode    : boolean;
  490. begin
  491.     if litptr = 0 then
  492.     return;
  493.     writeln(output, "\n\tSECTION\tTWO,DATA\n");
  494.     printlabel(litlab);
  495.     k := 1;
  496.     while k < litptr do begin
  497.     write(output, "\tdc.b\t");
  498.     j := 0;
  499.     quotemode := false;
  500.     while j < 40 do begin
  501.         if (ord(litq[k]) > 31) and (ord(litq[k]) <> 39) then begin
  502.         if quotemode then
  503.             write(output, litq[k])
  504.         else begin
  505.             if j > 0 then
  506.             write(output, ',');
  507.             write(output, chr(39), litq[k]);
  508.             quotemode := true;
  509.         end;
  510.         end else begin
  511.         if quotemode then begin
  512.             write(output, chr(39));
  513.             quotemode := false;
  514.         end;
  515.         if j > 0 then
  516.             write(output, ',');
  517.         write(output, ord(litq[k]));
  518.         if j > 32 then
  519.             j := 40
  520.         else
  521.             j := j + 3;
  522.         end;
  523.         j := j + 1;
  524.         k := k + 1;
  525.         if k >= litptr then
  526.         j := 40;
  527.     end;
  528.     if quotemode then
  529.         write(output, chr(39));
  530.     writeln(output);
  531.     end
  532. end;
  533.  
  534. procedure dumpids;
  535.  
  536. {
  537.     This routine does whatever is appropriate with the various
  538. identifers.  If it's a global, it either references it or allocates
  539. space.  Similar stuff for the other ids.  When the modularity of
  540. PCQ is better defined, this routine will have to do more work.
  541. }
  542.  
  543. var
  544.     vartype    : integer;
  545.     index    : integer;
  546.     isodd    : boolean;
  547. begin
  548.     if mainmode then
  549.     writeln(output, "\n\tSECTION\tTHREE,BSS\n");
  550.     index:= 1;
  551.     isodd := false;
  552.     while index < identptr do begin
  553.     if idents[index].object = global then begin
  554.         if mainmode then begin
  555.         vartype := idents[index].vtype;
  556.         if isodd and (idents[vartype].size > 1) then begin
  557.             writeln(output, "\tCNOP\t0,2");
  558.             isodd := false;
  559.         end;
  560.         writeln(output, "\tXDEF\t_", idents[index].name);
  561.         write(output, '_', idents[index].name);
  562.         writeln(output, "\tds.b\t", idents[vartype].size);
  563.         if odd(idents[vartype].size) then
  564.             isodd := not isodd;
  565.         end else
  566.         writeln(output, "\tXREF\t_", idents[index].name);
  567.     end else if (idents[index].object = proc) or
  568.             (idents[index].object = func) then
  569.         if idents[index].upper = 0 then
  570.         writeln(output, "\tXREF\t_", idents[index].name);
  571.     index := index + 1;
  572.     end
  573. end;
  574.  
  575. procedure readword;
  576.  
  577. {
  578.     This reads a Pascal identifier into symtext.
  579. }
  580.  
  581. var
  582.     index    : integer;
  583.     ptr        : string;
  584. begin
  585.     index := 0;
  586.     ptr := symtext;
  587.     while an(currentchar) do begin
  588.     ptr^ := currentchar;
  589.     gch;
  590.     ptr := string(integer(ptr) + 1); { here's that thing again...}
  591.     end;
  592.     ptr^ := chr(0);
  593.     currsym := searchreserved();
  594.     if currsym = 0 then
  595.     currsym := ident1;
  596.     symloc := 0;
  597. end;
  598.  
  599. procedure readnumber;
  600.  
  601. {
  602.     This routine reads a literal integer.  Since it uses *, it
  603. will not properly handle numbers whose magnitude is greater than
  604. about 200,000 or 300,000.  Note that _ can be used.
  605. }
  606.  
  607. var
  608.     negative : boolean;
  609. begin
  610.     if currentchar = '-' then begin
  611.     negative := true;
  612.     gch();
  613.     end else
  614.     negative := false;
  615.     symloc:= 0;
  616.     while numeric(currentchar) do begin
  617.     symloc := symloc * 10 + ord(currentchar) - ord('0');
  618.     gch();
  619.     if currentchar = '_' then
  620.         gch();
  621.     end;
  622.     if negative then
  623.     symloc := -symloc;
  624.     currsym := numeral1;
  625. end;
  626.  
  627. procedure readhex;
  628.  
  629. {
  630.     readhex() reads a hexadecimal number.  Since it uses the
  631. assembly instructions it is able to read full 32 bit values.
  632. }
  633.  
  634. var
  635.    rc : integer;
  636. begin
  637.     gch;
  638.     symloc := 0;
  639.     rc := ord(raise(currentchar));
  640.     while numeric(currentchar) or
  641.       ((rc >= ord('A')) and (rc <= ord('F'))) do begin
  642.  
  643. {$A    move.l    _symloc,d0
  644.     asl.l    #4,d0
  645.     move.l    d0,_symloc    ; symloc := symloc * 16;
  646. }
  647.     if numeric(currentchar) then
  648.         symloc := symloc + ord(currentchar) - ord('0')
  649.     else
  650.         symloc := symloc + rc - ord('A') + 10;
  651.     gch;
  652.     rc := ord(raise(currentchar));
  653.     end;
  654.     currsym := numeral1;
  655. end;
  656.  
  657. procedure writehex(num : integer);
  658.  
  659. {
  660.     This writes full 32 bit hexadecimal numbers.
  661. }
  662.  
  663. var
  664.     numary  : array [1..8] of char;
  665.     pos     : integer;
  666.     ch      : char;
  667. begin
  668.     pos := 8;
  669.     while (num <> 0) and (pos > 0) do begin
  670. {$A    move.l    8(a5),d0
  671.     and.b    #15,d0
  672.     move.b    d0,-13(a5)    ; ch := num AND $0f;
  673. }
  674.     if ord(ch) < 10 then
  675.         numary[pos] := chr(ord(ch) + ord('0'))
  676.     else
  677.         numary[pos] := chr(ord(ch) + ord('A') - 10);
  678.     pos := pos - 1;
  679.  
  680. {$A    move.l    8(a5),d0
  681.     lsr.l    #4,d0
  682.     move.l    d0,8(a5)    ; num := num div 16;
  683. }
  684.     end;
  685.     if pos = 8 then begin
  686.     pos := 7;
  687.     numary[8] := '0';
  688.     end;
  689.     write(output, '$');
  690.     for num := pos + 1 to 8 do
  691.     write(output, numary[num]);
  692. end;
  693.  
  694. procedure nextsymbol;
  695.  
  696. {
  697.     This is the workhorse lexical analysis routine.  It sets
  698. currsym to the appropriate symbol number, sets symtext equal to
  699. whatever identifier is read, and symloc to the value of a literal
  700. integer.
  701.     Soon this will be a big case statement.
  702. }
  703.  
  704. begin
  705.     errorptr := eqend;
  706.     blanks;
  707.     if endoffile() then begin
  708.     currentchar := chr(0);
  709.     currsym := endtext1; { I don't think this routine is ever hit }
  710.     return;
  711.     end;
  712.     while currentchar = '{' do begin
  713.     docomment;    { I think this is unused }
  714.     blanks;
  715.     end;
  716.     if alpha(currentchar) then
  717.     readword
  718.     else if numeric(currentchar) then
  719.     readnumber
  720.     else if currentchar = '[' then begin
  721.     currsym:= leftbrack1;
  722.     readchar;
  723.     end else if currentchar = ']' then begin
  724.     currsym:= rightbrack1;
  725.     readchar;
  726.     end else if currentchar = '(' then begin
  727.     currsym:= leftparent1;
  728.     readchar;
  729.     end else if currentchar = ')' then begin
  730.     currsym:= rightparent1;
  731.     readchar;
  732.     end else if currentchar = '+' then begin
  733.     currsym := plus1;
  734.     readchar;
  735.     end else if currentchar = '-' then begin
  736.     currsym := minus1;
  737.     readchar;
  738.     end else if currentchar = '*' then begin
  739.     currsym:= asterisk1;
  740.     readchar;
  741.     end else if currentchar = '<' then begin
  742.     gch;
  743.     if currentchar = '=' then begin
  744.         currsym := notgreater1;
  745.         readchar;
  746.     end else if currentchar = '>' then begin
  747.         currsym := notequal1;
  748.         readchar;
  749.     end else
  750.         currsym:= less1;
  751.     end else if currentchar = '=' then begin
  752.     currsym:= equal1;
  753.     readchar;
  754.     end else if currentchar = '>' then begin
  755.     gch;
  756.     if currentchar = '=' then begin
  757.         currsym:= notless1;
  758.         readchar;
  759.     end else
  760.         currsym:= greater1;
  761.     end else if currentchar = ':' then begin
  762.     gch;
  763.     if currentchar = '=' then begin
  764.         currsym:= becomes1;
  765.         readchar;
  766.     end else
  767.         currsym:= colon1;
  768.     end else if currentchar = ',' then begin
  769.     currsym:= comma1;
  770.     readchar;
  771.     end else if currentchar = '.' then begin
  772.     gch;
  773.     if currentchar = '.' then begin
  774.         currsym:= dotdot1;
  775.         readchar;
  776.     end else
  777.         currsym:= period1;
  778.     end else if currentchar = ';' then begin
  779.     currsym:= semicolon1;
  780.     readchar;
  781.     end else if currentchar = chr(39) then begin
  782.     currsym:= apostrophe1;
  783.     readchar;
  784.     end else if currentchar = '"' then begin
  785.     currsym:= quote1;
  786.     readchar;
  787.     end else if currentchar = '^' then begin
  788.     currsym:= carat1;
  789.     readchar;
  790.     end else if currentchar = '$' then
  791.     readhex;
  792.     else if currentchar = chr(0) then
  793.     currsym:= endtext1;
  794.     else begin
  795.     error("Unknown symbol.");
  796.     readchar;
  797.     end
  798. end;
  799.