]> git.lizzy.rs Git - rust.git/blob - src/libproc_macro/quote.rs
Rename TokenStream::empty to TokenStream::new
[rust.git] / src / libproc_macro / quote.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! # Quasiquoter
12 //! This file contains the implementation internals of the quasiquoter provided by `quote!`.
13
14 //! This quasiquoter uses macros 2.0 hygiene to reliably access
15 //! items from `proc_macro`, to build a `proc_macro::TokenStream`.
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 pub struct Quoter;
25
26 pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
27     tokens.clone().into()
28 }
29
30 pub trait Quote {
31     fn quote(self) -> TokenStream;
32 }
33
34 macro_rules! tt2ts {
35     ($e:expr) => (TokenStream::from(TokenTree::from($e)))
36 }
37
38 macro_rules! quote_tok {
39     (,) => { tt2ts!(Punct::new(',', Spacing::Alone)) };
40     (.) => { tt2ts!(Punct::new('.', Spacing::Alone)) };
41     (:) => { tt2ts!(Punct::new(':', Spacing::Alone)) };
42     (|) => { tt2ts!(Punct::new('|', Spacing::Alone)) };
43     (::) => {
44         [
45             TokenTree::from(Punct::new(':', Spacing::Joint)),
46             TokenTree::from(Punct::new(':', Spacing::Alone)),
47         ].iter()
48             .cloned()
49             .map(|mut x| {
50                 x.set_span(Span::def_site());
51                 x
52             })
53             .collect::<TokenStream>()
54     };
55     (!) => { tt2ts!(Punct::new('!', Spacing::Alone)) };
56     (<) => { tt2ts!(Punct::new('<', Spacing::Alone)) };
57     (>) => { tt2ts!(Punct::new('>', Spacing::Alone)) };
58     (_) => { tt2ts!(Punct::new('_', Spacing::Alone)) };
59     (0) => { tt2ts!(Literal::i8_unsuffixed(0)) };
60     (&) => { tt2ts!(Punct::new('&', Spacing::Alone)) };
61     ($i:ident) => { tt2ts!(Ident::new(stringify!($i), Span::def_site())) };
62 }
63
64 macro_rules! quote_tree {
65     ((unquote $($t:tt)*)) => { $($t)* };
66     ((quote $($t:tt)*)) => { ($($t)*).quote() };
67     (($($t:tt)*)) => { tt2ts!(Group::new(Delimiter::Parenthesis, quote!($($t)*))) };
68     ([$($t:tt)*]) => { tt2ts!(Group::new(Delimiter::Bracket, quote!($($t)*))) };
69     ({$($t:tt)*}) => { tt2ts!(Group::new(Delimiter::Brace, quote!($($t)*))) };
70     ($t:tt) => { quote_tok!($t) };
71 }
72
73 macro_rules! quote {
74     () => { TokenStream::new() };
75     ($($t:tt)*) => {
76         [$(quote_tree!($t),)*].iter()
77             .cloned()
78             .flat_map(|x| x.into_iter())
79             .collect::<TokenStream>()
80     };
81 }
82
83 impl ProcMacro for Quoter {
84     fn expand<'cx>(&self, cx: &'cx mut ExtCtxt,
85                    _: ::syntax_pos::Span,
86                    stream: tokenstream::TokenStream)
87                    -> tokenstream::TokenStream {
88         let mut info = cx.current_expansion.mark.expn_info().unwrap();
89         info.callee.allow_internal_unstable = true;
90         cx.current_expansion.mark.set_expn_info(info);
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         }
101     }
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| {
111             if after_dollar {
112                 after_dollar = false;
113                 match tree {
114                     TokenTree::Ident(_) => {
115                         let tree = TokenStream::from(tree);
116                         return Some(quote!(::__internal::unquote(&(unquote tree)),));
117                     }
118                     TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
119                     _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
120                 }
121             } else if let TokenTree::Punct(ref tt) = tree {
122                 if tt.as_char() == '$' {
123                     after_dollar = true;
124                     return None;
125                 }
126             }
127
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         }
270
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         }
279     }
280 }
281
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     }
296 }
297
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     }
310 }