ptopu.pp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  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. S^.Read(C,1);
  432. If S^.Status=stReadError then
  433. C:=#0;
  434. Until C<>#13;
  435. ReadChar:=C;
  436. end;
  437. Function EoSLn (S : PStream) : Char;
  438. Const WhiteSpace = [' ', #9, #13 ];
  439. Var C : Char;
  440. begin
  441. Repeat
  442. S^.Read(C,1);
  443. Until (Not (C in WhiteSpace)) or ((C=#10) or (S^.Status=stReadError));
  444. If S^.Status=stReadError then
  445. EoSln:=#0
  446. else
  447. EoSln:=C;
  448. end;
  449. Function ReadString (S: PStream): String;
  450. Var Buffer : String;
  451. I : Byte;
  452. begin
  453. Buffer:='';
  454. I:=0;
  455. Repeat
  456. S^.Read(Buffer[I+1],1);
  457. Inc(I);
  458. until (I=255) or (Buffer[I]=#10) Or (S^.Status=StReadError);
  459. If S^.Status=stReadError then Dec(I);
  460. If Buffer[i]=#10 Then Dec(I);
  461. If Buffer[I]=#13 then Dec(I);
  462. Buffer[0] := chr(I);
  463. ReadString:=Buffer;
  464. end;
  465. Procedure WriteString (S : PStream; ST : String);
  466. begin
  467. S^.Write(St[1],length(St));
  468. end;
  469. Procedure WriteCR (S: PStream);
  470. Const
  471. {$ifdef linux}
  472. Newline = #10;
  473. {$else}
  474. NewLine = #13#10;
  475. {$endif}
  476. begin
  477. WriteString(S,Newline);
  478. end;
  479. Procedure WriteLnString (S : PStream; ST : String);
  480. begin
  481. WriteString(S,ST);
  482. WriteCR(S);
  483. end;
  484. { ---------------------------------------------------------------------
  485. TPrettyPrinter object
  486. ---------------------------------------------------------------------}
  487. Procedure TPrettyPrinter.Verbose (Const Msg : String);
  488. begin
  489. If Assigned (DiagS) then
  490. WriteLnString (DiagS,Msg);
  491. end;
  492. Procedure TPrettyPrinter.GetChar;
  493. { Read the next character and classify it }
  494. VAR Ch: CHAR;
  495. BEGIN
  496. currchar := nextchar;
  497. WITH nextchar DO
  498. begin
  499. Ch:=ReadCHar(Ins);
  500. If Ch=#0 then
  501. BEGIN
  502. name := filemark;
  503. Value := Blank
  504. END
  505. ELSE If (Ch=#10) THEN
  506. BEGIN
  507. name := endofline;
  508. Value := Blank;
  509. Inc(inlines);
  510. END
  511. ELSE
  512. BEGIN
  513. Value := Ch;
  514. IF Ch IN ['a'..'z', 'A'..'Z', '_'] THEN name := letter
  515. ELSE IF Ch IN ['0'..'9'] THEN name := digit
  516. ELSE IF Ch = '''' THEN name := quote
  517. ELSE IF Ch in [#13,' ',#9] THEN name := space
  518. ELSE name := otherchar
  519. END
  520. end;
  521. END; { of GetChar }
  522. Procedure TPrettyPrinter.StoreNextChar(VAR lngth: INTEGER;
  523. VAR Value: Token);
  524. { Store a character in the current symbol }
  525. BEGIN
  526. GetChar;
  527. IF lngth < maxsymbolsize THEN BEGIN
  528. Inc(lngth);
  529. Value[lngth] := currchar.Value;
  530. Value[0] := chr(Lngth);
  531. END;
  532. END; { of StoreNextChar }
  533. Procedure TPrettyPrinter.SkipBlanks(VAR spacesbefore, crsbefore: INTEGER);
  534. { Count the spaces between symbols }
  535. BEGIN
  536. spacesbefore := 0;
  537. crsbefore := 0;
  538. WHILE nextchar.name IN [space, endofline] DO BEGIN
  539. GetChar;
  540. CASE currchar.name OF
  541. space: Inc(spacesbefore);
  542. endofline: BEGIN
  543. Inc(crsbefore);
  544. spacesbefore := 0;
  545. END;
  546. END; {case}
  547. END;
  548. END; { of SkipBlanks }
  549. Procedure TPrettyPrinter.GetComment(sym: symbolinfo);
  550. { Process comments using either brace or parenthesis notation }
  551. BEGIN
  552. sym^.name := opencomment;
  553. WHILE NOT (((currchar.Value = '*') AND (nextchar.Value = ')'))
  554. OR (currchar.Value = '}') OR (nextchar.name = endofline)
  555. OR (nextchar.name = filemark)) DO
  556. StoreNextChar(sym^.length, sym^.Value);
  557. IF (currchar.Value = '*') AND (nextchar.Value = ')') THEN BEGIN
  558. StoreNextChar(sym^.LENGTH, sym^.Value);
  559. sym^.name := closecomment;
  560. END;
  561. IF currchar.Value = '}' THEN sym^.name := closecomment;
  562. END; { of GetCommment }
  563. Procedure TPrettyPrinter.GetDelphiComment(sym: symbolinfo);
  564. { Process comments using either brace or parenthesis notation }
  565. BEGIN
  566. sym^.name := Delphicomment;
  567. WHILE NOT ((nextchar.name = endofline) OR (nextchar.name = filemark)) DO
  568. StoreNextChar(sym^.length, sym^.Value);
  569. END; { of GetDelphiCommment }
  570. Procedure TPrettyPrinter.GetIdentifier(sym: symbolinfo);
  571. { Read an identifier and classify it }
  572. BEGIN
  573. WHILE nextchar.name IN [letter, digit] DO
  574. StoreNextChar(sym^.length, sym^.Value);
  575. ClassID(sym^.Value, sym^.length, sym^.name, sym^.IsKeyWord);
  576. IF sym^.name IN [recordsym, casesym, endsym] THEN
  577. CASE sym^.name OF
  578. recordsym : RecordSeen := TRUE;
  579. casesym : IF RecordSeen THEN sym^.name := casevarsym;
  580. endsym : RecordSeen := FALSE;
  581. END; {case}
  582. END; { of GetIdentifier }
  583. { Read a number and store it as a string }
  584. Procedure TPrettyPrinter.GetNumber(sym: symbolinfo);
  585. BEGIN
  586. WHILE nextchar.name = digit DO StoreNextChar(sym^.length, sym^.Value);
  587. sym^.name := othersym;
  588. END; { of GetNumber }
  589. PROCEDURE TPrettyPrinter.GetCharLiteral(sym: symbolinfo);
  590. { Read a quoted string }
  591. BEGIN
  592. WHILE nextchar.name = quote DO BEGIN
  593. StoreNextChar(sym^.length, sym^.Value);
  594. WHILE NOT (nextchar.name IN [quote, endofline, filemark]) DO
  595. StoreNextChar(sym^.length, sym^.Value);
  596. IF nextchar.name = quote THEN StoreNextChar(sym^.length, sym^.Value);
  597. END;
  598. sym^.name := othersym;
  599. END; { of GetCharLiteral }
  600. FUNCTION TPrettyPrinter.char_Type: keysymbol;
  601. { Classify a character pair }
  602. VAR
  603. NextTwoChars: SpecialChar;
  604. Hit: BOOLEAN;
  605. thischar: keysymbol;
  606. BEGIN
  607. NextTwoChars[1] := currchar.Value;
  608. NextTwoChars[2] := nextchar.Value;
  609. thischar := becomes;
  610. Hit := FALSE;
  611. WHILE NOT (Hit OR (thischar = closecomment)) DO BEGIN
  612. IF NextTwoChars = DblChar[thischar] THEN Hit := TRUE
  613. ELSE Inc(thischar);
  614. END;
  615. IF NOT Hit THEN BEGIN
  616. thischar := opencomment;
  617. WHILE NOT (Hit OR (PRED(thischar) = period)) DO BEGIN
  618. IF currchar.Value = SglChar[thischar] THEN Hit := TRUE
  619. ELSE Inc(thischar);
  620. END;
  621. END;
  622. IF Hit THEN char_Type := thischar
  623. ELSE char_Type := othersym;
  624. END; { of char_Type }
  625. Procedure TPrettyPrinter.GetSpecialChar(sym: symbolinfo);
  626. { Read special characters }
  627. BEGIN
  628. StoreNextChar(sym^.length, sym^.Value);
  629. sym^.name := char_Type;
  630. IF sym^.name IN dblch THEN StoreNextChar(sym^.length, sym^.Value)
  631. END; { of GetSpecialChar }
  632. Procedure TPrettyPrinter.GetNextSymbol(sym: symbolinfo);
  633. { Read a symbol using the appropriate procedure }
  634. BEGIN
  635. CASE nextchar.name OF
  636. letter: GetIdentifier(sym);
  637. digit: GetNumber(sym);
  638. quote: GetCharLiteral(sym);
  639. otherchar: BEGIN
  640. GetSpecialChar(sym);
  641. IF sym^.name = opencomment THEN GetComment(sym)
  642. else IF sym^.name= DelphiComment then GetDelphiComment(Sym)
  643. END;
  644. filemark: sym^.name := endoffile;
  645. ELSE {:} {Turbo}
  646. WRITELN('Unknown character type: ', ORD(nextchar.name));
  647. END; {case}
  648. END; { of GetNextSymbol }
  649. Procedure TprettyPrinter.GetSymbol;
  650. { Store the next symbol in NEXTSYM }
  651. VAR
  652. dummy: symbolinfo;
  653. BEGIN
  654. dummy := currsym;
  655. currsym := nextsym;
  656. nextsym := dummy;
  657. SkipBlanks(nextsym^.spacesbefore, nextsym^.crsbefore);
  658. nextsym^.length := 0;
  659. nextsym^.IsKeyWord := FALSE;
  660. IF currsym^.name = opencomment THEN GetComment(nextsym)
  661. ELSE GetNextSymbol(nextsym);
  662. END; {of GetSymbol}
  663. Procedure TprettyPrinter.PopStack(VAR indentsymbol: keysymbol;
  664. VAR prevmargin: INTEGER);
  665. { Manage stack of indentation symbols and margins }
  666. BEGIN
  667. IF top > 0 THEN BEGIN
  668. indentsymbol := stack[top].indentsymbol;
  669. prevmargin := stack[top].prevmargin;
  670. Dec(top);
  671. END
  672. ELSE BEGIN
  673. indentsymbol := othersym;
  674. prevmargin := 0;
  675. END;
  676. END; { of PopStack }
  677. Procedure TPrettyPrinter.PushStack(indentsymbol: keysymbol;
  678. prevmargin: INTEGER );
  679. BEGIN
  680. Inc(top);
  681. stack[top].indentsymbol := indentsymbol;
  682. stack[top].prevmargin := prevmargin;
  683. END; { of PushStack }
  684. Procedure TPrettyPrinter.WriteCRs(numberofcrs: INTEGER);
  685. VAR
  686. i: INTEGER;
  687. BEGIN
  688. IF numberofcrs > 0 THEN BEGIN
  689. FOR i := 1 TO numberofcrs DO
  690. WriteCr(OutS);
  691. Inc(outlines,numberofcrs);
  692. currlinepos := 0;
  693. END;
  694. END; { of WriteCRs }
  695. Procedure TPrettyPrinter.InsertCR;
  696. BEGIN
  697. IF currsym^.crsbefore = 0 THEN BEGIN
  698. WriteCRs(1);
  699. currsym^.spacesbefore := 0;
  700. END;
  701. END; { of InsertCR }
  702. Procedure TPrettyPrinter.InsertBlankLine;
  703. BEGIN
  704. IF currsym^.crsbefore = 0 THEN BEGIN
  705. IF currlinepos = 0 THEN WriteCRs(1)
  706. ELSE WriteCRs(2);
  707. currsym^.spacesbefore := 0;
  708. END
  709. ELSE IF currsym^.crsbefore = 1 THEN
  710. IF currlinepos > 0 THEN WriteCRs(1);
  711. END; { of InsertBlankLine }
  712. Procedure TPrettyPrinter.LShiftOn(dindsym: keysymset);
  713. { Move margin left according to stack configuration and current symbol }
  714. VAR
  715. indentsymbol: keysymbol;
  716. prevmargin: INTEGER;
  717. BEGIN
  718. IF top > 0 THEN BEGIN
  719. REPEAT
  720. PopStack(indentsymbol, prevmargin);
  721. IF indentsymbol IN dindsym THEN currmargin := prevmargin;
  722. UNTIL NOT (indentsymbol IN dindsym) OR (top = 0);
  723. IF NOT (indentsymbol IN dindsym) THEN
  724. PushStack(indentsymbol, prevmargin);
  725. END;
  726. END; { of LShiftOn }
  727. Procedure TprettyPrinter.LShift;
  728. { Move margin left according to stack top }
  729. VAR
  730. indentsymbol: keysymbol;
  731. prevmargin: INTEGER;
  732. BEGIN
  733. IF top > 0 THEN BEGIN
  734. PopStack(indentsymbol, prevmargin);
  735. currmargin := prevmargin;
  736. (* maybe PopStack(indentsymbol,currmargin); *)
  737. END;
  738. END; { of LShift }
  739. Procedure TPrettyPrinter.InsertSpace(VAR symbol: symbolinfo);
  740. { Insert space if room on line }
  741. BEGIN
  742. IF currlinepos < LineSize THEN BEGIN
  743. WriteString(OutS, Blank);
  744. Inc(currlinepos);
  745. IF (symbol^.crsbefore = 0) AND (symbol^.spacesbefore > 0)
  746. THEN Dec(symbol^.spacesbefore);
  747. END;
  748. END; { of InsertSpace }
  749. Procedure TPrettyPrinter.MoveLinePos(newlinepos: INTEGER);
  750. { Insert spaces until correct line position reached }
  751. VAR i: INTEGER;
  752. BEGIN
  753. FOR i := SUCC(currlinepos) TO newlinepos DO
  754. WriteString(OutS, Blank);
  755. currlinepos := newlinepos;
  756. END; { of MoveLinePos }
  757. Procedure TPrettyPrinter.PrintSymbol;
  758. BEGIN
  759. IF (currsym^.IsKeyWord) then
  760. begin
  761. If upper in sets^.selected Then
  762. WriteString (OutS,UpperStr(currsym^.value))
  763. else if lower in sets^.selected then
  764. WriteString (OutS,LowerStr(currsym^.value))
  765. else if capital in sets^.selected then
  766. begin
  767. WriteString(OutS,UpCase(CurrSym^.Value[1]));
  768. WriteString(OutS,LowerStr(Copy(CurrSym^.Value,2,255)));
  769. end
  770. else
  771. WriteString(OutS,Currsym^.Value);
  772. end
  773. ELSE
  774. WriteString(OutS, currsym^.Value);
  775. startpos := currlinepos;
  776. Inc(currlinepos,currsym^.length);
  777. END; { of PrintSymbol }
  778. Procedure TPrettyPrinter.PPSymbol;
  779. { Find position for symbol and then print it }
  780. VAR newlinepos: INTEGER;
  781. BEGIN
  782. WriteCRs(currsym^.crsbefore);
  783. IF (currlinepos + currsym^.spacesbefore > currmargin)
  784. OR (currsym^.name IN [opencomment, closecomment])
  785. THEN newlinepos := currlinepos + currsym^.spacesbefore
  786. ELSE newlinepos := currmargin;
  787. IF newlinepos + currsym^.length > MAXLINESIZE THEN BEGIN
  788. WriteCRs(1);
  789. IF currmargin + currsym^.length <= MAXLINESIZE
  790. THEN newlinepos := currmargin
  791. ELSE IF currsym^.length < MAXLINESIZE
  792. THEN newlinepos := MAXLINESIZE - currsym^.length
  793. ELSE newlinepos := 0;
  794. END;
  795. MoveLinePos(newlinepos);
  796. PrintSymbol;
  797. END; { of PPSymbol }
  798. Procedure TPrettyPrinter.Gobble(terminators: keysymset);
  799. { Print symbols which follow a formatting symbol but which do not
  800. affect layout }
  801. BEGIN
  802. IF top < MAXSTACKSIZE THEN PushStack(currsym^.name, currmargin);
  803. currmargin := currlinepos;
  804. WHILE NOT ((nextsym^.name IN terminators)
  805. OR (nextsym^.name = endoffile)) DO BEGIN
  806. GetSymbol;
  807. PPSymbol;
  808. END;
  809. LShift;
  810. END; { of Gobble }
  811. Procedure TprettyPrinter.RShift(currmsym: keysymbol);
  812. { Move right, stacking margin positions }
  813. BEGIN
  814. IF top < MAXSTACKSIZE THEN PushStack(currmsym, currmargin);
  815. IF startpos > currmargin THEN currmargin := startpos;
  816. Inc(currmargin,INDENT);
  817. END; { of RShift }
  818. Function TPrettyPrinter.ReadConfigFile : Boolean;
  819. Var I,J : Longint;
  820. Procedure SetOption(TheKey : KeySymbol;Var OptionList : String);
  821. Var TheOpt : Options;
  822. Found : Boolean;
  823. K : longint;
  824. opt : string;
  825. begin
  826. Repeat
  827. K:=pos(',',optionlist);
  828. If k>0 then
  829. begin
  830. opt:=Copy(OptionList,1,k-1);
  831. strip(opt);
  832. Delete(OptionList,1,k);
  833. end
  834. else
  835. opt:=OptionList;
  836. If Length(Opt)>0 then
  837. begin
  838. Found:=False;
  839. for TheOpt :=firstopt to lastopt do
  840. begin
  841. found:=opt=OptionNames[Theopt];
  842. If found then break;
  843. end;
  844. If not found then
  845. Verbose ('Unknown option on line '+inttostr(i)+': '+Opt)
  846. else
  847. Option[TheKey]^.Selected:=Option[TheKey]^.Selected+[TheOpt];
  848. end;
  849. until k=0;
  850. end;
  851. Procedure SetIndent(TheKey : KeySymbol; Var OptionList : String);
  852. Var
  853. TheIndent : Keysymbol;
  854. Found : Boolean;
  855. K : longint;
  856. opt : string;
  857. begin
  858. Repeat
  859. K:=pos(',',optionlist);
  860. If k>0 then
  861. begin
  862. opt:=Copy(OptionList,1,k-1);
  863. strip(opt);
  864. Delete(OptionList,1,k);
  865. end
  866. else
  867. opt:=OptionList;
  868. If Length(Opt)>0 then
  869. begin
  870. Found:=False;
  871. for TheIndent :=firstKey to lastKey do
  872. begin
  873. found:=opt=EntryNames[Theindent];
  874. If found then break;
  875. end;
  876. If not found then
  877. begin
  878. Verbose ('Unknown indent keysym on line '+inttostr(i)+': '+Opt);
  879. exit;
  880. end;
  881. Option[TheKey]^.dindsym:=Option[TheKey]^.dindsym+[Theindent];
  882. end;
  883. until k=0;
  884. end;
  885. Var TheKey : KeySymbol;
  886. Found,DoIndent : Boolean;
  887. Line, Name : String;
  888. begin
  889. ReadConfigFile:=false;
  890. I:=0;
  891. while not (CfgS^.Status=stReadError) do
  892. begin
  893. inc(i);
  894. Line:='';
  895. Line:=ReadString(cfgS);
  896. { Strip comment }
  897. If pos('#',Line)<>0 then
  898. Line:=Copy(Line,1,Pos('#',Line)-1);
  899. If length(Line)<>0 then
  900. begin
  901. J:=Pos('=',Line);
  902. If J>0 then
  903. begin
  904. Line:=LowerStr(Line);
  905. Name:=Copy(Line,1,j-1);
  906. Delete(Line,1,J);
  907. { indents or options ? }
  908. If (Name[1]='[') and
  909. (Name[Length(Name)]=']') then
  910. begin
  911. Name:=Copy(Name,2,Length(Name)-2);
  912. Doindent:=True;
  913. end
  914. else
  915. DoIndent:=False;
  916. Strip(Name);
  917. found:=false;
  918. for thekey:=firstkey to lastkey do
  919. begin
  920. found:=Name=EntryNames[thekey];
  921. If Found then break;
  922. end;
  923. If not found then
  924. Verbose ('Unknown keyword on line '+inttostr(i)+': '+Name)
  925. else
  926. If DoIndent then
  927. SetIndent(TheKey,Line)
  928. else
  929. SetOption(TheKey,Line)
  930. end
  931. else
  932. verbose ('Error in config file on line '+IntToStr(i));
  933. end;
  934. end;
  935. Verbose ('Processed configfile: read '+IntToStr(I)+' lines');
  936. ReadConfigFile:=true;
  937. end;
  938. Procedure GenerateCfgFile(S : PStream);
  939. Var TheKey,TheIndent : KeySymbol;
  940. TheOpt : Options;
  941. Written : Boolean;
  942. Option : OptionTable;
  943. begin
  944. CreateOptions(option);
  945. SetDefaults(option);
  946. SetDefaultIndents(option);
  947. For TheKey:=Firstkey to lastkey do
  948. begin
  949. { Write options }
  950. WriteString (S,EntryNames[TheKey]+'=');
  951. Written:=False;
  952. for TheOpt:=FirstOpt to LastOpt do
  953. If TheOpt in Option[TheKey]^.Selected then
  954. begin
  955. if written then
  956. WriteString (S,',')
  957. else
  958. Written:=True;
  959. writeString (S,OptionNames[TheOpt]);
  960. end;
  961. WriteCr (S);
  962. { Write de-indent keysyms, if any }
  963. If Option[TheKey]^.dindsym<>[] then
  964. begin
  965. WriteString (S,'['+EntryNames[TheKey]+']=');
  966. Written:=False;
  967. For TheIndent:=FirstKey to lastkey do
  968. If TheIndent in Option[TheKey]^.dindsym then
  969. begin
  970. if written then
  971. WriteString (S,',')
  972. else
  973. Written:=True;
  974. WriteString (S,EntryNames[Theindent]);
  975. end;
  976. WriteCr (S);
  977. end;
  978. end;
  979. end;
  980. Function TPrettyPrinter.PrettyPrint : Boolean;
  981. Begin
  982. PrettyPrint:=False;
  983. If Not Assigned(Ins) or Not Assigned(OutS) then
  984. exit;
  985. If Not Assigned(CfgS) then
  986. begin
  987. SetDefaults(Option);
  988. SetDefaultIndents(Option);
  989. end
  990. else
  991. ReadConfigFile;
  992. { Initialize variables }
  993. top := 0;
  994. currlinepos := 0;
  995. currmargin := 0;
  996. inlines := 0;
  997. outlines := 0;
  998. CrPending := FALSE;
  999. RecordSeen := FALSE;
  1000. GetChar;
  1001. NEW(currsym);
  1002. NEW(nextsym);
  1003. GetSymbol;
  1004. WHILE nextsym^.name <> endoffile DO BEGIN
  1005. GetSymbol;
  1006. sets := option[currsym^.name];
  1007. IF (CrPending AND NOT (crsupp IN sets^.selected))
  1008. OR (crbefore IN sets^.selected) THEN BEGIN
  1009. InsertCR;
  1010. CrPending := FALSE
  1011. END;
  1012. IF blinbefore IN sets^.selected THEN BEGIN
  1013. InsertBlankLine;
  1014. CrPending := FALSE
  1015. END;
  1016. IF dindonkey IN sets^.selected THEN LShiftOn(sets^.dindsym);
  1017. IF dindent IN sets^.selected THEN LShift;
  1018. IF spbef IN sets^.selected THEN InsertSpace(currsym);
  1019. PPSymbol;
  1020. IF spaft IN sets^.selected THEN InsertSpace(nextsym);
  1021. IF inbytab IN sets^.selected THEN RShift(currsym^.name);
  1022. IF gobsym IN sets^.selected THEN Gobble(sets^.terminators);
  1023. IF crafter IN sets^.selected THEN CrPending := TRUE
  1024. END;
  1025. IF CrPending THEN WriteCRs(1);
  1026. Verbose(IntToStr(inlines)+' lines read, '+IntToStr(outlines)+' lines written.');
  1027. PrettyPrint:=True;
  1028. end;
  1029. Constructor TPrettyPrinter.Create;
  1030. Begin
  1031. LineSize:=MaxLineSize;
  1032. CreateOptions (Option);
  1033. SetTerminators(Option);
  1034. DiagS:=Nil;
  1035. InS:=Nil;
  1036. OutS:=Nil;
  1037. CfgS:=Nil;
  1038. End;
  1039. { ---------------------------------------------------------------------
  1040. Unit initialization
  1041. ---------------------------------------------------------------------}
  1042. Begin
  1043. CreateHash;
  1044. dblch := [becomes, opencomment];
  1045. end.
  1046. {
  1047. $Log$
  1048. Revision 1.1 2000-07-13 10:16:22 michael
  1049. + Initial import
  1050. Revision 1.8 2000/06/01 10:59:22 peter
  1051. * removed warning/notes
  1052. Revision 1.7 2000/05/03 13:04:08 pierre
  1053. * avoid a problem with range check
  1054. Revision 1.6 2000/02/09 16:44:15 peter
  1055. * log truncated
  1056. Revision 1.5 2000/02/06 19:57:45 carl
  1057. + More TP syntax compatible
  1058. Revision 1.4 2000/01/07 16:46:04 daniel
  1059. * copyright 2000
  1060. }