home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / PBTURBO.ZIP / PBTURBO.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-28  |  45.8 KB  |  1,108 lines

  1.  program pascalformatter (infile, outfile);
  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. |   This portable program formats Pascal programs and acceptable
  12. |   program fragments according to structured formatting principles
  13. |   [SIGPLAN Notices, Vol. 13, No. 11, Nov. 1978, pp. 15-22].
  14. |   The actions of the program are as follows:
  15. |
  16. |   PREPARATION:  For each structured statement that controls a
  17. |      structured statement, the program converts the controlled
  18. |      statement into a compound statement.  The inserted BEGIN/END
  19. |      pair are in capital letters.  A null statement (with semicolon)
  20. |      is inserted before the last END symbol of each program/
  21. |      procedure/function, if needed.  The semicolon forces the END
  22. |      symbol to appear on a line by itself.
  23. |
  24. |   FORMATTING:  Each structured statement that controls a simple
  25. |      statement is placed on a single line, as if it were a simple
  26. |      statement.  Otherwise, each structured statement is formatted
  27. |      in the following pattern (with indentation "indent"):
  28. |
  29. |            XXXXXX header XXXXXXXX
  30. |               XXXXXXXXXXXXXXXXXX
  31. |               XXXXX body XXXXXX
  32. |               XXXXXXXXXXXXXXXXXX
  33. |
  34. |      where the header is one of:
  35. |
  36. |            while <expression> do begin
  37. |            for <control variable> := <for list> do begin
  38. |            with <record variable list> do begin
  39. |            repeat
  40. |            if <expression> then begin
  41. |            else if <expression> then begin
  42. |            else begin
  43. |            case <expression> of
  44. |            <case label list>: begin
  45. |
  46. |      and the last line either begins with UNTIL or ends with END.
  47. |      Other program parts are formatted similarly.  The headers are:
  48. |
  49. |            <program/procedure/function heading>;
  50. |            label
  51. |            const
  52. |            type
  53. |            var
  54. |            begin
  55. |            (various for records and record variants)
  56. |
  57. |   COMMENTS:  Each comment that starts before or on a specified
  58. |      column on an infile line (program constant "commthresh") is
  59. |      copied without shifting or reformatting.  Each comment that
  60. |      starts after "commthresh" is reformatted and left-justified
  61. |      following the aligned comment base column ("alcommbase").
  62. |
  63. |   LABELS:  Each statement label is justified to the left margin and
  64. |      is placed on a line by itself.
  65. |
  66. |   SPACES AND BLANK LINES:  Spaces not at line breaks are copied from
  67. |      the infile.  Blank lines are copied from the infile if they appear
  68. |      between statements (or appropriate declaration units).  A blank
  69. |      line is inserted above each significant part of each program/
  70. |      procedure/function if one is not already there.
  71. |
  72. |   CONTINUATION:  Lines that are too long for an outfile line are
  73. |      continued with additional indentation ("contindent").
  74. |
  75. |   INPUT FORM:  The program expects as infile a program or program
  76. |      fragment in Standard Pascal.  A program fragment is acceptable
  77. |      if it consists of a sequence of (one or more) properly ordered
  78. |      program parts; examples are:  a statement part (that is, a
  79. |      compound statement), or a TYPE part and a VAR part followed by
  80. |      procedure declarations.  If the program fragment is in serious
  81. |      error, then the program may copy the remainder of the infile file
  82. |      to the outfile file without significant modification.  Error
  83. |      messages may be inserted into the outfile file as comments.
  84. |}
  85.  
  86.     const
  87.        maxrwlen = 10;              { size of reserved word strings }
  88.        ordminchar = 32;            { ord of lowest char in char set }
  89.        ordmaxchar = 126;           { ord of highest char in char set }
  90.  {  Although this program uses the ASCII character set, conversion to
  91.     most other character sets should be straightforward. }
  92.  
  93.  {  The following parameters may be adjusted for the installation: }
  94.        maxinlen = 255;             { maximum width of infile line + 1 }
  95.        maxoutlen = 72;             { maximum width of outfile line }
  96.        initmargin = 1;             { initial value of outfile margin }
  97.        commthresh = 4;             { column threshhold in infile for
  98.                                       comments to be aligned }
  99.        alcommbase = 35;            { aligned comments in outfile start
  100.                                       AFTER this column }
  101.        indent = 3;                 { RECOMMENDED indentation increment }
  102.        contindent = 5;             { continuation indentation, >indent }
  103.        endspaces = 3;              { number of spaces to precede 'END' }
  104.        commindent = 3;             { comment continuation indentation }
  105.  
  106.     type
  107.        natural = 0..maxint;
  108.        inrange = 0..maxinlen;
  109.        outrange = 0..maxoutlen;
  110.  
  111.        errortype = (longline, noendcomm, notquote, longword, notdo,
  112.             notof, notend, notthen, notbegin, notuntil, notsemicolon,
  113.             notcolon, notparen, noeof);
  114.  
  115.        chartype = (illegal, special, chapostrophe, chleftparen,
  116.             chrightparen, chperiod, digit, chcolon, chsemicolon,
  117.             chlessthan, chgreaterthan, letter, chleftbrace);
  118.  
  119.                                    { for reserved word recognition }
  120.        resword = (                 { reserved words ordered by length }
  121.             rwif, rwdo, rwof, rwto, rwin, rwor,
  122.                                    { length: 2 }
  123.             rwend, rwfor, rwvar, rwdiv, rwmod, rwset, rwand, rwnot,
  124.             rwnil,                 { length: 3 }
  125.             rwthen, rwelse, rwwith, rwgoto, rwcase, rwtype, rwfile,
  126.                                    { length: 4 }
  127.             rwbegin, rwuntil, rwwhile, rwarray, rwconst, rwlabel,
  128.                                    { length: 5 }
  129.             rwrepeat, rwrecord, rwdownto, rwpacked,
  130.                                    { length: 6 }
  131.             rwprogram,             { length: 7 }
  132.             rwfunction,            { length: 8 }
  133.             rwprocedure,           { length: 9 }
  134.             rwx);                  { length: 10 for table sentinel }
  135.        rwstring = packed array [1..maxrwlen] of char;
  136.  
  137.        firstclass = (              { class of word if on new line }
  138.             newclause,             { start of new clause }
  139.             continue,              { continuation of clause }
  140.             alcomm,                { start of aligned comment }
  141.             contalcomm,            { continuation of aligned comment }
  142.             uncomm,                { start of unaligned comment }
  143.             contuncomm,            { continuation of unaligned comment }
  144.             stmtlabel);            { statement label }
  145.        wordtype = record           { data record for word }
  146.           whenfirst: firstclass;   { class of word if on new line }
  147.           puncfollows: boolean;    { to reduce dangling punctuation }
  148.           blanklncount: natural;   { number of preceding blank lines }
  149.           spaces: integer;         { number of spaces preceding word }
  150.           base: -9..maxinlen;      { in_line.buf[base] precedes word }
  151.           size: inrange   end;     { length of word in in_line.buf }
  152.  
  153.        symboltype = (              { symbols for syntax analysis }
  154.             semicolon, sybegin, syend,
  155.                                    { three insertable symbols first }
  156.             syif, sydo, syof, sythen, syelse, sygoto, sycase, syuntil,
  157.             syrepeat, syrecord, forwhilewith, progprocfunc, declarator,
  158.             otherword, othersym, leftparen, rightparen, period,
  159.             sysubrange, intconst, colon, ident, comment, syeof);
  160.        inserttype = semicolon..syend;
  161.        symbolset = set of symboltype;
  162.  { *** NOTE: set size of 0..26 REQUIRED for symbolset! }
  163.  
  164.     var
  165.        in_line: record              { infile line data }
  166.           endoffile: boolean;      { end of file on infile? }
  167.           ch: char;                { current char, buf[index] }
  168.           index: inrange;          { subscript of current char }
  169.           len: natural;            { length of infile line in buf }
  170.                                    { string ';BEGINEND' in buf[-8..0] }
  171.           buf: array [-8..maxinlen] of char   end;
  172.        outline: record             { outfile line data }
  173.           blanklns: natural;       { number of preceding blank lines }
  174.           len: outrange;           { number of chars in buf }
  175.           buf: array [1..maxoutlen] of char   end;
  176.        word: wordtype;             { current word }
  177.        margin: outrange;           { left margin }
  178.        lnpending: boolean;         { new line before next symbol? }
  179.        symbol: symboltype;         { current symbol }
  180.        infile,outfile :text;
  181.   { Structured Constants }
  182.        headersyms: symbolset;      { headers for program parts }
  183.        strucsyms: symbolset;       { symbols that begin structured
  184.                                       statements }
  185.        stmtbeginsyms: symbolset;   { symbols that begin statements }
  186.        stmtendsyms: symbolset;     { symbols that follow statements }
  187.        stopsyms: symbolset;        { symbols that stop expression scan }
  188.        recendsyms: symbolset;      { symbols that stop record scan }
  189.        datawords: symbolset;       { to reduce dangling punctuation }
  190.        newword: array [inserttype] of wordtype;
  191.        instring: packed array [1..9] of char;
  192.        firstrw: array [1..maxrwlen] of resword;
  193.        rwword: array [rwif..rwprocedure] of rwstring;
  194.        rwsy: array [rwif..rwprocedure] of symboltype;
  195.        charclass: array [char] of chartype;
  196.   { above is portable form; possible ASCII form is: }
  197.   {    charclass: array [' '..'~'] of chartype;     }
  198.        symbolclass: array [chartype] of symboltype;
  199.  
  200.     function capital (ch: char): char;
  201.                                    { capitalize char if lower-case
  202.                                       letter }
  203.                                    { !!! implementation-dependent! }
  204.  
  205.        const
  206.           lettercasediff = 32;     { ASCII character set }
  207.  
  208.        begin
  209.           if (ch < 'a') or (ch > 'z') then capital := ch
  210.           else capital := chr (ord (ch) - lettercasediff);
  211.           end;                     { capital }
  212.  
  213.     procedure strucconsts;         { establish values of structured
  214.                                       constants }
  215.  
  216.        var
  217.           i: ordminchar..ordmaxchar;
  218.                                    { loop index }
  219.           ch: char;                { loop index }
  220.  
  221.        procedure buildinsert (symbol: inserttype;
  222.             inclass: firstclass;
  223.             inpuncfollows: boolean;
  224.             inspaces, inbase: integer;
  225.             insize: inrange);
  226.  
  227.           begin
  228.              with newword[symbol] do begin
  229.                 whenfirst := inclass;
  230.                 puncfollows := inpuncfollows;
  231.                 blanklncount := 0;
  232.                 spaces := inspaces;
  233.                 base := inbase;
  234.                 size := insize   end;
  235.              end;                  { buildinsert }
  236.  
  237.        procedure buildrw (rw: resword;
  238.             symword: rwstring;
  239.             symbol: symboltype);
  240.  
  241.           begin
  242.              rwword[rw] := symword;{ reserved word string }
  243.              rwsy[rw] := symbol;   { map to symbol }
  244.              end;                  { buildrw }
  245.  
  246.        begin                       { strucconsts }
  247.                                    { symbol sets for syntax analysis }
  248.           headersyms := [progprocfunc, declarator, sybegin, syeof];
  249.           strucsyms := [sycase, syrepeat, syif, forwhilewith];
  250.           stmtbeginsyms := strucsyms + [sybegin, ident, sygoto];
  251.           stmtendsyms := [semicolon, syend, syuntil, syelse, syeof];
  252.           stopsyms := headersyms + strucsyms + stmtendsyms + [sygoto];
  253.           recendsyms := [rightparen, syend, syeof];
  254.  
  255.           datawords := [otherword, intconst, ident, syend];
  256.  
  257.                                    { words for insertable symbols }
  258.           buildinsert (semicolon, continue, false, 0, -9, 1);
  259.           buildinsert (sybegin, continue, false, 1, -8, 5);
  260.           buildinsert (syend, newclause, true, endspaces, -3, 3);
  261.           instring := ';BEGINEND';
  262.  
  263.                                    { constants for recognizing reserved
  264.                                       words }
  265.           firstrw[1] := rwif;      { length: 1 }
  266.           firstrw[2] := rwif;      { length: 2 }
  267.           buildrw (rwif, 'IF        ', syif);
  268.           buildrw (rwdo, 'DO        ', sydo);
  269.           buildrw (rwof, 'OF        ', syof);
  270.           buildrw (rwto, 'TO        ', othersym);
  271.           buildrw (rwin, 'IN        ', othersym);
  272.           buildrw (rwor, 'OR        ', othersym);
  273.           firstrw[3] := rwend;     { length: 3 }
  274.           buildrw (rwend, 'END       ', syend);
  275.           buildrw (rwfor, 'FOR       ', forwhilewith);
  276.           buildrw (rwvar, 'VAR       ', declarator);
  277.           buildrw (rwdiv, 'DIV       ', othersym);
  278.           buildrw (rwmod, 'MOD       ', othersym);
  279.           buildrw (rwset, 'SET       ', othersym);
  280.           buildrw (rwand, 'AND       ', othersym);
  281.           buildrw (rwnot, 'NOT       ', othersym);
  282.           buildrw (rwnil, 'NIL       ', otherword);
  283.           firstrw[4] := rwthen;    { length: 4 }
  284.           buildrw (rwthen, 'THEN      ', sythen);
  285.           buildrw (rwelse, 'ELSE      ', syelse);
  286.           buildrw (rwwith, 'WITH      ', forwhilewith);
  287.           buildrw (rwgoto, 'GOTO      ', sygoto);
  288.           buildrw (rwcase, 'CASE      ', sycase);
  289.           buildrw (rwtype, 'TYPE      ', declarator);
  290.           buildrw (rwfile, 'FILE      ', othersym);
  291.           firstrw[5] := rwbegin;   { length: 5 }
  292.           buildrw (rwbegin, 'BEGIN     ', sybegin);
  293.           buildrw (rwuntil, 'UNTIL     ', syuntil);
  294.           buildrw (rwwhile, 'WHILE     ', forwhilewith);
  295.           buildrw (rwarray, 'ARRAY     ', othersym);
  296.           buildrw (rwconst, 'CONST     ', declarator);
  297.           buildrw (rwlabel, 'LABEL     ', declarator);
  298.           firstrw[6] := rwrepeat;  { length: 6 }
  299.           buildrw (rwrepeat, 'REPEAT    ', syrepeat);
  300.           buildrw (rwrecord, 'RECORD    ', syrecord);
  301.           buildrw (rwdownto, 'DOWNTO    ', othersym);
  302.           buildrw (rwpacked, 'PACKED    ', othersym);
  303.           firstrw[7] := rwprogram; { length: 7 }
  304.           buildrw (rwprogram, 'PROGRAM   ', progprocfunc);
  305.           firstrw[8] := rwfunction;{ length: 8 }
  306.           buildrw (rwfunction, 'FUNCTION  ', progprocfunc);
  307.           firstrw[9] := rwprocedure;
  308.                                    { length: 9 }
  309.           buildrw (rwprocedure, 'PROCEDURE ', progprocfunc);
  310.           firstrw[10] := rwx;      { length: 10 for table sentinel }
  311.  
  312.                                    { constants for lexical scan }
  313.           for i := ordminchar to ordmaxchar do begin
  314.              charclass[chr (i)] := illegal   end;
  315.           for ch := 'a' to 'z' do begin
  316.                                    { !!! implementation-dependent!  (but
  317.                                       can be replaced with 52 explicit
  318.                                       assignments) }
  319.              charclass[ch] := letter;
  320.              charclass[capital (ch)] := letter   end;
  321.           for ch := '0' to '9' do charclass[ch] := digit;
  322.           charclass[' '] := special;
  323.           charclass['$'] := special;
  324.           charclass[''''] := chapostrophe;
  325.           charclass['('] := chleftparen;
  326.           charclass[')'] := chrightparen;
  327.           charclass['*'] := special;
  328.           charclass['+'] := special;
  329.           charclass['-'] := special;
  330.           charclass['.'] := chperiod;
  331.           charclass['/'] := special;
  332.           charclass[':'] := chcolon;
  333.           charclass[';'] := chsemicolon;
  334.           charclass['<'] := chlessthan;
  335.           charclass['='] := special;
  336.           charclass['>'] := chgreaterthan;
  337.           charclass['@'] := special;
  338.           charclass['['] := special;
  339.           charclass[']'] := special;
  340.           charclass['^'] := special;
  341.           charclass['{'] := chleftbrace;
  342.           symbolclass[illegal] := othersym;
  343.           symbolclass[special] := othersym;
  344.           symbolclass[chapostrophe] := otherword;
  345.           symbolclass[chleftparen] := leftparen;
  346.           symbolclass[chrightparen] := rightparen;
  347.           symbolclass[chperiod] := period;
  348.           symbolclass[digit] := intconst;
  349.           symbolclass[chcolon] := colon;
  350.           symbolclass[chsemicolon] := semicolon;
  351.           symbolclass[chlessthan] := othersym;
  352.           symbolclass[chgreaterthan] := othersym;
  353.           symbolclass[letter] := ident;
  354.           symbolclass[chleftbrace] := comment;
  355.  
  356.           end;                     { strucconsts }
  357.  
  358. { writeline/writeerror/readline convert between files and lines. }
  359.  
  360.     procedure writeline;           { write buffer into outfile file }
  361.  
  362.        var
  363.           i: outrange;             { loop index }
  364.  
  365.        begin
  366.           with outline do begin
  367.              while blanklns > 0 do begin
  368.                 writeln (outfile);
  369.                 blanklns := blanklns - 1   end;
  370.              if len > 0 then begin
  371.                 for i := 1 to len do write (outfile, buf[i]);
  372.                 writeln (outfile);
  373.                 len := 0   end   end;
  374.           end;                     { writeline }
  375.  
  376.     procedure writeerror (error: errortype);
  377.                                    { report error to outfile }
  378.  
  379.        var
  380.           i, ix: inrange;          { loop index, limit }
  381.  
  382.        begin
  383.           writeline;
  384.           write (outfile, ' (*  !!! error, ');
  385.           case error of
  386.              longline:     write (outfile, 'shorter line');
  387.              noendcomm:    write (outfile, 'end of comment');
  388.              notquote:     write (outfile, 'final "''" on line');
  389.              longword:     write (outfile, 'shorter word');
  390.              notdo:        write (outfile, '"do"');
  391.              notof:        write (outfile, '"of"');
  392.              notend:       write (outfile, '"end"');
  393.              notthen:      write (outfile, '"then"');
  394.              notbegin:     write (outfile, '"begin"');
  395.              notuntil:     write (outfile, '"until"');
  396.              notsemicolon: write (outfile, '";"');
  397.              notcolon:     write (outfile, '":"');
  398.              notparen:     write (outfile, '")"');
  399.              noeof:        write (outfile, 'end of file')   end;
  400.           write (outfile, ' expected');
  401.           if error >= longword then begin
  402.              write (outfile, ', not "');
  403.              with in_line, word do begin
  404.                 if size > maxrwlen then ix := maxrwlen
  405.                 else ix := size;
  406.                 for i := 1 to ix do write (outfile, buf[base + i])   end;
  407.              write (outfile, '"')   end;
  408.           if error = noeof then write (outfile, ', FORMATTING STOPS');
  409.           writeln (outfile, ' !!!  *)');
  410.           end;                     { writeerror }
  411.  
  412.     procedure readline;            { read line into infile buffer }
  413.  
  414.        var
  415.           c: char;                 { infile character }
  416.           nonblank: boolean;       { is char other than space? }
  417.  
  418.        begin
  419.           with in_line do begin
  420.              len := 0;
  421.              if eof (infile) then endoffile := true
  422.              else begin            { get next line }
  423.                 while not eoln (infile) do begin
  424.                    read (infile, c);
  425.                    if c < ' ' then begin
  426.                                    { convert ASCII control chars (except
  427.                                       leading form feed) to spaces }
  428.                       if c = chr (9) then begin
  429.                                    { ASCII tab char }
  430.                          c := ' '; { add last space at end }
  431.                          while len mod 8 <> 7 do begin
  432.                             len := len + 1;
  433.                             if len < maxinlen then buf[len] := c   end;
  434.                          end       { end tab handling }
  435.                       else if (c <> chr (12)) or (len > 0) then c :=
  436.                            ' ';
  437.                       end;         { end ASCII control char conversion }
  438.                    len := len + 1;
  439.                    if len < maxinlen then buf[len] := c   end;
  440.                 readln (infile);
  441.                 if len >= maxinlen then begin
  442.                                    { infile line too long }
  443.                    writeerror (longline);
  444.                    len := maxinlen - 1   end;
  445.                 nonblank := false;
  446.                 repeat             { trim line }
  447.                    if len = 0 then nonblank := true
  448.                    else if buf[len] <> ' ' then nonblank := true
  449.                    else len := len - 1
  450.                    until nonblank   end;
  451.              len := len + 1;       { add exactly ONE trailing blank }
  452.              buf[len] := ' ';
  453.              index := 0   end;
  454.           end;                     { readline }
  455.  
  456. { startword/finishword/copyword convert between lines and words.
  457.    auxiliary procedures getchar/nextchar precede. }
  458.  
  459.     procedure getchar;             { get next char from infile buffer }
  460.  
  461.        begin
  462.           with in_line do begin
  463.              index := index + 1;
  464.              ch := buf[index]   end;
  465.           end;                     { getchar }
  466.  
  467.     function nextchar: char;       { look at next char in infile buffer }
  468.  
  469.        begin
  470.           with in_line do nextchar := buf[index + 1];
  471.           end;                     { nextchar }
  472.  
  473.     procedure startword (startclass: firstclass);
  474.                                    { note beginning of word, and count
  475.                                       preceding lines and spaces }
  476.  
  477.        var
  478.           first: boolean;          { is word the first on infile line? }
  479.  
  480.        begin
  481.           first := false;
  482.           with in_line, word do begin
  483.              whenfirst := startclass;
  484.              blanklncount := 0;
  485.              while (index >= len) and not endoffile do begin
  486.                 if len = 1 then blanklncount := blanklncount + 1;
  487.                 if startclass = contuncomm then writeline
  488.                 else first := true;
  489.                 readline;          { with exactly ONE trailing blank }
  490.                 getchar;
  491. { ASCII:        if ch = chr (12) then begin
  492.                                    [ ASCII form feed char ]
  493.                    writeline;
  494.                    writeln (outfile, chr (12));
  495.                    blanklncount := 0;
  496.                    getchar   end;  [ end ASCII form feed handling }
  497.                 end;
  498.              spaces := 0;          { count leading spaces }
  499.              if not endoffile then begin
  500.                 while ch = ' ' do begin
  501.                    spaces := spaces + 1;
  502.                    getchar   end   end;
  503.              if first then spaces := 1;
  504.              base := index - 1   end;
  505.           end;                     { startword }
  506.  
  507.     procedure finishword;          { note end of word }
  508.  
  509.        begin
  510.           with in_line, word do begin
  511.              puncfollows := (symbol in datawords) and (ch <> ' ');
  512.              size := index - base - 1   end;
  513.           end;                     { finishword }
  514.  
  515.     procedure copyword (newline: boolean;
  516.          word: wordtype);          { copy word from infile buffer into
  517.                                       outfile buffer }
  518.  
  519.        var
  520.           i: integer;              { outline.len excess, loop index }
  521.  
  522.        begin
  523.           with word, outline do begin
  524.              i := maxoutlen - len - spaces - size;
  525.              if newline or (i < 0) or ((i = 0) and puncfollows) then
  526.                   writeline;
  527.              if len = 0 then begin { first word on outfile line }
  528.                 blanklns := blanklncount;
  529.                 case whenfirst of  { update LOCAL word.spaces }
  530.                    newclause:  spaces := margin;
  531.                    continue:   spaces := margin + contindent;
  532.                    alcomm:     spaces := alcommbase;
  533.                    contalcomm: spaces := alcommbase + commindent;
  534.                    uncomm:     spaces := base;
  535.                    contuncomm: ;   { spaces := spaces }
  536.                    stmtlabel:  spaces := initmargin   end;
  537.                 if spaces + size > maxoutlen then begin
  538.                    spaces := maxoutlen - size;
  539.                                    { reduce spaces }
  540.                    if spaces < 0 then begin
  541.                       writeerror (longword);
  542.                       size := maxoutlen;
  543.                       spaces := 0   end   end   end;
  544.              for i := 1 to spaces do begin
  545.                                    { put out spaces }
  546.                 len := len + 1;
  547.                 buf[len] := ' '   end;
  548.              for i := 1 to size do begin
  549.                                    { copy actual word }
  550.                 len := len + 1;
  551.                 buf[len] := in_line.buf[base + i]   end   end;
  552.           end;                     { copyword }
  553.  
  554. { docomment/copysymbol/insert/getsymbol/findsymbol convert between
  555.    words and symbols. }
  556.  
  557.     procedure docomment;           { copy aligned or unaligned comment }
  558.  
  559.        procedure copycomment (commclass: firstclass;
  560.             commbase: inrange);    { copy words of comment }
  561.  
  562.           var
  563.              endcomment: boolean;  { end of comment? }
  564.  
  565.           begin
  566.              with word do begin    { copy comment begin symbol }
  567.                 whenfirst := commclass;
  568.                 spaces := commbase - outline.len;
  569.                 copyword ((spaces < 0) or (blanklncount > 0), word)
  570.                 end;
  571.              commclass := succ (commclass);
  572.              with in_line do begin
  573.                 repeat             { loop for successive words }
  574.                    startword (commclass);
  575.                    endcomment := endoffile;
  576.                                    { premature end? }
  577.                    if endcomment then writeerror (noendcomm)
  578.                    else begin
  579.                       repeat
  580.                          if ch = '*' then begin
  581.                             getchar;
  582.                             if ch = ')' then begin
  583.                                endcomment := true;
  584.                                getchar   end   end
  585.                          else if ch = '}' then begin
  586.                             endcomment := true;
  587.                             getchar   end
  588.                          else getchar
  589.                          until (ch = ' ') or endcomment   end;
  590.                    finishword;
  591.                    copyword (false, word)
  592.                    until endcomment   end;
  593.              end;                  { copycomment }
  594.  
  595.        begin                       { docomment }
  596.           if word.base < commthresh then begin
  597.                                    { copy comment without alignment }
  598.              copycomment (uncomm, word.base)   end
  599.           else begin               { align and format comment }
  600.              copycomment (alcomm, alcommbase)   end;
  601.           end;                     { docomment }
  602.  
  603.     procedure copysymbol (symbol: symboltype;
  604.          word: wordtype);          { copy word(s) of symbol }
  605.  
  606.        begin
  607.           if symbol = comment then begin
  608.              docomment;            { NOTE: docomment uses global word! }
  609.              lnpending := true   end
  610.           else if symbol = semicolon then begin
  611.              copyword (false, word);
  612.              lnpending := true   end
  613.           else begin
  614.              copyword (lnpending, word);
  615.              lnpending := false   end;
  616.           end;                     { copysymbol }
  617.  
  618.     procedure insert (newsymbol: inserttype);
  619.                                    { copy word for inserted symbol into
  620.                                       outfile buffer }
  621.  
  622.        begin
  623.           copysymbol (newsymbol, newword[newsymbol]);
  624.           end;                     { insert }
  625.  
  626.     procedure getsymbol;           { get next non-comment symbol }
  627.  
  628.        procedure findsymbol;       { find next symbol in infile buffer }
  629.  
  630.           var
  631.              chclass: chartype;    { classification of leading char }
  632.  
  633.           procedure checkresword;  { check if current identifier is
  634.                                       reserved word/symbol }
  635.  
  636.              var
  637.                 rw, rwbeyond: resword;
  638.                                    { loop index, limit }
  639.                 symword: rwstring; { copy of symbol word }
  640.                 i: 1..maxrwlen;    { loop index }
  641.  
  642.              begin
  643.                 with word, in_line do begin
  644.                    size := index - base - 1;
  645.                    if size < maxrwlen then begin
  646.                       symword := '          ';
  647.                       for i := 1 to size do symword[i] := capital (buf[
  648.                            base + i]);
  649.                       rw := firstrw[size];
  650.                       rwbeyond := firstrw[size + 1];
  651.                       symbol := semicolon;
  652.                       repeat
  653.                          if rw >= rwbeyond then symbol := ident
  654.                          else if symword = rwword[rw] then symbol :=
  655.                               rwsy[rw]
  656.                          else rw := succ (rw)
  657.                          until symbol <> semicolon;
  658.                       if symbol = syend then begin
  659.                          if spaces < endspaces then spaces := endspaces;
  660.                          whenfirst := newclause   end   end   end;
  661.                 end;               { checkresword }
  662.  
  663.           procedure getname;
  664.  
  665.              begin
  666.                 while charclass[in_line.ch] in [letter, digit] do
  667.                      getchar;
  668.                 checkresword;
  669.                 end;               { getname }
  670.  
  671.           procedure getnumber;
  672.  
  673.              begin
  674.                 with in_line do begin
  675.                    while charclass[ch] = digit do getchar;
  676.                    if ch = '.' then begin
  677.                                    { thanks to A.H.J.Sale, watch for
  678.                                       '..' }
  679.                       if charclass[nextchar] = digit then begin
  680.                                    { NOTE: nextchar is a function! }
  681.                          symbol := otherword;
  682.                          getchar;
  683.                          while charclass[ch] = digit do getchar   end
  684.                       end;
  685.                    if capital (ch) = 'E' then begin
  686.                       symbol := otherword;
  687.                       getchar;
  688.                       if (ch = '+') or (ch = '-') then getchar;
  689.                       while charclass[ch] = digit do getchar   end
  690.                    end;
  691.                 end;               { getnumber }
  692.  
  693.           procedure getstringliteral;
  694.  
  695.              var
  696.                 endstring: boolean;{ end of string literal? }
  697.  
  698.              begin
  699.                 with in_line do begin
  700.                    endstring := false;
  701.                    repeat
  702.                       if ch = '''' then begin
  703.                          getchar;
  704.                          if ch = '''' then getchar
  705.                          else endstring := true   end
  706.                       else if index >= len then begin
  707.                                    { error, final "'" not on line }
  708.                          writeerror (notquote);
  709.                          symbol := syeof;
  710.                          endstring := true   end
  711.                       else getchar
  712.                       until endstring   end;
  713.                 end;               { getstringliteral }
  714.  
  715.           begin                    { findsymbol }
  716.              startword (continue);
  717.              with in_line do begin
  718.                 if endoffile then symbol := syeof
  719.                 else begin
  720.                    chclass := charclass[ch];
  721.                    symbol := symbolclass[chclass];
  722.                    getchar;        { second char }
  723.                    case chclass of
  724.                       chsemicolon, chrightparen, chleftbrace, special,
  725.                            illegal:   ;
  726.                       letter:  getname;
  727.                       digit:  getnumber;
  728.                       chapostrophe:  getstringliteral;
  729.                       chcolon:  begin
  730.                          if ch = '=' then begin
  731.                             symbol := othersym;
  732.                             getchar   end   end;
  733.                       chlessthan:  begin
  734.                          if (ch = '=') or (ch = '>') then getchar   end;
  735.                       chgreaterthan:  begin
  736.                          if ch = '=' then getchar   end;
  737.                       chleftparen:  begin
  738.                          if ch = '*' then begin
  739.                             symbol := comment;
  740.                             getchar   end   end;
  741.                       chperiod:  begin
  742.                          if ch = '.' then begin
  743.                             symbol := sysubrange;
  744.                             getchar   end   end   end   end   end;
  745.              finishword;
  746.              end;                  { findsymbol }
  747.  
  748.        begin                       { getsymbol }
  749.           repeat
  750.              copysymbol (symbol, word);
  751.                                    { copy word for symbol to outfile }
  752.              findsymbol            { get next symbol }
  753.              until symbol <> comment;
  754.           end;                     { getsymbol }
  755.  
  756. { block performs recursive-descent syntax analysis with symbols,
  757.    adjusting margin, lnpending, word.whenfirst, and
  758.    word.blanklncount.  auxiliary procedures precede. }
  759.  
  760.     procedure startclause;         { (this may be a simple clause, or
  761.                                       the start of a header) }
  762.  
  763.        begin
  764.           word.whenfirst := newclause;
  765.           lnpending := true;
  766.           end;                     { startclause }
  767.  
  768.     procedure passsemicolons;      { pass consecutive semicolons }
  769.  
  770.        begin
  771.           while symbol = semicolon do begin
  772.              getsymbol;
  773.              startclause   end;    { new line after ';' }
  774.           end;                     { passsemicolons }
  775.  
  776.     procedure startpart;           { start program part }
  777.  
  778.        begin
  779.           with word do begin
  780.              if blanklncount = 0 then blanklncount := 1   end;
  781.           startclause;
  782.           end;                     { startpart }
  783.  
  784.     procedure startbody;           { finish header, start body of
  785.                                       structure }
  786.  
  787.        begin
  788.           passsemicolons;
  789.           margin := margin + indent;
  790.           startclause;
  791.           end;                     { startbody }
  792.  
  793.     procedure finishbody;
  794.  
  795.        begin
  796.           margin := margin - indent;
  797.           end;                     { finishbody }
  798.  
  799.     procedure passphrase (finalsymbol: symboltype);
  800.                                    { process symbols until significant
  801.                                       symbol encountered }
  802.  
  803.        var
  804.           endsyms: symbolset;      { complete set of stopping symbols }
  805.  
  806.        begin
  807.           if symbol <> syeof then begin
  808.              endsyms := stopsyms + [finalsymbol];
  809.              repeat
  810.                 getsymbol
  811.                 until symbol in endsyms   end;
  812.           end;                     { passphrase }
  813.  
  814.     procedure expect (expectedsym: symboltype;
  815.          error: errortype;
  816.          syms: symbolset);
  817.  
  818.        begin
  819.           if symbol = expectedsym then getsymbol
  820.           else begin
  821.              writeerror (error);
  822.              while not (symbol in [expectedsym] + syms) do getsymbol;
  823.              if symbol = expectedsym then getsymbol   end;
  824.           end;                     { expect }
  825.  
  826.     procedure dolabel;             { process statement label }
  827.  
  828.        var
  829.           nextfirst: firstclass;   { (pass whenfirst to statement) }
  830.  
  831.        begin
  832.           with word do begin
  833.              nextfirst := whenfirst;
  834.              whenfirst := stmtlabel;
  835.              lnpending := true;
  836.              getsymbol;
  837.              expect (colon, notcolon, stopsyms);
  838.              whenfirst := nextfirst;
  839.              lnpending := true   end;
  840.           end;                     { dolabel }
  841.  
  842.     procedure block;               { process block }
  843.  
  844.        procedure heading;          { process heading for program,
  845.                                       procedure, or function }
  846.  
  847.           procedure matchparens;   { process parentheses in heading }
  848.  
  849.              begin
  850.                 getsymbol;
  851.                 while not (symbol in recendsyms) do begin
  852.                    if symbol = leftparen then matchparens
  853.                    else getsymbol   end;
  854.                 expect (rightparen, notparen, stopsyms + recendsyms);
  855.                 end;               { matchparens }
  856.  
  857.           begin                    { heading }
  858.              getsymbol;
  859.              passphrase (leftparen);
  860.              if symbol = leftparen then matchparens;
  861.              if symbol = colon then passphrase (semicolon);
  862.              expect (semicolon, notsemicolon, stopsyms);
  863.              end;                  { heading }
  864.  
  865.        procedure statement;        { process statement }
  866.  
  867.           forward;
  868.  
  869.        procedure stmtlist;         { process sequence of statements }
  870.  
  871.           begin
  872.              repeat
  873.                 statement;
  874.                 passsemicolons
  875.                 until symbol in stmtendsyms;
  876.              end;                  { stmtlist }
  877.  
  878.        procedure compoundstmt (    { process compound statement }
  879.             stmtpart: boolean);    { statement part of block? }
  880.  
  881.           begin
  882.              getsymbol;
  883.              startbody;            { new line, indent after 'BEGIN' }
  884.              stmtlist;
  885.              if stmtpart and not lnpending then insert (semicolon);
  886.              expect (syend, notend, stmtendsyms);
  887.              finishbody;           { left-indent after 'END' }
  888.              end;                  { compoundstmt }
  889.  
  890.        procedure statement;        { process statement }
  891.  
  892.           procedure checkcompound; { if structured then force compound }
  893.  
  894.              begin
  895.                 if symbol = intconst then dolabel;
  896.                 if symbol in strucsyms then begin
  897.                                    { force compound }
  898.                    insert (sybegin);
  899.                    startbody;      { new line, indent after 'BEGIN' }
  900.                    statement;
  901.                    insert (syend);
  902.                    finishbody   end{ left-indent after 'END' }
  903.                 else statement;
  904.                 end;               { checkcompound }
  905.  
  906.           procedure ifstmt;        { process if statement }
  907.  
  908.              begin
  909.                 passphrase (sythen);
  910.                 expect (sythen, notthen, stopsyms);
  911.                 checkcompound;
  912.                 if symbol = syelse then begin
  913.                    startclause;    { new line before 'ELSE' }
  914.                    getsymbol;
  915.                    if symbol = syif then ifstmt
  916.                    else checkcompound   end;
  917.                 end;               { ifstmt }
  918.  
  919.           procedure repeatstmt;    { process repeat statement }
  920.  
  921.              begin
  922.                 getsymbol;
  923.                 startbody;         { new line, indent after 'REPEAT' }
  924.                 stmtlist;
  925.                 startclause;       { new line before 'UNTIL' }
  926.                 expect (syuntil, notuntil, stmtendsyms);
  927.                 passphrase (semicolon);
  928.                 finishbody;        { left-ident after 'UNTIL' }
  929.                 end;               { repeatstmt }
  930.  
  931.           procedure fwwstmt;       { process for, while, or with
  932.                                       statement }
  933.  
  934.              begin
  935.                 passphrase (sydo);
  936.                 expect (sydo, notdo, stopsyms);
  937.                 checkcompound;
  938.                 end;               { fwwstmt }
  939.  
  940.           procedure casestmt;      { process case statement }
  941.  
  942.              begin
  943.                 passphrase (syof);
  944.                 expect (syof, notof, stopsyms);
  945.                 startbody;         { new line, indent after 'OF' }
  946.                 repeat
  947.                    passphrase (colon);
  948.                    expect (colon, notcolon, stopsyms);
  949.                    checkcompound;
  950.                    passsemicolons
  951.                    until symbol in stopsyms;
  952.                 expect (syend, notend, stmtendsyms);
  953.                 finishbody;        { left-indent after 'END' }
  954.                 end;               { casestmt }
  955.  
  956.           begin                    { statement }
  957.              if symbol = intconst then dolabel;
  958.              if symbol in stmtbeginsyms then begin
  959.                 case symbol of
  960.                    sybegin:       compoundstmt (false);
  961.                    sycase:        casestmt;
  962.                    syif:          ifstmt;
  963.                    syrepeat:      repeatstmt;
  964.                    forwhilewith:  fwwstmt;
  965.                    ident, sygoto: passphrase (semicolon)   end   end;
  966.              if not (symbol in stmtendsyms) then begin
  967.                 writeerror (notsemicolon);
  968.                                    { ';' expected }
  969.                 passphrase (semicolon)   end;
  970.              end;                  { statement }
  971.  
  972.        procedure passfields (forvariant: boolean);
  973.  
  974.           forward;
  975.  
  976.        procedure dorecord;         { process record declaration }
  977.  
  978.           begin
  979.              getsymbol;
  980.              startbody;
  981.              passfields (false);
  982.              expect (syend, notend, recendsyms);
  983.              finishbody;
  984.              end;                  { dorecord }
  985.  
  986.        procedure dovariant;        { process (case) variant part }
  987.  
  988.           begin
  989.              passphrase (syof);
  990.              expect (syof, notof, stopsyms);
  991.              startbody;
  992.              passfields (true);
  993.              finishbody;
  994.              end;                  { dovariant }
  995.  
  996.        procedure doparens (forvariant: boolean);
  997.                                    { process parentheses in record }
  998.  
  999.           begin
  1000.              getsymbol;
  1001.              if forvariant then startbody;
  1002.              passfields (false);
  1003.              lnpending := false;   { for empty field list }
  1004.              expect (rightparen, notparen, recendsyms);
  1005.              if forvariant then finishbody;
  1006.              end;                  { doparens }
  1007.  
  1008.        procedure passfields;       { process declarations }
  1009.  {     procedure passfields (forvariant: boolean); }
  1010.  
  1011.           begin                    { passfields }
  1012.              while not (symbol in recendsyms) do begin
  1013.                 if symbol = semicolon then passsemicolons
  1014.                 else if symbol = syrecord then dorecord
  1015.                 else if symbol = sycase then dovariant
  1016.                 else if symbol = leftparen then doparens (forvariant)
  1017.                 else getsymbol   end;
  1018.              end;                  { passfields }
  1019.  
  1020.        begin                       { block }
  1021.           while symbol = declarator do begin
  1022.              startpart;            { label, const, type, var }
  1023.              getsymbol;
  1024.              startbody;
  1025.              repeat
  1026.                 passphrase (syrecord);
  1027.                 if symbol = syrecord then dorecord;
  1028.                 if symbol = semicolon then passsemicolons
  1029.                 until symbol in headersyms;
  1030.              finishbody   end;
  1031.           while symbol = progprocfunc do begin
  1032.              startpart;            { program, procedure, function }
  1033.              heading;
  1034.              startbody;
  1035.              if symbol in headersyms then block
  1036.              else if symbol = ident then begin
  1037.                 startpart;         { directive: forward, etc. }
  1038.                 passphrase (semicolon);
  1039.                 passsemicolons   end
  1040.              else writeerror (notbegin);
  1041.              finishbody   end;
  1042.           if symbol = sybegin then begin
  1043.              startpart;            { statement part }
  1044.              compoundstmt (true);
  1045.              if symbol in [sysubrange, period] then symbol := semicolon;
  1046.                                    { treat final period as semicolon }
  1047.              passsemicolons   end;
  1048.           end;                     { block }
  1049.  
  1050.     procedure copyrem;             { copy remainder of infile }
  1051.  
  1052.        begin
  1053.           writeerror (noeof);
  1054.           with in_line do begin
  1055.              repeat
  1056.                 copyword (false, word);
  1057.                 startword (contuncomm);
  1058.                 if not endoffile then begin
  1059.                    repeat
  1060.                       getchar
  1061.                       until ch = ' '   end;
  1062.                 finishword;
  1063.                 until endoffile   end;
  1064.           end;                     { copyrem }
  1065.  
  1066.     procedure initialize;          { initialize global variables }
  1067.  
  1068.        var
  1069.           i: 1..9;                 { loop index }
  1070.  
  1071.        begin
  1072.           with in_line do begin
  1073.              for i := 1 to 9 do buf[i - 9] := instring[i];
  1074.                                    { string ';BEGINEND' in buf[-8..0] }
  1075.              endoffile := false;
  1076.              ch := ' ';
  1077.              index := 0;
  1078.              len := 0   end;
  1079.           with outline do begin
  1080.              blanklns := 0;
  1081.              len := 0   end;
  1082.           with word do begin
  1083.              whenfirst := contuncomm;
  1084.              puncfollows := false;
  1085.              blanklncount := 0;
  1086.              spaces := 0;
  1087.              base := 0;
  1088.              size := 0   end;
  1089.           margin := initmargin;
  1090.           lnpending := false;
  1091.           symbol := othersym;
  1092.           end;                     { initialize }
  1093.  
  1094.     begin                          { pascalformatter }
  1095.        strucconsts;
  1096.        assign(infile,'e:\temp');
  1097.        assign(outfile,'e:\temp.out');
  1098.        reset(infile);
  1099.        rewrite(outfile);
  1100.        initialize;
  1101.  {  ***************  Files may be opened here. }
  1102.        getsymbol;
  1103.        block;
  1104.        if not in_line.endoffile then copyrem;
  1105.        writeline;
  1106.        close(outfile);
  1107.        end                         { pascalformatter } .
  1108.