home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / PASFORMA.ZIP / PASFORMA.PAS
Encoding:
Pascal/Delphi Source File  |  1986-11-08  |  56.3 KB  |  1,273 lines

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