ptopu.pp 35 KB

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