1
- #![ feature( proc_macro_span) ]
2
-
3
- extern crate proc_macro;
4
- use std:: iter:: Peekable ;
5
1
use proc_macro2:: {
6
- token_stream:: IntoIter ,
7
2
TokenStream ,
8
3
TokenTree ,
9
- LineColumn ,
10
4
Ident ,
11
5
Literal ,
6
+ Span ,
12
7
} ;
13
8
use quote:: quote;
14
9
15
10
#[ proc_macro]
16
11
pub fn run_cmd ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
17
- let mut s = Source :: new ( ) ;
18
- let ( vars, lits, src) = s. reconstruct_from ( TokenStream :: from ( input) ) ;
12
+ let ( vars, lits, src) = source_text ( input) ;
19
13
quote ! (
20
14
cmd_lib_core:: run_cmd_with_ctx(
21
15
#src,
@@ -31,8 +25,7 @@ pub fn run_cmd(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
31
25
32
26
#[ proc_macro]
33
27
pub fn run_fun ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
34
- let mut s = Source :: new ( ) ;
35
- let ( vars, lits, src) = s. reconstruct_from ( TokenStream :: from ( input) ) ;
28
+ let ( vars, lits, src) = source_text ( input) ;
36
29
quote ! (
37
30
cmd_lib_core:: run_fun_with_ctx(
38
31
#src,
@@ -46,105 +39,87 @@ pub fn run_fun(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
46
39
) . into ( )
47
40
}
48
41
49
- // from inline-python: https://blog.m-ou.se/writing-python-inside-rust-1/
50
- struct Source {
51
- source : String ,
52
- line : usize ,
53
- col : usize ,
54
- sym_table_vars : Vec < Ident > ,
55
- str_lits : Vec < Literal > ,
56
- }
57
-
58
- impl Source {
59
- fn new ( ) -> Self {
60
- Self {
61
- source : String :: new ( ) ,
62
- sym_table_vars : vec ! [ ] ,
63
- str_lits : vec ! [ ] ,
64
- line : 1 ,
65
- col : 0 ,
42
+ fn span_location ( span : & Span ) -> ( usize , usize ) {
43
+ let s = format ! ( "{:?}" , span) ;
44
+ let mut start = 0 ;
45
+ let mut end = 0 ;
46
+ let mut parse_second = false ;
47
+ for c in s. chars ( ) . skip ( 6 ) {
48
+ if c == '.' {
49
+ parse_second = true ;
50
+ } else if c. is_ascii_digit ( ) {
51
+ let digit = c. to_digit ( 10 ) . unwrap ( ) as usize ;
52
+ if !parse_second {
53
+ start = start * 10 + digit;
54
+ } else {
55
+ end = end * 10 + digit;
56
+ }
66
57
}
67
58
}
59
+ ( start, end)
60
+ }
68
61
69
- fn reconstruct_from ( & mut self , input : TokenStream ) -> ( & Vec < Ident > , & Vec < Literal > , & str ) {
70
- let mut input = input . into_iter ( ) . peekable ( ) ;
71
- let mut with_captures = false ;
72
-
73
- if let Some ( t ) = input . peek ( ) {
74
- if let TokenTree :: Punct ( ch ) = & t {
75
- if ch . as_char ( ) == '|' {
76
- with_captures = true ;
77
- }
78
- }
79
- }
62
+ fn source_text ( input : proc_macro :: TokenStream ) -> ( Vec < Ident > , Vec < Literal > , String ) {
63
+ let input = TokenStream :: from ( input ) ;
64
+ let mut source_text = String :: new ( ) ;
65
+ let mut sym_table_vars : Vec < Ident > = vec ! [ ] ;
66
+ let mut str_lits : Vec < Literal > = vec ! [ ] ;
67
+ let mut end = 0 ;
68
+ let mut with_captures = false ;
69
+ let mut expect_var = false ;
70
+ for t in input {
71
+ let ( _start , _end ) = span_location ( & t . span ( ) ) ;
72
+ let src = t . to_string ( ) ;
80
73
if with_captures {
81
- self . parse_captures ( & mut input) ;
82
- }
83
-
84
- while let Some ( t) = input. next ( ) {
85
- if let TokenTree :: Group ( g) = t {
86
- let s = g. to_string ( ) ;
87
- self . add_whitespace ( g. span_open ( ) . start ( ) ) ;
88
- self . add_str ( & s[ ..1 ] ) ; // the '[', '{', or '('.
89
- self . reconstruct_from ( g. stream ( ) ) ;
90
- self . add_whitespace ( g. span_close ( ) . start ( ) ) ;
91
- self . add_str ( & s[ s. len ( ) - 1 ..] ) ; // the ']', '}', or ')'.
74
+ if expect_var {
75
+ if let TokenTree :: Ident ( var) = t {
76
+ sym_table_vars. push ( var) ;
77
+ }
92
78
} else {
93
- self . add_whitespace ( t. span ( ) . start ( ) ) ;
94
- if let TokenTree :: Literal ( lit) = t {
95
- let s = lit. to_string ( ) ;
96
- if s. starts_with ( "\" " ) || s. starts_with ( "r" ) {
97
- self . str_lits . push ( lit) ;
98
- }
99
- self . add_str ( & s) ;
100
- } else if let TokenTree :: Ident ( var) = t {
101
- if self . source . ends_with ( "$" ) || self . source . ends_with ( "${" ) {
102
- self . add_str ( & var. to_string ( ) ) ;
103
- self . sym_table_vars . push ( var) ;
79
+ if let TokenTree :: Punct ( ch) = t {
80
+ if ch. as_char ( ) == '|' {
81
+ with_captures = false ;
104
82
} else {
105
- self . add_str ( & var . to_string ( ) ) ;
83
+ assert_eq ! ( ch . as_char ( ) , ',' ) ;
106
84
}
107
- } else {
108
- self . add_str ( & t. to_string ( ) ) ;
109
85
}
110
86
}
111
- }
112
- ( & self . sym_table_vars , & self . str_lits , & self . source )
113
- }
114
-
115
- fn parse_captures ( & mut self , input : & mut Peekable < IntoIter > ) {
116
- input. next ( ) ;
117
- while let Some ( TokenTree :: Ident ( var) ) = input. next ( ) {
118
- self . sym_table_vars . push ( var) ;
119
- if let Some ( TokenTree :: Punct ( ch) ) = input. next ( ) {
120
- if ch. as_char ( ) == ',' {
87
+ expect_var = !expect_var;
88
+ } else if source_text. ends_with ( "$" ) {
89
+ if let TokenTree :: Group ( g) = t {
90
+ for tt in g. stream ( ) {
91
+ if let TokenTree :: Ident ( var) = tt {
92
+ source_text += "{" ;
93
+ source_text += & var. to_string ( ) ;
94
+ source_text += "}" ;
95
+ sym_table_vars. push ( var) ;
96
+ break ;
97
+ }
98
+ }
99
+ } else if let TokenTree :: Ident ( var) = t {
100
+ source_text += & var. to_string ( ) ;
101
+ sym_table_vars. push ( var) ;
102
+ }
103
+ } else {
104
+ if let TokenTree :: Punct ( ch) = t {
105
+ if end == 0 && ch. as_char ( ) == '|' {
106
+ with_captures = true ;
107
+ expect_var = true ;
121
108
continue ;
122
- } else if ch. as_char ( ) == '|' {
123
- break ;
124
- } else {
125
- unreachable ! ( ) ;
126
109
}
127
- } else {
128
- unreachable ! ( ) ;
110
+ } else if let TokenTree :: Literal ( lit) = t {
111
+ let s = lit. to_string ( ) ;
112
+ if s. starts_with ( "\" " ) || s. starts_with ( "r" ) {
113
+ str_lits. push ( lit) ;
114
+ }
129
115
}
130
- }
131
- }
132
116
133
- fn add_str ( & mut self , s : & str ) {
134
- // let's assume for now s contains no newlines.
135
- self . source += s;
136
- self . col += s. len ( ) ;
137
- }
138
-
139
- fn add_whitespace ( & mut self , loc : LineColumn ) {
140
- while self . line < loc. line {
141
- self . source . push ( '\n' ) ;
142
- self . line += 1 ;
143
- self . col = 0 ;
144
- }
145
- while self . col < loc. column {
146
- self . source . push ( ' ' ) ;
147
- self . col += 1 ;
117
+ if end != 0 && end < _start {
118
+ source_text += " " ;
119
+ }
120
+ source_text += & src;
148
121
}
122
+ end = _end;
149
123
}
124
+ ( sym_table_vars, str_lits, source_text)
150
125
}
0 commit comments