1
1
use std:: collections:: { VecDeque , HashMap } ;
2
2
use crate :: process:: { GroupCmds , Cmds , Cmd , FdOrFile } ;
3
3
4
- #[ doc( hidden) ]
5
- #[ macro_export]
6
- macro_rules! parse_string_literal {
7
- ( & $sl: expr; ) => {
8
- $sl
9
- } ;
10
- ( & $sl: expr; - $( $other: tt) * ) => {
11
- $crate:: parse_string_literal!{ & $sl; $( $other) * }
12
- } ;
13
- ( & $sl: expr; $cur: literal $( $other: tt) * ) => {
14
- let s = stringify!( $cur) ;
15
- // only save string literals
16
- if s. starts_with( "\" " ) || s. starts_with( "r" ) {
17
- $sl. push_back( $cur. to_string( ) ) ;
18
- }
19
- $crate:: parse_string_literal!{ & $sl; $( $other) * }
20
- } ;
21
- ( & $sl: expr; $cur: tt $( $other: tt) * ) => {
22
- $crate:: parse_string_literal!{ & $sl; $( $other) * }
23
- } ;
24
- ( $cur: tt $( $other: tt) * ) => { {
25
- let mut __str_lits = std:: collections:: VecDeque :: <String >:: new( ) ;
26
- $crate:: parse_string_literal!{ & __str_lits; $cur $( $other) * }
27
- } } ;
28
- }
29
-
30
4
#[ doc( hidden) ]
31
5
pub struct Parser {
32
6
str_lits : Option < VecDeque < String > > ,
33
- sym_table : Option < HashMap < String , String > > ,
7
+ sym_table : Option < HashMap < & ' static str , String > > ,
34
8
35
9
file : & ' static str ,
36
10
line : u32 ,
@@ -54,7 +28,7 @@ impl Parser {
54
28
self
55
29
}
56
30
57
- pub fn with_sym_table ( & mut self , sym_table : HashMap < String , String > ) -> & mut Self {
31
+ pub fn with_sym_table ( & mut self , sym_table : HashMap < & ' static str , String > ) -> & mut Self {
58
32
self . sym_table = Some ( sym_table) ;
59
33
self
60
34
}
@@ -65,6 +39,47 @@ impl Parser {
65
39
self
66
40
}
67
41
42
+ fn resolve_name ( src : & str , sym_table : & HashMap < & ' static str , String > , file : & str , line : u32 ) -> String {
43
+ let mut output = String :: new ( ) ;
44
+ let input: Vec < char > = src. chars ( ) . collect ( ) ;
45
+ let len = input. len ( ) ;
46
+
47
+ let mut i = 0 ;
48
+ while i < len {
49
+ if input[ i] == '$' && ( i == 0 || input[ i - 1 ] != '\\' ) {
50
+ i += 1 ;
51
+ let with_bracket = i < len && input[ i] == '{' ;
52
+ let mut var = String :: new ( ) ;
53
+ if with_bracket { i += 1 ; }
54
+ while i < len
55
+ && ( ( input[ i] >= 'a' && input[ i] <= 'z' )
56
+ || ( input[ i] >= 'A' && input[ i] <= 'Z' )
57
+ || ( input[ i] >= '0' && input[ i] <= '9' )
58
+ || ( input[ i] == '_' ) )
59
+ {
60
+ var. push ( input[ i] ) ;
61
+ i += 1 ;
62
+ }
63
+ if with_bracket {
64
+ if input[ i] != '}' {
65
+ panic ! ( "invalid name {}, {}:{}\n {}" , var, file, line, src) ;
66
+ }
67
+ } else {
68
+ i -= 1 ; // back off 1 char
69
+ }
70
+ match sym_table. get ( var. as_str ( ) ) {
71
+ None => panic ! ( "resolve {} failed, {}:{}\n {}" , var, file, line, src) ,
72
+ Some ( v) => output += v,
73
+ } ;
74
+ } else {
75
+ output. push ( input[ i] ) ;
76
+ }
77
+ i += 1 ;
78
+ }
79
+
80
+ output
81
+ }
82
+
68
83
pub fn parse ( & mut self ) -> GroupCmds {
69
84
let mut ret = GroupCmds :: new ( ) ;
70
85
let s: Vec < char > = self . src . chars ( ) . collect ( ) ;
@@ -173,10 +188,7 @@ impl Parser {
173
188
174
189
let mut arg1 = self . parse_normal_arg ( s, i) ;
175
190
if let Some ( sym_table) = self . sym_table . as_ref ( ) {
176
- arg1 = crate :: sym_table:: resolve_name ( & arg1,
177
- sym_table,
178
- & self . file ,
179
- self . line ) ;
191
+ arg1 = Parser :: resolve_name ( & arg1, sym_table, & self . file , self . line ) ;
180
192
}
181
193
arg += & arg1;
182
194
}
@@ -260,10 +272,7 @@ impl Parser {
260
272
261
273
let mut file = self . parse_normal_arg ( s, i) ;
262
274
if let Some ( sym_table) = self . sym_table . as_ref ( ) {
263
- file = crate :: sym_table:: resolve_name ( & file,
264
- sym_table,
265
- & self . file ,
266
- self . line ) ;
275
+ file = Parser :: resolve_name ( & file, sym_table, & self . file , self . line ) ;
267
276
}
268
277
FdOrFile :: File ( file, append)
269
278
}
@@ -318,14 +327,11 @@ impl Parser {
318
327
return str_lit;
319
328
}
320
329
321
- str_lit = self . str_lits . as_mut ( ) . unwrap ( ) . pop_front ( ) . unwrap ( ) ;
330
+ str_lit = self . str_lits . as_mut ( ) . unwrap ( ) . pop_front ( ) . unwrap ( ) . to_string ( ) ;
322
331
if is_raw {
323
332
return str_lit; // don't resolve names for raw string literals
324
333
} else {
325
- return crate :: sym_table:: resolve_name ( & str_lit,
326
- self . sym_table . as_ref ( ) . unwrap ( ) ,
327
- & self . file ,
328
- self . line ) ;
334
+ return Parser :: resolve_name ( & str_lit, self . sym_table . as_ref ( ) . unwrap ( ) , & self . file , self . line ) ;
329
335
}
330
336
}
331
337
}
@@ -334,18 +340,6 @@ impl Parser {
334
340
mod tests {
335
341
use super :: * ;
336
342
337
- #[ test]
338
- fn test_parse_string_literal ( ) {
339
- let str_lits1 = parse_string_literal ! ( ls "/tmp" "/" ) ;
340
- assert_eq ! ( str_lits1, [ "/tmp" , "/" ] ) ;
341
-
342
- let str_lits2 = parse_string_literal ! ( ping -c 3 r"127.0.0.1" ) ;
343
- assert_eq ! ( str_lits2, [ "127.0.0.1" ] ) ;
344
-
345
- let str_lits3 = parse_string_literal ! ( echo r#"rust"cmd_lib"# ) ;
346
- assert_eq ! ( str_lits3, [ "rust\" cmd_lib" ] ) ;
347
- }
348
-
349
343
#[ test]
350
344
fn test_parser_or_cmd ( ) {
351
345
assert ! ( Parser :: new( "ls /nofile || true; echo continue" )
0 commit comments