ptopu.pp 35 KB

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