fpc/utils/ptopu.pp
2000-07-13 10:16:21 +00:00

1244 lines
36 KiB
ObjectPascal

Unit PtoPu;
{
$Id$
This file is part of the Free Pascal run time library.
Copyright (c) 1999-2000 by Michael Van Canneyt, member of
the Free Pascal development team
Pascal Pretty-Printer object implementation
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{
This unit is based heavily on the code by
Author: Peter Grogono
This program is based on a Pascal pretty-printer written by Ledgard,
Hueras, and Singer. See SIGPLAN Notices, Vol. 12, No. 7, July 1977,
pages 101-105, and PP.DOC/HLP.
This version of PP developed under Pascal/Z V4.0 or later.
Very minor modifications for Turbo Pascal made by Willett Kempton
March 1984 and Oct 84. Runs under 8-bit Turbo or 16-bit Turbo.
Toad Hall tweak, rewrite for TP 5, 28 Nov 89
The following was changed :
- Object oriented
- Uses streams
- Run-time customizable.
}
Interface
Uses objects;
Const
MAXSYMBOLSIZE = 80;
MAXSTACKSIZE = 100;
MAXKEYLENGTH = 15; { The longest keyword is PROCEDURE }
MAXLINESIZE = 90; { Maximum length of output line }
TYPE
Token = String[MAXSYMBOLSIZE];
String0 = STRING[1]; {Pascal/z had 0}
FileName = STRING;
{ Keysymbols }
{ If you add keysyms, adjust the definition of lastkey }
keysymbol = { keywords }
(endsym,beginsym,ifsym,thensym,elsesym,procsym,varsym,ofsym,
whilesym,dosym,casesym,withsym,forsym,repeatsym,untilsym,
funcsym,labelsym,constsym,typesym,recordsym,stringsym,progsym,
{ TP and Delphi keywords}
asmsym, trysym, finallysym,exceptsym,raisesym,classsym,objectsym,
constructorsym,destructorsym,inheritedsym,propertysym,
privatesym,publicsym,protectedsym,publishedsym,
initializationsym,finalizationsym,
inlinesym,librarysym,interfacesym,implementationsym,
readsym,writesym,unitsym,
{ Not used for formatting }
andsym,arrsym,divsym,downsym,filesym,gotosym,insym,modsym,
notsym,nilsym,orsym,setsym,tosym,
casevarsym,
{ other symbols }
becomes,delphicomment,opencomment,closecomment,semicolon,colon,equals,
openparen,closeparen,period,endoffile,othersym);
{ Formatting options }
{ If you add options, adjust the definition of lastopt }
options = (crsupp,crbefore,blinbefore,
dindonkey,dindent,spbef,
spaft,gobsym,inbytab,crafter,upper,lower,capital);
optionset = SET OF options;
keysymset = SET OF keysymbol;
tableentry = RECORD
selected : optionset;
dindsym : keysymset;
terminators : keysymset
END;
{ Character identification }
charname = (letter,digit,space,quote,endofline,
filemark,otherchar);
charinfo = RECORD
name : charname;
Value : CHAR
END;
symbol = RECORD
name : keysymbol;
Value : Token;
IsKeyWord : BOOLEAN;
length, spacesbefore, crsbefore : INTEGER;
END;
symbolinfo = ^ symbol;
stackentry = RECORD
indentsymbol : keysymbol;
prevmargin : INTEGER
END;
symbolstack = ARRAY [1..MAXSTACKSIZE] OF stackentry;
Const FirstOpt = crsupp;
LastOpt = capital; { Adjust this if you add options }
FirstKey = endsym;
LastKey = othersym; { Adjust this if you add options }
LastFormatsym = tosym;
Type
tableptr = ^tableentry;
optiontable = ARRAY [keysymbol] OF tableptr;
OEntriesTable = Array [keysymbol] OF String[15];
ONamesTable = Array [Options] of String[15];
KeywordTable = ARRAY [endsym..lastFormatsym] OF String[MAXKEYLENGTH];
SpecialChar = ARRAY [1..2] OF CHAR;
dblcharset = SET OF endsym..othersym;
DblCharTable = ARRAY [becomes..opencomment] OF SpecialChar;
SglCharTable = ARRAY [opencomment..period] OF CHAR;
TPrettyPrinter=Object(TObject)
Private
RecordSeen,
CRPending : BOOLEAN;
currchar,nextchar : charinfo;
currsym,nextsym : symbolinfo;
inlines,outlines : INTEGER;
stack : symbolstack;
top,startpos,currlinepos,currmargin : Integer;
option : OptionTable;
Procedure Verbose (Const Msg : String);
Procedure GetChar;
Procedure StoreNextChar(VAR lngth: INTEGER;
VAR Value: Token);
Procedure SkipBlanks(VAR spacesbefore, crsbefore: INTEGER);
Procedure GetComment(sym: symbolinfo);
Procedure GetDelphiComment(sym: symbolinfo);
Procedure GetNumber(sym: symbolinfo);
Procedure GetCharLiteral(sym: symbolinfo);
Function char_Type: keysymbol;
Procedure GetSpecialChar(sym: symbolinfo);
Procedure GetNextSymbol(sym: symbolinfo);
Procedure GetIdentifier(sym: symbolinfo);
Procedure GetSymbol;
Procedure PopStack(VAR indentsymbol: keysymbol;
VAR prevmargin: INTEGER);
Procedure PushStack(indentsymbol: keysymbol;
prevmargin: INTEGER );
Procedure WriteCRs(numberofcrs: INTEGER);
Procedure InsertCR;
Procedure InsertBlankLine;
Procedure LShiftOn(dindsym: keysymset);
Procedure LShift;
Procedure InsertSpace(VAR symbol: symbolinfo);
Procedure MoveLinePos(newlinepos: INTEGER);
Procedure PrintSymbol;
Procedure PPSymbol;
Procedure Gobble(terminators: keysymset);
Procedure RShift(currmsym: keysymbol);
Function ReadConfigFile: Boolean;
Public
LineSize : longint;
Indent : Integer; { How many characters to indent ? }
InS,
OutS,
DiagS,cfgS : PStream;
Constructor Create;
Function PrettyPrint : Boolean;
end;
Procedure GenerateCfgFile(S: PStream);
Implementation
CONST
version = '28 November 1989'; {was '11 October 1984'; ..ancient stuff!}
NUL = 0; { ASCII null character }
TAB = 9; { ASCII tab character }
FF = 12; { ASCII formfeed character }
CR = 13; { ASCII carriage return }
ESC = 27; { ASCII escape character }
Blank = ' ';
MAXBYTE = 255;{ Largest value of 1 byte variable }
Type
hashentry = RECORD
Keyword : String[MAXKEYLENGTH];
symtype : keysymbol
END;
VAR
sets : tableptr;
dblch : dblcharset;
hashtable : ARRAY [Byte] OF hashentry;
CONST
Keyword : KeywordTable =
('END', 'BEGIN', 'IF', 'THEN',
'ELSE', 'PROCEDURE', 'VAR', 'OF',
'WHILE', 'DO', 'CASE', 'WITH',
'FOR', 'REPEAT', 'UNTIL', 'FUNCTION',
'LABEL', 'CONST', 'TYPE', 'RECORD',
'STRING', 'PROGRAM',
'ASM','TRY','FINALLY','EXCEPT','RAISE','CLASS','OBJECT',
'CONSTRUCTOR','DESCTRUCTOR','INHERITED','PROPERTY',
'PRIVATE','PUBLIC','PROTECTED','PUBLISHED',
'INITIALIZATION','FINALIZATION',
'INLINE','LIBRARY','INTERFACE','IMPLEMENTATION',
'READ','WRITE','UNIT',
{keywords not used for formatting }
'AND', 'ARRAY', 'DIV', 'DOWNTO',
'FILE', 'GOTO', 'IN', 'MOD',
'NOT', 'NIL', 'OR', 'SET','TO'
);
EntryNames : OEntriesTable =
('end','begin','if','then','else','proc','var',
'of','while','do','case','with','for','repeat','until',
'func','label','const','type','record','string',
'prog',
'asm','try','finally','except','raise','class','object',
'constructor','destructor','inherited','property',
'private','public','protected','published',
'initialization','finalization',
'inline','library','interface','implementation',
'read','write','unit',
'and','arr','div','down','file','goto',
'in','mod','not','nil','or','set','to',
'casevar',
'becomes','delphicomment','opencomment','closecomment','semicolon',
'colon','equals',
'openparen','closeparen','period','endoffile','other');
OptionNames : ONamesTable =
('crsupp','crbefore','blinbefore',
'dindonkey','dindent','spbef','spaft',
'gobsym','inbytab','crafter','upper',
'lower','capital');
DblChar : DblCharTable =
( ':=', '//','(*' );
SglChar : SglCharTable =
('{', '}', ';', ':', '=', '(', ')', '.' );
{ ---------------------------------------------------------------------
General functions, not part of the object.
---------------------------------------------------------------------}
function upperStr(const s : string) : string;
var
i : longint;
begin
for i:=1 to length(s) do
if s[i] in ['a'..'z'] then
upperStr[i]:=char(byte(s[i])-32)
else
upperStr[i]:=s[i];
upperStr[0]:=s[0];
end;
function LowerStr(const s : string) : string;
var
i : longint;
begin
for i:=1 to length(s) do
if s[i] in ['A'..'Z'] then
LowerStr[i]:=char(byte(s[i])+32)
else
LowerStr[i]:=s[i];
LowerStr[0]:=s[0];
end;
Function IntToStr(I : LongInt) : String;
var
s : string;
begin
str(I,s);
IntToStr := s;
end;
Function StrToInt(Const S : String) : Integer;
Var Code : integer;
Res : Integer;
begin
Val(S, Res, Code);
StrToInt := Res;
If Code<>0 then StrToInt:=0;
end;
Procedure Strip (Var S : String);
Const WhiteSpace = [#32,#9,#10,#13];
Var I,J : Longint;
begin
If length(s)=0 then exit;
I:=1;
While (S[I] in whitespace) and (I<Length(S)) do inc(i);
J:=length(S);
While (S[J] in whitespace) and (J>1) do dec(j);
If I<=J then
S:=Copy(S,i,j-i+1)
else
S:='';
end;
{ ---------------------------------------------------------------------
Hash table related functions
---------------------------------------------------------------------}
Function hash(Symbol: String): Byte;
{ Hashing function for identifiers. The formula gives a unique value
in the range 0..255 for each Pascal/Z keyword. Note that range and
overflow checking must be turned off for this function even if they
are enabled for the rest of the program. }
BEGIN
{$R-}
hash := (ORD(Symbol[1]) * 5 + ORD(Symbol[length(Symbol)])) * 5 + length(Symbol);
{$R+}
END; { of hash }
Procedure CreateHash;
Var psn : Byte;
sym : keysymbol;
begin
FOR psn := 0 TO MAXBYTE DO BEGIN
hashtable[psn].Keyword := ' ';
hashtable[psn].symtype := othersym
END;
FOR sym := endsym TO lastformatsym DO BEGIN
psn := hash(Keyword[sym]);
hashtable[psn].Keyword := Keyword[sym];
hashtable[psn].symtype := sym
END; { for }
end;
Procedure ClassID(Value: Token;
lngth: INTEGER;
VAR idtype: keysymbol;
VAR IsKeyWord: BOOLEAN);
{ Classify an identifier. We are only interested
in it if it is a keyword, so we use the hash table. }
VAR
Keyvalue: String[MAXKEYLENGTH];
tabent: INTEGER;
BEGIN
IF lngth > MAXKEYLENGTH THEN BEGIN
idtype := othersym;
IsKeyWord := FALSE
END
ELSE BEGIN
KeyValue:= UpperStr(Value);
tabent := hash(Keyvalue);
IF Keyvalue = hashtable[tabent].Keyword THEN BEGIN
idtype := hashtable[tabent].symtype;
IsKeyWord := TRUE;
END
ELSE BEGIN
idtype := othersym;
IsKeyWord := FALSE;
END
END
END; { of ClassID }
{ ---------------------------------------------------------------------
Functions to create options and set defaults.
---------------------------------------------------------------------}
Procedure CreateOptions (Var Option : OptionTable);
Var Sym : KeySymbol;
begin
FOR sym := endsym TO othersym DO BEGIN
NEW(option[sym]);
option[sym]^.selected := [];
option[sym]^.dindsym := [];
option[sym]^.terminators := []
END;
end;
Procedure SetTerminators(Var Option : OptionTable);
begin
option[casesym]^.terminators := [ofsym];
option[casevarsym]^.terminators := [ofsym];
option[forsym]^.terminators := [dosym];
option[whilesym]^.terminators := [dosym];
option[withsym]^.terminators := [dosym];
option[ifsym]^.terminators := [thensym];
option[untilsym]^.terminators := [endsym, untilsym, elsesym, semicolon];
option[becomes]^.terminators := [endsym, untilsym, elsesym, semicolon];
option[openparen]^.terminators := [closeparen];
end;
Procedure SetDefaultIndents (Var Option : OptionTable);
begin
option[recordsym]^.dindsym := [endsym];
option[funcsym]^.dindsym := [labelsym, constsym, typesym, varsym];
option[procsym]^.dindsym := [labelsym, constsym, typesym, varsym];
option[constsym]^.dindsym := [labelsym, constsym, typesym, varsym];
option[typesym]^.dindsym := [labelsym, constsym, typesym, varsym];
option[varsym]^.dindsym := [labelsym, constsym, typesym, varsym];
option[beginsym]^.dindsym := [labelsym, constsym, typesym, varsym];
option[publicsym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
option[privatesym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
option[protectedsym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
option[publishedsym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
option[finallysym]^.dindsym := [trysym];
option[exceptsym]^.dindsym := [trysym];
option[elsesym]^.dindsym := [ifsym, thensym, elsesym];
option[untilsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
withsym, colon, equals];
option[endsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
withsym, casevarsym, colon, equals, recordsym,
classsym,objectsym];
option[semicolon]^.dindsym := [ifsym, thensym, elsesym, forsym,
whilesym, withsym, colon, equals];
end;
Procedure SetDefaults (Var Option : OptionTable);
{ Sets default values for the formatting rules. }
begin
option[progsym]^.selected := [capital,blinbefore, spaft];
option[unitsym]^.selected := [capital,blinbefore, spaft];
option[librarysym]^.selected := [capital,blinbefore, spaft];
option[funcsym]^.selected := [capital,blinbefore, dindonkey, spaft];
option[procsym]^.selected := [capital,blinbefore, dindonkey, spaft];
option[labelsym]^.selected := [capital,blinbefore, spaft, inbytab];
option[constsym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
option[typesym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
option[varsym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
option[beginsym]^.selected := [capital,dindonkey, crbefore, crafter, inbytab];
option[repeatsym]^.selected := [capital,inbytab, crafter];
option[recordsym]^.selected := [capital,inbytab, crafter];
option[objectsym]^.selected := [capital,inbytab, crafter];
option[classsym]^.selected := [capital,inbytab, crafter];
option[publicsym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
option[publishedsym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
option[protectedsym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
option[privatesym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
option[trysym]^.Selected := [capital,crbefore,crafter,inbytab];
option[finallysym]^.selected := [capital,crbefore,dindonkey,crafter,inbytab];
option[exceptsym]^.selected := [capital,crbefore,dindonkey,crafter,inbytab];
option[casesym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
option[casevarsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
option[ofsym]^.selected := [capital,crsupp, spbef];
option[forsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
option[whilesym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
option[withsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
option[dosym]^.selected := [capital,crsupp, spbef];
option[ifsym]^.selected := [capital,spaft, inbytab, gobsym];
option[thensym]^.selected := [capital];
option[elsesym]^.selected := [capital,crbefore, dindonkey, inbytab];
option[endsym]^.selected := [capital,crbefore, crafter,dindonkey,dindent];
option[untilsym]^.selected := [capital,crbefore, dindonkey, dindent, spaft,
gobsym, crafter];
option[becomes]^.selected := [capital,spbef, spaft, gobsym];
option[Delphicomment]^.Selected := [crafter];
option[opencomment]^.selected := [capital,crsupp];
option[closecomment]^.selected := [capital,crsupp];
option[semicolon]^.selected := [capital,crsupp, dindonkey, crafter];
option[colon]^.selected := [capital,inbytab];
option[equals]^.selected := [capital,spbef, spaft, inbytab];
option[openparen]^.selected := [capital,gobsym];
option[period]^.selected := [capital,crsupp];
end;
{ ---------------------------------------------------------------------
Stream handling routines
---------------------------------------------------------------------}
Function ReadChar (S : PStream) : Char;
Var C : Char;
begin
repeat
S^.Read(C,1);
If S^.Status=stReadError then
C:=#0;
Until C<>#13;
ReadChar:=C;
end;
Function EoSLn (S : PStream) : Char;
Const WhiteSpace = [' ', #9, #13 ];
Var C : Char;
begin
Repeat
S^.Read(C,1);
Until (Not (C in WhiteSpace)) or ((C=#10) or (S^.Status=stReadError));
If S^.Status=stReadError then
EoSln:=#0
else
EoSln:=C;
end;
Function ReadString (S: PStream): String;
Var Buffer : String;
I : Byte;
begin
Buffer:='';
I:=0;
Repeat
S^.Read(Buffer[I+1],1);
Inc(I);
until (I=255) or (Buffer[I]=#10) Or (S^.Status=StReadError);
If S^.Status=stReadError then Dec(I);
If Buffer[i]=#10 Then Dec(I);
If Buffer[I]=#13 then Dec(I);
Buffer[0] := chr(I);
ReadString:=Buffer;
end;
Procedure WriteString (S : PStream; ST : String);
begin
S^.Write(St[1],length(St));
end;
Procedure WriteCR (S: PStream);
Const
{$ifdef linux}
Newline = #10;
{$else}
NewLine = #13#10;
{$endif}
begin
WriteString(S,Newline);
end;
Procedure WriteLnString (S : PStream; ST : String);
begin
WriteString(S,ST);
WriteCR(S);
end;
{ ---------------------------------------------------------------------
TPrettyPrinter object
---------------------------------------------------------------------}
Procedure TPrettyPrinter.Verbose (Const Msg : String);
begin
If Assigned (DiagS) then
WriteLnString (DiagS,Msg);
end;
Procedure TPrettyPrinter.GetChar;
{ Read the next character and classify it }
VAR Ch: CHAR;
BEGIN
currchar := nextchar;
WITH nextchar DO
begin
Ch:=ReadCHar(Ins);
If Ch=#0 then
BEGIN
name := filemark;
Value := Blank
END
ELSE If (Ch=#10) THEN
BEGIN
name := endofline;
Value := Blank;
Inc(inlines);
END
ELSE
BEGIN
Value := Ch;
IF Ch IN ['a'..'z', 'A'..'Z', '_'] THEN name := letter
ELSE IF Ch IN ['0'..'9'] THEN name := digit
ELSE IF Ch = '''' THEN name := quote
ELSE IF Ch in [#13,' ',#9] THEN name := space
ELSE name := otherchar
END
end;
END; { of GetChar }
Procedure TPrettyPrinter.StoreNextChar(VAR lngth: INTEGER;
VAR Value: Token);
{ Store a character in the current symbol }
BEGIN
GetChar;
IF lngth < maxsymbolsize THEN BEGIN
Inc(lngth);
Value[lngth] := currchar.Value;
Value[0] := chr(Lngth);
END;
END; { of StoreNextChar }
Procedure TPrettyPrinter.SkipBlanks(VAR spacesbefore, crsbefore: INTEGER);
{ Count the spaces between symbols }
BEGIN
spacesbefore := 0;
crsbefore := 0;
WHILE nextchar.name IN [space, endofline] DO BEGIN
GetChar;
CASE currchar.name OF
space: Inc(spacesbefore);
endofline: BEGIN
Inc(crsbefore);
spacesbefore := 0;
END;
END; {case}
END;
END; { of SkipBlanks }
Procedure TPrettyPrinter.GetComment(sym: symbolinfo);
{ Process comments using either brace or parenthesis notation }
BEGIN
sym^.name := opencomment;
WHILE NOT (((currchar.Value = '*') AND (nextchar.Value = ')'))
OR (currchar.Value = '}') OR (nextchar.name = endofline)
OR (nextchar.name = filemark)) DO
StoreNextChar(sym^.length, sym^.Value);
IF (currchar.Value = '*') AND (nextchar.Value = ')') THEN BEGIN
StoreNextChar(sym^.LENGTH, sym^.Value);
sym^.name := closecomment;
END;
IF currchar.Value = '}' THEN sym^.name := closecomment;
END; { of GetCommment }
Procedure TPrettyPrinter.GetDelphiComment(sym: symbolinfo);
{ Process comments using either brace or parenthesis notation }
BEGIN
sym^.name := Delphicomment;
WHILE NOT ((nextchar.name = endofline) OR (nextchar.name = filemark)) DO
StoreNextChar(sym^.length, sym^.Value);
END; { of GetDelphiCommment }
Procedure TPrettyPrinter.GetIdentifier(sym: symbolinfo);
{ Read an identifier and classify it }
BEGIN
WHILE nextchar.name IN [letter, digit] DO
StoreNextChar(sym^.length, sym^.Value);
ClassID(sym^.Value, sym^.length, sym^.name, sym^.IsKeyWord);
IF sym^.name IN [recordsym, casesym, endsym] THEN
CASE sym^.name OF
recordsym : RecordSeen := TRUE;
casesym : IF RecordSeen THEN sym^.name := casevarsym;
endsym : RecordSeen := FALSE;
END; {case}
END; { of GetIdentifier }
{ Read a number and store it as a string }
Procedure TPrettyPrinter.GetNumber(sym: symbolinfo);
BEGIN
WHILE nextchar.name = digit DO StoreNextChar(sym^.length, sym^.Value);
sym^.name := othersym;
END; { of GetNumber }
PROCEDURE TPrettyPrinter.GetCharLiteral(sym: symbolinfo);
{ Read a quoted string }
BEGIN
WHILE nextchar.name = quote DO BEGIN
StoreNextChar(sym^.length, sym^.Value);
WHILE NOT (nextchar.name IN [quote, endofline, filemark]) DO
StoreNextChar(sym^.length, sym^.Value);
IF nextchar.name = quote THEN StoreNextChar(sym^.length, sym^.Value);
END;
sym^.name := othersym;
END; { of GetCharLiteral }
FUNCTION TPrettyPrinter.char_Type: keysymbol;
{ Classify a character pair }
VAR
NextTwoChars: SpecialChar;
Hit: BOOLEAN;
thischar: keysymbol;
BEGIN
NextTwoChars[1] := currchar.Value;
NextTwoChars[2] := nextchar.Value;
thischar := becomes;
Hit := FALSE;
WHILE NOT (Hit OR (thischar = closecomment)) DO BEGIN
IF NextTwoChars = DblChar[thischar] THEN Hit := TRUE
ELSE Inc(thischar);
END;
IF NOT Hit THEN BEGIN
thischar := opencomment;
WHILE NOT (Hit OR (PRED(thischar) = period)) DO BEGIN
IF currchar.Value = SglChar[thischar] THEN Hit := TRUE
ELSE Inc(thischar);
END;
END;
IF Hit THEN char_Type := thischar
ELSE char_Type := othersym;
END; { of char_Type }
Procedure TPrettyPrinter.GetSpecialChar(sym: symbolinfo);
{ Read special characters }
BEGIN
StoreNextChar(sym^.length, sym^.Value);
sym^.name := char_Type;
IF sym^.name IN dblch THEN StoreNextChar(sym^.length, sym^.Value)
END; { of GetSpecialChar }
Procedure TPrettyPrinter.GetNextSymbol(sym: symbolinfo);
{ Read a symbol using the appropriate procedure }
BEGIN
CASE nextchar.name OF
letter: GetIdentifier(sym);
digit: GetNumber(sym);
quote: GetCharLiteral(sym);
otherchar: BEGIN
GetSpecialChar(sym);
IF sym^.name = opencomment THEN GetComment(sym)
else IF sym^.name= DelphiComment then GetDelphiComment(Sym)
END;
filemark: sym^.name := endoffile;
ELSE {:} {Turbo}
WRITELN('Unknown character type: ', ORD(nextchar.name));
END; {case}
END; { of GetNextSymbol }
Procedure TprettyPrinter.GetSymbol;
{ Store the next symbol in NEXTSYM }
VAR
dummy: symbolinfo;
BEGIN
dummy := currsym;
currsym := nextsym;
nextsym := dummy;
SkipBlanks(nextsym^.spacesbefore, nextsym^.crsbefore);
nextsym^.length := 0;
nextsym^.IsKeyWord := FALSE;
IF currsym^.name = opencomment THEN GetComment(nextsym)
ELSE GetNextSymbol(nextsym);
END; {of GetSymbol}
Procedure TprettyPrinter.PopStack(VAR indentsymbol: keysymbol;
VAR prevmargin: INTEGER);
{ Manage stack of indentation symbols and margins }
BEGIN
IF top > 0 THEN BEGIN
indentsymbol := stack[top].indentsymbol;
prevmargin := stack[top].prevmargin;
Dec(top);
END
ELSE BEGIN
indentsymbol := othersym;
prevmargin := 0;
END;
END; { of PopStack }
Procedure TPrettyPrinter.PushStack(indentsymbol: keysymbol;
prevmargin: INTEGER );
BEGIN
Inc(top);
stack[top].indentsymbol := indentsymbol;
stack[top].prevmargin := prevmargin;
END; { of PushStack }
Procedure TPrettyPrinter.WriteCRs(numberofcrs: INTEGER);
VAR
i: INTEGER;
BEGIN
IF numberofcrs > 0 THEN BEGIN
FOR i := 1 TO numberofcrs DO
WriteCr(OutS);
Inc(outlines,numberofcrs);
currlinepos := 0;
END;
END; { of WriteCRs }
Procedure TPrettyPrinter.InsertCR;
BEGIN
IF currsym^.crsbefore = 0 THEN BEGIN
WriteCRs(1);
currsym^.spacesbefore := 0;
END;
END; { of InsertCR }
Procedure TPrettyPrinter.InsertBlankLine;
BEGIN
IF currsym^.crsbefore = 0 THEN BEGIN
IF currlinepos = 0 THEN WriteCRs(1)
ELSE WriteCRs(2);
currsym^.spacesbefore := 0;
END
ELSE IF currsym^.crsbefore = 1 THEN
IF currlinepos > 0 THEN WriteCRs(1);
END; { of InsertBlankLine }
Procedure TPrettyPrinter.LShiftOn(dindsym: keysymset);
{ Move margin left according to stack configuration and current symbol }
VAR
indentsymbol: keysymbol;
prevmargin: INTEGER;
BEGIN
IF top > 0 THEN BEGIN
REPEAT
PopStack(indentsymbol, prevmargin);
IF indentsymbol IN dindsym THEN currmargin := prevmargin;
UNTIL NOT (indentsymbol IN dindsym) OR (top = 0);
IF NOT (indentsymbol IN dindsym) THEN
PushStack(indentsymbol, prevmargin);
END;
END; { of LShiftOn }
Procedure TprettyPrinter.LShift;
{ Move margin left according to stack top }
VAR
indentsymbol: keysymbol;
prevmargin: INTEGER;
BEGIN
IF top > 0 THEN BEGIN
PopStack(indentsymbol, prevmargin);
currmargin := prevmargin;
(* maybe PopStack(indentsymbol,currmargin); *)
END;
END; { of LShift }
Procedure TPrettyPrinter.InsertSpace(VAR symbol: symbolinfo);
{ Insert space if room on line }
BEGIN
IF currlinepos < LineSize THEN BEGIN
WriteString(OutS, Blank);
Inc(currlinepos);
IF (symbol^.crsbefore = 0) AND (symbol^.spacesbefore > 0)
THEN Dec(symbol^.spacesbefore);
END;
END; { of InsertSpace }
Procedure TPrettyPrinter.MoveLinePos(newlinepos: INTEGER);
{ Insert spaces until correct line position reached }
VAR i: INTEGER;
BEGIN
FOR i := SUCC(currlinepos) TO newlinepos DO
WriteString(OutS, Blank);
currlinepos := newlinepos;
END; { of MoveLinePos }
Procedure TPrettyPrinter.PrintSymbol;
BEGIN
IF (currsym^.IsKeyWord) then
begin
If upper in sets^.selected Then
WriteString (OutS,UpperStr(currsym^.value))
else if lower in sets^.selected then
WriteString (OutS,LowerStr(currsym^.value))
else if capital in sets^.selected then
begin
WriteString(OutS,UpCase(CurrSym^.Value[1]));
WriteString(OutS,LowerStr(Copy(CurrSym^.Value,2,255)));
end
else
WriteString(OutS,Currsym^.Value);
end
ELSE
WriteString(OutS, currsym^.Value);
startpos := currlinepos;
Inc(currlinepos,currsym^.length);
END; { of PrintSymbol }
Procedure TPrettyPrinter.PPSymbol;
{ Find position for symbol and then print it }
VAR newlinepos: INTEGER;
BEGIN
WriteCRs(currsym^.crsbefore);
IF (currlinepos + currsym^.spacesbefore > currmargin)
OR (currsym^.name IN [opencomment, closecomment])
THEN newlinepos := currlinepos + currsym^.spacesbefore
ELSE newlinepos := currmargin;
IF newlinepos + currsym^.length > MAXLINESIZE THEN BEGIN
WriteCRs(1);
IF currmargin + currsym^.length <= MAXLINESIZE
THEN newlinepos := currmargin
ELSE IF currsym^.length < MAXLINESIZE
THEN newlinepos := MAXLINESIZE - currsym^.length
ELSE newlinepos := 0;
END;
MoveLinePos(newlinepos);
PrintSymbol;
END; { of PPSymbol }
Procedure TPrettyPrinter.Gobble(terminators: keysymset);
{ Print symbols which follow a formatting symbol but which do not
affect layout }
BEGIN
IF top < MAXSTACKSIZE THEN PushStack(currsym^.name, currmargin);
currmargin := currlinepos;
WHILE NOT ((nextsym^.name IN terminators)
OR (nextsym^.name = endoffile)) DO BEGIN
GetSymbol;
PPSymbol;
END;
LShift;
END; { of Gobble }
Procedure TprettyPrinter.RShift(currmsym: keysymbol);
{ Move right, stacking margin positions }
BEGIN
IF top < MAXSTACKSIZE THEN PushStack(currmsym, currmargin);
IF startpos > currmargin THEN currmargin := startpos;
Inc(currmargin,INDENT);
END; { of RShift }
Function TPrettyPrinter.ReadConfigFile : Boolean;
Var I,J : Longint;
Procedure SetOption(TheKey : KeySymbol;Var OptionList : String);
Var TheOpt : Options;
Found : Boolean;
K : longint;
opt : string;
begin
Repeat
K:=pos(',',optionlist);
If k>0 then
begin
opt:=Copy(OptionList,1,k-1);
strip(opt);
Delete(OptionList,1,k);
end
else
opt:=OptionList;
If Length(Opt)>0 then
begin
Found:=False;
for TheOpt :=firstopt to lastopt do
begin
found:=opt=OptionNames[Theopt];
If found then break;
end;
If not found then
Verbose ('Unknown option on line '+inttostr(i)+': '+Opt)
else
Option[TheKey]^.Selected:=Option[TheKey]^.Selected+[TheOpt];
end;
until k=0;
end;
Procedure SetIndent(TheKey : KeySymbol; Var OptionList : String);
Var
TheIndent : Keysymbol;
Found : Boolean;
K : longint;
opt : string;
begin
Repeat
K:=pos(',',optionlist);
If k>0 then
begin
opt:=Copy(OptionList,1,k-1);
strip(opt);
Delete(OptionList,1,k);
end
else
opt:=OptionList;
If Length(Opt)>0 then
begin
Found:=False;
for TheIndent :=firstKey to lastKey do
begin
found:=opt=EntryNames[Theindent];
If found then break;
end;
If not found then
begin
Verbose ('Unknown indent keysym on line '+inttostr(i)+': '+Opt);
exit;
end;
Option[TheKey]^.dindsym:=Option[TheKey]^.dindsym+[Theindent];
end;
until k=0;
end;
Var TheKey : KeySymbol;
Found,DoIndent : Boolean;
Line, Name : String;
begin
ReadConfigFile:=false;
I:=0;
while not (CfgS^.Status=stReadError) do
begin
inc(i);
Line:='';
Line:=ReadString(cfgS);
{ Strip comment }
If pos('#',Line)<>0 then
Line:=Copy(Line,1,Pos('#',Line)-1);
If length(Line)<>0 then
begin
J:=Pos('=',Line);
If J>0 then
begin
Line:=LowerStr(Line);
Name:=Copy(Line,1,j-1);
Delete(Line,1,J);
{ indents or options ? }
If (Name[1]='[') and
(Name[Length(Name)]=']') then
begin
Name:=Copy(Name,2,Length(Name)-2);
Doindent:=True;
end
else
DoIndent:=False;
Strip(Name);
found:=false;
for thekey:=firstkey to lastkey do
begin
found:=Name=EntryNames[thekey];
If Found then break;
end;
If not found then
Verbose ('Unknown keyword on line '+inttostr(i)+': '+Name)
else
If DoIndent then
SetIndent(TheKey,Line)
else
SetOption(TheKey,Line)
end
else
verbose ('Error in config file on line '+IntToStr(i));
end;
end;
Verbose ('Processed configfile: read '+IntToStr(I)+' lines');
ReadConfigFile:=true;
end;
Procedure GenerateCfgFile(S : PStream);
Var TheKey,TheIndent : KeySymbol;
TheOpt : Options;
Written : Boolean;
Option : OptionTable;
begin
CreateOptions(option);
SetDefaults(option);
SetDefaultIndents(option);
For TheKey:=Firstkey to lastkey do
begin
{ Write options }
WriteString (S,EntryNames[TheKey]+'=');
Written:=False;
for TheOpt:=FirstOpt to LastOpt do
If TheOpt in Option[TheKey]^.Selected then
begin
if written then
WriteString (S,',')
else
Written:=True;
writeString (S,OptionNames[TheOpt]);
end;
WriteCr (S);
{ Write de-indent keysyms, if any }
If Option[TheKey]^.dindsym<>[] then
begin
WriteString (S,'['+EntryNames[TheKey]+']=');
Written:=False;
For TheIndent:=FirstKey to lastkey do
If TheIndent in Option[TheKey]^.dindsym then
begin
if written then
WriteString (S,',')
else
Written:=True;
WriteString (S,EntryNames[Theindent]);
end;
WriteCr (S);
end;
end;
end;
Function TPrettyPrinter.PrettyPrint : Boolean;
Begin
PrettyPrint:=False;
If Not Assigned(Ins) or Not Assigned(OutS) then
exit;
If Not Assigned(CfgS) then
begin
SetDefaults(Option);
SetDefaultIndents(Option);
end
else
ReadConfigFile;
{ Initialize variables }
top := 0;
currlinepos := 0;
currmargin := 0;
inlines := 0;
outlines := 0;
CrPending := FALSE;
RecordSeen := FALSE;
GetChar;
NEW(currsym);
NEW(nextsym);
GetSymbol;
WHILE nextsym^.name <> endoffile DO BEGIN
GetSymbol;
sets := option[currsym^.name];
IF (CrPending AND NOT (crsupp IN sets^.selected))
OR (crbefore IN sets^.selected) THEN BEGIN
InsertCR;
CrPending := FALSE
END;
IF blinbefore IN sets^.selected THEN BEGIN
InsertBlankLine;
CrPending := FALSE
END;
IF dindonkey IN sets^.selected THEN LShiftOn(sets^.dindsym);
IF dindent IN sets^.selected THEN LShift;
IF spbef IN sets^.selected THEN InsertSpace(currsym);
PPSymbol;
IF spaft IN sets^.selected THEN InsertSpace(nextsym);
IF inbytab IN sets^.selected THEN RShift(currsym^.name);
IF gobsym IN sets^.selected THEN Gobble(sets^.terminators);
IF crafter IN sets^.selected THEN CrPending := TRUE
END;
IF CrPending THEN WriteCRs(1);
Verbose(IntToStr(inlines)+' lines read, '+IntToStr(outlines)+' lines written.');
PrettyPrint:=True;
end;
Constructor TPrettyPrinter.Create;
Begin
LineSize:=MaxLineSize;
CreateOptions (Option);
SetTerminators(Option);
DiagS:=Nil;
InS:=Nil;
OutS:=Nil;
CfgS:=Nil;
End;
{ ---------------------------------------------------------------------
Unit initialization
---------------------------------------------------------------------}
Begin
CreateHash;
dblch := [becomes, opencomment];
end.
{
$Log$
Revision 1.1 2000-07-13 10:16:22 michael
+ Initial import
Revision 1.8 2000/06/01 10:59:22 peter
* removed warning/notes
Revision 1.7 2000/05/03 13:04:08 pierre
* avoid a problem with range check
Revision 1.6 2000/02/09 16:44:15 peter
* log truncated
Revision 1.5 2000/02/06 19:57:45 carl
+ More TP syntax compatible
Revision 1.4 2000/01/07 16:46:04 daniel
* copyright 2000
}