1
|
{
|
2
|
(* This file is part of the Kind 2 model checker.
|
3
|
|
4
|
Copyright (c) 2015 by the Board of Trustees of the University of Iowa
|
5
|
|
6
|
Licensed under the Apache License, Version 2.0 (the "License"); you
|
7
|
may not use this file except in compliance with the License. You
|
8
|
may obtain a copy of the License at
|
9
|
|
10
|
http://www.apache.org/licenses/LICENSE-2.0
|
11
|
|
12
|
Unless required by applicable law or agreed to in writing, software
|
13
|
distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
15
|
implied. See the License for the specific language governing
|
16
|
permissions and limitations under the License.
|
17
|
|
18
|
*)
|
19
|
|
20
|
|
21
|
open KindLustreParser
|
22
|
|
23
|
|
24
|
(* Imported from Kind flags.ml *)
|
25
|
|
26
|
(* Input flag. *)
|
27
|
let input_file_default = ""
|
28
|
let input_file = ref input_file_default
|
29
|
let set_input_file f = input_file := f
|
30
|
let input_file () = !input_file
|
31
|
|
32
|
|
33
|
(* All files in the cone of influence of the input file. *)
|
34
|
let all_input_files = ref []
|
35
|
let clear_input_files () = all_input_files := []
|
36
|
let add_input_file file =
|
37
|
(* Additional input file're relative to main input file. *)
|
38
|
let path = input_file () |> Filename.dirname in
|
39
|
all_input_files := (path ^ "/" ^ file) :: ! all_input_files
|
40
|
let get_all_input_files () = (input_file ()) :: ! all_input_files
|
41
|
|
42
|
(* End of Import from Kind flags.ml *)
|
43
|
|
44
|
|
45
|
(* Pretty-print an array of integers *)
|
46
|
let rec pp_print_int_array i ppf a =
|
47
|
|
48
|
if i = 0 then
|
49
|
Format.fprintf ppf "@[<hv 3>[|";
|
50
|
|
51
|
if i >= Array.length a then
|
52
|
|
53
|
Format.fprintf ppf "|]@]"
|
54
|
|
55
|
else
|
56
|
|
57
|
(
|
58
|
|
59
|
Format.fprintf ppf "%d" a.(i);
|
60
|
|
61
|
if i+2 = Array.length a then
|
62
|
Format.fprintf ppf ";@ ";
|
63
|
|
64
|
pp_print_int_array (succ i) ppf a
|
65
|
|
66
|
)
|
67
|
|
68
|
(* Pretty-print a position *)
|
69
|
let pp_print_position ppf
|
70
|
{ Lexing.pos_fname;
|
71
|
Lexing.pos_lnum;
|
72
|
Lexing.pos_bol;
|
73
|
Lexing.pos_cnum } =
|
74
|
|
75
|
Format.fprintf ppf
|
76
|
"@[<hv 2>{ pos_fname : %s;@ \
|
77
|
pos_lnum : %d;@ \
|
78
|
pos_bol : %d;@ \
|
79
|
pos_cnum : %d; }@]"
|
80
|
pos_fname
|
81
|
pos_lnum
|
82
|
pos_bol
|
83
|
pos_cnum
|
84
|
|
85
|
|
86
|
(* Pretty-print a lexing buffer *)
|
87
|
let pp_print_lexbuf ppf
|
88
|
{ Lexing.lex_buffer;
|
89
|
Lexing.lex_buffer_len;
|
90
|
Lexing.lex_abs_pos;
|
91
|
Lexing.lex_start_pos;
|
92
|
Lexing.lex_curr_pos;
|
93
|
Lexing.lex_last_pos;
|
94
|
Lexing.lex_last_action;
|
95
|
Lexing.lex_eof_reached;
|
96
|
Lexing.lex_mem;
|
97
|
Lexing.lex_start_p;
|
98
|
Lexing.lex_curr_p } =
|
99
|
|
100
|
Format.fprintf ppf
|
101
|
"@[<hv 2>{ lex_buffer : %s;@ \
|
102
|
lex_buffer_len : %d;@ \
|
103
|
lex_abs_pos : %d;@ \
|
104
|
lex_start_pos : %d;@ \
|
105
|
lex_curr_pos : %d;@ \
|
106
|
lex_last_pos : %d;@ \
|
107
|
lex_last_action : %d;@ \
|
108
|
lex_eof_reached : %B;@ \
|
109
|
lex_mem : %a;@ \
|
110
|
lex_start_p : %a;@ \
|
111
|
lex_curr_p : %a;@]"
|
112
|
(Bytes.to_string lex_buffer)
|
113
|
lex_buffer_len
|
114
|
lex_abs_pos
|
115
|
lex_start_pos
|
116
|
lex_curr_pos
|
117
|
lex_last_pos
|
118
|
lex_last_action
|
119
|
lex_eof_reached
|
120
|
(pp_print_int_array 0) lex_mem
|
121
|
pp_print_position lex_start_p
|
122
|
pp_print_position lex_curr_p
|
123
|
|
124
|
|
125
|
let char_for_backslash = function
|
126
|
| 'n' -> '\010'
|
127
|
| 'r' -> '\013'
|
128
|
| 'b' -> '\008'
|
129
|
| 't' -> '\009'
|
130
|
| c -> c
|
131
|
|
132
|
(* Buffer to store strings *)
|
133
|
let string_buf = Buffer.create 1024
|
134
|
|
135
|
(* A stack of pairs of channels, a directory and lexing buffers to
|
136
|
handle included files
|
137
|
|
138
|
The channel at the head of the list is the current channel to read
|
139
|
from, the directory is the directory the channel is in, the lexing
|
140
|
buffer is the one to return to once all characters have been read
|
141
|
from the channel.
|
142
|
|
143
|
Have only one lexing buffer and push a shallow copy of it to this
|
144
|
stack when switching to an included file. At the end of the
|
145
|
included file, restore the state of the lexing buffer from its
|
146
|
shallow copy.
|
147
|
|
148
|
When an eof is read from the lexing buffer, do not terminate but
|
149
|
call pop_channel_of_lexbuf continue. If this raises the exception
|
150
|
End_of_file, all input files have been read.
|
151
|
|
152
|
*)
|
153
|
let lexbuf_stack = ref []
|
154
|
|
155
|
|
156
|
(* Initialize the stack *)
|
157
|
let lexbuf_init channel curdir =
|
158
|
|
159
|
(* Reset input files before lexing. *)
|
160
|
clear_input_files () ;
|
161
|
|
162
|
(* A dummy lexing buffer to return to *)
|
163
|
let lexbuf = Lexing.from_channel channel in
|
164
|
|
165
|
(* Initialize the stack *)
|
166
|
lexbuf_stack := [(channel, curdir, lexbuf)]
|
167
|
|
168
|
|
169
|
(* Switch to a new channel *)
|
170
|
let lexbuf_switch_to_channel lexbuf channel curdir =
|
171
|
|
172
|
(* Add channel and shallow copy of the previous lexing buffer to the
|
173
|
top of the stack *)
|
174
|
lexbuf_stack :=
|
175
|
(channel,
|
176
|
curdir,
|
177
|
{ lexbuf with
|
178
|
Lexing.lex_buffer = Bytes.copy lexbuf.Lexing.lex_buffer}) ::
|
179
|
!lexbuf_stack;
|
180
|
|
181
|
(* Flush lexing buffer *)
|
182
|
lexbuf.Lexing.lex_curr_pos <- 0;
|
183
|
lexbuf.Lexing.lex_abs_pos <- 0;
|
184
|
lexbuf.Lexing.lex_curr_p <-
|
185
|
{ lexbuf.Lexing.lex_curr_p with Lexing.pos_cnum = 0 };
|
186
|
lexbuf.Lexing.lex_buffer_len <- 0
|
187
|
|
188
|
|
189
|
(* Pop lexing buffer from the stack an restore state of the lexing buffer *)
|
190
|
let pop_channel_of_lexbuf lexbuf =
|
191
|
|
192
|
match !lexbuf_stack with
|
193
|
|
194
|
(* Exception if last channel has been popped off the stack *)
|
195
|
| [] -> raise End_of_file
|
196
|
|
197
|
(* Take channel and lexing buffer from top of stack *)
|
198
|
| (ch, _, prev_lexbuf) :: tl ->
|
199
|
|
200
|
|
201
|
(* Close channel *)
|
202
|
close_in ch;
|
203
|
|
204
|
(* Pop off stack *)
|
205
|
lexbuf_stack := tl;
|
206
|
|
207
|
(* Restore state of the lexing buffer *)
|
208
|
lexbuf.Lexing.lex_curr_pos <- prev_lexbuf.Lexing.lex_curr_pos;
|
209
|
lexbuf.Lexing.lex_abs_pos <- prev_lexbuf.Lexing.lex_abs_pos;
|
210
|
lexbuf.Lexing.lex_curr_p <- prev_lexbuf.Lexing.lex_curr_p;
|
211
|
lexbuf.Lexing.lex_buffer <- prev_lexbuf.Lexing.lex_buffer;
|
212
|
lexbuf.Lexing.lex_buffer_len <- prev_lexbuf.Lexing.lex_buffer_len
|
213
|
|
214
|
|
215
|
(* Get the directory associated with the current channel *)
|
216
|
let curdir_of_lexbuf_stack () = match !lexbuf_stack with
|
217
|
| [] -> Sys.getcwd ()
|
218
|
| (_, curdir, _) :: _ -> curdir
|
219
|
|
220
|
|
221
|
|
222
|
(* Function to read from the channel at the top of the stack *)
|
223
|
let read_from_lexbuf_stack buf n =
|
224
|
|
225
|
match !lexbuf_stack with
|
226
|
| [] -> 0
|
227
|
| (ch, _, _) :: _ -> input ch buf 0 n
|
228
|
|
229
|
|
230
|
(* Create and populate a hashtable *)
|
231
|
let mk_hashtbl init =
|
232
|
let tbl = List.length init |> Hashtbl.create in
|
233
|
init |> List.iter (fun (k, v) -> Hashtbl.add tbl k v) ;
|
234
|
tbl
|
235
|
|
236
|
(* Use hash tables instead of rule matches to keep numer of transition
|
237
|
of lexer small *)
|
238
|
|
239
|
(* Hashtable of keywords *)
|
240
|
let keyword_table = mk_hashtbl [
|
241
|
|
242
|
(* Types *)
|
243
|
"type", TYPE ;
|
244
|
"int", INT ;
|
245
|
"real", REAL ;
|
246
|
"bool", BOOL ;
|
247
|
"subrange", SUBRANGE ;
|
248
|
"of", OF ;
|
249
|
(* "array", ARRAY) ; *)
|
250
|
"struct", STRUCT ;
|
251
|
"enum", ENUM ;
|
252
|
|
253
|
(* Constant declaration *)
|
254
|
"const", CONST ;
|
255
|
|
256
|
(* Node / function declaration *)
|
257
|
"imported", IMPORTED ;
|
258
|
"node", NODE ;
|
259
|
"function", FUNCTION ;
|
260
|
"returns", RETURNS ;
|
261
|
"var", VAR ;
|
262
|
"let", LET ;
|
263
|
"tel", TEL ;
|
264
|
|
265
|
(* Assertion *)
|
266
|
"assert", ASSERT ;
|
267
|
|
268
|
(* Check *)
|
269
|
"check", CHECK ;
|
270
|
|
271
|
(* Annotations. *)
|
272
|
"PROPERTY", PROPERTY ;
|
273
|
"MAIN", MAIN ;
|
274
|
|
275
|
(* Contract related things. *)
|
276
|
"contract", CONTRACT ;
|
277
|
"import", IMPORTCONTRACT ;
|
278
|
|
279
|
(* Boolean operators *)
|
280
|
"true", TRUE ;
|
281
|
"false", FALSE ;
|
282
|
"not", NOT ;
|
283
|
"and", AND ;
|
284
|
"xor", XOR ;
|
285
|
"forall", FORALL ;
|
286
|
"exists", EXISTS ;
|
287
|
"or", OR ;
|
288
|
"if", IF ;
|
289
|
"then", THEN ;
|
290
|
"else", ELSE ;
|
291
|
"with", WITH ;
|
292
|
"div", INTDIV ;
|
293
|
"mod", MOD ;
|
294
|
|
295
|
(* Clock operators *)
|
296
|
"when", WHEN ;
|
297
|
"current", CURRENT ;
|
298
|
"condact", CONDACT ;
|
299
|
"activate", ACTIVATE ;
|
300
|
"initial", INITIAL ;
|
301
|
"default", DEFAULT ;
|
302
|
"every", EVERY ;
|
303
|
"restart", RESTART ;
|
304
|
"merge", MERGE ;
|
305
|
|
306
|
(* Automata *)
|
307
|
"automaton", AUTOMATON;
|
308
|
"state", STATE;
|
309
|
"unless", UNLESS;
|
310
|
"until", UNTIL;
|
311
|
"resume", RESUME;
|
312
|
"elsif", ELSIF;
|
313
|
"end", END;
|
314
|
|
315
|
(* Temporal operators *)
|
316
|
"pre", PRE ;
|
317
|
"last", LAST ;
|
318
|
"fby", FBY ;
|
319
|
|
320
|
(* |===| Block annotation contract stuff. *)
|
321
|
"mode", MODE;
|
322
|
"assume", ASSUME;
|
323
|
"guarantee", GUARANTEE;
|
324
|
"require", REQUIRE;
|
325
|
"ensure", ENSURE;
|
326
|
|
327
|
]
|
328
|
|
329
|
|
330
|
}
|
331
|
|
332
|
|
333
|
(* Identifier
|
334
|
|
335
|
C syntax: alphanumeric characters including the underscore, starting
|
336
|
with a letter or the underscore *)
|
337
|
let id = ['a'-'z' 'A'-'Z' '_'] ['a'-'z' 'A'-'Z' '_' '0'-'9']*
|
338
|
|
339
|
(* Keep these separated from alphabetic characters, otherwise a->b would
|
340
|
be one token *)
|
341
|
let printable = ['+' '-' '*' '/' '>' '<' '=' ]+
|
342
|
|
343
|
(* Floating point decimal
|
344
|
|
345
|
Don't allow floats like "2." this will conflict with array slicing "2..3" *)
|
346
|
let decimal = ['0'-'9']* '.' ['0'-'9']+ (('E'|'e') ('+'|'-')? ['0'-'9']+)?
|
347
|
|
348
|
(* Floating-point decimal with exponent only *)
|
349
|
let exponent_decimal = ['0'-'9']+ '.'? ('E'|'e') ('+'|'-')? ['0'-'9']+
|
350
|
|
351
|
(* Integer numeral *)
|
352
|
let numeral = ['0'-'9']+
|
353
|
|
354
|
(* Hexadecimal numerals *)
|
355
|
let hexdigit = ['0'-'9' 'a'-'f' 'A'-'F']
|
356
|
let hexpo = 'p' ('+'|'-')? ['0'-'9']+
|
357
|
|
358
|
let hex_num = "0x" hexdigit+
|
359
|
let hex_dec1 = "0x" hexdigit+ ('.' hexdigit*)? hexpo?
|
360
|
let hex_dec2 = "0x" '.' hexdigit+ hexpo?
|
361
|
|
362
|
(* Whitespace *)
|
363
|
let whitespace = [' ' '\t']
|
364
|
|
365
|
(* Newline *)
|
366
|
let newline = '\r'* '\n'
|
367
|
|
368
|
(* Toplevel function *)
|
369
|
rule token = parse
|
370
|
|
371
|
(* |===| Annotations. *)
|
372
|
|
373
|
(* Inline. *)
|
374
|
|"--%" { PERCENTANNOT }
|
375
|
|"--!" { BANGANNOT }
|
376
|
|
377
|
(* Parenthesis star (PS) block annotations. *)
|
378
|
| "(*" ('%'|'!') { PSBLOCKSTART }
|
379
|
| "(*@" { PSATBLOCK }
|
380
|
|
381
|
(* End of parenthesis star (PS). *)
|
382
|
| "*)" { PSBLOCKEND }
|
383
|
|
384
|
(* Slash star (SS) block annotations. *)
|
385
|
| "/*" ('%'|'!') { SSBLOCKSTART }
|
386
|
| "/*@" { SSATBLOCK }
|
387
|
|
388
|
(* End of slash star (SS). *)
|
389
|
| "*/" { SSBLOCKEND }
|
390
|
|
391
|
|
392
|
(* |===| Actual comments. *)
|
393
|
|
394
|
(* Inline.
|
395
|
Need to have the '-'* here, otherwise "---" would be matched
|
396
|
as operator *)
|
397
|
| "--" '-'* { skip_to_eol lexbuf }
|
398
|
|
399
|
(* Multi-line. *)
|
400
|
| "/*" { skip_commented_slashstar lexbuf }
|
401
|
| "(*" { skip_commented_parenstar lexbuf }
|
402
|
|
403
|
|
404
|
(* |===| Include file. *)
|
405
|
| "include" whitespace* '\"' ([^'\"']* as p) '\"' {
|
406
|
|
407
|
(* Open include file *)
|
408
|
let include_channel, include_curdir =
|
409
|
try (
|
410
|
let include_channel, include_curdir =
|
411
|
open_in (Filename.concat (curdir_of_lexbuf_stack ()) p),
|
412
|
Filename.dirname p
|
413
|
in
|
414
|
|
415
|
include_channel, include_curdir
|
416
|
) with Sys_error e ->
|
417
|
Format.sprintf "Error opening include file %s: %s" p e
|
418
|
|> failwith
|
419
|
in
|
420
|
|
421
|
(* Add `p` to the list of input files. *)
|
422
|
add_input_file p ;
|
423
|
|
424
|
(* New lexing buffer from include file *)
|
425
|
lexbuf_switch_to_channel lexbuf include_channel include_curdir ;
|
426
|
|
427
|
Lexing.flush_input lexbuf ;
|
428
|
|
429
|
(* Starting position in new file *)
|
430
|
let zero_pos =
|
431
|
{ Lexing.pos_fname = p ;
|
432
|
Lexing.pos_lnum = 1 ;
|
433
|
Lexing.pos_bol = 0 ;
|
434
|
Lexing.pos_cnum = 0 }
|
435
|
in
|
436
|
|
437
|
(* Set new position in lexing buffer *)
|
438
|
lexbuf.Lexing.lex_curr_p <- zero_pos ;
|
439
|
|
440
|
(* Continue with included file *)
|
441
|
token lexbuf
|
442
|
}
|
443
|
|
444
|
(* Operators that are not identifiers *)
|
445
|
| ';' { SEMICOLON }
|
446
|
| '=' { EQUALS }
|
447
|
| ':' { COLON }
|
448
|
| ',' { COMMA }
|
449
|
| '[' { LSQBRACKET }
|
450
|
| ']' { RSQBRACKET }
|
451
|
| '(' { LPAREN }
|
452
|
| ')' { RPAREN }
|
453
|
| ')' { RPAREN }
|
454
|
| '.' { DOT }
|
455
|
| ".." { DOTDOT }
|
456
|
| '^' { CARET }
|
457
|
| '{' { LCURLYBRACKET }
|
458
|
| '}' { RCURLYBRACKET }
|
459
|
| '}' { RCURLYBRACKET }
|
460
|
| ".%" { DOTPERCENT }
|
461
|
| '|' { PIPE }
|
462
|
| "<<" { LPARAMBRACKET }
|
463
|
| ">>" { RPARAMBRACKET }
|
464
|
| "=>" { IMPL }
|
465
|
| '#' { HASH }
|
466
|
| "<=" { LTE }
|
467
|
| ">=" { GTE }
|
468
|
| '<' { LT }
|
469
|
| '>' { GT }
|
470
|
| "<>" { NEQ }
|
471
|
| '-' { MINUS }
|
472
|
| '+' { PLUS }
|
473
|
| '/' { DIV }
|
474
|
| '*' { MULT }
|
475
|
| "->" { ARROW }
|
476
|
|
477
|
(* Decimal or numeral *)
|
478
|
| decimal as p { DECIMAL p }
|
479
|
| exponent_decimal as p { DECIMAL p }
|
480
|
| numeral as p { NUMERAL p }
|
481
|
|
482
|
| hex_num as p { NUMERAL p }
|
483
|
| hex_dec1 as p { DECIMAL p }
|
484
|
| hex_dec2 as p { DECIMAL p }
|
485
|
|
486
|
(* Keyword *)
|
487
|
| id as p {
|
488
|
try Hashtbl.find keyword_table p with Not_found -> (SYM p)
|
489
|
}
|
490
|
|
491
|
(* Identifier with quote, throw quote away *)
|
492
|
| '\'' (id as p) { QUOTSYM p }
|
493
|
|
494
|
(* Whitespace *)
|
495
|
| whitespace { token lexbuf }
|
496
|
|
497
|
(* Newline *)
|
498
|
| newline { Lexing.new_line lexbuf ; token lexbuf }
|
499
|
|
500
|
(* String *)
|
501
|
| "\"" { Buffer.clear string_buf; string lexbuf }
|
502
|
|
503
|
(* End of file *)
|
504
|
| eof {
|
505
|
(* Pop previous lexing buffer form stack if at end of included file *)
|
506
|
try pop_channel_of_lexbuf lexbuf ; token lexbuf with End_of_file -> EOF
|
507
|
}
|
508
|
|
509
|
(* Unrecognized character *)
|
510
|
| _ as c {
|
511
|
Format.sprintf "Unrecognized token %c (0x%X)" c (Char.code c) |> failwith
|
512
|
}
|
513
|
|
514
|
(* Parse until end of comment, count newlines and otherwise discard
|
515
|
characters *)
|
516
|
and skip_commented_slashstar = parse
|
517
|
|
518
|
(* End of comment *)
|
519
|
| "*/" { token lexbuf }
|
520
|
|
521
|
(* Count new line *)
|
522
|
| newline { Lexing.new_line lexbuf ; skip_commented_slashstar lexbuf }
|
523
|
|
524
|
| eof { Format.sprintf "Unterminated comment" |> failwith }
|
525
|
|
526
|
(* Ignore characters in comments *)
|
527
|
| _ { skip_commented_slashstar lexbuf }
|
528
|
|
529
|
|
530
|
(* Parse until end of comment, count newlines and otherwise discard
|
531
|
characters *)
|
532
|
and skip_commented_parenstar = parse
|
533
|
|
534
|
(* End of comment *)
|
535
|
| "*)" { token lexbuf }
|
536
|
|
537
|
(* Count new line *)
|
538
|
| newline { Lexing.new_line lexbuf; skip_commented_parenstar lexbuf }
|
539
|
|
540
|
| eof { Format.sprintf "Unterminated comment" |> failwith }
|
541
|
|
542
|
(* Ignore characters in comments *)
|
543
|
| _ { skip_commented_parenstar lexbuf }
|
544
|
|
545
|
and skip_to_eol = parse
|
546
|
|
547
|
(* Count new line and resume *)
|
548
|
| newline { Lexing.new_line lexbuf; token lexbuf }
|
549
|
|
550
|
(* Line ends at end of file *)
|
551
|
| eof { token lexbuf }
|
552
|
|
553
|
(* Ignore characters *)
|
554
|
| _ { skip_to_eol lexbuf }
|
555
|
|
556
|
|
557
|
and return_at_eol t = parse
|
558
|
|
559
|
(* Count new line and resume *)
|
560
|
| newline { Lexing.new_line lexbuf; t }
|
561
|
|
562
|
(* Line ends at end of file *)
|
563
|
| eof { t }
|
564
|
|
565
|
(* Ignore characters *)
|
566
|
| _ { return_at_eol t lexbuf }
|
567
|
|
568
|
|
569
|
and string = parse
|
570
|
| "\""
|
571
|
{ STRING (Buffer.contents string_buf) }
|
572
|
| "\\" (_ as c)
|
573
|
{ Buffer.add_char string_buf (char_for_backslash c); string lexbuf }
|
574
|
| newline
|
575
|
{ Lexing.new_line lexbuf; Buffer.add_char string_buf '\n'; string lexbuf }
|
576
|
| eof
|
577
|
{ failwith (Format.sprintf "Unterminated string") }
|
578
|
| _ as c
|
579
|
{ Buffer.add_char string_buf c; string lexbuf }
|
580
|
|
581
|
|
582
|
|
583
|
{
|
584
|
|
585
|
(*
|
586
|
|
587
|
(* Test code *)
|
588
|
let main () =
|
589
|
|
590
|
(* Read all tokens and output their positions *)
|
591
|
let rec aux ppf lexbuf =
|
592
|
|
593
|
(* Read a token *)
|
594
|
let token = token lexbuf in
|
595
|
|
596
|
(* Output position and the token *)
|
597
|
Format.fprintf
|
598
|
ppf
|
599
|
"%a %s"
|
600
|
pp_print_position lexbuf.Lexing.lex_start_p
|
601
|
(Lexing.lexeme lexbuf);
|
602
|
|
603
|
(* Terminate at the end of the file, otherwise continue *)
|
604
|
if token = EOF then () else
|
605
|
|
606
|
(Format.fprintf ppf "@,";
|
607
|
aux ppf lexbuf)
|
608
|
|
609
|
in
|
610
|
|
611
|
(* Create lexing buffer *)
|
612
|
let lexbuf = Lexing.from_function read_from_lexbuf_stack in
|
613
|
|
614
|
(* Read from file or standard input *)
|
615
|
let in_ch =
|
616
|
if Array.length Sys.argv > 1 then
|
617
|
(let fname = Sys.argv.(1) in
|
618
|
|
619
|
let zero_pos =
|
620
|
{ Lexing.pos_fname = fname;
|
621
|
Lexing.pos_lnum = 1;
|
622
|
Lexing.pos_bol = 0;
|
623
|
Lexing.pos_cnum = 0 }
|
624
|
in
|
625
|
lexbuf.Lexing.lex_curr_p <- zero_pos;
|
626
|
|
627
|
open_in fname)
|
628
|
else
|
629
|
stdin
|
630
|
in
|
631
|
|
632
|
(* Initialize lexing buffer with channel *)
|
633
|
lexbuf_init in_ch;
|
634
|
|
635
|
(* Output all tokens and their positions *)
|
636
|
Format.printf "@[<v>%t@]@." (function ppf -> aux ppf lexbuf)
|
637
|
|
638
|
;;
|
639
|
|
640
|
main ()
|
641
|
*)
|
642
|
|
643
|
}
|
644
|
|
645
|
(*
|
646
|
Local Variables:
|
647
|
compile-command: "make -C .. -k"
|
648
|
indent-tabs-mode: nil
|
649
|
End:
|
650
|
*)
|
651
|
|