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.
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.
12 //! This file contains the implementation internals of the quasiquoter provided by `quote!`.
14 //! This quasiquoter uses macros 2.0 hygiene to reliably access
15 //! items from `proc_macro`, to build a `proc_macro::TokenStream`.
17 use {Delimiter, Literal, Spacing, Span, Ident, Punct, Group, TokenStream, TokenTree};
19 use syntax::ext::base::{ExtCtxt, ProcMacro};
20 use syntax::parse::token;
21 use syntax::symbol::Symbol;
22 use syntax::tokenstream;
26 pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
31 fn quote(self) -> TokenStream;
35 ($e:expr) => (TokenStream::from(TokenTree::from($e)))
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)) };
45 TokenTree::from(Punct::new(':', Spacing::Joint)),
46 TokenTree::from(Punct::new(':', Spacing::Alone)),
50 x.set_span(Span::def_site());
53 .collect::<TokenStream>()
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())) };
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) };
74 () => { TokenStream::new() };
76 [$(quote_tree!($t),)*].iter()
78 .flat_map(|x| x.into_iter())
79 .collect::<TokenStream>()
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)
95 impl<T: Quote> Quote for Option<T> {
96 fn quote(self) -> TokenStream {
98 Some(t) => quote!(Some((quote t))),
104 impl Quote for TokenStream {
105 fn quote(self) -> TokenStream {
107 return quote!(::TokenStream::new());
109 let mut after_dollar = false;
110 let tokens = self.into_iter().filter_map(|tree| {
112 after_dollar = false;
114 TokenTree::Ident(_) => {
115 let tree = TokenStream::from(tree);
116 return Some(quote!(::__internal::unquote(&(unquote tree)),));
118 TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
119 _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
121 } else if let TokenTree::Punct(ref tt) = tree {
122 if tt.as_char() == '$' {
128 Some(quote!(::TokenStream::from((quote tree)),))
129 }).flat_map(|t| t.into_iter()).collect::<TokenStream>();
132 panic!("unexpected trailing `$` in `quote!`");
136 [(unquote tokens)].iter()
138 .flat_map(|x| x.into_iter())
139 .collect::<::TokenStream>()
144 impl Quote for TokenTree {
145 fn quote(self) -> TokenStream {
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) )),
155 impl Quote for char {
156 fn quote(self) -> TokenStream {
157 TokenTree::from(Literal::character(self)).into()
161 impl<'a> Quote for &'a str {
162 fn quote(self) -> TokenStream {
163 TokenTree::from(Literal::string(self)).into()
168 fn quote(self) -> TokenStream {
169 TokenTree::from(Literal::u16_unsuffixed(self)).into()
173 impl Quote for Group {
174 fn quote(self) -> TokenStream {
175 quote!(::Group::new((quote self.delimiter()), (quote self.stream())))
179 impl Quote for Punct {
180 fn quote(self) -> TokenStream {
181 quote!(::Punct::new((quote self.as_char()), (quote self.spacing())))
185 impl Quote for Ident {
186 fn quote(self) -> TokenStream {
187 quote!(::Ident::new((quote self.sym.as_str()), (quote self.span())))
191 impl Quote for Span {
192 fn quote(self) -> TokenStream {
193 quote!(::Span::def_site())
197 macro_rules! literals {
198 ($($i:ident),*; $($raw:ident),*) => {
199 pub struct SpannedSymbol {
205 pub fn new(string: &str, span: Span) -> SpannedSymbol {
206 SpannedSymbol { sym: Symbol::intern(string), span }
210 impl Quote for SpannedSymbol {
211 fn quote(self) -> TokenStream {
212 quote!(::__internal::SpannedSymbol::new((quote self.sym.as_str()),
217 pub enum 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);
228 $(LiteralKind::$i => {
230 lit: token::Lit::$i(sym),
235 $(LiteralKind::$raw(n) => {
237 lit: token::Lit::$raw(sym, n),
247 fn kind_contents_and_suffix(self) -> (LiteralKind, SpannedSymbol, Option<SpannedSymbol>)
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),)*
253 let suffix = self.suffix.map(|sym| SpannedSymbol::new(&sym.as_str(), self.span()));
254 (kind, SpannedSymbol::new(&contents.as_str(), self.span()), suffix)
258 impl Quote for LiteralKind {
259 fn quote(self) -> TokenStream {
261 $(LiteralKind::$i => quote! {
262 ::__internal::LiteralKind::$i
264 $(LiteralKind::$raw(n) => quote! {
265 ::__internal::LiteralKind::$raw((quote n))
271 impl Quote for Literal {
272 fn quote(self) -> TokenStream {
273 let (kind, contents, suffix) = self.kind_contents_and_suffix();
275 (quote kind).with_contents_and_suffix((quote contents), (quote suffix))
282 literals!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw);
284 impl Quote for Delimiter {
285 fn quote(self) -> TokenStream {
286 macro_rules! gen_match {
289 $(Delimiter::$i => { quote!(::Delimiter::$i) })*
294 gen_match!(Parenthesis, Brace, Bracket, None)
298 impl Quote for Spacing {
299 fn quote(self) -> TokenStream {
300 macro_rules! gen_match {
303 $(Spacing::$i => { quote!(::Spacing::$i) })*
308 gen_match!(Alone, Joint)