home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / turbopas / niceprnt.lbr / PP.PQS / PP.PAS
Pascal/Delphi Source File  |  1986-06-21  |  21KB  |  814 lines

  1. { Pascal pretty printer.                  Version of   15 March 1985  }
  2.  
  3. { This program is based on a Pascal pretty-printer written by Ledgard,
  4.   Hueras, and Singer.  See SIGPLAN Notices, Vol. 12, No. 7, July 1977,
  5.   pages 101-105.  }
  6. { This version of PP developed under Pascal/Z V4.0 by Peter Grogono.  }
  7. { Minor mods adapting to Turbo Pascal made by Willett Kempton, Mar 1984,
  8.   Oct 84, Mar 85.  Tested under: CP/M-86, MS-DOS, CP/M-80.   }
  9.  
  10. { This program will be more readable after it has been run on itself. }
  11. { Leading blanks are not removed by PP; thus over-indentation must be
  12.    corrected manually. }
  13. { Formatting rules can be adapted to user's taste by simply changing the
  14.    initialization of "options" in procedure "initialze". }
  15.  
  16.  
  17. program pp;
  18.  
  19. const
  20. { Grogono had following 7 CONSTs as include file "CONSTS.PAS" }
  21. NUL = 0;        { ASCII null character }
  22. TAB = 9;        { ASCII tab character }
  23. FF = 12;        { ASCII formfeed character }
  24. CR = 13;        { ASCII carriage return }
  25. ESC = 27;        { ASCII escape character }
  26. blank = ' ';
  27. maxbyte = 255;    { Largest value of 1 byte variable }
  28.  
  29. maxsymbolsize = 80;
  30. maxstacksize = 100;
  31. maxkeylength = 9;     { The longest keyword is PROCEDURE }
  32. maxlinesize = 90;     { Maximum length of output line }
  33. indent = 2;           { Indentation step size for structured statements }
  34. upcasekeywords=FALSE; { If all keywords are to be capitalized }
  35. casediff = 32;        { ord('a') - ord('A') }
  36.  
  37.  
  38. type
  39.  
  40. byte = 0..maxbyte;
  41.  
  42. keysymbol =
  43. { keywords }
  44. (endsym,beginsym,ifsym,thensym,elsesym,procsym,varsym,ofsym,
  45. whilesym,dosym,casesym,withsym,forsym,repeatsym,untilsym,
  46. funcsym,labelsym,constsym,typesym,recordsym,stringsym,progsym,
  47. andsym,arrsym,divsym,downsym,filesym,gotosym,insym,modsym,
  48. notsym,nilsym,orsym,setsym,tosym,casevarsym,
  49. { other symbols }
  50. becomes,opencomment,closecomment,semicolon,colon,equals,
  51. openparen,closeparen,period,endoffile,othersym);
  52.  
  53. options = (crsupp,crbefore,blinbefore,
  54. dindonkey,dindent,spbef,
  55. spaft,gobsym,inbytab,crafter);
  56.  
  57. optionset = set of options;
  58. keysymset = set of keysymbol;
  59.  
  60. tableentry = record
  61. selected : optionset;
  62. dindsym : keysymset;
  63. terminators : keysymset
  64. end;
  65.  
  66. tableptr = ^ tableentry;
  67. optiontable = array [keysymbol] of tableptr;
  68. key = array [1..maxkeylength] of char;
  69. keywordtable = array [endsym..tosym] of key;
  70. specialchar = array [1..2] of char;
  71. dblcharset = set of endsym..othersym;
  72. dblchartable = array [becomes..opencomment] of specialchar;
  73. sglchartable = array [opencomment..period] of char;
  74. token = array [1..maxsymbolsize] of char;
  75.  
  76. symbol = record
  77. name : keysymbol;
  78. value : token;
  79. iskeyword : boolean;
  80. length, spacesbefore, crsbefore : byte
  81. end;
  82.  
  83. symbolinfo = ^ symbol;
  84. charname = (letter,digit,space,quote,endofline,
  85. filemark,otherchar);
  86.  
  87. charinfo = record
  88. name : charname;
  89. value : char
  90. end;
  91.  
  92. stackentry = record
  93. indentsymbol : keysymbol;
  94. prevmargin : byte
  95. end;
  96.  
  97. symbolstack = array [1..maxstacksize] of stackentry;
  98.  
  99. hashentry = record
  100. keyword : key;
  101. symtype : keysymbol
  102. end;
  103.  
  104. var
  105.  
  106. infile,outfile : text;
  107. recordseen : boolean;
  108. currchar,nextchar : charinfo;
  109. currsym,nextsym : symbolinfo;
  110. crpending : boolean;
  111. option : optiontable;
  112. sets : tableptr;
  113. keyword : keywordtable;
  114. dblch : dblcharset;
  115. dblchar : dblchartable;
  116. sglchar : sglchartable;
  117. stack : symbolstack;
  118. top,startpos,currlinepos,currmargin,
  119. inlines,outlines : integer;
  120. hashtable : array [byte] of hashentry;
  121.  
  122.  
  123. {$I ArgLib.pas  } { portable command line routines }
  124.  
  125.  
  126. { Convert letters to upper case }
  127.  
  128. function upper (ch : char) : char;
  129.  
  130. begin
  131. {if ch in ['a'..'z'] then upper := chr(ord(ch)-casediff) else upper := ch }
  132. upper := UpCase(ch);  { use built-in Turbo routine }
  133. end; { upper }
  134.  
  135. { Read the next character and classify it }
  136.  
  137. procedure getchar;
  138.  
  139. var
  140. ch : char;
  141.  
  142. begin
  143. currchar := nextchar;
  144. with nextchar do
  145. if eof(infile) then
  146. begin name := filemark; value := blank end
  147. else
  148. if eoln(infile) then
  149. begin name := endofline; value := blank;
  150. inlines := inlines + 1; readln(infile) end
  151. else
  152. begin
  153. read(infile,ch);
  154. value := ch;
  155. if ch in ['a'..'z','A'..'Z','_'] then name := letter
  156. else
  157. if ch in ['0'..'9'] then name := digit
  158. else
  159. if ch = '''' then name := quote
  160. else
  161. if (ch = blank) or (ch = chr(tab)) then name := space
  162. else name := otherchar
  163. end
  164. end; { getchar }
  165.  
  166. { Store a character in the current symbol }
  167.  
  168. procedure storenextchar(var length : byte; var value : token);
  169.  
  170. begin
  171. getchar;
  172. if length < maxsymbolsize then
  173. begin length := length + 1; value[length] := currchar.value end;
  174. end; { storenextchar }
  175.  
  176. { Count the spaces between symbols }
  177.  
  178. procedure skipblanks (var spacesbefore,crsbefore : byte);
  179.  
  180. begin
  181. spacesbefore := 0;
  182. crsbefore := 0;
  183. while nextchar.name in [space,endofline] do
  184. begin
  185. getchar;
  186. case currchar.name of
  187. space : spacesbefore := spacesbefore + 1;
  188. endofline : begin
  189. crsbefore := crsbefore + 1;
  190. spacesbefore := 0
  191. end
  192. end
  193. end
  194. end; { skipspaces }
  195.  
  196. { Process comments using either brace or parenthesis notation }
  197.  
  198. procedure getcomment (sym : symbolinfo);
  199.  
  200. begin
  201. sym^.name := opencomment;
  202. while not (((currchar.value = '*') and (nextchar.value = ')'))
  203. or (currchar.value = '}')
  204. or (nextchar.name = endofline)
  205. or (nextchar.name = filemark)) do
  206. storenextchar(sym^.length,sym^.value);
  207. if (currchar.value = '*') and (nextchar.value = ')') 
  208. then
  209. begin
  210. storenextchar(sym^.length,sym^.value); sym^.name := closecomment
  211. end;
  212. if currchar.value = '}' 
  213. then sym^.name := closecomment
  214. end; { getcommment }
  215.  
  216. { Hashing function for identifiers.  The formula gives a unique value
  217.   in the range 0..255 for each Pascal/Z keyword.  Note that range and
  218.   overflow checking must be turned off for this function even if they
  219.   are enabled for the rest of the program.  }
  220.  
  221. function hash (symbol : key; length : byte) : byte;
  222.  
  223. begin
  224. hash := (ord(symbol[1]) * 5 + ord(symbol[length])) * 5 + length
  225. end; { hash }
  226.  
  227. { Classify an identifier.  We are only interested
  228.   in it if it is a keyword, so we use the hash table. }
  229.  
  230. procedure classid (value : token; length : byte;
  231. var idtype : keysymbol; var iskeyword : boolean);
  232.  
  233. var
  234. keyvalue : key;
  235. i, tabent : byte;
  236.  
  237. begin
  238. if length > maxkeylength then 
  239. begin idtype := othersym; iskeyword := false end
  240. else
  241. begin
  242. for i := 1 to length do keyvalue[i] := upper(value[i]);
  243. for i := length + 1 to maxkeylength do keyvalue[i] := blank;
  244. tabent := hash(keyvalue,length);
  245. if keyvalue = hashtable[tabent].keyword then
  246. begin idtype := hashtable[tabent].symtype; iskeyword := true end
  247. else
  248. begin idtype := othersym; iskeyword := false end
  249. end
  250. end; { classid }
  251.  
  252. { Read an identifier and classify it }
  253.  
  254. procedure getidentifier (sym : symbolinfo);
  255.  
  256. begin
  257. while nextchar.name in [letter,digit] do
  258. storenextchar(sym^.length,sym^.value);
  259. classid(sym^.value,sym^.length,sym^.name,sym^.iskeyword);
  260. if sym^.name in [recordsym,casesym,endsym]
  261. then case sym^.name of
  262. recordsym : recordseen := true;
  263. casesym : if recordseen then sym^.name := casevarsym;
  264. endsym : recordseen := false
  265. end
  266. end; { getidentifier }
  267.  
  268. { Read a number and store it as a string }
  269.  
  270. procedure getnumber (sym : symbolinfo);
  271.  
  272. begin
  273. while nextchar.name = digit do
  274. storenextchar(sym^.length,sym^.value);
  275. sym^.name := othersym
  276. end; { getnumber }
  277.  
  278. { Read a quoted string }
  279.  
  280. procedure getcharliteral (sym : symbolinfo);
  281.  
  282. begin
  283. while nextchar.name = quote do
  284. begin
  285. storenextchar(sym^.length,sym^.value);
  286. while not (nextchar.name in [quote,endofline,filemark]) do
  287. storenextchar(sym^.length,sym^.value);
  288. if nextchar.name = quote
  289. then storenextchar(sym^.length,sym^.value)
  290. end;
  291. sym^.name := othersym
  292. end; { getcharliteral }
  293.  
  294. { Classify a character pair }
  295.  
  296. function chartype : keysymbol;
  297.  
  298. var
  299. nexttwochars : specialchar;
  300. hit : boolean;
  301. thischar : keysymbol;
  302.  
  303. begin
  304. nexttwochars[1] := currchar.value;
  305. nexttwochars[2] := nextchar.value;
  306. thischar := becomes;
  307. hit := false;
  308. while not (hit or (thischar = closecomment)) do
  309. begin
  310. if nexttwochars = dblchar[thischar]
  311. then hit := true
  312. else thischar := succ(thischar)
  313. end;
  314. if not hit then
  315. begin
  316. thischar := opencomment;
  317. while not (hit or (pred(thischar) = period)) do
  318. begin
  319. if currchar.value = sglchar[thischar]
  320. then hit := true
  321. else thischar := succ(thischar) 
  322. end
  323. end;
  324. if hit then chartype := thischar
  325. else chartype := othersym;
  326. end; { chartype }
  327.  
  328. { Read special characters }
  329.  
  330. procedure getspecialchar (sym : symbolinfo);
  331.  
  332. begin
  333. storenextchar(sym^.length,sym^.value);
  334. sym^.name := chartype;
  335. if sym^.name in dblch then storenextchar(sym^.length,sym^.value)
  336. end; { getspecialchar }
  337.  
  338. { Read a symbol using the appropriate procedure }
  339.  
  340. procedure getnextsymbol (sym : symbolinfo);
  341.  
  342. begin
  343. case nextchar.name of
  344. letter : getidentifier(sym);
  345. digit : getnumber(sym);
  346. quote : getcharliteral(sym);
  347. otherchar : begin
  348. getspecialchar(sym);
  349. if sym^.name = opencomment then getcomment(sym)
  350. end;
  351. filemark : sym^.name := endoffile;
  352. space,
  353. endofline: {else:} writeln('Unexpected character type: ',ord(nextchar.name))
  354. end
  355. end; { getnextsymbol }
  356.  
  357. { Store the next symbol in NEXTSYM }
  358.  
  359. procedure getsymbol;
  360.  
  361. var
  362. dummy : symbolinfo;
  363.  
  364. begin
  365. dummy := currsym;
  366. currsym := nextsym;
  367. nextsym := dummy;
  368. skipblanks(nextsym^.spacesbefore,nextsym^.crsbefore);
  369. nextsym^.length := 0;
  370. nextsym^.iskeyword := false;
  371. if currsym^.name = opencomment
  372. then getcomment(nextsym)
  373. else getnextsymbol(nextsym)
  374. end;
  375.  
  376. { Manage stack of indentation symbols and margins }
  377.  
  378. procedure popstack (var indentsymbol : keysymbol; var prevmargin : byte);
  379.  
  380. begin
  381. if top > 0 
  382. then
  383. begin
  384. indentsymbol := stack[top].indentsymbol;
  385. prevmargin := stack[top].prevmargin;
  386. top := top - 1
  387. end
  388. else 
  389. begin
  390. indentsymbol := othersym; 
  391. prevmargin := 0
  392. end
  393. end; { popstack }
  394.  
  395. procedure pushstack (indentsymbol : keysymbol; prevmargin : byte);
  396.  
  397. begin
  398. top := top + 1;
  399. stack[top].indentsymbol := indentsymbol;
  400. stack[top].prevmargin := prevmargin
  401. end; { pushstack }
  402.  
  403. procedure writecrs (numberofcrs : byte);
  404.  
  405. var
  406. i : byte;
  407.  
  408. begin
  409. if numberofcrs > 0 then
  410. begin
  411. for i := 1 to numberofcrs do writeln(outfile);
  412. outlines := outlines + numberofcrs;
  413. currlinepos := 0
  414. end
  415. end; { writecrs }
  416.  
  417. procedure insertcr;
  418.  
  419. begin
  420. if currsym^.crsbefore = 0
  421. then
  422. begin
  423. writecrs(1); currsym^.spacesbefore := 0
  424. end
  425. end; { insertcr }
  426.  
  427. procedure insertblankline;
  428.  
  429. begin
  430. if currsym^.crsbefore = 0
  431. then
  432. begin
  433. if currlinepos = 0
  434. then writecrs(1)
  435. else writecrs(2);
  436. currsym^.spacesbefore := 0
  437. end
  438. else
  439. if currsym^.crsbefore = 1 then
  440. if currlinepos > 0 then writecrs(1)
  441. end; { insertblankline }
  442.  
  443. { Move margin left according to stack configuration and current symbol }
  444.  
  445. procedure lshifton (dindsym : keysymset);
  446.  
  447. var
  448. indentsymbol : keysymbol;
  449. prevmargin : byte;
  450.  
  451. begin
  452. if top > 0 then
  453. begin
  454. repeat
  455. popstack(indentsymbol,prevmargin);
  456. if indentsymbol in dindsym
  457. then currmargin := prevmargin
  458. until not (indentsymbol in dindsym) or (top = 0);
  459. if not (indentsymbol in dindsym)
  460. then pushstack(indentsymbol,prevmargin)
  461. end
  462. end; { lshifton }
  463.  
  464. { Move margin left according to stack top }
  465.  
  466. procedure lshift;
  467.  
  468. var
  469. indentsymbol : keysymbol;
  470. prevmargin : byte;
  471.  
  472. begin
  473. if top > 0 then
  474. begin
  475. popstack(indentsymbol,prevmargin);
  476. currmargin := prevmargin
  477. end
  478. end; { lshift }
  479.  
  480. { Insert space if room on line }
  481.  
  482. procedure insertspace (var symbol : symbolinfo);
  483.  
  484. begin
  485. if currlinepos < maxlinesize
  486. then
  487. begin
  488. write(outfile,blank);
  489. currlinepos := currlinepos + 1;
  490. if (symbol^.crsbefore = 0) and (symbol^.spacesbefore > 0)
  491. then symbol^.spacesbefore := symbol^.spacesbefore - 1
  492. end
  493. end; { insertspace }
  494.  
  495. { Insert spaces until correct line position reached }
  496.  
  497. procedure movelinepos (newlinepos : byte);
  498.  
  499. var
  500. i : byte;
  501.  
  502. begin
  503. for i := currlinepos + 1 to newlinepos do write(outfile,blank);
  504. currlinepos := newlinepos
  505. end; { movelinepos }
  506.  
  507. { Print a symbol converting keywords to upper case }
  508.  
  509. procedure printsymbol;
  510.  
  511. var
  512. i : byte;
  513.  
  514. begin
  515. if (currsym^.iskeyword and upcasekeywords) then
  516. for i := 1 to currsym^.length do write(outfile,upper(currsym^.value[i]))
  517. else
  518. for i := 1 to currsym^.length do write(outfile,currsym^.value[i]);
  519. startpos := currlinepos;
  520. currlinepos := currlinepos + currsym^.length
  521. end; { printsymbol }
  522.  
  523. { Find position for symbol and then print it }
  524.  
  525. procedure ppsymbol;
  526.  
  527. var
  528. newlinepos : byte;
  529.  
  530. begin
  531. writecrs(currsym^.crsbefore);
  532. if (currlinepos + currsym^.spacesbefore > currmargin)
  533. or (currsym^.name in [opencomment,closecomment])
  534. then newlinepos := currlinepos + currsym^.spacesbefore
  535. else newlinepos := currmargin;
  536. if newlinepos + currsym^.length > maxlinesize
  537. then
  538. begin
  539. writecrs(1);
  540. if currmargin + currsym^.length <= maxlinesize
  541. then newlinepos := currmargin
  542. else
  543. if currsym^.length < maxlinesize
  544. then newlinepos := maxlinesize - currsym^.length
  545. else newlinepos := 0
  546. end;
  547. movelinepos(newlinepos);
  548. printsymbol
  549. end; { ppsymbol }
  550.  
  551. { Print symbols which follow a formatting symbol but which do not
  552.   affect layout }
  553.  
  554. procedure gobble (terminators : keysymset);
  555.  
  556. begin
  557. if top < maxstacksize 
  558. then pushstack(currsym^.name,currmargin);
  559. currmargin := currlinepos;
  560. while not ((nextsym^.name in terminators)
  561.            or (nextsym^.name = endoffile)) do
  562. begin
  563. getsymbol; ppsymbol
  564. end;
  565. lshift
  566. end; { gobble }
  567.  
  568. { Move right, stacking margin positions }
  569.  
  570. procedure rshift (currsym : keysymbol);
  571.  
  572. begin
  573. if top < maxstacksize
  574. then pushstack(currsym,currmargin);
  575. if startpos > currmargin
  576. then currmargin := startpos;
  577. currmargin := currmargin + indent
  578. end; { rshift }
  579.  
  580.  
  581. procedure goodbye;
  582. begin
  583.   close(infile); close(outfile); {Turbo}
  584. end;
  585.  
  586. { Initialize everything }
  587.  
  588. procedure initialize;
  589.  
  590. var
  591. sym : keysymbol;
  592. ch : char;
  593. pos, len : byte;
  594. NumFiles: integer; { from Command Line }
  595. ArgString1,ArgString2: ArgStrType; { File names }
  596.  
  597. begin
  598.  
  599. { Get file name and open files }
  600.  
  601. { IMPORT from ArgLib.pas:  argcount, argv, resetOK }
  602. {PZ used getfilenames(extin,extout);}
  603. NumFiles := argcount;
  604. if (NumFiles < 2) or (NumFiles > 2) then
  605.   begin writeln(output,'Usage:  PP  OldProgram  NewProgram'); halt; end;
  606. argv(1,ArgString1);  argv(2,ArgString2);
  607. write('Reading from ',ArgString1);
  608. if not resetOK(infile,ArgString1) then
  609.   begin writeln('--> empty file'); halt; end;
  610. writeln('  Writing to ',ArgString2);
  611. assign(outfile,ArgString2); rewrite( outfile);
  612.  
  613. { Initialize variables and set up control tables }
  614.  
  615. top := 0;
  616. currlinepos := 0;
  617. currmargin := 0;
  618. inlines := 0;
  619. outlines := 0;
  620.  
  621. { Keywords used for formatting }
  622.  
  623. keyword[progsym]    := 'PROGRAM  ';
  624. keyword[funcsym]    := 'FUNCTION ';
  625. keyword[procsym]    := 'PROCEDURE';
  626. keyword[labelsym]   := 'LABEL    ';
  627. keyword[constsym]   := 'CONST    ';
  628. keyword[typesym]    := 'TYPE     ';
  629. keyword[varsym]     := 'VAR      ';
  630. keyword[beginsym]   := 'BEGIN    ';
  631. keyword[repeatsym]  := 'REPEAT   ';
  632. keyword[recordsym]  := 'RECORD   ';
  633. keyword[casesym]    := 'CASE     ';
  634. keyword[ofsym]      := 'OF       ';
  635. keyword[forsym]     := 'FOR      ';
  636. keyword[whilesym]   := 'WHILE    ';
  637. keyword[withsym]    := 'WITH     ';
  638. keyword[dosym]      := 'DO       ';
  639. keyword[ifsym]      := 'IF       ';
  640. keyword[thensym]    := 'THEN     ';
  641. keyword[elsesym]    := 'ELSE     ';
  642. keyword[endsym]     := 'END      ';
  643. keyword[untilsym]   := 'UNTIL    ';
  644.  
  645. { Keywords not used for formatting }
  646.  
  647. keyword[andsym]     := 'AND      ';
  648. keyword[arrsym]     := 'ARRAY    ';
  649. keyword[divsym]     := 'DIV      ';
  650. keyword[downsym]    := 'DOWNTO   ';
  651. keyword[filesym]    := 'FILE     ';
  652. keyword[gotosym]    := 'GOTO     ';
  653. keyword[insym]      := 'IN       ';
  654. keyword[modsym]     := 'MOD      ';
  655. keyword[notsym]     := 'NOT      ';
  656. keyword[nilsym]     := 'NIL      ';
  657. keyword[orsym]      := 'OR       ';
  658. keyword[setsym]     := 'SET      ';
  659. keyword[tosym]      := 'TO       ';
  660. keyword[stringsym]  := 'STRING   ';
  661.  
  662. { Create hash table }
  663.  
  664. for pos := 0 to maxbyte do
  665. begin
  666. hashtable[pos].keyword := '         ';
  667. hashtable[pos].symtype := othersym
  668. end; { for }
  669. for sym := endsym to tosym do
  670. begin
  671. len := maxkeylength;
  672. while keyword[sym,len] = blank do len := len - 1;
  673. pos := hash(keyword[sym],len);
  674. hashtable[pos].keyword := keyword[sym];
  675. hashtable[pos].symtype := sym
  676. end; { for }
  677.  
  678. { Set up other special symbols }
  679.  
  680. dblch := [becomes,opencomment];
  681.  
  682. dblchar[becomes] := ':=';
  683. dblchar[opencomment] := '(*';
  684.  
  685. sglchar[semicolon] := ';';
  686. sglchar[colon]     := ':';
  687. sglchar[equals]    := '=';
  688. sglchar[openparen] := '(';
  689. sglchar[closeparen] := ')';
  690. sglchar[period]    := '.';
  691. sglchar[opencomment] := '{';
  692. sglchar[closecomment] := '}';
  693.  
  694. { Set up the sets that control formatting.  If you want PP to insert a
  695.   line break before every statement, include CRBEFORE in the SELECTED
  696.   set of the appropriate keywords (WHILE, IF, REPEAT, etc.).  The
  697.   disadvantage of this is that PP will sometimes put line breaks 
  698.   where you don't want them, e.g. after ':' in CASE statements.  Note
  699.   also that PP does not understand the Pascal/Z use of ELSE as a
  700.   CASE label -- I wish they'd used OTHERWISE like everybody else.  }
  701.  
  702. for sym := endsym to othersym do
  703. begin
  704. new(option[sym]);
  705. option[sym]^.selected := [];
  706. option[sym]^.dindsym := [];
  707. option[sym]^.terminators := []
  708. end;
  709.  
  710. option[progsym]^.selected    := [blinbefore,spaft];
  711. option[funcsym]^.selected    := [blinbefore,dindonkey,spaft];
  712. option[funcsym]^.dindsym     := [labelsym,constsym,typesym,varsym];
  713. option[procsym]^.selected    := [blinbefore,dindonkey,spaft];
  714. option[procsym]^.dindsym     := [labelsym,constsym,typesym,varsym];
  715. option[labelsym]^.selected   := [blinbefore,spaft,inbytab];
  716. option[constsym]^.selected   := [blinbefore,dindonkey,spaft,inbytab];
  717. option[constsym]^.dindsym    := [labelsym];
  718. option[typesym]^.selected    := [blinbefore,dindonkey,spaft,inbytab];
  719. option[typesym]^.dindsym     := [labelsym,constsym];
  720. option[varsym]^.selected     := [blinbefore,dindonkey,spaft,inbytab];
  721. option[varsym]^.dindsym      := [labelsym,constsym,typesym];
  722. option[beginsym]^.selected   := [dindonkey,inbytab,crafter];
  723. option[beginsym]^.dindsym    := [labelsym,constsym,typesym,varsym];
  724. option[repeatsym]^.selected  := [inbytab,crafter];
  725. option[recordsym]^.selected  := [inbytab,crafter];
  726. option[casesym]^.selected    := [spaft,inbytab,gobsym,crafter];
  727. option[casesym]^.terminators := [ofsym];
  728. option[casevarsym]^.selected := [spaft,inbytab,gobsym,crafter];
  729. option[casevarsym]^.terminators := [ofsym]; 
  730. option[ofsym]^.selected      := [crsupp,spbef];
  731. option[forsym]^.selected     := [spaft,inbytab,gobsym,crafter];
  732. option[forsym]^.terminators  := [dosym];
  733. option[whilesym]^.selected   := [spaft,inbytab,gobsym,crafter];
  734. option[whilesym]^.terminators := [dosym];
  735. option[withsym]^.selected    := [spaft,inbytab,gobsym,crafter];
  736. option[withsym]^.terminators := [dosym];
  737. option[dosym]^.selected      := [crsupp,spbef];
  738. option[ifsym]^.selected      := [spaft,inbytab,gobsym,crafter];
  739. option[ifsym]^.terminators   := [thensym];
  740. option[thensym]^.selected    := [inbytab];
  741. option[elsesym]^.selected    := [crbefore,dindonkey,dindent,inbytab];
  742. option[elsesym]^.dindsym     := [ifsym,elsesym];
  743. option[endsym]^.selected     := [crbefore,dindonkey,dindent,crafter];
  744. option[endsym]^.dindsym      := [ifsym,thensym,elsesym,forsym,whilesym,
  745. withsym,casevarsym,colon,equals];
  746. option[untilsym]^.selected   := [crbefore,dindonkey,dindent,
  747. spaft,gobsym,crafter];
  748. option[untilsym]^.dindsym    := [ifsym,thensym,elsesym,forsym,whilesym,
  749. withsym,colon,equals];
  750. option[untilsym]^.terminators := [endsym,untilsym,elsesym,semicolon];
  751. option[becomes]^.selected    := [spbef,spaft,gobsym];
  752. option[becomes]^.terminators := [endsym,untilsym,elsesym,semicolon];
  753. option[opencomment]^.selected := [crsupp];
  754. option[closecomment]^.selected := [crsupp];
  755. option[semicolon]^.selected  := [crsupp,dindonkey,crafter];
  756. option[semicolon]^.dindsym   := [ifsym,thensym,elsesym,forsym,whilesym,
  757. withsym,colon,equals];
  758. option[colon]^.selected      := [inbytab];
  759. option[equals]^.selected     := [spbef,spaft,inbytab];
  760. option[openparen]^.selected  := [gobsym];
  761. option[openparen]^.terminators := [closeparen];
  762. option[period]^.selected     := [crsupp]; 
  763.  
  764. { Start i/o }
  765.  
  766. crpending := false;
  767. recordseen := false;
  768. getchar;
  769. new(currsym); new(nextsym);
  770. getsymbol;
  771.  
  772. end; { initialize }
  773.  
  774. { Main Program }
  775.  
  776. begin
  777. initialize;
  778. while nextsym^.name <> endoffile do
  779. begin
  780. getsymbol;
  781. sets := option[currsym^.name];
  782. if (crpending and not (crsupp in sets^.selected))
  783. or (crbefore in sets^.selected) then
  784. begin
  785. insertcr; crpending := false
  786. end;
  787. if blinbefore in sets^.selected then
  788. begin
  789. insertblankline; crpending := false
  790. end;
  791. if dindonkey in sets^.selected
  792. then lshifton(sets^.dindsym);
  793. if dindent in sets^.selected
  794. then lshift;
  795. if spbef in sets^.selected
  796. then insertspace(currsym);
  797. ppsymbol;
  798. if spaft in sets^.selected
  799. then insertspace(nextsym);
  800. if inbytab in sets^.selected
  801. then rshift(currsym^.name);
  802. if gobsym in sets^.selected
  803. then gobble(sets^.terminators);
  804. if crafter in sets^.selected
  805. then crpending := true
  806. end;
  807. if crpending then writecrs(1);
  808.  
  809. writeln(inlines:1,' lines read, ',outlines:1,' lines written.');
  810.  
  811. goodbye;
  812.  
  813. end.
  814.