@@ -7,15 +7,18 @@ use cfgrammar::{
77 Location , Span ,
88} ;
99use getopts:: Options ;
10- use lrlex:: { DefaultLexerTypes , LRNonStreamingLexerDef , LexerDef } ;
10+ use lrlex:: { DefaultLexerTypes , LRLexError , LRNonStreamingLexerDef , LexerDef } ;
1111use lrpar:: {
1212 parser:: { RTParserBuilder , RecoveryKind } ,
1313 LexerTypes ,
1414} ;
15- use lrtable:: { from_yacc, Minimiser } ;
15+ use lrtable:: { from_yacc, Minimiser , StateTable } ;
16+ use num_traits:: AsPrimitive ;
1617use num_traits:: ToPrimitive as _;
1718use std:: {
1819 env,
20+ error:: Error ,
21+ fmt,
1922 fs:: File ,
2023 io:: Read ,
2124 path:: { Path , PathBuf } ,
@@ -34,7 +37,7 @@ fn usage(prog: &str, msg: &str) -> ! {
3437 if !msg. is_empty ( ) {
3538 eprintln ! ( "{}" , msg) ;
3639 }
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) ;
3841 process:: exit ( 1 ) ;
3942}
4043
@@ -155,8 +158,8 @@ fn main() {
155158 ) ) ;
156159 }
157160 } ;
158-
159- if matches . free . len ( ) != 3 {
161+ let args_len = matches . free . len ( ) ;
162+ if args_len < 2 {
160163 usage ( prog, "Too few arguments given." ) ;
161164 }
162165
@@ -319,8 +322,8 @@ fn main() {
319322 )
320323 ) ;
321324 }
325+ eprintln ! ( ) ;
322326 }
323- eprintln ! ( ) ;
324327 if let Some ( token_spans) = missing_from_parser {
325328 let formatter = SpannedDiagnosticFormatter :: new ( & lex_src, & lex_l_path) . unwrap ( ) ;
326329 let err_indent = " " . repeat ( ERROR . len ( ) ) ;
@@ -346,24 +349,214 @@ fn main() {
346349 }
347350 }
348351
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,
355359 } ;
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)
362411 }
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)
365417 }
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 ( ( ) )
368561 }
369562}
0 commit comments