ptopu.pp 41 KB

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