home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / LANGUAGS / PASCAL / PPAS80.LBR / PP.PQS / PP.PAS
Pascal/Delphi Source File  |  2000-06-30  |  17KB  |  630 lines

  1. { Pascal pretty printer }
  2.  
  3. { Author:  Peter Grogono }
  4.  
  5. { This program is based on a Pascal pretty-printer written by Ledgard,
  6.   Hueras, and Singer.  See SIGPLAN Notices, Vol. 12, No. 7, July 1977,
  7.   pages 101-105.  }
  8.  
  9. { This version of PP developed under Pascal/Z V4.0 or later.  }
  10.  
  11. { Very minor modifications for Turbo Pascal made by Willett Kempton
  12.   March 1984 and Oct 84.  Runs under 8-bit Turbo or 16-bit Turbo  }
  13.  
  14. { Following 4 options are for Pascal/Z }
  15. { $M- inhibit integer multiply/divide check }
  16. { $R- inhibit range/bound check - see procedure HASH }
  17. { $S- inhibit stack overflow check }
  18. { $U- inhibit range/bound check on parameters }
  19.  
  20. program pp;
  21.  
  22. const
  23. version = '11 October 1984';
  24.  
  25. {$I PPCONST.PAS }
  26. {$I PPTYPES.PAS }
  27.  
  28. {$I ArgLib.pas  } { portable command line routines }
  29.                   { Grogono version was GETFILES.PAS }
  30. {$I PPINC1.PAS }
  31.  
  32. { Hashing function for identifiers.  The formula gives a unique value
  33.   in the range 0..255 for each Pascal/Z keyword.  Note that range and
  34.   overflow checking must be turned off for this function even if they
  35.   are enabled for the rest of the program.  }
  36.  
  37. function hash (symbol : key; length : byte) : byte;
  38.  
  39. begin
  40. hash := (ord(symbol[1]) * 5 + ord(symbol[length])) * 5 + length
  41. end; { hash }
  42.  
  43. { Classify an identifier.  We are only interested
  44.   in it if it is a keyword, so we use the hash table. }
  45.  
  46. procedure classid (value : token; length : byte;
  47. var idtype : keysymbol; var iskeyword : boolean);
  48.  
  49. var
  50. keyvalue : key;
  51. i, tabent : byte;
  52.  
  53. begin
  54. if length > maxkeylength then 
  55. begin idtype := othersym; iskeyword := false end
  56. else
  57. begin
  58. for i := 1 to length do keyvalue[i] := upper(value[i]);
  59. for i := length + 1 to maxkeylength do keyvalue[i] := blank;
  60. tabent := hash(keyvalue,length);
  61. if keyvalue = hashtable[tabent].keyword then
  62. begin idtype := hashtable[tabent].symtype; iskeyword := true end
  63. else
  64. begin idtype := othersym; iskeyword := false end
  65. end
  66. end; { classid }
  67.  
  68. { Read an identifier and classify it }
  69.  
  70. procedure getidentifier (sym : symbolinfo);
  71.  
  72. begin
  73. while nextchar.name in [letter,digit] do
  74. storenextchar(sym^.length,sym^.value);
  75. classid(sym^.value,sym^.length,sym^.name,sym^.iskeyword);
  76. if sym^.name in [recordsym,casesym,endsym]
  77. then case sym^.name of
  78. recordsym : recordseen := true;
  79. casesym : if recordseen then sym^.name := casevarsym;
  80. endsym : recordseen := false
  81. end
  82. end; { getidentifier }
  83.  
  84. { Read a number and store it as a string }
  85.  
  86. procedure getnumber (sym : symbolinfo);
  87.  
  88. begin
  89. while nextchar.name = digit do
  90. storenextchar(sym^.length,sym^.value);
  91. sym^.name := othersym
  92. end; { getnumber }
  93.  
  94. { Read a quoted string }
  95.  
  96. procedure getcharliteral (sym : symbolinfo);
  97.  
  98. begin
  99. while nextchar.name = quote do
  100. begin
  101. storenextchar(sym^.length,sym^.value);
  102. while not (nextchar.name in [quote,endofline,filemark]) do
  103. storenextchar(sym^.length,sym^.value);
  104. if nextchar.name = quote
  105. then storenextchar(sym^.length,sym^.value)
  106. end;
  107. sym^.name := othersym
  108. end; { getcharliteral }
  109.  
  110. { Classify a character pair }
  111.  
  112. function chartype : keysymbol;
  113.  
  114. var
  115. nexttwochars : specialchar;
  116. hit : boolean;
  117. thischar : keysymbol;
  118.  
  119. begin
  120. nexttwochars[1] := currchar.value;
  121. nexttwochars[2] := nextchar.value;
  122. thischar := becomes;
  123. hit := false;
  124. while not (hit or (thischar = closecomment)) do
  125. begin
  126. if nexttwochars = dblchar[thischar]
  127. then hit := true
  128. else thischar := succ(thischar)
  129. end;
  130. if not hit then
  131. begin
  132. thischar := opencomment;
  133. while not (hit or (pred(thischar) = period)) do
  134. begin
  135. if currchar.value = sglchar[thischar]
  136. then hit := true
  137. else thischar := succ(thischar) 
  138. end
  139. end;
  140. if hit then chartype := thischar
  141. else chartype := othersym;
  142. end; { chartype }
  143.  
  144. { Read special characters }
  145.  
  146. procedure getspecialchar (sym : symbolinfo);
  147.  
  148. begin
  149. storenextchar(sym^.length,sym^.value);
  150. sym^.name := chartype;
  151. if sym^.name in dblch then storenextchar(sym^.length,sym^.value)
  152. end; { getspecialchar }
  153.  
  154. { Read a symbol using the appropriate procedure }
  155.  
  156. procedure getnextsymbol (sym : symbolinfo);
  157.  
  158. begin
  159. case nextchar.name of
  160. letter : getidentifier(sym);
  161. digit : getnumber(sym);
  162. quote : getcharliteral(sym);
  163. otherchar : begin
  164. getspecialchar(sym);
  165. if sym^.name = opencomment then getcomment(sym)
  166. end;
  167. filemark : sym^.name := endoffile;
  168. else {:} {Turbo} writeln('Unknown character type: ',ord(nextchar.name))
  169. end
  170. end; { getnextsymbol }
  171.  
  172. { Store the next symbol in NEXTSYM }
  173.  
  174. procedure getsymbol;
  175.  
  176. var
  177. dummy : symbolinfo;
  178.  
  179. begin
  180. dummy := currsym;
  181. currsym := nextsym;
  182. nextsym := dummy;
  183. skipblanks(nextsym^.spacesbefore,nextsym^.crsbefore);
  184. nextsym^.length := 0;
  185. nextsym^.iskeyword := false;
  186. if currsym^.name = opencomment
  187. then getcomment(nextsym)
  188. else getnextsymbol(nextsym)
  189. end;
  190.  
  191. { Manage stack of indentation symbols and margins }
  192.  
  193. procedure popstack (var indentsymbol : keysymbol; var prevmargin : byte);
  194.  
  195. begin
  196. if top > 0 
  197. then
  198. begin
  199. indentsymbol := stack[top].indentsymbol;
  200. prevmargin := stack[top].prevmargin;
  201. top := top - 1
  202. end
  203. else 
  204. begin
  205. indentsymbol := othersym; 
  206. prevmargin := 0
  207. end
  208. end; { popstack }
  209.  
  210. procedure pushstack (indentsymbol : keysymbol; prevmargin : byte);
  211.  
  212. begin
  213. top := top + 1;
  214. stack[top].indentsymbol := indentsymbol;
  215. stack[top].prevmargin := prevmargin
  216. end; { pushstack }
  217.  
  218. procedure writecrs (numberofcrs : byte);
  219.  
  220. var
  221. i : byte;
  222.  
  223. begin
  224. if numberofcrs > 0 then
  225. begin
  226. for i := 1 to numberofcrs do writeln(outfile);
  227. outlines := outlines + numberofcrs;
  228. currlinepos := 0
  229. end
  230. end; { writecrs }
  231.  
  232. procedure insertcr;
  233.  
  234. begin
  235. if currsym^.crsbefore = 0
  236. then
  237. begin
  238. writecrs(1); currsym^.spacesbefore := 0
  239. end
  240. end; { insertcr }
  241.  
  242. procedure insertblankline;
  243.  
  244. begin
  245. if currsym^.crsbefore = 0
  246. then
  247. begin
  248. if currlinepos = 0
  249. then writecrs(1)
  250. else writecrs(2);
  251. currsym^.spacesbefore := 0
  252. end
  253. else
  254. if currsym^.crsbefore = 1 then
  255. if currlinepos > 0 then writecrs(1)
  256. end; { insertblankline }
  257.  
  258. { Move margin left according to stack configuration and current symbol }
  259.  
  260. procedure lshifton (dindsym : keysymset);
  261.  
  262. var
  263. indentsymbol : keysymbol;
  264. prevmargin : byte;
  265.  
  266. begin
  267. if top > 0 then
  268. begin
  269. repeat
  270. popstack(indentsymbol,prevmargin);
  271. if indentsymbol in dindsym
  272. then currmargin := prevmargin
  273. until not (indentsymbol in dindsym) or (top = 0);
  274. if not (indentsymbol in dindsym)
  275. then pushstack(indentsymbol,prevmargin)
  276. end
  277. end; { lshifton }
  278.  
  279. { Move margin left according to stack top }
  280.  
  281. procedure lshift;
  282.  
  283. var
  284. indentsymbol : keysymbol;
  285. prevmargin : byte;
  286.  
  287. begin
  288. if top > 0 then
  289. begin
  290. popstack(indentsymbol,prevmargin);
  291. currmargin := prevmargin
  292. end
  293. end; { lshift }
  294.  
  295. { Insert space if room on line }
  296.  
  297. procedure insertspace (var symbol : symbolinfo);
  298.  
  299. begin
  300. if currlinepos < maxlinesize
  301. then
  302. begin
  303. write(outfile,blank);
  304. currlinepos := currlinepos + 1;
  305. if (symbol^.crsbefore = 0) and (symbol^.spacesbefore > 0)
  306. then symbol^.spacesbefore := symbol^.spacesbefore - 1
  307. end
  308. end; { insertspace }
  309.  
  310. { Insert spaces until correct line position reached }
  311.  
  312. procedure movelinepos (newlinepos : byte);
  313.  
  314. var
  315. i : byte;
  316.  
  317. begin
  318. for i := currlinepos + 1 to newlinepos do write(outfile,blank);
  319. currlinepos := newlinepos
  320. end; { movelinepos }
  321.  
  322. { Print a symbol converting keywords to upper case }
  323.  
  324. procedure printsymbol;
  325.  
  326. var
  327. i : byte;
  328.  
  329. begin
  330. if (currsym^.iskeyword and upcasekeywords) then
  331. for i := 1 to currsym^.length do write(outfile,upper(currsym^.value[i]))
  332. else
  333. for i := 1 to currsym^.length do write(outfile,currsym^.value[i]);
  334. startpos := currlinepos;
  335. currlinepos := currlinepos + currsym^.length
  336. end; { printsymbol }
  337.  
  338. { Find position for symbol and then print it }
  339.  
  340. procedure ppsymbol;
  341.  
  342. var
  343. newlinepos : byte;
  344.  
  345. begin
  346. writecrs(currsym^.crsbefore);
  347. if (currlinepos + currsym^.spacesbefore > currmargin)
  348. or (currsym^.name in [opencomment,closecomment])
  349. then newlinepos := currlinepos + currsym^.spacesbefore
  350. else newlinepos := currmargin;
  351. if newlinepos + currsym^.length > maxlinesize
  352. then
  353. begin
  354. writecrs(1);
  355. if currmargin + currsym^.length <= maxlinesize
  356. then newlinepos := currmargin
  357. else
  358. if currsym^.length < maxlinesize
  359. then newlinepos := maxlinesize - currsym^.length
  360. else newlinepos := 0
  361. end;
  362. movelinepos(newlinepos);
  363. printsymbol
  364. end; { ppsymbol }
  365.  
  366. { Print symbols which follow a formatting symbol but which do not
  367.   affect layout }
  368.  
  369. procedure gobble (terminators : keysymset);
  370.  
  371. begin
  372. if top < maxstacksize 
  373. then pushstack(currsym^.name,currmargin);
  374. currmargin := currlinepos;
  375. while not ((nextsym^.name in terminators)
  376.            or (nextsym^.name = endoffile)) do
  377. begin
  378. getsymbol; ppsymbol
  379. end;
  380. lshift
  381. end; { gobble }
  382.  
  383. { Move right, stacking margin positions }
  384.  
  385. procedure rshift (currsym : keysymbol);
  386.  
  387. begin
  388. if top < maxstacksize
  389. then pushstack(currsym,currmargin);
  390. if startpos > currmargin
  391. then currmargin := startpos;
  392. currmargin := currmargin + indent
  393. end; { rshift }
  394.  
  395.  
  396. procedure goodbye;
  397. begin
  398.   close(infile); close(outfile); {Turbo}
  399. end;
  400.  
  401. { Initialize everything }
  402.  
  403. procedure initialize;
  404.  
  405. var
  406. sym : keysymbol;
  407. ch : char;
  408. pos, len : byte;
  409. NumFiles: integer; { from Command Line }
  410. ArgString1,ArgString2: ArgStrType; { File name }
  411.  
  412. begin
  413. LowVideo;  { reverse Turbo's insistence on all-bold console }
  414.  
  415. { Get file name and open files }
  416.  
  417. { IMPORT from ArgLib.pas:  argc, argv, resetOK }
  418. {PZ used getfilenames(extin,extout);}
  419. NumFiles := argc - 1;
  420. if (NumFiles < 2) or (NumFiles > 2) then
  421.     begin writeln(output,'Usage:  PP OldProgram NewProgram'); halt; end;
  422. argv(1,ArgString1);  argv(2,ArgString2);
  423. writeln('Reading from ',ArgString1);
  424. if not resetOK(infile,ArgString1) then
  425.   begin writeln('empty file'); halt; end;
  426. writeln('Writing to   ',ArgString2); assign(outfile,ArgString2);
  427. rewrite({outfilename,} outfile);
  428.  
  429. { Initialize variables and set up control tables }
  430.  
  431. top := 0;
  432. currlinepos := 0;
  433. currmargin := 0;
  434. inlines := 0;
  435. outlines := 0;
  436.  
  437. { Keywords used for formatting }
  438.  
  439. keyword[progsym]    := 'PROGRAM  ';
  440. keyword[funcsym]    := 'FUNCTION ';
  441. keyword[procsym]    := 'PROCEDURE';
  442. keyword[labelsym]   := 'LABEL    ';
  443. keyword[constsym]   := 'CONST    ';
  444. keyword[typesym]    := 'TYPE     ';
  445. keyword[varsym]     := 'VAR      ';
  446. keyword[beginsym]   := 'BEGIN    ';
  447. keyword[repeatsym]  := 'REPEAT   ';
  448. keyword[recordsym]  := 'RECORD   ';
  449. keyword[casesym]    := 'CASE     ';
  450. keyword[ofsym]      := 'OF       ';
  451. keyword[forsym]     := 'FOR      ';
  452. keyword[whilesym]   := 'WHILE    ';
  453. keyword[withsym]    := 'WITH     ';
  454. keyword[dosym]      := 'DO       ';
  455. keyword[ifsym]      := 'IF       ';
  456. keyword[thensym]    := 'THEN     ';
  457. keyword[elsesym]    := 'ELSE     ';
  458. keyword[endsym]     := 'END      ';
  459. keyword[untilsym]   := 'UNTIL    ';
  460.  
  461. { Keywords not used for formatting }
  462.  
  463. keyword[andsym]     := 'AND      ';
  464. keyword[arrsym]     := 'ARRAY    ';
  465. keyword[divsym]     := 'DIV      ';
  466. keyword[downsym]    := 'DOWNTO   ';
  467. keyword[filesym]    := 'FILE     ';
  468. keyword[gotosym]    := 'GOTO     ';
  469. keyword[insym]      := 'IN       ';
  470. keyword[modsym]     := 'MOD      ';
  471. keyword[notsym]     := 'NOT      ';
  472. keyword[nilsym]     := 'NIL      ';
  473. keyword[orsym]      := 'OR       ';
  474. keyword[setsym]     := 'SET      ';
  475. keyword[tosym]      := 'TO       ';
  476. keyword[stringsym]  := 'STRING   ';
  477.  
  478. { Create hash table }
  479.  
  480. for pos := 0 to maxbyte do
  481. begin
  482. hashtable[pos].keyword := '         ';
  483. hashtable[pos].symtype := othersym
  484. end; { for }
  485. for sym := endsym to tosym do
  486. begin
  487. len := maxkeylength;
  488. while keyword[sym,len] = blank do len := len - 1;
  489. pos := hash(keyword[sym],len);
  490. hashtable[pos].keyword := keyword[sym];
  491. hashtable[pos].symtype := sym
  492. end; { for }
  493.  
  494. { Set up other special symbols }
  495.  
  496. dblch := [becomes,opencomment];
  497.  
  498. dblchar[becomes] := ':=';
  499. dblchar[opencomment] := '(*';
  500.  
  501. sglchar[semicolon] := ';';
  502. sglchar[colon]     := ':';
  503. sglchar[equals]    := '=';
  504. sglchar[openparen] := '(';
  505. sglchar[closeparen] := ')';
  506. sglchar[period]    := '.';
  507. sglchar[opencomment] := '{';
  508. sglchar[closecomment] := '}';
  509.  
  510. { Set up the sets that control formatting.  If you want PP to insert a
  511.   line break before every statement, include CRBEFORE in the SELECTED
  512.   set of the appropriate keywords (WHILE, IF, REPEAT, etc.).  The
  513.   disadvantage of this is that PP will sometimes put line breaks 
  514.   where you don't want them, e.g. after ':' in CASE statements.  Note
  515.   also that PP does not understand the Pascal/Z use of ELSE as a
  516.   CASE label -- I wish they'd used OTHERWISE like everybody else.  }
  517.  
  518. for sym := endsym to othersym do
  519. begin
  520. new(option[sym]);
  521. option[sym]^.selected := [];
  522. option[sym]^.dindsym := [];
  523. option[sym]^.terminators := []
  524. end;
  525.  
  526. option[progsym]^.selected    := [blinbefore,spaft];
  527. option[funcsym]^.selected    := [blinbefore,dindonkey,spaft];
  528. option[funcsym]^.dindsym     := [labelsym,constsym,typesym,varsym];
  529. option[procsym]^.selected    := [blinbefore,dindonkey,spaft];
  530. option[procsym]^.dindsym     := [labelsym,constsym,typesym,varsym];
  531. option[labelsym]^.selected   := [blinbefore,spaft,inbytab];
  532. option[constsym]^.selected   := [blinbefore,dindonkey,spaft,inbytab];
  533. option[constsym]^.dindsym    := [labelsym];
  534. option[typesym]^.selected    := [blinbefore,dindonkey,spaft,inbytab];
  535. option[typesym]^.dindsym     := [labelsym,constsym];
  536. option[varsym]^.selected     := [blinbefore,dindonkey,spaft,inbytab];
  537. option[varsym]^.dindsym      := [labelsym,constsym,typesym];
  538. option[beginsym]^.selected   := [dindonkey,inbytab,crafter];
  539. option[beginsym]^.dindsym    := [labelsym,constsym,typesym,varsym];
  540. option[repeatsym]^.selected  := [inbytab,crafter];
  541. option[recordsym]^.selected  := [inbytab,crafter];
  542. option[casesym]^.selected    := [spaft,inbytab,gobsym,crafter];
  543. option[casesym]^.terminators := [ofsym];
  544. option[casevarsym]^.selected := [spaft,inbytab,gobsym,crafter];
  545. option[casevarsym]^.terminators := [ofsym]; 
  546. option[ofsym]^.selected      := [crsupp,spbef];
  547. option[forsym]^.selected     := [spaft,inbytab,gobsym,crafter];
  548. option[forsym]^.terminators  := [dosym];
  549. option[whilesym]^.selected   := [spaft,inbytab,gobsym,crafter];
  550. option[whilesym]^.terminators := [dosym];
  551. option[withsym]^.selected    := [spaft,inbytab,gobsym,crafter];
  552. option[withsym]^.terminators := [dosym];
  553. option[dosym]^.selected      := [crsupp,spbef];
  554. option[ifsym]^.selected      := [spaft,inbytab,gobsym,crafter];
  555. option[ifsym]^.terminators   := [thensym];
  556. option[thensym]^.selected    := [inbytab];
  557. option[elsesym]^.selected    := [crbefore,dindonkey,dindent,inbytab];
  558. option[elsesym]^.dindsym     := [ifsym,elsesym];
  559. option[endsym]^.selected     := [crbefore,dindonkey,dindent,crafter];
  560. option[endsym]^.dindsym      := [ifsym,thensym,elsesym,forsym,whilesym,
  561. withsym,casevarsym,colon,equals];
  562. option[untilsym]^.selected   := [crbefore,dindonkey,dindent,
  563. spaft,gobsym,crafter];
  564. option[untilsym]^.dindsym    := [ifsym,thensym,elsesym,forsym,whilesym,
  565. withsym,colon,equals];
  566. option[untilsym]^.terminators := [endsym,untilsym,elsesym,semicolon];
  567. option[becomes]^.selected    := [spbef,spaft,gobsym];
  568. option[becomes]^.terminators := [endsym,untilsym,elsesym,semicolon];
  569. option[opencomment]^.selected := [crsupp];
  570. option[closecomment]^.selected := [crsupp];
  571. option[semicolon]^.selected  := [crsupp,dindonkey,crafter];
  572. option[semicolon]^.dindsym   := [ifsym,thensym,elsesym,forsym,whilesym,
  573. withsym,colon,equals];
  574. option[colon]^.selected      := [inbytab];
  575. option[equals]^.selected     := [spbef,spaft,inbytab];
  576. option[openparen]^.selected  := [gobsym];
  577. option[openparen]^.terminators := [closeparen];
  578. option[period]^.selected     := [crsupp]; 
  579.  
  580. { Start i/o }
  581.  
  582. crpending := false;
  583. recordseen := false;
  584. getchar;
  585. new(currsym); new(nextsym);
  586. getsymbol;
  587.  
  588. end; { initialize }
  589.  
  590. { Main Program }
  591.  
  592. begin
  593. initialize;
  594. while nextsym^.name <> endoffile do
  595. begin
  596. getsymbol;
  597. sets := option[currsym^.name];
  598. if (crpending and not (crsupp in sets^.selected))
  599. or (crbefore in sets^.selected) then
  600. begin
  601. insertcr; crpending := false
  602. end;
  603. if blinbefore in sets^.selected then
  604. begin
  605. insertblankline; crpending := false
  606. end;
  607. if dindonkey in sets^.selected
  608. then lshifton(sets^.dindsym);
  609. if dindent in sets^.selected
  610. then lshift;
  611. if spbef in sets^.selected
  612. then insertspace(currsym);
  613. ppsymbol;
  614. if spaft in sets^.selected
  615. then insertspace(nextsym);
  616. if inbytab in sets^.selected
  617. then rshift(currsym^.name);
  618. if gobsym in sets^.selected
  619. then gobble(sets^.terminators);
  620. if crafter in sets^.selected
  621. then crpending := true
  622. end;
  623. if crpending then writecrs(1);
  624.  
  625. writeln(inlines:1,' lines read, ',outlines:1,' lines written.');
  626.  
  627. goodbye;
  628.  
  629. end.
  630.