11
11
//! # Quasiquoter
12
12
//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
13
13
14
- //! This quasiquoter uses macros 2.0 hygiene to reliably use items from `__rt`,
15
- //! including re-exported API `libsyntax`, to build a `syntax::tokenstream::TokenStream`
16
- //! and wrap it into a `proc_macro::TokenStream`.
14
+ //! This quasiquoter uses macros 2.0 hygiene to reliably access
15
+ //! items from `proc_macro`, to build a `proc_macro::TokenStream`.
17
16
18
17
use { Delimiter , Literal , Spacing , Span , Term , TokenNode , TokenStream , TokenTree } ;
19
18
20
- use std:: iter;
21
19
use syntax:: ext:: base:: { ExtCtxt , ProcMacro } ;
22
20
use syntax:: parse:: token;
23
21
use syntax:: tokenstream;
24
22
25
23
pub struct Quoter ;
26
24
27
- pub mod __rt {
28
- pub use syntax:: ast:: Ident ;
29
- pub use syntax:: parse:: token;
30
- pub use syntax:: symbol:: Symbol ;
31
- pub use syntax:: tokenstream:: { TokenStream , TokenStreamBuilder , TokenTree , Delimited } ;
32
-
33
- use syntax_pos:: Span ;
34
- use syntax_pos:: hygiene:: SyntaxContext ;
35
-
36
- pub fn unquote < T : Into < :: TokenStream > + Clone > ( tokens : & T ) -> TokenStream {
37
- T :: into ( tokens. clone ( ) ) . 0
38
- }
39
-
40
- pub fn ctxt ( ) -> SyntaxContext {
41
- :: __internal:: with_sess ( |( _, mark) | SyntaxContext :: empty ( ) . apply_mark ( mark) )
42
- }
43
-
44
- pub fn span ( ) -> Span {
45
- :: Span :: default ( ) . 0
46
- }
25
+ pub fn unquote < T : Into < TokenStream > + Clone > ( tokens : & T ) -> TokenStream {
26
+ T :: into ( tokens. clone ( ) )
47
27
}
48
28
49
29
pub trait Quote {
@@ -75,7 +55,6 @@ macro_rules! quote_tree {
75
55
( ( $( $t: tt) * ) ) => { TokenNode :: Group ( Delimiter :: Parenthesis , quote!( $( $t) * ) ) } ;
76
56
( [ $( $t: tt) * ] ) => { TokenNode :: Group ( Delimiter :: Bracket , quote!( $( $t) * ) ) } ;
77
57
( { $( $t: tt) * } ) => { TokenNode :: Group ( Delimiter :: Brace , quote!( $( $t) * ) ) } ;
78
- ( rt) => { quote!( :: __internal:: __rt) } ;
79
58
( $t: tt) => { quote_tok!( $t) } ;
80
59
}
81
60
@@ -96,9 +75,7 @@ impl ProcMacro for Quoter {
96
75
let mut info = cx. current_expansion . mark . expn_info ( ) . unwrap ( ) ;
97
76
info. callee . allow_internal_unstable = true ;
98
77
cx. current_expansion . mark . set_expn_info ( info) ;
99
- :: __internal:: set_sess ( cx, || quote ! ( :: TokenStream {
100
- 0 : ( quote TokenStream ( stream) )
101
- } ) . 0 )
78
+ :: __internal:: set_sess ( cx, || TokenStream ( stream) . quote ( ) . 0 )
102
79
}
103
80
}
104
81
@@ -113,102 +90,61 @@ impl<T: Quote> Quote for Option<T> {
113
90
114
91
impl Quote for TokenStream {
115
92
fn quote ( self ) -> TokenStream {
93
+ if self . is_empty ( ) {
94
+ return quote ! ( :: TokenStream :: empty( ) ) ;
95
+ }
116
96
let mut after_dollar = false ;
117
- let stream = iter:: once ( quote ! ( rt:: TokenStreamBuilder :: new( ) ) )
118
- . chain ( self . into_iter ( ) . filter_map ( |tree| {
119
- if after_dollar {
120
- after_dollar = false ;
121
- match tree. kind {
122
- TokenNode :: Term ( _) => {
123
- return Some ( quote ! ( . add( rt:: unquote( & ( unquote tree) ) ) ) ) ;
124
- }
125
- TokenNode :: Op ( '$' , _) => { }
126
- _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
97
+ let tokens = self . into_iter ( ) . filter_map ( |tree| {
98
+ if after_dollar {
99
+ after_dollar = false ;
100
+ match tree. kind {
101
+ TokenNode :: Term ( _) => {
102
+ return Some ( quote ! ( :: __internal:: unquote( & ( unquote tree) ) , ) ) ;
127
103
}
128
- } else if let TokenNode :: Op ( '$' , _) = tree. kind {
129
- after_dollar = true ;
130
- return None ;
104
+ TokenNode :: Op ( '$' , _) => { }
105
+ _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
131
106
}
107
+ } else if let TokenNode :: Op ( '$' , _) = tree. kind {
108
+ after_dollar = true ;
109
+ return None ;
110
+ }
132
111
133
- Some ( quote ! ( . add( rt:: TokenStream :: from( ( quote tree) ) ) ) )
134
- } ) )
135
- . chain ( iter:: once ( quote ! ( . build( ) ) ) ) . collect ( ) ;
112
+ Some ( quote ! ( :: TokenStream :: from( ( quote tree) ) , ) )
113
+ } ) . collect :: < TokenStream > ( ) ;
136
114
137
115
if after_dollar {
138
116
panic ! ( "unexpected trailing `$` in `quote!`" ) ;
139
117
}
140
118
141
- stream
119
+ quote ! ( [ ( unquote tokens ) ] . iter ( ) . cloned ( ) . collect :: < :: TokenStream > ( ) )
142
120
}
143
121
}
144
122
145
123
impl Quote for TokenTree {
146
124
fn quote ( self ) -> TokenStream {
147
- let ( op, kind) = match self . kind {
148
- TokenNode :: Op ( op, kind) => ( op, kind) ,
149
- TokenNode :: Group ( delimiter, tokens) => {
150
- return quote ! {
151
- rt:: TokenTree :: Delimited ( ( quote self . span) , rt:: Delimited {
152
- delim: ( quote delimiter) ,
153
- tts: ( quote tokens) . into( )
154
- } )
155
- } ;
156
- } ,
157
- TokenNode :: Term ( term) => {
158
- let variant = if term. as_str ( ) . starts_with ( "'" ) {
159
- quote ! ( Lifetime )
160
- } else {
161
- quote ! ( Ident )
162
- } ;
163
- return quote ! {
164
- rt:: TokenTree :: Token ( ( quote self . span) ,
165
- rt:: token:: ( unquote variant) ( rt:: Ident {
166
- name: ( quote term) ,
167
- ctxt: rt:: ctxt( )
168
- } ) )
169
- } ;
170
- }
171
- TokenNode :: Literal ( lit) => {
172
- return quote ! {
173
- rt:: TokenTree :: Token ( ( quote self . span) , ( quote lit) )
174
- } ;
125
+ quote ! ( :: TokenTree { span: ( quote self . span) , kind: ( quote self . kind) } )
126
+ }
127
+ }
128
+
129
+ impl Quote for TokenNode {
130
+ fn quote ( self ) -> TokenStream {
131
+ macro_rules! gen_match {
132
+ ( $( $i: ident( $( $arg: ident) ,+) ) ,* ) => {
133
+ match self {
134
+ $( TokenNode :: $i( $( $arg) ,+) => quote! {
135
+ :: TokenNode :: $i( $( ( quote $arg) ) ,+)
136
+ } , ) *
137
+ }
175
138
}
176
- } ;
177
-
178
- let token = match op {
179
- '=' => quote ! ( Eq ) ,
180
- '<' => quote ! ( Lt ) ,
181
- '>' => quote ! ( Gt ) ,
182
- '!' => quote ! ( Not ) ,
183
- '~' => quote ! ( Tilde ) ,
184
- '+' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Plus ) ) ,
185
- '-' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Minus ) ) ,
186
- '*' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Star ) ) ,
187
- '/' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Slash ) ) ,
188
- '%' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Percent ) ) ,
189
- '^' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Caret ) ) ,
190
- '&' => quote ! ( BinOp ( rt:: token:: BinOpToken :: And ) ) ,
191
- '|' => quote ! ( BinOp ( rt:: token:: BinOpToken :: Or ) ) ,
192
- '@' => quote ! ( At ) ,
193
- '.' => quote ! ( Dot ) ,
194
- ',' => quote ! ( Comma ) ,
195
- ';' => quote ! ( Semi ) ,
196
- ':' => quote ! ( Colon ) ,
197
- '#' => quote ! ( Pound ) ,
198
- '$' => quote ! ( Dollar ) ,
199
- '?' => quote ! ( Question ) ,
200
- '_' => quote ! ( Underscore ) ,
201
- _ => panic ! ( "unsupported character {}" , op) ,
202
- } ;
203
-
204
- match kind {
205
- Spacing :: Alone => quote ! {
206
- rt:: TokenTree :: Token ( ( quote self . span) , rt:: token:: ( unquote token) )
207
- } ,
208
- Spacing :: Joint => quote ! {
209
- rt:: TokenTree :: Token ( ( quote self . span) , rt:: token:: ( unquote token) ) . joint( )
210
- } ,
211
139
}
140
+
141
+ gen_match ! { Op ( op, kind) , Group ( delim, tokens) , Term ( term) , Literal ( lit) }
142
+ }
143
+ }
144
+
145
+ impl Quote for char {
146
+ fn quote ( self ) -> TokenStream {
147
+ TokenNode :: Literal ( Literal :: character ( self ) ) . into ( )
212
148
}
213
149
}
214
150
@@ -226,52 +162,104 @@ impl Quote for usize {
226
162
227
163
impl Quote for Term {
228
164
fn quote ( self ) -> TokenStream {
229
- quote ! ( rt :: Symbol :: intern( ( quote self . as_str( ) ) ) )
165
+ quote ! ( :: Term :: intern( ( quote self . as_str( ) ) ) )
230
166
}
231
167
}
232
168
233
169
impl Quote for Span {
234
170
fn quote ( self ) -> TokenStream {
235
- quote ! ( rt :: span ( ) )
171
+ quote ! ( :: Span :: default ( ) )
236
172
}
237
173
}
238
174
239
- impl Quote for Literal {
240
- fn quote ( self ) -> TokenStream {
241
- let ( lit , sfx ) = match self . 0 {
242
- token :: Literal ( lit , sfx ) => ( lit , sfx . map ( Term ) ) ,
243
- _ => panic ! ( "unsupported literal {:?}" , self . 0 ) ,
244
- } ;
175
+ macro_rules! literals {
176
+ ( $ ( $i : ident ) , * ; $ ( $raw : ident ) , * ) => {
177
+ pub enum LiteralKind {
178
+ $ ( $i , ) *
179
+ $ ( $raw ( usize ) , ) *
180
+ }
245
181
246
- macro_rules! gen_match {
247
- ( $( $i: ident) ,* ; $( $raw: ident) ,* ) => {
248
- match lit {
249
- $( token:: Lit :: $i( lit) => quote! {
250
- rt:: token:: Literal ( rt:: token:: Lit :: $i( ( quote Term ( lit) ) ) ,
251
- ( quote sfx) )
182
+ impl LiteralKind {
183
+ pub fn with_contents_and_suffix( self , contents: Term , suffix: Option <Term >)
184
+ -> Literal {
185
+ let contents = contents. 0 ;
186
+ let suffix = suffix. map( |t| t. 0 ) ;
187
+ match self {
188
+ $( LiteralKind :: $i => {
189
+ Literal ( token:: Literal ( token:: Lit :: $i( contents) , suffix) )
190
+ } ) *
191
+ $( LiteralKind :: $raw( n) => {
192
+ Literal ( token:: Literal ( token:: Lit :: $raw( contents, n) , suffix) )
193
+ } ) *
194
+ }
195
+ }
196
+ }
197
+
198
+ impl Literal {
199
+ fn kind_contents_and_suffix( self ) -> ( LiteralKind , Term , Option <Term >) {
200
+ let ( lit, suffix) = match self . 0 {
201
+ token:: Literal ( lit, suffix) => ( lit, suffix) ,
202
+ _ => panic!( "unsupported literal {:?}" , self . 0 ) ,
203
+ } ;
204
+
205
+ let ( kind, contents) = match lit {
206
+ $( token:: Lit :: $i( contents) => ( LiteralKind :: $i, contents) , ) *
207
+ $( token:: Lit :: $raw( contents, n) => ( LiteralKind :: $raw( n) , contents) , ) *
208
+ } ;
209
+ ( kind, Term ( contents) , suffix. map( Term ) )
210
+ }
211
+ }
212
+
213
+ impl Quote for LiteralKind {
214
+ fn quote( self ) -> TokenStream {
215
+ match self {
216
+ $( LiteralKind :: $i => quote! {
217
+ :: __internal:: LiteralKind :: $i
252
218
} , ) *
253
- $( token:: Lit :: $raw( lit, n) => quote! {
254
- rt:: token:: Literal ( rt:: token:: Lit :: $raw( ( quote Term ( lit) ) , ( quote n) ) ,
255
- ( quote sfx) )
219
+ $( LiteralKind :: $raw( n) => quote! {
220
+ :: __internal:: LiteralKind :: $raw( ( quote n) )
256
221
} , ) *
257
222
}
258
223
}
259
224
}
260
225
261
- gen_match ! ( Byte , Char , Float , Str_ , Integer , ByteStr ; StrRaw , ByteStrRaw )
226
+ impl Quote for Literal {
227
+ fn quote( self ) -> TokenStream {
228
+ let ( kind, contents, suffix) = self . kind_contents_and_suffix( ) ;
229
+ quote! {
230
+ ( quote kind) . with_contents_and_suffix( ( quote contents) , ( quote suffix) )
231
+ }
232
+ }
233
+ }
262
234
}
263
235
}
264
236
237
+ literals ! ( Byte , Char , Float , Str_ , Integer , ByteStr ; StrRaw , ByteStrRaw ) ;
238
+
265
239
impl Quote for Delimiter {
266
240
fn quote ( self ) -> TokenStream {
267
241
macro_rules! gen_match {
268
- ( $( $i: ident => $j: ident) ,* ) => {
242
+ ( $( $i: ident) ,* ) => {
243
+ match self {
244
+ $( Delimiter :: $i => { quote!( :: Delimiter :: $i) } ) *
245
+ }
246
+ }
247
+ }
248
+
249
+ gen_match ! ( Parenthesis , Brace , Bracket , None )
250
+ }
251
+ }
252
+
253
+ impl Quote for Spacing {
254
+ fn quote ( self ) -> TokenStream {
255
+ macro_rules! gen_match {
256
+ ( $( $i: ident) ,* ) => {
269
257
match self {
270
- $( Delimiter :: $i => { quote!( rt :: token :: DelimToken :: $j ) } ) *
258
+ $( Spacing :: $i => { quote!( :: Spacing :: $i ) } ) *
271
259
}
272
260
}
273
261
}
274
262
275
- gen_match ! ( Parenthesis => Paren , Brace => Brace , Bracket => Bracket , None => NoDelim )
263
+ gen_match ! ( Alone , Joint )
276
264
}
277
265
}
0 commit comments