1
- mod reconstruct;
1
+ /*
2
+ Includes bits of code from the great & almighty Mara Bos and her amazing inline-python project.
3
+
4
+ Copyright (c) 2019-2020 Drones for Work B.V. and contributors
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ */
27
+
28
+ #![ feature( proc_macro_span) ]
2
29
3
30
use litrs:: StringLit ;
31
+ use proc_macro:: LineColumn ;
32
+ use proc_macro:: Span ;
4
33
use proc_macro2:: TokenStream ;
34
+ use proc_macro2:: { Delimiter , Ident , Spacing , TokenTree } ;
5
35
use quote:: quote;
6
- use crate :: reconstruct:: reconstruct;
36
+ use quote:: quote_spanned;
37
+ use std:: collections:: BTreeMap ;
38
+ use std:: fmt:: Write ;
39
+
7
40
extern crate proc_macro;
8
41
9
- #[ proc_macro]
10
- pub fn vbs ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream
11
- {
12
- let code = reconstruct ( TokenStream :: from ( input) ) ;
42
+ fn macro_impl ( input : TokenStream ) -> Result < TokenStream , TokenStream > {
43
+ let mut x = EmbedVbs :: new ( ) ;
13
44
14
- quote ! [ :: inline_vbs:: run_code( #code) ] . into ( )
45
+ x. add ( input) ?;
46
+
47
+ let EmbedVbs {
48
+ code, variables, ..
49
+ } = x;
50
+
51
+ let varname = variables. keys ( ) ;
52
+ let var = variables. values ( ) ;
53
+
54
+ Ok ( quote ! {
55
+ {
56
+ #( :: inline_vbs:: set_variable( #varname, #var) . unwrap( ) ; ) *
57
+ :: inline_vbs:: Runner :: run_code( #code)
58
+ }
59
+ } )
15
60
}
16
61
17
62
#[ proc_macro]
18
- pub fn vbs_ ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream
19
- {
20
- let code = reconstruct ( TokenStream :: from ( input) ) ;
63
+ pub fn vbs ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
64
+ proc_macro:: TokenStream :: from ( process_tokens ( input) )
65
+ }
66
+
67
+ fn process_tokens ( input : proc_macro:: TokenStream ) -> TokenStream {
68
+ match macro_impl ( TokenStream :: from ( input) ) {
69
+ Ok ( tokens) => tokens,
70
+ Err ( tokens) => tokens,
71
+ }
72
+ }
21
73
22
- quote ! [ :: inline_vbs:: run_expr( #code) ] . into ( )
74
+ #[ proc_macro]
75
+ pub fn vbs_ ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
76
+ let call = process_tokens ( input) ;
77
+ ( quote ! {
78
+ {
79
+ let res: Variant = #call;
80
+ res
81
+ }
82
+ } )
83
+ . into ( )
23
84
}
24
85
25
86
#[ proc_macro]
26
- pub fn vbs_raw ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream
27
- {
87
+ pub fn vbs_raw ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
28
88
let input = input. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
29
89
30
- if input. len ( ) != 1
31
- {
90
+ if input. len ( ) != 1 {
32
91
let msg = format ! ( "expected exactly one input token, got {}" , input. len( ) ) ;
33
92
return quote ! { compile_error!( #msg) } . into ( ) ;
34
93
}
35
94
36
- let string_lit = match StringLit :: try_from ( & input[ 0 ] )
37
- {
95
+ let string_lit = match StringLit :: try_from ( & input[ 0 ] ) {
38
96
Err ( e) => return e. to_compile_error ( ) ,
39
97
Ok ( lit) => lit,
40
98
} ;
41
99
42
100
let code = string_lit. value ( ) ;
43
101
44
102
quote ! [ :: inline_vbs:: run_code( #code) ] . into ( )
45
- }
103
+ }
104
+
105
+ struct EmbedVbs {
106
+ pub code : String ,
107
+ pub variables : BTreeMap < String , Ident > ,
108
+ pub first_indent : Option < usize > ,
109
+ pub loc : LineColumn ,
110
+ }
111
+
112
+ impl EmbedVbs {
113
+ pub fn new ( ) -> Self {
114
+ Self {
115
+ code : String :: new ( ) ,
116
+ variables : BTreeMap :: new ( ) ,
117
+ loc : LineColumn { line : 1 , column : 0 } ,
118
+ first_indent : None ,
119
+ }
120
+ }
121
+
122
+ fn add_whitespace ( & mut self , span : Span , loc : LineColumn ) -> Result < ( ) , TokenStream > {
123
+ #[ allow( clippy:: comparison_chain) ]
124
+ if loc. line > self . loc . line {
125
+ while loc. line > self . loc . line {
126
+ self . code . push ( '\n' ) ;
127
+ self . loc . line += 1 ;
128
+ }
129
+ let first_indent = * self . first_indent . get_or_insert ( loc. column ) ;
130
+ let indent = loc. column . checked_sub ( first_indent) ;
131
+ let indent = indent. ok_or_else (
132
+ || quote_spanned ! ( span. into( ) => compile_error!{ "Invalid indentation" } ) ,
133
+ ) ?;
134
+ for _ in 0 ..indent {
135
+ self . code . push ( ' ' ) ;
136
+ }
137
+ self . loc . column = loc. column ;
138
+ } else if loc. line == self . loc . line {
139
+ while loc. column > self . loc . column {
140
+ self . code . push ( ' ' ) ;
141
+ self . loc . column += 1 ;
142
+ }
143
+ }
144
+
145
+ Ok ( ( ) )
146
+ }
147
+
148
+ pub fn add ( & mut self , input : TokenStream ) -> Result < ( ) , TokenStream > {
149
+ let mut tokens = input. into_iter ( ) ;
150
+
151
+ while let Some ( token) = tokens. next ( ) {
152
+ let span = token. span ( ) . unwrap ( ) ;
153
+ self . add_whitespace ( span, span. start ( ) ) ?;
154
+
155
+ match & token {
156
+ TokenTree :: Group ( x) => {
157
+ let ( start, end) = match x. delimiter ( ) {
158
+ Delimiter :: Parenthesis => ( "(" , ")" ) ,
159
+ Delimiter :: Brace => ( "{" , "}" ) ,
160
+ Delimiter :: Bracket => ( "[" , "]" ) ,
161
+ Delimiter :: None => ( "" , "" ) ,
162
+ } ;
163
+ self . code . push_str ( start) ;
164
+ self . loc . column += start. len ( ) ;
165
+ self . add ( x. stream ( ) ) ?;
166
+ let mut end_loc = token. span ( ) . unwrap ( ) . end ( ) ;
167
+ end_loc. column = end_loc. column . saturating_sub ( end. len ( ) ) ;
168
+ self . add_whitespace ( span, end_loc) ?;
169
+ self . code . push_str ( end) ;
170
+ self . loc . column += end. len ( ) ;
171
+ }
172
+ TokenTree :: Punct ( x) => {
173
+ if x. as_char ( ) == '\'' && x. spacing ( ) == Spacing :: Joint {
174
+ let name = if let Some ( TokenTree :: Ident ( name) ) = tokens. next ( ) {
175
+ name
176
+ } else {
177
+ unreachable ! ( )
178
+ } ;
179
+ let name_str = format ! ( "RUST{}" , name) ;
180
+ self . code . push_str ( & name_str) ;
181
+ self . loc . column += name_str. chars ( ) . count ( ) - 4 + 1 ;
182
+ self . variables . entry ( name_str) . or_insert ( name) ;
183
+ } else {
184
+ self . code . push ( x. as_char ( ) ) ;
185
+ self . loc . column += 1 ;
186
+ }
187
+ }
188
+ TokenTree :: Ident ( x) => {
189
+ write ! ( & mut self . code, "{}" , x) . unwrap ( ) ;
190
+ self . loc = token. span ( ) . unwrap ( ) . end ( ) ;
191
+ }
192
+ TokenTree :: Literal ( x) => {
193
+ let s = x. to_string ( ) ;
194
+ self . code += & s;
195
+ self . loc = token. span ( ) . unwrap ( ) . end ( ) ;
196
+ }
197
+ }
198
+ }
199
+
200
+ Ok ( ( ) )
201
+ }
202
+ }
0 commit comments