home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / TPF.ZIP / TPF.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1985-05-27  |  38.5 KB  |  1,159 lines

  1. {[B+,O=78]}
  2. {$R-}
  3.  
  4. {program TPF(Input, Output, Source, Result);
  5. !
  6. ! TPF: A Turbo Pascal Text Formatter.
  7. !
  8. ! TPF is a program which formats a Pascal program (or fragment)
  9. ! according to standardized formatting rules.  It also converts the case
  10. ! of the identifiers and reserved words to your specification.
  11. !
  12. ! A series of directives allow control over various aspects of the
  13. ! formatting.  Many of these are the result of strong differences of
  14. ! opinion amoung potential users of the formatter.
  15. !
  16. ! Anyone studying this program should have a copy of the TPF users
  17. ! manual.
  18. !
  19. ! The formatter does an (almost) complete syntactic check of the program
  20. ! as it formats, and if it gets confused as to where it is, aborts and
  21. ! does not create an output element.  This avoids a problem which the
  22. ! previous formatter had of losing track of its parsing and producing
  23. ! complete garbage as an output element.  This would sometimes get
  24. ! substituted for the original element, and made recovery very
  25. ! difficult.  The extra checking costs a bit of time and code space,
  26. ! but seems worth it overall.  It also allows a very flexible formatting
  27. ! policy.
  28. !
  29. ! There is a delayed output buffer to allow conditional modification of
  30. ! formatting decisions. This allows the user to make tentative
  31. ! decisions and modify them later.  For instance, the user can note
  32. ! potential break points in a line and go back and use them when
  33. ! the line fills.  This facility is also used to allow short statements
  34. ! to follow case labels directly.  The statement is put on the next
  35. ! line, then if it would have fit can be moved back up to the line with
  36. ! the label.
  37. !
  38. ! Comments are always difficult to handle in a Pascal formatter, and
  39. ! TPF attempts to handle them in a way which provides the user with
  40. ! some control of their formatting.  The comment handling is completly
  41. ! separate from the normal formatting, and can be changed without
  42. ! affecting other areas.
  43. !
  44. ! Revision History:
  45. !
  46. ! SW, 9/30/84
  47. !     The WHILE and WITH statements now do not force a blank line before and
  48. !     and after.
  49. !
  50. !     The block header does not emit extra blank lines. Note the programmer
  51. !     must insert a blank line before the BEGIN if so desired.
  52. !
  53. !     The lexical scanner was changed to handle Turbo's hex constants.
  54. !
  55. !     Turbo's FILE type was implemented.
  56. !
  57. ! SW, 5/11/85
  58. !     Handles the OVERLAY keyword.
  59. !     Handles the INLINE statement.
  60. !     Allow for nested comments.
  61. !     Handle Turbo's style of external routines.
  62. !
  63. ! SW, 5/20/85
  64. !     Changed the logic for formatting statement comments. Comments in CONST,
  65. !     TYPE, and VAR declarations are aligned to the "remark" column, whose
  66. !     initial value is 40. All other statement comments are formatted as
  67. !     before.
  68. }
  69.  
  70. const
  71.     Max_Line_Len = 127;                 {max output line length}
  72.     Buf_Size = 129;                     {output buffer size, > max_line_len}
  73.     Buf_Size_P1 = 130;                  {buffer size +- 1}
  74.     Buf_Size_M1 = 128;
  75.     Max_Word_Len = 9;                   {reserved words char size}
  76.     No_Res_Words = 44;                  {number of reserved words}
  77.     Default_Out_Line = 78;              {default output line length}
  78.     Default_Tab_Spaces = 4;             {logical indentation increments}
  79.     Default_Comment_Spaces = 1;         {spacing before and after comments}
  80.     Max_Break_Level = 4;                {max number of break levels}
  81.     Ff = 12;                            {ascii form feed character}
  82.     Ht = 9;                             {ascii tab character}
  83.  
  84.     {identifier spelling constants}
  85.  
  86.     Hash_Max = 64;                      {size of hash table}
  87.     Hash_Lim = 63;                      {top entry in hash table}
  88.     String_Block_Size = 512;            {size of a block of the string table}
  89.     String_Block_Max = 511;             {max entry in a string block}
  90.     String_Index_Max = 63;              {max entry in the string index}
  91.     Tab_Interval = 8;                   {standard tab interval}
  92.  
  93. type
  94.     Symbols = (Absolute_Sym, And_Sym, Array_Sym, Begin_Sym, Case_Sym,
  95.                Const_sym, Div_Sym, Do_Sym, Downto_sym, Else_Sym, End_Sym,
  96.                Extern_Sym, File_Sym, For_Sym, Forward_Sym, Function_Sym,
  97.                Goto_Sym, If_Sym, In_Sym, Inline_Sym, Label_Sym, Mod_Sym,
  98.                Nil_Sym, Not_Sym, Of_Sym, Or_Sym, Overlay_Sym, Packed_Sym,
  99.                Procedure_Sym, Program_Sym, Record_Sym, Repeat_Sym, Set_Sym,
  100.                Shl_Sym, Shr_Sym, String_Sym, Then_Sym, To_Sym, Type_Sym,
  101.                Until_Sym, Var_Sym, While_Sym, With_Sym, Xor_Sym, Plus, Minus,
  102.                Mult, Divide, Becomes, Period, Comma, Semicolon, Colon, Equal,
  103.                Rel_Op, Pointer, Subrange, Apostrophy, Open_Paren, Close_Paren,
  104.                Open_Brack, Close_Brack, Identifier, Number, Str_Const,
  105.                Comment, Text_End);
  106.     {basic symbol enumeration}
  107.  
  108.     Set_Of_Syms = set of Symbols;       {set ops on basic symbols}
  109.     StringType = packed array [1..12] of Char; {identifier type for sirtag}
  110.     Word_Type = packed array [1..Max_Word_Len] of Char; {reserved}
  111.     Len_Table_Type =                    {index into reserved word table by
  112.                                         length}
  113.         record
  114.             Low_Index, Hi_Index: 1..No_Res_Words;
  115.         end;
  116.  
  117.     Line_Index = 0..Max_Line_Len;
  118.     Actions = (Graphic, Spaces, Begin_Line);
  119.     Buffer_Index = 0..Buf_Size_M1;      {output buffer index}
  120.     Char_Buffer = array [Buffer_Index] of
  121.             record
  122.                 case Action_Is: Actions of
  123.                     Spaces, Begin_Line: (Spacing: Line_Index);
  124.                     Graphic: (Character: Char)
  125.             end;
  126.  
  127.     Col_Log =
  128.         record
  129.             Log_Char: Integer;          {char_count at time of log}
  130.             Log_Col: Line_Index;        {write_col at time of log}
  131.             Log_Line: Integer;          {current_line at time of log}
  132.         end;
  133.  
  134.     Break_Lev = 0..Max_Break_Level;     {expression break priorities}
  135.     Abort_Kind = (Syntax, Nesting, Com_Format); {error types}
  136.     Hash_Value = 0..Hash_Max;           {possible hash values}
  137.  
  138.     {string table description}
  139.  
  140.     String_Block_Index = 0..String_Index_Max; {index table index}
  141.     String_Piece_Index = 0..String_Block_Max; {index to chars in a piece}
  142.     String_Block = packed array [String_Piece_Index] of Char;
  143.  
  144.     {identifier spelling bookkeeping}
  145.  
  146.     Id_Ptr = ^Id_Descr;
  147.  
  148.     Id_Descr =
  149.         packed record
  150.             Next: Id_Ptr;               {next id with this hash entry}
  151.             Start: Integer;             {start of identifier spelling in
  152.                                         string table}
  153.             Len: Line_Index;            {length of identifier}
  154.         end;
  155.  
  156. var
  157.     {Structured Constants}
  158.  
  159.     Space_Before, Space_After: Set_Of_Syms; {individual symbol spacing}
  160.     Alphanumerics: Set_Of_Syms;         {alpha symbols}
  161.     Resv_Wrd: array [1..No_Res_Words] of Word_Type; {reserved word table}
  162.     Res_Symbol: array [1..No_Res_Words] of Symbols; {symbols for resv_wrd}
  163.     Res_Len: array [2..Max_Word_Len] of Len_Table_Type; {length index}
  164.     UpperCase: array [Char] of Char;
  165.     LowerCase: array [Char] of Char;    {case conversion tables}
  166.     Prog_Set, Block_Beg_Sys, Stat_Set: Set_Of_Syms; {syntactic symbol types}
  167.     Constants: Set_Of_Syms;             {symbols which can be constants}
  168.     Heading_Beg_Sys: Set_Of_Syms;       {symbols which begin a block heading}
  169.     Type_Beg_Sys: Set_Of_Syms;          {type beginning symbols}
  170.     Expr_Beg_Sys: Set_Of_Syms;          {expression beginning symbols}
  171.     Rel_Ops: Set_Of_Syms;               {relational operators}
  172.     Arith_Ops: Set_Of_Syms;             {arithmetic operators}
  173.  
  174.     {Formatting variables}
  175.  
  176.     Indent: Integer;                    {current number of indentation spaces}
  177.     Stat_Indent: Integer;               {indentation for major statement}
  178.     Write_Col: Integer;                 {current output column}
  179.     Remark_Col: Integer;
  180.     Symbol_Break: array [Break_Lev] of
  181.             record
  182.                 Buf_Char: Integer;      {character in buffer}
  183.                 Break_Col: Line_Index;  {output column}
  184.             end;
  185.     Last_Sym: Symbols;                  {last symbol processed}
  186.     Sym_Written: Boolean;               {last symbol was written}
  187.     Indent_State: array [Line_Index] of Line_Index;
  188.     Indent_Level: Line_Index;           {these make a stack of indent levels}
  189.     End_Line: Boolean;                  {last symbol ends the line}
  190.  
  191.     {miscellaneous}
  192.  
  193.     Result: Text;                       {output file}
  194.     Source: Text;                       {input file}
  195.     Output_Line: Integer;               {line numbers for output}
  196.     Current_Line: Integer;              {line number being written}
  197.     Input_Line: Integer;                {input line number}
  198.  
  199.     {Formatting Control Values}
  200.  
  201.     Out_Line_Len: Integer;              {current output line length}
  202.     One_Half_Line: Integer;             {significant point upon line}
  203.     Five_Eighth_Line: Integer;          {"}
  204.     Three_Fourth_Line: Integer;         {"}
  205.  
  206.     Tab_Spaces: Integer;                {spaces to indent for each level}
  207.     Continue_Spaces: Integer;           {spaces to indent continuation line}
  208.     Comment_Spaces: Integer;            {spaces before statement comment}
  209.     Stats_Per_Line: Integer;            {statements per line}
  210.  
  211.     {flags to direct formatting}
  212.  
  213.     Uc_Res_Words: Boolean;              {convert reserved words to UC}
  214.     Lc_Res_Words: Boolean;              {convert reserved words to LC}
  215.     Uc_Idents: Boolean;                 {convert identifiers to UC}
  216.     Lc_Idents: Boolean;                 {convert identifiers to LC}
  217.     Portability_Mode: Boolean;          {eliminate underscores}
  218.     Formatting: Boolean;                {do formatting (otherwise, copy)}
  219.     New_Formatting: Boolean;            {start formatting at end of comment}
  220.     Bunching: Boolean;                  {bunch statements on one line}
  221.     Convert_To_Tabs: Boolean;           {convert leading spaces to tabs}
  222.     First_Spelling: Boolean;            {convert equivalent ids to first
  223.                                         spelling}
  224.  
  225.     {lexical scanner variables}
  226.  
  227.     Symbol_Found: Boolean;              {success from lexical analysis}
  228.     New_Input_Line: Boolean;            {true when no chars as yet on new
  229.                                         line}
  230.     Blank_Lines: Integer;               {Count of blank lines read but not
  231.                                         printed}
  232.     End_File: Boolean;                  {eof read}
  233.     Ch: Char;                           {current character for lexical
  234.                                         analysis}
  235.     Double_Period: Boolean;             {set if double period found}
  236.     Column: Integer;                    {input column for last char input}
  237.     Tab_Column: Integer;                {column at end of tab, for conversion
  238.                                         to spaces}
  239.     Sym: Symbols;                       {current basic symbol from lex}
  240.     Symbol: array [Line_Index] of Char; {workspace for lex analysis}
  241.     Sym_Len: 0..Max_Line_Len;           {index into WINDOW array}
  242.     In_Type_Or_Var_Dcl: Boolean;        {true if parsing the TYPE or VAR
  243.                                         declaration}
  244.     In_Declaration: Boolean;            {true if parsing a CONST, TYPE, or VAR
  245.                                         declaration }
  246.  
  247.     {output character buffering}
  248.  
  249.     Unwritten: Char_Buffer;             {unwritten characters}
  250.     Char_Count: Integer;                {characters written so far}
  251.     Oldest: Buffer_Index;               {oldest char in buffer}
  252.     Initial_Blanks: Integer;            {initial blanks on a line}
  253.     Saving_Blanks: Boolean;             {true if saving blanks to convert to
  254.                                         tabs}
  255.  
  256.     {error handling variables}
  257.  
  258.     Overflows: 0..Maxint;               {number of line overflows}
  259.     First_Overflow: 0..Maxint;          {line where first overflow occured}
  260.     Com_Overflows: 0..Maxint;           {number of comment overflows}
  261.     First_Com_Overflow: 0..Maxint;      {line of first comment overflow}
  262.  
  263.     {identifier spelling variables}
  264.  
  265.     Hash_Table: array [Hash_Value] of Id_Ptr; {main hash table}
  266.     String_Index: array [String_Block_Index] of ^String_Block;
  267.  
  268.     {string table base array}
  269.  
  270.     String_Top: Integer;                {last character in string table}
  271.  
  272. {*---------------------------------*
  273.  | Read and Process Command String |
  274.  *---------------------------------*}
  275.  
  276. const
  277.     Default_Ext = 'PAS';                {default filename extension}
  278.     CSI_Prompt = 'TPF>';                {prompt to use if necessary}
  279.  
  280. var
  281.     {Operating system interface variables}
  282.  
  283.     Cmd_String: string [80] absolute CSeg: $0080;
  284.     Cmd_Line: string [80];
  285.     Source_Filename: string [8];
  286.     Source_Extension: string [3];
  287.  
  288.  
  289. procedure CSI;
  290. {
  291. ! Read and process command line.
  292. }
  293.  
  294.     var
  295.         I: Integer;
  296.  
  297.     begin
  298.  
  299.     if Length(Cmd_String) > 0 then
  300.         Cmd_Line := Copy(Cmd_String, 2, Length(Cmd_String) - 1)
  301.     else
  302.         repeat
  303.             Write(CSI_Prompt);
  304.             ReadLn(Cmd_Line);
  305.         until Length(Cmd_Line) > 0;
  306.  
  307.     I := Pos('.', Cmd_Line);
  308.  
  309.     if I = 0 then
  310.         begin
  311.         Source_Filename := Cmd_Line;
  312.         Source_Extension := Default_Ext;
  313.         end
  314.     else
  315.         begin
  316.         Source_Filename := Copy(Cmd_Line, 1, I - 1);
  317.         Source_Extension := Copy(Cmd_Line, I + 1, 3);
  318.         end;
  319.  
  320.     Assign(Source, Source_Filename + '.' + Source_Extension);
  321.     {$I-}
  322.     Reset(Source);
  323.     {$I+}
  324.  
  325.     if IoResult <> 0 then
  326.         begin
  327.         WriteLn(Output);
  328.         WriteLn('Cannot open input file');
  329.         Halt;
  330.         end;
  331.  
  332.     Assign(Result, 'TEMP.TMP');
  333.     Rewrite(Result);
  334.     end; {csi}
  335.  
  336. {*--------------------------*
  337.  | Initialize Set Constants |
  338.  *--------------------------*}
  339.  
  340.  
  341. procedure Init_Sets; {initialize set constants}
  342.     begin
  343.     Space_Before := [Absolute_Sym, And_Sym, Div_Sym, Do_Sym, Downto_sym,
  344.                     In_Sym, Mod_Sym, Of_Sym, Or_Sym, Shl_Sym, Shr_Sym,
  345.                     Then_Sym, To_Sym, Xor_Sym, Plus, Minus, Mult, Divide,
  346.                     Becomes, Equal, Rel_Op];
  347.  
  348.     Space_After := [Absolute_Sym, And_Sym, Array_Sym, Case_Sym, Div_Sym,
  349.                    Downto_Sym, Extern_Sym, For_Sym, Function_Sym, Goto_Sym,
  350.                    If_Sym, In_Sym, Inline_Sym, Mod_Sym, Not_Sym, Of_Sym,
  351.                    Or_Sym, Overlay_Sym, Packed_Sym, Procedure_Sym,
  352.                    Program_Sym, Shl_Sym, Shr_Sym, String_Sym, To_Sym,
  353.                    Until_Sym, While_Sym, With_Sym, Xor_Sym, Plus, Minus, Mult,
  354.                    Divide, Becomes, Comma, Semicolon, Colon, Equal, Rel_Op,
  355.                    Comment];
  356.  
  357.     Alphanumerics := [Absolute_Sym..Xor_Sym, Identifier, Number];
  358.  
  359.     Heading_Beg_Sys := [Label_Sym, Const_sym, Type_Sym, Var_Sym, Overlay_Sym,
  360.                        Procedure_Sym, Function_Sym];
  361.  
  362.     Block_Beg_Sys := Heading_Beg_Sys + [Begin_Sym];
  363.  
  364.     Stat_Set := [Begin_Sym, If_Sym, Case_Sym, While_Sym, Repeat_Sym, For_Sym,
  365.                 With_Sym, Inline_Sym, Goto_Sym, Number, Identifier];
  366.  
  367.     Constants := [Number, Identifier, Str_Const, Plus, Minus, Nil_Sym];
  368.  
  369.     Expr_Beg_Sys := Constants + [Pointer, Not_Sym, Nil_Sym, Open_Brack,
  370.                     Open_Paren];
  371.  
  372.     Arith_Ops := [Plus, Minus, Mult, Divide, Div_Sym, Mod_Sym];
  373.     Rel_Ops := [Equal, Rel_Op, In_Sym];
  374.  
  375.     Type_Beg_Sys := Constants + [Pointer, Set_Sym, Record_Sym, File_Sym,
  376.                     Array_Sym, Open_Paren, String_Sym] - [Nil_Sym];
  377.     end {init_sets} ;
  378.  
  379.  
  380. procedure Init_Resv_Wrd;
  381. {
  382. ! Initialize reserved word array and length indices into reserved word
  383. ! array for length keyed search
  384. }
  385.     begin {[s=2]}
  386.     Res_Len[2].Low_Index := 1;           Res_Len[2].Hi_Index := 6;
  387.     Res_Len[3].Low_Index := 7;           Res_Len[3].Hi_Index := 18;
  388.     Res_Len[4].Low_Index := 19;          Res_Len[4].Hi_Index := 25;
  389.     Res_Len[5].Low_Index := 26;          Res_Len[5].Hi_Index := 31;
  390.     Res_Len[6].Low_Index := 32;          Res_Len[6].Hi_Index := 37;
  391.     Res_Len[7].Low_Index := 38;          Res_Len[7].Hi_Index := 40;
  392.     Res_Len[8].Low_Index := 41;          Res_Len[8].Hi_Index := 43;
  393.     Res_Len[9].Low_Index := 44;          Res_Len[9].Hi_Index := 44;
  394.     Resv_Wrd[1] := 'do       ';          Res_Symbol[1] := Do_Sym;
  395.     Resv_Wrd[2] := 'if       ';          Res_Symbol[2] := If_Sym;
  396.     Resv_Wrd[3] := 'in       ';          Res_Symbol[3] := In_Sym;
  397.     Resv_Wrd[4] := 'of       ';          Res_Symbol[4] := Of_Sym;
  398.     Resv_Wrd[5] := 'or       ';          Res_Symbol[5] := Or_Sym;
  399.     Resv_Wrd[6] := 'to       ';          Res_Symbol[6] := To_Sym;
  400.     Resv_Wrd[7] := 'and      ';          Res_Symbol[7] := And_Sym;
  401.     Resv_Wrd[8] := 'div      ';          Res_Symbol[8] := Div_Sym;
  402.     Resv_Wrd[9] := 'end      ';          Res_Symbol[9] := End_Sym;
  403.     Resv_Wrd[10] := 'for      ';         Res_Symbol[10] := For_Sym;
  404.     Resv_Wrd[11] := 'mod      ';         Res_Symbol[11] := Mod_Sym;
  405.     Resv_Wrd[12] := 'nil      ';         Res_Symbol[12] := Nil_Sym;
  406.     Resv_Wrd[13] := 'not      ';         Res_Symbol[13] := Not_Sym;
  407.     Resv_Wrd[14] := 'set      ';         Res_Symbol[14] := Set_Sym;
  408.     Resv_Wrd[15] := 'shl      ';         Res_Symbol[15] := Shl_Sym;
  409.     Resv_Wrd[16] := 'shr      ';         Res_Symbol[16] := Shr_Sym;
  410.     Resv_Wrd[17] := 'var      ';         Res_Symbol[17] := Var_Sym;
  411.     Resv_Wrd[18] := 'xor      ';         Res_Symbol[18] := Xor_Sym;
  412.     Resv_Wrd[19] := 'case     ';         Res_Symbol[19] := Case_Sym;
  413.     Resv_Wrd[20] := 'else     ';         Res_Symbol[20] := Else_Sym;
  414.     Resv_Wrd[21] := 'file     ';         Res_Symbol[21] := File_Sym;
  415.     Resv_Wrd[22] := 'goto     ';         Res_Symbol[22] := Goto_Sym;
  416.     Resv_Wrd[23] := 'then     ';         Res_Symbol[23] := Then_Sym;
  417.     Resv_Wrd[24] := 'type     ';         Res_Symbol[24] := Type_Sym;
  418.     Resv_Wrd[25] := 'with     ';         Res_Symbol[25] := With_Sym;
  419.     Resv_Wrd[26] := 'array    ';         Res_Symbol[26] := Array_Sym;
  420.     Resv_Wrd[27] := 'begin    ';         Res_Symbol[27] := Begin_Sym;
  421.     Resv_Wrd[28] := 'const    ';         Res_Symbol[28] := Const_sym;
  422.     Resv_Wrd[29] := 'label    ';         Res_Symbol[29] := Label_Sym;
  423.     Resv_Wrd[30] := 'until    ';         Res_Symbol[30] := Until_Sym;
  424.     Resv_Wrd[31] := 'while    ';         Res_Symbol[31] := While_Sym;
  425.     Resv_Wrd[32] := 'downto   ';         Res_Symbol[32] := Downto_sym;
  426.     Resv_Wrd[33] := 'inline   ';         Res_Symbol[33] := Inline_Sym;
  427.     Resv_Wrd[34] := 'packed   ';         Res_Symbol[34] := Packed_Sym;
  428.     Resv_Wrd[35] := 'record   ';         Res_Symbol[35] := Record_Sym;
  429.     Resv_Wrd[36] := 'repeat   ';         Res_Symbol[36] := Repeat_Sym;
  430.     Resv_Wrd[37] := 'string   ';         Res_Symbol[37] := String_Sym;
  431.     Resv_Wrd[38] := 'forward  ';         Res_Symbol[38] := Forward_Sym;
  432.     Resv_Wrd[39] := 'overlay  ';         Res_Symbol[39] := Overlay_Sym;
  433.     Resv_Wrd[40] := 'program  ';         Res_Symbol[40] := Program_Sym;
  434.     Resv_Wrd[41] := 'absolute ';         Res_Symbol[41] := Absolute_Sym;
  435.     Resv_Wrd[42] := 'external ';         Res_Symbol[42] := Extern_Sym;
  436.     Resv_Wrd[43] := 'function ';         Res_Symbol[43] := Function_Sym;
  437.     Resv_Wrd[44] := 'procedure';         Res_Symbol[44] := Procedure_Sym;
  438.     end {[s=1] init_resv_wrd} ;
  439.  
  440.  
  441. procedure Initialize;
  442.  
  443.     var
  444.         P: Integer;
  445.         C: Char;                        {induction var}
  446.         H: Hash_Value;                  {induction var}
  447.         S: String_Block_Index;          {induction var}
  448.  
  449.     begin {initialize all global variables}
  450.     Init_Sets;
  451.  
  452.     for C := Chr(0) to Chr(127) do
  453.         begin
  454.         LowerCase[C] := C;
  455.         UpperCase[C] := C;
  456.         end;
  457.  
  458.     for C := 'A' to 'Z' do
  459.         begin
  460.         LowerCase[C] := Chr(Ord(C) + Ord('a') - Ord('A'));
  461.         UpperCase[Chr(Ord(C) + Ord('a') - Ord('A'))] := C;
  462.         end;
  463.  
  464.     Char_Count := 0;
  465.     Out_Line_Len := Default_Out_Line;
  466.     Tab_Spaces := Default_Tab_Spaces;
  467.     Continue_Spaces := (Tab_Spaces + 1) div 2;
  468.     Comment_Spaces := Default_Comment_Spaces;
  469.     Indent_Level := 0;
  470.     One_Half_Line := Out_Line_Len div 2;
  471.     Five_Eighth_Line := 5 * Out_Line_Len div 8;
  472.     Three_Fourth_Line := 3 * Out_Line_Len div 4;
  473.     Stats_Per_Line := 1;
  474.     for P := 1 to Out_Line_Len do Symbol[P] := ' ';
  475.     Sym_Len := 0;
  476.     Indent := 0;
  477.     Stat_Indent := 0;
  478.     Write_Col := 0;
  479.     Remark_Col := 40;
  480.     Saving_Blanks := false;
  481.     Column := 0;
  482.     Tab_Column := 0;
  483.     Output_Line := 1;
  484.     Current_Line := 0;
  485.     Input_Line := 1;
  486.     New_Input_Line := true;
  487.     Blank_Lines := 0;
  488.     Sym := Period;
  489.     End_Line := false;
  490.     End_File := false;
  491.     Last_Sym := Period;
  492.     Sym_Written := false;
  493.     Ch := ' ';
  494.     In_Type_Or_Var_Dcl := false;
  495.     In_Declaration := false;
  496.     Double_Period := false;
  497.     Formatting := true;
  498.     New_Formatting := true;
  499.     Uc_Res_Words := false;
  500.     Lc_Res_Words := false;
  501.     Uc_Idents := false;
  502.     Lc_Idents := false;
  503.     Portability_Mode := false;
  504.     Bunching := false;
  505.     Convert_To_Tabs := false;
  506.     First_Spelling := false;
  507.     Overflows := 0;
  508.     Com_Overflows := 0;
  509.     Init_Resv_Wrd;
  510.     for H := 0 to Hash_Max do Hash_Table[H] := nil;
  511.     for S := 0 to String_Index_Max do String_Index[S] := nil;
  512.     String_Top := 0;
  513.     end {initialize} ;
  514.  
  515. {*-----------------------------*
  516.  | Terminate and Print Message |
  517.  *-----------------------------*}
  518.  
  519.  
  520. procedure Backup_Source;
  521.  
  522.     var
  523.         F: Text;
  524.  
  525.     begin
  526.     Assign(F, Source_Filename + '.TPF');
  527.     {$I-}
  528.     Reset(F);
  529.     {$I+}
  530.  
  531.     if IoResult = 0 then {Backup file already exist}
  532.         begin
  533.         Close(F);
  534.         Erase(F);
  535.         end;
  536.  
  537.     Rename(Source, Source_Filename + '.TPF');
  538.     end; {Backup_Source}
  539.  
  540.  
  541. procedure Final_Data; {print summary data}
  542.     begin
  543.     Close(Source);
  544.     Close(Result);
  545.     Backup_Source;
  546.     Rename(Result, Source_Filename + '.' + Source_Extension);
  547.  
  548.     if Overflows > 0 then
  549.         begin
  550.         write(Output, 'Token too wide for output at ', Overflows: 1,
  551.               ' place');
  552.         if Overflows > 1 then write(Output, 's, first error');
  553.         writeln(Output, ' on line ', First_Overflow: 1, '.');
  554.         end;
  555.  
  556.     if Com_Overflows > 0 then
  557.         begin
  558.         write(Output, 'Comment too wide for output at ', Com_Overflows: 1,
  559.               ' place');
  560.         if Com_Overflows > 1 then write(Output, 's, first');
  561.         writeln(Output, ' on line ', First_Com_Overflow: 1, '.');
  562.         end;
  563.     write(Output, ' Formatting complete, ', Output_Line - 1: 1, ' line');
  564.     if Output_Line > 2 then write('s');
  565.     writeln(Output, ' output.');
  566.     end; {final_data}
  567.  
  568. {*------------------*
  569.  | Character output |
  570.  *------------------*}
  571.  
  572.  
  573. procedure Clear_Breaks; {clear out all symbol breaks}
  574.  
  575.     var
  576.         i: Break_Lev;                   {induction var}
  577.  
  578.     begin
  579.     for i := 0 to Max_Break_Level do Symbol_Break[i].Buf_Char := 0;
  580.     end; {clear_breaks}
  581.  
  582.  
  583. procedure Reset_Char_Count;
  584. {
  585. ! Reset the output character count to avoid overflow, taking care to
  586. ! preserve the actual buffer loc
  587. }
  588.     begin
  589.     if Char_Count > Buf_Size_P1 then
  590.         Char_Count := Char_Count mod Buf_Size + 2 * Buf_Size;
  591.     Clear_Breaks;
  592.     end; {reset_char_count}
  593.  
  594.  
  595. procedure Write_A(Ch: Char);
  596. {
  597. ! Write a character to the output buffer. If necessary (which it
  598. ! always is after the buffer is filled), write the previous contents
  599. ! of the buffer) 
  600. }
  601.  
  602.     var
  603.         i: Line_Index;
  604.  
  605.     begin
  606.     Char_Count := Char_Count + 1;
  607.     Oldest := Char_Count mod Buf_Size;
  608.  
  609.     with Unwritten[Oldest] do
  610.         begin
  611.         if Char_Count >= Buf_Size_P1 then
  612.             if Action_Is = Graphic then
  613.                 begin
  614.                 if Saving_Blanks then
  615.                     if Character = ' ' then
  616.                         Initial_Blanks := Initial_Blanks + 1
  617.                     else
  618.                         begin
  619.  
  620.                         while Convert_To_Tabs and
  621.                               (Initial_Blanks >= Tab_Interval) do
  622.                             begin
  623.                             write(Result, Chr(Ht));
  624.                             Initial_Blanks := Initial_Blanks - Tab_Interval;
  625.                             end;
  626.  
  627.                         while Initial_Blanks > 0 do
  628.                             begin
  629.                             write(Result, ' ');
  630.                             Initial_Blanks := Initial_Blanks - 1;
  631.                             end;
  632.  
  633.                         Saving_Blanks := false;
  634.                         write(Result, Character)
  635.                         end
  636.                 else write(Result, Character);
  637.                 end
  638.             else if Action_Is = Spaces then
  639.                 begin
  640.                 if Saving_Blanks then
  641.                     Initial_Blanks := Initial_Blanks + Spacing
  642.                 else
  643.                     for i := 1 to Spacing do write(Result, ' ');
  644.                 end
  645.             else {action_is = begin_line}
  646.                 begin
  647.                 if Char_Count > Buf_Size_P1 then writeln(Result);
  648.                 Saving_Blanks := true;
  649.                 Initial_Blanks := Spacing;
  650.                 Output_Line := Output_Line + 1;
  651.                 end;
  652.         Action_Is := Graphic;
  653.         Character := Ch;
  654.         if Ch = Chr(Ht) then
  655.             Write_Col := ((Write_Col + Tab_Interval) div Tab_Interval) *
  656.                          Tab_Interval
  657.         else Write_Col := Write_Col + 1;
  658.         end; {with}
  659.     end; {write_a}
  660.  
  661.  
  662. procedure New_Line(Indent: Line_Index);
  663.  
  664.   {start a new line and indent it as specified}
  665.  
  666.     begin
  667.     {fake a character, then change it}
  668.     End_Line := false;
  669.     Write_A(' ');
  670.  
  671.     with Unwritten[Oldest] do
  672.         begin
  673.         Action_Is := Begin_Line;
  674.         Spacing := Indent;
  675.         end;
  676.  
  677.     Write_Col := Indent;
  678.     Current_Line := Current_Line + 1;
  679.     end; {new_line}
  680.  
  681.  
  682. procedure Print_Line(Indent: Integer);
  683. {
  684. ! Print a line for formatting
  685. }
  686.     begin
  687.  
  688.     if Formatting then
  689.         begin
  690.  
  691.         while (Blank_Lines > 0) and (Current_Line > 0) do
  692.             begin
  693.             New_Line(0);
  694.             Blank_Lines := 0;
  695.             end;
  696.  
  697.         New_Line(Indent);
  698.         end;
  699.     Blank_Lines := 0;
  700.     Clear_Breaks;
  701.     end; {print_line}
  702.  
  703.  
  704. procedure Space(N: Integer); {space n characters}
  705.     begin
  706.     if Formatting then
  707.         begin
  708.         Write_A(' ');
  709.  
  710.         with Unwritten[Oldest] do
  711.             begin
  712.             Action_Is := Spaces;
  713.             if N >= 0 then Spacing := N
  714.             else Spacing := 0;
  715.             end;
  716.  
  717.         Write_Col := Write_Col + N - 1;
  718.         end;
  719.     end; {space}
  720.  
  721.  
  722. procedure Flush_Buffer; {flush any unwritten buffer}
  723.  
  724.     var
  725.         i: 0..Buf_Size_M1;
  726.  
  727.     begin
  728.     for i := 0 to Buf_Size_M1 do Write_A(' ');
  729.     writeln(Result);
  730.     end; {flush_buffer}
  731.  
  732.  
  733. procedure Flush_Symbol;
  734.  
  735.     var
  736.         P: Line_Index;                  {induction var}
  737.  
  738.     begin {flush any accumulated characters in the buffer}
  739.     if not Sym_Written then for P := 1 to Sym_Len do Write_A(Symbol[P]);
  740.     end; {flush_symbol}
  741.  
  742.  
  743. procedure throwaway(Ch: Char);
  744.     begin {dummy procedure to throw away an output character}
  745.     end; {throwaway}
  746.  
  747.  
  748. procedure Get_Char;
  749.  
  750.   {read next character from input file}
  751.  
  752.     begin
  753.     if Column < Tab_Column then
  754.         begin
  755.         Column := Column + 1;
  756.         Ch := ' ';
  757.         if not Formatting then Write_A(' ');
  758.         end
  759.     else if not Eof(Source) then
  760.         if not eoln(Source) then
  761.             begin {normal}
  762.             Read(Source, Ch);
  763.             if Ch = Chr(Ht) then
  764.                 begin {kluge in input tabs}
  765.                 Tab_Column := ((Column + Tab_Interval) div Tab_Interval) *
  766.                               Tab_Interval;
  767.                 Ch := ' ';
  768.                 end;
  769.             if not Formatting then Write_A(Ch);
  770.             Column := Column + 1;
  771.             end {normal}
  772.         else
  773.             begin {eoln}
  774.             if New_Input_Line then Blank_Lines := Blank_Lines + 1
  775.             else New_Input_Line := true;
  776.             Column := 0;
  777.             Tab_Column := 0;
  778.             Input_Line := Input_Line + 1;
  779.             readln(Source);
  780.             if not Formatting then
  781.                 begin
  782.                 New_Line(0);
  783.                 Reset_Char_Count;
  784.                 end;
  785.             Ch := ' ';
  786.             end {eoln}
  787.     else
  788.         begin {eof}
  789.         End_File := true;
  790.         Ch := ' ';
  791.         end {eof}
  792.     end {get_char} ;
  793.  
  794. {*----------------*
  795.  | Error Handling |
  796.  *----------------*}
  797.  
  798.  
  799. procedure Line_Overflow;
  800.  
  801.   {token too long for output line, note it}
  802.  
  803.     begin
  804.     Overflows := Overflows + 1;
  805.     if Overflows = 1 then First_Overflow := Current_Line + 1;
  806.     end; {line_overflow}
  807.  
  808.  
  809. procedure Comment_Overflow;
  810. {
  811. ! Block comment too long for output line, note it
  812. }
  813.     begin
  814.     Com_Overflows := Com_Overflows + 1;
  815.     if Com_Overflows = 1 then First_Com_Overflow := Current_Line;
  816.     end; {comment_overflow}
  817.  
  818.  
  819. procedure Abort(Kind: Abort_Kind);
  820. {
  821. ! Abort processing and do not create output element
  822. }
  823.     begin
  824.     Flush_Symbol;
  825.     Write_A(Ch);
  826.     writeln(Output);
  827.     if Kind = Syntax then write(Output, 'Syntax error detected, ')
  828.     else if Kind = Nesting then
  829.         write(Output, 'Too many indentation levels, ')
  830.     else write(Output, 'Could not format comment, ');
  831.     WriteLn(Output, 'processing aborted at input line ', Input_Line: 1);
  832.     Close(Result);
  833.     {       Erase(Result); }
  834.     Halt;
  835.     end; {abort}
  836.  
  837. {*---------------------*
  838.  | Indentation Control |
  839.  *---------------------*}
  840.  
  841.  
  842. procedure Indent_Plus(Delta: Integer);
  843. {
  844. ! Increment indentation and check for overflow
  845. }
  846.     begin
  847.     if Indent_Level > Max_Line_Len then Abort(Nesting);
  848.     Indent_Level := Indent_Level + 1;
  849.     Indent_State[Indent_Level] := Indent;
  850.     Indent := Indent + Delta;
  851.     if Indent > Out_Line_Len then Indent := Out_Line_Len
  852.     else if Indent < 0 then Indent := 0;
  853.     end; {indent_plus}
  854.  
  855.  
  856. procedure Undent;
  857. {
  858. ! Reset indent to the last value
  859. }
  860.     begin
  861.     Indent := Indent_State[Indent_Level];
  862.     Indent_Level := Indent_Level - 1;
  863.     end; {undent}
  864.  
  865.  
  866. procedure Set_Symbol_Break(Level: Break_Lev);
  867. {
  868. ! Mark a good spot to break a line
  869. }
  870.     begin
  871.     Space(0);
  872.  
  873.     with Symbol_Break[Level] do
  874.         begin
  875.         Buf_Char := Char_Count;
  876.         Break_Col := Write_Col;
  877.         end;
  878.  
  879.     end; {set_symbol_break}
  880.  
  881.  
  882. procedure Format_Line(Indent: Integer);
  883. {
  884. ! Make a newline if allowed, otherwise mark this as a good break
  885. ! point.
  886. }
  887.     begin
  888.     Print_Line(Indent);
  889.     end;
  890.  
  891.  
  892. procedure Make_White;
  893. {
  894. ! Force a blank line if allowed
  895. }
  896.     begin
  897.     if Formatting and (Blank_Lines = 0) then Blank_Lines := 1;
  898.     end; {Make_White}
  899.  
  900.  
  901. procedure Put_Sym;
  902. {
  903. ! Put the current symbol to the output, taking care of spaces before
  904. ! the symbol. This also handles full lines, and tries to break lines
  905. ! at a convenient place
  906. }
  907.  
  908.     var
  909.         Before: Line_Index;             {spaces before this character}
  910.         Sym_Indent: Integer;            {indentation before this symbol}
  911.         i: Line_Index;                  {induction var}
  912.         L: Break_Lev;                   {induction var}
  913.         Last_Break: Integer;            {last break character}
  914.  
  915.  
  916.     function Spaces_Before(ThisSym, OldSym: Symbols): Line_Index;
  917.     {
  918.     ! Determine the number of spaces before a symbol
  919.     }
  920.         begin
  921.         if ((ThisSym in Alphanumerics) and (OldSym in Alphanumerics)) or
  922.            (ThisSym in Space_Before) or (OldSym in Space_After) then
  923.             Spaces_Before := 1
  924.         else Spaces_Before := 0;
  925.         end; {spaces_before}
  926.  
  927.     begin
  928.     Before := Spaces_Before(Sym, Last_Sym);
  929.     if End_Line or (Before + Sym_Len + Write_Col > Out_Line_Len) then
  930.         begin {must handle an end of line}
  931.         L := Max_Break_Level;
  932.  
  933.         while (L > 0) and (Symbol_Break[L].Buf_Char = 0) do L := L - 1;
  934.  
  935.         with Symbol_Break[L] do
  936.             if not End_Line and Formatting and (Buf_Char > 0) and
  937.                (Char_Count - Buf_Char < Buf_Size) and
  938.                (Before + Sym_Len + Indent + Write_Col - Break_Col <=
  939.                Out_Line_Len) then
  940.                 begin
  941.  
  942.                 with Unwritten[Buf_Char mod Buf_Size] do
  943.                     begin
  944.                     Action_Is := Begin_Line;
  945.                     Spacing := Indent
  946.                     end;
  947.  
  948.                 Write_Col := Write_Col - Break_Col + Indent;
  949.                 Current_Line := Current_Line + 1;
  950.                 Last_Break := Buf_Char;
  951.                 end
  952.             else
  953.                 begin {no good break spot, break it here}
  954.                 Sym_Indent := Out_Line_Len - Sym_Len;
  955.                 if Sym_Indent > Indent then Sym_Indent := Indent
  956.                 else if Sym_Indent < 0 then
  957.                     begin
  958.                     Sym_Indent := 0;
  959.                     Line_Overflow
  960.                     end;
  961.                 Print_Line(Sym_Indent);
  962.                 Last_Break := Char_Count;
  963.                 end;
  964.  
  965.         for L := 0 to Max_Break_Level do
  966.  
  967.             with Symbol_Break[L] do
  968.                 if Buf_Char <= Last_Break then Buf_Char := 0;
  969.  
  970.         end; {if line overflow}
  971.     if Unwritten[Oldest].Action_Is = Begin_Line then Before := 0;
  972.     if Before > 0 then
  973.  
  974.         with Unwritten[Char_Count mod Buf_Size] do
  975.             if Formatting and (Action_Is = Spaces) then
  976.                 begin
  977.                 Write_Col := Write_Col - Spacing + Before;
  978.                 Spacing := Before;
  979.                 end
  980.             else Space(Before);
  981.  
  982.     if Formatting then for i := 1 to Sym_Len do Write_A(Symbol[i]);
  983.     Last_Sym := Sym;
  984.     Sym_Written := true;
  985.     End_Line := false;
  986.     end; {put_sym}
  987.  
  988.  
  989. procedure Block_Com_Char(Character: Char);
  990.     forward;
  991.  
  992.  
  993. procedure Stat_Com_Char(Character: Char);
  994.     forward;
  995.  
  996.  
  997. procedure Do_Formatter_Directives(Put_Ch_Idx: Integer);
  998. {
  999. ! Read a formatter directive and set flags and value appropriately
  1000. }
  1001.  
  1002.     var
  1003.         Temp_Flag: Boolean;
  1004.         Opt_char: Char;                 {which option specified}
  1005.  
  1006.  
  1007.     procedure Copy_A_Char;
  1008.         begin {copy a character and get a new one}
  1009.         case Put_Ch_Idx of
  1010.             1: Block_Com_Char(Ch);
  1011.             2: Stat_Com_Char(Ch);
  1012.             end;
  1013.         Get_Char;
  1014.         end; {copy_a_char}
  1015.  
  1016.  
  1017.     procedure Switch_Dir(var Switch: Boolean);
  1018.         begin {read and set a switch directive, if char is not + or -, the
  1019.                                         value is unchanged}
  1020.  
  1021.         if Ch = '+' then
  1022.             begin
  1023.             Switch := true;
  1024.             Copy_A_Char
  1025.             end
  1026.         else if Ch = '-' then
  1027.             begin
  1028.             Switch := false;
  1029.             Copy_A_Char
  1030.             end;
  1031.         end; {switch_dir}
  1032.  
  1033.  
  1034.     procedure Num_Dir(var Value: Integer;
  1035.                       Min, Max: Integer {limits} );
  1036.     {
  1037.     ! Read a numeric directive and set value. If the value is out
  1038.     ! of bounds it is set to the limit value
  1039.     }
  1040.  
  1041.         var
  1042.             Temp_Val: Integer;          {value being accumulated}
  1043.  
  1044.         begin
  1045.         if Ch = '=' then Copy_A_Char;
  1046.         if (Ch >= '0') and (Ch <= '9') then
  1047.             begin
  1048.             Temp_Val := 0;
  1049.  
  1050.             while (Ch >= '0') and (Ch <= '9') do
  1051.                 begin
  1052.                 if Temp_Val <= (Maxint - 9) div 10 then
  1053.                     Temp_Val := Temp_Val * 10 + (Ord(Ch) - Ord('0'));
  1054.                 Copy_A_Char;
  1055.                 end;
  1056.  
  1057.             if Temp_Val < Min then Temp_Val := Min;
  1058.             if Temp_Val > Max then Temp_Val := Max;
  1059.             Value := Temp_Val;
  1060.             end;
  1061.         end; {num_dir}
  1062.  
  1063.     begin
  1064.     Copy_A_Char;
  1065.     repeat
  1066.         if (Ch <> ']') and (Ch <> '}') and (Ch <> '*') then
  1067.             begin
  1068.             Opt_char := Ch;
  1069.             Copy_A_Char;
  1070.             case Opt_char of
  1071.                 'a', 'A': Switch_Dir(First_Spelling);
  1072.                 'b', 'B': Switch_Dir(Bunching);
  1073.                 'c', 'C': Switch_Dir(Convert_To_Tabs);
  1074.                 'f', 'F': Switch_Dir(New_Formatting);
  1075.  
  1076.                 'o', 'O':
  1077.                     begin
  1078.                     Num_Dir(Out_Line_Len, 1, Max_Line_Len);
  1079.                     One_Half_Line := Out_Line_Len div 2;
  1080.                     Five_Eighth_Line := (5 * Out_Line_Len) div 8;
  1081.                     Three_Fourth_Line := (3 * Out_Line_Len) div 4;
  1082.                     end;
  1083.  
  1084.                 'p', 'P': Switch_Dir(Portability_Mode);
  1085.  
  1086.                 'r', 'R':
  1087.                     begin
  1088.                     Switch_Dir(Temp_Flag);
  1089.                     if Temp_Flag then
  1090.                         begin
  1091.                         Uc_Res_Words := true;
  1092.                         Lc_Res_Words := false;
  1093.                         end
  1094.                     else
  1095.                         begin
  1096.                         Uc_Res_Words := false;
  1097.                         Lc_Res_Words := true;
  1098.                         end;
  1099.                     end;
  1100.  
  1101.                 's', 'S': Num_Dir(Stats_Per_Line, 1, Max_Line_Len);
  1102.  
  1103.                 't', 'T':
  1104.                     begin
  1105.                     Num_Dir(Tab_Spaces, 0, Max_Line_Len);
  1106.                     Continue_Spaces := (Tab_Spaces + 1) div 2;
  1107.                     end;
  1108.  
  1109.                 'u', 'U':
  1110.                     begin
  1111.                     Switch_Dir(Temp_Flag);
  1112.                     if Temp_Flag then
  1113.                         begin
  1114.                         Uc_Idents := true;
  1115.                         LC_Idents := false;
  1116.                         end
  1117.                     else
  1118.                         begin
  1119.                         Uc_Idents := false;
  1120.                         Lc_Idents := true;
  1121.                         end;
  1122.                     end;
  1123.                 else;
  1124.                 end; {case}
  1125.             end;
  1126.     until (Ch = ']') or (Ch = '}') or (Ch = '*');
  1127.     if Ch = ']' then Copy_A_Char;
  1128.     end; {do_formatter_directives}
  1129.  
  1130. {$I TPFCOM.PAS}
  1131. {$I TPFSCN.PAS}
  1132. {$I TPFSTM.PAS}
  1133. {$I TPFDCL.PAS}
  1134.  
  1135.  
  1136. procedure Process_Text;
  1137.     begin
  1138.     Clear_Breaks;
  1139.     if Sym = Program_Sym then Do_Program
  1140.     else if Sym in Block_Beg_Sys then
  1141.         begin
  1142.         Do_Block;
  1143.         if Sym = Semicolon then Next_Sym;
  1144.         if Sym = Period then Next_Sym; {set of external procs}
  1145.         end
  1146.     else if Sym in Stat_Set then Stat_List;
  1147.     check([Text_End]);
  1148.     Flush_Buffer;
  1149.     end {Process_Text} ;
  1150.  
  1151. begin {TPF}
  1152. Initialize;
  1153. csi;
  1154. Get_Char; {lead one char}
  1155. Get_Sym; {lead one symbol}
  1156. Process_Text;
  1157. Final_Data;
  1158. end {TPF} .
  1159.