]>
Commit | Line | Data |
---|---|---|
041b39d2 XL |
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 | ||
abe05a73 XL |
14 | //! This quasiquoter uses macros 2.0 hygiene to reliably access |
15 | //! items from `proc_macro`, to build a `proc_macro::TokenStream`. | |
16 | ||
94b46f34 | 17 | use {Delimiter, Literal, Spacing, Span, Ident, Punct, Group, TokenStream, TokenTree}; |
041b39d2 | 18 | |
041b39d2 | 19 | use syntax::ext::base::{ExtCtxt, ProcMacro}; |
abe05a73 | 20 | use syntax::parse::token; |
94b46f34 | 21 | use syntax::symbol::Symbol; |
abe05a73 | 22 | use syntax::tokenstream; |
041b39d2 XL |
23 | |
24 | pub struct Quoter; | |
25 | ||
abe05a73 | 26 | pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream { |
83c7162d | 27 | tokens.clone().into() |
041b39d2 XL |
28 | } |
29 | ||
30 | pub trait Quote { | |
abe05a73 | 31 | fn quote(self) -> TokenStream; |
041b39d2 XL |
32 | } |
33 | ||
83c7162d XL |
34 | macro_rules! tt2ts { |
35 | ($e:expr) => (TokenStream::from(TokenTree::from($e))) | |
36 | } | |
37 | ||
041b39d2 | 38 | macro_rules! quote_tok { |
94b46f34 XL |
39 | (,) => { tt2ts!(Punct::new(',', Spacing::Alone)) }; |
40 | (.) => { tt2ts!(Punct::new('.', Spacing::Alone)) }; | |
41 | (:) => { tt2ts!(Punct::new(':', Spacing::Alone)) }; | |
42 | (|) => { tt2ts!(Punct::new('|', Spacing::Alone)) }; | |
abe05a73 XL |
43 | (::) => { |
44 | [ | |
94b46f34 XL |
45 | TokenTree::from(Punct::new(':', Spacing::Joint)), |
46 | TokenTree::from(Punct::new(':', Spacing::Alone)), | |
83c7162d XL |
47 | ].iter() |
48 | .cloned() | |
49 | .map(|mut x| { | |
50 | x.set_span(Span::def_site()); | |
51 | x | |
52 | }) | |
53 | .collect::<TokenStream>() | |
abe05a73 | 54 | }; |
94b46f34 XL |
55 | (!) => { tt2ts!(Punct::new('!', Spacing::Alone)) }; |
56 | (<) => { tt2ts!(Punct::new('<', Spacing::Alone)) }; | |
57 | (>) => { tt2ts!(Punct::new('>', Spacing::Alone)) }; | |
58 | (_) => { tt2ts!(Punct::new('_', Spacing::Alone)) }; | |
83c7162d | 59 | (0) => { tt2ts!(Literal::i8_unsuffixed(0)) }; |
94b46f34 XL |
60 | (&) => { tt2ts!(Punct::new('&', Spacing::Alone)) }; |
61 | ($i:ident) => { tt2ts!(Ident::new(stringify!($i), Span::def_site())) }; | |
041b39d2 XL |
62 | } |
63 | ||
64 | macro_rules! quote_tree { | |
abe05a73 | 65 | ((unquote $($t:tt)*)) => { $($t)* }; |
041b39d2 | 66 | ((quote $($t:tt)*)) => { ($($t)*).quote() }; |
83c7162d XL |
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)*))) }; | |
abe05a73 | 70 | ($t:tt) => { quote_tok!($t) }; |
041b39d2 XL |
71 | } |
72 | ||
73 | macro_rules! quote { | |
94b46f34 | 74 | () => { TokenStream::new() }; |
abe05a73 | 75 | ($($t:tt)*) => { |
83c7162d XL |
76 | [$(quote_tree!($t),)*].iter() |
77 | .cloned() | |
78 | .flat_map(|x| x.into_iter()) | |
79 | .collect::<TokenStream>() | |
abe05a73 | 80 | }; |
041b39d2 XL |
81 | } |
82 | ||
83 | impl ProcMacro for Quoter { | |
abe05a73 XL |
84 | fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, |
85 | _: ::syntax_pos::Span, | |
86 | stream: tokenstream::TokenStream) | |
87 | -> tokenstream::TokenStream { | |
041b39d2 XL |
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); | |
abe05a73 | 91 | ::__internal::set_sess(cx, || TokenStream(stream).quote().0) |
041b39d2 XL |
92 | } |
93 | } | |
94 | ||
95 | impl<T: Quote> Quote for Option<T> { | |
abe05a73 XL |
96 | fn quote(self) -> TokenStream { |
97 | match self { | |
98 | Some(t) => quote!(Some((quote t))), | |
041b39d2 XL |
99 | None => quote!(None), |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | impl Quote for TokenStream { | |
abe05a73 XL |
105 | fn quote(self) -> TokenStream { |
106 | if self.is_empty() { | |
94b46f34 | 107 | return quote!(::TokenStream::new()); |
abe05a73 XL |
108 | } |
109 | let mut after_dollar = false; | |
110 | let tokens = self.into_iter().filter_map(|tree| { | |
111 | if after_dollar { | |
112 | after_dollar = false; | |
83c7162d | 113 | match tree { |
94b46f34 | 114 | TokenTree::Ident(_) => { |
83c7162d | 115 | let tree = TokenStream::from(tree); |
abe05a73 | 116 | return Some(quote!(::__internal::unquote(&(unquote tree)),)); |
041b39d2 | 117 | } |
94b46f34 | 118 | TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} |
041b39d2 XL |
119 | _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), |
120 | } | |
94b46f34 XL |
121 | } else if let TokenTree::Punct(ref tt) = tree { |
122 | if tt.as_char() == '$' { | |
83c7162d XL |
123 | after_dollar = true; |
124 | return None; | |
125 | } | |
041b39d2 XL |
126 | } |
127 | ||
abe05a73 | 128 | Some(quote!(::TokenStream::from((quote tree)),)) |
83c7162d | 129 | }).flat_map(|t| t.into_iter()).collect::<TokenStream>(); |
abe05a73 XL |
130 | |
131 | if after_dollar { | |
132 | panic!("unexpected trailing `$` in `quote!`"); | |
041b39d2 | 133 | } |
abe05a73 | 134 | |
83c7162d XL |
135 | quote!( |
136 | [(unquote tokens)].iter() | |
137 | .cloned() | |
138 | .flat_map(|x| x.into_iter()) | |
139 | .collect::<::TokenStream>() | |
140 | ) | |
041b39d2 XL |
141 | } |
142 | } | |
143 | ||
144 | impl Quote for TokenTree { | |
abe05a73 | 145 | fn quote(self) -> TokenStream { |
83c7162d | 146 | match self { |
94b46f34 | 147 | TokenTree::Punct(tt) => quote!(::TokenTree::Punct( (quote tt) )), |
83c7162d | 148 | TokenTree::Group(tt) => quote!(::TokenTree::Group( (quote tt) )), |
94b46f34 | 149 | TokenTree::Ident(tt) => quote!(::TokenTree::Ident( (quote tt) )), |
83c7162d XL |
150 | TokenTree::Literal(tt) => quote!(::TokenTree::Literal( (quote tt) )), |
151 | } | |
041b39d2 XL |
152 | } |
153 | } | |
154 | ||
83c7162d | 155 | impl Quote for char { |
abe05a73 | 156 | fn quote(self) -> TokenStream { |
83c7162d XL |
157 | TokenTree::from(Literal::character(self)).into() |
158 | } | |
159 | } | |
abe05a73 | 160 | |
83c7162d XL |
161 | impl<'a> Quote for &'a str { |
162 | fn quote(self) -> TokenStream { | |
163 | TokenTree::from(Literal::string(self)).into() | |
041b39d2 XL |
164 | } |
165 | } | |
166 | ||
83c7162d | 167 | impl Quote for u16 { |
abe05a73 | 168 | fn quote(self) -> TokenStream { |
83c7162d | 169 | TokenTree::from(Literal::u16_unsuffixed(self)).into() |
041b39d2 XL |
170 | } |
171 | } | |
172 | ||
83c7162d | 173 | impl Quote for Group { |
abe05a73 | 174 | fn quote(self) -> TokenStream { |
83c7162d | 175 | quote!(::Group::new((quote self.delimiter()), (quote self.stream()))) |
041b39d2 XL |
176 | } |
177 | } | |
178 | ||
94b46f34 | 179 | impl Quote for Punct { |
abe05a73 | 180 | fn quote(self) -> TokenStream { |
94b46f34 | 181 | quote!(::Punct::new((quote self.as_char()), (quote self.spacing()))) |
041b39d2 XL |
182 | } |
183 | } | |
184 | ||
94b46f34 | 185 | impl Quote for Ident { |
abe05a73 | 186 | fn quote(self) -> TokenStream { |
94b46f34 | 187 | quote!(::Ident::new((quote self.sym.as_str()), (quote self.span()))) |
041b39d2 XL |
188 | } |
189 | } | |
190 | ||
191 | impl Quote for Span { | |
abe05a73 XL |
192 | fn quote(self) -> TokenStream { |
193 | quote!(::Span::def_site()) | |
041b39d2 XL |
194 | } |
195 | } | |
196 | ||
abe05a73 XL |
197 | macro_rules! literals { |
198 | ($($i:ident),*; $($raw:ident),*) => { | |
94b46f34 XL |
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 | ||
abe05a73 XL |
217 | pub enum LiteralKind { |
218 | $($i,)* | |
83c7162d | 219 | $($raw(u16),)* |
abe05a73 XL |
220 | } |
221 | ||
222 | impl LiteralKind { | |
94b46f34 XL |
223 | pub fn with_contents_and_suffix(self, contents: SpannedSymbol, |
224 | suffix: Option<SpannedSymbol>) -> Literal { | |
83c7162d XL |
225 | let sym = contents.sym; |
226 | let suffix = suffix.map(|t| t.sym); | |
abe05a73 XL |
227 | match self { |
228 | $(LiteralKind::$i => { | |
83c7162d XL |
229 | Literal { |
230 | lit: token::Lit::$i(sym), | |
231 | suffix, | |
232 | span: contents.span, | |
233 | } | |
abe05a73 XL |
234 | })* |
235 | $(LiteralKind::$raw(n) => { | |
83c7162d XL |
236 | Literal { |
237 | lit: token::Lit::$raw(sym, n), | |
238 | suffix, | |
239 | span: contents.span, | |
240 | } | |
abe05a73 | 241 | })* |
041b39d2 XL |
242 | } |
243 | } | |
244 | } | |
245 | ||
abe05a73 | 246 | impl Literal { |
94b46f34 XL |
247 | fn kind_contents_and_suffix(self) -> (LiteralKind, SpannedSymbol, Option<SpannedSymbol>) |
248 | { | |
83c7162d | 249 | let (kind, contents) = match self.lit { |
abe05a73 XL |
250 | $(token::Lit::$i(contents) => (LiteralKind::$i, contents),)* |
251 | $(token::Lit::$raw(contents, n) => (LiteralKind::$raw(n), contents),)* | |
252 | }; | |
94b46f34 XL |
253 | let suffix = self.suffix.map(|sym| SpannedSymbol::new(&sym.as_str(), self.span())); |
254 | (kind, SpannedSymbol::new(&contents.as_str(), self.span()), suffix) | |
abe05a73 | 255 | } |
041b39d2 | 256 | } |
041b39d2 | 257 | |
abe05a73 XL |
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 | },)* | |
041b39d2 XL |
267 | } |
268 | } | |
269 | } | |
270 | ||
abe05a73 XL |
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 | } | |
041b39d2 XL |
279 | } |
280 | } | |
281 | ||
abe05a73 XL |
282 | literals!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw); |
283 | ||
284 | impl Quote for Delimiter { | |
285 | fn quote(self) -> TokenStream { | |
041b39d2 | 286 | macro_rules! gen_match { |
abe05a73 XL |
287 | ($($i:ident),*) => { |
288 | match self { | |
289 | $(Delimiter::$i => { quote!(::Delimiter::$i) })* | |
041b39d2 XL |
290 | } |
291 | } | |
292 | } | |
293 | ||
abe05a73 | 294 | gen_match!(Parenthesis, Brace, Bracket, None) |
041b39d2 XL |
295 | } |
296 | } | |
297 | ||
abe05a73 XL |
298 | impl Quote for Spacing { |
299 | fn quote(self) -> TokenStream { | |
041b39d2 XL |
300 | macro_rules! gen_match { |
301 | ($($i:ident),*) => { | |
abe05a73 XL |
302 | match self { |
303 | $(Spacing::$i => { quote!(::Spacing::$i) })* | |
041b39d2 XL |
304 | } |
305 | } | |
306 | } | |
307 | ||
abe05a73 | 308 | gen_match!(Alone, Joint) |
041b39d2 XL |
309 | } |
310 | } |