home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TPUTIL1.ZIP / PASFORMA.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-03-28  |  56.3 KB  |  1,274 lines

  1.  
  2.  PROGRAM pascalformatter;
  3. {
  4. | **                Pascal Program Formatter                       **
  5. | **                                                               **
  6. | **   by J. E. Crider, Shell Oil Company, Houston, Texas 77025    **
  7. | **                                                               **
  8. | **   Copyright (c) 1980 by Shell Oil Company.  Permission to     **
  9. | **   copy, modify, and distribute, but not for profit, is        **
  10. | **   hereby granted, provided that this note is included.        **
  11. |
  12. | Changes:
  13. |   The program has been updated to replace keywords according to
  14. |   the TURBO Pascal conventions. They are capitalized the way they
  15. |   are in the book.
  16. |
  17. |   calling conventions:
  18. |        the program expects two arguments (input & output files)
  19. |
  20. |   This portable program formats Pascal programs and acceptable
  21. |   program fragments according to structured formatting principles
  22. |   [SIGPLAN Notices, Vol. 13, No. 11, Nov. 1978, pp. 15-22].
  23. |   The actions of the program are as follows:
  24. |
  25. |   PREPARATION:  For each structured statement that controls a
  26. |      structured statement, the program converts the controlled
  27. |      statement into a compound statement.  The inserted BEGIN/END
  28. |      pair are in capital letters.  A null statement (with semicolon)
  29. |      is inserted before the last END symbol of each program/
  30. |      procedure/function, if needed.  The semicolon forces the END
  31. |      symbol to appear on a line by itself.
  32. |
  33. |   FORMATTING:  Each structured statement that controls a simple
  34. |      statement is placed on a single line, as if it were a simple
  35. |      statement.  Otherwise, each structured statement is formatted
  36. |      in the following pattern (with indentation "indent"):
  37. |
  38. |            XXXXXX header XXXXXXXX
  39. |               XXXXXXXXXXXXXXXXXX
  40. |               XXXXX body XXXXXX
  41. |               XXXXXXXXXXXXXXXXXX
  42. |
  43. |      where the header is one of:
  44. |
  45. |            while <expression> do begin
  46. |            for <control variable> := <for list> do begin
  47. |            with <record variable list> do begin
  48. |            repeat
  49. |            if <expression> then begin
  50. |            else if <expression> then begin
  51. |            else begin
  52. |            case <expression> of
  53. |            <case label list>: begin
  54. |
  55. |      and the last line either begins with UNTIL or ends with END.
  56. |      Other program parts are formatted similarly.  The headers are:
  57. |
  58. |            <program/procedure/function heading>;
  59. |            label
  60. |            const
  61. |            type
  62. |            var
  63. |            begin
  64. |            (various for records and record variants)
  65. |
  66. |   COMMENTS:  Each comment that starts before or on a specified
  67. |      column on an input line (program constant "commthresh") is
  68. |      copied without shifting or reformatting.  Each comment that
  69. |      starts after "commthresh" is reformatted and left-justified
  70. |      following the aligned comment base column ("alcommbase").
  71. |
  72. |   LABELS:  Each statement label is justified to the left margin and
  73. |      is placed on a line by itself.
  74. |
  75. |   SPACES AND BLANK LINES:  Spaces not at line breaks are copied from
  76. |      the input.  Blank lines are copied from the input if they appear
  77. |      between statements (or appropriate declaration units).  A blank
  78. |      line is inserted above each significant part of each program/
  79. |      procedure/function if one is not already there.
  80. |
  81. |   CONTINUATION:  Lines that are too long for an output line are
  82. |      continued with additional indentation ("contindent").
  83. |
  84. |   INPUT FORM:  The program expects as input a program or program
  85. |      fragment in Standard Pascal.  A program fragment is acceptable
  86. |      if it consists of a sequence of (one or more) properly ordered
  87. |      program parts; examples are:  a statement part (that is, a
  88. |      compound statement), or a TYPE part and a VAR part followed by
  89. |      procedure declarations.  If the program fragment is in serious
  90. |      error, then the program may copy the remainder of the input file
  91. |      to the output file without significant modification.  Error
  92. |      messages may be inserted into the output file as comments.
  93. |}
  94.  
  95.     CONST
  96.        maxrwlen = 10;              { size of reserved word strings }
  97.        ordminchar = 32;            { ord of lowest char in char set }
  98.        ordmaxchar = 126;           { ord of highest char in char set }
  99.                                    {  Although this program uses the ASCII
  100.                                       character set, conversion to most other
  101.                                       character sets should be straightforward.
  102.                                       }
  103.  
  104.  {  The following parameters may be adjusted for the installation: }
  105.        maxinlen = 255;             { maximum width of input line + 1 }
  106.        maxoutlen = 80;             { maximum width of output line }
  107.        initmargin = 1;             { initial value of output margin }
  108.        commthresh = 4;             { column threshhold in input for comments to
  109.                                       be aligned }
  110.        alcommbase = 35;            { aligned comments in output start AFTER this
  111.                                       column }
  112.        indent = 3;                 { RECOMMENDED indentation increment }
  113.        contindent = 5;             { continuation indentation, >indent }
  114.        endspaces = 3;              { number of spaces to precede 'END' }
  115.        commindent = 3;             { comment continuation indentation }
  116.        line_number : INTEGER = 0;
  117.  
  118.     TYPE
  119.        natural = 0..MaxInt;
  120.        inrange = 0..maxinlen;
  121.        outrange = 0..maxoutlen;
  122.  
  123.        errortype = (longline, noendcomm, notquote, longword, notdo, notof,
  124.             notend, notthen, notbegin, notuntil, notsemicolon, notcolon,
  125.             notparen, noeof);
  126.  
  127.        chartype = (illegal, special, chapostrophe, chleftparen, chrightparen,
  128.             chperiod, digit, chcolon, chsemicolon, chlessthan, chgreaterthan,
  129.             letter, chleftbrace);
  130.  
  131.                                    { for reserved word recognition }
  132.        resword = (                 { reserved words ordered by length }
  133.             rwif, rwdo, rwof, rwto, rwin, rwor,
  134.                                    { length: 2 }
  135.             rwend, rwfor, rwvar, rwdiv, rwmod, rwset, rwand, rwnot, rwnil,
  136.                                    { length: 3 }
  137.             rwthen, rwelse, rwwith, rwgoto, rwcase, rwtype, rwfile, rwuses,
  138.             rwunit,                { length: 4 }
  139.             rwbegin, rwuntil, rwwhile, rwarray, rwconst, rwlabel, rwvalue,
  140.                                    { length: 5 }
  141.             rwrepeat, rwrecord, rwdownto, rwpacked,rwmodule,
  142.                                    { length: 6 }
  143.             rwprogram,             { length: 7 }
  144.             rwfunction,            { length: 8 }
  145.             rwotherwise,rwprocedure,
  146.                                    { length: 9 }
  147.             rwx);                  { length: 10 for table sentinel }
  148.        rwstring = PACKED ARRAY [1..maxrwlen] OF CHAR;
  149.  
  150.        firstclass = (              { class of word if on new line }
  151.             newclause,             { start of new clause }
  152.             continue,              { continuation of clause }
  153.             alcomm,                { start of aligned comment }
  154.             contalcomm,            { continuation of aligned comment }
  155.             uncomm,                { start of unaligned comment }
  156.             contuncomm,            { continuation of unaligned comment }
  157.             stmtlabel);            { statement label }
  158.        wordtype = RECORD           { data record for word }
  159.           whenfirst: firstclass;   { class of word if on new line }
  160.           puncfollows: BOOLEAN;    { to reduce dangling punctuation }
  161.           blanklncount: natural;   { number of preceding blank lines }
  162.           spaces: INTEGER;         { number of spaces preceding word }
  163.           base: -9..maxinlen;      { inlinexx.buf[base] precedes word }
  164.           size: inrange   END;     { length of word in inlinexx.buf }
  165.  
  166.        symboltype = (              { symbols for syntax analysis }
  167.             semicolon, sybegin, syend,
  168.                                    { three insertable symbols first }
  169.             syif, sydo, syof, sythen, syelse, sygoto, sycase, syuntil, syrepeat,
  170.             syrecord, forwhilewith, progprocfunc, declarator, otherword,
  171.             othersym, leftparen, rightparen, period, syotherwise, sysubrange,
  172.             intconst, colon, ident, comment, syeof);
  173.        inserttype = semicolon..syend;
  174.        symbolset = SET OF symboltype;
  175.                                    { *** NOTE: set size of 0..26 REQUIRED for
  176.                                       symbolset! }
  177.  
  178.     VAR
  179.        Input,Output : TEXT[$800];
  180.        response : STRING[10];
  181.        no_error_output : BOOLEAN;
  182.        infilename,outfilename : STRING[80];
  183.        inlinexx: RECORD            { input line data }
  184.           endoffile: BOOLEAN;      { end of file on input? }
  185.           ch: CHAR;                { current char, buf[index] }
  186.           index: inrange;          { subscript of current char }
  187.           len: natural;            { length of input line in buf }
  188.                                    { string ';BEGINEND' in buf[-8..0] }
  189.           buf: ARRAY [-8..maxinlen] OF CHAR   END;
  190.        outline: RECORD             { output line data }
  191.           blanklns: natural;       { number of preceding blank lines }
  192.           len: outrange;           { number of chars in buf }
  193.           buf: ARRAY [1..maxoutlen] OF CHAR   END;
  194.        WORD: wordtype;             { current word }
  195.        margin: outrange;           { left margin }
  196.        lnpending: BOOLEAN;         { new line before next symbol? }
  197.        symbol: symboltype;         { current symbol }
  198.  
  199.   { Structured Constants }
  200.        headersyms: symbolset;      { headers for program parts }
  201.        strucsyms: symbolset;       { symbols that begin structured statements }
  202.        stmtbeginsyms: symbolset;   { symbols that begin statements }
  203.        stmtendsyms: symbolset;     { symbols that follow statements }
  204.        stopsyms: symbolset;        { symbols that stop expression scan }
  205.        recendsyms: symbolset;      { symbols that stop record scan }
  206.        datawords: symbolset;       { to reduce dangling punctuation }
  207.        newword: ARRAY [inserttype] OF wordtype;
  208.        instring: PACKED ARRAY [1..9] OF CHAR;
  209.        firstrw: ARRAY [1..maxrwlen] OF resword;
  210.        rwword: ARRAY [rwif..rwprocedure] OF rwstring;
  211.        rwsy: ARRAY [rwif..rwprocedure] OF symboltype;
  212.        charclass: ARRAY [CHAR] OF chartype;
  213.                                    { above is portable form; possible ASCII form
  214.                                       is: }
  215.                                    {    charclass: array [' '..'~'] of chartype;
  216.                                       }
  217.        symbolclass: ARRAY [chartype] OF symboltype;
  218.  
  219.     PROCEDURE strucconsts;         { establish values of structured constants }
  220.  
  221.        VAR
  222.           i: ordminchar..ordmaxchar;
  223.                                    { loop index }
  224.           ch: CHAR;                { loop index }
  225.  
  226.        PROCEDURE buildinsert (symbol: inserttype;
  227.             inclass: firstclass;
  228.             inpuncfollows: BOOLEAN;
  229.             inspaces, inbase: INTEGER;
  230.             insize: inrange);
  231.  
  232.           BEGIN
  233.              WITH newword[symbol] DO BEGIN
  234.                 whenfirst := inclass;
  235.                 puncfollows := inpuncfollows;
  236.                 blanklncount := 0;
  237.                 spaces := inspaces;
  238.                 base := inbase;
  239.                 size := insize   END;
  240.              END;                  { buildinsert }
  241.  
  242.        PROCEDURE buildrw (rw: resword;
  243.             symword: rwstring;
  244.             symbol: symboltype);
  245.  
  246.           BEGIN
  247.              rwword[rw] := symword;{ reserved word string }
  248.              rwsy[rw] := symbol;   { map to symbol }
  249.              END;                  { buildrw }
  250.  
  251.        BEGIN                       { strucconsts }
  252.                                    { symbol sets for syntax analysis }
  253.           headersyms := [progprocfunc, declarator, sybegin, syeof];
  254.           strucsyms := [sycase, syrepeat, syif, forwhilewith];
  255.           stmtbeginsyms := strucsyms + [sybegin, ident, sygoto, syotherwise];
  256.           stmtendsyms := [semicolon, syend, syuntil, syelse, syeof];
  257.           stopsyms := headersyms + strucsyms + stmtendsyms + [sygoto];
  258.           recendsyms := [rightparen, syend, syeof];
  259.  
  260.           datawords := [otherword, intconst, ident, syend];
  261.  
  262.                                    { words for insertable symbols }
  263.           buildinsert (semicolon, continue, FALSE, 0, -9, 1);
  264.           buildinsert (sybegin, continue, FALSE, 1, -8, 5);
  265.           buildinsert (syend, newclause, TRUE, endspaces, -3, 3);
  266.           instring := ';        '; {';BEGINEND'}
  267.  
  268.                                    { constants for recognizing reserved words }
  269.           firstrw[1] := rwif;      { length: 1 }
  270.           firstrw[2] := rwif;      { length: 2 }
  271.           buildrw (rwif, 'IF        ', syif);
  272.           buildrw (rwdo, 'DO        ', sydo);
  273.           buildrw (rwof, 'OF        ', syof);
  274.           buildrw (rwto, 'TO        ', othersym);
  275.           buildrw (rwin, 'IN        ', othersym);
  276.           buildrw (rwor, 'OR        ', othersym);
  277.           firstrw[3] := rwend;     { length: 3 }
  278.           buildrw (rwend, 'END       ', syend);
  279.           buildrw (rwfor, 'FOR       ', forwhilewith);
  280.           buildrw (rwvar, 'VAR       ', declarator);
  281.           buildrw (rwdiv, 'DIV       ', othersym);
  282.           buildrw (rwmod, 'MOD       ', othersym);
  283.           buildrw (rwset, 'SET       ', othersym);
  284.           buildrw (rwand, 'AND       ', othersym);
  285.           buildrw (rwnot, 'NOT       ', othersym);
  286.           buildrw (rwnil, 'NIL       ', otherword);
  287.           firstrw[4] := rwthen;    { length: 4 }
  288.           buildrw (rwthen, 'THEN      ', sythen);
  289.           buildrw (rwelse, 'ELSE      ', syelse);
  290.           buildrw (rwwith, 'WITH      ', forwhilewith);
  291.           buildrw (rwgoto, 'GOTO      ', sygoto);
  292.           buildrw (rwcase, 'CASE      ', sycase);
  293.           buildrw (rwtype, 'TYPE      ', declarator);
  294.           buildrw (rwfile, 'FILE      ', othersym);
  295.           buildrw (rwuses, 'USES      ', declarator);
  296.           buildrw (rwunit, 'UNIT      ', declarator);
  297.           firstrw[5] := rwbegin;   { length: 5 }
  298.           buildrw (rwbegin, 'BEGIN     ', sybegin);
  299.           buildrw (rwuntil, 'UNTIL     ', syuntil);
  300.           buildrw (rwwhile, 'WHILE     ', forwhilewith);
  301.           buildrw (rwarray, 'ARRAY     ', othersym);
  302.           buildrw (rwconst, 'CONST     ', declarator);
  303.           buildrw (rwlabel, 'LABEL     ', declarator);
  304.           buildrw (rwvalue, 'VALUE     ', declarator);
  305.           firstrw[6] := rwrepeat;  { length: 6 }
  306.           buildrw (rwrepeat, 'REPEAT    ', syrepeat);
  307.           buildrw (rwrecord, 'RECORD    ', syrecord);
  308.           buildrw (rwdownto, 'DOWNTO    ', othersym);
  309.           buildrw (rwpacked, 'PACKED    ', othersym);
  310.           buildrw (rwmodule, 'MODULE    ',progprocfunc);
  311.           firstrw[7] := rwprogram; { length: 7 }
  312.           buildrw (rwprogram, 'PROGRAM   ', progprocfunc);
  313.           firstrw[8] := rwfunction;{ length: 8 }
  314.           buildrw (rwfunction, 'FUNCTION  ', progprocfunc);
  315.           firstrw[9] := rwotherwise;
  316.                                    { length: 9 }
  317.           buildrw (rwotherwise, 'OTHERWISE ', syotherwise);
  318.           buildrw (rwprocedure, 'PROCEDURE ', progprocfunc);
  319.           firstrw[10] := rwx;      { length: 10 for table sentinel }
  320.  
  321.                                    { constants for lexical scan }
  322.           FOR i := ordminchar TO ordmaxchar DO BEGIN
  323.              charclass[Chr (i)] := illegal   END;
  324.           FOR ch := 'a' TO 'z' DO BEGIN
  325.                                    { !!! implementation-dependent!  (but can be
  326.                                       replaced with 52 explicit assignments) }
  327.              charclass[ch] := letter;
  328.              charclass[UpCase(ch)] := letter   END;
  329.           charclass['_'] := letter;
  330.           charclass['#'] := letter;
  331.           FOR ch := '0' TO '9' DO charclass[ch] := digit;
  332.           charclass[' '] := special;
  333.           charclass['$'] := special;
  334.           charclass[''''] := chapostrophe;
  335.           charclass['('] := chleftparen;
  336.           charclass[')'] := chrightparen;
  337.           charclass['*'] := special;
  338.           charclass['+'] := special;
  339.           charclass['-'] := special;
  340.           charclass['.'] := chperiod;
  341.           charclass['/'] := special;
  342.           charclass[':'] := chcolon;
  343.           charclass[';'] := chsemicolon;
  344.           charclass['<'] := chlessthan;
  345.           charclass['='] := special;
  346.           charclass['>'] := chgreaterthan;
  347.           charclass['@'] := special;
  348.           charclass['['] := special;
  349.           charclass[']'] := special;
  350.           charclass['^'] := special;
  351.           charclass['{'] := chleftbrace;
  352.           symbolclass[illegal] := othersym;
  353.           symbolclass[special] := othersym;
  354.           symbolclass[chapostrophe] := otherword;
  355.           symbolclass[chleftparen] := leftparen;
  356.           symbolclass[chrightparen] := rightparen;
  357.           symbolclass[chperiod] := period;
  358.           symbolclass[digit] := intconst;
  359.           symbolclass[chcolon] := colon;
  360.           symbolclass[chsemicolon] := semicolon;
  361.           symbolclass[chlessthan] := othersym;
  362.           symbolclass[chgreaterthan] := othersym;
  363.           symbolclass[letter] := ident;
  364.           symbolclass[chleftbrace] := comment;
  365.  
  366.           END;                     { strucconsts }
  367.  
  368. { writeline/writeerror/readline convert between files and lines. }
  369.  
  370.     PROCEDURE writeline;           { write buffer into output file }
  371.  
  372.        VAR
  373.           i: outrange;             { loop index }
  374.  
  375.        BEGIN
  376.           WITH outline DO BEGIN
  377.              WHILE blanklns > 0 DO BEGIN
  378.                 Writeln (Output);
  379.                 blanklns := blanklns - 1   END;
  380.              IF len > 0 THEN BEGIN
  381.                 FOR i := 1 TO len DO Write (Output, buf[i]);
  382.                 Writeln (Output);
  383.                 len := 0   END   END;
  384.           END;                     { writeline }
  385.  
  386.     PROCEDURE writeerror (error: errortype);
  387.                                    { report error to output }
  388.  
  389.        VAR
  390.           i, ix: inrange;          { loop index, limit }
  391.  
  392.        BEGIN
  393.           IF NOT no_error_output THEN BEGIN
  394.              writeline;
  395.              Write (Output, ' (*  !!! error, ');
  396.              CASE error OF
  397.                 longline:     Write (Output, 'shorter line');
  398.                 noendcomm:    Write (Output, 'end of comment');
  399.                 notquote:     Write (Output, 'final "''" on line');
  400.                 longword:     Write (Output, 'shorter word');
  401.                 notdo:        Write (Output, '"do"');
  402.                 notof:        Write (Output, '"of"');
  403.                 notend:       Write (Output, '"end"');
  404.                 notthen:      Write (Output, '"then"');
  405.                 notbegin:     Write (Output, '"begin"');
  406.                 notuntil:     Write (Output, '"until"');
  407.                 notsemicolon: Write (Output, '";"');
  408.                 notcolon:     Write (Output, '":"');
  409.                 notparen:     Write (Output, '")"');
  410.                 noeof:        Write (Output, 'end of file')   END;
  411.              Write (Output, ' expected');
  412.              IF error >= longword THEN BEGIN
  413.                 Write (Output, ', not "');
  414.                 WITH inlinexx, WORD DO BEGIN
  415.                    IF size > maxrwlen THEN ix := maxrwlen
  416.                    ELSE ix := size;
  417.                    FOR i := 1 TO ix DO Write (Output, buf[base + i])   END;
  418.                 Write (Output, '"')   END;
  419.              IF error = noeof THEN Write (Output, ', FORMATTING STOPS');
  420.              Writeln (Output, ' !!!  *)');
  421.              END
  422.           ELSE BEGIN
  423.              Write (Con,line_number, ' (*  !!! error, ');
  424.              CASE error OF
  425.                 longline:     Write (Con, 'shorter line');
  426.                 noendcomm:    Write (Con, 'end of comment');
  427.                 notquote:     Write (Con, 'final "''" on line');
  428.                 longword:     Write (Con, 'shorter word');
  429.                 notdo:        Write (Con, '"do"');
  430.                 notof:        Write (Con, '"of"');
  431.                 notend:       Write (Con, '"end"');
  432.                 notthen:      Write (Con, '"then"');
  433.                 notbegin:     Write (Con, '"begin"');
  434.                 notuntil:     Write (Con, '"until"');
  435.                 notsemicolon: Write (Con, '";"');
  436.                 notcolon:     Write (Con, '":"');
  437.                 notparen:     Write (Con, '")"');
  438.                 noeof:        Write (Con, 'end of file')   END;
  439.              Write (Con, ' expected');
  440.              IF error >= longword THEN BEGIN
  441.                 Write (Con, ', not "');
  442.                 WITH inlinexx, WORD DO BEGIN
  443.                    IF size > maxrwlen THEN ix := maxrwlen
  444.                    ELSE ix := size;
  445.                    FOR i := 1 TO ix DO Write (Con, buf[base + i])   END;
  446.                 Write (Con, '"')   END;
  447.              IF error = noeof THEN Write (Con, ', FORMATTING STOPS');
  448.              Writeln (Con, ' !!!  *)');
  449.              END;
  450.  
  451.           END;                     { writeerror }
  452.  
  453.     PROCEDURE readline;            { read line into input buffer }
  454.  
  455.        VAR
  456.           c: CHAR;                 { input character }
  457.           nonblank: BOOLEAN;       { is char other than space? }
  458.  
  459.        BEGIN
  460.           WITH inlinexx DO BEGIN
  461.              len := 0;
  462.              IF Eof (Input) THEN endoffile := TRUE
  463.              ELSE BEGIN            { get next line }
  464.                 WHILE NOT Eoln (Input) DO BEGIN
  465.                    Read (Input, c);
  466.                    IF c < ' ' THEN BEGIN
  467.                                    { convert ASCII control chars (except leading
  468.                                       form feed) to spaces }
  469.                       IF c = Chr (9) THEN BEGIN
  470.                                    { ASCII tab char }
  471.                          c := ' '; { add last space at end }
  472.                          WHILE len MOD 8 <> 7 DO BEGIN
  473.                             len := len + 1;
  474.                             IF len < maxinlen THEN buf[len] := c   END;
  475.                          END       { end tab handling }
  476.                       ELSE IF (c <> Chr (12)) OR (len > 0) THEN c := ' ';
  477.                       END;         { end ASCII control char conversion }
  478.                    len := len + 1;
  479.                    IF len < maxinlen THEN buf[len] := c   END;
  480.                 Readln (Input);
  481.                 line_number := line_number+1;
  482.                 IF len >= maxinlen THEN BEGIN
  483.                                    { input line too long }
  484.                    writeerror (longline);
  485.                    len := maxinlen - 1   END;
  486.                 nonblank := FALSE;
  487.                 REPEAT             { trim line }
  488.                    IF len = 0 THEN nonblank := TRUE
  489.                    ELSE IF buf[len] <> ' ' THEN nonblank := TRUE
  490.                    ELSE len := len - 1
  491.                    UNTIL nonblank   END;
  492.              len := len + 1;       { add exactly ONE trailing blank }
  493.              buf[len] := ' ';
  494.              index := 0   END;
  495.           END;                     { readline }
  496.  
  497. { startword/finishword/copyword convert between lines and words.
  498.    auxiliary procedures getchar/nextchar precede. }
  499.  
  500.     PROCEDURE getchar;             { get next char from input buffer }
  501.  
  502.        BEGIN
  503.           WITH inlinexx DO BEGIN
  504.              index := index + 1;
  505.              ch := buf[index]   END;
  506.           END;                     { getchar }
  507.  
  508.     FUNCTION nextchar: CHAR;       { look at next char in input buffer }
  509.  
  510.        BEGIN
  511.           WITH inlinexx DO nextchar := buf[index + 1];
  512.           END;                     { nextchar }
  513.  
  514.     PROCEDURE startword (startclass: firstclass);
  515.                                    { note beginning of word, and count preceding
  516.                                       lines and spaces }
  517.  
  518.        VAR
  519.           first: BOOLEAN;          { is word the first on input line? }
  520.  
  521.        BEGIN
  522.           first := FALSE;
  523.           WITH inlinexx, WORD DO BEGIN
  524.              whenfirst := startclass;
  525.              blanklncount := 0;
  526.              WHILE (index >= len) AND NOT endoffile DO BEGIN
  527.                 IF len = 1 THEN blanklncount := blanklncount + 1;
  528.                 IF startclass = contuncomm THEN writeline
  529.                 ELSE first := TRUE;
  530.                 readline;          { with exactly ONE trailing blank }
  531.                 getchar;           { ASCII:        if ch = chr (12) then begin [
  532.                                       ASCII form feed char ] writeline; writeln
  533.                                       (output, chr (12)); blanklncount := 0;
  534.                                       getchar   end;  [ end ASCII form feed
  535.                                       handling }
  536.                 END;
  537.              spaces := 0;          { count leading spaces }
  538.              IF NOT endoffile THEN BEGIN
  539.                 WHILE ch = ' ' DO BEGIN
  540.                    spaces := spaces + 1;
  541.                    getchar   END   END;
  542.              IF first THEN spaces := 1;
  543.              base := index - 1   END;
  544.           END;                     { startword }
  545.  
  546.     PROCEDURE finishword;          { note end of word }
  547.  
  548.        BEGIN
  549.           WITH inlinexx, WORD DO BEGIN
  550.              puncfollows := (symbol IN datawords) AND (ch <> ' ');
  551.              size := index - base - 1   END;
  552.           END;                     { finishword }
  553.  
  554.     PROCEDURE copyword (newline: BOOLEAN;
  555.          WORD: wordtype);          { copy word from input buffer into output
  556.                                       buffer }
  557.  
  558.        VAR
  559.           i: INTEGER;              { outline.len excess, loop index }
  560.  
  561.        BEGIN
  562.           WITH WORD, outline DO BEGIN
  563.              i := maxoutlen - len - spaces - size;
  564.              IF newline OR (i < 0) OR ((i = 0) AND puncfollows) THEN writeline;
  565.              IF len = 0 THEN BEGIN { first word on output line }
  566.                 blanklns := blanklncount;
  567.                 CASE whenfirst OF  { update LOCAL word.spaces }
  568.                    newclause:  spaces := margin;
  569.                    continue:   spaces := margin + contindent;
  570.                    alcomm:     spaces := alcommbase;
  571.                    contalcomm: spaces := alcommbase + commindent;
  572.                    uncomm:     spaces := base;
  573.                    contuncomm: ;   { spaces := spaces }
  574.                    stmtlabel:  spaces := initmargin   END;
  575.                 IF spaces + size > maxoutlen THEN BEGIN
  576.                    spaces := maxoutlen - size;
  577.                                    { reduce spaces }
  578.                    IF spaces < 0 THEN BEGIN
  579.                       writeerror (longword);
  580.                       size := maxoutlen;
  581.                       spaces := 0   END   END   END;
  582.              FOR i := 1 TO spaces DO BEGIN
  583.                                    { put out spaces }
  584.                 len := len + 1;
  585.                 buf[len] := ' '   END;
  586.              FOR i := 1 TO size DO BEGIN
  587.                                    { copy actual word }
  588.                 len := len + 1;
  589.                 buf[len] := inlinexx.buf[base + i]   END   END;
  590.           END;                     { copyword }
  591.  
  592. { docomment/copysymbol/insert/getsymbol/findsymbol convert between
  593.    words and symbols. }
  594.  
  595.     PROCEDURE docomment;           { copy aligned or unaligned comment }
  596.  
  597.        PROCEDURE copycomment (commclass: firstclass;
  598.             commbase: inrange);    { copy words of comment }
  599.  
  600.           VAR
  601.              endcomment: BOOLEAN;  { end of comment? }
  602.  
  603.           BEGIN
  604.              WITH WORD DO BEGIN    { copy comment begin symbol }
  605.                 whenfirst := commclass;
  606.                 spaces := commbase - outline.len;
  607.                 copyword ((spaces < 0) OR (blanklncount > 0), WORD)   END;
  608.              commclass := Succ (commclass);
  609.              WITH inlinexx DO BEGIN
  610.                 REPEAT             { loop for successive words }
  611.                    startword (commclass);
  612.                    endcomment := endoffile;
  613.                                    { premature end? }
  614.                    IF endcomment THEN writeerror (noendcomm)
  615.                    ELSE BEGIN
  616.                       REPEAT
  617.                          IF ch = '*' THEN BEGIN
  618.                             getchar;
  619.                             IF ch = ')' THEN BEGIN
  620.                                endcomment := TRUE;
  621.                                getchar   END   END
  622.                          ELSE IF ch = '}' THEN BEGIN
  623.                             endcomment := TRUE;
  624.                             getchar   END
  625.                          ELSE getchar
  626.                          UNTIL (ch = ' ') OR endcomment   END;
  627.                    finishword;
  628.                    copyword (FALSE, WORD)
  629.                    UNTIL endcomment   END;
  630.              END;                  { copycomment }
  631.  
  632.        BEGIN                       { docomment }
  633.           IF WORD.base < commthresh THEN BEGIN
  634.                                    { copy comment without alignment }
  635.              copycomment (uncomm, WORD.base)   END
  636.           ELSE BEGIN               { align and format comment }
  637.              copycomment (alcomm, alcommbase)   END;
  638.           END;                     { docomment }
  639.  
  640.     PROCEDURE copysymbol (symbol: symboltype;
  641.          WORD: wordtype);          { copy word(s) of symbol }
  642.  
  643.        BEGIN
  644.           IF symbol = comment THEN BEGIN
  645.              docomment;            { NOTE: docomment uses global word! }
  646.              lnpending := TRUE   END
  647.           ELSE IF symbol = semicolon THEN BEGIN
  648.              copyword (FALSE, WORD);
  649.              lnpending := TRUE   END
  650.           ELSE BEGIN
  651.              copyword (lnpending, WORD);
  652.              lnpending := FALSE   END;
  653.           END;                     { copysymbol }
  654.  
  655.     PROCEDURE Insert (newsymbol: inserttype);
  656.                                    { copy word for inserted symbol into output
  657.                                       buffer }
  658.  
  659.        BEGIN
  660.           copysymbol (newsymbol, newword[newsymbol]);
  661.           END;                     { insert }
  662.  
  663.     PROCEDURE getsymbol;           { get next non-comment symbol }
  664.  
  665.        PROCEDURE findsymbol;       { find next symbol in input buffer }
  666.  
  667.           VAR
  668.              chclass: chartype;    { classification of leading char }
  669.  
  670.           PROCEDURE checkresword;  { check if current identifier is reserved
  671.                                       word/symbol }
  672.  
  673.              CONST
  674.                 keyword_size = 226;
  675.                 keyword_len = 15;
  676.                 keyword : ARRAY[1..keyword_size] OF ARRAY[1..2] OF STRING[
  677.                      keyword_len] = ( ('ABORT','Abort'),('ABSOLUTE','Absolute'),
  678.                      ('ADDR','Addr'), ('ADR',''),('ADRMEM','AdrMem'),('ADS',''),
  679.                      ('ADSMEM','AdsMem'), ('AND',''), ('APPEND','Append'), (
  680.                      'ARCTAN','Arctan'), ('ARRAY',''), ('ASSIGN', 'Assign'), (
  681.                      'AUX','Aux'), ('AUXINPTR','AuxInPtr'), ( 'AUXOUTPTR',
  682.                      'AuxOutPtr'), ('BEGIN',''), ('BLOCKREAD', 'BlockRead'), (
  683.                      'BLOCKWRITE','BlockWrite'), ('BOOLEAN',''), ('BREAK',''),
  684.                      ('BUFLEN','BufLen'), ('BYTE',''), ('BYWORD','ByWord'), (
  685.                      'CASE',''), ( 'CHAIN','Chain'), ('CHAR',''), ('CHDIR',
  686.                      'ChDir'), ('CHR','Chr'), ('CLOSE', 'Close'), ('CLREOL',
  687.                      'ClrEol'), ('CLRSCR','ClrScr'), ('CON', 'Con'), ('CONCAT',
  688.                      'Concat'), ('CONINPTR','ConInPtr'), ( 'CONOUTPTR',
  689.                      'ConOutPtr'), ('CONST',''), ('CONSTPTR', 'ConstPtr'), (
  690.                      'COPY','Copy'), ('COPYLST','CopyLst'),('COPYSTR','CopyStr')
  691.                      , ('COS','Cos'), ('CRTEXIT', 'CrtExit'), ('CRTINIT',
  692.                      'CrtInit'), ('CSEG','CSeg'), ('CYCLE',''),('DECODE',
  693.                      'Decode'), ( 'DELAY','Delay'), ('DELETE','Delete'), (
  694.                      'DELLINE', 'DelLine'), ('DISPOSE','Dispose'), ('DIV',''), (
  695.                      'DO',''), ('DOWNTO',''), ( 'DRAW','Draw'), ('DSEG','DSeg'),
  696.                      ('ELSE',''), ('ENCODE','Encode'), ('END',''), ( 'EOF',
  697.                      'Eof'), ('EOLN','Eoln'), ('ERASE','Erase'), ('EVAL','Eval')
  698.                      , ('EXECUTE', 'Execute'), ('EXP','Exp'), ('EXTERN',''), (
  699.                      'EXTERNAL',''), ('FALSE',''), ( 'FILE',''), ('FILEPOS',
  700.                      'FilePos'), ('FILESIZE','FileSize'), ('FILLC','FillC'), (
  701.                      'FILLCHAR','FillChar'), ('FILLSC','FillSC'), ('FLUSH',
  702.                      'Flush'), ('FOR',''), ( 'FORWARD',''), ('FRAC','Frac'), (
  703.                      'FREEMEM','FreeMem'), ( 'FUNCTION',''), ('GETDIR','GetDir')
  704.                      , ('GETMEM','GetMem'), ('GOTO',''), ( 'GOTOXY','GotoXY'), (
  705.                      'GRAPHBACKGROUND','GraphBackGround'), ('GRAPHCOLORMODE',
  706.                      'GraphColorMode'), ('GRAPHMODE', 'GraphMode'), (
  707.                      'GRAPHWINDOW','GraphWindow'), ('HALT', 'Halt'), ('HEAPSTR',
  708.                      'HeapStr'), ('HI','Hi'), ('HIBYTE','HiByte'),
  709.                      ('HIRES', 'HiRes'), ('HIRESCOLOR',
  710.                      'HiResColor'), ('IF',''), ('IN','') , ('INLINE','InLine'),
  711.                      ('INPUT','Input'), ('INSERT', 'Insert'), ('INSLINE',
  712.                      'InsLine'), ('INT',''), ('INTEGER', ''), ('INTR','Intr'), (
  713.                      'IORESULT','IOResult'), ('KBD', 'Kbd'), ('KEYPRESSED',
  714.                      'KeyPressed'), ('LABEL',''), ( 'LENGTH','Length'), ('LN',
  715.                      'Ln'), ('LO','Lo'), ( 'LONGFILEPOS','LongFilePos'), (
  716.                      'LONGFILESIZE', 'LongFileSize'), ('LONGSEEK','LongSeek'),
  717.                      ('LOBYTE','LoByte'),('LOWER','Lower'),
  718.                      ('LOWVIDEO', 'LowVideo'), ('LST','Lst'),
  719.                      ('LSTOUTPTR','LstOutPtr'), ('LSTRING',''), ( 'MARK','Mark')
  720.                      , ('MAXAVAIL','MaxAvail'), ('MAXINT', 'MaxInt'), ('MEM',
  721.                      'Mem'), ('MEMAVAIL','MemAvail'), ('MEMW', 'MemW'), (
  722.                      'MKDIR','MkDir'), ('MOD',''), ('MODULE',''), ('MOVE',
  723.                      'Move'), ('MOVEL','MoveL'),('MOVER','MoveR'), ('MOVESL',
  724.                      'MoveSL'),('MOVESR','MoveSR'), ('MSDOS','MSDos'), ('NEW',
  725.                      'New'), ('NIL',''), ('NORMVIDEO','NormVideo'), ( 'NOSOUND',
  726.                      'NoSound'), ('NOT',''), ('NULL',''),
  727.                      ('ODD','Odd'), ('OF',''), ('OFS',
  728.                      'Ofs'), ('OR',''), ('ORD','Ord'), ('OTHERWISE',''),
  729.                      ('OUTPUT','Output'), (
  730.                      'OVRPATH','OvrPath'), ('PACKED',''), ('PALETTE','Palette'),
  731.                      ('PARAMCOUNT','ParamCount'), ('PARAMSTR','ParamStr'), (
  732.                      'PI','Pi'), ('PLOT', 'Plot'), ('PORT','Port'), ('PORTW',
  733.                      'PortW'), ('POS','Pos'), ('POSITN','Positn'), ('PRED',''),
  734.                      ('PROCEDURE',''), ('PROGRAM',''), ('PTR', 'Ptr'), (
  735.                      'PUBLIC',''), ('RANDOM','Random'), ('RANDOMIZE',
  736.                      'Randomize'), ( 'READ','Read'), ('READLN','Readln'), (
  737.                      'REAL',''), ( 'RECORD',''), ('RELEASE','Release'), (
  738.                      'RENAME','Rename'), ( 'REPEAT',''), ('RESET','Reset'), (
  739.                      'RETURN',''), ('REWRITE','Rewrite'), ('RMDIR','RmDir'), (
  740.                      'ROUND','Round'), ('SCANEQ','ScanEQ'),('SCANNE','ScanNE'),
  741.                      ('SEEK','Seek'), ('SEG','Seg'), ('SET', ''), ('SHL','ShL'),
  742.                      ('SHR','ShR'), ('SIN','Sin'), ( 'SIZEOF','SizeOf'), (
  743.                      'SOUND','Sound'), ('SQR','Sqr'), ( 'SQRT','Sqrt'), ('SSEG',
  744.                      'SSeg'), ('STATIC',''), ('STR','Str'), ('STRING', ''), (
  745.                      'SUCC','Succ'),('SUPER',''),
  746.                      ('SWAP','Swap'), ('TEXT',''), (
  747.                      'TEXTBACKGROUND','TextBackGround'), ('TEXTCOLOR',
  748.                      'TextColor'), ('TEXTMODE','TextMode'), ('THEN',''), ('TO',
  749.                      ''), ('TRM','Trm'), ('TRUE',''), ('TRUNC','Trunc'), (
  750.                      'TRUNCATE','Truncate'), ( 'TYPE',''), ('UNTIL',''), (
  751.                      'UPCASE','UpCase'), ('UPPER','Upper'),('USES',''), ('USR',
  752.                      'Usr'), ('USRINPTR','UsrInPtr'), ('USROUTPTR','UsrOutPtr'),
  753.                      ('VAL','Val'), ('VALUE',''), ('VAR',''), ('WHEREX',
  754.                      'WhereX'), ('WHEREY', 'WhereY'), ('WHILE',''), ('WINDOW',
  755.                      'Window'), ('WITH',''), ('WORD',''),('WRD','Wrd'), (
  756.                      'WRITE','Write'), ('WRITELN','Writeln'), ('XOR',''));
  757.  
  758.              LABEL
  759.                 bypass;
  760.  
  761.              VAR
  762.                 rw, rwbeyond: resword;
  763.                                    { loop index, limit }
  764.                 symword: rwstring; { copy of symbol word }
  765.                 i: 1..maxrwlen;    { loop index }
  766.                 high_index,low_index,key_index,select,key_size : INTEGER;
  767.                 test_keyword : STRING[keyword_len];
  768.  
  769.              BEGIN
  770.                 WITH WORD, inlinexx DO BEGIN
  771.                    size := index - base - 1;
  772.                    IF size < maxrwlen THEN BEGIN
  773.                       symword := '          ';
  774.                       FOR i := 1 TO size DO symword[i] := UpCase(buf[ base + i]
  775.                            );
  776.                       rw := firstrw[size];
  777.                       rwbeyond := firstrw[size + 1];
  778.                       symbol := semicolon;
  779.                       REPEAT
  780.                          IF rw >= rwbeyond THEN symbol := ident
  781.                          ELSE IF symword = rwword[rw] THEN symbol := rwsy[rw]
  782.                          ELSE rw := Succ (rw)
  783.                          UNTIL symbol <> semicolon;
  784.                       IF symbol = syend THEN BEGIN
  785.                          IF spaces < endspaces THEN spaces := endspaces;
  786.                          whenfirst := newclause   END   END;
  787.                                    {goto bypass;}
  788.                    IF size <= keyword_len THEN BEGIN
  789.                       FOR key_size := 1 TO size DO test_keyword[key_size] :=
  790.                            UpCase(buf[base+key_size]);
  791.                       test_keyword[0] := Chr(size);
  792.                       low_index := 1;
  793.                       high_index := keyword_size;
  794.                       WHILE low_index <= high_index DO BEGIN
  795.                          key_index := (high_index + low_index) DIV 2;
  796.                          IF keyword[key_index,1] = test_keyword THEN BEGIN
  797.                             IF keyword[key_index,2] = '' THEN select := 1
  798.                             ELSE select := 2;
  799.                             FOR key_size := 1 TO size DO buf[base+key_size] :=
  800.                                  keyword[key_index,select][key_size];
  801.                             low_index := high_index+1;
  802.                                    {terminate the loop}
  803.                             END
  804.                          ELSE IF keyword[key_index,1] > test_keyword THEN
  805.                               high_index := key_index - 1
  806.                          ELSE low_index := key_index + 1;
  807.                          END;
  808.                       END;
  809.                    bypass:;
  810.                    END;
  811.                 END;               { checkresword }
  812.  
  813.           PROCEDURE getname;
  814.  
  815.              BEGIN
  816.                 WHILE charclass[inlinexx.ch] IN [letter, digit] DO getchar;
  817.                 checkresword;
  818.                 END;               { getname }
  819.  
  820.           PROCEDURE getnumber;
  821.  
  822.              BEGIN
  823.                 WITH inlinexx DO BEGIN
  824.                    WHILE charclass[ch] = digit DO getchar;
  825.                    IF ch = '.' THEN BEGIN
  826.                                    { thanks to A.H.J.Sale, watch for '..' }
  827.                       IF charclass[nextchar] = digit THEN BEGIN
  828.                                    { NOTE: nextchar is a function! }
  829.                          symbol := otherword;
  830.                          getchar;
  831.                          WHILE charclass[ch] = digit DO getchar   END   END;
  832.                    IF UpCase (ch) = 'E' THEN BEGIN
  833.                       symbol := otherword;
  834.                       getchar;
  835.                       IF (ch = '+') OR (ch = '-') THEN getchar;
  836.                       WHILE charclass[ch] = digit DO getchar   END   END;
  837.                 END;               { getnumber }
  838.  
  839.           PROCEDURE getstringliteral;
  840.  
  841.              VAR
  842.                 endstring: BOOLEAN;{ end of string literal? }
  843.  
  844.              BEGIN
  845.                 WITH inlinexx DO BEGIN
  846.                    endstring := FALSE;
  847.                    REPEAT
  848.                       IF ch = '''' THEN BEGIN
  849.                          getchar;
  850.                          IF ch = '''' THEN getchar
  851.                          ELSE endstring := TRUE   END
  852.                       ELSE IF index >= len THEN BEGIN
  853.                                    { error, final "'" not on line }
  854.                          writeerror (notquote);
  855.                          symbol := syeof;
  856.                          endstring := TRUE   END
  857.                       ELSE getchar
  858.                       UNTIL endstring   END;
  859.                 END;               { getstringliteral }
  860.  
  861.           BEGIN                    { findsymbol }
  862.              startword (continue);
  863.              WITH inlinexx DO BEGIN
  864.                 IF endoffile THEN symbol := syeof
  865.                 ELSE BEGIN
  866.                    chclass := charclass[ch];
  867.                    symbol := symbolclass[chclass];
  868.                    getchar;        { second char }
  869.                    CASE chclass OF
  870.                       chsemicolon, chrightparen, chleftbrace, special, illegal:
  871.                            ;
  872.                       letter:  getname;
  873.                       digit:  getnumber;
  874.                       chapostrophe:  getstringliteral;
  875.                       chcolon:  BEGIN
  876.                          IF ch = '=' THEN BEGIN
  877.                             symbol := othersym;
  878.                             getchar   END   END;
  879.                       chlessthan:  BEGIN
  880.                          IF (ch = '=') OR (ch = '>') THEN getchar   END;
  881.                       chgreaterthan:  BEGIN
  882.                          IF ch = '=' THEN getchar   END;
  883.                       chleftparen:  BEGIN
  884.                          IF ch = '*' THEN BEGIN
  885.                             symbol := comment;
  886.                             getchar   END   END;
  887.                       chperiod:  BEGIN
  888.                          IF ch = '.' THEN BEGIN
  889.                             symbol := sysubrange;
  890.                             getchar   END   END   END   END   END;
  891.              finishword;
  892.              END;                  { findsymbol }
  893.  
  894.        BEGIN                       { getsymbol }
  895.           REPEAT
  896.              copysymbol (symbol, WORD);
  897.                                    { copy word for symbol to output }
  898.              findsymbol            { get next symbol }
  899.              UNTIL symbol <> comment;
  900.           END;                     { getsymbol }
  901.  
  902. { block performs recursive-descent syntax analysis with symbols,
  903.    adjusting margin, lnpending, word.whenfirst, and
  904.    word.blanklncount.  auxiliary procedures precede. }
  905.  
  906.     PROCEDURE startclause;         { (this may be a simple clause, or the start
  907.                                       of a header) }
  908.  
  909.        BEGIN
  910.           WORD.whenfirst := newclause;
  911.           lnpending := TRUE;
  912.           END;                     { startclause }
  913.  
  914.     PROCEDURE passsemicolons;      { pass consecutive semicolons }
  915.  
  916.        BEGIN
  917.           WHILE symbol = semicolon DO BEGIN
  918.              getsymbol;
  919.              startclause   END;    { new line after ';' }
  920.           END;                     { passsemicolons }
  921.  
  922.     PROCEDURE startpart;           { start program part }
  923.  
  924.        BEGIN
  925.           WITH WORD DO BEGIN
  926.              IF blanklncount = 0 THEN blanklncount := 1   END;
  927.           startclause;
  928.           END;                     { startpart }
  929.  
  930.     PROCEDURE startbody;           { finish header, start body of structure }
  931.  
  932.        BEGIN
  933.           passsemicolons;
  934.           margin := margin + indent;
  935.           startclause;
  936.           END;                     { startbody }
  937.  
  938.     PROCEDURE finishbody;
  939.  
  940.        BEGIN
  941.           margin := margin - indent;
  942.           END;                     { finishbody }
  943.  
  944.     PROCEDURE passphrase (finalsymbol: symboltype);
  945.                                    { process symbols until significant symbol
  946.                                       encountered }
  947.  
  948.        VAR
  949.           endsyms: symbolset;      { complete set of stopping symbols }
  950.  
  951.        BEGIN
  952.           IF symbol <> syeof THEN BEGIN
  953.              endsyms := stopsyms + [finalsymbol];
  954.              REPEAT
  955.                 getsymbol
  956.                 UNTIL symbol IN endsyms   END;
  957.           END;                     { passphrase }
  958.  
  959.     PROCEDURE expect (expectedsym: symboltype;
  960.          error: errortype;
  961.          syms: symbolset);
  962.  
  963.        BEGIN
  964.           IF symbol = expectedsym THEN getsymbol
  965.           ELSE BEGIN
  966.              writeerror (error);
  967.              WHILE NOT (symbol IN [expectedsym] + syms) DO getsymbol;
  968.              IF symbol = expectedsym THEN getsymbol   END;
  969.           END;                     { expect }
  970.  
  971.     PROCEDURE dolabel;             { process statement label }
  972.  
  973.        VAR
  974.           nextfirst: firstclass;   { (pass whenfirst to statement) }
  975.  
  976.        BEGIN
  977.           WITH WORD DO BEGIN
  978.              nextfirst := whenfirst;
  979.              whenfirst := stmtlabel;
  980.              lnpending := TRUE;
  981.              getsymbol;
  982.              expect (colon, notcolon, stopsyms);
  983.              whenfirst := nextfirst;
  984.              lnpending := TRUE   END;
  985.           END;                     { dolabel }
  986.  
  987.     PROCEDURE block;               { process block }
  988.  
  989.        PROCEDURE heading;          { process heading for program, procedure, or
  990.                                       function }
  991.  
  992.           PROCEDURE matchparens;   { process parentheses in heading }
  993.  
  994.              BEGIN
  995.                 getsymbol;
  996.                 WHILE NOT (symbol IN recendsyms) DO BEGIN
  997.                    IF symbol = leftparen THEN matchparens
  998.                    ELSE getsymbol   END;
  999.                 expect (rightparen, notparen, stopsyms + recendsyms);
  1000.                 END;               { matchparens }
  1001.  
  1002.           BEGIN                    { heading }
  1003.              getsymbol;
  1004.              passphrase (leftparen);
  1005.              IF symbol = leftparen THEN matchparens;
  1006.              IF symbol = colon THEN passphrase (semicolon);
  1007.              IF symbol = othersym THEN BEGIN
  1008.                                    {'['}
  1009.                 passphrase(semicolon);
  1010.                 IF symbol = othersym THEN passphrase(semicolon);
  1011.                                    {']'}
  1012.                 END;
  1013.              expect (semicolon, notsemicolon, stopsyms);
  1014.              END;                  { heading }
  1015.  
  1016.        PROCEDURE statement;        { process statement }
  1017.  
  1018.           FORWARD;
  1019.  
  1020.        PROCEDURE stmtlist;         { process sequence of statements }
  1021.  
  1022.           BEGIN
  1023.              REPEAT
  1024.                 statement;
  1025.                 passsemicolons
  1026.                 UNTIL symbol IN stmtendsyms;
  1027.              END;                  { stmtlist }
  1028.  
  1029.        PROCEDURE compoundstmt (    { process compound statement }
  1030.             stmtpart: BOOLEAN);    { statement part of block? }
  1031.  
  1032.           BEGIN
  1033.              getsymbol;
  1034.              startbody;            { new line, indent after 'BEGIN' }
  1035.              stmtlist;
  1036.              IF stmtpart AND NOT lnpending THEN Insert (semicolon);
  1037.              expect (syend, notend, stmtendsyms);
  1038.              finishbody;           { left-indent after 'END' }
  1039.              END;                  { compoundstmt }
  1040.  
  1041.        PROCEDURE statement;        { process statement }
  1042.  
  1043.           PROCEDURE checkcompound; { if structured then force compound }
  1044.  
  1045.              BEGIN
  1046.                 IF symbol = intconst THEN dolabel;
  1047.                 IF symbol IN strucsyms THEN BEGIN
  1048.                                    { force compound }
  1049.                                    {insert (sybegin);}
  1050.                    startbody;      { new line, indent after 'BEGIN' }
  1051.                    statement;      {insert (syend);}
  1052.                    finishbody   END{ left-indent after 'END' }
  1053.                 ELSE statement;
  1054.                 END;               { checkcompound }
  1055.  
  1056.           PROCEDURE ifstmt;        { process if statement }
  1057.  
  1058.              BEGIN
  1059.                 passphrase (sythen);
  1060.                 expect (sythen, notthen, stopsyms);
  1061.                 checkcompound;
  1062.                 IF symbol = syelse THEN BEGIN
  1063.                    startclause;    { new line before 'ELSE' }
  1064.                    getsymbol;
  1065.                    IF symbol = syif THEN ifstmt
  1066.                    ELSE checkcompound   END;
  1067.                 END;               { ifstmt }
  1068.  
  1069.           PROCEDURE repeatstmt;    { process repeat statement }
  1070.  
  1071.              BEGIN
  1072.                 getsymbol;
  1073.                 startbody;         { new line, indent after 'REPEAT' }
  1074.                 stmtlist;
  1075.                 startclause;       { new line before 'UNTIL' }
  1076.                 expect (syuntil, notuntil, stmtendsyms);
  1077.                 passphrase (semicolon);
  1078.                 finishbody;        { left-ident after 'UNTIL' }
  1079.                 END;               { repeatstmt }
  1080.  
  1081.           PROCEDURE fwwstmt;       { process for, while, or with statement }
  1082.  
  1083.              BEGIN
  1084.                 passphrase (sydo);
  1085.                 expect (sydo, notdo, stopsyms);
  1086.                 checkcompound;
  1087.                 END;               { fwwstmt }
  1088.  
  1089.           PROCEDURE casestmt;      { process case statement }
  1090.  
  1091.              BEGIN
  1092.                 passphrase (syof);
  1093.                 expect (syof, notof, stopsyms);
  1094.                 startbody;         { new line, indent after 'OF' }
  1095.                 REPEAT
  1096.                    IF symbol = syelse THEN symbol := syotherwise;
  1097.                    IF symbol <> syotherwise THEN BEGIN
  1098.                       passphrase (colon);
  1099.                       expect (colon, notcolon, stopsyms);
  1100.                       END;
  1101.                    checkcompound;
  1102.                    passsemicolons
  1103.                    UNTIL symbol IN (stopsyms - [syelse]);
  1104.                 expect (syend, notend, stmtendsyms);
  1105.                 finishbody;        { left-indent after 'END' }
  1106.                 END;               { casestmt }
  1107.  
  1108.           BEGIN                    { statement }
  1109.              IF symbol = intconst THEN dolabel;
  1110.              IF symbol IN stmtbeginsyms THEN BEGIN
  1111.                 CASE symbol OF
  1112.                    sybegin:       compoundstmt (FALSE);
  1113.                    sycase:        casestmt;
  1114.                    syif:          ifstmt;
  1115.                    syrepeat:      repeatstmt;
  1116.                    forwhilewith:  fwwstmt;
  1117.                    syotherwise:   BEGIN
  1118.                       getsymbol;
  1119.                       startbody;
  1120.                       stmtlist;
  1121.                       finishbody;
  1122.                       END;
  1123.                    ident, sygoto: passphrase (semicolon)   END   END;
  1124.              IF NOT (symbol IN stmtendsyms) THEN BEGIN
  1125.                 writeerror (notsemicolon);
  1126.                                    { ';' expected }
  1127.                 passphrase (semicolon)   END;
  1128.              END;                  { statement }
  1129.  
  1130.        PROCEDURE passfields (forvariant: BOOLEAN);
  1131.  
  1132.           FORWARD;
  1133.  
  1134.        PROCEDURE dorecord;         { process record declaration }
  1135.  
  1136.           BEGIN
  1137.              getsymbol;
  1138.              startbody;
  1139.              passfields (FALSE);
  1140.              expect (syend, notend, recendsyms);
  1141.              finishbody;
  1142.              END;                  { dorecord }
  1143.  
  1144.        PROCEDURE dovariant;        { process (case) variant part }
  1145.  
  1146.           BEGIN
  1147.              passphrase (syof);
  1148.              expect (syof, notof, stopsyms);
  1149.              startbody;
  1150.              passfields (TRUE);
  1151.              finishbody;
  1152.              END;                  { dovariant }
  1153.  
  1154.        PROCEDURE doparens (forvariant: BOOLEAN);
  1155.                                    { process parentheses in record }
  1156.  
  1157.           BEGIN
  1158.              getsymbol;
  1159.              IF forvariant THEN startbody;
  1160.              passfields (FALSE);
  1161.              lnpending := FALSE;   { for empty field list }
  1162.              expect (rightparen, notparen, recendsyms);
  1163.              IF forvariant THEN finishbody;
  1164.              END;                  { doparens }
  1165.  
  1166.        PROCEDURE passfields;       { process declarations }
  1167.                                    {     procedure passfields (forvariant:
  1168.                                       boolean); }
  1169.  
  1170.           BEGIN                    { passfields }
  1171.              WHILE NOT (symbol IN recendsyms) DO BEGIN
  1172.                 IF symbol = semicolon THEN passsemicolons
  1173.                 ELSE IF symbol = syrecord THEN dorecord
  1174.                 ELSE IF symbol = sycase THEN dovariant
  1175.                 ELSE IF symbol = leftparen THEN doparens (forvariant)
  1176.                 ELSE getsymbol   END;
  1177.              END;                  { passfields }
  1178.  
  1179.        BEGIN                       { block }
  1180.           WHILE symbol = declarator DO BEGIN
  1181.              startpart;            { label, const, type, var }
  1182.              getsymbol;
  1183.              startbody;
  1184.              REPEAT
  1185.                 passphrase (syrecord);
  1186.                 IF symbol = syrecord THEN dorecord;
  1187.                 IF symbol = semicolon THEN passsemicolons
  1188.                 UNTIL symbol IN headersyms;
  1189.              finishbody   END;
  1190.           WHILE symbol = progprocfunc DO BEGIN
  1191.              startpart;            { program, procedure, function }
  1192.              heading;
  1193.              startbody;
  1194.              IF symbol IN headersyms THEN block
  1195.              ELSE IF symbol = ident THEN BEGIN
  1196.                 startpart;         { directive: forward, etc. }
  1197.                 passphrase (semicolon);
  1198.                 passsemicolons   END
  1199.              ELSE writeerror (notbegin);
  1200.              finishbody   END;
  1201.           IF symbol = sybegin THEN BEGIN
  1202.              startpart;            { statement part }
  1203.              compoundstmt (TRUE);
  1204.              IF symbol IN [sysubrange, period] THEN symbol := semicolon;
  1205.                                    { treat final period as semicolon }
  1206.              passsemicolons   END;
  1207.           END;                     { block }
  1208.  
  1209.     PROCEDURE copyrem;             { copy remainder of input }
  1210.  
  1211.        BEGIN
  1212.           writeerror (noeof);
  1213.           WITH inlinexx DO BEGIN
  1214.              REPEAT
  1215.                 copyword (FALSE, WORD);
  1216.                 startword (contuncomm);
  1217.                 IF NOT endoffile THEN BEGIN
  1218.                    REPEAT
  1219.                       getchar
  1220.                       UNTIL ch = ' '   END;
  1221.                 finishword;
  1222.                 UNTIL endoffile   END;
  1223.           END;                     { copyrem }
  1224.  
  1225.     PROCEDURE initialize;          { initialize global variables }
  1226.  
  1227.        VAR
  1228.           i: 1..9;                 { loop index }
  1229.  
  1230.        BEGIN
  1231.           WITH inlinexx DO BEGIN
  1232.              FOR i := 1 TO 9 DO buf[i - 9] := instring[i];
  1233.                                    { string ';BEGINEND' in buf[-8..0] }
  1234.              endoffile := FALSE;
  1235.              ch := ' ';
  1236.              index := 0;
  1237.              len := 0   END;
  1238.           WITH outline DO BEGIN
  1239.              blanklns := 0;
  1240.              len := 0   END;
  1241.           WITH WORD DO BEGIN
  1242.              whenfirst := contuncomm;
  1243.              puncfollows := FALSE;
  1244.              blanklncount := 0;
  1245.              spaces := 0;
  1246.              base := 0;
  1247.              size := 0   END;
  1248.           margin := initmargin;
  1249.           lnpending := FALSE;
  1250.           symbol := othersym;
  1251.           END;                     { initialize }
  1252.  
  1253.     BEGIN                          { pascalformatter }
  1254.        IF (ParamCount<2) OR (ParamCount>3) THEN BEGIN
  1255.           Writeln('Incorrect # of parameters');
  1256.           Halt;
  1257.           END;
  1258.        IF ParamCount = 3 THEN no_error_output := FALSE
  1259.        ELSE no_error_output := TRUE;
  1260.        Assign(Input,ParamStr(1));
  1261.        Reset(Input);
  1262.        Assign(Output,ParamStr(2));
  1263.        Rewrite(Output);
  1264.        strucconsts;
  1265.        initialize;                 {  ***************  Files may be opened here.
  1266.                                       }
  1267.        getsymbol;
  1268.        block;
  1269.        IF NOT inlinexx.endoffile THEN copyrem;
  1270.        writeline;
  1271.        Write(Output,Chr(26));      {put EOF character}
  1272.        Close(Output);
  1273.        END                         { pascalformatter } .
  1274.