diff options
Diffstat (limited to 'src/libproc_macro')
| -rw-r--r-- | src/libproc_macro/lib.rs | 14 | ||||
| -rw-r--r-- | src/libproc_macro/quote.rs | 265 |
2 files changed, 96 insertions, 183 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index ea4ca03a0a1..c88a245d3b3 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -145,6 +145,9 @@ impl fmt::Debug for TokenStream { } } +#[unstable(feature = "proc_macro_quote", issue = "38356")] +pub use quote::{quote, quote_span}; + /// Creates a token stream containing a single token tree. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl From<TokenTree> for TokenStream { @@ -237,7 +240,7 @@ pub mod token_stream { /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. /// -/// This is a dummy macro, the actual implementation is in quote::Quoter +/// This is a dummy macro, the actual implementation is in `quote::quote`.` #[unstable(feature = "proc_macro_quote", issue = "38356")] #[macro_export] macro_rules! quote { () => {} } @@ -246,13 +249,6 @@ macro_rules! quote { () => {} } #[doc(hidden)] mod quote; -/// Quote a `Span` into a `TokenStream`. -/// This is needed to implement a custom quoter. -#[unstable(feature = "proc_macro_quote", issue = "38356")] -pub fn quote_span(span: Span) -> TokenStream { - quote::Quote::quote(span) -} - /// A region of source code, along with macro expansion information. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] #[derive(Copy, Clone)] @@ -1364,8 +1360,6 @@ impl TokenTree { #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] pub mod __internal { - pub use quote::{Quoter, unquote}; - use std::cell::Cell; use std::ptr; diff --git a/src/libproc_macro/quote.rs b/src/libproc_macro/quote.rs index 8ae1d7b8525..7ae7b13a152 100644 --- a/src/libproc_macro/quote.rs +++ b/src/libproc_macro/quote.rs @@ -14,34 +14,26 @@ //! This quasiquoter uses macros 2.0 hygiene to reliably access //! items from `proc_macro`, to build a `proc_macro::TokenStream`. -use {Delimiter, Literal, Spacing, Span, Ident, Punct, Group, TokenStream, TokenTree}; - -use syntax::ext::base::{ExtCtxt, ProcMacro}; -use syntax::tokenstream; - -/// This is the actual quote!() proc macro -/// -/// It is manually loaded in CStore::load_macro_untracked -pub struct Quoter; - -pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream { - tokens.clone().into() -} - -pub trait Quote { - fn quote(self) -> TokenStream; -} - -macro_rules! tt2ts { - ($e:expr) => (TokenStream::from(TokenTree::from($e))) -} - -macro_rules! quote_tok { - (,) => { tt2ts!(Punct::new(',', Spacing::Alone)) }; - (.) => { tt2ts!(Punct::new('.', Spacing::Alone)) }; - (:) => { tt2ts!(Punct::new(':', Spacing::Alone)) }; - (;) => { tt2ts!(Punct::new(';', Spacing::Alone)) }; - (|) => { tt2ts!(Punct::new('|', Spacing::Alone)) }; +use {Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; + +macro_rules! quote_tt { + (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; + ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; + ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; + (,) => { Punct::new(',', Spacing::Alone) }; + (.) => { Punct::new('.', Spacing::Alone) }; + (:) => { Punct::new(':', Spacing::Alone) }; + (;) => { Punct::new(';', Spacing::Alone) }; + (!) => { Punct::new('!', Spacing::Alone) }; + (<) => { Punct::new('<', Spacing::Alone) }; + (>) => { Punct::new('>', Spacing::Alone) }; + (&) => { Punct::new('&', Spacing::Alone) }; + (=) => { Punct::new('=', Spacing::Alone) }; + ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; +} + +macro_rules! quote_ts { + ((@ $($t:tt)*)) => { $($t)* }; (::) => { [ TokenTree::from(Punct::new(':', Spacing::Joint)), @@ -54,57 +46,45 @@ macro_rules! quote_tok { }) .collect::<TokenStream>() }; - (!) => { tt2ts!(Punct::new('!', Spacing::Alone)) }; - (<) => { tt2ts!(Punct::new('<', Spacing::Alone)) }; - (>) => { tt2ts!(Punct::new('>', Spacing::Alone)) }; - (_) => { tt2ts!(Punct::new('_', Spacing::Alone)) }; - (0) => { tt2ts!(Literal::i8_unsuffixed(0)) }; - (&) => { tt2ts!(Punct::new('&', Spacing::Alone)) }; - (=) => { tt2ts!(Punct::new('=', Spacing::Alone)) }; - ($i:ident) => { tt2ts!(Ident::new(stringify!($i), Span::def_site())) }; -} - -macro_rules! quote_tree { - ((unquote $($t:tt)*)) => { $($t)* }; - ((quote $($t:tt)*)) => { ($($t)*).quote() }; - (($($t:tt)*)) => { tt2ts!(Group::new(Delimiter::Parenthesis, quote!($($t)*))) }; - ([$($t:tt)*]) => { tt2ts!(Group::new(Delimiter::Bracket, quote!($($t)*))) }; - ({$($t:tt)*}) => { tt2ts!(Group::new(Delimiter::Brace, quote!($($t)*))) }; - ($t:tt) => { quote_tok!($t) }; + ($t:tt) => { TokenTree::from(quote_tt!($t)) }; } +/// Simpler version of the real `quote!` macro, implemented solely +/// through `macro_rules`, for bootstrapping the real implementation +/// (see the `quote` function), which does not have access to the +/// real `quote!` macro due to the `proc_macro` crate not being +/// able to depend on itself. +/// +/// Note: supported tokens are a subset of the real `quote!`, but +/// unquoting is different: instead of `$x`, this uses `(@ expr)`. macro_rules! quote { () => { TokenStream::new() }; ($($t:tt)*) => { - [$(quote_tree!($t),)*].iter() - .cloned() - .flat_map(|x| x.into_iter()) - .collect::<TokenStream>() + [ + $(TokenStream::from(quote_ts!($t)),)* + ].iter().cloned().collect::<TokenStream>() }; } -impl ProcMacro for Quoter { - fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, - _: ::syntax_pos::Span, - stream: tokenstream::TokenStream) - -> tokenstream::TokenStream { - ::__internal::set_sess(cx, || TokenStream(stream).quote().0) +/// Quote a `TokenStream` into a `TokenStream`. +/// This is the actual `quote!()` proc macro. +/// +/// It is manually loaded in `CStore::load_macro_untracked`. +#[unstable(feature = "proc_macro_quote", issue = "38356")] +pub fn quote(stream: TokenStream) -> TokenStream { + if stream.is_empty() { + return quote!(::TokenStream::new()); } -} - -impl Quote for TokenStream { - fn quote(self) -> TokenStream { - if self.is_empty() { - return quote!(::TokenStream::new()); - } - let mut after_dollar = false; - let tokens = self.into_iter().filter_map(|tree| { + let mut after_dollar = false; + let tokens = stream + .into_iter() + .filter_map(|tree| { if after_dollar { after_dollar = false; match tree { TokenTree::Ident(_) => { - let tree = TokenStream::from(tree); - return Some(quote!(::__internal::unquote(&(unquote tree)),)); + return Some(quote!(Into::<::TokenStream>::into( + Clone::clone(&(@ tree))),)); } TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), @@ -116,116 +96,55 @@ impl Quote for TokenStream { } } - Some(quote!(::TokenStream::from((quote tree)),)) - }).flat_map(|t| t.into_iter()).collect::<TokenStream>(); - - if after_dollar { - panic!("unexpected trailing `$` in `quote!`"); - } - - quote!( - [(unquote tokens)].iter() - .cloned() - .flat_map(|x| x.into_iter()) - .collect::<::TokenStream>() - ) - } -} - -impl Quote for TokenTree { - fn quote(self) -> TokenStream { - match self { - TokenTree::Punct(tt) => quote!(::TokenTree::Punct( (quote tt) )), - TokenTree::Group(tt) => quote!(::TokenTree::Group( (quote tt) )), - TokenTree::Ident(tt) => quote!(::TokenTree::Ident( (quote tt) )), - TokenTree::Literal(tt) => quote!(::TokenTree::Literal( (quote tt) )), - } - } -} - -impl Quote for char { - fn quote(self) -> TokenStream { - TokenTree::from(Literal::character(self)).into() - } -} - -impl<'a> Quote for &'a str { - fn quote(self) -> TokenStream { - TokenTree::from(Literal::string(self)).into() - } -} - -impl Quote for u16 { - fn quote(self) -> TokenStream { - TokenTree::from(Literal::u16_unsuffixed(self)).into() - } -} - -impl Quote for Group { - fn quote(self) -> TokenStream { - quote!(::Group::new((quote self.delimiter()), (quote self.stream()))) - } -} - -impl Quote for Punct { - fn quote(self) -> TokenStream { - quote!(::Punct::new((quote self.as_char()), (quote self.spacing()))) - } -} - -impl Quote for Ident { - fn quote(self) -> TokenStream { - quote!(::Ident::new((quote self.sym.as_str()), (quote self.span()))) - } -} + Some(quote!(::TokenStream::from((@ match tree { + TokenTree::Punct(tt) => quote!(::TokenTree::Punct(::Punct::new( + (@ TokenTree::from(Literal::character(tt.as_char()))), + (@ match tt.spacing() { + Spacing::Alone => quote!(::Spacing::Alone), + Spacing::Joint => quote!(::Spacing::Joint), + }), + ))), + TokenTree::Group(tt) => quote!(::TokenTree::Group(::Group::new( + (@ match tt.delimiter() { + Delimiter::Parenthesis => quote!(::Delimiter::Parenthesis), + Delimiter::Brace => quote!(::Delimiter::Brace), + Delimiter::Bracket => quote!(::Delimiter::Bracket), + Delimiter::None => quote!(::Delimiter::None), + }), + (@ quote(tt.stream())), + ))), + TokenTree::Ident(tt) => quote!(::TokenTree::Ident(::Ident::new( + (@ TokenTree::from(Literal::string(&tt.to_string()))), + (@ quote_span(tt.span())), + ))), + TokenTree::Literal(tt) => quote!(::TokenTree::Literal({ + let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) + .parse::<::TokenStream>() + .unwrap() + .into_iter(); + if let (Some(::TokenTree::Literal(mut lit)), None) = + (iter.next(), iter.next()) + { + lit.set_span((@ quote_span(tt.span()))); + lit + } else { + unreachable!() + } + })) + })),)) + }) + .collect::<TokenStream>(); -impl Quote for Span { - fn quote(self) -> TokenStream { - quote!(::Span::def_site()) + if after_dollar { + panic!("unexpected trailing `$` in `quote!`"); } -} - -impl Quote for Literal { - fn quote(self) -> TokenStream { - quote! {{ - let mut iter = (quote self.to_string()) - .parse::<::TokenStream>() - .unwrap() - .into_iter(); - if let (Some(::TokenTree::Literal(mut lit)), None) = (iter.next(), iter.next()) { - lit.set_span((quote self.span)); - lit - } else { - unreachable!() - } - }} - } -} - -impl Quote for Delimiter { - fn quote(self) -> TokenStream { - macro_rules! gen_match { - ($($i:ident),*) => { - match self { - $(Delimiter::$i => { quote!(::Delimiter::$i) })* - } - } - } - gen_match!(Parenthesis, Brace, Bracket, None) - } + quote!([(@ tokens)].iter().cloned().collect::<::TokenStream>()) } -impl Quote for Spacing { - fn quote(self) -> TokenStream { - macro_rules! gen_match { - ($($i:ident),*) => { - match self { - $(Spacing::$i => { quote!(::Spacing::$i) })* - } - } - } - - gen_match!(Alone, Joint) - } +/// Quote a `Span` into a `TokenStream`. +/// This is needed to implement a custom quoter. +#[unstable(feature = "proc_macro_quote", issue = "38356")] +pub fn quote_span(_: Span) -> TokenStream { + quote!(::Span::def_site()) } |
