ptopu.pp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. Unit PtoPu;
  2. {
  3. $Id$
  4. This file is part of the Free Pascal run time library.
  5. Copyright (c) 1999-2000 by Michael Van Canneyt, member of
  6. the Free Pascal development team
  7. Pascal Pretty-Printer object implementation
  8. See the file COPYING.FPC, included in this distribution,
  9. for details about the copyright.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. **********************************************************************}
  14. {
  15. This unit is based heavily on the code by
  16. Author: Peter Grogono
  17. This program is based on a Pascal pretty-printer written by Ledgard,
  18. Hueras, and Singer. See SIGPLAN Notices, Vol. 12, No. 7, July 1977,
  19. pages 101-105, and PP.DOC/HLP.
  20. This version of PP developed under Pascal/Z V4.0 or later.
  21. Very minor modifications for Turbo Pascal made by Willett Kempton
  22. March 1984 and Oct 84. Runs under 8-bit Turbo or 16-bit Turbo.
  23. Toad Hall tweak, rewrite for TP 5, 28 Nov 89
  24. The following was changed :
  25. - Object oriented
  26. - Uses streams
  27. - Run-time customizable.
  28. }
  29. Interface
  30. Uses objects;
  31. Const
  32. MAXSYMBOLSIZE = 80;
  33. MAXSTACKSIZE = 100;
  34. MAXKEYLENGTH = 15; { The longest keyword is PROCEDURE }
  35. MAXLINESIZE = 90; { Maximum length of output line }
  36. TYPE
  37. Token = String[MAXSYMBOLSIZE];
  38. String0 = STRING[1]; {Pascal/z had 0}
  39. FileName = STRING;
  40. { Keysymbols }
  41. { If you add keysyms, adjust the definition of lastkey }
  42. keysymbol = { keywords }
  43. (endsym,beginsym,ifsym,thensym,elsesym,procsym,varsym,ofsym,
  44. whilesym,dosym,casesym,withsym,forsym,repeatsym,untilsym,
  45. funcsym,labelsym,constsym,typesym,recordsym,stringsym,progsym,
  46. { TP and Delphi keywords}
  47. asmsym, trysym, finallysym,exceptsym,raisesym,classsym,objectsym,
  48. constructorsym,destructorsym,inheritedsym,propertysym,
  49. privatesym,publicsym,protectedsym,publishedsym,
  50. initializationsym,finalizationsym,
  51. inlinesym,librarysym,interfacesym,implementationsym,
  52. readsym,writesym,unitsym,
  53. { Not used for formatting }
  54. andsym,arrsym,divsym,downsym,filesym,gotosym,insym,modsym,
  55. notsym,nilsym,orsym,setsym,tosym,
  56. casevarsym,
  57. { other symbols }
  58. becomes,delphicomment,opencomment,closecomment,semicolon,colon,equals,
  59. openparen,closeparen,period,endoffile,othersym);
  60. { Formatting options }
  61. { If you add options, adjust the definition of lastopt }
  62. options = (crsupp,crbefore,blinbefore,
  63. dindonkey,dindent,spbef,
  64. spaft,gobsym,inbytab,crafter,upper,lower,capital);
  65. optionset = SET OF options;
  66. keysymset = SET OF keysymbol;
  67. tableentry = RECORD
  68. selected : optionset;
  69. dindsym : keysymset;
  70. terminators : keysymset
  71. END;
  72. { Character identification }
  73. charname = (letter,digit,space,quote,endofline,
  74. filemark,otherchar);
  75. charinfo = RECORD
  76. name : charname;
  77. Value : CHAR
  78. END;
  79. symbol = RECORD
  80. name : keysymbol;
  81. Value : Token;
  82. IsKeyWord : BOOLEAN;
  83. length, spacesbefore, crsbefore : INTEGER;
  84. END;
  85. symbolinfo = ^ symbol;
  86. stackentry = RECORD
  87. indentsymbol : keysymbol;
  88. prevmargin : INTEGER
  89. END;
  90. symbolstack = ARRAY [1..MAXSTACKSIZE] OF stackentry;
  91. Const FirstOpt = crsupp;
  92. LastOpt = capital; { Adjust this if you add options }
  93. FirstKey = endsym;
  94. LastKey = othersym; { Adjust this if you add options }
  95. LastFormatsym = tosym;
  96. Type
  97. tableptr = ^tableentry;
  98. optiontable = ARRAY [keysymbol] OF tableptr;
  99. OEntriesTable = Array [keysymbol] OF String[15];
  100. ONamesTable = Array [Options] of String[15];
  101. KeywordTable = ARRAY [endsym..lastFormatsym] OF String[MAXKEYLENGTH];
  102. SpecialChar = ARRAY [1..2] OF CHAR;
  103. dblcharset = SET OF endsym..othersym;
  104. DblCharTable = ARRAY [becomes..opencomment] OF SpecialChar;
  105. SglCharTable = ARRAY [opencomment..period] OF CHAR;
  106. TPrettyPrinter=Object(TObject)
  107. Private
  108. RecordSeen,
  109. CRPending : BOOLEAN;
  110. currchar,nextchar : charinfo;
  111. currsym,nextsym : symbolinfo;
  112. inlines,outlines : INTEGER;
  113. stack : symbolstack;
  114. top,startpos,currlinepos,currmargin : Integer;
  115. option : OptionTable;
  116. Procedure Verbose (Const Msg : String);
  117. Procedure GetChar;
  118. Procedure StoreNextChar(VAR lngth: INTEGER;
  119. VAR Value: Token);
  120. Procedure SkipBlanks(VAR spacesbefore, crsbefore: INTEGER);
  121. Procedure GetComment(sym: symbolinfo);
  122. Procedure GetDelphiComment(sym: symbolinfo);
  123. Procedure GetNumber(sym: symbolinfo);
  124. Procedure GetCharLiteral(sym: symbolinfo);
  125. Function char_Type: keysymbol;
  126. Procedure GetSpecialChar(sym: symbolinfo);
  127. Procedure GetNextSymbol(sym: symbolinfo);
  128. Procedure GetIdentifier(sym: symbolinfo);
  129. Procedure GetSymbol;
  130. Procedure PopStack(VAR indentsymbol: keysymbol;
  131. VAR prevmargin: INTEGER);
  132. Procedure PushStack(indentsymbol: keysymbol;
  133. prevmargin: INTEGER );
  134. Procedure WriteCRs(numberofcrs: INTEGER);
  135. Procedure InsertCR;
  136. Procedure InsertBlankLine;
  137. Procedure LShiftOn(dindsym: keysymset);
  138. Procedure LShift;
  139. Procedure InsertSpace(VAR symbol: symbolinfo);
  140. Procedure MoveLinePos(newlinepos: INTEGER);
  141. Procedure PrintSymbol;
  142. Procedure PPSymbol;
  143. Procedure Gobble(terminators: keysymset);
  144. Procedure RShift(currmsym: keysymbol);
  145. Function ReadConfigFile: Boolean;
  146. Public
  147. LineSize : longint;
  148. Indent : Integer; { How many characters to indent ? }
  149. InS,
  150. OutS,
  151. DiagS,cfgS : PStream;
  152. Constructor Create;
  153. Function PrettyPrint : Boolean;
  154. end;
  155. Procedure GenerateCfgFile(S: PStream);
  156. Implementation
  157. CONST
  158. version = '28 November 1989'; {was '11 October 1984'; ..ancient stuff!}
  159. NUL = 0; { ASCII null character }
  160. TAB = 9; { ASCII tab character }
  161. FF = 12; { ASCII formfeed character }
  162. CR = 13; { ASCII carriage return }
  163. ESC = 27; { ASCII escape character }
  164. Blank = ' ';
  165. MAXBYTE = 255;{ Largest value of 1 byte variable }
  166. Type
  167. hashentry = RECORD
  168. Keyword : String[MAXKEYLENGTH];
  169. symtype : keysymbol
  170. END;
  171. VAR
  172. sets : tableptr;
  173. dblch : dblcharset;
  174. hashtable : ARRAY [Byte] OF hashentry;
  175. CONST
  176. Keyword : KeywordTable =
  177. ('END', 'BEGIN', 'IF', 'THEN',
  178. 'ELSE', 'PROCEDURE', 'VAR', 'OF',
  179. 'WHILE', 'DO', 'CASE', 'WITH',
  180. 'FOR', 'REPEAT', 'UNTIL', 'FUNCTION',
  181. 'LABEL', 'CONST', 'TYPE', 'RECORD',
  182. 'STRING', 'PROGRAM',
  183. 'ASM','TRY','FINALLY','EXCEPT','RAISE','CLASS','OBJECT',
  184. 'CONSTRUCTOR','DESCTRUCTOR','INHERITED','PROPERTY',
  185. 'PRIVATE','PUBLIC','PROTECTED','PUBLISHED',
  186. 'INITIALIZATION','FINALIZATION',
  187. 'INLINE','LIBRARY','INTERFACE','IMPLEMENTATION',
  188. 'READ','WRITE','UNIT',
  189. {keywords not used for formatting }
  190. 'AND', 'ARRAY', 'DIV', 'DOWNTO',
  191. 'FILE', 'GOTO', 'IN', 'MOD',
  192. 'NOT', 'NIL', 'OR', 'SET','TO'
  193. );
  194. EntryNames : OEntriesTable =
  195. ('end','begin','if','then','else','proc','var',
  196. 'of','while','do','case','with','for','repeat','until',
  197. 'func','label','const','type','record','string',
  198. 'prog',
  199. 'asm','try','finally','except','raise','class','object',
  200. 'constructor','destructor','inherited','property',
  201. 'private','public','protected','published',
  202. 'initialization','finalization',
  203. 'inline','library','interface','implementation',
  204. 'read','write','unit',
  205. 'and','arr','div','down','file','goto',
  206. 'in','mod','not','nil','or','set','to',
  207. 'casevar',
  208. 'becomes','delphicomment','opencomment','closecomment','semicolon',
  209. 'colon','equals',
  210. 'openparen','closeparen','period','endoffile','other');
  211. OptionNames : ONamesTable =
  212. ('crsupp','crbefore','blinbefore',
  213. 'dindonkey','dindent','spbef','spaft',
  214. 'gobsym','inbytab','crafter','upper',
  215. 'lower','capital');
  216. DblChar : DblCharTable =
  217. ( ':=', '//','(*' );
  218. SglChar : SglCharTable =
  219. ('{', '}', ';', ':', '=', '(', ')', '.' );
  220. { ---------------------------------------------------------------------
  221. General functions, not part of the object.
  222. ---------------------------------------------------------------------}
  223. function upperStr(const s : string) : string;
  224. var
  225. i : longint;
  226. begin
  227. for i:=1 to length(s) do
  228. if s[i] in ['a'..'z'] then
  229. upperStr[i]:=char(byte(s[i])-32)
  230. else
  231. upperStr[i]:=s[i];
  232. upperStr[0]:=s[0];
  233. end;
  234. function LowerStr(const s : string) : string;
  235. var
  236. i : longint;
  237. begin
  238. for i:=1 to length(s) do
  239. if s[i] in ['A'..'Z'] then
  240. LowerStr[i]:=char(byte(s[i])+32)
  241. else
  242. LowerStr[i]:=s[i];
  243. LowerStr[0]:=s[0];
  244. end;
  245. Function IntToStr(I : LongInt) : String;
  246. var
  247. s : string;
  248. begin
  249. str(I,s);
  250. IntToStr := s;
  251. end;
  252. Function StrToInt(Const S : String) : Integer;
  253. Var Code : integer;
  254. Res : Integer;
  255. begin
  256. Val(S, Res, Code);
  257. StrToInt := Res;
  258. If Code<>0 then StrToInt:=0;
  259. end;
  260. Procedure Strip (Var S : String);
  261. Const WhiteSpace = [#32,#9,#10,#13];
  262. Var I,J : Longint;
  263. begin
  264. If length(s)=0 then exit;
  265. I:=1;
  266. While (S[I] in whitespace) and (I<Length(S)) do inc(i);
  267. J:=length(S);
  268. While (S[J] in whitespace) and (J>1) do dec(j);
  269. If I<=J then
  270. S:=Copy(S,i,j-i+1)
  271. else
  272. S:='';
  273. end;
  274. { ---------------------------------------------------------------------
  275. Hash table related functions
  276. ---------------------------------------------------------------------}
  277. Function hash(Symbol: String): Byte;
  278. { Hashing function for identifiers. The formula gives a unique value
  279. in the range 0..255 for each Pascal/Z keyword. Note that range and
  280. overflow checking must be turned off for this function even if they
  281. are enabled for the rest of the program. }
  282. BEGIN
  283. {$R-}
  284. hash := (ORD(Symbol[1]) * 5 + ORD(Symbol[length(Symbol)])) * 5 + length(Symbol);
  285. {$R+}
  286. END; { of hash }
  287. Procedure CreateHash;
  288. Var psn : Byte;
  289. sym : keysymbol;
  290. begin
  291. FOR psn := 0 TO MAXBYTE DO BEGIN
  292. hashtable[psn].Keyword := ' ';
  293. hashtable[psn].symtype := othersym
  294. END;
  295. FOR sym := endsym TO lastformatsym DO BEGIN
  296. psn := hash(Keyword[sym]);
  297. hashtable[psn].Keyword := Keyword[sym];
  298. hashtable[psn].symtype := sym
  299. END; { for }
  300. end;
  301. Procedure ClassID(Value: Token;
  302. lngth: INTEGER;
  303. VAR idtype: keysymbol;
  304. VAR IsKeyWord: BOOLEAN);
  305. { Classify an identifier. We are only interested
  306. in it if it is a keyword, so we use the hash table. }
  307. VAR
  308. Keyvalue: String[MAXKEYLENGTH];
  309. tabent: INTEGER;
  310. BEGIN
  311. IF lngth > MAXKEYLENGTH THEN BEGIN
  312. idtype := othersym;
  313. IsKeyWord := FALSE
  314. END
  315. ELSE BEGIN
  316. KeyValue:= UpperStr(Value);
  317. tabent := hash(Keyvalue);
  318. IF Keyvalue = hashtable[tabent].Keyword THEN BEGIN
  319. idtype := hashtable[tabent].symtype;
  320. IsKeyWord := TRUE;
  321. END
  322. ELSE BEGIN
  323. idtype := othersym;
  324. IsKeyWord := FALSE;
  325. END
  326. END
  327. END; { of ClassID }
  328. { ---------------------------------------------------------------------
  329. Functions to create options and set defaults.
  330. ---------------------------------------------------------------------}
  331. Procedure CreateOptions (Var Option : OptionTable);
  332. Var Sym : KeySymbol;
  333. begin
  334. FOR sym := endsym TO othersym DO BEGIN
  335. NEW(option[sym]);
  336. option[sym]^.selected := [];
  337. option[sym]^.dindsym := [];
  338. option[sym]^.terminators := []
  339. END;
  340. end;
  341. Procedure SetTerminators(Var Option : OptionTable);
  342. begin
  343. option[casesym]^.terminators := [ofsym];
  344. option[casevarsym]^.terminators := [ofsym];
  345. option[forsym]^.terminators := [dosym];
  346. option[whilesym]^.terminators := [dosym];
  347. option[withsym]^.terminators := [dosym];
  348. option[ifsym]^.terminators := [thensym];
  349. option[untilsym]^.terminators := [endsym, untilsym, elsesym, semicolon];
  350. option[becomes]^.terminators := [endsym, untilsym, elsesym, semicolon];
  351. option[openparen]^.terminators := [closeparen];
  352. end;
  353. Procedure SetDefaultIndents (Var Option : OptionTable);
  354. begin
  355. option[recordsym]^.dindsym := [endsym];
  356. option[funcsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  357. option[procsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  358. option[constsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  359. option[typesym]^.dindsym := [labelsym, constsym, typesym, varsym];
  360. option[varsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  361. option[beginsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  362. option[publicsym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
  363. option[privatesym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
  364. option[protectedsym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
  365. option[publishedsym]^.dindsym := [protectedsym,privatesym,publicsym,publishedsym];
  366. option[finallysym]^.dindsym := [trysym];
  367. option[exceptsym]^.dindsym := [trysym];
  368. option[elsesym]^.dindsym := [ifsym, thensym, elsesym];
  369. option[untilsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
  370. withsym, colon, equals];
  371. option[endsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
  372. withsym, casevarsym, colon, equals, recordsym,
  373. classsym,objectsym];
  374. option[semicolon]^.dindsym := [ifsym, thensym, elsesym, forsym,
  375. whilesym, withsym, colon, equals];
  376. end;
  377. Procedure SetDefaults (Var Option : OptionTable);
  378. { Sets default values for the formatting rules. }
  379. begin
  380. option[progsym]^.selected := [capital,blinbefore, spaft];
  381. option[unitsym]^.selected := [capital,blinbefore, spaft];
  382. option[librarysym]^.selected := [capital,blinbefore, spaft];
  383. option[funcsym]^.selected := [capital,blinbefore, dindonkey, spaft];
  384. option[procsym]^.selected := [capital,blinbefore, dindonkey, spaft];
  385. option[labelsym]^.selected := [capital,blinbefore, spaft, inbytab];
  386. option[constsym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
  387. option[typesym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
  388. option[varsym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
  389. option[beginsym]^.selected := [capital,dindonkey, crbefore, crafter, inbytab];
  390. option[repeatsym]^.selected := [capital,inbytab, crafter];
  391. option[recordsym]^.selected := [capital,inbytab, crafter];
  392. option[objectsym]^.selected := [capital,inbytab, crafter];
  393. option[classsym]^.selected := [capital,inbytab, crafter];
  394. option[publicsym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
  395. option[publishedsym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
  396. option[protectedsym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
  397. option[privatesym]^.selected := [capital,crbefore, dindonkey, spaft, inbytab];
  398. option[trysym]^.Selected := [capital,crbefore,crafter,inbytab];
  399. option[finallysym]^.selected := [capital,crbefore,dindonkey,crafter,inbytab];
  400. option[exceptsym]^.selected := [capital,crbefore,dindonkey,crafter,inbytab];
  401. option[casesym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  402. option[casevarsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  403. option[ofsym]^.selected := [capital,crsupp, spbef];
  404. option[forsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  405. option[whilesym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  406. option[withsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  407. option[dosym]^.selected := [capital,crsupp, spbef];
  408. option[ifsym]^.selected := [capital,spaft, inbytab, gobsym];
  409. option[thensym]^.selected := [capital];
  410. option[elsesym]^.selected := [capital,crbefore, dindonkey, inbytab];
  411. option[endsym]^.selected := [capital,crbefore, crafter,dindonkey,dindent];
  412. option[untilsym]^.selected := [capital,crbefore, dindonkey, dindent, spaft,
  413. gobsym, crafter];
  414. option[becomes]^.selected := [capital,spbef, spaft, gobsym];
  415. option[Delphicomment]^.Selected := [crafter];
  416. option[opencomment]^.selected := [capital,crsupp];
  417. option[closecomment]^.selected := [capital,crsupp];
  418. option[semicolon]^.selected := [capital,crsupp, dindonkey, crafter];
  419. option[colon]^.selected := [capital,inbytab];
  420. option[equals]^.selected := [capital,spbef, spaft, inbytab];
  421. option[openparen]^.selected := [capital,gobsym];
  422. option[period]^.selected := [capital,crsupp];
  423. end;
  424. { ---------------------------------------------------------------------
  425. Stream handling routines
  426. ---------------------------------------------------------------------}
  427. Function ReadChar (S : PStream) : Char;
  428. Var C : Char;
  429. begin
  430. repeat
  431. if S^.GetPos = S^.GetSize then
  432. C:=#0
  433. else
  434. S^.Read(C,1);
  435. Until C<>#13;
  436. ReadChar:=C;
  437. end;
  438. Function EoSLn (S : PStream) : Char;
  439. Const WhiteSpace = [' ', #9, #13 ];
  440. Var C : Char;
  441. begin
  442. Repeat
  443. if S^.GetPos = S^.GetSize then
  444. C:=#0
  445. else
  446. S^.Read(C,1);
  447. Until (Not (C in WhiteSpace)) or ((C=#10));
  448. EoSln:=C;
  449. end;
  450. Function ReadString (S: PStream): String;
  451. Var Buffer : String;
  452. I : Byte;
  453. begin
  454. Buffer:='';
  455. I:=0;
  456. Repeat
  457. S^.Read(Buffer[I+1],1);
  458. Inc(I);
  459. until (I=255) or (Buffer[I]=#10) Or (S^.Status=StReadError);
  460. If S^.Status=stReadError then Dec(I);
  461. If Buffer[i]=#10 Then Dec(I);
  462. If Buffer[I]=#13 then Dec(I);
  463. Buffer[0] := chr(I);
  464. ReadString:=Buffer;
  465. end;
  466. Procedure WriteString (S : PStream; ST : String);
  467. begin
  468. S^.Write(St[1],length(St));
  469. end;
  470. Procedure WriteCR (S: PStream);
  471. Const
  472. Newline = System.LineEnding;
  473. begin
  474. WriteString(S,Newline);
  475. end;
  476. Procedure WriteLnString (S : PStream; ST : String);
  477. begin
  478. WriteString(S,ST);
  479. WriteCR(S);
  480. end;
  481. { ---------------------------------------------------------------------
  482. TPrettyPrinter object
  483. ---------------------------------------------------------------------}
  484. Procedure TPrettyPrinter.Verbose (Const Msg : String);
  485. begin
  486. If Assigned (DiagS) then
  487. WriteLnString (DiagS,Msg);
  488. end;
  489. Procedure TPrettyPrinter.GetChar;
  490. { Read the next character and classify it }
  491. VAR Ch: CHAR;
  492. BEGIN
  493. currchar := nextchar;
  494. WITH nextchar DO
  495. begin
  496. Ch:=ReadCHar(Ins);
  497. If Ch=#0 then
  498. BEGIN
  499. name := filemark;
  500. Value := Blank
  501. END
  502. ELSE If (Ch=#10) THEN
  503. BEGIN
  504. name := endofline;
  505. Value := Blank;
  506. Inc(inlines);
  507. END
  508. ELSE
  509. BEGIN
  510. Value := Ch;
  511. IF Ch IN ['a'..'z', 'A'..'Z', '_'] THEN name := letter
  512. ELSE IF Ch IN ['0'..'9'] THEN name := digit
  513. ELSE IF Ch = '''' THEN name := quote
  514. ELSE IF Ch in [#13,' ',#9] THEN name := space
  515. ELSE name := otherchar
  516. END
  517. end;
  518. END; { of GetChar }
  519. Procedure TPrettyPrinter.StoreNextChar(VAR lngth: INTEGER;
  520. VAR Value: Token);
  521. { Store a character in the current symbol }
  522. BEGIN
  523. GetChar;
  524. IF lngth < maxsymbolsize THEN BEGIN
  525. Inc(lngth);
  526. Value[lngth] := currchar.Value;
  527. Value[0] := chr(Lngth);
  528. END;
  529. END; { of StoreNextChar }
  530. Procedure TPrettyPrinter.SkipBlanks(VAR spacesbefore, crsbefore: INTEGER);
  531. { Count the spaces between symbols }
  532. BEGIN
  533. spacesbefore := 0;
  534. crsbefore := 0;
  535. WHILE nextchar.name IN [space, endofline] DO BEGIN
  536. GetChar;
  537. CASE currchar.name OF
  538. space: Inc(spacesbefore);
  539. endofline: BEGIN
  540. Inc(crsbefore);
  541. spacesbefore := 0;
  542. END;
  543. END; {case}
  544. END;
  545. END; { of SkipBlanks }
  546. Procedure TPrettyPrinter.GetComment(sym: symbolinfo);
  547. { Process comments using either brace or parenthesis notation }
  548. BEGIN
  549. sym^.name := opencomment;
  550. WHILE NOT (((currchar.Value = '*') AND (nextchar.Value = ')'))
  551. OR (currchar.Value = '}') OR (nextchar.name = endofline)
  552. OR (nextchar.name = filemark)) DO
  553. StoreNextChar(sym^.length, sym^.Value);
  554. IF (currchar.Value = '*') AND (nextchar.Value = ')') THEN BEGIN
  555. StoreNextChar(sym^.LENGTH, sym^.Value);
  556. sym^.name := closecomment;
  557. END;
  558. IF currchar.Value = '}' THEN sym^.name := closecomment;
  559. END; { of GetCommment }
  560. Procedure TPrettyPrinter.GetDelphiComment(sym: symbolinfo);
  561. { Process comments using either brace or parenthesis notation }
  562. BEGIN
  563. sym^.name := Delphicomment;
  564. WHILE NOT ((nextchar.name = endofline) OR (nextchar.name = filemark)) DO
  565. StoreNextChar(sym^.length, sym^.Value);
  566. END; { of GetDelphiCommment }
  567. Procedure TPrettyPrinter.GetIdentifier(sym: symbolinfo);
  568. { Read an identifier and classify it }
  569. BEGIN
  570. WHILE nextchar.name IN [letter, digit] DO
  571. StoreNextChar(sym^.length, sym^.Value);
  572. ClassID(sym^.Value, sym^.length, sym^.name, sym^.IsKeyWord);
  573. IF sym^.name IN [recordsym, casesym, endsym] THEN
  574. CASE sym^.name OF
  575. recordsym : RecordSeen := TRUE;
  576. casesym : IF RecordSeen THEN sym^.name := casevarsym;
  577. endsym : RecordSeen := FALSE;
  578. END; {case}
  579. END; { of GetIdentifier }
  580. { Read a number and store it as a string }
  581. Procedure TPrettyPrinter.GetNumber(sym: symbolinfo);
  582. BEGIN
  583. WHILE nextchar.name = digit DO StoreNextChar(sym^.length, sym^.Value);
  584. sym^.name := othersym;
  585. END; { of GetNumber }
  586. PROCEDURE TPrettyPrinter.GetCharLiteral(sym: symbolinfo);
  587. { Read a quoted string }
  588. BEGIN
  589. WHILE nextchar.name = quote DO BEGIN
  590. StoreNextChar(sym^.length, sym^.Value);
  591. WHILE NOT (nextchar.name IN [quote, endofline, filemark]) DO
  592. StoreNextChar(sym^.length, sym^.Value);
  593. IF nextchar.name = quote THEN StoreNextChar(sym^.length, sym^.Value);
  594. END;
  595. sym^.name := othersym;
  596. END; { of GetCharLiteral }
  597. FUNCTION TPrettyPrinter.char_Type: keysymbol;
  598. { Classify a character pair }
  599. VAR
  600. NextTwoChars: SpecialChar;
  601. Hit: BOOLEAN;
  602. thischar: keysymbol;
  603. BEGIN
  604. NextTwoChars[1] := currchar.Value;
  605. NextTwoChars[2] := nextchar.Value;
  606. thischar := becomes;
  607. Hit := FALSE;
  608. WHILE NOT (Hit OR (thischar = closecomment)) DO BEGIN
  609. IF NextTwoChars = DblChar[thischar] THEN Hit := TRUE
  610. ELSE Inc(thischar);
  611. END;
  612. IF NOT Hit THEN BEGIN
  613. thischar := opencomment;
  614. WHILE NOT (Hit OR (PRED(thischar) = period)) DO BEGIN
  615. IF currchar.Value = SglChar[thischar] THEN Hit := TRUE
  616. ELSE Inc(thischar);
  617. END;
  618. END;
  619. IF Hit THEN char_Type := thischar
  620. ELSE char_Type := othersym;
  621. END; { of char_Type }
  622. Procedure TPrettyPrinter.GetSpecialChar(sym: symbolinfo);
  623. { Read special characters }
  624. BEGIN
  625. StoreNextChar(sym^.length, sym^.Value);
  626. sym^.name := char_Type;
  627. IF sym^.name IN dblch THEN StoreNextChar(sym^.length, sym^.Value)
  628. END; { of GetSpecialChar }
  629. Procedure TPrettyPrinter.GetNextSymbol(sym: symbolinfo);
  630. { Read a symbol using the appropriate procedure }
  631. BEGIN
  632. CASE nextchar.name OF
  633. letter: GetIdentifier(sym);
  634. digit: GetNumber(sym);
  635. quote: GetCharLiteral(sym);
  636. otherchar: BEGIN
  637. GetSpecialChar(sym);
  638. IF sym^.name = opencomment THEN GetComment(sym)
  639. else IF sym^.name= DelphiComment then GetDelphiComment(Sym)
  640. END;
  641. filemark: sym^.name := endoffile;
  642. ELSE {:} {Turbo}
  643. WRITELN('Unknown character type: ', ORD(nextchar.name));
  644. END; {case}
  645. END; { of GetNextSymbol }
  646. Procedure TprettyPrinter.GetSymbol;
  647. { Store the next symbol in NEXTSYM }
  648. VAR
  649. dummy: symbolinfo;
  650. BEGIN
  651. dummy := currsym;
  652. currsym := nextsym;
  653. nextsym := dummy;
  654. SkipBlanks(nextsym^.spacesbefore, nextsym^.crsbefore);
  655. nextsym^.length := 0;
  656. nextsym^.IsKeyWord := FALSE;
  657. IF currsym^.name = opencomment THEN GetComment(nextsym)
  658. ELSE GetNextSymbol(nextsym);
  659. END; {of GetSymbol}
  660. Procedure TprettyPrinter.PopStack(VAR indentsymbol: keysymbol;
  661. VAR prevmargin: INTEGER);
  662. { Manage stack of indentation symbols and margins }
  663. BEGIN
  664. IF top > 0 THEN BEGIN
  665. indentsymbol := stack[top].indentsymbol;
  666. prevmargin := stack[top].prevmargin;
  667. Dec(top);
  668. END
  669. ELSE BEGIN
  670. indentsymbol := othersym;
  671. prevmargin := 0;
  672. END;
  673. END; { of PopStack }
  674. Procedure TPrettyPrinter.PushStack(indentsymbol: keysymbol;
  675. prevmargin: INTEGER );
  676. BEGIN
  677. Inc(top);
  678. stack[top].indentsymbol := indentsymbol;
  679. stack[top].prevmargin := prevmargin;
  680. END; { of PushStack }
  681. Procedure TPrettyPrinter.WriteCRs(numberofcrs: INTEGER);
  682. VAR
  683. i: INTEGER;
  684. BEGIN
  685. IF numberofcrs > 0 THEN BEGIN
  686. FOR i := 1 TO numberofcrs DO
  687. WriteCr(OutS);
  688. Inc(outlines,numberofcrs);
  689. currlinepos := 0;
  690. END;
  691. END; { of WriteCRs }
  692. Procedure TPrettyPrinter.InsertCR;
  693. BEGIN
  694. IF currsym^.crsbefore = 0 THEN BEGIN
  695. WriteCRs(1);
  696. currsym^.spacesbefore := 0;
  697. END;
  698. END; { of InsertCR }
  699. Procedure TPrettyPrinter.InsertBlankLine;
  700. BEGIN
  701. IF currsym^.crsbefore = 0 THEN BEGIN
  702. IF currlinepos = 0 THEN WriteCRs(1)
  703. ELSE WriteCRs(2);
  704. currsym^.spacesbefore := 0;
  705. END
  706. ELSE IF currsym^.crsbefore = 1 THEN
  707. IF currlinepos > 0 THEN WriteCRs(1);
  708. END; { of InsertBlankLine }
  709. Procedure TPrettyPrinter.LShiftOn(dindsym: keysymset);
  710. { Move margin left according to stack configuration and current symbol }
  711. VAR
  712. indentsymbol: keysymbol;
  713. prevmargin: INTEGER;
  714. BEGIN
  715. IF top > 0 THEN BEGIN
  716. REPEAT
  717. PopStack(indentsymbol, prevmargin);
  718. IF indentsymbol IN dindsym THEN currmargin := prevmargin;
  719. UNTIL NOT (indentsymbol IN dindsym) OR (top = 0);
  720. IF NOT (indentsymbol IN dindsym) THEN
  721. PushStack(indentsymbol, prevmargin);
  722. END;
  723. END; { of LShiftOn }
  724. Procedure TprettyPrinter.LShift;
  725. { Move margin left according to stack top }
  726. VAR
  727. indentsymbol: keysymbol;
  728. prevmargin: INTEGER;
  729. BEGIN
  730. IF top > 0 THEN BEGIN
  731. PopStack(indentsymbol, prevmargin);
  732. currmargin := prevmargin;
  733. (* maybe PopStack(indentsymbol,currmargin); *)
  734. END;
  735. END; { of LShift }
  736. Procedure TPrettyPrinter.InsertSpace(VAR symbol: symbolinfo);
  737. { Insert space if room on line }
  738. BEGIN
  739. IF currlinepos < LineSize THEN BEGIN
  740. WriteString(OutS, Blank);
  741. Inc(currlinepos);
  742. IF (symbol^.crsbefore = 0) AND (symbol^.spacesbefore > 0)
  743. THEN Dec(symbol^.spacesbefore);
  744. END;
  745. END; { of InsertSpace }
  746. Procedure TPrettyPrinter.MoveLinePos(newlinepos: INTEGER);
  747. { Insert spaces until correct line position reached }
  748. VAR i: INTEGER;
  749. BEGIN
  750. FOR i := SUCC(currlinepos) TO newlinepos DO
  751. WriteString(OutS, Blank);
  752. currlinepos := newlinepos;
  753. END; { of MoveLinePos }
  754. Procedure TPrettyPrinter.PrintSymbol;
  755. BEGIN
  756. IF (currsym^.IsKeyWord) then
  757. begin
  758. If upper in sets^.selected Then
  759. WriteString (OutS,UpperStr(currsym^.value))
  760. else if lower in sets^.selected then
  761. WriteString (OutS,LowerStr(currsym^.value))
  762. else if capital in sets^.selected then
  763. begin
  764. WriteString(OutS,UpCase(CurrSym^.Value[1]));
  765. WriteString(OutS,LowerStr(Copy(CurrSym^.Value,2,255)));
  766. end
  767. else
  768. WriteString(OutS,Currsym^.Value);
  769. end
  770. ELSE
  771. WriteString(OutS, currsym^.Value);
  772. startpos := currlinepos;
  773. Inc(currlinepos,currsym^.length);
  774. END; { of PrintSymbol }
  775. Procedure TPrettyPrinter.PPSymbol;
  776. { Find position for symbol and then print it }
  777. VAR newlinepos: INTEGER;
  778. BEGIN
  779. WriteCRs(currsym^.crsbefore);
  780. IF (currlinepos + currsym^.spacesbefore > currmargin)
  781. OR (currsym^.name IN [opencomment, closecomment])
  782. THEN newlinepos := currlinepos + currsym^.spacesbefore
  783. ELSE newlinepos := currmargin;
  784. IF newlinepos + currsym^.length > LINESIZE THEN BEGIN
  785. WriteCRs(1);
  786. IF currmargin + currsym^.length <= LINESIZE
  787. THEN newlinepos := currmargin
  788. ELSE IF currsym^.length < LINESIZE
  789. THEN newlinepos := LINESIZE - currsym^.length
  790. ELSE newlinepos := 0;
  791. END;
  792. MoveLinePos(newlinepos);
  793. PrintSymbol;
  794. END; { of PPSymbol }
  795. Procedure TPrettyPrinter.Gobble(terminators: keysymset);
  796. { Print symbols which follow a formatting symbol but which do not
  797. affect layout }
  798. BEGIN
  799. IF top < MAXSTACKSIZE THEN PushStack(currsym^.name, currmargin);
  800. currmargin := currlinepos;
  801. WHILE NOT ((nextsym^.name IN terminators)
  802. OR (nextsym^.name = endoffile)) DO BEGIN
  803. GetSymbol;
  804. PPSymbol;
  805. END;
  806. LShift;
  807. END; { of Gobble }
  808. Procedure TprettyPrinter.RShift(currmsym: keysymbol);
  809. { Move right, stacking margin positions }
  810. BEGIN
  811. IF top < MAXSTACKSIZE THEN PushStack(currmsym, currmargin);
  812. IF startpos > currmargin THEN currmargin := startpos;
  813. Inc(currmargin,INDENT);
  814. END; { of RShift }
  815. Function TPrettyPrinter.ReadConfigFile : Boolean;
  816. Var I,J : Longint;
  817. Procedure SetOption(TheKey : KeySymbol;Var OptionList : String);
  818. Var TheOpt : Options;
  819. Found : Boolean;
  820. K : longint;
  821. opt : string;
  822. begin
  823. Repeat
  824. K:=pos(',',optionlist);
  825. If k>0 then
  826. begin
  827. opt:=Copy(OptionList,1,k-1);
  828. strip(opt);
  829. Delete(OptionList,1,k);
  830. end
  831. else
  832. opt:=OptionList;
  833. If Length(Opt)>0 then
  834. begin
  835. Found:=False;
  836. for TheOpt :=firstopt to lastopt do
  837. begin
  838. found:=opt=OptionNames[Theopt];
  839. If found then break;
  840. end;
  841. If not found then
  842. Verbose ('Unknown option on line '+inttostr(i)+': '+Opt)
  843. else
  844. Option[TheKey]^.Selected:=Option[TheKey]^.Selected+[TheOpt];
  845. end;
  846. until k=0;
  847. end;
  848. Procedure SetIndent(TheKey : KeySymbol; Var OptionList : String);
  849. Var
  850. TheIndent : Keysymbol;
  851. Found : Boolean;
  852. K : longint;
  853. opt : string;
  854. begin
  855. Repeat
  856. K:=pos(',',optionlist);
  857. If k>0 then
  858. begin
  859. opt:=Copy(OptionList,1,k-1);
  860. strip(opt);
  861. Delete(OptionList,1,k);
  862. end
  863. else
  864. opt:=OptionList;
  865. If Length(Opt)>0 then
  866. begin
  867. Found:=False;
  868. for TheIndent :=firstKey to lastKey do
  869. begin
  870. found:=opt=EntryNames[Theindent];
  871. If found then break;
  872. end;
  873. If not found then
  874. begin
  875. Verbose ('Unknown indent keysym on line '+inttostr(i)+': '+Opt);
  876. exit;
  877. end;
  878. Option[TheKey]^.dindsym:=Option[TheKey]^.dindsym+[Theindent];
  879. end;
  880. until k=0;
  881. end;
  882. Var TheKey : KeySymbol;
  883. Found,DoIndent : Boolean;
  884. Line, Name : String;
  885. begin
  886. ReadConfigFile:=false;
  887. I:=0;
  888. while not (CfgS^.Status=stReadError) do
  889. begin
  890. inc(i);
  891. Line:='';
  892. Line:=ReadString(cfgS);
  893. { Strip comment }
  894. If pos('#',Line)<>0 then
  895. Line:=Copy(Line,1,Pos('#',Line)-1);
  896. If length(Line)<>0 then
  897. begin
  898. J:=Pos('=',Line);
  899. If J>0 then
  900. begin
  901. Line:=LowerStr(Line);
  902. Name:=Copy(Line,1,j-1);
  903. Delete(Line,1,J);
  904. { indents or options ? }
  905. If (Name[1]='[') and
  906. (Name[Length(Name)]=']') then
  907. begin
  908. Name:=Copy(Name,2,Length(Name)-2);
  909. Doindent:=True;
  910. end
  911. else
  912. DoIndent:=False;
  913. Strip(Name);
  914. found:=false;
  915. for thekey:=firstkey to lastkey do
  916. begin
  917. found:=Name=EntryNames[thekey];
  918. If Found then break;
  919. end;
  920. If not found then
  921. Verbose ('Unknown keyword on line '+inttostr(i)+': '+Name)
  922. else
  923. If DoIndent then
  924. SetIndent(TheKey,Line)
  925. else
  926. SetOption(TheKey,Line)
  927. end
  928. else
  929. verbose ('Error in config file on line '+IntToStr(i));
  930. end;
  931. end;
  932. Verbose ('Processed configfile: read '+IntToStr(I)+' lines');
  933. ReadConfigFile:=true;
  934. end;
  935. Procedure GenerateCfgFile(S : PStream);
  936. Var TheKey,TheIndent : KeySymbol;
  937. TheOpt : Options;
  938. Written : Boolean;
  939. Option : OptionTable;
  940. begin
  941. CreateOptions(option);
  942. SetDefaults(option);
  943. SetDefaultIndents(option);
  944. For TheKey:=Firstkey to lastkey do
  945. begin
  946. { Write options }
  947. WriteString (S,EntryNames[TheKey]+'=');
  948. Written:=False;
  949. for TheOpt:=FirstOpt to LastOpt do
  950. If TheOpt in Option[TheKey]^.Selected then
  951. begin
  952. if written then
  953. WriteString (S,',')
  954. else
  955. Written:=True;
  956. writeString (S,OptionNames[TheOpt]);
  957. end;
  958. WriteCr (S);
  959. { Write de-indent keysyms, if any }
  960. If Option[TheKey]^.dindsym<>[] then
  961. begin
  962. WriteString (S,'['+EntryNames[TheKey]+']=');
  963. Written:=False;
  964. For TheIndent:=FirstKey to lastkey do
  965. If TheIndent in Option[TheKey]^.dindsym then
  966. begin
  967. if written then
  968. WriteString (S,',')
  969. else
  970. Written:=True;
  971. WriteString (S,EntryNames[Theindent]);
  972. end;
  973. WriteCr (S);
  974. end;
  975. end;
  976. end;
  977. Function TPrettyPrinter.PrettyPrint : Boolean;
  978. Begin
  979. PrettyPrint:=False;
  980. If Not Assigned(Ins) or Not Assigned(OutS) then
  981. exit;
  982. If Not Assigned(CfgS) then
  983. begin
  984. SetDefaults(Option);
  985. SetDefaultIndents(Option);
  986. end
  987. else
  988. ReadConfigFile;
  989. { Initialize variables }
  990. top := 0;
  991. currlinepos := 0;
  992. currmargin := 0;
  993. inlines := 0;
  994. outlines := 0;
  995. CrPending := FALSE;
  996. RecordSeen := FALSE;
  997. GetChar;
  998. NEW(currsym);
  999. NEW(nextsym);
  1000. GetSymbol;
  1001. WHILE nextsym^.name <> endoffile DO BEGIN
  1002. GetSymbol;
  1003. sets := option[currsym^.name];
  1004. IF (CrPending AND NOT (crsupp IN sets^.selected))
  1005. OR (crbefore IN sets^.selected) THEN BEGIN
  1006. InsertCR;
  1007. CrPending := FALSE
  1008. END;
  1009. IF blinbefore IN sets^.selected THEN BEGIN
  1010. InsertBlankLine;
  1011. CrPending := FALSE
  1012. END;
  1013. IF dindonkey IN sets^.selected THEN LShiftOn(sets^.dindsym);
  1014. IF dindent IN sets^.selected THEN LShift;
  1015. IF spbef IN sets^.selected THEN InsertSpace(currsym);
  1016. PPSymbol;
  1017. IF spaft IN sets^.selected THEN InsertSpace(nextsym);
  1018. IF inbytab IN sets^.selected THEN RShift(currsym^.name);
  1019. IF gobsym IN sets^.selected THEN Gobble(sets^.terminators);
  1020. IF crafter IN sets^.selected THEN CrPending := TRUE
  1021. END;
  1022. IF CrPending THEN WriteCRs(1);
  1023. Verbose(IntToStr(inlines)+' lines read, '+IntToStr(outlines)+' lines written.');
  1024. PrettyPrint:=True;
  1025. end;
  1026. Constructor TPrettyPrinter.Create;
  1027. Begin
  1028. LineSize:=MaxLineSize;
  1029. CreateOptions (Option);
  1030. SetTerminators(Option);
  1031. DiagS:=Nil;
  1032. InS:=Nil;
  1033. OutS:=Nil;
  1034. CfgS:=Nil;
  1035. End;
  1036. { ---------------------------------------------------------------------
  1037. Unit initialization
  1038. ---------------------------------------------------------------------}
  1039. Begin
  1040. CreateHash;
  1041. dblch := [becomes, opencomment];
  1042. end.
  1043. {
  1044. $Log$
  1045. Revision 1.6 2003-03-27 14:23:00 michael
  1046. + Fixed use of linesize property, reported by Wolfgang Waffenschmidt
  1047. Revision 1.5 2002/09/07 15:40:31 peter
  1048. * old logs removed and tabs fixed
  1049. Revision 1.4 2002/07/14 13:39:45 carl
  1050. * use special symbols for portability's sake
  1051. Revision 1.3 2002/06/01 18:39:14 marco
  1052. * Renamefest
  1053. Revision 1.2 2002/02/27 16:35:31 carl
  1054. * bugfix of stream errors - would always give error!
  1055. }