ptopu.pp 35 KB

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