ptopu.pp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  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,notequal,lessorequal,greaterorequal,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(Out 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(Out indentsymbol: keysymbol;
  151. Out 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. Blank = ' ';
  182. VAR
  183. sets : tableptr;
  184. dblch : dblcharset;
  185. CONST
  186. Keyword : KeywordTable =
  187. ('END', 'BEGIN', 'IF', 'THEN',
  188. 'ELSE', 'PROCEDURE', 'VAR', 'OF',
  189. 'WHILE', 'DO', 'CASE', 'WITH',
  190. 'FOR', 'REPEAT', 'UNTIL', 'FUNCTION',
  191. 'LABEL', 'CONST', 'TYPE', 'RECORD',
  192. 'STRING', 'PROGRAM',
  193. 'ASM','TRY','FINALLY','EXCEPT','RAISE','CLASS','OBJECT',
  194. 'CONSTRUCTOR','DESTRUCTOR','INHERITED','PROPERTY',
  195. 'PRIVATE','PUBLIC','PROTECTED','PUBLISHED',
  196. 'INITIALIZATION','FINALIZATION',
  197. 'INLINE','LIBRARY','INTERFACE','IMPLEMENTATION',
  198. 'READ','WRITE','UNIT',
  199. {keywords not used for formatting }
  200. 'AND', 'ARRAY', 'DIV', 'DOWNTO',
  201. 'FILE', 'GOTO', 'IN', 'MOD',
  202. 'NOT', 'NIL', 'OR', 'SET','TO','VIRTUAL','USES'
  203. );
  204. EntryNames : OEntriesTable =
  205. ('end','begin','if','then','else','proc','var',
  206. 'of','while','do','case','with','for','repeat','until',
  207. 'func','label','const','type','record','string',
  208. 'prog',
  209. 'asm','try','finally','except','raise','class','object',
  210. 'constructor','destructor','inherited','property',
  211. 'private','public','protected','published',
  212. 'initialization','finalization',
  213. 'inline','library','interface','implementation',
  214. 'read','write','unit',
  215. 'and','arr','div','down','file','goto',
  216. 'in','mod','not','nil','or','set','to','virtual','uses',
  217. 'casevar','ofobject',
  218. 'becomes','notequal','lessorequal','greaterorequal','delphicomment','dopencomment','dclosecomment',
  219. 'opencomment','closecomment','semicolon',
  220. 'colon','equals',
  221. 'openparen','closeparen','period','endoffile','other');
  222. OptionNames : ONamesTable =
  223. ('crsupp','crbefore','blinbefore',
  224. 'dindonkey','dindent','spbef','spaft',
  225. 'gobsym','inbytab','inbyindent','crafter','upper',
  226. 'lower','capital');
  227. DblChar : DblCharTable =
  228. ( ':=', '<>', '<=', '>=', '//','(*','*)' );
  229. SglChar : SglCharTable =
  230. ('{', '}', ';', ':', '=', '(', ')', '.' );
  231. { ---------------------------------------------------------------------
  232. General functions, not part of the object.
  233. ---------------------------------------------------------------------}
  234. function upperStr(const s : string) : string;
  235. var
  236. i : longint;
  237. begin
  238. setLength(upperStr,length(s));
  239. for i:=1 to length(s) do
  240. if s[i] in ['a'..'z'] then
  241. upperStr[i]:=char(byte(s[i])-32)
  242. else
  243. upperStr[i]:=s[i];
  244. end;
  245. function LowerStr(const s : string) : string;
  246. var
  247. i : longint;
  248. begin
  249. setLength(LowerStr,length(s));
  250. for i:=1 to length(s) do
  251. if s[i] in ['A'..'Z'] then
  252. LowerStr[i]:=char(byte(s[i])+32)
  253. else
  254. LowerStr[i]:=s[i];
  255. end;
  256. Function IntToStr(I : LongInt) : String;
  257. var
  258. s : string;
  259. begin
  260. str(I,s);
  261. IntToStr := s;
  262. end;
  263. Function StrToInt(Const S : String) : Integer;
  264. Var Code : integer;
  265. Res : Integer;
  266. begin
  267. Val(S, Res, Code);
  268. StrToInt := Res;
  269. If Code<>0 then StrToInt:=0;
  270. end;
  271. Procedure Strip (Var S : String);
  272. Const WhiteSpace = [#32,#9,#10,#13];
  273. Var I,J : Longint;
  274. begin
  275. If length(s)=0 then exit;
  276. I:=1;
  277. While (S[I] in whitespace) and (I<Length(S)) do inc(i);
  278. J:=length(S);
  279. While (S[J] in whitespace) and (J>1) do dec(j);
  280. If I<=J then
  281. S:=Copy(S,i,j-i+1)
  282. else
  283. S:='';
  284. end;
  285. Procedure ClassID(Value: Token;
  286. lngth: INTEGER;
  287. VAR idtype: keysymbol;
  288. VAR IsKeyWord: BOOLEAN);
  289. { Classify an identifier. We are only interested
  290. in it if it is a keyword, so we use the hash table. }
  291. VAR
  292. Keyvalue: String[MAXKEYLENGTH];
  293. Sym : keysymbol;
  294. BEGIN
  295. IF lngth > MAXKEYLENGTH THEN BEGIN
  296. idtype := othersym;
  297. IsKeyWord := FALSE
  298. END
  299. ELSE
  300. BEGIN
  301. IsKeyWord := FALSE;
  302. KeyValue:= UpperStr(Value);
  303. sym:=endsym;
  304. While (Not IsKeyword) and (sym<=lastformatsym) DO
  305. begin
  306. iskeyword:=(KeyValue=Keyword[sym]);
  307. if not iskeyword then
  308. Sym:=Succ(sym);
  309. end;
  310. if IsKeyWord then
  311. idtype:=sym
  312. ELSE
  313. idtype := othersym;
  314. END
  315. END; { of ClassID }
  316. { ---------------------------------------------------------------------
  317. Functions to create options and set defaults.
  318. ---------------------------------------------------------------------}
  319. Procedure CreateOptions (Out Option : OptionTable);
  320. Var Sym : KeySymbol;
  321. T : TTokenScope;
  322. begin
  323. FOR sym := endsym TO othersym DO
  324. For T:=Low(TTokenScope) to High(TTokenScope) do
  325. begin
  326. NEW(option[T,sym]);
  327. option[T,sym]^.selected := [];
  328. option[T,sym]^.dindsym := [];
  329. option[T,sym]^.terminators := []
  330. END;
  331. end;
  332. Procedure SetTerminators(Var Option : OptionTable);
  333. Var
  334. T : TTokenScope;
  335. begin
  336. For T:=Low(TTokenScope) to High(TTokenScope) do
  337. begin
  338. option[t,casesym]^.terminators := [ofsym];
  339. option[t,casevarsym]^.terminators := [ofsym];
  340. option[t,forsym]^.terminators := [dosym];
  341. option[t,whilesym]^.terminators := [dosym];
  342. option[t,withsym]^.terminators := [dosym];
  343. option[t,ifsym]^.terminators := [thensym];
  344. option[t,untilsym]^.terminators := [endsym, untilsym, elsesym, semicolon];
  345. option[t,becomes]^.terminators := [endsym, untilsym, elsesym, semicolon];
  346. option[t,openparen]^.terminators := [closeparen];
  347. option[t,usessym]^.terminators := [semicolon];
  348. end;
  349. end;
  350. Procedure SetDefaultIndents (Var Option : OptionTable);
  351. Var
  352. T : TTokenScope;
  353. begin
  354. For T:=Low(TTokenScope) to High(TTokenScope) do
  355. begin
  356. option[t,recordsym]^.dindsym := [endsym];
  357. option[t,funcsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  358. option[t,procsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  359. option[t,constsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  360. option[t,typesym]^.dindsym := [labelsym, constsym, typesym, varsym];
  361. option[t,varsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  362. option[t,beginsym]^.dindsym := [labelsym, constsym, typesym, varsym];
  363. option[t,publicsym]^.dindsym := [endsym,protectedsym,privatesym,publicsym,publishedsym];
  364. option[t,privatesym]^.dindsym := [endsym,protectedsym,privatesym,publicsym,publishedsym];
  365. option[t,protectedsym]^.dindsym := [endsym,protectedsym,privatesym,publicsym,publishedsym];
  366. option[t,publishedsym]^.dindsym := [endsym,protectedsym,privatesym,publicsym,publishedsym];
  367. option[t,finallysym]^.dindsym := [trysym];
  368. option[t,exceptsym]^.dindsym := [trysym];
  369. option[t,elsesym]^.dindsym := [ifsym, thensym, elsesym];
  370. option[t,untilsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
  371. withsym, colon, equals];
  372. option[t,endsym]^.dindsym := [ifsym, thensym, elsesym, forsym, whilesym,
  373. withsym, casevarsym, colon, equals, recordsym,
  374. trysym,classsym,objectsym,protectedsym,privatesym,
  375. publicsym,publishedsym,finallysym,exceptsym];
  376. option[t,semicolon]^.dindsym := [ifsym, thensym, elsesym, forsym,
  377. whilesym, withsym, colon, equals];
  378. option[t,implementationsym]^.dindsym := [labelsym, varsym, typesym, constsym,
  379. endsym,propertysym];
  380. end;
  381. end;
  382. Procedure SetDefaults (Var Option : OptionTable);
  383. { Sets default values for the formatting rules. }
  384. Var
  385. T : TTokenScope;
  386. begin
  387. For T:=Low(TTokenScope) to High(TTokenScope) do
  388. begin
  389. option[t,progsym]^.selected := [capital,blinbefore, spaft];
  390. option[t,unitsym]^.selected := [capital,blinbefore, spaft];
  391. option[t,librarysym]^.selected := [capital,blinbefore, spaft];
  392. option[t,funcsym]^.selected := [capital,blinbefore, dindonkey, spaft];
  393. option[t,procsym]^.selected := [capital,blinbefore, dindonkey, spaft];
  394. option[t,labelsym]^.selected := [capital,blinbefore, spaft, inbytab];
  395. option[t,constsym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
  396. option[t,typesym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
  397. option[t,varsym]^.selected := [capital,blinbefore, dindonkey, spaft, inbytab];
  398. option[t,beginsym]^.selected := [capital,dindonkey, crbefore, crafter, inbytab];
  399. option[t,repeatsym]^.selected := [capital,inbytab, crafter];
  400. option[t,recordsym]^.selected := [capital,inbyIndent, crafter];
  401. option[t,objectsym]^.selected := [capital,inbyIndent];
  402. option[t,classsym]^.selected := [capital,inbyIndent];
  403. option[t,publicsym]^.selected := [capital,crbefore, dindonkey, spaft,inbytab];
  404. option[t,publishedsym]^.selected := [capital,crbefore, dindonkey, spaft,inbytab];
  405. option[t,protectedsym]^.selected := [capital,crbefore, dindonkey, spaft,inbytab];
  406. option[t,privatesym]^.selected := [capital,crbefore, dindonkey, spaft,inbytab];
  407. option[t,trysym]^.Selected := [capital,crbefore,crafter,inbytab];
  408. option[t,finallysym]^.selected := [capital,crbefore,dindent,crafter,inbytab];
  409. option[t,exceptsym]^.selected := [capital,crbefore,dindent,crafter,inbytab];
  410. option[t,casesym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  411. option[t,casevarsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  412. option[t,ofsym]^.selected := [capital,crsupp, spbef, spaft];
  413. option[t,forsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  414. option[t,whilesym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  415. option[t,withsym]^.selected := [capital,spaft, inbytab, gobsym, crafter];
  416. option[t,dosym]^.selected := [capital,crsupp, spbef];
  417. option[t,ifsym]^.selected := [capital,spaft, inbytab, gobsym];
  418. option[t,implementationsym]^.selected := [capital,blinbefore,crafter,dindonkey];
  419. option[t,interfacesym]^.selected := [capital,blinbefore,crafter];
  420. option[t,usessym]^.selected := [capital,blinbefore,spaft];
  421. option[t,thensym]^.selected := [capital];
  422. option[t,elsesym]^.selected := [capital,crbefore, dindonkey, inbytab];
  423. option[t,endsym]^.selected := [capital,crbefore, crafter,dindonkey,dindent];
  424. option[t,untilsym]^.selected := [capital,crbefore, dindonkey, dindent, spaft,
  425. gobsym, crafter];
  426. option[t,becomes]^.selected := [capital,spbef, spaft, gobsym];
  427. option[t,Delphicomment]^.Selected := [crafter];
  428. option[t,opencomment]^.selected := [capital,crsupp];
  429. option[t,closecomment]^.selected := [capital,crsupp];
  430. option[t,semicolon]^.selected := [capital,crsupp, dindonkey, crafter];
  431. option[t,colon]^.selected := [capital,inbytab];
  432. option[t,equals]^.selected := [capital,spbef, spaft, inbytab];
  433. option[t,openparen]^.selected := [capital,gobsym];
  434. option[t,period]^.selected := [capital,crsupp];
  435. end;
  436. option[tsInterface,funcsym]^.selected := [capital, dindonkey, spaft];
  437. option[tsInterface,procsym]^.selected := [capital, dindonkey, spaft];
  438. end;
  439. { ---------------------------------------------------------------------
  440. Stream handling routines
  441. ---------------------------------------------------------------------}
  442. Function ReadChar (S : TStream) : Char;
  443. Var C : Char;
  444. begin
  445. repeat
  446. if S.Position=S.Size then
  447. C:=#0
  448. else
  449. S.Read(C,1);
  450. Until (C<>#13);
  451. ReadChar:=C;
  452. end;
  453. Function EoSLn (S : TStream) : Char;
  454. Const WhiteSpace = [' ', #9, #13 ];
  455. Var C : Char;
  456. begin
  457. Repeat
  458. if S.Position = S.Size then
  459. C:=#0
  460. else
  461. S.Read(C,1);
  462. Until (Not (C in WhiteSpace)) or ((C=#10));
  463. EoSln:=C;
  464. end;
  465. Function ReadString (S: TStream): String;
  466. Var
  467. I : Byte;
  468. Count : Integer;
  469. begin
  470. Result:='';
  471. I:=0;
  472. Repeat
  473. If ((I+1)>Length(Result)) then
  474. SetLength(Result,Length(Result)+255);
  475. Count:=S.Read(Result[I+1],1);
  476. If Count>0 then
  477. Inc(I);
  478. until (Result[I]=#10) or (Count=0);
  479. If Result[i]=#10 Then Dec(I);
  480. If Result[I]=#13 then Dec(I);
  481. SetLength(Result,I);
  482. end;
  483. Procedure WriteString (S : TStream; ST : String);
  484. begin
  485. S.Write(St[1],length(St));
  486. end;
  487. Procedure WriteAnsiString (S : TStream; ST : AnsiString);
  488. begin
  489. S.Write(St[1],length(St));
  490. end;
  491. Procedure WriteCR (S: TStream);
  492. Const
  493. Newline = System.LineEnding;
  494. begin
  495. WriteString(S,Newline);
  496. end;
  497. Procedure WriteLnString (S : TStream; ST : String);
  498. begin
  499. WriteString(S,ST);
  500. WriteCR(S);
  501. end;
  502. { ---------------------------------------------------------------------
  503. TPrettyPrinter object
  504. ---------------------------------------------------------------------}
  505. Procedure TPrettyPrinter.Verbose (Const Msg : String);
  506. begin
  507. If Assigned (FOnVerbose) then
  508. FOnVerbose(Self,Msg);
  509. end;
  510. Procedure TPrettyPrinter.GetChar;
  511. { Read the next character and classify it }
  512. VAR Ch: CHAR;
  513. BEGIN
  514. currchar := nextchar;
  515. WITH nextchar DO
  516. begin
  517. Ch:=ReadCHar(Ins);
  518. If Ch=#0 then
  519. BEGIN
  520. name := filemark;
  521. Value := Blank
  522. END
  523. ELSE If (Ch=#10) THEN
  524. BEGIN
  525. name := endofline;
  526. Value := Ch;
  527. Inc(inlines);
  528. END
  529. ELSE
  530. BEGIN
  531. Value := Ch;
  532. IF Ch IN ['a'..'z', 'A'..'Z', '_'] THEN name := letter
  533. ELSE IF Ch IN ['0'..'9'] THEN name := digit
  534. ELSE IF Ch = '''' THEN name := quote
  535. ELSE IF Ch in [#13,' ',#9] THEN name := space
  536. ELSE name := otherchar
  537. END
  538. end;
  539. END; { of GetChar }
  540. Procedure TPrettyPrinter.StoreNextChar(VAR lngth: INTEGER;
  541. VAR Value: Token);
  542. { Store a character in the current symbol }
  543. BEGIN
  544. GetChar;
  545. IF lngth < MAXSYMBOLSIZE THEN BEGIN {XXX - should there be a limit at all?}
  546. Inc(lngth);
  547. setlength(Value,lngth);
  548. Value[lngth] := currchar.Value;
  549. END;
  550. END; { of StoreNextChar }
  551. Procedure TPrettyPrinter.SkipBlanks(out spacesbefore, crsbefore: INTEGER);
  552. { Count the spaces between symbols }
  553. BEGIN
  554. spacesbefore := 0;
  555. crsbefore := 0;
  556. WHILE nextchar.name IN [space, endofline] DO BEGIN
  557. GetChar;
  558. CASE currchar.name OF
  559. space: Inc(spacesbefore);
  560. endofline: BEGIN
  561. Inc(crsbefore);
  562. spacesbefore := 0;
  563. END;
  564. END; {case}
  565. END;
  566. END; { of SkipBlanks }
  567. Procedure TPrettyPrinter.GetComment(sym: symbolinfo);
  568. { Process comments using brace notation }
  569. BEGIN
  570. sym^.name := opencomment;
  571. WHILE NOT ((currchar.Value = '}')
  572. OR (nextchar.name = filemark)) DO
  573. StoreNextChar(sym^.length, sym^.Value);
  574. IF currchar.Value = '}' THEN sym^.name := closecomment;
  575. END; { of GetCommment }
  576. Procedure TPrettyPrinter.GetDoubleComment(sym: symbolinfo);
  577. { Process comments using parenthesis notation }
  578. BEGIN
  579. sym^.name := dopencomment;
  580. WHILE NOT (((currchar.Value = '*') AND (nextchar.Value = ')'))
  581. OR (nextchar.name = filemark)) DO
  582. StoreNextChar(sym^.length, sym^.Value);
  583. IF (currchar.Value = '*') AND (nextchar.Value = ')') THEN BEGIN
  584. StoreNextChar(sym^.length, sym^.Value);
  585. sym^.name := dclosecomment;
  586. END;
  587. END; { of GetDoubleCommment }
  588. Procedure TPrettyPrinter.GetDelphiComment(sym: symbolinfo);
  589. { Process comments using either brace or parenthesis notation }
  590. BEGIN
  591. sym^.name := Delphicomment;
  592. WHILE NOT ((nextchar.name = endofline) OR (nextchar.name = filemark)) DO
  593. StoreNextChar(sym^.length, sym^.Value);
  594. END; { of GetDelphiCommment }
  595. Procedure TPrettyPrinter.GetIdentifier(sym: symbolinfo);
  596. { Read an identifier and classify it }
  597. BEGIN
  598. WHILE nextchar.name IN [letter, digit] DO
  599. StoreNextChar(sym^.length, sym^.Value);
  600. ClassID(sym^.Value, sym^.length, sym^.name, sym^.IsKeyWord);
  601. IF sym^.name IN [recordsym, objectsym,classsym, casesym, endsym] THEN
  602. begin
  603. if sym^.name=implementationsym then
  604. FTokenScope:=tsImplementation;
  605. if sym^.name in [recordsym,objectsym,classsym] then
  606. LastStruct:=sym^.name;
  607. CASE sym^.name OF
  608. RecordSym : Inc(RecordLevel);
  609. ClassSym : ClassSeen:=True;
  610. objectsym : begin
  611. if (PreviousSymbol=Ofsym) then
  612. sym^.name:=ofobjectsym
  613. else
  614. ObjectSeen:=True;
  615. end;
  616. casesym : IF (RecordLevel>0) and (LastStruct=recordsym) THEN sym^.name := casevarsym;
  617. endsym : If (LastStruct=recordsym) then
  618. Dec(Recordlevel);
  619. else
  620. begin
  621. ClassSeen:=False;
  622. ObjectSeen:=False;
  623. end
  624. END; {case}
  625. end;
  626. If (PreviousSymbol=ClassSym) and (sym^.Name=ofsym) then
  627. ClassSeen:=False;
  628. PreviousSymbol:=sym^.Name;
  629. END; { of GetIdentifier }
  630. { Read a number and store it as a string }
  631. Procedure TPrettyPrinter.GetNumber(sym: symbolinfo);
  632. BEGIN
  633. WHILE nextchar.name = digit DO StoreNextChar(sym^.length, sym^.Value);
  634. sym^.name := othersym;
  635. END; { of GetNumber }
  636. PROCEDURE TPrettyPrinter.GetCharLiteral(sym: symbolinfo);
  637. { Read a quoted string }
  638. BEGIN
  639. WHILE nextchar.name = quote DO BEGIN
  640. StoreNextChar(sym^.length, sym^.Value);
  641. WHILE NOT (nextchar.name IN [quote, endofline, filemark]) DO
  642. StoreNextChar(sym^.length, sym^.Value);
  643. IF nextchar.name = quote THEN StoreNextChar(sym^.length, sym^.Value);
  644. END;
  645. sym^.name := othersym;
  646. END; { of GetCharLiteral }
  647. FUNCTION TPrettyPrinter.char_Type: keysymbol;
  648. { Classify a character pair }
  649. VAR
  650. NextTwoChars: SpecialChar;
  651. Hit: BOOLEAN;
  652. thischar: keysymbol;
  653. BEGIN
  654. NextTwoChars[1] := currchar.Value;
  655. NextTwoChars[2] := nextchar.Value;
  656. thischar := becomes;
  657. Hit := FALSE;
  658. WHILE NOT (Hit OR (thischar = opencomment)) DO BEGIN
  659. IF NextTwoChars = DblChar[thischar] THEN Hit := TRUE
  660. ELSE Inc(thischar);
  661. END;
  662. IF NOT Hit THEN BEGIN
  663. thischar := opencomment;
  664. WHILE NOT (Hit OR (PRED(thischar) = period)) DO BEGIN
  665. IF currchar.Value = SglChar[thischar] THEN Hit := TRUE
  666. ELSE Inc(thischar);
  667. END;
  668. END;
  669. IF Hit THEN char_Type := thischar
  670. ELSE char_Type := othersym;
  671. END; { of char_Type }
  672. Procedure TPrettyPrinter.GetSpecialChar(sym: symbolinfo);
  673. { Read special characters }
  674. BEGIN
  675. StoreNextChar(sym^.length, sym^.Value);
  676. sym^.name := char_Type;
  677. IF sym^.name IN dblch THEN StoreNextChar(sym^.length, sym^.Value)
  678. END; { of GetSpecialChar }
  679. Procedure TPrettyPrinter.GetNextSymbol(sym: symbolinfo);
  680. { Read a symbol using the appropriate procedure }
  681. BEGIN
  682. CASE nextchar.name OF
  683. letter: GetIdentifier(sym);
  684. digit: GetNumber(sym);
  685. quote: GetCharLiteral(sym);
  686. otherchar: BEGIN
  687. GetSpecialChar(sym);
  688. IF sym^.name = opencomment THEN GetComment(sym)
  689. else IF sym^.name = dopencomment THEN GetDoubleComment(sym)
  690. else IF sym^.name= DelphiComment then GetDelphiComment(Sym)
  691. END;
  692. filemark: sym^.name := endoffile;
  693. ELSE {:} {Turbo}
  694. WRITELN('Unknown character type: ', ORD(nextchar.name));
  695. END; {case}
  696. END; { of GetNextSymbol }
  697. Procedure TprettyPrinter.GetSymbol;
  698. { Store the next symbol in NEXTSYM }
  699. VAR
  700. dummy: symbolinfo;
  701. BEGIN
  702. dummy := currsym;
  703. currsym := nextsym;
  704. nextsym := dummy;
  705. SkipBlanks(nextsym^.spacesbefore, nextsym^.crsbefore);
  706. nextsym^.length := 0;
  707. nextsym^.IsKeyWord := FALSE;
  708. IF currsym^.name = opencomment THEN GetComment(nextsym)
  709. ELSE IF currsym^.name = dopencomment THEN GetDoubleComment(nextsym)
  710. ELSE GetNextSymbol(nextsym);
  711. END; {of GetSymbol}
  712. Procedure TprettyPrinter.PopStack(Out indentsymbol: keysymbol;
  713. Out prevmargin: INTEGER);
  714. { Manage stack of indentation symbols and margins }
  715. BEGIN
  716. IF top > 0 THEN BEGIN
  717. indentsymbol := stack[top].indentsymbol;
  718. prevmargin := stack[top].prevmargin;
  719. Dec(top);
  720. END
  721. ELSE BEGIN
  722. indentsymbol := othersym;
  723. prevmargin := 0;
  724. END;
  725. END; { of PopStack }
  726. Procedure TPrettyPrinter.PushStack(indentsymbol: keysymbol;
  727. prevmargin: INTEGER );
  728. BEGIN
  729. Inc(top);
  730. stack[top].indentsymbol := indentsymbol;
  731. stack[top].prevmargin := prevmargin;
  732. END; { of PushStack }
  733. Procedure TPrettyPrinter.WriteCRs(numberofcrs: INTEGER);
  734. VAR
  735. i: INTEGER;
  736. BEGIN
  737. IF numberofcrs > 0 THEN BEGIN
  738. FOR i := 1 TO numberofcrs DO
  739. WriteCr(OutS);
  740. Inc(outlines,numberofcrs);
  741. Currlinepos := 0;
  742. FirstWordStackPos:=-1;
  743. END;
  744. END; { of WriteCRs }
  745. Procedure TPrettyPrinter.InsertCR;
  746. BEGIN
  747. IF currsym^.crsbefore = 0 THEN BEGIN
  748. WriteCRs(1);
  749. currsym^.spacesbefore := 0;
  750. END;
  751. END; { of InsertCR }
  752. Procedure TPrettyPrinter.InsertBlankLine;
  753. BEGIN
  754. IF currsym^.crsbefore = 0 THEN
  755. BEGIN
  756. IF currlinepos = 0 THEN
  757. WriteCRs(1)
  758. ELSE
  759. WriteCRs(2);
  760. currsym^.spacesbefore := 0;
  761. END
  762. ELSE
  763. IF currsym^.crsbefore = 1 THEN
  764. IF currlinepos > 0 THEN
  765. begin
  766. WriteCRs(1);
  767. currsym^.spacesbefore := 0;
  768. end;
  769. END; { of InsertBlankLine }
  770. Procedure TPrettyPrinter.LShiftOn(dindsym: keysymset);
  771. { Move margin left according to stack configuration and current symbol }
  772. VAR
  773. indentsymbol: keysymbol;
  774. prevmargin: INTEGER;
  775. BEGIN
  776. {$ifdef debug}
  777. Write('LShiftOn ',EntryNames[currsym^.name],' : ',FirstWordPos,'/',CurrMargin);
  778. {$endif debug}
  779. IF top > 0 THEN BEGIN
  780. REPEAT
  781. PopStack(indentsymbol, prevmargin);
  782. IF indentsymbol IN dindsym THEN currmargin := prevmargin;
  783. UNTIL NOT (indentsymbol IN dindsym) OR (top = 0);
  784. IF NOT (indentsymbol IN dindsym) THEN
  785. PushStack(indentsymbol, prevmargin);
  786. END;
  787. {$ifdef debug}
  788. Writeln('-> ',CurrMargin);
  789. {$endif debug}
  790. END; { of LShiftOn }
  791. Procedure TprettyPrinter.LShift;
  792. { Move margin left according to stack top }
  793. VAR
  794. indentsymbol: keysymbol;
  795. prevmargin: INTEGER;
  796. BEGIN
  797. {$ifdef debug}
  798. Write('LShift ',EntryNames[currsym^.name],' : ',FirstWordPos,'/',CurrMargin);
  799. {$endif debug}
  800. IF top > 0 THEN BEGIN
  801. PopStack(indentsymbol, prevmargin);
  802. currmargin := prevmargin;
  803. (* maybe PopStack(indentsymbol,currmargin); *)
  804. END;
  805. {$ifdef debug}
  806. Writeln('-> ',CurrMargin);
  807. {$endif debug}
  808. END; { of LShift }
  809. Procedure TprettyPrinter.RShift(currmsym: keysymbol);
  810. { Move right, stacking margin positions }
  811. BEGIN
  812. {$ifdef debug}
  813. Write('RShift ',EntryNames[currmsym],' : ',FirstWordPos,'/',Currmargin);
  814. {$endif debug}
  815. IF top < MAXSTACKSIZE THEN PushStack(currmsym, currmargin);
  816. IF startpos > currmargin THEN currmargin := startpos;
  817. Inc(currmargin,INDENT);
  818. {$ifdef debug}
  819. Writeln(' -> ',Currmargin)
  820. {$endif debug}
  821. END; { of RShift }
  822. Procedure TprettyPrinter.RShiftIndent(currmsym: keysymbol);
  823. { Move right, stacking margin positions }
  824. BEGIN
  825. {$ifdef debug}
  826. Write('RShiftIndent ',EntryNames[currmsym],' : ',FirstWordPos,'/',Currmargin);
  827. {$endif debug}
  828. If (FirstWordStackPos>=0) then
  829. Top:=FirstWordStackPos
  830. else
  831. Top:=0;
  832. {$ifdef debug}
  833. If (Top>0) then
  834. Write(' Stackpos ',Top,' Item: ',EntryNames[Stack[Top].IndentSymbol],' Pos: ',Stack[Top].Prevmargin)
  835. else
  836. Write(' no item on stack');
  837. {$endif debug}
  838. IF top < MAXSTACKSIZE THEN PushStack(othersym, FirstWordPos);
  839. // IF top < MAXSTACKSIZE THEN PushStack(currmsym, currmargin);
  840. CurrMargin:=FirstWordPos+Indent;
  841. {$ifdef debug}
  842. Writeln(' -> ',Currmargin)
  843. {$endif debug}
  844. END; { of RShift }
  845. Procedure TPrettyPrinter.InsertSpace(VAR symbol: symbolinfo);
  846. { Insert space if room on line }
  847. BEGIN
  848. IF currlinepos < LineSize THEN BEGIN
  849. WriteString(OutS, Blank);
  850. Inc(currlinepos);
  851. IF (symbol^.crsbefore = 0) AND (symbol^.spacesbefore > 0)
  852. THEN Dec(symbol^.spacesbefore);
  853. END;
  854. END; { of InsertSpace }
  855. Procedure TPrettyPrinter.MoveLinePos(newlinepos: INTEGER);
  856. { Insert spaces until correct line position reached }
  857. VAR i: INTEGER;
  858. BEGIN
  859. FOR i := SUCC(currlinepos) TO newlinepos DO
  860. WriteString(OutS, Blank);
  861. currlinepos := newlinepos;
  862. END; { of MoveLinePos }
  863. Procedure TPrettyPrinter.PrintSymbol;
  864. BEGIN
  865. IF (currsym^.IsKeyWord) then
  866. begin
  867. If upper in sets^.selected Then
  868. WriteString (OutS,UpperStr(currsym^.value))
  869. else if lower in sets^.selected then
  870. WriteString (OutS,LowerStr(currsym^.value))
  871. else if capital in sets^.selected then
  872. begin
  873. WriteString(OutS,UpCase(CurrSym^.Value[1]));
  874. WriteString(OutS,LowerStr(Copy(CurrSym^.Value,2,MAXSYMBOLSIZE)));{XXX - ?should it be length?}
  875. end
  876. else
  877. WriteString(OutS,Currsym^.Value);
  878. end
  879. ELSE
  880. WriteAnsiString(OutS, currsym^.Value);
  881. startpos := currlinepos;
  882. Inc(currlinepos,currsym^.length);
  883. if (FirstWordStackPos=-1) then
  884. begin
  885. FirstWordPos:=startpos;
  886. FirstWordStackPos:=Top;
  887. {$ifdef debug}
  888. write('First word : ',currlinepos,': ',currsym^.value);
  889. If (FirstWordStackPos>0) then
  890. writeln(' [Stack: ',FirstWordStackPos,' Item: "',EntryNames[Stack[FirstWordStackPos].IndentSymbol],'" Pos: ',Stack[FirstWordStackPos].Prevmargin,']')
  891. else
  892. Writeln(' No stack')
  893. {$endif debug}
  894. end;
  895. END; { of PrintSymbol }
  896. Procedure TPrettyPrinter.PPSymbol;
  897. { Find position for symbol and then print it }
  898. VAR newlinepos: INTEGER;
  899. BEGIN
  900. WriteCRs(currsym^.crsbefore);
  901. IF ((currLinePos<>0) and (currlinepos + currsym^.spacesbefore > currmargin)) OR
  902. (currsym^.name IN [opencomment, closecomment,dopencomment, dclosecomment])
  903. THEN
  904. newlinepos := currlinepos + currsym^.spacesbefore
  905. ELSE
  906. newlinepos := currmargin;
  907. IF newlinepos + currsym^.length > LINESIZE THEN
  908. BEGIN {XXX - this needs to be cleaned for case of long symbol values}
  909. WriteCRs(1);
  910. IF currmargin + currsym^.length <= LINESIZE THEN
  911. newlinepos := currmargin
  912. ELSE IF currsym^.length < LINESIZE THEN
  913. newlinepos := LINESIZE - currsym^.length
  914. ELSE
  915. newlinepos := 0;
  916. END;
  917. MoveLinePos(newlinepos);
  918. PrintSymbol;
  919. END; { of PPSymbol }
  920. Procedure TPrettyPrinter.Gobble(terminators: keysymset);
  921. { Print symbols which follow a formatting symbol but which do not
  922. affect layout }
  923. BEGIN
  924. {$ifdef debug}
  925. Inc(GobbleLevel);
  926. Writeln('Gobble start ',GobbleLevel,' : ',EntryNames[currsym^.name]);
  927. {$endif debug}
  928. IF top < MAXSTACKSIZE THEN PushStack(currsym^.name, currmargin);
  929. currmargin := currlinepos;
  930. WHILE NOT ((nextsym^.name IN terminators)
  931. OR (nextsym^.name = endoffile)) DO BEGIN
  932. GetSymbol;
  933. PPSymbol;
  934. END;
  935. LShift;
  936. {$ifdef debug}
  937. Writeln('Gobble end ',gobblelevel,' : ',EntryNames[nextsym^.name],' ',nextsym^.name in terminators );
  938. Dec(GobbleLevel);
  939. {$endif debug}
  940. END; { of Gobble }
  941. Function TPrettyPrinter.ReadConfigFile : Boolean;
  942. Type
  943. TLineType = (ltNormal,ltIndent,ltGobble);
  944. Var
  945. I,J : Longint;
  946. Procedure SetOption(TheKey : KeySymbol;Var OptionList : String);
  947. Var TheOpt : Options;
  948. Found : Boolean;
  949. K : longint;
  950. opt : string;
  951. TS : TTokenScope;
  952. begin
  953. Repeat
  954. K:=pos(',',optionlist);
  955. If k>0 then
  956. begin
  957. opt:=Copy(OptionList,1,k-1);
  958. strip(opt);
  959. Delete(OptionList,1,k);
  960. end
  961. else
  962. opt:=OptionList;
  963. If Length(Opt)>0 then
  964. begin
  965. Found:=False;
  966. for TheOpt :=firstopt to lastopt do
  967. begin
  968. found:=opt=OptionNames[Theopt];
  969. If found then break;
  970. end;
  971. If not found then
  972. Verbose ('Unknown option on line '+inttostr(i)+': '+Opt)
  973. else
  974. For TS:=Low(TTokenScope) to High(TTokenScope) do
  975. Option[TS,TheKey]^.Selected:=Option[TS,TheKey]^.Selected+[TheOpt];
  976. end;
  977. until k=0;
  978. end;
  979. Function GetKeySimList(Const aType : String; Var OptionList : String) : keysymset;
  980. Var
  981. TheIndent : Keysymbol;
  982. Found : Boolean;
  983. K : longint;
  984. opt : string;
  985. begin
  986. Result:=[];
  987. Repeat
  988. K:=pos(',',optionlist);
  989. If k>0 then
  990. begin
  991. opt:=Copy(OptionList,1,k-1);
  992. strip(opt);
  993. Delete(OptionList,1,k);
  994. end
  995. else
  996. opt:=OptionList;
  997. If Length(Opt)>0 then
  998. begin
  999. Found:=False;
  1000. for TheIndent :=firstKey to lastKey do
  1001. begin
  1002. found:=opt=EntryNames[Theindent];
  1003. If found then break;
  1004. end;
  1005. If not found then
  1006. begin
  1007. Verbose ('Unknown indent '+aType+' on line '+inttostr(i)+': '+Opt);
  1008. exit;
  1009. end;
  1010. Include(Result,Theindent);
  1011. end;
  1012. until k=0;
  1013. end;
  1014. Procedure SetIndent(TheKey : KeySymbol; Var OptionList : String);
  1015. Var
  1016. TS : TTokenScope;
  1017. Syms : KeySymSet;
  1018. begin
  1019. Syms:=GetKeySimList('indent',OptionList);
  1020. For TS:=Low(TTokenScope) to High(TTokenScope) do
  1021. With Option[TS,TheKey]^ do
  1022. dindsym:=dindsym+Syms;
  1023. end;
  1024. Procedure SetGobble(TheKey : KeySymbol; Var OptionList : String);
  1025. Var
  1026. TS : TTokenScope;
  1027. Syms : KeySymSet;
  1028. begin
  1029. Syms:=GetKeySimList('gobble',OptionList);
  1030. For TS:=Low(TTokenScope) to High(TTokenScope) do
  1031. With Option[TS,TheKey]^ do
  1032. Terminators:=Terminators+Syms;
  1033. end;
  1034. Function CheckLineType (var Name : String) : TLineType;
  1035. begin
  1036. If (Name[1]='[') and (Name[Length(Name)]=']') then
  1037. begin
  1038. Name:=Copy(Name,2,Length(Name)-2);
  1039. Result:=ltIndent
  1040. end
  1041. else If (Name[1]='<') and (Name[Length(Name)]='>') then
  1042. begin
  1043. Name:=Copy(Name,2,Length(Name)-2);
  1044. Result:=ltgobble
  1045. end
  1046. else
  1047. Result:=ltNormal;
  1048. end;
  1049. Var
  1050. TheKey : KeySymbol;
  1051. Found : Boolean;
  1052. Line, Name : String;
  1053. L : TStringList;
  1054. LT : TLineType;
  1055. begin
  1056. ReadConfigFile:=false;
  1057. L:=TStringList.Create;
  1058. Try
  1059. L.LoadFromStream(CfgS);
  1060. For I:=1 to L.Count do
  1061. begin
  1062. Line:=L[i-1];
  1063. { Strip comment }
  1064. If pos('#',Line)<>0 then
  1065. Line:=Copy(Line,1,Pos('#',Line)-1);
  1066. If length(Line)<>0 then
  1067. begin
  1068. J:=Pos('=',Line);
  1069. If J=0 then
  1070. verbose ('Error in config file on line '+IntToStr(i))
  1071. else
  1072. begin
  1073. Line:=LowerStr(Line);
  1074. Name:=Copy(Line,1,j-1);
  1075. Delete(Line,1,J);
  1076. { indents or options ? }
  1077. LT:=CheckLineType(Name);
  1078. Strip(Name);
  1079. found:=false;
  1080. for thekey:=firstkey to lastkey do
  1081. begin
  1082. found:=Name=EntryNames[thekey];
  1083. If Found then break;
  1084. end;
  1085. If not found then
  1086. Verbose ('Unknown keyword on line '+inttostr(i)+': '+Name)
  1087. else
  1088. Case LT of
  1089. ltIndent: SetIndent(TheKey,Line);
  1090. ltNormal: SetOption(TheKey,Line);
  1091. ltGobble: SetGobble(TheKey,Line);
  1092. end;
  1093. end;
  1094. end;
  1095. end;
  1096. Finally
  1097. L.Free;
  1098. end;
  1099. Verbose ('Processed configfile: read '+IntToStr(I)+' lines');
  1100. ReadConfigFile:=true;
  1101. end;
  1102. Procedure GenerateCfgFile(S : TStream);
  1103. Var TheKey,TheIndent : KeySymbol;
  1104. TheOpt : Options;
  1105. Written : Boolean;
  1106. Option : OptionTable;
  1107. begin
  1108. CreateOptions(option);
  1109. SetDefaults(option);
  1110. SetDefaultIndents(option);
  1111. For TheKey:=Firstkey to lastkey do
  1112. begin
  1113. { Write options }
  1114. WriteString (S,EntryNames[TheKey]+'=');
  1115. Written:=False;
  1116. for TheOpt:=FirstOpt to LastOpt do
  1117. If TheOpt in Option[tsInterface,TheKey]^.Selected then
  1118. begin
  1119. if written then
  1120. WriteString (S,',')
  1121. else
  1122. Written:=True;
  1123. writeString (S,OptionNames[TheOpt]);
  1124. end;
  1125. WriteCr (S);
  1126. { Write de-indent keysyms, if any }
  1127. If Option[tsInterface,TheKey]^.dindsym<>[] then
  1128. begin
  1129. WriteString (S,'['+EntryNames[TheKey]+']=');
  1130. Written:=False;
  1131. For TheIndent:=FirstKey to lastkey do
  1132. If TheIndent in Option[tsInterface,TheKey]^.dindsym then
  1133. begin
  1134. if written then
  1135. WriteString (S,',')
  1136. else
  1137. Written:=True;
  1138. WriteString (S,EntryNames[Theindent]);
  1139. end;
  1140. WriteCr (S);
  1141. end;
  1142. end;
  1143. end;
  1144. Function trimMiddle ( a:ansistring; lnght: integer; size: integer):string;
  1145. var
  1146. half:Integer;
  1147. begin
  1148. if lnght > size
  1149. then
  1150. begin
  1151. half := (size - 3) div 2;
  1152. trimMiddle := copy(a,1,half) + '...' + copy(a,lnght-half+1,half);
  1153. end
  1154. else
  1155. trimMiddle := a;
  1156. end;
  1157. Function TPrettyPrinter.PrettyPrint : Boolean;
  1158. Begin
  1159. PrettyPrint:=False;
  1160. If Not Assigned(Ins) or Not Assigned(OutS) then
  1161. exit;
  1162. If Not Assigned(CfgS) then
  1163. begin
  1164. SetDefaults(Option);
  1165. SetDefaultIndents(Option);
  1166. end
  1167. else
  1168. ReadConfigFile;
  1169. { Initialize variables }
  1170. top := 0;
  1171. currlinepos := 0;
  1172. currmargin := 0;
  1173. inlines := 0;
  1174. outlines := 0;
  1175. CrPending := FALSE;
  1176. FirstWordStackPos:=-1;
  1177. RecordLevel := 0;
  1178. GetChar;
  1179. NEW(currsym);
  1180. NEW(nextsym);
  1181. GetSymbol;
  1182. WHILE nextsym^.name <> endoffile DO BEGIN
  1183. GetSymbol;
  1184. {$ifdef debug}
  1185. Writeln('line in-'+IntToStr(inlines)+' out-'+IntToStr(outlines)+
  1186. ' symbol "'+EntryNames[currsym^.name]+'" = "'+
  1187. trimMiddle(currsym^.value,length(currsym^.value),MAXSHOWSIZE)+'"');
  1188. {$endif debug}
  1189. sets := option[FTokenScope,currsym^.name];
  1190. IF (CrPending AND NOT (crsupp IN sets^.selected))
  1191. OR (crbefore IN sets^.selected) THEN BEGIN
  1192. InsertCR;
  1193. CrPending := FALSE
  1194. END;
  1195. IF blinbefore IN sets^.selected THEN BEGIN
  1196. InsertBlankLine;
  1197. CrPending := FALSE
  1198. END;
  1199. IF dindonkey IN sets^.selected THEN
  1200. LShiftOn(sets^.dindsym);
  1201. IF dindent IN sets^.selected THEN
  1202. LShift;
  1203. IF spbef IN sets^.selected THEN InsertSpace(currsym);
  1204. PPSymbol;
  1205. IF spaft IN sets^.selected THEN InsertSpace(nextsym);
  1206. IF inbytab IN sets^.selected THEN
  1207. RShift(currsym^.name)
  1208. else IF inbyindent IN sets^.selected THEN
  1209. RShiftIndent(currsym^.name);
  1210. IF gobsym IN sets^.selected THEN Gobble(sets^.terminators);
  1211. IF crafter IN sets^.selected THEN CrPending := TRUE
  1212. END;
  1213. IF CrPending THEN WriteCRs(1);
  1214. Verbose(IntToStr(inlines)+' lines read, '+IntToStr(outlines)+' lines written.');
  1215. PrettyPrint:=True;
  1216. end;
  1217. Constructor TPrettyPrinter.Create;
  1218. Begin
  1219. Indent:=DefIndent;
  1220. LineSize:=DefLineSize;
  1221. CreateOptions (Option);
  1222. SetTerminators(Option);
  1223. InS:=Nil;
  1224. OutS:=Nil;
  1225. CfgS:=Nil;
  1226. End;
  1227. { ---------------------------------------------------------------------
  1228. Unit initialization
  1229. ---------------------------------------------------------------------}
  1230. Begin
  1231. dblch := [becomes, notequal, lessorequal, greaterorequal, opencomment];
  1232. end.