2
0

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