home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of Shareware - Software Farm 2
/
wosw_2.zip
/
wosw_2
/
PASCAL
/
EPB233.ZIP
/
EPB233.PAS
< prev
Wrap
Pascal/Delphi Source File
|
1992-07-25
|
49KB
|
1,671 lines
{ED'S PASCAL BEAUTIFIER v2.33}
{Copyright 1992 by Edward Lee}
{edlee@chinet.chi.il.us}
{Turbo Pascal v4.0}
{31Jan1990 20:00 Program begun}
{1 Feb1990 16:41}
{2 Feb1990 16:47 v1.0 complete Capitalizes keywords}
{4 Feb1990 22:34 v1.1 complete -Lower case option added}
{7 Feb1990 00:29 v1.2 complete Non-alphabetic token padding added}
{ Identifier parsing debugged}
{25Mar1990 21:15 v1.3 maintenance ) append rule modified;}
{ (***) parsing debugged; REGISTERS and TEXT keywords added}
{26May1990 16:56 v1.4 complete optimized loop in identifier parsing}
{ Added identifier substitution option}
{7 Sep1991 13:03 v1.5 maintenance}
{ The inputfile and outputfile may have the same name.}
{ If only the inputfile is specified, the outputfile is assumed to have}
{ the same name unless -o to STDOUT is specified.}
{ An extension of .PAS is assumed for filenames if the extension is not}
{ specified.}
{24Nov1991 21:30 v1.51 maintenance corrected minor typos}
{25Nov1991 06:45 v1.52 maintenance corrected -i and -o options}
{v1.6x were experimental hashing versions}
{26Jan1992 23:15 v1.7}
{ Added -m option for Mixed-case keywords.}
{ The first instance of a user-defined identifier sets the precedent in}
{ capitalization for all further instances of that identifier.}
{24Feb1992 4:46 v1.71 Removed -Lowercase normalization for user identifiers}
{19Mar1992 v2.0 Many rules have been added or modified. This version}
{ variably nests compound IF THEN ELSE, WHILE, FOR, REPEAT operations + more}
{02May1992 v2.1 Bugfix. Added pops for nested, non-compound FOR DOs}
{ and WHILE DOs. Restored '(' padding.}
{14Jun1992 v2.2 Bugfix. Corrected indentation of nested IF THEN ELSE}
{ constructs, indentation of nested WHILE DO constructs}
{16Jun1992 Added an ElseIndent that is independent from IfIndent
{ to allow: ElseIndent=0 }
{03Jul1992 v2.3 Replaced binary searches and insertion sorting with hybrid}
{ radix/child-sibling trees for faster average performance.}
{04Jul1992 v2.31 Bugfix. Corrected an underflow associated with the}
{ conditional line break after a RECORD identifier}
{ v2.32 Modified indentation behavior after line breaks, Added}
{ a conditional line break after the OF keyword}
{23July1992 v2.33 Bugfix. Exponential real and hexadecimal constants}
{ are now mostly invisible to the indentation and identifier}
{ replacement routines. I extend my apologies to anyone}
{ who was inconvenienced by the previous lack of this context}
{ sensitivity.}
{24July1992 Added another error message for a full directory}
CONST
(* Hanging indents after various keywords, in spaces *)
BeginIndent = 0; (* See LeftmostBeginIndent, below *)
CaseIndent = 5;
ConstIndent = 2;
ElseIndent = 3;
ForIndent = 4;
IfIndent = 3;
LabelIndent = 2;
LeftmostBeginIndent = 2;
ProcedureIndent = 2;
RecordIndent = 2;
RepeatIndent = 2;
TypeIndent = 2;
UntilIndent = 6;
VarIndent = 2;
WhileIndent = 6;
WithIndent = 5;
nkeys = 258; (* The number of keywords in keylist[] *)
maxkeylen = 17; (* The maximum length of any keyword in keylist[] *)
(* If you want to insert or delete keywords in the following list, you
* must make sure that the constant NKEYS is updated so that it indicates
* the number of keywords in the list and maintain the value of MAXKEYLEN
* to be always 1 greater than the maximum length of any keyword in the
* list. The order no longer matters, except that placing the most
* frequent keys at the start of the list will speed up the processing of
* your source programs.
*)
keylist : ARRAY [1..nkeys] OF
STRING [maxkeylen] =
(
'Abs', 'Absolute', 'Addr', 'And', 'Append', 'Arc', 'Arctan', 'Array',
'Assign', 'AssignCRT', 'Begin', 'Bar', 'Bar3D', 'BlockRead', 'BlockWrite',
'Boolean', 'Byte', 'Case', 'Char', 'Chdir', 'Chr', 'Circle', 'ClearDevice',
'ClearViewport', 'Close', 'CloseGraph', 'ClrEOL', 'ClrScr', 'Comp',
'Concat', 'Const', 'Copy', 'Cos', 'CSeg', 'Dec', 'Delay', 'Delete',
'DelLine', 'DetectGraph', 'DiskFree', 'DiskSize', 'Dispose', 'Div', 'Do',
'DOSExitCode', 'Double', 'DownTo', 'DrawPoly', 'DSeg', 'Ellipse', 'Else',
'End', 'Eof', 'Eoln', 'Erase', 'Exec', 'Exit', 'Exp', 'Extended',
'External', 'False', 'File', 'FilePos', 'FileSize', 'FillChar', 'FillPoly',
'FindFirst', 'FindNext', 'FloodFill', 'Flush', 'For', 'Forward', 'Frac',
'FreeMem', 'Function', 'GetArcCoords', 'GetAspectRatio', 'GetBkColor',
'GetColor', 'GetDate', 'GetDir', 'GetFattr', 'GetFillPattern',
'GetFillSettings', 'GetFTime', 'GetGraphMode', 'GetImage', 'GetIntVec',
'GetLineSettings', 'GetMaxColor', 'GetMaxX', 'GetMaxY', 'GetMem',
'GetModeRange', 'GetPallette', 'GetPixel', 'GetTextSettings', 'GetTime',
'GetViewSettings', 'GetX', 'GetY', 'Goto', 'GotoXY', 'GraphDefaults',
'GraphErrorMesg', 'GraphResult', 'Halt', 'Hi', 'HighVideo', 'If',
'ImageSize', 'Implementation', 'In', 'Inc', 'InitGraph', 'InLine',
'Insert', 'InsLine', 'Int', 'Integer', 'Interface', 'Interrupt', 'Intr',
'IOResult', 'Keep', 'KeyPressed', 'Label', 'Length', 'Line', 'LineRel',
'LineTo', 'Ln', 'Lo', 'LongInt', 'LowVideo', 'Mark', 'MaxAvail',
'MemAvail', 'MkDir', 'Mod', 'Move', 'MoveRel', 'MoveTo', 'MSDOS', 'New',
'Nil', 'NormVideo', 'NoSound', 'Not', 'Odd', 'Of', 'Ofs', 'Or', 'Ord',
'OutText', 'OutTextXY', 'Packed', 'PackTime', 'ParamCount', 'ParamStr',
'Pi', 'PieSlice', 'Pointer', 'Pos', 'Pred', 'Procedure', 'Program', 'Ptr',
'PutImage', 'PutPixel', 'Random', 'Randomize', 'Read', 'ReadKey', 'ReadLn',
'Real', 'Record', 'Rectangle', 'RegisterBGIFont', 'RegisterBGIDriver',
'Registers', 'Release', 'Rename', 'Repeat', 'Reset', 'RestoreCRTMode',
'Rewrite', 'RmDir', 'Round', 'Seek', 'SeekEOF', 'SeekEOLn', 'Seg', 'Set',
'SetActivePage', 'SetAllPalette', 'SetBkColor', 'SetColor', 'SetDate',
'SetFAttr', 'SetFillPattern', 'SetFillStyle', 'SetFTime',
'SetGraphBufSize', 'SetGraphMode', 'SetIntVec', 'SetLineStyle',
'SetPalette', 'SetTextBuf', 'SetTextJustify', 'SetTextStyle', 'SetTime',
'SetUserCharSize', 'SetViewPort', 'SetVisualPage', 'ShL', 'ShortInt',
'ShR', 'Sin', 'Single', 'SizeOf', 'Sound', 'SPtr', 'Sqr', 'Sqrt', 'SSeg',
'Str', 'String', 'Succ', 'Swap', 'Text', 'TextBackground', 'TextColor',
'TextHeight', 'TextMode', 'TextWidth', 'Then', 'To', 'True', 'Trunc',
'Truncate', 'Type', 'Unit', 'UnpackTime', 'Until', 'UpCase', 'Uses', 'Val',
'Var', 'WhereX', 'WhereY', 'While', 'Window', 'With', 'Word', 'Write',
'WriteLn', 'Xor'
);
sizebuf = 65520;
(* If you want to conserve memory at the price of speed, you can reduce
* sizebuf to any amount down to 1 (not recommended), change the maximum
* index of mybuf to the value sizebuf-1, and recompile the program.
*)
TYPE
mybuf = ARRAY [0..65519] OF
CHAR;
KeyNode = RECORD
character : CHAR;
index : WORD;
sibling : POINTER;
child : POINTER;
END;
KeyNodePtr = ^KeyNode;
StringPtr = ^STRING;
UserNode = RECORD
character : CHAR;
instance : StringPtr;
sibling : POINTER;
child : POINTER;
END;
UserNodePtr = ^UserNode;
VAR
a, b (* Input and Output buffer pointers *)
: ^mybuf;
FirstKeyTreeLevel (* Using more space than absolutely necessary, for speed *)
: ARRAY [#0..#255] OF
KeyNode;
FirstUserTreeLevel (* Using more space than absolutely necessary, for speed *)
: ARRAY [#0..#255] OF
UserNode;
IndentationStack
: ARRAY [0..255] OF
WORD;
KeyStack
: ARRAY [0..255] OF
WORD;
istream, NormalizeKeysToUpperCase, ostream, showbrackcom, showparencom
: BOOLEAN;
ch, lastch
: CHAR;
infile, outfile
: FILE;
i, j, len
: INTEGER;
HeapPtr
: POINTER;
ext, filename, iname, CurrentIdentifier, oname, SearchIdent, path,
ReplacementIdent, ReplacementUpCaseIdent, s, UpCaseIdent
: STRING;
UPtr
: UserNodePtr;
col, ibegin, icase, iconst, ido, ielse, iend, ifunction, iif, ifor,
ilabel, iprocedure, iprogram, irecord, irepeat, ithen, itype, iuntil,
ivar, iwhile, iwith, ia, ib, iks, is, nread, nwrit, index, index1, iof,
lastindex
: WORD;
LABEL
findasterisk, out, start;
FUNCTION NewKeyNode (c : CHAR) : KeyNodePtr;
(* Returns a pointer to a newly constructed child-sibling node *)
VAR
p : KeyNodePtr;
BEGIN
NEW (p);
IF (p = NIL) THEN
BEGIN
WRITELN ('epb: out of memory');
RELEASE (HeapPtr);
HALT;
END;
p^.character := c;
p^.index := 0;
p^.sibling := NIL;
p^.child := NIL;
NewKeyNode := p;
END;
FUNCTION NewUserNode (c : CHAR) : UserNodePtr;
(* Returns a pointer to a newly constructed child-sibling node *)
VAR
p : UserNodePtr;
BEGIN
NEW (p);
IF (p = NIL) THEN
BEGIN
WRITELN ('epb: out of memory');
RELEASE (HeapPtr);
HALT;
END;
p^.character := c;
p^.instance := NIL;
p^.sibling := NIL;
p^.child := NIL;
NewUserNode := p;
END;
(* Initialize the first level for the child-sibling trees *)
PROCEDURE InitFirstTreeLevels;
VAR
i : WORD;
c : CHAR;
BEGIN
FOR i := 0 TO 255 DO
BEGIN
c := CHR (i);
IF ( (c >= 'A') AND (c <= 'Z') ) OR
(c = '_') THEN
FirstKeyTreeLevel [c] .character := c
ELSE
FirstKeyTreeLevel [c] .character := ' ';
FirstKeyTreeLevel [c] .index := 0;
FirstKeyTreeLevel [c] .sibling := NIL;
FirstKeyTreeLevel [c] .child := NIL;
IF ( (c >= 'A') AND (c <= 'Z') ) OR
(c = '_') THEN
FirstUserTreeLevel [c] .character := c
ELSE
FirstUserTreeLevel [c] .character := ' ';
FirstUserTreeLevel [c] .instance := NIL;
FirstUserTreeLevel [c] .sibling := NIL;
FirstUserTreeLevel [c] .child := NIL;
END;
END; (* InitFirstLevels *)
PROCEDURE InsertKeyTree (s : STRING;
slot : INTEGER);
(* Inserts a string in the Pascal Keyword Tree *)
VAR
uc : CHAR;
i, len : WORD;
p : KeyNodePtr;
LABEL
loop;
BEGIN
len := LENGTH (s);
IF (len = 0) THEN (* There is nothing to insert *)
EXIT;
uc := UPCASE (s [1]);
IF (uc <> FirstKeyTreeLevel [uc] .character) THEN
FirstKeyTreeLevel [uc] .character := uc;
IF (len = 1) THEN
BEGIN
FirstKeyTreeLevel [uc] .index := slot;
EXIT;
END;
i := 2;
p := FirstKeyTreeLevel [uc] .child;
IF (p = NIL) THEN (* If the first child does not exist, create it *)
BEGIN
p := NewKeyNode (UPCASE (s [2]) );
FirstKeyTreeLevel [uc] .child := p;
END;
loop :
IF (UPCASE (s [i]) = p^.character) THEN
BEGIN
IF (i = len) THEN (* Indicate the termination of the string *)
BEGIN
IF (p^.index = 0) THEN
p^.index := slot;
EXIT;
END;
(* Assert: i < len *)
INC (i);
IF (p^.child = NIL) THEN
p^.child := NewKeyNode (UPCASE (s [i]) );
p := p^.child;
GOTO loop;
END
ELSE
BEGIN
IF (p^.sibling = NIL) THEN
p^.sibling := NewKeyNode (UPCASE (s [i]) );
p := p^.sibling;
GOTO loop;
END;
END; (* InsertKeyTree *)
FUNCTION SearchKeyTree (s : STRING) : INTEGER;
(* Determines whether or not a string is in the Pascal Keyword Tree *)
(* Returns an index to the keylist[] element on success, a 0 on failure *)
VAR
i, len : INTEGER;
p : KeyNodePtr;
LABEL
loop;
BEGIN
len := LENGTH (s);
IF (len = 0) THEN (* Should a null string be considered to be present? *)
BEGIN
SearchKeyTree := 0; (* In this program, no *)
EXIT;
END;
IF (s [1] <> FirstKeyTreeLevel [s [1] ] .character) THEN
BEGIN
SearchKeyTree := 0; (* Because the length of the string is >= 1 *)
EXIT;
END;
IF (len = 1) THEN
BEGIN
IF (FirstKeyTreeLevel [s [1] ] .index = 0) THEN
SearchKeyTree := 0
ELSE
SearchKeyTree := FirstKeyTreeLevel [s [1] ] .index;
EXIT;
END;
i := 2;
p := FirstKeyTreeLevel [s [1] ] .child;
IF (p = NIL) THEN
BEGIN
SearchKeyTree := 0; (* Because the tree terminated early *)
EXIT;
END;
loop :
IF (s [i] = p^.character) THEN
BEGIN
IF (i = len) THEN (* Stop searching *)
BEGIN
IF (p^.index = 0) THEN
SearchKeyTree := 0
ELSE
SearchKeyTree := p^.index;
EXIT;
END;
(* Assert: i < len *)
p := p^.child;
IF (p = NIL) THEN
BEGIN
SearchKeyTree := 0; (* Because the tree terminated early *)
EXIT;
END;
INC (i);
GOTO loop;
END
ELSE
BEGIN
p := p^.sibling;
IF (p = NIL) THEN
BEGIN
SearchKeyTree := 0; (* Because the tree terminated early *)
EXIT;
END;
GOTO loop;
END;
END; (* SearchKeyTree *)
PROCEDURE InsertUserTree (s : STRING);
(* Inserts a string in the User Identifier Tree *)
VAR
uc : CHAR;
i, len : WORD;
p : UserNodePtr;
LABEL loop;
BEGIN
len := LENGTH (s);
IF (len = 0) THEN (* There is nothing to insert *)
EXIT;
uc := UPCASE (s [1]);
IF (uc <> FirstUserTreeLevel [uc] .character) THEN
FirstUserTreeLevel [uc] .character := uc;
IF (len = 1) THEN
BEGIN
GETMEM (FirstUserTreeLevel [uc] .instance, 2); (* 1 for the length indicator, 1 for the string *)
FirstUserTreeLevel [uc] .instance^ := s;
EXIT;
END;
i := 2;
p := FirstUserTreeLevel [uc] .child;
IF (p = NIL) THEN (* If the first child does not exist, create it *)
BEGIN
p := NewUserNode (UPCASE (s [2]) );
FirstUserTreeLevel [uc] .child := p;
END;
loop :
IF (UPCASE (s [i]) = p^.character) THEN
BEGIN
IF (i = len) THEN (* Indicate the termination of the string *)
BEGIN
IF (p^.instance = NIL) THEN
BEGIN
GETMEM (p^.instance, 1 + len);
p^.instance^ := s;
END;
EXIT;
END;
(* Assert: i < len *)
INC (i);
IF (p^.child = NIL) THEN
p^.child := NewUserNode (UPCASE (s [i]) );
p := p^.child;
GOTO loop;
END
ELSE
BEGIN
IF (p^.sibling = NIL) THEN
p^.sibling := NewUserNode (UPCASE (s [i]) );
p := p^.sibling;
GOTO loop;
END;
END; (* InsertUserTree *)
FUNCTION SearchUserTree (s : STRING) : UserNodePtr;
(* Determines whether or not a string is in the User Identifier Tree *)
(* Returns a pointer to the final node on success, a NIL pointer on failure *)
VAR
i, len : INTEGER;
p : UserNodePtr;
LABEL
loop;
BEGIN
len := LENGTH (s);
IF (len = 0) THEN (* Should a null string be considered to be present? *)
BEGIN
SearchUserTree := NIL; (* In this program, no *)
EXIT;
END;
IF (s [1] <> FirstUserTreeLevel [s [1] ] .character) THEN
BEGIN
SearchUserTree := NIL; (* Because the length of the string is >= 1 *)
EXIT;
END;
IF (len = 1) THEN
BEGIN
IF (FirstUserTreeLevel [s [1] ] .instance = NIL) THEN
SearchUserTree := NIL
ELSE
SearchUserTree := @FirstUserTreeLevel [s [1] ];
EXIT;
END;
i := 2;
p := FirstUserTreeLevel [s [1] ] .child;
IF (p = NIL) THEN
BEGIN
SearchUserTree := NIL; (* Because the tree terminated early *)
EXIT;
END;
loop :
IF (s [i] = p^.character) THEN
BEGIN
IF (i = len) THEN (* Stop searching *)
BEGIN
IF (p^.instance = NIL) THEN
SearchUserTree := NIL
ELSE
SearchUserTree := p;
EXIT;
END;
(* Assert: i < len *)
p := p^.child;
IF (p = NIL) THEN
BEGIN
SearchUserTree := NIL; (* Because the tree terminated early *)
EXIT;
END;
INC (i);
GOTO loop;
END
ELSE
BEGIN
p := p^.sibling;
IF (p = NIL) THEN
BEGIN
SearchUserTree := NIL; (* Because the tree terminated early *)
EXIT;
END;
GOTO loop;
END;
END; (* SearchUserTree *)
{$F+}
FUNCTION HeapFunc (size : WORD) : INTEGER; {$F-}
BEGIN
HeapFunc := 1; (* Make NEW return a NIL pointer when out of memory *)
END;
PROCEDURE PushIndent (indent : WORD);
BEGIN
IF (is < 256) THEN
BEGIN
INC (is);
IndentationStack [is] := IndentationStack [is - 1] + indent;
END;
END;
PROCEDURE PopIndent;
BEGIN
IF (is > 0) THEN
DEC (is);
END;
PROCEDURE PushKey (key : WORD);
BEGIN
IF (iks < 256) THEN
BEGIN
INC (iks);
KeyStack [iks] := key;
END;
END;
PROCEDURE PopKey;
BEGIN
IF (iks > 0) THEN
DEC (iks);
END;
PROCEDURE writeblock;
BEGIN
BLOCKWRITE (outfile, b^, ib, nwrit);
IF (nwrit <> ib) AND (oname <> '') THEN (* Don't check output to STDOUT *)
BEGIN
WRITELN ('epb: Cannot finish outputting (out of disk space?)');
CLOSE (outfile);
RELEASE (HeapPtr);
HALT;
END;
ib := 0;
END; (* writeblock *)
PROCEDURE getblock;
BEGIN
ia := 0;
BLOCKREAD (infile, a^, sizebuf, nread);
IF (nread = 0) THEN
BEGIN
writeblock;
CLOSE (infile);
RELEASE (HeapPtr);
HALT;
END;
END; (* getblock *)
PROCEDURE OutPaddedChar (c : CHAR); (* Output a character, possibly w/ padding *)
BEGIN
CASE c OF
'[', '(', '<', '+', '/', '*', '-', ':' :
IF (lastch <> #32) THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
'=' :
IF (lastch > #32) AND
(lastch <> ':') AND (lastch <> '<') AND (lastch <> '>') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
'>' :
IF (lastch > #32) AND
(lastch <> '<') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
')' :
IF (lastch = ')') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
ELSE (* case c *)
IF (c > #32) THEN
CASE lastch OF
':' :
IF (c <> '=') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
'<' :
IF (c <> '>') AND (c <> '=') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
'>' :
IF (c <> '=') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
')' :
IF (c <> ';') AND (c <> ',') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
'=', '+', '/', '*', '-', ',' :
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
']' :
IF (c <> ')') AND (c <> ';') AND (c <> ',') AND (c <> '^') THEN
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
END; (* case lastch *)
END; (* case c *)
b^ [ib] := c;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
lastch := c;
END; (* OutPaddedChar *)
PROCEDURE OutLiteralChar (c : CHAR); (* Output a character without padding *)
BEGIN
b^ [ib] := c;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
lastch := c;
END; (* OutLiteralChar *)
PROCEDURE OutIdent (s : STRING); (* Output an identifier *)
VAR
i, len
: INTEGER;
BEGIN
len := LENGTH (s);
IF (len <> 0) THEN
OutPaddedChar (s [1]);
FOR i := 2 TO len DO
BEGIN
b^ [ib] := s [i];
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END;
lastch := s [len];
END; (* OutIdent *)
(* Split up a Path, Filename, Extension string *)
PROCEDURE SplitPFE (pf : STRING;
VAR p : STRING;
VAR f : STRING;
VAR e : STRING);
VAR i : INTEGER;
BEGIN
p := '';
f := '';
e := '';
i := LENGTH (pf);
WHILE ( (POS (COPY (pf, i, 1), ':/\') = 0) AND (i > 0) ) DO
DEC (i);
p := COPY (pf, 1, i);
f := COPY (pf, i + 1, 255);
i := POS ('.', f);
IF (i > 0) THEN
BEGIN
e := COPY (f, i + 1, 3);
f := COPY (f, 1, i);
END;
END;
PROCEDURE breakline;
BEGIN
b^ [ib] := #13;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
b^ [ib] := #10;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
lastch := #10;
col := 0;
END;
PROCEDURE skipwhitespace;
BEGIN
WHILE (a^ [ia] < #33) DO
BEGIN
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
END; (* skipwhitespace *)
PROCEDURE skipspace;
BEGIN
WHILE (a^ [ia] < #33) AND (a^ [ia] <> #13) AND (a^ [ia] <> #10) DO
BEGIN
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
END; (* skipspace *)
PROCEDURE indent;
VAR i : WORD;
BEGIN
FOR i := 1 TO IndentationStack [is] DO
BEGIN
b^ [ib] := #32;
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
END;
IF (IndentationStack [is] <> 0) THEN (* Keep track of the current column *)
BEGIN
col := col + IndentationStack [is];
lastch := #32;
END;
END; (* indent *)
PROCEDURE condbreakline;
VAR
ch : CHAR;
s : STRING;
i, len : WORD;
BEGIN
ch := a^ [ia];
IF (ch <> #13) THEN
BEGIN
s := '';
WHILE (a^ [ia] < #33) AND (a^ [ia] <> #13) AND (a^ [ia] <> #10) DO
BEGIN
s := s + a^ [ia]; (* Save spaces *)
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
len := LENGTH (s);
ch := a^ [ia];
IF (ch = '(') OR (ch = '{') THEN
FOR i := 1 TO len DO (* Write saved spaces *)
BEGIN
b^ [ib] := s [i];
INC (ib);
IF (ib = sizebuf) THEN
writeblock;
INC (col);
END
ELSE
breakline;
END;
END; (* condbreakline *)
{---- MAIN PROGRAM ----}
BEGIN
IF (PARAMCOUNT = 0) THEN
BEGIN
WRITELN (#10'ED''S PASCAL BEAUTIFIER v2.33, Copyright 1992 by Edward Lee, -Ed L');
WRITELN ('edlee@chinet.chi.il.us THIS PROGRAM MAY NOT BE DISTRIBUTED FOR PROFIT');
WRITELN (#10'EPB normalizes the indentation of (Turbo) Pascal source code, including');
WRITELN ('nested IF THEN ELSE constructs, and normalizes the capitalization of');
WRITELN ('(Turbo) Pascal identifiers to either upper case or mixed case, defaulting');
WRITELN ('to upper case. Each non-(Turbo) Pascal identifier has its capitalization');
WRITELN ('normalized to the way it first appears in the input stream. EPB can');
WRITELN ('do identifier substitutions by ignoring comments, sub-strings, and literal');
WRITELN ('strings. An input file, if specified, is renamed to *.BAK before execution.');
WRITELN ('This program, EPB, is provided without warranty. Use EPB at your own risk.');
WRITELN (#10'INVOCATION (items in brackets are optional):');
WRITELN (' epb [-bimop] [InputFile[.PAS]] [OutputFile[.PAS]] [-s Original Replacement]');
WRITELN (#10'OPTIONS (flexible in case, grouping, and positioning on the command line):');
WRITELN (' -b Shut off the output of Bracket comments: { ... }');
WRITELN (' -p Shut off the output of Parentheses comments: (* ... *)');
WRITELN (' -i Use the standard Input (STDIN) stream for input instead of InputFile');
WRITELN (' -o Use the standard Output (STDOUT) stream for output instead of OutputFile');
WRITELN (' -m Normalize all keywords to Mixed case rather than the default upper case');
WRITELN (' -s Substitute all occurrences of an Original identifier with a Replacement');
HALT;
END;
InitFirstTreeLevels;
(* Copy keylist[] in a normalized form to the Key Tree *)
FOR i := 1 TO nkeys DO
InsertKeyTree (keylist [i], i);
showparencom := TRUE;
showbrackcom := TRUE;
istream := FALSE;
ostream := FALSE;
NormalizeKeysToUpperCase := TRUE;
SearchIdent := '';
ReplacementIdent := '';
ReplacementUpCaseIdent := '';
i := 0;
WHILE (i < PARAMCOUNT) DO (* Process options *)
BEGIN
INC (i);
s := PARAMSTR (i);
IF (s [1] = '-') THEN
BEGIN
IF (POS ('b', s) > 0) OR (POS ('B', s) > 0) THEN
showbrackcom := FALSE;
IF (POS ('p', s) > 0) OR (POS ('P', s) > 0) THEN
showparencom := FALSE;
IF (POS ('i', s) > 0) OR (POS ('I', s) > 0) THEN
istream := TRUE;
IF (POS ('o', s) > 0) OR (POS ('O', s) > 0) THEN
ostream := TRUE;
IF (POS ('m', s) > 0) OR (POS ('M', s) > 0) THEN
NormalizeKeysToUpperCase := FALSE;
IF (POS ('s', s) > 0) OR (POS ('S', s) > 0) THEN
BEGIN
INC (i);
SearchIdent := PARAMSTR (i);
INC (i);
ReplacementIdent := PARAMSTR (i);
IF (i > PARAMCOUNT) THEN
BEGIN
WRITELN ('epb: Error. The -s option has been used without enough parameters.');
HALT;
END;
END; (* if (pos ('s' ... *)
END; (* if (s [1] ... *)
END; (* while *)
(* Normalize Original and Replacement strings via upper case function *)
FOR i := 1 TO LENGTH (SearchIdent) DO
SearchIdent [i] := UPCASE (SearchIdent [i]);
FOR i := 1 TO LENGTH (ReplacementIdent) DO
ReplacementUpCaseIdent := ReplacementUpCaseIdent + UPCASE (ReplacementIdent [i]);
iname := '';
oname := '';
IF NOT (istream AND ostream) THEN
BEGIN
i := 0;
WHILE (i < PARAMCOUNT) DO (* Get filename(s) *)
BEGIN
INC (i);
s := PARAMSTR (i);
IF (s [1] <> '-') THEN (* Skip option flags *)
BEGIN
IF (istream) THEN (* Input is from STDIN *)
BEGIN
oname := s;
GOTO out;
END
ELSE
IF (ostream) THEN (* Output is to STDOUT *)
BEGIN
iname := s;
GOTO out;
END
ELSE
IF (iname = '') THEN (* Input is from infile *)
iname := s
ELSE
BEGIN
oname := s; (* Output is to outfile *)
GOTO out;
END;
END (* if (s [1] ... *)
ELSE
IF (POS ('s', s) > 0) OR (POS ('S', s) > 0) THEN
i := i + 2;
END; (* while *)
END; (* if not *)
out :
SplitPFE (iname, path, filename, ext);
IF (filename <> '') THEN
IF (COPY (filename, LENGTH (filename), 1) <> '.') THEN
BEGIN
filename := filename + '.';
iname := path + filename + 'PAS';
END;
s := path + filename + 'BAK';
SplitPFE (oname, path, filename, ext);
IF (filename <> '') THEN
IF (COPY (filename, LENGTH (filename), 1) <> '.') THEN
oname := path + filename + '.PAS';
IF (iname <> '') THEN
IF (iname = oname) OR
( (oname = '') AND NOT ostream) THEN
BEGIN
ASSIGN (infile, s); (* If a backup file already exists, erase it *)
{$I-}
RESET (infile, 1); {$I+}
IF (IORESULT = 0) THEN
BEGIN
CLOSE (infile);
ERASE (infile);
END;
ASSIGN (infile, iname);
{$I-}
RESET (infile, 1); {$I+}
IF (IORESULT = 0) THEN
BEGIN
CLOSE (infile);
RENAME (infile, s);
END
ELSE
BEGIN
WRITELN ('epb: Cannot rename original file, ', iname, ', to ', s, '.');
HALT;
END;
oname := iname;
iname := s;
END;
ASSIGN (infile, iname);
{$I-}
RESET (infile, 1); {$I+}
IF (IORESULT <> 0) THEN
BEGIN
WRITELN ('epb: Cannot open input file, ', iname);
HALT;
END;
ASSIGN (outfile, oname);
{$I-}
REWRITE (outfile, 1); {$I+}
IF (IORESULT <> 0) THEN
BEGIN
WRITELN ('epb: Error opening output file, ', oname, '. DOS file limit reached?');
HALT;
END;
HeapError := @HeapFunc;
MARK (HeapPtr);
NEW (a);
NEW (b);
getblock;
IF (a = NIL) OR (b = NIL) THEN
BEGIN
WRITELN ('epb: There is not enough free conventional memory for epb to run.');
RELEASE (HeapPtr);
HALT;
END;
col := 0;
ib := 0;
iks := 0;
KeyStack [iks] := 0;
is := 0;
IndentationStack [is] := 0;
index := 0;
lastch := #0;
CurrentIdentifier := '';
UpCaseIdent := '';
(* Soft-coded indexes to some keywords of interest *)
ibegin := SearchKeyTree ('BEGIN');
icase := SearchKeyTree ('CASE');
iconst := SearchKeyTree ('CONST');
ido := SearchKeyTree ('DO');
iend := SearchKeyTree ('END');
ifor := SearchKeyTree ('FOR');
ifunction := SearchKeyTree ('FUNCTION');
iif := SearchKeyTree ('IF');
ithen := SearchKeyTree ('THEN');
ielse := SearchKeyTree ('ELSE');
ilabel := SearchKeyTree ('LABEL');
iof := SearchKeyTree ('OF');
iprocedure := SearchKeyTree ('PROCEDURE');
iprogram := SearchKeyTree ('PROGRAM');
irecord := SearchKeyTree ('RECORD');
irepeat := SearchKeyTree ('REPEAT');
itype := SearchKeyTree ('TYPE');
iuntil := SearchKeyTree ('UNTIL');
ivar := SearchKeyTree ('VAR');
iwhile := SearchKeyTree ('WHILE');
iwith := SearchKeyTree ('WITH');
skipwhitespace;
PushKey (iprogram);
start :
ch := a^ [ia];
IF (lastch = #10) THEN
BEGIN
col := 0;
skipspace;
ch := a^ [ia];
IF ( (ch < 'A') OR (ch > 'Z') ) AND
( (ch < 'a') OR (ch > 'z') ) AND
(ch <> '_') THEN
indent;
END;
CASE ch OF
';' :
BEGIN
OutLiteralChar (ch);
INC (ia);
IF (ia >= nread) THEN
getblock;
IF (KeyStack [iks] = iuntil) THEN
BEGIN
PopIndent;
PopKey;
END;
condbreakline;
WHILE (KeyStack [iks] = ido) DO
BEGIN
PopIndent;
PopKey;
END;
WHILE (KeyStack [iks] = ithen) OR (KeyStack [iks] = ielse) DO
BEGIN
PopIndent;
PopKey;
END;
GOTO start;
END; (* ';' *)
#39 : (* Do not process the contents of literal strings *)
BEGIN
OutPaddedChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
WHILE (a^ [ia] <> #39) DO
BEGIN
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
GOTO start;
END; (* ' *)
'{' : (* Do not process the contents of { ... } comments *)
BEGIN
IF (showbrackcom) THEN
BEGIN
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
WHILE (a^ [ia] <> '}') DO
BEGIN
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
END
ELSE
BEGIN
INC (ia);
IF (ia >= nread) THEN
getblock;
WHILE (a^ [ia] <> '}') DO
BEGIN
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
IF (a^ [ia] <> #13) THEN
BEGIN
breakline;
skipspace;
END;
GOTO start;
END; (* {} *)
'(' : { Do not process the contents of (* ... *) comments }
BEGIN
INC (ia);
IF (ia >= nread) THEN
getblock;
IF (a^ [ia] <> '*') THEN
BEGIN
OutPaddedChar (ch);
GOTO start;
END
ELSE (* A comment has begun *)
BEGIN
IF (showparencom) THEN
BEGIN
OutLiteralChar (ch);
OutLiteralChar (a^ [ia]);
END;
INC (ia);
IF (ia >= nread) THEN
getblock;
IF (showparencom) THEN
OutLiteralChar (a^ [ia]);
findasterisk :
WHILE (a^ [ia] <> '*') DO
BEGIN
INC (ia);
IF (ia >= nread) THEN
getblock;
IF (showparencom) THEN
OutLiteralChar (a^ [ia]);
END; (* a^[ia] = '*' *)
INC (ia);
IF (ia >= nread) THEN
getblock;
IF (showparencom) THEN
OutLiteralChar (a^ [ia]);
IF (a^ [ia] <> ')') THEN
GOTO findasterisk;
INC (ia);
IF (ia >= nread) THEN
getblock;
IF (a^ [ia] <> #13) THEN
BEGIN
breakline;
skipspace;
END;
GOTO start;
END;
END; { (* *) }
'A'..'Z', 'a'..'z', '_' : (* Collect and process identifiers *)
BEGIN
REPEAT
UpCaseIdent := UpCaseIdent + UPCASE (ch);
CurrentIdentifier := CurrentIdentifier + ch;
INC (ia);
IF (ia >= nread) THEN
getblock;
ch := a^ [ia];
UNTIL ( (ch < 'A') OR (ch > 'Z') ) AND
( (ch < 'a') OR (ch > 'z') ) AND
( (ch < '0') OR (ch > '9') ) AND
(ch <> '_'); {Turbo Pascal Sets are too slow}
(*
if (upcaseident = 'FOOZ') then
begin
writeln('{');
writeln('iks = ', iks);
writeln('keystack[iks] = ', keystack[iks]);
if (keystack[iks] > 0) then
writeln('keylist[keystack[iks]] = ', keylist[keystack[iks]]);
writeln('is = ', is);
writeln('indentationstack[is] = ', indentationstack[is]);
writeln('col = ', col);
writeln('}');
end;
*)
IF (UpCaseIdent = SearchIdent) THEN
BEGIN
UpCaseIdent := ReplacementUpCaseIdent;
CurrentIdentifier := ReplacementIdent;
END;
lastindex := index;
index := SearchKeyTree (UpCaseIdent);
IF (index = iend) THEN
BEGIN
PopIndent;
IF (KeyStack [iks] = icase) THEN
BEGIN
PopKey;
IF (KeyStack [iks] = irecord) THEN
PopIndent;
END;
END
ELSE
IF (index = iuntil) THEN
BEGIN
PopIndent;
PopKey;
END
ELSE
IF (index = ielse) AND (KeyStack [iks] = icase) THEN
PopIndent
ELSE
IF (KeyStack [iks] = iprogram) OR
(KeyStack [iks] = iprocedure) OR
(KeyStack [iks] = ifunction) THEN
BEGIN
IF (index = ivar) OR
(index = iconst) OR
(index = itype) OR
(index = iprocedure) OR
(index = ifunction) OR
(index = ilabel) THEN
PopIndent
ELSE
IF (index = ibegin) THEN
BEGIN
PopIndent;
PopKey;
END;
END;
IF (lastch = #10) THEN
indent;
(* Output Identifier *)
IF (index <> 0) THEN
IF (NormalizeKeysToUpperCase) THEN
OutIdent (UpCaseIdent)
ELSE
OutIdent (keylist [index])
ELSE
BEGIN
UPtr := SearchUserTree (UpCaseIdent);
IF (UPtr <> NIL) THEN
OutIdent (UPtr^.instance^)
ELSE
BEGIN
InsertUserTree (CurrentIdentifier);
OutIdent (CurrentIdentifier);
END;
END;
IF (index = iend) THEN
BEGIN
IF (KeyStack [iks] = ibegin) THEN
BEGIN
PopKey;
WHILE (KeyStack [iks] = ido) DO
BEGIN
PopIndent;
PopKey;
END;
WHILE (KeyStack [iks] = ielse) DO
BEGIN
PopIndent;
PopKey;
END;
IF (KeyStack [iks] = ithen) THEN
BEGIN
PopIndent;
PopKey;
END;
IF (KeyStack [iks] = iprocedure) OR (KeyStack [iks] = ifunction) THEN
PopKey;
END
ELSE
IF (KeyStack [iks] = irecord) THEN
BEGIN
PopIndent;
PopKey;
END;
END
ELSE
IF (lastindex = ido) AND
(index <> ibegin) AND
(index <> iif) AND
(index <> ifor) AND
(index <> irepeat) AND
(index <> iwhile) AND
(index <> icase) THEN
BEGIN
REPEAT
PopIndent;
PopKey;
UNTIL (KeyStack [iks] <> ido);
WHILE (KeyStack [iks] = ielse) DO
BEGIN
PopIndent;
PopKey;
END;
IF (KeyStack [iks] = ithen) THEN
BEGIN
PopIndent;
PopKey;
END;
END
ELSE
IF (lastindex = ielse) AND
(index <> ibegin) AND
(index <> iif) AND
(index <> ifor) AND
(index <> irepeat) AND
(index <> iwhile) AND
(index <> icase) AND
(index <> iwith) THEN
BEGIN
REPEAT
PopIndent;
PopKey;
UNTIL (KeyStack [iks] <> ielse);
IF (KeyStack [iks] = ithen) THEN
BEGIN
PopIndent;
PopKey;
END;
END
ELSE
IF (lastindex = ithen) AND
(index <> ibegin) AND
(index <> iif) AND
(index <> ifor) AND
(index <> irepeat) AND
(index <> iwhile) AND
(index <> icase) AND
(index <> iwith) THEN
BEGIN
PopIndent;
PopKey;
END;
IF (index = ibegin) OR
(index = ithen) OR
(index = ielse) OR
(index = ido) OR
(index = irepeat) THEN
condbreakline;
IF (index = ibegin) THEN
BEGIN
IF (is > 0) THEN
PushIndent (BeginIndent)
ELSE
PushIndent (LeftmostBeginIndent);
PushKey (ibegin);
END
ELSE
IF (index = iif) THEN
PushIndent (IfIndent)
ELSE
IF (index = ithen) THEN
PushKey (ithen)
ELSE
IF (index = ielse) THEN
BEGIN
IF (KeyStack [iks] <> icase) THEN
BEGIN
PushIndent (ElseIndent);
PushKey (ielse);
END
ELSE
PushIndent (CaseIndent)
END
ELSE
IF (index = iwhile) THEN
PushIndent (WhileIndent)
ELSE
IF (index = ifor) THEN
PushIndent (ForIndent)
ELSE
IF (index = ido) THEN
PushKey (ido)
ELSE
IF (index = irepeat) THEN
BEGIN
PushIndent (RepeatIndent);
PushKey (irepeat);
END
ELSE
IF (index = iuntil) THEN
BEGIN
PushIndent (UntilIndent);
PushKey (iuntil);
END
ELSE
IF (index = iconst) THEN
PushIndent (ConstIndent)
ELSE
IF (index = itype) THEN
PushIndent (TypeIndent)
ELSE
IF (index = ivar) THEN
PushIndent (VarIndent)
ELSE
IF (index = irecord) THEN
BEGIN
PushIndent (col - 6 - IndentationStack [is]);
PushIndent (RecordIndent);
PushKey (irecord);
condbreakline;
END
ELSE
IF (index = iprocedure) THEN
BEGIN
PushIndent (ProcedureIndent);
PushKey (iprocedure);
END
ELSE
IF (index = ifunction) THEN
PushKey (ifunction)
ELSE
IF (index = ilabel) THEN
PushIndent (LabelIndent)
ELSE
IF (index = icase) THEN
BEGIN
PushIndent (CaseIndent);
PushKey (icase);
END
ELSE
IF (index = iof) THEN
condbreakline
ELSE
IF (index = iwith) THEN
PushIndent (WithIndent);
CurrentIdentifier := '';
UpCaseIdent := '';
GOTO start;
END; (* 'A'..'Z', 'a'..'z', '_' *)
'0'..'9' : (* Process decimal integer or real constants *)
BEGIN
OutPaddedChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
WHILE ( (a^ [ia] >= '0') AND (a^ [ia] <= '9') ) OR
(a^ [ia] = '.') DO
BEGIN
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
IF (a^ [ia] = 'e') OR (a^ [ia] = 'E') THEN
BEGIN
OutLiteralChar ('e');
INC (ia); (* Go to the next character *)
IF (ia >= nread) THEN
getblock;
OutLiteralChar (a^ [ia]); (* Output the sign or digit or (?) *)
INC (ia); (* Go to the next character *)
IF (ia >= nread) THEN
getblock;
IF ( (lastch >= '0') AND (lastch <= '9') ) OR
(lastch = '-') OR
(lastch = '+') THEN
WHILE ( (a^ [ia] >= '0') AND (a^ [ia] <= '9') ) OR
(a^ [ia] = '.') DO
BEGIN
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
END; (* if *)
GOTO start;
END; (* '0'..'9' *)
'$' : (* Process hexadecimal constants, specific to Turbo Pascal *)
BEGIN
OutPaddedChar ('$');
INC (ia);
IF (ia >= nread) THEN
getblock;
WHILE ( (a^ [ia] >= 'a') AND (a^ [ia] <= 'f') ) OR
( (a^ [ia] >= 'A') AND (a^ [ia] <= 'F') ) OR
( (a^ [ia] >= '0') AND (a^ [ia] <= '9') ) DO
BEGIN
OutLiteralChar (a^ [ia]);
INC (ia);
IF (ia >= nread) THEN
getblock;
END;
GOTO start;
END; (* '$' *)
ELSE
BEGIN
OutPaddedChar (ch);
INC (ia);
IF (ia >= nread) THEN
getblock;
GOTO start;
END;
END; (* CASE ch *)
END.