@@ -7,15 +7,18 @@ use cfgrammar::{
7
7
Location , Span ,
8
8
} ;
9
9
use getopts:: Options ;
10
- use lrlex:: { DefaultLexerTypes , LRNonStreamingLexerDef , LexerDef } ;
10
+ use lrlex:: { DefaultLexerTypes , LRLexError , LRNonStreamingLexerDef , LexerDef } ;
11
11
use lrpar:: {
12
12
parser:: { RTParserBuilder , RecoveryKind } ,
13
13
LexerTypes ,
14
14
} ;
15
- use lrtable:: { from_yacc, Minimiser } ;
15
+ use lrtable:: { from_yacc, Minimiser , StateTable } ;
16
+ use num_traits:: AsPrimitive ;
16
17
use num_traits:: ToPrimitive as _;
17
18
use std:: {
18
19
env,
20
+ error:: Error ,
21
+ fmt,
19
22
fs:: File ,
20
23
io:: Read ,
21
24
path:: { Path , PathBuf } ,
@@ -34,7 +37,7 @@ fn usage(prog: &str, msg: &str) -> ! {
34
37
if !msg. is_empty ( ) {
35
38
eprintln ! ( "{}" , msg) ;
36
39
}
37
- eprintln ! ( "Usage: {} [-r <cpctplus|none>] [-y <eco|grmtools|original>] [-dq] <lexer.l> <parser.y> <input file> " , leaf) ;
40
+ eprintln ! ( "Usage: {} [-r <cpctplus|none>] [-y <eco|grmtools|original>] [-dq] <lexer.l> <parser.y> <input files> ... " , leaf) ;
38
41
process:: exit ( 1 ) ;
39
42
}
40
43
@@ -155,8 +158,8 @@ fn main() {
155
158
) ) ;
156
159
}
157
160
} ;
158
-
159
- if matches . free . len ( ) != 3 {
161
+ let args_len = matches . free . len ( ) ;
162
+ if args_len < 2 {
160
163
usage ( prog, "Too few arguments given." ) ;
161
164
}
162
165
@@ -319,8 +322,8 @@ fn main() {
319
322
)
320
323
) ;
321
324
}
325
+ eprintln ! ( ) ;
322
326
}
323
- eprintln ! ( ) ;
324
327
if let Some ( token_spans) = missing_from_parser {
325
328
let formatter = SpannedDiagnosticFormatter :: new ( & lex_src, & lex_l_path) . unwrap ( ) ;
326
329
let err_indent = " " . repeat ( ERROR . len ( ) ) ;
@@ -346,24 +349,214 @@ fn main() {
346
349
}
347
350
}
348
351
349
- let input = if & matches. free [ 2 ] == "-" {
350
- let mut s = String :: new ( ) ;
351
- std:: io:: stdin ( ) . read_to_string ( & mut s) . unwrap ( ) ;
352
- s
353
- } else {
354
- read_file ( & matches. free [ 2 ] )
352
+ let parser_build_ctxt = ParserBuildCtxt {
353
+ header,
354
+ lexerdef,
355
+ grm,
356
+ stable,
357
+ yacc_y_path,
358
+ recoverykind,
355
359
} ;
356
- let lexer = lexerdef. lexer ( & input) ;
357
- let pb = RTParserBuilder :: new ( & grm, & stable) . recoverer ( recoverykind) ;
358
- let ( pt, errs) = pb. parse_generictree ( & lexer) ;
359
- match pt {
360
- Some ( pt) => println ! ( "{}" , pt. pp( & grm, & input) ) ,
361
- None => println ! ( "Unable to repair input sufficiently to produce parse tree.\n " ) ,
360
+
361
+ if matches. free . len ( ) == 3 {
362
+ let input_path = PathBuf :: from ( & matches. free [ 2 ] ) ;
363
+ // If there is only one input file we want to print the generic parse tree.
364
+ // We also want to handle `-` as stdin.
365
+ let input = if & matches. free [ 2 ] == "-" {
366
+ let mut s = String :: new ( ) ;
367
+ std:: io:: stdin ( ) . read_to_string ( & mut s) . unwrap ( ) ;
368
+ s
369
+ } else {
370
+ read_file ( & matches. free [ 2 ] )
371
+ } ;
372
+ if let Err ( e) = parser_build_ctxt. parse_string ( input_path, input) {
373
+ eprintln ! ( "{}" , e) ;
374
+ process:: exit ( 1 ) ;
375
+ }
376
+ } else if let Err ( e) = parser_build_ctxt. parse_many ( & matches. free [ 2 ..] ) {
377
+ eprintln ! ( "{}" , e) ;
378
+ process:: exit ( 1 ) ;
379
+ }
380
+ }
381
+
382
+ struct ParserBuildCtxt < LexerTypesT >
383
+ where
384
+ LexerTypesT : LexerTypes ,
385
+ usize : AsPrimitive < LexerTypesT :: StorageT > ,
386
+ {
387
+ header : Header < Location > ,
388
+ lexerdef : LRNonStreamingLexerDef < LexerTypesT > ,
389
+ grm : YaccGrammar < LexerTypesT :: StorageT > ,
390
+ yacc_y_path : PathBuf ,
391
+ stable : StateTable < LexerTypesT :: StorageT > ,
392
+ recoverykind : RecoveryKind ,
393
+ }
394
+
395
+ #[ derive( Debug ) ]
396
+ enum NimbleparseError {
397
+ Source {
398
+ // We could consider including the source text here and
399
+ // using the span error formatting machinery.
400
+ src_path : PathBuf ,
401
+ errs : Vec < String > ,
402
+ } ,
403
+ Glob ( glob:: GlobError ) ,
404
+ Pattern ( glob:: PatternError ) ,
405
+ Other ( Box < dyn Error > ) ,
406
+ }
407
+
408
+ impl From < glob:: GlobError > for NimbleparseError {
409
+ fn from ( it : glob:: GlobError ) -> Self {
410
+ NimbleparseError :: Glob ( it)
362
411
}
363
- for e in & errs {
364
- println ! ( "{}" , e. pp( & lexer, & |t| grm. token_epp( t) ) ) ;
412
+ }
413
+
414
+ impl From < Box < dyn Error > > for NimbleparseError {
415
+ fn from ( it : Box < dyn Error > ) -> Self {
416
+ NimbleparseError :: Other ( it)
365
417
}
366
- if !errs. is_empty ( ) {
367
- process:: exit ( 1 ) ;
418
+ }
419
+
420
+ impl From < glob:: PatternError > for NimbleparseError {
421
+ fn from ( it : glob:: PatternError ) -> Self {
422
+ NimbleparseError :: Pattern ( it)
423
+ }
424
+ }
425
+
426
+ impl Error for NimbleparseError { }
427
+
428
+ impl fmt:: Display for NimbleparseError {
429
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
430
+ match self {
431
+ Self :: Source { src_path, errs } => {
432
+ writeln ! ( f, "While parsing: {}" , src_path. display( ) ) ?;
433
+ for e in errs {
434
+ writeln ! ( f, "{}" , e) ?
435
+ }
436
+ Ok ( ( ) )
437
+ }
438
+ Self :: Glob ( e) => {
439
+ write ! ( f, "{}" , e)
440
+ }
441
+ Self :: Pattern ( e) => {
442
+ write ! ( f, "{}" , e)
443
+ }
444
+ Self :: Other ( e) => {
445
+ write ! ( f, "{}" , e)
446
+ }
447
+ }
448
+ }
449
+ }
450
+
451
+ impl < LexerTypesT > ParserBuildCtxt < LexerTypesT >
452
+ where
453
+ LexerTypesT : LexerTypes < LexErrorT = LRLexError > ,
454
+ usize : AsPrimitive < LexerTypesT :: StorageT > ,
455
+ LexerTypesT :: StorageT : TryFrom < usize > ,
456
+ {
457
+ fn parse_string ( self , input_path : PathBuf , input_src : String ) -> Result < ( ) , NimbleparseError > {
458
+ let lexer = self . lexerdef . lexer ( & input_src) ;
459
+ let pb = RTParserBuilder :: new ( & self . grm , & self . stable ) . recoverer ( self . recoverykind ) ;
460
+ let ( pt, errs) = pb. parse_generictree ( & lexer) ;
461
+ match pt {
462
+ Some ( pt) => println ! ( "{}" , pt. pp( & self . grm, & input_src) ) ,
463
+ None => println ! ( "Unable to repair input sufficiently to produce parse tree.\n " ) ,
464
+ }
465
+ if !errs. is_empty ( ) {
466
+ return Err ( NimbleparseError :: Source {
467
+ src_path : input_path,
468
+ errs : errs
469
+ . iter ( )
470
+ . map ( |e| e. pp ( & lexer, & |t| self . grm . token_epp ( t) ) )
471
+ . collect :: < Vec < _ > > ( ) ,
472
+ } ) ;
473
+ }
474
+ Ok ( ( ) )
475
+ }
476
+
477
+ fn parse_many ( self , input_paths : & [ String ] ) -> Result < ( ) , NimbleparseError > {
478
+ let input_paths = if input_paths. is_empty ( ) {
479
+ // If given no input paths, try to find some with `test_files` in the header.
480
+ if let Some ( HeaderValue ( _, val) ) = self . header . get ( "test_files" ) {
481
+ let s = val. expect_string_with_context ( "'test_files' in %grmtools" ) ?;
482
+ if let Some ( yacc_y_path_dir) = self . yacc_y_path . parent ( ) {
483
+ let joined = yacc_y_path_dir. join ( s) ;
484
+ let joined = joined. as_os_str ( ) . to_str ( ) ;
485
+ if let Some ( s) = joined {
486
+ let mut paths = glob:: glob ( s) ?. peekable ( ) ;
487
+ if paths. peek ( ) . is_none ( ) {
488
+ return Err ( NimbleparseError :: Other (
489
+ format ! ( "'test_files' glob '{}' matched no paths" , s)
490
+ . to_string ( )
491
+ . into ( ) ,
492
+ ) ) ;
493
+ }
494
+ let mut input_paths = Vec :: new ( ) ;
495
+ for path in paths {
496
+ input_paths. push ( path?) ;
497
+ }
498
+ input_paths
499
+ } else {
500
+ return Err ( NimbleparseError :: Other (
501
+ format ! (
502
+ "Unable to convert joined path to str {} with glob '{}'" ,
503
+ self . yacc_y_path. display( ) ,
504
+ s
505
+ )
506
+ . into ( ) ,
507
+ ) ) ;
508
+ }
509
+ } else {
510
+ return Err ( NimbleparseError :: Other (
511
+ format ! (
512
+ "Unable to find parent path for {}" ,
513
+ self . yacc_y_path. display( )
514
+ )
515
+ . into ( ) ,
516
+ ) ) ;
517
+ }
518
+ } else {
519
+ return Err ( NimbleparseError :: Other (
520
+ "Missing <input file> argument" . into ( ) ,
521
+ ) ) ;
522
+ }
523
+ } else {
524
+ // Just convert the given arguments to paths.
525
+ input_paths
526
+ . iter ( )
527
+ . map ( PathBuf :: from)
528
+ . collect :: < Vec < PathBuf > > ( )
529
+ } ;
530
+ if input_paths. is_empty ( ) {
531
+ return Err ( NimbleparseError :: Other (
532
+ "Missing <input file> argument" . into ( ) ,
533
+ ) ) ;
534
+ }
535
+ let pb = RTParserBuilder :: new ( & self . grm , & self . stable ) . recoverer ( self . recoverykind ) ;
536
+ // Actually parse the given arguments or the `test_files` specified in the grammar.
537
+ for input_path in input_paths {
538
+ let input = read_file ( & input_path) ;
539
+ let lexer = self . lexerdef . lexer ( & input) ;
540
+ let ( pt, errs) = pb. parse_generictree ( & lexer) ;
541
+ if errs. is_empty ( ) && pt. is_some ( ) {
542
+ // Since we're parsing many files, don't output all of their parse trees, just print the file name.
543
+ println ! ( "parsed: {}" , input_path. display( ) )
544
+ } else {
545
+ if pt. is_none ( ) {
546
+ eprintln ! (
547
+ "Unable to repair input at '{}' sufficiently to produce parse tree.\n " ,
548
+ input_path. display( )
549
+ ) ;
550
+ }
551
+ return Err ( NimbleparseError :: Source {
552
+ src_path : input_path,
553
+ errs : errs
554
+ . iter ( )
555
+ . map ( |e| e. pp ( & lexer, & |t| self . grm . token_epp ( t) ) )
556
+ . collect :: < Vec < _ > > ( ) ,
557
+ } ) ;
558
+ }
559
+ }
560
+ Ok ( ( ) )
368
561
}
369
562
}
0 commit comments