plex.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. {
  2. TP Lex - A lexical analyzer generator for Turbo Pascal
  3. Copyright (c) 1990-92 Albert Graef <[email protected]>
  4. Copyright (C) 1996 Berend de Boer <[email protected]>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. $Revision: 1.4 $
  17. $Modtime: 96-08-01 10:22 $
  18. $History: LEX.PAS $
  19. *
  20. * ***************** Version 2 *****************
  21. * User: Berend Date: 96-10-10 Time: 21:16
  22. * Updated in $/Lex and Yacc/tply
  23. * Updated for protected mode, windows and Delphi 1.X and 2.X.
  24. ------------------------- Synopsis ------------------------
  25. Synopsis lex [options] lex-file[.l] [output-file[.pas]]
  26. Options
  27. -v "Verbose:" Lex generates a readable description of the generated
  28. lexical analyzer, written to lex-file with new extension .LST.
  29. -o "Optimize:" Lex optimizes DFA tables to produce a minimal DFA
  30. Description
  31. This is a reimplementation of the popular UNIX lexical analyzer generator
  32. Lex for MS-DOS and Turbo Pascal.
  33. Differences from UNIX Lex:
  34. - Produces output code for Turbo Pascal, rather than for C.
  35. - Character tables (%T) are not supported; neither are any directives
  36. to determine internal table sizes (%p, %n, etc.).
  37. ------------------------- Synopsis ------------------------
  38. }
  39. {$I-}
  40. program Lex;
  41. uses
  42. LexBase, LexTable, LexPos, LexDFA, LexOpt, LexList, LexRules, LexMsgs, SysUtils;
  43. procedure get_line;
  44. (* obtain line from source file *)
  45. begin
  46. readln(yyin, line);
  47. inc(lno);
  48. end(*get_line*);
  49. procedure next_section;
  50. (* find next section mark (%%) in code template *)
  51. var line : String;
  52. begin
  53. while not eof(yycod) do
  54. begin
  55. readln(yycod, line);
  56. if line='%%' then exit;
  57. writeln(yyout, line);
  58. end;
  59. end(*next_section*);
  60. (* Semantic routines: *)
  61. var n_rules : Integer; (* current number of rules *)
  62. procedure define_start_state ( symbol : String; pos : Integer; excl : Boolean );
  63. (* process start state definition *)
  64. begin
  65. {$ifdef fpc}
  66. with sym_table^[key(symbol, max_keys, @lookup, @entry)] do
  67. {$else}
  68. with sym_table^[key(symbol, max_keys, lookup, entry)] do
  69. {$endif}
  70. if sym_type=none_sym then
  71. begin
  72. inc(n_start_states);
  73. if n_start_states>max_start_states then
  74. fatal(state_table_overflow);
  75. sym_type := start_state_sym;
  76. start_state := n_start_states;
  77. writeln(yyout, 'const ', symbol, ' = ', 2*start_state, ';');
  78. first_pos_table^[2*start_state] := newIntSet;
  79. first_pos_table^[2*start_state+1] := newIntSet;
  80. start_excl^[start_state] := excl;
  81. end
  82. else
  83. error(symbol_already_defined, pos)
  84. end(*define_start_state*);
  85. procedure define_macro ( symbol, replacement : String );
  86. (* process macro definition *)
  87. begin
  88. {$ifdef fpc}
  89. with sym_table^[key('{'+symbol+'}', max_keys, @lookup, @entry)] do
  90. {$else}
  91. with sym_table^[key('{'+symbol+'}', max_keys, lookup, entry)] do
  92. {$endif}
  93. if sym_type=none_sym then
  94. begin
  95. sym_type := macro_sym;
  96. subst := newStr(strip(replacement));
  97. end
  98. else
  99. error(symbol_already_defined, 1)
  100. end(*define_macro*);
  101. procedure add_rule;
  102. (* process rule *)
  103. var i : Integer;
  104. FIRST : IntSet;
  105. begin
  106. addExpr(r, FIRST);
  107. if n_st=0 then
  108. if cf then
  109. setunion(first_pos_table^[1]^, FIRST)
  110. else
  111. begin
  112. setunion(first_pos_table^[0]^, FIRST);
  113. setunion(first_pos_table^[1]^, FIRST);
  114. end
  115. else
  116. if cf then
  117. for i := 1 to n_st do
  118. setunion(first_pos_table^[2*st[i]+1]^, FIRST)
  119. else
  120. for i := 1 to n_st do
  121. begin
  122. setunion(first_pos_table^[2*st[i]]^, FIRST);
  123. setunion(first_pos_table^[2*st[i]+1]^, FIRST);
  124. end
  125. end(*add_rule*);
  126. procedure generate_table;
  127. (* write the DFA table to the output file
  128. Tables are represented as a collection of typed array constants:
  129. type YYTRec = record
  130. cc : set of Char; { characters }
  131. s : Integer; { next state }
  132. end;
  133. const
  134. { table sizes: }
  135. yynmarks = ...;
  136. yynmatches = ...;
  137. yyntrans = ...;
  138. yynstates = ...;
  139. { rules of mark positions for each state: }
  140. yyk : array [1..yynmarks] of Integer = ...;
  141. { rules of matches for each state: }
  142. yym : array [1..yynmatches] of Integer = ...;
  143. { transition table: }
  144. yyt : array [1..yyntrans] of YYTRec = ...;
  145. { offsets into the marks, matches and transition tables: }
  146. yykl, yykh,
  147. yyml, yymh,
  148. yytl, yyth : array [0..yynstates-1] of Integer = ...;
  149. *)
  150. var yynmarks, yynmatches, yyntrans, yynstates : Integer;
  151. yykl, yykh,
  152. yyml, yymh,
  153. yytl, yyth : array [0..max_states-1] of Integer;
  154. procedure counters;
  155. (* compute counters and offsets *)
  156. var s, i : Integer;
  157. begin
  158. yynstates := n_states; yyntrans := n_trans;
  159. yynmarks := 0; yynmatches := 0;
  160. for s := 0 to n_states-1 do with state_table^[s] do
  161. begin
  162. yytl[s] := trans_lo; yyth[s] := trans_hi;
  163. yykl[s] := yynmarks+1; yyml[s] := yynmatches+1;
  164. for i := 1 to size(state_pos^) do
  165. with pos_table^[state_pos^[i]] do
  166. if pos_type=mark_pos then
  167. if pos=0 then
  168. inc(yynmatches)
  169. else if pos=1 then
  170. inc(yynmarks);
  171. yykh[s] := yynmarks; yymh[s] := yynmatches;
  172. end;
  173. end(*counters*);
  174. procedure writecc(var f : Text; cc : CClass);
  175. (* print the given character class *)
  176. function charStr(c : Char) : String;
  177. begin
  178. case c of
  179. #0..#31, (* nonprintable characters *)
  180. #127..#255 : charStr := '#'+intStr(ord(c));
  181. '''' : charStr := '''''''''';
  182. else charStr := ''''+c+'''';
  183. end;
  184. end(*charStr*);
  185. const
  186. MaxChar = #255;
  187. var
  188. c1, c2 : Char;
  189. col : Integer;
  190. tag : String;
  191. Quit: Boolean;
  192. begin
  193. write(f, '[ ');
  194. col := 0;
  195. c1 := chr(0);
  196. Quit := False;
  197. while not Quit do begin
  198. if c1 in cc then begin
  199. if col>0 then
  200. begin
  201. write(f, ',');
  202. inc(col);
  203. end;
  204. if col>40 then
  205. { insert line break }
  206. begin
  207. writeln(f);
  208. write(f, ' ':12);
  209. col := 0;
  210. end;
  211. c2 := c1;
  212. while (c2<MaxChar) and (succ(c2) in cc) do
  213. c2 := succ(c2);
  214. if c1=c2 then
  215. tag := charStr(c1)
  216. else if c2=succ(c1) then
  217. tag := charStr(c1)+','+charStr(c2)
  218. else
  219. tag := charStr(c1)+'..'+charStr(c2);
  220. write(f, tag);
  221. col := col + length(tag);
  222. c1 := c2;
  223. end;
  224. Quit := c1 = MaxChar;
  225. if not Quit then
  226. c1 := Succ(c1);
  227. end; { of while }
  228. write(f, ' ]');
  229. end(*writecc*);
  230. procedure tables;
  231. (* print tables *)
  232. var s, i, count : Integer;
  233. begin
  234. writeln(yyout);
  235. writeln(yyout, 'type YYTRec = record');
  236. writeln(yyout, ' cc : set of Char;');
  237. writeln(yyout, ' s : Integer;');
  238. writeln(yyout, ' end;');
  239. writeln(yyout);
  240. writeln(yyout, 'const');
  241. (* table sizes: *)
  242. writeln(yyout);
  243. writeln(yyout, 'yynmarks = ', yynmarks, ';');
  244. writeln(yyout, 'yynmatches = ', yynmatches, ';');
  245. writeln(yyout, 'yyntrans = ', yyntrans, ';');
  246. writeln(yyout, 'yynstates = ', yynstates, ';');
  247. (* mark table: *)
  248. writeln(yyout);
  249. writeln(yyout, 'yyk : array [1..yynmarks] of Integer = (');
  250. count := 0;
  251. for s := 0 to n_states-1 do with state_table^[s] do
  252. begin
  253. writeln(yyout, ' { ', s, ': }');
  254. for i := 1 to size(state_pos^) do
  255. with pos_table^[state_pos^[i]] do
  256. if (pos_type=mark_pos) and (pos=1) then
  257. begin
  258. write(yyout, ' ', rule); inc(count);
  259. if count<yynmarks then write(yyout, ',');
  260. writeln(yyout);
  261. end;
  262. end;
  263. writeln(yyout, ');');
  264. (* match table: *)
  265. writeln(yyout);
  266. writeln(yyout, 'yym : array [1..yynmatches] of Integer = (');
  267. count := 0;
  268. for s := 0 to n_states-1 do with state_table^[s] do
  269. begin
  270. writeln(yyout, '{ ', s, ': }');
  271. for i := 1 to size(state_pos^) do
  272. with pos_table^[state_pos^[i]] do
  273. if (pos_type=mark_pos) and (pos=0) then
  274. begin
  275. write(yyout, ' ', rule); inc(count);
  276. if count<yynmatches then write(yyout, ',');
  277. writeln(yyout);
  278. end;
  279. end;
  280. writeln(yyout, ');');
  281. (* transition table: *)
  282. writeln(yyout);
  283. writeln(yyout, 'yyt : array [1..yyntrans] of YYTrec = (');
  284. count := 0;
  285. for s := 0 to n_states-1 do with state_table^[s] do
  286. begin
  287. writeln(yyout, '{ ', s, ': }');
  288. for i := trans_lo to trans_hi do
  289. with trans_table^[i] do
  290. begin
  291. write(yyout, ' ( cc: ');
  292. writecc(yyout, cc^);
  293. write(yyout, '; s: ');
  294. write(yyout, next_state, ')');
  295. inc(count);
  296. if count<yyntrans then write(yyout, ',');
  297. writeln(yyout);
  298. end;
  299. end;
  300. writeln(yyout, ');');
  301. (* offset tables: *)
  302. writeln(yyout);
  303. writeln(yyout, 'yykl : array [0..yynstates-1] of Integer = (');
  304. for s := 0 to n_states-1 do
  305. begin
  306. write(yyout, '{ ', s, ': } ', yykl[s]);
  307. if s<n_states-1 then write(yyout, ',');
  308. writeln(yyout);
  309. end;
  310. writeln(yyout, ');');
  311. writeln(yyout);
  312. writeln(yyout, 'yykh : array [0..yynstates-1] of Integer = (');
  313. for s := 0 to n_states-1 do
  314. begin
  315. write(yyout, '{ ', s, ': } ', yykh[s]);
  316. if s<n_states-1 then write(yyout, ',');
  317. writeln(yyout);
  318. end;
  319. writeln(yyout, ');');
  320. writeln(yyout);
  321. writeln(yyout, 'yyml : array [0..yynstates-1] of Integer = (');
  322. for s := 0 to n_states-1 do
  323. begin
  324. write(yyout, '{ ', s, ': } ', yyml[s]);
  325. if s<n_states-1 then write(yyout, ',');
  326. writeln(yyout);
  327. end;
  328. writeln(yyout, ');');
  329. writeln(yyout);
  330. writeln(yyout, 'yymh : array [0..yynstates-1] of Integer = (');
  331. for s := 0 to n_states-1 do
  332. begin
  333. write(yyout, '{ ', s, ': } ', yymh[s]);
  334. if s<n_states-1 then write(yyout, ',');
  335. writeln(yyout);
  336. end;
  337. writeln(yyout, ');');
  338. writeln(yyout);
  339. writeln(yyout, 'yytl : array [0..yynstates-1] of Integer = (');
  340. for s := 0 to n_states-1 do
  341. begin
  342. write(yyout, '{ ', s, ': } ', yytl[s]);
  343. if s<n_states-1 then write(yyout, ',');
  344. writeln(yyout);
  345. end;
  346. writeln(yyout, ');');
  347. writeln(yyout);
  348. writeln(yyout, 'yyth : array [0..yynstates-1] of Integer = (');
  349. for s := 0 to n_states-1 do
  350. begin
  351. write(yyout, '{ ', s, ': } ', yyth[s]);
  352. if s<n_states-1 then write(yyout, ',');
  353. writeln(yyout);
  354. end;
  355. writeln(yyout, ');');
  356. writeln(yyout);
  357. end(*tables*);
  358. begin
  359. counters; tables;
  360. end(*generate_table*);
  361. (* Parser: *)
  362. const
  363. max_items = 255;
  364. var
  365. itemstr : String;
  366. itemc : Integer;
  367. itempos,
  368. itemlen : array [1..max_items] of Integer;
  369. procedure split ( str : String; count : Integer );
  370. (* split str into at most count whitespace-delimited items
  371. (result in itemstr, itemc, itempos, itemlen) *)
  372. procedure scan(var act_pos : Integer);
  373. (* scan one item *)
  374. var l : Integer;
  375. begin
  376. while (act_pos<=length(itemstr)) and
  377. ((itemstr[act_pos]=' ') or (itemstr[act_pos]=tab)) do
  378. inc(act_pos);
  379. l := 0;
  380. while (act_pos+l<=length(itemstr)) and
  381. (itemstr[act_pos+l]<>' ') and (itemstr[act_pos+l]<>tab) do
  382. inc(l);
  383. inc(itemc);
  384. itempos[itemc] := act_pos;
  385. itemlen[itemc] := l;
  386. inc(act_pos, l+1);
  387. while (act_pos<=length(itemstr)) and
  388. ((itemstr[act_pos]=' ') or (itemstr[act_pos]=tab)) do
  389. inc(act_pos);
  390. end(*scan*);
  391. var act_pos : Integer;
  392. begin
  393. itemstr := str; act_pos := 1;
  394. itemc := 0;
  395. while (itemc<count-1) and (act_pos<=length(itemstr)) do scan(act_pos);
  396. if act_pos<=length(itemstr) then
  397. begin
  398. inc(itemc);
  399. itempos[itemc] := act_pos;
  400. itemlen[itemc] := length(itemstr)-act_pos+1;
  401. end;
  402. end(*split*);
  403. function itemv ( i : Integer ) : String;
  404. (* return ith item in splitted string (whole string for i=0) *)
  405. begin
  406. if i=0 then
  407. itemv := itemstr
  408. else if (i<0) or (i>itemc) then
  409. itemv := ''
  410. else
  411. itemv := copy(itemstr, itempos[i], itemlen[i])
  412. end(*itemv*);
  413. procedure code;
  414. begin
  415. while not eof(yyin) do
  416. begin
  417. get_line;
  418. if line='%}' then
  419. exit
  420. else
  421. writeln(yyout, line);
  422. end;
  423. error(unmatched_lbrace, length(line)+1);
  424. end(*code*);
  425. procedure definitions;
  426. procedure definition;
  427. function check_id ( symbol : String ) : Boolean;
  428. var i : Integer;
  429. begin
  430. if (symbol='') or not (symbol[1] in letters) then
  431. check_id := false
  432. else
  433. begin
  434. for i := 2 to length(symbol) do
  435. if not (symbol[i] in alphanums) then
  436. begin
  437. check_id := false;
  438. exit;
  439. end;
  440. check_id := true
  441. end
  442. end(*check_id*);
  443. var i : Integer;
  444. com : String;
  445. begin
  446. split(line, 2);
  447. com := upper(itemv(1));
  448. if (com='%S') or (com='%START') or (com='%X') then
  449. begin
  450. split(line, max_items);
  451. for i := 2 to itemc do
  452. if check_id(itemv(i)) then
  453. define_start_state(itemv(i), itempos[i], com='%X')
  454. else
  455. error(syntax_error, itempos[i]);
  456. end
  457. else if check_id(itemv(1)) then
  458. define_macro(itemv(1), itemv(2))
  459. else
  460. error(syntax_error, 1);
  461. end(*definition*);
  462. begin
  463. while not eof(yyin) do
  464. begin
  465. get_line;
  466. if line='' then
  467. writeln(yyout)
  468. else if line='%%' then
  469. exit
  470. else if line='%{' then
  471. code
  472. else if (line[1]='%') or (line[1] in letters) then
  473. definition
  474. else
  475. writeln(yyout, line)
  476. end;
  477. end(*definitions*);
  478. procedure rules;
  479. begin
  480. next_section;
  481. if line='%%' then
  482. while not eof(yyin) do
  483. begin
  484. get_line;
  485. if line='' then
  486. writeln(yyout)
  487. else if line='%%' then
  488. begin
  489. next_section;
  490. exit;
  491. end
  492. else if line='%{' then
  493. code
  494. else if (line[1]<>' ') and (line[1]<>tab) then
  495. begin
  496. if n_rules=0 then next_section;
  497. inc(n_rules);
  498. parse_rule(n_rules);
  499. if errors=0 then
  500. begin
  501. add_rule;
  502. write(yyout, ' ', n_rules);
  503. if strip(stmt)='|' then
  504. writeln(yyout, ',')
  505. else
  506. begin
  507. writeln(yyout, ':');
  508. writeln(yyout, blankStr(expr), stmt);
  509. end;
  510. end
  511. end
  512. else
  513. writeln(yyout, line)
  514. end
  515. else
  516. error(unexpected_eof, length(line)+1);
  517. next_section;
  518. end(*rules*);
  519. procedure auxiliary_procs;
  520. begin
  521. if line='%%' then
  522. begin
  523. writeln(yyout);
  524. while not eof(yyin) do
  525. begin
  526. get_line;
  527. writeln(yyout, line);
  528. end;
  529. end;
  530. end(*auxiliary_procs*);
  531. (* Main program: *)
  532. var i : Integer;
  533. begin
  534. {$ifdef Unix}
  535. codfilepath1:=path(paramstr(0));
  536. if (codfilepath1<>'') then
  537. codfilepath1:=codfilepath1+'../lib/fpc/lexyacc/'
  538. else
  539. codfilepath1:='/usr/local/lib/fpc/lexyacc/';
  540. codfilepath2:='/usr/lib/fpc/lexyacc/';
  541. {$else}
  542. codfilepath1:=path(paramstr(0));
  543. codfilepath2:='';
  544. {$endif}
  545. (* sign-on: *)
  546. writeln(sign_on);
  547. (* parse command line: *)
  548. if paramCount=0 then
  549. begin
  550. writeln(usage);
  551. writeln(options);
  552. halt(0);
  553. end;
  554. lfilename := '';
  555. pasfilename := '';
  556. for i := 1 to paramCount do
  557. if copy(paramStr(i), 1, 1)='-' then
  558. if upper(paramStr(i))='-V' then
  559. verbose := true
  560. else if upper(paramStr(i))='-O' then
  561. optimize := true
  562. else
  563. begin
  564. writeln(invalid_option, paramStr(i));
  565. halt(1);
  566. end
  567. else if lfilename='' then
  568. lfilename := addExt(paramStr(i), 'l')
  569. else if pasfilename='' then
  570. pasfilename := addExt(paramStr(i), 'pas')
  571. else
  572. begin
  573. writeln(illegal_no_args);
  574. halt(1);
  575. end;
  576. if lfilename='' then
  577. begin
  578. writeln(illegal_no_args);
  579. halt(1);
  580. end;
  581. if pasfilename='' then pasfilename := root(lfilename)+'.pas';
  582. lstfilename := root(lfilename)+'.lst';
  583. (* open files: *)
  584. assign(yyin, lfilename);
  585. assign(yyout, pasfilename);
  586. assign(yylst, lstfilename);
  587. reset(yyin); if ioresult<>0 then fatal(cannot_open_file+lfilename);
  588. rewrite(yyout); if ioresult<>0 then fatal(cannot_open_file+pasfilename);
  589. rewrite(yylst); if ioresult<>0 then fatal(cannot_open_file+lstfilename);
  590. (* search code template *)
  591. codfilename := 'yylex.cod';
  592. assign(yycod, codfilename);
  593. reset(yycod);
  594. if ioresult<>0 then
  595. begin
  596. codfilename := IncludeTrailingPathDelimiter(GetEnvironmentVariable('FPCDIR'))+'lexyacc'+DirectorySeparator+'yylex.cod';
  597. assign(yycod, codfilename);
  598. reset(yycod);
  599. if ioresult<>0 then
  600. begin
  601. codfilename := codfilepath1+'yylex.cod';
  602. assign(yycod, codfilename);
  603. reset(yycod);
  604. if (codfilepath2<>'') and (ioresult<>0) then
  605. begin
  606. codfilename := codfilepath2+'yylex.cod';
  607. assign(yycod, codfilename);
  608. reset(yycod);
  609. if ioresult<>0 then
  610. fatal(cannot_open_file+codfilename);
  611. end;
  612. end;
  613. end;
  614. (* parse source grammar: *)
  615. write('parse ... ');
  616. lno := 0; n_rules := 0; next_section;
  617. first_pos_table^[0] := newIntSet;
  618. first_pos_table^[1] := newIntSet;
  619. definitions;
  620. rules;
  621. if n_rules=0 then error(empty_grammar, length(line)+1);
  622. if errors=0 then
  623. begin
  624. (* generate DFA table and listings and write output code: *)
  625. write('DFA construction ... ');
  626. makeDFATable;
  627. if optimize then
  628. begin
  629. write('DFA optimization ... ');
  630. optimizeDFATable;
  631. end;
  632. write('code generation ... ');
  633. if verbose then listDFATable;
  634. generate_table; next_section;
  635. end;
  636. auxiliary_procs;
  637. if errors=0 then writeln('DONE');
  638. (* close files: *)
  639. close(yyin); close(yyout); close(yylst); close(yycod);
  640. (* print statistics: *)
  641. if errors>0 then
  642. writeln( lno, ' lines, ',
  643. errors, ' errors found.' )
  644. else
  645. writeln( lno, ' lines, ',
  646. n_rules, ' rules, ',
  647. n_pos, '/', max_pos, ' p, ',
  648. n_states, '/', max_states, ' s, ',
  649. n_trans, '/', max_trans, ' t.');
  650. if warnings>0 then writeln(warnings, ' warnings.');
  651. (* terminate: *)
  652. if errors>0 then erase(yyout);
  653. if file_size(lstfilename)=0 then
  654. erase(yylst)
  655. else
  656. writeln('(see ', lstfilename, ' for more information)');
  657. halt(errors);
  658. end(*Lex*).