14
14
//! This quasiquoter uses macros 2.0 hygiene to reliably access
15
15
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
16
16
17
- use { Delimiter , Literal , Spacing , Span , Ident , Punct , Group , TokenStream , TokenTree } ;
18
-
19
- use syntax:: ext:: base:: { ExtCtxt , ProcMacro } ;
20
- use syntax:: parse:: token;
21
- use syntax:: symbol:: Symbol ;
22
- use syntax:: tokenstream;
23
-
24
- /// This is the actual quote!() proc macro
25
- ///
26
- /// It is manually loaded in CStore::load_macro_untracked
27
- pub struct Quoter ;
28
-
29
- pub fn unquote < T : Into < TokenStream > + Clone > ( tokens : & T ) -> TokenStream {
30
- tokens. clone ( ) . into ( )
31
- }
32
-
33
- pub trait Quote {
34
- fn quote ( self ) -> TokenStream ;
35
- }
36
-
37
- macro_rules! tt2ts {
38
- ( $e: expr) => ( TokenStream :: from( TokenTree :: from( $e) ) )
39
- }
40
-
41
- macro_rules! quote_tok {
42
- ( , ) => { tt2ts!( Punct :: new( ',' , Spacing :: Alone ) ) } ;
43
- ( . ) => { tt2ts!( Punct :: new( '.' , Spacing :: Alone ) ) } ;
44
- ( : ) => { tt2ts!( Punct :: new( ':' , Spacing :: Alone ) ) } ;
45
- ( |) => { tt2ts!( Punct :: new( '|' , Spacing :: Alone ) ) } ;
17
+ use { Delimiter , Group , Ident , Literal , Punct , Spacing , Span , TokenStream , TokenTree } ;
18
+
19
+ macro_rules! quote_tt {
20
+ ( ( $( $t: tt) * ) ) => { Group :: new( Delimiter :: Parenthesis , quote!( $( $t) * ) ) } ;
21
+ ( [ $( $t: tt) * ] ) => { Group :: new( Delimiter :: Bracket , quote!( $( $t) * ) ) } ;
22
+ ( { $( $t: tt) * } ) => { Group :: new( Delimiter :: Brace , quote!( $( $t) * ) ) } ;
23
+ ( , ) => { Punct :: new( ',' , Spacing :: Alone ) } ;
24
+ ( . ) => { Punct :: new( '.' , Spacing :: Alone ) } ;
25
+ ( : ) => { Punct :: new( ':' , Spacing :: Alone ) } ;
26
+ ( ; ) => { Punct :: new( ';' , Spacing :: Alone ) } ;
27
+ ( !) => { Punct :: new( '!' , Spacing :: Alone ) } ;
28
+ ( <) => { Punct :: new( '<' , Spacing :: Alone ) } ;
29
+ ( >) => { Punct :: new( '>' , Spacing :: Alone ) } ;
30
+ ( & ) => { Punct :: new( '&' , Spacing :: Alone ) } ;
31
+ ( =) => { Punct :: new( '=' , Spacing :: Alone ) } ;
32
+ ( $i: ident) => { Ident :: new( stringify!( $i) , Span :: def_site( ) ) } ;
33
+ }
34
+
35
+ macro_rules! quote_ts {
36
+ ( ( @ $( $t: tt) * ) ) => { $( $t) * } ;
46
37
( :: ) => {
47
38
[
48
39
TokenTree :: from( Punct :: new( ':' , Spacing :: Joint ) ) ,
@@ -55,65 +46,45 @@ macro_rules! quote_tok {
55
46
} )
56
47
. collect:: <TokenStream >( )
57
48
} ;
58
- ( !) => { tt2ts!( Punct :: new( '!' , Spacing :: Alone ) ) } ;
59
- ( <) => { tt2ts!( Punct :: new( '<' , Spacing :: Alone ) ) } ;
60
- ( >) => { tt2ts!( Punct :: new( '>' , Spacing :: Alone ) ) } ;
61
- ( _) => { tt2ts!( Punct :: new( '_' , Spacing :: Alone ) ) } ;
62
- ( 0 ) => { tt2ts!( Literal :: i8_unsuffixed( 0 ) ) } ;
63
- ( & ) => { tt2ts!( Punct :: new( '&' , Spacing :: Alone ) ) } ;
64
- ( $i: ident) => { tt2ts!( Ident :: new( stringify!( $i) , Span :: def_site( ) ) ) } ;
65
- }
66
-
67
- macro_rules! quote_tree {
68
- ( ( unquote $( $t: tt) * ) ) => { $( $t) * } ;
69
- ( ( quote $( $t: tt) * ) ) => { ( $( $t) * ) . quote( ) } ;
70
- ( ( $( $t: tt) * ) ) => { tt2ts!( Group :: new( Delimiter :: Parenthesis , quote!( $( $t) * ) ) ) } ;
71
- ( [ $( $t: tt) * ] ) => { tt2ts!( Group :: new( Delimiter :: Bracket , quote!( $( $t) * ) ) ) } ;
72
- ( { $( $t: tt) * } ) => { tt2ts!( Group :: new( Delimiter :: Brace , quote!( $( $t) * ) ) ) } ;
73
- ( $t: tt) => { quote_tok!( $t) } ;
49
+ ( $t: tt) => { TokenTree :: from( quote_tt!( $t) ) } ;
74
50
}
75
51
52
+ /// Simpler version of the real `quote!` macro, implemented solely
53
+ /// through `macro_rules`, for bootstrapping the real implementation
54
+ /// (see the `quote` function), which does not have access to the
55
+ /// real `quote!` macro due to the `proc_macro` crate not being
56
+ /// able to depend on itself.
57
+ ///
58
+ /// Note: supported tokens are a subset of the real `quote!`, but
59
+ /// unquoting is different: instead of `$x`, this uses `(@ expr)`.
76
60
macro_rules! quote {
77
61
( ) => { TokenStream :: new( ) } ;
78
62
( $( $t: tt) * ) => {
79
- [ $( quote_tree!( $t) , ) * ] . iter( )
80
- . cloned( )
81
- . flat_map( |x| x. into_iter( ) )
82
- . collect:: <TokenStream >( )
63
+ [
64
+ $( TokenStream :: from( quote_ts!( $t) ) , ) *
65
+ ] . iter( ) . cloned( ) . collect:: <TokenStream >( )
83
66
} ;
84
67
}
85
68
86
- impl ProcMacro for Quoter {
87
- fn expand < ' cx > ( & self , cx : & ' cx mut ExtCtxt ,
88
- _: :: syntax_pos:: Span ,
89
- stream : tokenstream:: TokenStream )
90
- -> tokenstream:: TokenStream {
91
- :: __internal:: set_sess ( cx, || TokenStream ( stream) . quote ( ) . 0 )
92
- }
93
- }
94
-
95
- impl < T : Quote > Quote for Option < T > {
96
- fn quote ( self ) -> TokenStream {
97
- match self {
98
- Some ( t) => quote ! ( Some ( ( quote t) ) ) ,
99
- None => quote ! ( None ) ,
100
- }
69
+ /// Quote a `TokenStream` into a `TokenStream`.
70
+ /// This is the actual `quote!()` proc macro.
71
+ ///
72
+ /// It is manually loaded in `CStore::load_macro_untracked`.
73
+ #[ unstable( feature = "proc_macro_quote" , issue = "38356" ) ]
74
+ pub fn quote ( stream : TokenStream ) -> TokenStream {
75
+ if stream. is_empty ( ) {
76
+ return quote ! ( :: TokenStream :: new( ) ) ;
101
77
}
102
- }
103
-
104
- impl Quote for TokenStream {
105
- fn quote ( self ) -> TokenStream {
106
- if self . is_empty ( ) {
107
- return quote ! ( :: TokenStream :: new( ) ) ;
108
- }
109
- let mut after_dollar = false ;
110
- let tokens = self . into_iter ( ) . filter_map ( |tree| {
78
+ let mut after_dollar = false ;
79
+ let tokens = stream
80
+ . into_iter ( )
81
+ . filter_map ( |tree| {
111
82
if after_dollar {
112
83
after_dollar = false ;
113
84
match tree {
114
85
TokenTree :: Ident ( _) => {
115
- let tree = TokenStream :: from ( tree ) ;
116
- return Some ( quote ! ( :: __internal :: unquote ( & ( unquote tree) ) , ) ) ;
86
+ return Some ( quote ! ( Into :: < :: TokenStream > :: into (
87
+ Clone :: clone ( & ( @ tree) ) ) , ) ) ;
117
88
}
118
89
TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
119
90
_ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
@@ -125,186 +96,55 @@ impl Quote for TokenStream {
125
96
}
126
97
}
127
98
128
- Some ( quote ! ( :: TokenStream :: from( ( quote tree) ) , ) )
129
- } ) . flat_map ( |t| t. into_iter ( ) ) . collect :: < TokenStream > ( ) ;
130
-
131
- if after_dollar {
132
- panic ! ( "unexpected trailing `$` in `quote!`" ) ;
133
- }
134
-
135
- quote ! (
136
- [ ( unquote tokens) ] . iter( )
137
- . cloned( )
138
- . flat_map( |x| x. into_iter( ) )
139
- . collect:: <:: TokenStream >( )
140
- )
141
- }
142
- }
143
-
144
- impl Quote for TokenTree {
145
- fn quote ( self ) -> TokenStream {
146
- match self {
147
- TokenTree :: Punct ( tt) => quote ! ( :: TokenTree :: Punct ( ( quote tt) ) ) ,
148
- TokenTree :: Group ( tt) => quote ! ( :: TokenTree :: Group ( ( quote tt) ) ) ,
149
- TokenTree :: Ident ( tt) => quote ! ( :: TokenTree :: Ident ( ( quote tt) ) ) ,
150
- TokenTree :: Literal ( tt) => quote ! ( :: TokenTree :: Literal ( ( quote tt) ) ) ,
151
- }
152
- }
153
- }
154
-
155
- impl Quote for char {
156
- fn quote ( self ) -> TokenStream {
157
- TokenTree :: from ( Literal :: character ( self ) ) . into ( )
158
- }
159
- }
160
-
161
- impl < ' a > Quote for & ' a str {
162
- fn quote ( self ) -> TokenStream {
163
- TokenTree :: from ( Literal :: string ( self ) ) . into ( )
164
- }
165
- }
166
-
167
- impl Quote for u16 {
168
- fn quote ( self ) -> TokenStream {
169
- TokenTree :: from ( Literal :: u16_unsuffixed ( self ) ) . into ( )
170
- }
171
- }
172
-
173
- impl Quote for Group {
174
- fn quote ( self ) -> TokenStream {
175
- quote ! ( :: Group :: new( ( quote self . delimiter( ) ) , ( quote self . stream( ) ) ) )
176
- }
177
- }
178
-
179
- impl Quote for Punct {
180
- fn quote ( self ) -> TokenStream {
181
- quote ! ( :: Punct :: new( ( quote self . as_char( ) ) , ( quote self . spacing( ) ) ) )
182
- }
183
- }
184
-
185
- impl Quote for Ident {
186
- fn quote ( self ) -> TokenStream {
187
- quote ! ( :: Ident :: new( ( quote self . sym. as_str( ) ) , ( quote self . span( ) ) ) )
188
- }
189
- }
190
-
191
- impl Quote for Span {
192
- fn quote ( self ) -> TokenStream {
193
- quote ! ( :: Span :: def_site( ) )
194
- }
195
- }
196
-
197
- macro_rules! literals {
198
- ( $( $i: ident) ,* ; $( $raw: ident) ,* ) => {
199
- pub struct SpannedSymbol {
200
- sym: Symbol ,
201
- span: Span ,
202
- }
203
-
204
- impl SpannedSymbol {
205
- pub fn new( string: & str , span: Span ) -> SpannedSymbol {
206
- SpannedSymbol { sym: Symbol :: intern( string) , span }
207
- }
208
- }
209
-
210
- impl Quote for SpannedSymbol {
211
- fn quote( self ) -> TokenStream {
212
- quote!( :: __internal:: SpannedSymbol :: new( ( quote self . sym. as_str( ) ) ,
213
- ( quote self . span) ) )
214
- }
215
- }
216
-
217
- pub enum LiteralKind {
218
- $( $i, ) *
219
- $( $raw( u16 ) , ) *
220
- }
221
-
222
- impl LiteralKind {
223
- pub fn with_contents_and_suffix( self , contents: SpannedSymbol ,
224
- suffix: Option <SpannedSymbol >) -> Literal {
225
- let sym = contents. sym;
226
- let suffix = suffix. map( |t| t. sym) ;
227
- match self {
228
- $( LiteralKind :: $i => {
229
- Literal {
230
- lit: token:: Lit :: $i( sym) ,
231
- suffix,
232
- span: contents. span,
233
- }
234
- } ) *
235
- $( LiteralKind :: $raw( n) => {
236
- Literal {
237
- lit: token:: Lit :: $raw( sym, n) ,
238
- suffix,
239
- span: contents. span,
240
- }
241
- } ) *
242
- }
243
- }
244
- }
245
-
246
- impl Literal {
247
- fn kind_contents_and_suffix( self ) -> ( LiteralKind , SpannedSymbol , Option <SpannedSymbol >)
248
- {
249
- let ( kind, contents) = match self . lit {
250
- $( token:: Lit :: $i( contents) => ( LiteralKind :: $i, contents) , ) *
251
- $( token:: Lit :: $raw( contents, n) => ( LiteralKind :: $raw( n) , contents) , ) *
252
- } ;
253
- let suffix = self . suffix. map( |sym| SpannedSymbol :: new( & sym. as_str( ) , self . span( ) ) ) ;
254
- ( kind, SpannedSymbol :: new( & contents. as_str( ) , self . span( ) ) , suffix)
255
- }
256
- }
257
-
258
- impl Quote for LiteralKind {
259
- fn quote( self ) -> TokenStream {
260
- match self {
261
- $( LiteralKind :: $i => quote! {
262
- :: __internal:: LiteralKind :: $i
263
- } , ) *
264
- $( LiteralKind :: $raw( n) => quote! {
265
- :: __internal:: LiteralKind :: $raw( ( quote n) )
266
- } , ) *
267
- }
268
- }
269
- }
99
+ Some ( quote ! ( :: TokenStream :: from( ( @ match tree {
100
+ TokenTree :: Punct ( tt) => quote!( :: TokenTree :: Punct ( :: Punct :: new(
101
+ ( @ TokenTree :: from( Literal :: character( tt. as_char( ) ) ) ) ,
102
+ ( @ match tt. spacing( ) {
103
+ Spacing :: Alone => quote!( :: Spacing :: Alone ) ,
104
+ Spacing :: Joint => quote!( :: Spacing :: Joint ) ,
105
+ } ) ,
106
+ ) ) ) ,
107
+ TokenTree :: Group ( tt) => quote!( :: TokenTree :: Group ( :: Group :: new(
108
+ ( @ match tt. delimiter( ) {
109
+ Delimiter :: Parenthesis => quote!( :: Delimiter :: Parenthesis ) ,
110
+ Delimiter :: Brace => quote!( :: Delimiter :: Brace ) ,
111
+ Delimiter :: Bracket => quote!( :: Delimiter :: Bracket ) ,
112
+ Delimiter :: None => quote!( :: Delimiter :: None ) ,
113
+ } ) ,
114
+ ( @ quote( tt. stream( ) ) ) ,
115
+ ) ) ) ,
116
+ TokenTree :: Ident ( tt) => quote!( :: TokenTree :: Ident ( :: Ident :: new(
117
+ ( @ TokenTree :: from( Literal :: string( & tt. to_string( ) ) ) ) ,
118
+ ( @ quote_span( tt. span( ) ) ) ,
119
+ ) ) ) ,
120
+ TokenTree :: Literal ( tt) => quote!( :: TokenTree :: Literal ( {
121
+ let mut iter = ( @ TokenTree :: from( Literal :: string( & tt. to_string( ) ) ) )
122
+ . parse:: <:: TokenStream >( )
123
+ . unwrap( )
124
+ . into_iter( ) ;
125
+ if let ( Some ( :: TokenTree :: Literal ( mut lit) ) , None ) =
126
+ ( iter. next( ) , iter. next( ) )
127
+ {
128
+ lit. set_span( ( @ quote_span( tt. span( ) ) ) ) ;
129
+ lit
130
+ } else {
131
+ unreachable!( )
132
+ }
133
+ } ) )
134
+ } ) ) , ) )
135
+ } )
136
+ . collect :: < TokenStream > ( ) ;
270
137
271
- impl Quote for Literal {
272
- fn quote( self ) -> TokenStream {
273
- let ( kind, contents, suffix) = self . kind_contents_and_suffix( ) ;
274
- quote! {
275
- ( quote kind) . with_contents_and_suffix( ( quote contents) , ( quote suffix) )
276
- }
277
- }
278
- }
138
+ if after_dollar {
139
+ panic ! ( "unexpected trailing `$` in `quote!`" ) ;
279
140
}
280
- }
281
141
282
- literals ! ( Byte , Char , Float , Str_ , Integer , ByteStr ; StrRaw , ByteStrRaw ) ;
283
-
284
- impl Quote for Delimiter {
285
- fn quote ( self ) -> TokenStream {
286
- macro_rules! gen_match {
287
- ( $( $i: ident) ,* ) => {
288
- match self {
289
- $( Delimiter :: $i => { quote!( :: Delimiter :: $i) } ) *
290
- }
291
- }
292
- }
293
-
294
- gen_match ! ( Parenthesis , Brace , Bracket , None )
295
- }
142
+ quote ! ( [ ( @ tokens) ] . iter( ) . cloned( ) . collect:: <:: TokenStream >( ) )
296
143
}
297
144
298
- impl Quote for Spacing {
299
- fn quote ( self ) -> TokenStream {
300
- macro_rules! gen_match {
301
- ( $( $i: ident) ,* ) => {
302
- match self {
303
- $( Spacing :: $i => { quote!( :: Spacing :: $i) } ) *
304
- }
305
- }
306
- }
307
-
308
- gen_match ! ( Alone , Joint )
309
- }
145
+ /// Quote a `Span` into a `TokenStream`.
146
+ /// This is needed to implement a custom quoter.
147
+ #[ unstable( feature = "proc_macro_quote" , issue = "38356" ) ]
148
+ pub fn quote_span ( _: Span ) -> TokenStream {
149
+ quote ! ( :: Span :: def_site( ) )
310
150
}
0 commit comments