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:: tokenstream;
21
-
22
- /// This is the actual quote!() proc macro
23
- ///
24
- /// It is manually loaded in CStore::load_macro_untracked
25
- pub struct Quoter ;
26
-
27
- pub fn unquote < T : Into < TokenStream > + Clone > ( tokens : & T ) -> TokenStream {
28
- tokens. clone ( ) . into ( )
29
- }
30
-
31
- pub trait Quote {
32
- fn quote ( self ) -> TokenStream ;
33
- }
34
-
35
- macro_rules! tt2ts {
36
- ( $e: expr) => ( TokenStream :: from( TokenTree :: from( $e) ) )
37
- }
38
-
39
- macro_rules! quote_tok {
40
- ( , ) => { tt2ts!( Punct :: new( ',' , Spacing :: Alone ) ) } ;
41
- ( . ) => { tt2ts!( Punct :: new( '.' , Spacing :: Alone ) ) } ;
42
- ( : ) => { tt2ts!( Punct :: new( ':' , Spacing :: Alone ) ) } ;
43
- ( ; ) => { tt2ts!( Punct :: new( ';' , Spacing :: Alone ) ) } ;
44
- ( |) => { 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) * } ;
45
37
( :: ) => {
46
38
[
47
39
TokenTree :: from( Punct :: new( ':' , Spacing :: Joint ) ) ,
@@ -54,57 +46,45 @@ macro_rules! quote_tok {
54
46
} )
55
47
. collect:: <TokenStream >( )
56
48
} ;
57
- ( !) => { tt2ts!( Punct :: new( '!' , Spacing :: Alone ) ) } ;
58
- ( <) => { tt2ts!( Punct :: new( '<' , Spacing :: Alone ) ) } ;
59
- ( >) => { tt2ts!( Punct :: new( '>' , Spacing :: Alone ) ) } ;
60
- ( _) => { tt2ts!( Punct :: new( '_' , Spacing :: Alone ) ) } ;
61
- ( 0 ) => { tt2ts!( Literal :: i8_unsuffixed( 0 ) ) } ;
62
- ( & ) => { tt2ts!( Punct :: new( '&' , Spacing :: Alone ) ) } ;
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 )
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( ) ) ;
92
77
}
93
- }
94
-
95
- impl Quote for TokenStream {
96
- fn quote ( self ) -> TokenStream {
97
- if self . is_empty ( ) {
98
- return quote ! ( :: TokenStream :: new( ) ) ;
99
- }
100
- let mut after_dollar = false ;
101
- 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| {
102
82
if after_dollar {
103
83
after_dollar = false ;
104
84
match tree {
105
85
TokenTree :: Ident ( _) => {
106
- let tree = TokenStream :: from ( tree ) ;
107
- return Some ( quote ! ( :: __internal :: unquote ( & ( unquote tree) ) , ) ) ;
86
+ return Some ( quote ! ( Into :: < :: TokenStream > :: into (
87
+ Clone :: clone ( & ( @ tree) ) ) , ) ) ;
108
88
}
109
89
TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
110
90
_ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
@@ -116,116 +96,55 @@ impl Quote for TokenStream {
116
96
}
117
97
}
118
98
119
- Some ( quote ! ( :: TokenStream :: from( ( quote tree) ) , ) )
120
- } ) . flat_map ( |t| t. into_iter ( ) ) . collect :: < TokenStream > ( ) ;
121
-
122
- if after_dollar {
123
- panic ! ( "unexpected trailing `$` in `quote!`" ) ;
124
- }
125
-
126
- quote ! (
127
- [ ( unquote tokens) ] . iter( )
128
- . cloned( )
129
- . flat_map( |x| x. into_iter( ) )
130
- . collect:: <:: TokenStream >( )
131
- )
132
- }
133
- }
134
-
135
- impl Quote for TokenTree {
136
- fn quote ( self ) -> TokenStream {
137
- match self {
138
- TokenTree :: Punct ( tt) => quote ! ( :: TokenTree :: Punct ( ( quote tt) ) ) ,
139
- TokenTree :: Group ( tt) => quote ! ( :: TokenTree :: Group ( ( quote tt) ) ) ,
140
- TokenTree :: Ident ( tt) => quote ! ( :: TokenTree :: Ident ( ( quote tt) ) ) ,
141
- TokenTree :: Literal ( tt) => quote ! ( :: TokenTree :: Literal ( ( quote tt) ) ) ,
142
- }
143
- }
144
- }
145
-
146
- impl Quote for char {
147
- fn quote ( self ) -> TokenStream {
148
- TokenTree :: from ( Literal :: character ( self ) ) . into ( )
149
- }
150
- }
151
-
152
- impl < ' a > Quote for & ' a str {
153
- fn quote ( self ) -> TokenStream {
154
- TokenTree :: from ( Literal :: string ( self ) ) . into ( )
155
- }
156
- }
157
-
158
- impl Quote for u16 {
159
- fn quote ( self ) -> TokenStream {
160
- TokenTree :: from ( Literal :: u16_unsuffixed ( self ) ) . into ( )
161
- }
162
- }
163
-
164
- impl Quote for Group {
165
- fn quote ( self ) -> TokenStream {
166
- quote ! ( :: Group :: new( ( quote self . delimiter( ) ) , ( quote self . stream( ) ) ) )
167
- }
168
- }
169
-
170
- impl Quote for Punct {
171
- fn quote ( self ) -> TokenStream {
172
- quote ! ( :: Punct :: new( ( quote self . as_char( ) ) , ( quote self . spacing( ) ) ) )
173
- }
174
- }
175
-
176
- impl Quote for Ident {
177
- fn quote ( self ) -> TokenStream {
178
- quote ! ( :: Ident :: new( ( quote self . sym. as_str( ) ) , ( quote self . span( ) ) ) )
179
- }
180
- }
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 > ( ) ;
181
137
182
- impl Quote for Span {
183
- fn quote ( self ) -> TokenStream {
184
- quote ! ( :: Span :: def_site( ) )
138
+ if after_dollar {
139
+ panic ! ( "unexpected trailing `$` in `quote!`" ) ;
185
140
}
186
- }
187
-
188
- impl Quote for Literal {
189
- fn quote ( self ) -> TokenStream {
190
- quote ! { {
191
- let mut iter = ( quote self . to_string( ) )
192
- . parse:: <:: TokenStream >( )
193
- . unwrap( )
194
- . into_iter( ) ;
195
- if let ( Some ( :: TokenTree :: Literal ( mut lit) ) , None ) = ( iter. next( ) , iter. next( ) ) {
196
- lit. set_span( ( quote self . span) ) ;
197
- lit
198
- } else {
199
- unreachable!( )
200
- }
201
- } }
202
- }
203
- }
204
-
205
- impl Quote for Delimiter {
206
- fn quote ( self ) -> TokenStream {
207
- macro_rules! gen_match {
208
- ( $( $i: ident) ,* ) => {
209
- match self {
210
- $( Delimiter :: $i => { quote!( :: Delimiter :: $i) } ) *
211
- }
212
- }
213
- }
214
141
215
- gen_match ! ( Parenthesis , Brace , Bracket , None )
216
- }
142
+ quote ! ( [ ( @ tokens) ] . iter( ) . cloned( ) . collect:: <:: TokenStream >( ) )
217
143
}
218
144
219
- impl Quote for Spacing {
220
- fn quote ( self ) -> TokenStream {
221
- macro_rules! gen_match {
222
- ( $( $i: ident) ,* ) => {
223
- match self {
224
- $( Spacing :: $i => { quote!( :: Spacing :: $i) } ) *
225
- }
226
- }
227
- }
228
-
229
- gen_match ! ( Alone , Joint )
230
- }
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( ) )
231
150
}
0 commit comments