1111//! # Quasiquoter
1212//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
1313
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`.
1716
1817use { Delimiter , Literal , Spacing , Span , Term , TokenNode , TokenStream , TokenTree } ;
1918
20- use std:: iter;
2119use syntax:: ext:: base:: { ExtCtxt , ProcMacro } ;
2220use syntax:: parse:: token;
2321use syntax:: tokenstream;
2422
2523pub struct Quoter ;
2624
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 ( ) )
4727}
4828
4929pub trait Quote {
@@ -75,7 +55,6 @@ macro_rules! quote_tree {
7555 ( ( $( $t: tt) * ) ) => { TokenNode :: Group ( Delimiter :: Parenthesis , quote!( $( $t) * ) ) } ;
7656 ( [ $( $t: tt) * ] ) => { TokenNode :: Group ( Delimiter :: Bracket , quote!( $( $t) * ) ) } ;
7757 ( { $( $t: tt) * } ) => { TokenNode :: Group ( Delimiter :: Brace , quote!( $( $t) * ) ) } ;
78- ( rt) => { quote!( :: __internal:: __rt) } ;
7958 ( $t: tt) => { quote_tok!( $t) } ;
8059}
8160
@@ -96,9 +75,7 @@ impl ProcMacro for Quoter {
9675 let mut info = cx. current_expansion . mark . expn_info ( ) . unwrap ( ) ;
9776 info. callee . allow_internal_unstable = true ;
9877 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 )
10279 }
10380}
10481
@@ -113,102 +90,61 @@ impl<T: Quote> Quote for Option<T> {
11390
11491impl Quote for TokenStream {
11592 fn quote ( self ) -> TokenStream {
93+ if self . is_empty ( ) {
94+ return quote ! ( :: TokenStream :: empty( ) ) ;
95+ }
11696 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) ) , ) ) ;
127103 }
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!`" ) ,
131106 }
107+ } else if let TokenNode :: Op ( '$' , _) = tree. kind {
108+ after_dollar = true ;
109+ return None ;
110+ }
132111
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 > ( ) ;
136114
137115 if after_dollar {
138116 panic ! ( "unexpected trailing `$` in `quote!`" ) ;
139117 }
140118
141- stream
119+ quote ! ( [ ( unquote tokens ) ] . iter ( ) . cloned ( ) . collect :: < :: TokenStream > ( ) )
142120 }
143121}
144122
145123impl Quote for TokenTree {
146124 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+ }
175138 }
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- } ,
211139 }
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 ( )
212148 }
213149}
214150
@@ -226,52 +162,104 @@ impl Quote for usize {
226162
227163impl Quote for Term {
228164 fn quote ( self ) -> TokenStream {
229- quote ! ( rt :: Symbol :: intern( ( quote self . as_str( ) ) ) )
165+ quote ! ( :: Term :: intern( ( quote self . as_str( ) ) ) )
230166 }
231167}
232168
233169impl Quote for Span {
234170 fn quote ( self ) -> TokenStream {
235- quote ! ( rt :: span ( ) )
171+ quote ! ( :: Span :: default ( ) )
236172 }
237173}
238174
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+ }
245181
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
252218 } , ) *
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) )
256221 } , ) *
257222 }
258223 }
259224 }
260225
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+ }
262234 }
263235}
264236
237+ literals ! ( Byte , Char , Float , Str_ , Integer , ByteStr ; StrRaw , ByteStrRaw ) ;
238+
265239impl Quote for Delimiter {
266240 fn quote ( self ) -> TokenStream {
267241 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) ,* ) => {
269257 match self {
270- $( Delimiter :: $i => { quote!( rt :: token :: DelimToken :: $j ) } ) *
258+ $( Spacing :: $i => { quote!( :: Spacing :: $i ) } ) *
271259 }
272260 }
273261 }
274262
275- gen_match ! ( Parenthesis => Paren , Brace => Brace , Bracket => Bracket , None => NoDelim )
263+ gen_match ! ( Alone , Joint )
276264 }
277265}
0 commit comments