proc_macro: clean up the implementation of quasi-quoting.
This commit is contained in:
parent
d10d0b3e9e
commit
56aaa53278
4 changed files with 99 additions and 183 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {Delimiter, Group, Ident, Literal, Punct, Spacing, Span, 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()
|
||||
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()) };
|
||||
}
|
||||
|
||||
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)) };
|
||||
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>();
|
||||
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>();
|
||||
|
||||
if after_dollar {
|
||||
panic!("unexpected trailing `$` in `quote!`");
|
||||
}
|
||||
|
||||
quote!(
|
||||
[(unquote tokens)].iter()
|
||||
.cloned()
|
||||
.flat_map(|x| x.into_iter())
|
||||
.collect::<::TokenStream>()
|
||||
)
|
||||
if after_dollar {
|
||||
panic!("unexpected trailing `$` in `quote!`");
|
||||
}
|
||||
|
||||
quote!([(@ tokens)].iter().cloned().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())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Quote for Span {
|
||||
fn quote(self) -> TokenStream {
|
||||
quote!(::Span::def_site())
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ use syntax::ast;
|
|||
use syntax::attr;
|
||||
use syntax::codemap;
|
||||
use syntax::edition::Edition;
|
||||
use syntax::ext::base::SyntaxExtension;
|
||||
use syntax::parse::filemap_to_stream;
|
||||
use syntax::symbol::Symbol;
|
||||
use syntax_pos::{Span, NO_EXPANSION, FileName};
|
||||
|
|
@ -517,8 +516,11 @@ impl CrateStore for cstore::CStore {
|
|||
return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
|
||||
} else if data.name == "proc_macro" &&
|
||||
self.get_crate_data(id.krate).item_name(id.index) == "quote" {
|
||||
use syntax::ext::base::SyntaxExtension;
|
||||
use syntax_ext::proc_macro_impl::BangProcMacro;
|
||||
|
||||
let ext = SyntaxExtension::ProcMacro {
|
||||
expander: Box::new(::proc_macro::__internal::Quoter),
|
||||
expander: Box::new(BangProcMacro { inner: ::proc_macro::quote }),
|
||||
allow_internal_unstable: true,
|
||||
edition: data.root.edition,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#![feature(libc)]
|
||||
#![feature(macro_at_most_once_rep)]
|
||||
#![feature(proc_macro_internals)]
|
||||
#![feature(proc_macro_quote)]
|
||||
#![feature(quote)]
|
||||
#![feature(rustc_diagnostic_macros)]
|
||||
#![feature(slice_sort_by_cached_key)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue