//! 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)),
})
.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!`"),
}
}
- 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())
}