m2p.mod 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. MODULE Modula2PrettyPrinter;
  2. FROM InOut IMPORT
  3. Done, Read, Write, WriteLn, WriteString, OpenInput, OpenOutput,
  4. CloseInput, CloseOutput;
  5. (*
  6. ** Modula-2 Prettyprinter, November 1985.
  7. **
  8. ** by Ken Yap, U of Rochester, CS Dept.
  9. **
  10. ** Permission to copy, modify, and distribute, but not for profit,
  11. ** is hereby granted, provided that this note is included.
  12. **
  13. ** adapted from a Pascal Program Formatter
  14. ** by J. E. Crider, Shell Oil Company,
  15. ** Houston, Texas 77025
  16. **
  17. ** This program formats Modula-2 programs according
  18. ** to structured formatting principles
  19. **
  20. ** A valid Modula-2 program is read from the input and
  21. ** a formatted program is written to the output.
  22. ** It is basically a recursive descent parser with actions
  23. ** intermixed with syntax scanning.
  24. **
  25. ** The actions of the program are as follows:
  26. **
  27. ** FORMATTING: Each structured statement is formatted
  28. ** in the following pattern (with indentation "indent"):
  29. **
  30. ** XXXXXX header XXXXXXXX
  31. ** XXXXXXXXXXXXXXXXXX
  32. ** XXXXX body XXXXXX
  33. ** XXXXXXXXXXXXXXXXXX
  34. ** END
  35. **
  36. ** where the header is one of:
  37. **
  38. ** IF <expression> THEN
  39. ** ELSIF <expression> THEN
  40. ** ELSE
  41. ** WHILE <expression> DO
  42. ** FOR <control variable> := <FOR list> DO
  43. ** WITH <RECORD variable> DO
  44. ** REPEAT
  45. ** LOOP
  46. ** CASE <expression> OF
  47. ** <CASE label list>:
  48. **
  49. ** and the last line begins with UNTIL or is END.
  50. ** Other program parts are formatted similarly. The headers are:
  51. **
  52. ** <MODULE/PROCEDURE heading>;
  53. ** CONST
  54. ** TYPE
  55. ** VAR
  56. ** BEGIN
  57. ** (various FOR records AND RECORD variants)
  58. **
  59. ** COMMENTS: Each comment that starts before or on a specified
  60. ** column on an input line (program constant "commthresh") is
  61. ** copied without shifting or reformatting. Each comment that
  62. ** starts after "commthresh" is reformatted and left-justified
  63. ** following the aligned comment base column ("alcommbase").
  64. **
  65. ** SPACES AND BLANK LINES: Spaces not at line breaks are copied from
  66. ** the input. Blank lines are copied from the input if they appear
  67. ** between statements (or appropriate declaration units). A blank
  68. ** line is inserted above each significant part of each program/
  69. ** procedure if one is not already there.
  70. **
  71. ** CONTINUATION: Lines that are too long for an output line are
  72. ** continued with additional indentation ("contindent").
  73. *)
  74. CONST
  75. TAB = 11C;
  76. NEWLINE = 12C; (* for Unix *)
  77. FF = 14C;
  78. maxrwlen = 15; (* size of reserved word strings *)
  79. ordminchar = 0; (* ord of lowest char in char set *)
  80. ordmaxchar = 127; (* ord of highest char in char set *)
  81. (* The following parameters may be adjusted for the installation: *)
  82. maxinlen = 255; (* maximum width of input line + 1 *)
  83. maxoutlen = 80; (* maximum width of output line *)
  84. tabinterval = 8; (* interval between tab columns *)
  85. initmargin = 0; (* initial value of output margin *)
  86. commthresh = tabinterval; (* column threshhold in input for
  87. comments to be aligned *)
  88. alcommbase = 40; (* aligned comments in output start
  89. after this column *)
  90. indent = tabinterval; (* RECOMMENDED indentation increment *)
  91. contindent = tabinterval; (* continuation indentation, >indent *)
  92. commindent = tabinterval; (* comment continuation indentation *)
  93. TYPE
  94. natural = INTEGER[0..32767];
  95. inrange = INTEGER[0..maxinlen];
  96. outrange = INTEGER[0..maxoutlen];
  97. errortype = (longline, noendcomm, notquote, longword, notdo, notof,
  98. notend, notthen, notbegin, notuntil, notident, notsemicolon, notcolon,
  99. notperiod, notparen, noeof);
  100. chartype = (illegal, special, chapostrophe, chleftparen, chrightparen,
  101. chperiod, digit, chcolon, chsemicolon, chlessthan, chgreaterthan,
  102. letter, chleftbrace, chbar);
  103. chartypeset = SET OF chartype; (* for reserved word recognition *)
  104. resword = ( (* reserved words ordered by length *)
  105. rwif, rwdo, rwof, rwto, rwin, rwor,
  106. (* length: 2 *)
  107. rwend, rwfor, rwvar, rwdiv, rwmod, rwset, rwand, rwnot, rwnil,
  108. (* length: 3 *)
  109. rwthen, rwelse, rwwith, rwcase, rwtype, rwloop, rwfrom,
  110. (* length: 4 *)
  111. rwbegin, rwelsif, rwuntil, rwwhile, rwarray, rwconst,
  112. (* length: 5 *)
  113. rwrepeat, rwrecord, rwmodule, rwimport, rwexport,
  114. (* length: 6 *)
  115. rwpointer, (* length: 7 *)
  116. rwprocedure, rwqualified, (* length: 9 *)
  117. rwdefinition, (* length: 10 *)
  118. rwimplementation, (* length: 14 *)
  119. rwx); (* length: 15 for table sentinel *)
  120. rwstring = ARRAY [1..maxrwlen] OF CHAR;
  121. firstclass = ( (* class of word if on new line *)
  122. newclause, (* start of new clause *)
  123. continue, (* continuation of clause *)
  124. alcomm, (* start of aligned comment *)
  125. contalcomm, (* continuation of aligned comment *)
  126. uncomm, (* start of unaligned comment *)
  127. contuncomm); (* continuation of unaligned comment *)
  128. wordtype = RECORD (* data record for word *)
  129. whenfirst : firstclass; (* class of word if on new line *)
  130. puncfollows : BOOLEAN; (* to reduce dangling punctuation *)
  131. blanklncount : natural; (* number of preceding blank lines *)
  132. spaces : INTEGER; (* number of spaces preceding word *)
  133. base : [-1..maxinlen]; (* inline.buf[base] precedes word *)
  134. size : inrange;
  135. END; (* length of word in inline.buf *)
  136. symboltype = ( (* symbols for syntax analysis *)
  137. symodule, sydefinition, syimplementation, syfrom, syimport, syexport,
  138. syqual, syproc, declarator, sybegin, syend, syif, sythen, syelsif,
  139. syelse, syloop, sycase, syof, syuntil, syrepeat, forwhilewith, sydo,
  140. syrecord, ident, intconst, semicolon, leftparen, rightparen, period,
  141. colon, bar, othersym, otherword, comment, syeof);
  142. symbolset = SET OF symboltype;
  143. VAR
  144. inline : RECORD (* input line data *)
  145. endoffile : BOOLEAN; (* end of file on input? *)
  146. ch : CHAR; (* current char, buf[index] *)
  147. index : inrange; (* subscript of current char *)
  148. len : natural; (* length of input line in buf *)
  149. buf : ARRAY [1..maxinlen] OF CHAR;
  150. END;
  151. outline : RECORD (* output line data *)
  152. blanklns : natural; (* number of preceding blank lines *)
  153. len : outrange; (* number of chars in buf *)
  154. buf : ARRAY [1..maxoutlen] OF CHAR;
  155. END;
  156. curword : wordtype; (* current word *)
  157. margin : outrange; (* left margin *)
  158. lnpending : BOOLEAN; (* new line before next symbol? *)
  159. inheader : BOOLEAN; (* are we scanning a proc header? *)
  160. symbol : symboltype; (* current symbol *)
  161. (* Structured Constants *)
  162. headersyms : symbolset; (* headers for program parts *)
  163. strucsyms : symbolset; (* symbols that begin structured
  164. statements *)
  165. stmtendsyms : symbolset; (* symbols that follow statements *)
  166. stopsyms : symbolset; (* symbols that stop expression scan *)
  167. recendsyms : symbolset; (* symbols that stop record scan *)
  168. datawords : symbolset; (* to reduce dangling punctuation *)
  169. firstrw : ARRAY [1..maxrwlen] OF resword;
  170. rwword : ARRAY [rwif..rwimplementation] OF rwstring;
  171. rwsy : ARRAY [rwif..rwimplementation] OF symboltype;
  172. charclass : ARRAY CHAR OF chartype;
  173. symbolclass : ARRAY chartype OF symboltype;
  174. PROCEDURE StrCmp(a, b : rwstring) : BOOLEAN;
  175. VAR
  176. i : INTEGER;
  177. BEGIN
  178. FOR i := 1 TO maxrwlen DO
  179. IF a[i] # b[i] THEN
  180. RETURN FALSE;
  181. END;
  182. END;
  183. RETURN TRUE;
  184. END StrCmp;
  185. PROCEDURE StructConsts;
  186. (* establish values of structured constants *)
  187. VAR
  188. i : [ordminchar..ordmaxchar]; (* loop index *)
  189. ch : CHAR; (* loop index *)
  190. PROCEDURE BuildResWord(rw : resword; symword : rwstring; symbol : symboltype);
  191. BEGIN
  192. rwword[rw] := symword; (* reserved word string *)
  193. rwsy[rw] := symbol; (* map to symbol *)
  194. END BuildResWord;
  195. BEGIN (* StructConsts *)
  196. (* symbol sets for syntax analysis *)
  197. headersyms := symbolset{symodule, syproc, declarator, sybegin, syend,
  198. syeof};
  199. strucsyms := symbolset{sycase, syrepeat, syif, forwhilewith, syloop};
  200. stmtendsyms := symbolset{semicolon, bar, syend, syuntil, syelsif,
  201. syelse, syeof};
  202. stopsyms := headersyms + strucsyms + stmtendsyms;
  203. recendsyms := symbolset{rightparen, syend, syeof};
  204. datawords := symbolset{otherword, intconst, ident, syend};
  205. (* constants for recognizing reserved words *)
  206. firstrw[1] := rwif; (* length: 1 *)
  207. firstrw[2] := rwif; (* length: 2 *)
  208. BuildResWord(rwif, 'IF ', syif);
  209. BuildResWord(rwdo, 'DO ', sydo);
  210. BuildResWord(rwof, 'OF ', syof);
  211. BuildResWord(rwto, 'TO ', othersym);
  212. BuildResWord(rwin, 'IN ', othersym);
  213. BuildResWord(rwor, 'OR ', othersym);
  214. firstrw[3] := rwend; (* length: 3 *)
  215. BuildResWord(rwend, 'END ', syend);
  216. BuildResWord(rwfor, 'FOR ', forwhilewith);
  217. BuildResWord(rwvar, 'VAR ', declarator);
  218. BuildResWord(rwdiv, 'DIV ', othersym);
  219. BuildResWord(rwmod, 'MOD ', othersym);
  220. BuildResWord(rwset, 'SET ', othersym);
  221. BuildResWord(rwand, 'AND ', othersym);
  222. BuildResWord(rwnot, 'NOT ', othersym);
  223. BuildResWord(rwnil, 'NIL ', otherword);
  224. firstrw[4] := rwthen; (* length: 4 *)
  225. BuildResWord(rwthen, 'THEN ', sythen);
  226. BuildResWord(rwelse, 'ELSE ', syelse);
  227. BuildResWord(rwwith, 'WITH ', forwhilewith);
  228. BuildResWord(rwloop, 'LOOP ', syloop);
  229. BuildResWord(rwfrom, 'FROM ', syfrom);
  230. BuildResWord(rwcase, 'CASE ', sycase);
  231. BuildResWord(rwtype, 'TYPE ', declarator);
  232. firstrw[5] := rwbegin; (* length: 5 *)
  233. BuildResWord(rwbegin, 'BEGIN ', sybegin);
  234. BuildResWord(rwelsif, 'ELSIF ', syelsif);
  235. BuildResWord(rwuntil, 'UNTIL ', syuntil);
  236. BuildResWord(rwwhile, 'WHILE ', forwhilewith);
  237. BuildResWord(rwarray, 'ARRAY ', othersym);
  238. BuildResWord(rwconst, 'CONST ', declarator);
  239. firstrw[6] := rwrepeat; (* length: 6 *)
  240. BuildResWord(rwrepeat, 'REPEAT ', syrepeat);
  241. BuildResWord(rwrecord, 'RECORD ', syrecord);
  242. BuildResWord(rwmodule, 'MODULE ', symodule);
  243. BuildResWord(rwimport, 'IMPORT ', syimport);
  244. BuildResWord(rwexport, 'EXPORT ', syexport);
  245. firstrw[7] := rwpointer; (* length: 7 *)
  246. BuildResWord(rwpointer, 'POINTER ', othersym);
  247. firstrw[8] := rwprocedure; (* length: 8 *)
  248. firstrw[9] := rwprocedure; (* length: 9 *)
  249. BuildResWord(rwprocedure, 'PROCEDURE ', syproc);
  250. BuildResWord(rwqualified, 'QUALIFIED ', syqual);
  251. firstrw[10] := rwdefinition; (* length: 10 *)
  252. BuildResWord(rwdefinition, 'DEFINITION ', sydefinition);
  253. firstrw[11] := rwimplementation;(* length: 11 *)
  254. firstrw[12] := rwimplementation;(* length: 12 *)
  255. firstrw[13] := rwimplementation;(* length: 13 *)
  256. firstrw[14] := rwimplementation;(* length: 14 *)
  257. BuildResWord(rwimplementation, 'IMPLEMENTATION ', syimplementation);
  258. firstrw[15] := rwx; (* length: 15 FOR table sentinel *)
  259. (* constants for lexical scan *)
  260. FOR i := ordminchar TO ordmaxchar DO
  261. charclass[CHR(i)] := illegal;
  262. END;
  263. FOR ch := 'a' TO 'z' DO
  264. charclass[ch] := letter;
  265. charclass[CAP(ch)] := letter;
  266. END;
  267. FOR ch := '0' TO '9' DO
  268. charclass[ch] := digit;
  269. END;
  270. charclass[' '] := special;
  271. charclass['"'] := chapostrophe;
  272. charclass['#'] := special;
  273. charclass['&'] := special;
  274. charclass["'"] := chapostrophe;
  275. charclass['('] := chleftparen;
  276. charclass[')'] := chrightparen;
  277. charclass['*'] := special;
  278. charclass['+'] := special;
  279. charclass[','] := special;
  280. charclass['-'] := special;
  281. charclass['.'] := chperiod;
  282. charclass['/'] := special;
  283. charclass[':'] := chcolon;
  284. charclass[';'] := chsemicolon;
  285. charclass['<'] := chlessthan;
  286. charclass['='] := special;
  287. charclass['>'] := chgreaterthan;
  288. charclass['@'] := special;
  289. charclass['['] := special;
  290. charclass[']'] := special;
  291. charclass['^'] := special;
  292. charclass['{'] := special;
  293. charclass['|'] := chbar;
  294. charclass['}'] := special;
  295. symbolclass[illegal] := othersym;
  296. symbolclass[special] := othersym;
  297. symbolclass[chapostrophe] := otherword;
  298. symbolclass[chleftparen] := leftparen;
  299. symbolclass[chrightparen] := rightparen;
  300. symbolclass[chperiod] := period;
  301. symbolclass[digit] := intconst;
  302. symbolclass[chcolon] := colon;
  303. symbolclass[chsemicolon] := semicolon;
  304. symbolclass[chlessthan] := othersym;
  305. symbolclass[chgreaterthan] := othersym;
  306. symbolclass[chbar] := bar;
  307. symbolclass[letter] := ident;
  308. END StructConsts;
  309. (* FlushLine/WriteError/ReadLine convert between files and lines. *)
  310. PROCEDURE FlushLine;
  311. (* Write buffer into output file *)
  312. VAR
  313. i, j, vircol : outrange; (* loop index *)
  314. nonblankseen : BOOLEAN;
  315. BEGIN
  316. WITH outline DO
  317. WHILE blanklns > 0 DO
  318. WriteLn;
  319. blanklns := blanklns - 1;
  320. END;
  321. IF len > 0 THEN
  322. vircol := 0;
  323. nonblankseen := FALSE;
  324. (* set this to TRUE if you don't want
  325. blanks to tab conversion *)
  326. FOR i := 0 TO len - 1 DO
  327. IF buf[i+1] <> ' ' THEN
  328. IF NOT nonblankseen THEN
  329. LOOP
  330. j := (vircol DIV
  331. tabinterval + 1) *
  332. tabinterval;
  333. IF j > i THEN
  334. EXIT;
  335. END;
  336. Write(TAB);
  337. vircol := j;
  338. END;
  339. END;
  340. nonblankseen := TRUE;
  341. WHILE vircol < i DO
  342. Write(' ');
  343. vircol := vircol + 1;
  344. END;
  345. Write(buf[i+1]);
  346. vircol := i + 1;
  347. END;
  348. END;
  349. WriteLn;
  350. len := 0;
  351. END;
  352. END;
  353. END FlushLine;
  354. PROCEDURE WriteError(error : errortype; nm : ARRAY OF CHAR);
  355. (* report error to output *)
  356. VAR
  357. i, ix : inrange; (* loop index, limit *)
  358. BEGIN
  359. FlushLine;
  360. WriteString('(* !!! error, ');
  361. WriteString(nm);
  362. CASE error OF
  363. longline:
  364. WriteString('shorter line');
  365. | noendcomm:
  366. WriteString('END OF comment');
  367. | notquote:
  368. WriteString("final ' on line");
  369. | longword:
  370. WriteString('shorter word');
  371. | notdo:
  372. WriteString('"DO"');
  373. | notof:
  374. WriteString('"OF"');
  375. | notend:
  376. WriteString('"END"');
  377. | notthen:
  378. WriteString('"THEN"');
  379. | notbegin:
  380. WriteString('"BEGIN"');
  381. | notuntil:
  382. WriteString('"UNTIL"');
  383. | notident:
  384. WriteString('"identifier"');
  385. | notsemicolon:
  386. WriteString('";"');
  387. | notperiod:
  388. WriteString('"."');
  389. | notcolon:
  390. WriteString('":"');
  391. | notparen:
  392. WriteString('")"');
  393. | noeof:
  394. WriteString('END OF file');
  395. END;
  396. WriteString(' expected');
  397. IF error >= longword THEN
  398. WriteString(', NOT "');
  399. WITH inline DO
  400. WITH curword DO
  401. IF size > maxrwlen THEN
  402. ix := maxrwlen
  403. ELSE
  404. ix := size;
  405. END;
  406. FOR i := 1 TO ix DO
  407. Write(buf[base + i]);
  408. END;
  409. END;
  410. END;
  411. Write('"');
  412. END;
  413. IF error = noeof THEN
  414. WriteString(', FORMATTING STOPS');
  415. END;
  416. WriteString(' !!! *)');
  417. WriteLn;
  418. END WriteError;
  419. PROCEDURE ReadLine;
  420. (* Read line into input buffer *)
  421. VAR
  422. c : CHAR; (* input character *)
  423. BEGIN
  424. WITH inline DO
  425. len := 0;
  426. LOOP
  427. Read(c);
  428. IF NOT Done THEN
  429. endoffile := TRUE;
  430. EXIT;
  431. END;
  432. IF c = NEWLINE THEN
  433. EXIT;
  434. END;
  435. IF c < ' ' THEN (* convert ISO control chars (except
  436. leading form feed) to spaces *)
  437. IF c = TAB THEN
  438. (* ISO TAB char *)
  439. c := ' ';
  440. (* add last space at end *)
  441. WHILE len MOD 8 <> 7 DO
  442. len := len + 1;
  443. IF len < maxinlen THEN
  444. buf[len] := c;
  445. END;
  446. END;
  447. (* END tab handling *)
  448. ELSIF (c <> FF) OR (len > 0) THEN
  449. c := ' ';
  450. END;
  451. END; (* END ISO control char conversion *)
  452. len := len + 1;
  453. IF len < maxinlen THEN
  454. buf[len] := c;
  455. END;
  456. END;
  457. IF NOT endoffile THEN
  458. IF len >= maxinlen THEN
  459. (* input line too long *)
  460. WriteError(longline, "(ReadLine), ");
  461. len := maxinlen - 1;
  462. END;
  463. WHILE (len > 0) AND (buf[len] = ' ') DO
  464. len := len - 1;
  465. END;
  466. END;
  467. len := len + 1; (* add exactly ONE trailing blank *)
  468. buf[len] := ' ';
  469. index := 0;
  470. END;
  471. END ReadLine;
  472. PROCEDURE GetChar;
  473. (* get next char from input buffer *)
  474. BEGIN
  475. WITH inline DO
  476. index := index + 1;
  477. ch := buf[index];
  478. END;
  479. END GetChar;
  480. PROCEDURE NextChar() : CHAR;
  481. (* look at next char in input buffer *)
  482. BEGIN
  483. RETURN inline.buf[inline.index + 1];
  484. END NextChar;
  485. PROCEDURE StartWord(startclass : firstclass);
  486. (* note beginning of word, and count preceding lines and spaces *)
  487. VAR
  488. first : BOOLEAN; (* is word the first on input line? *)
  489. BEGIN
  490. first := FALSE;
  491. WITH inline DO
  492. WITH curword DO
  493. whenfirst := startclass;
  494. blanklncount := 0;
  495. WHILE (index >= len) AND NOT endoffile DO
  496. IF len = 1 THEN
  497. blanklncount := blanklncount + 1;
  498. END;
  499. IF startclass = contuncomm THEN
  500. FlushLine
  501. ELSE
  502. first := TRUE;
  503. END;
  504. ReadLine;
  505. (* with exactly ONE trailing blank *)
  506. GetChar;
  507. IF ch = FF THEN
  508. FlushLine;
  509. Write(FF);
  510. blanklncount := 0;
  511. GetChar;
  512. END;
  513. END;
  514. spaces := 0; (* count leading spaces *)
  515. IF NOT endoffile THEN
  516. WHILE ch = ' ' DO
  517. spaces := spaces + 1;
  518. GetChar;
  519. END;
  520. END;
  521. IF first THEN
  522. spaces := 1;
  523. END;
  524. base := index - 1;
  525. END;
  526. END;
  527. END StartWord;
  528. PROCEDURE FinishWord;
  529. (* note end of word *)
  530. BEGIN
  531. WITH inline DO
  532. WITH curword DO
  533. puncfollows := (symbol IN datawords) AND (ch <> ' ');
  534. size := index - base - 1;
  535. END;
  536. END;
  537. END FinishWord;
  538. PROCEDURE CopyWord(newline : BOOLEAN; pword : wordtype);
  539. (* copy word from input buffer into output buffer *)
  540. VAR
  541. i : INTEGER; (* outline.len excess, loop index *)
  542. BEGIN
  543. WITH pword DO
  544. WITH outline DO
  545. i := maxoutlen - len - spaces - size;
  546. IF newline OR (i < 0) OR ((i = 0) AND puncfollows) THEN
  547. FlushLine;
  548. END;
  549. IF len = 0 THEN (* first word on output line *)
  550. blanklns := blanklncount;
  551. CASE whenfirst OF
  552. (* update LOCAL word.spaces *)
  553. newclause:
  554. spaces := margin;
  555. | continue:
  556. spaces := margin;
  557. | alcomm:
  558. spaces := alcommbase;
  559. | contalcomm:
  560. spaces := alcommbase + commindent;
  561. | uncomm:
  562. spaces := base;
  563. | contuncomm:
  564. (* spaces := spaces *);
  565. END;
  566. IF spaces + size > maxoutlen THEN
  567. spaces := maxoutlen - size;
  568. (* reduce spaces *)
  569. IF spaces < 0 THEN
  570. WriteError(longword,
  571. "(CopyWord), ");
  572. size := maxoutlen;
  573. spaces := 0;
  574. END;
  575. END;
  576. END;
  577. FOR i := 1 TO spaces DO
  578. (* put out spaces *)
  579. len := len + 1;
  580. buf[len] := ' ';
  581. END;
  582. FOR i := 1 TO size DO
  583. (* copy actual word *)
  584. len := len + 1;
  585. buf[len] := inline.buf[base + i];
  586. END;
  587. END;
  588. END;
  589. END CopyWord;
  590. PROCEDURE DoComment; (* copy aligned or unaligned comment *)
  591. PROCEDURE CopyComment(commclass : firstclass; commbase : inrange);
  592. (* copy words of comment *)
  593. VAR
  594. endcomment : BOOLEAN; (* end of comment? *)
  595. BEGIN
  596. WITH curword DO (* copy comment begin symbol *)
  597. whenfirst := commclass;
  598. spaces := commbase - outline.len;
  599. CopyWord((spaces < 0) OR (blanklncount > 0), curword);
  600. END;
  601. commclass := VAL(firstclass, ORD(commclass)+1);
  602. WITH inline DO
  603. REPEAT (* loop for successive words *)
  604. StartWord(commclass);
  605. endcomment := endoffile;
  606. (* premature end? *)
  607. IF endcomment THEN
  608. WriteError(noendcomm, "(CopyComment), ")
  609. ELSE
  610. REPEAT
  611. IF ch = '*' THEN
  612. GetChar;
  613. IF ch = ')' THEN
  614. endcomment := TRUE;
  615. GetChar;
  616. END;
  617. ELSE
  618. GetChar;
  619. END;
  620. UNTIL (ch = ' ') OR endcomment;
  621. END;
  622. FinishWord;
  623. CopyWord(FALSE, curword)
  624. UNTIL endcomment;
  625. END;
  626. END CopyComment;
  627. BEGIN (* DoComment *)
  628. IF curword.base < commthresh THEN
  629. (* copy comment without alignment *)
  630. CopyComment(uncomm, curword.base)
  631. ELSE (* align AND format comment *)
  632. CopyComment(alcomm, alcommbase);
  633. END;
  634. END DoComment;
  635. PROCEDURE GetSymbol;
  636. (* get next non-comment symbol *)
  637. PROCEDURE CopySymbol(symbol : symboltype; pword : wordtype);
  638. (* copy word(s) of symbol *)
  639. BEGIN
  640. IF symbol = comment THEN
  641. DoComment; (* NOTE: DoComment uses global word! *)
  642. lnpending := TRUE;
  643. ELSIF symbol = semicolon THEN
  644. CopyWord(FALSE, pword);
  645. lnpending := NOT inheader;
  646. ELSE
  647. CopyWord(lnpending, pword);
  648. lnpending := FALSE;
  649. END;
  650. END CopySymbol;
  651. PROCEDURE FindSymbol;
  652. (* find next symbol in input buffer *)
  653. VAR
  654. termch : CHAR; (* string terminator *)
  655. chclass : chartype; (* classification of leading char *)
  656. PROCEDURE CheckResWord;
  657. (* check if current identifier is reserved word/symbol *)
  658. VAR
  659. rw, rwbeyond : resword; (* loop index, limit *)
  660. symword : rwstring; (* copy of symbol word *)
  661. i : [-1..maxrwlen]; (* loop index *)
  662. BEGIN
  663. WITH curword DO
  664. WITH inline DO
  665. size := index - base - 1;
  666. IF size < maxrwlen THEN
  667. symword := ' ';
  668. FOR i := 1 TO size DO
  669. symword[i] := CAP(buf[ base + i]);
  670. END;
  671. rw := firstrw[size];
  672. rwbeyond := firstrw[size + 1];
  673. symbol := semicolon;
  674. REPEAT
  675. IF rw >= rwbeyond THEN
  676. symbol := ident
  677. ELSIF StrCmp(symword, rwword[rw]) THEN
  678. symbol := rwsy[rw]
  679. ELSE
  680. rw := VAL(resword,ORD(rw)+1);
  681. END;
  682. UNTIL symbol <> semicolon;
  683. END;
  684. whenfirst := newclause;
  685. END;
  686. END;
  687. END CheckResWord;
  688. PROCEDURE GetName;
  689. BEGIN
  690. WHILE charclass[inline.ch] IN chartypeset{letter, digit} DO
  691. GetChar;
  692. END;
  693. CheckResWord;
  694. END GetName;
  695. PROCEDURE GetNumber;
  696. BEGIN
  697. WITH inline DO
  698. WHILE charclass[ch] = digit DO
  699. GetChar;
  700. END;
  701. IF ch = '.' THEN
  702. IF charclass[NextChar()] = digit THEN
  703. (* NOTE: NextChar is a function! *)
  704. symbol := otherword;
  705. GetChar;
  706. WHILE charclass[ch] = digit DO
  707. GetChar;
  708. END;
  709. END;
  710. END;
  711. IF CAP(ch) = 'E' THEN
  712. symbol := otherword;
  713. GetChar;
  714. IF (ch = '+') OR (ch = '-') THEN
  715. GetChar;
  716. END;
  717. WHILE charclass[ch] = digit DO
  718. GetChar;
  719. END;
  720. END;
  721. END;
  722. END GetNumber;
  723. PROCEDURE GetStringLiteral;
  724. VAR
  725. endstring : BOOLEAN; (* end of string literal? *)
  726. BEGIN
  727. WITH inline DO
  728. endstring := FALSE;
  729. REPEAT
  730. GetChar;
  731. IF ch = termch THEN
  732. endstring := TRUE;
  733. ELSIF index >= len THEN
  734. (* error, final "'" not on line *)
  735. WriteError(notquote, "(GetStringLiteral), ");
  736. symbol := syeof;
  737. endstring := TRUE;
  738. END;
  739. UNTIL endstring;
  740. GetChar;
  741. END;
  742. END GetStringLiteral;
  743. BEGIN (* FindSymbol *)
  744. StartWord(continue);
  745. WITH inline DO
  746. IF endoffile THEN
  747. symbol := syeof
  748. ELSE
  749. termch := ch; (* save for string literal routine *)
  750. chclass := charclass[ch];
  751. symbol := symbolclass[chclass];
  752. GetChar; (* second CHAR *)
  753. CASE chclass OF
  754. chsemicolon, chrightparen, chleftbrace, special,
  755. illegal: ;
  756. | letter:
  757. GetName;
  758. | digit:
  759. GetNumber;
  760. | chapostrophe:
  761. GetStringLiteral;
  762. | chcolon:
  763. IF ch = '=' THEN
  764. symbol := othersym;
  765. GetChar;
  766. END;
  767. | chlessthan:
  768. IF (ch = '=') OR (ch = '>') THEN
  769. GetChar;
  770. END;
  771. | chgreaterthan:
  772. IF ch = '=' THEN
  773. GetChar;
  774. END;
  775. | chleftparen:
  776. IF ch = '*' THEN
  777. symbol := comment;
  778. GetChar;
  779. END;
  780. | chperiod:
  781. IF ch = '.' THEN
  782. symbol := colon;
  783. GetChar;
  784. END; (* Added by me (CJ): *)
  785. ELSE
  786. END;
  787. FinishWord;
  788. END;
  789. END; (* FindSymbol *)
  790. END FindSymbol;
  791. BEGIN (* GetSymbol *)
  792. REPEAT
  793. CopySymbol(symbol, curword);
  794. (* copy word for symbol to output *)
  795. FindSymbol (* get next symbol *)
  796. UNTIL symbol <> comment;
  797. END GetSymbol;
  798. PROCEDURE StartClause;
  799. (* (this may be a simple clause, or the start of a header) *)
  800. BEGIN
  801. curword.whenfirst := newclause;
  802. lnpending := TRUE;
  803. END StartClause;
  804. PROCEDURE PassSemicolons;
  805. (* pass consecutive semicolons *)
  806. BEGIN
  807. WHILE symbol = semicolon DO
  808. GetSymbol;
  809. StartClause;
  810. END;
  811. END PassSemicolons;
  812. PROCEDURE StartBody;
  813. (* finish header, start body of structure *)
  814. BEGIN
  815. StartClause;
  816. margin := margin + indent;
  817. END StartBody;
  818. PROCEDURE FinishBody;
  819. (* retract margin *)
  820. BEGIN
  821. margin := margin - indent;
  822. END FinishBody;
  823. PROCEDURE PassPhrase(finalsymbol : symboltype);
  824. (* process symbols until significant symbol encountered *)
  825. VAR
  826. endsyms : symbolset; (* complete set of stopping symbols *)
  827. BEGIN
  828. IF symbol <> syeof THEN
  829. endsyms := stopsyms;
  830. INCL(endsyms, finalsymbol);
  831. REPEAT
  832. GetSymbol
  833. UNTIL symbol IN endsyms;
  834. END;
  835. END PassPhrase;
  836. PROCEDURE Expect(expectedsym : symboltype; error : errortype; syms : symbolset;
  837. nm : ARRAY OF CHAR);
  838. (* fail if current symbol is not the expected one, then recover *)
  839. BEGIN
  840. IF symbol = expectedsym THEN
  841. GetSymbol
  842. ELSE
  843. WriteError(error, nm);
  844. INCL(syms, expectedsym);
  845. WHILE NOT (symbol IN syms) DO
  846. GetSymbol;
  847. END;
  848. IF symbol = expectedsym THEN
  849. GetSymbol;
  850. END;
  851. END;
  852. END Expect;
  853. PROCEDURE Heading;
  854. (* process heading for program or procedure *)
  855. PROCEDURE MatchParens; (* process parentheses in heading *)
  856. VAR
  857. endsyms : symbolset;
  858. BEGIN
  859. GetSymbol;
  860. WHILE NOT (symbol IN recendsyms) DO
  861. IF symbol = leftparen THEN
  862. MatchParens
  863. ELSE
  864. GetSymbol;
  865. END;
  866. END;
  867. endsyms := stopsyms + recendsyms;
  868. Expect(rightparen, notparen, endsyms, "(MatchParens), ");
  869. END MatchParens;
  870. BEGIN (* heading *)
  871. GetSymbol;
  872. PassPhrase(leftparen);
  873. IF symbol = leftparen THEN
  874. inheader := TRUE;
  875. MatchParens;
  876. inheader := FALSE;
  877. END;
  878. IF symbol = colon THEN
  879. PassPhrase(semicolon);
  880. END;
  881. Expect(semicolon, notsemicolon, stopsyms, "(Heading), ");
  882. END Heading;
  883. PROCEDURE DoRecord;
  884. (* process record declaration *)
  885. BEGIN
  886. GetSymbol;
  887. StartBody;
  888. PassFields(FALSE);
  889. FinishBody;
  890. Expect(syend, notend, recendsyms, "(DoRecord), ");
  891. END DoRecord;
  892. PROCEDURE DoVariant;
  893. (* process (case) variant part *)
  894. BEGIN
  895. PassPhrase(syof);
  896. Expect(syof, notof, stopsyms, "(Dovariant), ");
  897. StartBody;
  898. PassFields(TRUE);
  899. FinishBody;
  900. END DoVariant;
  901. PROCEDURE DoParens(forvariant : BOOLEAN);
  902. (* process parentheses in record *)
  903. BEGIN
  904. GetSymbol;
  905. IF forvariant THEN
  906. StartBody;
  907. END;
  908. PassFields(FALSE);
  909. lnpending := FALSE; (* for empty field list *)
  910. Expect(rightparen, notparen, recendsyms, "(DoParens), ");
  911. IF forvariant THEN
  912. FinishBody;
  913. END;
  914. END DoParens;
  915. PROCEDURE PassFields(forvariant : BOOLEAN);
  916. (* process declarations *)
  917. BEGIN
  918. WHILE NOT (symbol IN recendsyms) DO
  919. IF symbol = semicolon THEN
  920. PassSemicolons
  921. ELSIF symbol = syrecord THEN
  922. DoRecord
  923. ELSIF symbol = sycase THEN
  924. DoVariant
  925. ELSIF symbol = leftparen THEN
  926. DoParens(forvariant)
  927. ELSE
  928. GetSymbol;
  929. END;
  930. END;
  931. END PassFields;
  932. PROCEDURE Statement;
  933. (* process statement *)
  934. BEGIN
  935. CASE symbol OF
  936. sycase:
  937. CaseStatement;
  938. Expect(syend, notend, stmtendsyms, "(Case), ");
  939. | syif:
  940. IfStatement;
  941. Expect(syend, notend, stmtendsyms, "(If), ");
  942. | syloop:
  943. LoopStatement;
  944. Expect(syend, notend, stmtendsyms, "(Loop), ");
  945. | syrepeat:
  946. RepeatStatement;
  947. | forwhilewith:
  948. ForWhileWithStatement;
  949. Expect(syend, notend, stmtendsyms, "(ForWhileWith), ");
  950. | ident:
  951. AssignmentProccall;
  952. | semicolon: ; (*!!! Added by me (CJ) *)
  953. ELSE ;
  954. END;
  955. END Statement;
  956. PROCEDURE AssignmentProccall;
  957. (* pass an assignment statement or procedure call *)
  958. BEGIN
  959. WHILE NOT (symbol IN stmtendsyms) DO
  960. GetSymbol;
  961. END;
  962. END AssignmentProccall;
  963. PROCEDURE StatementSequence;
  964. (* process sequence of statements *)
  965. BEGIN
  966. Statement;
  967. LOOP
  968. IF symbol <> semicolon THEN
  969. EXIT;
  970. END;
  971. GetSymbol;
  972. Statement;
  973. END;
  974. END StatementSequence;
  975. PROCEDURE IfStatement;
  976. (* process if statement *)
  977. BEGIN
  978. PassPhrase(sythen);
  979. Expect(sythen, notthen, stopsyms, "(Ifstatement), ");
  980. StartBody;
  981. StatementSequence;
  982. FinishBody;
  983. WHILE symbol = syelsif DO
  984. StartClause;
  985. PassPhrase(sythen);
  986. Expect(sythen, notthen, stopsyms, "(Elseif), ");
  987. StartBody; (* new line after 'THEN' *)
  988. StatementSequence;
  989. FinishBody;
  990. END;
  991. IF symbol = syelse THEN
  992. StartClause;
  993. GetSymbol;
  994. StartBody; (* new line after 'ELSE' *)
  995. StatementSequence;
  996. FinishBody;
  997. END;
  998. END IfStatement;
  999. PROCEDURE CaseStatement;
  1000. (* process case statement *)
  1001. BEGIN
  1002. PassPhrase(syof);
  1003. Expect(syof, notof, stopsyms, "(caseStatement), ");
  1004. StartClause;
  1005. OneCase;
  1006. WHILE symbol = bar DO
  1007. GetSymbol;
  1008. OneCase;
  1009. END;
  1010. IF symbol = syelse THEN
  1011. GetSymbol;
  1012. StartBody;
  1013. StatementSequence;
  1014. FinishBody;
  1015. END;
  1016. END CaseStatement;
  1017. PROCEDURE OneCase;
  1018. (* process one case clause *)
  1019. BEGIN
  1020. IF NOT (symbol IN symbolset{bar, syelse}) THEN
  1021. PassPhrase(colon);
  1022. Expect(colon, notcolon, stopsyms, "(OneCase), ");
  1023. StartBody; (* new line, indent after colon *)
  1024. StatementSequence;
  1025. FinishBody; (* left-indent after case *)
  1026. END;
  1027. END OneCase;
  1028. PROCEDURE RepeatStatement;
  1029. (* process repeat statement *)
  1030. BEGIN
  1031. GetSymbol;
  1032. StartBody; (* new line, indent after 'REPEAT' *)
  1033. StatementSequence;
  1034. FinishBody; (* left-ident after UNTIL *)
  1035. StartClause; (* new line before UNTIL *)
  1036. Expect(syuntil, notuntil, stmtendsyms, "(repeatstatement), ");
  1037. PassPhrase(semicolon);
  1038. END RepeatStatement;
  1039. PROCEDURE LoopStatement;
  1040. (* process loop statement *)
  1041. BEGIN
  1042. GetSymbol;
  1043. StartBody; (* new line, indent after LOOP *)
  1044. StatementSequence;
  1045. FinishBody; (* left-ident before END *)
  1046. END LoopStatement;
  1047. PROCEDURE ForWhileWithStatement;
  1048. (* process for, while, or with statement *)
  1049. BEGIN
  1050. PassPhrase(sydo);
  1051. Expect(sydo, notdo, stopsyms, "(ForWhileWithstatement), ");
  1052. StartBody;
  1053. StatementSequence;
  1054. FinishBody;
  1055. END ForWhileWithStatement;
  1056. PROCEDURE ProcedureDeclaration;
  1057. (* pass a procedure declaration *)
  1058. BEGIN
  1059. ProcedureHeading;
  1060. Block;
  1061. Expect(ident, notident, stmtendsyms, "(Proceduredeclaration)1, ");
  1062. Expect(semicolon, notsemicolon, stmtendsyms,
  1063. "(Proceduredeclaration)2, ");
  1064. END ProcedureDeclaration;
  1065. PROCEDURE ProcedureHeading;
  1066. BEGIN
  1067. StartClause;
  1068. Heading;
  1069. END ProcedureHeading;
  1070. PROCEDURE Block;
  1071. BEGIN
  1072. WHILE symbol IN symbolset{declarator, symodule, syproc} DO
  1073. Declaration;
  1074. END;
  1075. IF symbol = sybegin THEN
  1076. GetSymbol;
  1077. StartBody;
  1078. StatementSequence;
  1079. FinishBody;
  1080. END;
  1081. Expect(syend, notend, stmtendsyms, "(Block), ");
  1082. END Block;
  1083. PROCEDURE Declaration;
  1084. BEGIN
  1085. IF symbol = declarator THEN
  1086. StartClause; (* CONST, TYPE, VAR *)
  1087. GetSymbol;
  1088. StartBody;
  1089. REPEAT
  1090. PassPhrase(syrecord);
  1091. IF symbol = syrecord THEN
  1092. DoRecord;
  1093. END;
  1094. IF symbol = semicolon THEN
  1095. PassSemicolons;
  1096. END;
  1097. UNTIL symbol IN headersyms;
  1098. FinishBody;
  1099. ELSIF symbol = symodule THEN
  1100. ModuleDeclaration;
  1101. ELSIF symbol = syproc THEN
  1102. ProcedureDeclaration;
  1103. END;
  1104. END Declaration;
  1105. PROCEDURE ModuleDeclaration;
  1106. BEGIN
  1107. PassPhrase(semicolon);
  1108. PassSemicolons;
  1109. WHILE symbol IN symbolset{syimport, syexport, syfrom} DO
  1110. ImportExport;
  1111. END;
  1112. Block;
  1113. Expect(ident, notident, stmtendsyms, "(ModuleDeclaration), ");
  1114. END ModuleDeclaration;
  1115. PROCEDURE ImportExport;
  1116. BEGIN
  1117. IF symbol = syfrom THEN
  1118. PassPhrase(syimport);
  1119. END;
  1120. IF symbol = syimport THEN
  1121. GetSymbol;
  1122. ELSIF symbol = syexport THEN
  1123. GetSymbol;
  1124. IF symbol = syqual THEN
  1125. GetSymbol;
  1126. END;
  1127. END;
  1128. StartBody;
  1129. PassPhrase(semicolon);
  1130. FinishBody;
  1131. GetSymbol;
  1132. END ImportExport;
  1133. PROCEDURE OneDefinition;
  1134. BEGIN
  1135. IF symbol = declarator THEN
  1136. Declaration;
  1137. ELSIF symbol = syproc THEN
  1138. ProcedureHeading;
  1139. END;
  1140. END OneDefinition;
  1141. PROCEDURE DefinitionModule;
  1142. BEGIN
  1143. GetSymbol;
  1144. PassPhrase(semicolon);
  1145. GetSymbol;
  1146. WHILE symbol IN symbolset{syimport, syexport, syfrom} DO
  1147. ImportExport;
  1148. END;
  1149. WHILE symbol IN symbolset{declarator, syproc} DO
  1150. OneDefinition;
  1151. END;
  1152. Expect(syend, notend, stmtendsyms, "DefinitionModule1, " );
  1153. GetSymbol;
  1154. Expect(period, notperiod, stmtendsyms, 'DefintionModule2, ');
  1155. END DefinitionModule;
  1156. PROCEDURE ProgramModule;
  1157. BEGIN
  1158. ModuleDeclaration;
  1159. Expect(period, notperiod, stmtendsyms, "ProgramModule, ");
  1160. END ProgramModule;
  1161. PROCEDURE CompilationUnit;
  1162. BEGIN
  1163. IF symbol = syimplementation THEN
  1164. GetSymbol;
  1165. ProgramModule;
  1166. ELSIF symbol = sydefinition THEN
  1167. DefinitionModule;
  1168. ELSE
  1169. ProgramModule;
  1170. END;
  1171. END CompilationUnit;
  1172. PROCEDURE CopyRemainder;
  1173. (* copy remainder of input *)
  1174. BEGIN
  1175. WriteError(noeof, "(Copyremainder), ");
  1176. WITH inline DO
  1177. REPEAT
  1178. CopyWord(FALSE, curword);
  1179. StartWord(contuncomm);
  1180. IF NOT endoffile THEN
  1181. REPEAT
  1182. GetChar
  1183. UNTIL ch = ' ';
  1184. END;
  1185. FinishWord;
  1186. UNTIL endoffile;
  1187. END;
  1188. END CopyRemainder;
  1189. PROCEDURE Initialize;
  1190. (* initialize global variables *)
  1191. BEGIN
  1192. WITH inline DO
  1193. endoffile := FALSE;
  1194. ch := ' ';
  1195. index := 0;
  1196. len := 0;
  1197. END;
  1198. WITH outline DO
  1199. blanklns := 0;
  1200. len := 0;
  1201. END;
  1202. WITH curword DO
  1203. whenfirst := contuncomm;
  1204. puncfollows := FALSE;
  1205. blanklncount := 0;
  1206. spaces := 0;
  1207. base := 0;
  1208. size := 0;
  1209. END;
  1210. margin := initmargin;
  1211. lnpending := FALSE;
  1212. symbol := othersym;
  1213. inheader := FALSE;
  1214. END Initialize;
  1215. BEGIN
  1216. StructConsts;
  1217. Initialize;
  1218. (* Files may be opened here. *)
  1219. OpenInput("mod");
  1220. OpenOutput("mod");
  1221. GetSymbol;
  1222. CompilationUnit;
  1223. IF NOT inline.endoffile THEN
  1224. CopyRemainder;
  1225. END;
  1226. FlushLine;
  1227. CloseInput;
  1228. CloseOutput;
  1229. END Modula2PrettyPrinter.