]> git.proxmox.com Git - rustc.git/blame - src/libproc_macro/quote.rs
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / libproc_macro / quote.rs
CommitLineData
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 17use {Delimiter, Literal, Spacing, Span, Ident, Punct, Group, TokenStream, TokenTree};
041b39d2 18
041b39d2 19use syntax::ext::base::{ExtCtxt, ProcMacro};
abe05a73 20use syntax::parse::token;
94b46f34 21use syntax::symbol::Symbol;
abe05a73 22use syntax::tokenstream;
041b39d2
XL
23
24pub struct Quoter;
25
abe05a73 26pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
83c7162d 27 tokens.clone().into()
041b39d2
XL
28}
29
30pub trait Quote {
abe05a73 31 fn quote(self) -> TokenStream;
041b39d2
XL
32}
33
83c7162d
XL
34macro_rules! tt2ts {
35 ($e:expr) => (TokenStream::from(TokenTree::from($e)))
36}
37
041b39d2 38macro_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
64macro_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
73macro_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
83impl 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
95impl<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
104impl 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
144impl 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 155impl Quote for char {
abe05a73 156 fn quote(self) -> TokenStream {
83c7162d
XL
157 TokenTree::from(Literal::character(self)).into()
158 }
159}
abe05a73 160
83c7162d
XL
161impl<'a> Quote for &'a str {
162 fn quote(self) -> TokenStream {
163 TokenTree::from(Literal::string(self)).into()
041b39d2
XL
164 }
165}
166
83c7162d 167impl Quote for u16 {
abe05a73 168 fn quote(self) -> TokenStream {
83c7162d 169 TokenTree::from(Literal::u16_unsuffixed(self)).into()
041b39d2
XL
170 }
171}
172
83c7162d 173impl 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 179impl 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 185impl 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
191impl Quote for Span {
abe05a73
XL
192 fn quote(self) -> TokenStream {
193 quote!(::Span::def_site())
041b39d2
XL
194 }
195}
196
abe05a73
XL
197macro_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
282literals!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw);
283
284impl 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
298impl 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}