11use getopts:: Options ;
22use std:: {
33 env,
4+ error:: Error ,
5+ fmt,
46 fs:: File ,
57 io:: { stderr, stdin, Read , Write } ,
68 path:: Path ,
79 process,
8- str:: FromStr ,
910} ;
1011
11- use cfgrammar:: { newlinecache:: NewlineCache , Spanned } ;
12- use lrlex:: { DefaultLexerTypes , LRNonStreamingLexerDef , LexerDef } ;
13- use lrpar:: { Lexeme , Lexer } ;
12+ use cfgrammar:: header:: { GrmtoolsSectionParser , HeaderValue } ;
13+ use lrlex:: { DefaultLexerTypes , LRNonStreamingLexerDef , LexFlags , LexerDef , LexerKind } ;
14+ use lrpar:: {
15+ diagnostics:: { DiagnosticFormatter , SpannedDiagnosticFormatter } ,
16+ Lexeme , Lexer ,
17+ } ;
18+
19+ const ERROR : & str = "[Error]" ;
20+
21+ /// A string which uses `Display` for it's `Debug` impl.
22+ struct ErrorString ( String ) ;
23+ impl fmt:: Display for ErrorString {
24+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
25+ let ErrorString ( s) = self ;
26+ write ! ( f, "{}" , s)
27+ }
28+ }
29+ impl fmt:: Debug for ErrorString {
30+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
31+ let ErrorString ( s) = self ;
32+ write ! ( f, "{}" , s)
33+ }
34+ }
35+ impl Error for ErrorString { }
1436
1537fn usage ( prog : & str , msg : & str ) {
1638 let path = Path :: new ( prog) ;
@@ -42,43 +64,80 @@ fn read_file(path: &str) -> String {
4264 s
4365}
4466
45- fn main ( ) {
67+ fn main ( ) -> Result < ( ) , Box < dyn Error > > {
4668 let args: Vec < String > = env:: args ( ) . collect ( ) ;
4769 let prog = args[ 0 ] . clone ( ) ;
4870 let matches = match Options :: new ( ) . optflag ( "h" , "help" , "" ) . parse ( & args[ 1 ..] ) {
4971 Ok ( m) => m,
5072 Err ( f) => {
5173 usage ( & prog, f. to_string ( ) . as_str ( ) ) ;
52- return ;
74+ return Ok ( ( ) ) ;
5375 }
5476 } ;
5577 if matches. opt_present ( "h" ) || matches. free . len ( ) != 2 {
5678 usage ( & prog, "" ) ;
57- return ;
79+ return Ok ( ( ) ) ;
5880 }
5981
6082 let lex_l_path = & matches. free [ 0 ] ;
6183 let lex_src = read_file ( lex_l_path) ;
62- let lexerdef = LRNonStreamingLexerDef :: < DefaultLexerTypes < u32 > > :: from_str ( & lex_src)
63- . unwrap_or_else ( |errs| {
64- let nlcache = NewlineCache :: from_str ( & lex_src) . unwrap ( ) ;
65- for e in errs {
66- if let Some ( ( line, column) ) = nlcache
67- . byte_to_line_num_and_col_num ( & lex_src, e. spans ( ) . first ( ) . unwrap ( ) . start ( ) )
68- {
69- writeln ! (
70- stderr( ) ,
71- "{}: {} at line {line} column {column}" ,
72- & lex_l_path,
73- & e
74- )
75- . ok ( ) ;
76- } else {
77- writeln ! ( stderr( ) , "{}: {}" , & lex_l_path, & e) . ok ( ) ;
78- }
84+ let lex_diag = SpannedDiagnosticFormatter :: new ( & lex_src, Path :: new ( lex_l_path) ) ;
85+ let ( mut header, _) = match GrmtoolsSectionParser :: new ( & lex_src, false ) . parse ( ) {
86+ Ok ( x) => x,
87+ Err ( es) => {
88+ eprintln ! (
89+ "\n {ERROR}{}" ,
90+ lex_diag. file_location_msg( " parsing the `%grmtools` section" , None )
91+ ) ;
92+ for e in es {
93+ eprintln ! (
94+ "{}" ,
95+ & indent( " " , & lex_diag. format_error( e) . to_string( ) )
96+ ) ;
7997 }
8098 process:: exit ( 1 ) ;
81- } ) ;
99+ }
100+ } ;
101+ header. mark_used ( & "lexerkind" . to_string ( ) ) ;
102+ let lexerkind = if let Some ( HeaderValue ( _, lk_val) ) = header. get ( "lexerkind" ) {
103+ LexerKind :: try_from ( lk_val) ?
104+ } else {
105+ LexerKind :: LRNonStreamingLexer
106+ } ;
107+
108+ let lexerdef = match lexerkind {
109+ LexerKind :: LRNonStreamingLexer => {
110+ let lex_flags = LexFlags :: try_from ( & mut header) ?;
111+ let lexerdef = match LRNonStreamingLexerDef :: < DefaultLexerTypes < u32 > > :: new_with_options (
112+ & lex_src, lex_flags,
113+ ) {
114+ Ok ( x) => x,
115+ Err ( errs) => {
116+ eprintln ! ( "\n {ERROR}{}" , lex_diag. file_location_msg( "" , None ) ) ;
117+ for e in errs {
118+ eprintln ! (
119+ "{}" ,
120+ & indent( " " , & lex_diag. format_error( e) . to_string( ) )
121+ ) ;
122+ }
123+ process:: exit ( 1 ) ;
124+ }
125+ } ;
126+ lexerdef
127+ }
128+ _ => {
129+ return Err ( ErrorString ( "Unrecognized lexer kind" . to_string ( ) ) ) ?;
130+ }
131+ } ;
132+ {
133+ let unused_header_values = header. unused ( ) ;
134+ if !unused_header_values. is_empty ( ) {
135+ return Err ( ErrorString ( format ! (
136+ "Unused header values: {}" ,
137+ unused_header_values. join( ", " )
138+ ) ) ) ?;
139+ }
140+ }
82141 let input = & read_file ( & matches. free [ 1 ] ) ;
83142 for r in lexerdef. lexer ( input) . iter ( ) {
84143 match r {
@@ -93,4 +152,19 @@ fn main() {
93152 }
94153 }
95154 }
155+ Ok ( ( ) )
156+ }
157+
158+ /// Indents a multi-line string and trims any trailing newline.
159+ /// This currently assumes that indentation on blank lines does not matter.
160+ ///
161+ /// The algorithm used by this function is:
162+ /// 1. Prefix `s` with the indentation, indenting the first line.
163+ /// 2. Trim any trailing newlines.
164+ /// 3. Replace all newlines with `\n{indent}`` to indent all lines after the first.
165+ ///
166+ /// It is plausible that we should a step 4, but currently do not:
167+ /// 4. Replace all `\n{indent}\n` with `\n\n`
168+ fn indent ( indent : & str , s : & str ) -> String {
169+ format ! ( "{indent}{}\n " , s. trim_end_matches( '\n' ) ) . replace ( '\n' , & format ! ( "\n {}" , indent) )
96170}
0 commit comments