home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / turbopas / pp50.arc / PP.PAS < prev   
Pascal/Delphi Source File  |  1989-11-28  |  25KB  |  777 lines

  1. { PP.PAS }
  2. {
  3.  Pascal pretty printer
  4.  
  5.  Author:  Peter Grogono
  6.  
  7.  This program is based on a Pascal pretty-printer written by Ledgard,
  8.   Hueras, and Singer.  See SIGPLAN Notices, Vol. 12, No. 7, July 1977,
  9.   pages 101-105, and PP.DOC/HLP.
  10.  
  11.  This version of PP developed under Pascal/Z V4.0 or later.
  12.  
  13.  Very minor modifications for Turbo Pascal made by Willett Kempton
  14.   March 1984 and Oct 84.  Runs under 8-bit Turbo or 16-bit Turbo.
  15.  
  16.   Toad Hall tweak, rewrite for TP 5, 28 Nov 89
  17. }
  18.  
  19. PROGRAM pp;
  20.  
  21. Uses Dos;
  22.  
  23.   CONST
  24.     version = '28 November 1989';  {was '11 October 1984'; ..ancient stuff!}
  25.  
  26. { I PPCONST.PAS }
  27.  
  28.   NUL = 0;      { ASCII null character }
  29.   TAB = 9;      { ASCII tab character }
  30.   FF = 12;      { ASCII formfeed character }
  31.   CR = 13;      { ASCII carriage return }
  32.   ESC = 27;     { ASCII escape character }
  33.   Blank = ' ';
  34.   MAXBYTE = 255;{ Largest value of 1 byte variable }
  35.  
  36.   MAXSYMBOLSIZE = 80;
  37.   MAXSTACKSIZE = 100;
  38.   MAXKEYLENGTH = 9;     { The longest keyword is PROCEDURE }
  39.   MAXLINESIZE = 90;     { Maximum length of output line }
  40.   INDENT = 2;           { Indentation step size for structured statements }
  41.   UPCASEKEYWORDS = TRUE;  { If all keywords are to be capitalized }
  42.  
  43. { I PPTYPES.PAS }
  44. { PPTYPES.PAS }
  45.  
  46. TYPE
  47.  
  48.   String0 = STRING[1]; {Pascal/z had 0}
  49.   FileName = STRING[20];
  50.   keysymbol =  { keywords }
  51.               (endsym,beginsym,ifsym,thensym,elsesym,procsym,varsym,ofsym,
  52.                whilesym,dosym,casesym,withsym,forsym,repeatsym,untilsym,
  53.                funcsym,labelsym,constsym,typesym,recordsym,stringsym,progsym,
  54.                andsym,arrsym,divsym,downsym,filesym,gotosym,insym,modsym,
  55.                notsym,nilsym,orsym,setsym,tosym,
  56.                casevarsym,
  57. { other symbols }
  58.                becomes,opencomment,closecomment,semicolon,colon,equals,
  59.                openparen,closeparen,period,endoffile,othersym);
  60.  
  61.   options = (crsupp,crbefore,blinbefore,
  62.              dindonkey,dindent,spbef,
  63.              spaft,gobsym,inbytab,crafter);
  64.  
  65.   optionset = SET OF options;
  66.   keysymset = SET OF keysymbol;
  67.  
  68.   tableentry = RECORD
  69.                  selected : optionset;
  70.                  dindsym : keysymset;
  71.                  terminators : keysymset
  72.                END;
  73.  
  74.   tableptr = ^tableentry;
  75.   optiontable = ARRAY [keysymbol] OF tableptr;
  76.   Key = ARRAY [1..MAXKEYLENGTH] OF CHAR;
  77.   KeywordTable = ARRAY [endsym..tosym] OF Key;
  78.   SpecialChar = ARRAY [1..2] OF CHAR;
  79.   dblcharset = SET OF endsym..othersym;
  80.   DblCharTable = ARRAY [becomes..opencomment] OF SpecialChar;
  81.   SglCharTable = ARRAY [opencomment..period] OF CHAR;
  82.   Token = ARRAY [1..MAXSYMBOLSIZE] OF CHAR;
  83.  
  84.   symbol = RECORD
  85.              name : keysymbol;
  86.              Value : Token;
  87.              IsKeyWord : BOOLEAN;
  88.              length, spacesbefore, crsbefore : INTEGER;
  89.            END;
  90.  
  91.   symbolinfo = ^ symbol;
  92.   charname = (letter,digit,space,quote,endofline,
  93.               filemark,otherchar);
  94.  
  95.   charinfo = RECORD
  96.                name : charname;
  97.                Value : CHAR
  98.              END;
  99.  
  100.   stackentry = RECORD
  101.                  indentsymbol : keysymbol;
  102.                  prevmargin : INTEGER
  103.                END;
  104.  
  105.   symbolstack = ARRAY [1..MAXSTACKSIZE] OF stackentry;
  106.  
  107.   hashentry = RECORD
  108.                 Keyword : Key;
  109.                 symtype : keysymbol
  110.               END;
  111.  
  112. VAR
  113.   InFileName,OutFileName : FileName;
  114.   InFile,OutFile : TEXT;
  115.   RecordSeen : BOOLEAN;
  116.   currchar,nextchar : charinfo;
  117.   currsym,nextsym : symbolinfo;
  118.   CRPending : BOOLEAN;
  119.   option : optiontable;
  120.   sets : tableptr;
  121. { v1.1 made these typed constants }
  122. (*  Keyword : KeywordTable;
  123.   DblChar : DblCharTable;
  124.   SglChar : SglCharTable;
  125. *)
  126.   dblch   : dblcharset;
  127.   stack   : symbolstack;
  128.   top,startpos,currlinepos,currmargin,
  129.   inlines,outlines : INTEGER;
  130.   hashtable : ARRAY [Byte] OF hashentry;
  131.  
  132. CONST
  133. (* Keywords used for formatting
  134. endsym,beginsym,ifsym,thensym,elsesym,procsym,varsym,ofsym,
  135. whilesym,dosym,casesym,withsym,forsym,repeatsym,untilsym,
  136. funcsym,labelsym,constsym,typesym,recordsym,stringsym,progsym,
  137. andsym,arrsym,divsym,downsym,filesym,gotosym,insym,modsym,
  138. notsym,nilsym,orsym,setsym,tosym)
  139. *)
  140.  
  141.   Keyword : KeywordTable =
  142.      ('END      ', 'BEGIN    ', 'IF       ', 'THEN     ',
  143.       'ELSE     ', 'PROCEDURE', 'VAR      ', 'OF       ',
  144.       'WHILE    ', 'DO       ', 'CASE     ', 'WITH     ',
  145.       'FOR      ', 'REPEAT   ', 'UNTIL    ', 'FUNCTION ',
  146.       'LABEL    ', 'CONST    ', 'TYPE     ', 'RECORD   ',
  147.       'STRING   ', 'PROGRAM  ',
  148.       {keywords not used for formatting }
  149.       'AND      ', 'ARRAY    ', 'DIV      ', 'DOWNTO   ',
  150.       'FILE     ', 'GOTO     ', 'IN       ', 'MOD      ',
  151.       'NOT      ', 'NIL      ', 'OR       ', 'SET      ',
  152.       'TO       '
  153.      );
  154.  
  155. {DblCharTable = ARRAY [becomes..opencomment] OF SpecialChar;}
  156.  
  157.   DblChar : DblCharTable =
  158.      ( ':=', '(*' );
  159.  
  160. (*
  161.   SglCharTable = ARRAY [opencomment..period] OF CHAR;
  162. opencomment,closecomment,semicolon,colon,equals,
  163. openparen,closeparen,period
  164. *)
  165.   SglChar : SglCharTable =
  166.     ('{', '}', ';', ':', '=', '(', ')', '.' );
  167.  
  168.  
  169. { I PPINC1.PAS }
  170. { PPINC1.PAS }
  171.  
  172.  
  173. PROCEDURE GetChar;
  174. { Read the next character and classify it }
  175.   VAR  Ch: CHAR;
  176.   BEGIN
  177.     currchar := nextchar;
  178.     WITH nextchar DO
  179.       IF EOF(InFile) THEN BEGIN
  180.         name := filemark;
  181.         Value := Blank
  182.       END
  183.       ELSE IF EOLN(InFile) THEN BEGIN
  184.         name := endofline;
  185.         Value := Blank;
  186.         Inc(inlines);
  187.         READLN(InFile)
  188.       END
  189.       ELSE BEGIN
  190.         READ(InFile, Ch);
  191.         Value := Ch;
  192.         IF Ch IN ['a'..'z', 'A'..'Z', '_'] THEN name := letter
  193.         ELSE IF Ch IN ['0'..'9'] THEN name := digit
  194.         ELSE IF Ch = '''' THEN name := quote
  195.         ELSE IF (Ch = Blank) OR (Ch = CHR(TAB)) THEN name := space
  196.         ELSE name := otherchar
  197.       END
  198.   END; { of GetChar }
  199.  
  200.  
  201. PROCEDURE StoreNextChar(VAR lngth: INTEGER;
  202.                         VAR Value: Token);
  203.   { Store a character in the current symbol }
  204.   BEGIN
  205.     GetChar;
  206.     IF lngth < maxsymbolsize THEN BEGIN
  207.       Inc(lngth);
  208.       Value[lngth] := currchar.Value
  209.     END;
  210.   END; { of StoreNextChar }
  211.  
  212.  
  213. PROCEDURE SkipBlanks(VAR spacesbefore, crsbefore: INTEGER);
  214.   { Count the spaces between symbols }
  215.   BEGIN
  216.     spacesbefore := 0;
  217.     crsbefore := 0;
  218.     WHILE nextchar.name IN [space, endofline] DO BEGIN
  219.       GetChar;
  220.       CASE currchar.name OF
  221.         space:      Inc(spacesbefore);
  222.         endofline:  BEGIN
  223.                       Inc(crsbefore);
  224.                       spacesbefore := 0;
  225.                     END;
  226.       END;  {case}
  227.     END;
  228.   END; { of SkipBlanks }
  229.  
  230.  
  231. PROCEDURE GetComment(sym: symbolinfo);
  232.   { Process comments using either brace or parenthesis notation }
  233.   BEGIN
  234.     sym^.name := opencomment;
  235.     WHILE NOT (((currchar.Value = '*') AND (nextchar.Value = ')'))
  236.     OR (currchar.Value = '}') OR (nextchar.name = endofline)
  237.     OR (nextchar.name = filemark)) DO
  238.       StoreNextChar(sym^.length, sym^.Value);
  239.     IF (currchar.Value = '*') AND (nextchar.Value = ')') THEN BEGIN
  240.       StoreNextChar(sym^.LENGTH, sym^.Value);
  241.       sym^.name := closecomment;
  242.     END;
  243.     IF currchar.Value = '}' THEN sym^.name := closecomment;
  244.   END; { of GetCommment }
  245.  
  246.  
  247. FUNCTION Reset_Ok(VAR F: TEXT;
  248.                  Name: FileName): BOOLEAN;
  249. { Associate name with file variable.  Return true if file is nonempty. }
  250.   BEGIN
  251.     Assign(F, Name);
  252.     {$I-}  RESET(F);
  253.     Reset_Ok := (IOResult = 0);
  254.     {$I+}
  255.   END; { of Reset_Ok }
  256.  
  257. {end of PPINC1}
  258.  
  259.  
  260.   FUNCTION hash(Symbol: Key; lngth: Byte): Byte;
  261.     { Hashing function for identifiers.  The formula gives a unique value
  262.       in the range 0..255 for each Pascal/Z keyword.  Note that range and
  263.       overflow checking must be turned off for this function even if they
  264.       are enabled for the rest of the program.  }
  265.     BEGIN
  266.       hash := (ORD(Symbol[1]) * 5 + ORD(Symbol[lngth])) * 5 + lngth
  267.     END; { of hash }
  268.  
  269.  
  270.   PROCEDURE ClassID(Value: Token;
  271.                     lngth: INTEGER;
  272.                     VAR idtype: keysymbol;
  273.                     VAR IsKeyWord: BOOLEAN);
  274.     { Classify an identifier.  We are only interested
  275.       in it if it is a keyword, so we use the hash table. }
  276.     VAR
  277.       Keyvalue: Key;
  278.       i, tabent: INTEGER;
  279.     BEGIN
  280.       IF lngth > MAXKEYLENGTH THEN BEGIN
  281.         idtype := othersym;
  282.         IsKeyWord := FALSE
  283.       END
  284.       ELSE BEGIN
  285. (*
  286.         FOR i := 1 TO lngth DO Keyvalue[i] := UpCase(Value[i]);
  287.         FOR i := SUCC(lngth) TO MAXKEYLENGTH DO Keyvalue[i] := Blank;
  288. *)
  289.         FillChar(Keyvalue[1],MAXKEYLENGTH, Blank);  {v1.1 fill with spaces}
  290.         FOR i := 1 TO lngth DO Keyvalue[i] := UpCase(Value[i]);
  291.  
  292.         tabent := hash(Keyvalue, lngth);
  293.         IF Keyvalue = hashtable[tabent].Keyword THEN BEGIN
  294.           idtype := hashtable[tabent].symtype;
  295.           IsKeyWord := TRUE;
  296.         END
  297.         ELSE BEGIN
  298.           idtype := othersym;
  299.           IsKeyWord := FALSE;
  300.         END
  301.       END
  302.     END; { of ClassID }
  303.  
  304.  
  305.   PROCEDURE GetIdentifier(sym: symbolinfo);
  306.     { Read an identifier and classify it }
  307.     BEGIN
  308.       WHILE nextchar.name IN [letter, digit] DO
  309.         StoreNextChar(sym^.length, sym^.Value);
  310.       ClassID(sym^.Value, sym^.length, sym^.name, sym^.IsKeyWord);
  311.       IF sym^.name IN [recordsym, casesym, endsym] THEN
  312.         CASE sym^.name OF
  313.           recordsym : RecordSeen := TRUE;
  314.           casesym   : IF RecordSeen THEN sym^.name := casevarsym;
  315.           endsym    : RecordSeen := FALSE;
  316.         END;  {case}
  317.     END; { of GetIdentifier }
  318.  
  319.  
  320.   { Read a number and store it as a string }
  321.   PROCEDURE GetNumber(sym: symbolinfo);
  322.     BEGIN
  323.       WHILE nextchar.name = digit DO StoreNextChar(sym^.length, sym^.Value);
  324.       sym^.name := othersym;
  325.     END; { of GetNumber }
  326.  
  327.  
  328.   PROCEDURE GetCharLiteral(sym: symbolinfo);
  329.     { Read a quoted string }
  330.     BEGIN
  331.       WHILE nextchar.name = quote DO BEGIN
  332.         StoreNextChar(sym^.length, sym^.Value);
  333.         WHILE NOT (nextchar.name IN [quote, endofline, filemark]) DO
  334.           StoreNextChar(sym^.length, sym^.Value);
  335.         IF nextchar.name = quote THEN StoreNextChar(sym^.length, sym^.Value);
  336.       END;
  337.       sym^.name := othersym;
  338.     END; { of GetCharLiteral }
  339.  
  340.  
  341.   FUNCTION char_Type: keysymbol;
  342.     { Classify a character pair }
  343.     VAR
  344.       NextTwoChars: SpecialChar;
  345.       Hit: BOOLEAN;
  346.       thischar: keysymbol;
  347.     BEGIN
  348.       NextTwoChars[1] := currchar.Value;
  349.       NextTwoChars[2] := nextchar.Value;
  350.       thischar := becomes;
  351.       Hit := FALSE;
  352.       WHILE NOT (Hit OR (thischar = closecomment)) DO BEGIN
  353.         IF NextTwoChars = DblChar[thischar] THEN Hit := TRUE
  354.         ELSE Inc(thischar);
  355.       END;
  356.       IF NOT Hit THEN BEGIN
  357.         thischar := opencomment;
  358.         WHILE NOT (Hit OR (PRED(thischar) = period)) DO BEGIN
  359.           IF currchar.Value = SglChar[thischar] THEN Hit := TRUE
  360.           ELSE Inc(thischar);
  361.         END;
  362.       END;
  363.       IF Hit THEN char_Type := thischar
  364.       ELSE char_Type := othersym;
  365.     END; { of char_Type }
  366.  
  367.  
  368.    PROCEDURE GetSpecialChar(sym: symbolinfo);
  369.      { Read special characters }
  370.     BEGIN
  371.       StoreNextChar(sym^.length, sym^.Value);
  372.       sym^.name := char_Type;
  373.       IF sym^.name IN dblch THEN StoreNextChar(sym^.length, sym^.Value)
  374.     END; { of GetSpecialChar }
  375.  
  376.  
  377.   PROCEDURE GetNextSymbol(sym: symbolinfo);
  378.     { Read a symbol using the appropriate procedure }
  379.     BEGIN
  380.       CASE nextchar.name OF
  381.         letter:     GetIdentifier(sym);
  382.         digit:      GetNumber(sym);
  383.         quote:      GetCharLiteral(sym);
  384.         otherchar:  BEGIN
  385.                       GetSpecialChar(sym);
  386.                       IF sym^.name = opencomment THEN GetComment(sym);
  387.                     END;
  388.         filemark:   sym^.name := endoffile;
  389.         ELSE {:} {Turbo}
  390.           WRITELN('Unknown character type: ', ORD(nextchar.name));
  391.       END;  {case}
  392.     END; { of GetNextSymbol }
  393.  
  394.  
  395.   PROCEDURE GetSymbol;
  396.   { Store the next symbol in NEXTSYM }
  397.     VAR
  398.       dummy: symbolinfo;
  399.     BEGIN
  400.       dummy := currsym;
  401.       currsym := nextsym;
  402.       nextsym := dummy;
  403.       SkipBlanks(nextsym^.spacesbefore, nextsym^.crsbefore);
  404.       nextsym^.length := 0;
  405.       nextsym^.IsKeyWord := FALSE;
  406.       IF currsym^.name = opencomment THEN GetComment(nextsym)
  407.       ELSE GetNextSymbol(nextsym);
  408.     END;  {of GetSymbol}
  409.  
  410.  
  411.   PROCEDURE PopStack(VAR indentsymbol: keysymbol;
  412.                      VAR prevmargin: INTEGER);
  413.     { Manage stack of indentation symbols and margins }
  414.     BEGIN
  415.       IF top > 0 THEN BEGIN
  416.         indentsymbol := stack[top].indentsymbol;
  417.         prevmargin := stack[top].prevmargin;
  418.         Dec(top);
  419.       END
  420.       ELSE BEGIN
  421.         indentsymbol := othersym;
  422.         prevmargin := 0;
  423.       END;
  424.     END; { of PopStack }
  425.  
  426.  
  427.   PROCEDURE PushStack(indentsymbol: keysymbol;
  428.                       prevmargin: INTEGER );
  429.     BEGIN
  430.       Inc(top);
  431.       stack[top].indentsymbol := indentsymbol;
  432.       stack[top].prevmargin := prevmargin;
  433.     END; { of PushStack }
  434.  
  435.  
  436.   PROCEDURE WriteCRs(numberofcrs: INTEGER);
  437.     VAR
  438.       i: INTEGER;
  439.     BEGIN
  440.       IF numberofcrs > 0 THEN BEGIN
  441.         FOR i := 1 TO numberofcrs DO WRITELN(OutFile);
  442.         Inc(outlines,numberofcrs);
  443.         currlinepos := 0;
  444.       END;
  445.     END; { of WriteCRs }
  446.  
  447.  
  448.   PROCEDURE InsertCR;
  449.     BEGIN
  450.       IF currsym^.crsbefore = 0 THEN BEGIN
  451.         WriteCRs(1);
  452.         currsym^.spacesbefore := 0;
  453.       END;
  454.     END; { of InsertCR }
  455.  
  456.  
  457.   PROCEDURE InsertBlankLine;
  458.     BEGIN
  459.       IF currsym^.crsbefore = 0 THEN BEGIN
  460.         IF currlinepos = 0 THEN WriteCRs(1)
  461.         ELSE WriteCRs(2);
  462.         currsym^.spacesbefore := 0;
  463.       END
  464.       ELSE IF currsym^.crsbefore = 1 THEN
  465.         IF currlinepos > 0 THEN WriteCRs(1);
  466.     END; { of InsertBlankLine }
  467.  
  468.  
  469.   PROCEDURE LShiftOn(dindsym: keysymset);
  470.     { Move margin left according to stack configuration and current symbol }
  471.     VAR
  472.       indentsymbol: keysymbol;
  473.       prevmargin: INTEGER;
  474.     BEGIN
  475.       IF top > 0 THEN BEGIN
  476.         REPEAT
  477.           PopStack(indentsymbol, prevmargin);
  478.           IF indentsymbol IN dindsym THEN currmargin := prevmargin;
  479.         UNTIL NOT (indentsymbol IN dindsym) OR (top = 0);
  480.         IF NOT (indentsymbol IN dindsym) THEN
  481.           PushStack(indentsymbol, prevmargin);
  482.       END;
  483.     END; { of LShiftOn }
  484.  
  485.  
  486.   PROCEDURE LShift;
  487.   { Move margin left according to stack top }
  488.     VAR
  489.       indentsymbol: keysymbol;
  490.       prevmargin: INTEGER;
  491.     BEGIN
  492.       IF top > 0 THEN BEGIN
  493.         PopStack(indentsymbol, prevmargin);
  494.         currmargin := prevmargin;
  495. (* maybe PopStack(indentsymbol,currmargin); *)
  496.       END;
  497.     END; { of LShift }
  498.  
  499.  
  500.   PROCEDURE InsertSpace(VAR symbol: symbolinfo);
  501.     { Insert space if room on line }
  502.     BEGIN
  503.       IF currlinepos < MAXLINESIZE THEN BEGIN
  504.         WRITE(OutFile, Blank);
  505.         Inc(currlinepos);
  506.         IF (symbol^.crsbefore = 0) AND (symbol^.spacesbefore > 0)
  507.         THEN Dec(symbol^.spacesbefore);
  508.       END;
  509.     END; { of InsertSpace }
  510.  
  511.  
  512.   PROCEDURE MoveLinePos(newlinepos: INTEGER);
  513.     { Insert spaces until correct line position reached }
  514.     VAR  i: INTEGER;
  515.     BEGIN
  516.       FOR i := SUCC(currlinepos) TO newlinepos DO WRITE(OutFile, Blank);
  517.       currlinepos := newlinepos;
  518.     END; { of MoveLinePos }
  519.  
  520.  
  521.   PROCEDURE PrintSymbol;
  522.     { Print a symbol converting keywords to upper case }
  523.     VAR  i: INTEGER;
  524.     BEGIN
  525.       IF (currsym^.IsKeyWord AND UPCASEKEYWORDS) THEN
  526.         FOR i := 1 TO currsym^.length DO
  527.           WRITE(OutFile, Upcase(currsym^.Value[i]))
  528.       ELSE
  529.         FOR i := 1 TO currsym^.length DO WRITE(OutFile, currsym^.Value[i]);
  530.       startpos := currlinepos;
  531.       Inc(currlinepos,currsym^.length);
  532.     END; { of PrintSymbol }
  533.  
  534.  
  535.   PROCEDURE PPSymbol;
  536.   { Find position for symbol and then print it }
  537.     VAR  newlinepos: INTEGER;
  538.     BEGIN
  539.       WriteCRs(currsym^.crsbefore);
  540.       IF (currlinepos + currsym^.spacesbefore > currmargin)
  541.       OR (currsym^.name IN [opencomment, closecomment])
  542.       THEN newlinepos := currlinepos + currsym^.spacesbefore
  543.       ELSE newlinepos := currmargin;
  544.  
  545.       IF newlinepos + currsym^.length > MAXLINESIZE THEN BEGIN
  546.         WriteCRs(1);
  547.         IF currmargin + currsym^.length <= MAXLINESIZE
  548.         THEN newlinepos := currmargin
  549.         ELSE IF currsym^.length < MAXLINESIZE
  550.         THEN newlinepos := MAXLINESIZE - currsym^.length
  551.         ELSE newlinepos := 0;
  552.       END;
  553.       MoveLinePos(newlinepos);
  554.       PrintSymbol;
  555.     END; { of PPSymbol }
  556.  
  557.  
  558.   PROCEDURE Gobble(terminators: keysymset);
  559.     { Print symbols which follow a formatting symbol but which do not
  560.       affect layout }
  561.     BEGIN
  562.       IF top < MAXSTACKSIZE THEN PushStack(currsym^.name, currmargin);
  563.       currmargin := currlinepos;
  564.       WHILE NOT ((nextsym^.name IN terminators)
  565.       OR (nextsym^.name = endoffile)) DO BEGIN
  566.         GetSymbol;
  567.         PPSymbol;
  568.       END;
  569.       LShift;
  570.     END; { of Gobble }
  571.  
  572.  
  573.   PROCEDURE RShift(currsym: keysymbol);
  574.     { Move right, stacking margin positions }
  575.     BEGIN
  576.       IF top < MAXSTACKSIZE THEN PushStack(currsym, currmargin);
  577.       IF startpos > currmargin THEN currmargin := startpos;
  578.       Inc(currmargin,INDENT);
  579.     END; { of RShift }
  580.  
  581.  
  582.   PROCEDURE GoodBye;
  583.     BEGIN
  584.       CLOSE(InFile);
  585.       CLOSE(OutFile);
  586.     END;  {of GoodBye}
  587.  
  588.  
  589.   PROCEDURE Initialize;
  590.     { Initialize everything }
  591.     VAR
  592.       sym: keysymbol;
  593.       psn, len: Byte;
  594.       numfiles: INTEGER;                { from Command Line }
  595.       ArgString1, ArgString2: FileName; { File name }
  596.     BEGIN
  597. (*
  598.       LowVideo; { reverse Turbo's insistence on all-bold console }
  599. *)
  600.       { Get file name and open files }
  601.  
  602.       numfiles := ParamCount;
  603.       IF numfiles <> 2 THEN BEGIN
  604.         WRITELN('Usage:  PP OldProgram NewProgram');
  605. (*
  606.         IF numfiles > 0 THEN
  607.           FOR len := 1 TO numfiles DO WRITE(OUTPUT, '[', ParamStr(len), ']');
  608.         WRITELN;
  609. *)
  610.         HALT;
  611.       END;
  612.       ArgString1 := ParamStr(1);
  613.       ArgString2 := ParamStr(2);
  614.       WRITELN('Reading from ', ArgString1);
  615.       IF NOT Reset_Ok(InFile, ArgString1) THEN BEGIN
  616.         WRITELN('empty file');
  617.         HALT;
  618.       END;
  619.  
  620.       WRITELN('Writing to   ', ArgString2);
  621.       Assign(OutFile, ArgString2);
  622.       REWRITE(OutFile);
  623.  
  624.       { Initialize variables and set up control tables }
  625.  
  626.       top := 0;
  627.       currlinepos := 0;
  628.       currmargin := 0;
  629.       inlines := 0;
  630.       outlines := 0;
  631.  
  632.       { Create hash table }
  633.  
  634.       FOR psn := 0 TO MAXBYTE DO BEGIN
  635.         hashtable[psn].Keyword := '         ';
  636.         hashtable[psn].symtype := othersym
  637.       END;
  638.  
  639.       FOR sym := endsym TO tosym DO BEGIN
  640.         len := MAXKEYLENGTH;
  641.         WHILE Keyword[sym, len] = Blank DO Dec(len);
  642.         psn := hash(Keyword[sym], len);
  643.         hashtable[psn].Keyword := Keyword[sym];
  644.         hashtable[psn].symtype := sym
  645.       END; { for }
  646.  
  647.       { Set up other special symbols }
  648.  
  649.       dblch := [becomes, opencomment];
  650. { now typed constants }
  651. (*
  652.       DblChar[becomes] := ':=';
  653.       DblChar[opencomment] := '(*';
  654.  
  655.       SglChar[semicolon] := ';';
  656.       SglChar[colon] := ':';
  657.       SglChar[equals] := '=';
  658.       SglChar[openparen] := '(';
  659.       SglChar[closeparen] := ')';
  660.       SglChar[period] := '.';
  661.       SglChar[opencomment] := '{';
  662.       SglChar[closecomment] := '}';
  663. *)
  664.  
  665. { Set up the sets that control formatting.  If you want PP to insert a
  666.   line break before every statement, include CRBEFORE in the SELECTED
  667.   set of the appropriate keywords (WHILE, IF, REPEAT, etc.).  The
  668.   disadvantage of this is that PP will sometimes put line breaks
  669.   where you don't want them, e.g. after ':' in CASE statements.  Note
  670.   also that PP does not understand the Pascal/Z use of ELSE as a
  671.   CASE label -- I wish they'd used OTHERWISE like everybody else.  }
  672.  
  673.       FOR sym := endsym TO othersym DO BEGIN
  674.         NEW(option[sym]);
  675.         option[sym]^.selected := [];
  676.         option[sym]^.dindsym := [];
  677.         option[sym]^.terminators := []
  678.       END;
  679.  
  680.       option[progsym]^.selected := [blinbefore, spaft];
  681.       option[funcsym]^.selected := [blinbefore, dindonkey, spaft];
  682.       option[funcsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  683.       option[procsym]^.selected := [blinbefore, dindonkey, spaft];
  684.       option[procsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  685.       option[labelsym]^.selected := [blinbefore, spaft, inbytab];
  686.       option[constsym]^.selected := [blinbefore, dindonkey, spaft, inbytab];
  687.       option[constsym]^.dindsym := [labelsym];
  688.       option[typesym]^.selected := [blinbefore, dindonkey, spaft, inbytab];
  689.       option[typesym]^.dindsym := [labelsym, constsym];
  690.       option[varsym]^.selected := [blinbefore, dindonkey, spaft, inbytab];
  691.       option[varsym]^.dindsym := [labelsym, constsym, typesym];
  692.       option[beginsym]^.selected := [dindonkey, inbytab, crafter];
  693.       option[beginsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  694.       option[repeatsym]^.selected := [inbytab, crafter];
  695.       option[recordsym]^.selected := [inbytab, crafter];
  696.       option[casesym]^.selected := [spaft, inbytab, gobsym, crafter];
  697.       option[casesym]^.terminators := [ofsym];
  698.       option[casevarsym]^.selected := [spaft, inbytab, gobsym, crafter];
  699.       option[casevarsym]^.terminators := [ofsym];
  700.       option[ofsym]^.selected := [crsupp, spbef];
  701.       option[forsym]^.selected := [spaft, inbytab, gobsym, crafter];
  702.       option[forsym]^.terminators := [dosym];
  703.       option[whilesym]^.selected := [spaft, inbytab, gobsym, crafter];
  704.       option[whilesym]^.terminators := [dosym];
  705.       option[withsym]^.selected := [spaft, inbytab, gobsym, crafter];
  706.       option[withsym]^.terminators := [dosym];
  707.       option[dosym]^.selected := [crsupp, spbef];
  708.       option[ifsym]^.selected := [spaft, inbytab, gobsym, crafter];
  709.       option[ifsym]^.terminators := [thensym];
  710.       option[thensym]^.selected := [inbytab];
  711.       option[elsesym]^.selected := [crbefore, dindonkey, dindent, inbytab];
  712.       option[elsesym]^.dindsym := [ifsym, elsesym];
  713.       option[endsym]^.selected := [crbefore, dindonkey, dindent, crafter];
  714.       option[endsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
  715.                                  withsym, casevarsym, colon, equals];
  716.       option[untilsym]^.selected := [crbefore, dindonkey, dindent, spaft,
  717.                                     gobsym, crafter];
  718.       option[untilsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
  719.                                    withsym, colon, equals];
  720.       option[untilsym]^.terminators := [endsym, untilsym, elsesym, semicolon];
  721.       option[becomes]^.selected := [spbef, spaft, gobsym];
  722.       option[becomes]^.terminators := [endsym, untilsym, elsesym, semicolon];
  723.       option[opencomment]^.selected := [crsupp];
  724.       option[closecomment]^.selected := [crsupp];
  725.       option[semicolon]^.selected := [crsupp, dindonkey, crafter];
  726.       option[semicolon]^.dindsym := [ifsym, thensym, elsesym, forsym,
  727.                                     whilesym, withsym, colon, equals];
  728.       option[colon]^.selected := [inbytab];
  729.       option[equals]^.selected := [spbef, spaft, inbytab];
  730.       option[openparen]^.selected := [gobsym];
  731.       option[openparen]^.terminators := [closeparen];
  732.       option[period]^.selected := [crsupp];
  733.  
  734.       { Start i/o }
  735.  
  736.       CrPending := FALSE;
  737.       RecordSeen := FALSE;
  738.       GetChar;
  739.       NEW(currsym);
  740.       NEW(nextsym);
  741.       GetSymbol;
  742.  
  743.     END; { Initialize }
  744.  
  745.   { Main Program }
  746.  
  747.   BEGIN
  748.     Initialize;
  749.     WHILE nextsym^.name <> endoffile DO BEGIN
  750.       GetSymbol;
  751.       sets := option[currsym^.name];
  752.       IF (CrPending AND NOT (crsupp IN sets^.selected))
  753.       OR (crbefore IN sets^.selected) THEN BEGIN
  754.         InsertCR;
  755.         CrPending := FALSE
  756.       END;
  757.       IF blinbefore IN sets^.selected THEN BEGIN
  758.         InsertBlankLine;
  759.         CrPending := FALSE
  760.       END;
  761.       IF dindonkey IN sets^.selected THEN LShiftOn(sets^.dindsym);
  762.       IF dindent IN sets^.selected THEN LShift;
  763.       IF spbef IN sets^.selected THEN InsertSpace(currsym);
  764.       PPSymbol;
  765.       IF spaft IN sets^.selected THEN InsertSpace(nextsym);
  766.       IF inbytab IN sets^.selected THEN RShift(currsym^.name);
  767.       IF gobsym IN sets^.selected THEN Gobble(sets^.terminators);
  768.       IF crafter IN sets^.selected THEN CrPending := TRUE
  769.     END;
  770.     IF CrPending THEN WriteCRs(1);
  771.  
  772.     WRITELN(inlines: 1, ' lines read, ', outlines: 1, ' lines written.');
  773.  
  774.     GoodBye;
  775.  
  776.   END.
  777.