plex.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  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 );
  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. end
  81. else
  82. error(symbol_already_defined, pos)
  83. end(*define_start_state*);
  84. procedure define_macro ( symbol, replacement : String );
  85. (* process macro definition *)
  86. begin
  87. {$ifdef fpc}
  88. with sym_table^[key('{'+symbol+'}', max_keys, @lookup, @entry)] do
  89. {$else}
  90. with sym_table^[key('{'+symbol+'}', max_keys, lookup, entry)] do
  91. {$endif}
  92. if sym_type=none_sym then
  93. begin
  94. sym_type := macro_sym;
  95. subst := newStr(strip(replacement));
  96. end
  97. else
  98. error(symbol_already_defined, 1)
  99. end(*define_macro*);
  100. procedure add_rule;
  101. (* process rule *)
  102. var i : Integer;
  103. FIRST : IntSet;
  104. begin
  105. addExpr(r, FIRST);
  106. if n_st=0 then
  107. if cf then
  108. setunion(first_pos_table^[1]^, FIRST)
  109. else
  110. begin
  111. setunion(first_pos_table^[0]^, FIRST);
  112. setunion(first_pos_table^[1]^, FIRST);
  113. end
  114. else
  115. if cf then
  116. for i := 1 to n_st do
  117. setunion(first_pos_table^[2*st[i]+1]^, FIRST)
  118. else
  119. for i := 1 to n_st do
  120. begin
  121. setunion(first_pos_table^[2*st[i]]^, FIRST);
  122. setunion(first_pos_table^[2*st[i]+1]^, FIRST);
  123. end
  124. end(*add_rule*);
  125. procedure generate_table;
  126. (* write the DFA table to the output file
  127. Tables are represented as a collection of typed array constants:
  128. type YYTRec = record
  129. cc : set of Char; { characters }
  130. s : Integer; { next state }
  131. end;
  132. const
  133. { table sizes: }
  134. yynmarks = ...;
  135. yynmatches = ...;
  136. yyntrans = ...;
  137. yynstates = ...;
  138. { rules of mark positions for each state: }
  139. yyk : array [1..yynmarks] of Integer = ...;
  140. { rules of matches for each state: }
  141. yym : array [1..yynmatches] of Integer = ...;
  142. { transition table: }
  143. yyt : array [1..yyntrans] of YYTRec = ...;
  144. { offsets into the marks, matches and transition tables: }
  145. yykl, yykh,
  146. yyml, yymh,
  147. yytl, yyth : array [0..yynstates-1] of Integer = ...;
  148. *)
  149. var yynmarks, yynmatches, yyntrans, yynstates : Integer;
  150. yykl, yykh,
  151. yyml, yymh,
  152. yytl, yyth : array [0..max_states-1] of Integer;
  153. procedure counters;
  154. (* compute counters and offsets *)
  155. var s, i : Integer;
  156. begin
  157. yynstates := n_states; yyntrans := n_trans;
  158. yynmarks := 0; yynmatches := 0;
  159. for s := 0 to n_states-1 do with state_table^[s] do
  160. begin
  161. yytl[s] := trans_lo; yyth[s] := trans_hi;
  162. yykl[s] := yynmarks+1; yyml[s] := yynmatches+1;
  163. for i := 1 to size(state_pos^) do
  164. with pos_table^[state_pos^[i]] do
  165. if pos_type=mark_pos then
  166. if pos=0 then
  167. inc(yynmatches)
  168. else if pos=1 then
  169. inc(yynmarks);
  170. yykh[s] := yynmarks; yymh[s] := yynmatches;
  171. end;
  172. end(*counters*);
  173. procedure writecc(var f : Text; cc : CClass);
  174. (* print the given character class *)
  175. function charStr(c : Char) : String;
  176. begin
  177. case c of
  178. #0..#31, (* nonprintable characters *)
  179. #127..#255 : charStr := '#'+intStr(ord(c));
  180. '''' : charStr := '''''''''';
  181. else charStr := ''''+c+'''';
  182. end;
  183. end(*charStr*);
  184. const
  185. MaxChar = #255;
  186. var
  187. c1, c2 : Char;
  188. col : Integer;
  189. tag : String;
  190. Quit: Boolean;
  191. begin
  192. write(f, '[ ');
  193. col := 0;
  194. c1 := chr(0);
  195. Quit := False;
  196. while not Quit do begin
  197. if c1 in cc then begin
  198. if col>0 then
  199. begin
  200. write(f, ',');
  201. inc(col);
  202. end;
  203. if col>40 then
  204. { insert line break }
  205. begin
  206. writeln(f);
  207. write(f, ' ':12);
  208. col := 0;
  209. end;
  210. c2 := c1;
  211. while (c2<MaxChar) and (succ(c2) in cc) do
  212. c2 := succ(c2);
  213. if c1=c2 then
  214. tag := charStr(c1)
  215. else if c2=succ(c1) then
  216. tag := charStr(c1)+','+charStr(c2)
  217. else
  218. tag := charStr(c1)+'..'+charStr(c2);
  219. write(f, tag);
  220. col := col + length(tag);
  221. c1 := c2;
  222. end;
  223. Quit := c1 = MaxChar;
  224. if not Quit then
  225. c1 := Succ(c1);
  226. end; { of while }
  227. write(f, ' ]');
  228. end(*writecc*);
  229. procedure tables;
  230. (* print tables *)
  231. var s, i, count : Integer;
  232. begin
  233. writeln(yyout);
  234. writeln(yyout, 'type YYTRec = record');
  235. writeln(yyout, ' cc : set of Char;');
  236. writeln(yyout, ' s : Integer;');
  237. writeln(yyout, ' end;');
  238. writeln(yyout);
  239. writeln(yyout, 'const');
  240. (* table sizes: *)
  241. writeln(yyout);
  242. writeln(yyout, 'yynmarks = ', yynmarks, ';');
  243. writeln(yyout, 'yynmatches = ', yynmatches, ';');
  244. writeln(yyout, 'yyntrans = ', yyntrans, ';');
  245. writeln(yyout, 'yynstates = ', yynstates, ';');
  246. (* mark table: *)
  247. writeln(yyout);
  248. writeln(yyout, 'yyk : array [1..yynmarks] of Integer = (');
  249. count := 0;
  250. for s := 0 to n_states-1 do with state_table^[s] do
  251. begin
  252. writeln(yyout, ' { ', s, ': }');
  253. for i := 1 to size(state_pos^) do
  254. with pos_table^[state_pos^[i]] do
  255. if (pos_type=mark_pos) and (pos=1) then
  256. begin
  257. write(yyout, ' ', rule); inc(count);
  258. if count<yynmarks then write(yyout, ',');
  259. writeln(yyout);
  260. end;
  261. end;
  262. writeln(yyout, ');');
  263. (* match table: *)
  264. writeln(yyout);
  265. writeln(yyout, 'yym : array [1..yynmatches] of Integer = (');
  266. count := 0;
  267. for s := 0 to n_states-1 do with state_table^[s] do
  268. begin
  269. writeln(yyout, '{ ', s, ': }');
  270. for i := 1 to size(state_pos^) do
  271. with pos_table^[state_pos^[i]] do
  272. if (pos_type=mark_pos) and (pos=0) then
  273. begin
  274. write(yyout, ' ', rule); inc(count);
  275. if count<yynmatches then write(yyout, ',');
  276. writeln(yyout);
  277. end;
  278. end;
  279. writeln(yyout, ');');
  280. (* transition table: *)
  281. writeln(yyout);
  282. writeln(yyout, 'yyt : array [1..yyntrans] of YYTrec = (');
  283. count := 0;
  284. for s := 0 to n_states-1 do with state_table^[s] do
  285. begin
  286. writeln(yyout, '{ ', s, ': }');
  287. for i := trans_lo to trans_hi do
  288. with trans_table^[i] do
  289. begin
  290. write(yyout, ' ( cc: ');
  291. writecc(yyout, cc^);
  292. write(yyout, '; s: ');
  293. write(yyout, next_state, ')');
  294. inc(count);
  295. if count<yyntrans then write(yyout, ',');
  296. writeln(yyout);
  297. end;
  298. end;
  299. writeln(yyout, ');');
  300. (* offset tables: *)
  301. writeln(yyout);
  302. writeln(yyout, 'yykl : array [0..yynstates-1] of Integer = (');
  303. for s := 0 to n_states-1 do
  304. begin
  305. write(yyout, '{ ', s, ': } ', yykl[s]);
  306. if s<n_states-1 then write(yyout, ',');
  307. writeln(yyout);
  308. end;
  309. writeln(yyout, ');');
  310. writeln(yyout);
  311. writeln(yyout, 'yykh : array [0..yynstates-1] of Integer = (');
  312. for s := 0 to n_states-1 do
  313. begin
  314. write(yyout, '{ ', s, ': } ', yykh[s]);
  315. if s<n_states-1 then write(yyout, ',');
  316. writeln(yyout);
  317. end;
  318. writeln(yyout, ');');
  319. writeln(yyout);
  320. writeln(yyout, 'yyml : array [0..yynstates-1] of Integer = (');
  321. for s := 0 to n_states-1 do
  322. begin
  323. write(yyout, '{ ', s, ': } ', yyml[s]);
  324. if s<n_states-1 then write(yyout, ',');
  325. writeln(yyout);
  326. end;
  327. writeln(yyout, ');');
  328. writeln(yyout);
  329. writeln(yyout, 'yymh : array [0..yynstates-1] of Integer = (');
  330. for s := 0 to n_states-1 do
  331. begin
  332. write(yyout, '{ ', s, ': } ', yymh[s]);
  333. if s<n_states-1 then write(yyout, ',');
  334. writeln(yyout);
  335. end;
  336. writeln(yyout, ');');
  337. writeln(yyout);
  338. writeln(yyout, 'yytl : array [0..yynstates-1] of Integer = (');
  339. for s := 0 to n_states-1 do
  340. begin
  341. write(yyout, '{ ', s, ': } ', yytl[s]);
  342. if s<n_states-1 then write(yyout, ',');
  343. writeln(yyout);
  344. end;
  345. writeln(yyout, ');');
  346. writeln(yyout);
  347. writeln(yyout, 'yyth : array [0..yynstates-1] of Integer = (');
  348. for s := 0 to n_states-1 do
  349. begin
  350. write(yyout, '{ ', s, ': } ', yyth[s]);
  351. if s<n_states-1 then write(yyout, ',');
  352. writeln(yyout);
  353. end;
  354. writeln(yyout, ');');
  355. writeln(yyout);
  356. end(*tables*);
  357. begin
  358. counters; tables;
  359. end(*generate_table*);
  360. (* Parser: *)
  361. const
  362. max_items = 255;
  363. var
  364. itemstr : String;
  365. itemc : Integer;
  366. itempos,
  367. itemlen : array [1..max_items] of Integer;
  368. procedure split ( str : String; count : Integer );
  369. (* split str into at most count whitespace-delimited items
  370. (result in itemstr, itemc, itempos, itemlen) *)
  371. procedure scan(var act_pos : Integer);
  372. (* scan one item *)
  373. var l : Integer;
  374. begin
  375. while (act_pos<=length(itemstr)) and
  376. ((itemstr[act_pos]=' ') or (itemstr[act_pos]=tab)) do
  377. inc(act_pos);
  378. l := 0;
  379. while (act_pos+l<=length(itemstr)) and
  380. (itemstr[act_pos+l]<>' ') and (itemstr[act_pos+l]<>tab) do
  381. inc(l);
  382. inc(itemc);
  383. itempos[itemc] := act_pos;
  384. itemlen[itemc] := l;
  385. inc(act_pos, l+1);
  386. while (act_pos<=length(itemstr)) and
  387. ((itemstr[act_pos]=' ') or (itemstr[act_pos]=tab)) do
  388. inc(act_pos);
  389. end(*scan*);
  390. var act_pos : Integer;
  391. begin
  392. itemstr := str; act_pos := 1;
  393. itemc := 0;
  394. while (itemc<count-1) and (act_pos<=length(itemstr)) do scan(act_pos);
  395. if act_pos<=length(itemstr) then
  396. begin
  397. inc(itemc);
  398. itempos[itemc] := act_pos;
  399. itemlen[itemc] := length(itemstr)-act_pos+1;
  400. end;
  401. end(*split*);
  402. function itemv ( i : Integer ) : String;
  403. (* return ith item in splitted string (whole string for i=0) *)
  404. begin
  405. if i=0 then
  406. itemv := itemstr
  407. else if (i<0) or (i>itemc) then
  408. itemv := ''
  409. else
  410. itemv := copy(itemstr, itempos[i], itemlen[i])
  411. end(*itemv*);
  412. procedure code;
  413. begin
  414. while not eof(yyin) do
  415. begin
  416. get_line;
  417. if line='%}' then
  418. exit
  419. else
  420. writeln(yyout, line);
  421. end;
  422. error(unmatched_lbrace, length(line)+1);
  423. end(*code*);
  424. procedure definitions;
  425. procedure definition;
  426. function check_id ( symbol : String ) : Boolean;
  427. var i : Integer;
  428. begin
  429. if (symbol='') or not (symbol[1] in letters) then
  430. check_id := false
  431. else
  432. begin
  433. for i := 2 to length(symbol) do
  434. if not (symbol[i] in alphanums) then
  435. begin
  436. check_id := false;
  437. exit;
  438. end;
  439. check_id := true
  440. end
  441. end(*check_id*);
  442. var i : Integer;
  443. com : String;
  444. begin
  445. split(line, 2);
  446. com := upper(itemv(1));
  447. if (com='%S') or (com='%START') then
  448. begin
  449. split(line, max_items);
  450. for i := 2 to itemc do
  451. if check_id(itemv(i)) then
  452. define_start_state(itemv(i), itempos[i])
  453. else
  454. error(syntax_error, itempos[i]);
  455. end
  456. else if check_id(itemv(1)) then
  457. define_macro(itemv(1), itemv(2))
  458. else
  459. error(syntax_error, 1);
  460. end(*definition*);
  461. begin
  462. while not eof(yyin) do
  463. begin
  464. get_line;
  465. if line='' then
  466. writeln(yyout)
  467. else if line='%%' then
  468. exit
  469. else if line='%{' then
  470. code
  471. else if (line[1]='%') or (line[1] in letters) then
  472. definition
  473. else
  474. writeln(yyout, line)
  475. end;
  476. end(*definitions*);
  477. procedure rules;
  478. begin
  479. next_section;
  480. if line='%%' then
  481. while not eof(yyin) do
  482. begin
  483. get_line;
  484. if line='' then
  485. writeln(yyout)
  486. else if line='%%' then
  487. begin
  488. next_section;
  489. exit;
  490. end
  491. else if line='%{' then
  492. code
  493. else if (line[1]<>' ') and (line[1]<>tab) then
  494. begin
  495. if n_rules=0 then next_section;
  496. inc(n_rules);
  497. parse_rule(n_rules);
  498. if errors=0 then
  499. begin
  500. add_rule;
  501. write(yyout, ' ', n_rules);
  502. if strip(stmt)='|' then
  503. writeln(yyout, ',')
  504. else
  505. begin
  506. writeln(yyout, ':');
  507. writeln(yyout, blankStr(expr), stmt);
  508. end;
  509. end
  510. end
  511. else
  512. writeln(yyout, line)
  513. end
  514. else
  515. error(unexpected_eof, length(line)+1);
  516. next_section;
  517. end(*rules*);
  518. procedure auxiliary_procs;
  519. begin
  520. if line='%%' then
  521. begin
  522. writeln(yyout);
  523. while not eof(yyin) do
  524. begin
  525. get_line;
  526. writeln(yyout, line);
  527. end;
  528. end;
  529. end(*auxiliary_procs*);
  530. (* Main program: *)
  531. var i : Integer;
  532. begin
  533. {$ifdef Unix}
  534. codfilepath1:=path(paramstr(0));
  535. if (codfilepath1<>'') then
  536. codfilepath1:=codfilepath1+'../lib/fpc/lexyacc/'
  537. else
  538. codfilepath1:='/usr/local/lib/fpc/lexyacc/';
  539. codfilepath2:='/usr/lib/fpc/lexyacc/';
  540. {$else}
  541. codfilepath1:=path(paramstr(0));
  542. codfilepath2:='';
  543. {$endif}
  544. (* sign-on: *)
  545. writeln(sign_on);
  546. (* parse command line: *)
  547. if paramCount=0 then
  548. begin
  549. writeln(usage);
  550. writeln(options);
  551. halt(0);
  552. end;
  553. lfilename := '';
  554. pasfilename := '';
  555. for i := 1 to paramCount do
  556. if copy(paramStr(i), 1, 1)='-' then
  557. if upper(paramStr(i))='-V' then
  558. verbose := true
  559. else if upper(paramStr(i))='-O' then
  560. optimize := true
  561. else
  562. begin
  563. writeln(invalid_option, paramStr(i));
  564. halt(1);
  565. end
  566. else if lfilename='' then
  567. lfilename := addExt(paramStr(i), 'l')
  568. else if pasfilename='' then
  569. pasfilename := addExt(paramStr(i), 'pas')
  570. else
  571. begin
  572. writeln(illegal_no_args);
  573. halt(1);
  574. end;
  575. if lfilename='' then
  576. begin
  577. writeln(illegal_no_args);
  578. halt(1);
  579. end;
  580. if pasfilename='' then pasfilename := root(lfilename)+'.pas';
  581. lstfilename := root(lfilename)+'.lst';
  582. (* open files: *)
  583. assign(yyin, lfilename);
  584. assign(yyout, pasfilename);
  585. assign(yylst, lstfilename);
  586. reset(yyin); if ioresult<>0 then fatal(cannot_open_file+lfilename);
  587. rewrite(yyout); if ioresult<>0 then fatal(cannot_open_file+pasfilename);
  588. rewrite(yylst); if ioresult<>0 then fatal(cannot_open_file+lstfilename);
  589. (* search code template *)
  590. codfilename := 'yylex.cod';
  591. assign(yycod, codfilename);
  592. reset(yycod);
  593. if ioresult<>0 then
  594. begin
  595. codfilename := IncludeTrailingPathDelimiter(GetEnvironmentVariable('FPCDIR'))+'lexyacc'+DirectorySeparator+'yylex.cod';
  596. assign(yycod, codfilename);
  597. reset(yycod);
  598. if ioresult<>0 then
  599. begin
  600. codfilename := codfilepath1+'yylex.cod';
  601. assign(yycod, codfilename);
  602. reset(yycod);
  603. if (codfilepath2<>'') and (ioresult<>0) then
  604. begin
  605. codfilename := codfilepath2+'yylex.cod';
  606. assign(yycod, codfilename);
  607. reset(yycod);
  608. if ioresult<>0 then
  609. fatal(cannot_open_file+codfilename);
  610. end;
  611. end;
  612. end;
  613. (* parse source grammar: *)
  614. write('parse ... ');
  615. lno := 0; n_rules := 0; next_section;
  616. first_pos_table^[0] := newIntSet;
  617. first_pos_table^[1] := newIntSet;
  618. definitions;
  619. rules;
  620. if n_rules=0 then error(empty_grammar, length(line)+1);
  621. if errors=0 then
  622. begin
  623. (* generate DFA table and listings and write output code: *)
  624. write('DFA construction ... ');
  625. makeDFATable;
  626. if optimize then
  627. begin
  628. write('DFA optimization ... ');
  629. optimizeDFATable;
  630. end;
  631. write('code generation ... ');
  632. if verbose then listDFATable;
  633. generate_table; next_section;
  634. end;
  635. auxiliary_procs;
  636. if errors=0 then writeln('DONE');
  637. (* close files: *)
  638. close(yyin); close(yyout); close(yylst); close(yycod);
  639. (* print statistics: *)
  640. if errors>0 then
  641. writeln( lno, ' lines, ',
  642. errors, ' errors found.' )
  643. else
  644. writeln( lno, ' lines, ',
  645. n_rules, ' rules, ',
  646. n_pos, '/', max_pos, ' p, ',
  647. n_states, '/', max_states, ' s, ',
  648. n_trans, '/', max_trans, ' t.');
  649. if warnings>0 then writeln(warnings, ' warnings.');
  650. (* terminate: *)
  651. if errors>0 then erase(yyout);
  652. if file_size(lstfilename)=0 then
  653. erase(yylst)
  654. else
  655. writeln('(see ', lstfilename, ' for more information)');
  656. halt(errors);
  657. end(*Lex*).