1414//! This quasiquoter uses macros 2.0 hygiene to reliably access
1515//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
1616
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) * } ;
4537 ( :: ) => {
4638 [
4739 TokenTree :: from( Punct :: new( ':' , Spacing :: Joint ) ) ,
@@ -54,57 +46,45 @@ macro_rules! quote_tok {
5446 } )
5547 . collect:: <TokenStream >( )
5648 } ;
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) ) } ;
7450}
7551
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)`.
7660macro_rules! quote {
7761 ( ) => { TokenStream :: new( ) } ;
7862 ( $( $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 >( )
8366 } ;
8467}
8568
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( ) ) ;
9277 }
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| {
10282 if after_dollar {
10383 after_dollar = false ;
10484 match tree {
10585 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) ) ) , ) ) ;
10888 }
10989 TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
11090 _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
@@ -116,116 +96,55 @@ impl Quote for TokenStream {
11696 }
11797 }
11898
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 > ( ) ;
181137
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!`" ) ;
185140 }
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- }
214141
215- gen_match ! ( Parenthesis , Brace , Bracket , None )
216- }
142+ quote ! ( [ ( @ tokens) ] . iter( ) . cloned( ) . collect:: <:: TokenStream >( ) )
217143}
218144
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( ) )
231150}
0 commit comments