//! including re-exported API `libsyntax`, to build a `syntax::tokenstream::TokenStream`
//! and wrap it into a `proc_macro::TokenStream`.
-use syntax::ast::Ident;
+use {Delimiter, Literal, Spacing, Span, Term, TokenNode, TokenStream, TokenTree};
+
+use std::iter;
use syntax::ext::base::{ExtCtxt, ProcMacro};
-use syntax::parse::token::{self, Token, Lit};
-use syntax::symbol::Symbol;
-use syntax::tokenstream::{Delimited, TokenTree, TokenStream, TokenStreamBuilder};
-use syntax_pos::{DUMMY_SP, Span};
-use syntax_pos::hygiene::SyntaxContext;
+use syntax::parse::token;
+use syntax::tokenstream;
pub struct Quoter;
pub use syntax::parse::token;
pub use syntax::symbol::Symbol;
pub use syntax::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree, Delimited};
- pub use super::{ctxt, span};
+
+ use syntax_pos::Span;
+ use syntax_pos::hygiene::SyntaxContext;
pub fn unquote<T: Into<::TokenStream> + Clone>(tokens: &T) -> TokenStream {
T::into(tokens.clone()).0
}
-}
-pub fn ctxt() -> SyntaxContext {
- ::__internal::with_sess(|(_, mark)| SyntaxContext::empty().apply_mark(mark))
-}
+ pub fn ctxt() -> SyntaxContext {
+ ::__internal::with_sess(|(_, mark)| SyntaxContext::empty().apply_mark(mark))
+ }
-pub fn span() -> Span {
- ::Span::default().0
+ pub fn span() -> Span {
+ ::Span::default().0
+ }
}
pub trait Quote {
- fn quote(&self) -> TokenStream;
+ fn quote(self) -> TokenStream;
}
macro_rules! quote_tok {
- (,) => { Token::Comma };
- (.) => { Token::Dot };
- (:) => { Token::Colon };
- (::) => { Token::ModSep };
- (!) => { Token::Not };
- (<) => { Token::Lt };
- (>) => { Token::Gt };
- (_) => { Token::Underscore };
- (0) => { Token::Literal(token::Lit::Integer(Symbol::intern("0")), None) };
- (&) => { Token::BinOp(token::And) };
- ($i:ident) => { Token::Ident(Ident { name: Symbol::intern(stringify!($i)), ctxt: ctxt() }) };
+ (,) => { TokenNode::Op(',', Spacing::Alone) };
+ (.) => { TokenNode::Op('.', Spacing::Alone) };
+ (:) => { TokenNode::Op(':', Spacing::Alone) };
+ (::) => {
+ [
+ TokenNode::Op(':', Spacing::Joint),
+ TokenNode::Op(':', Spacing::Alone)
+ ].iter().cloned().collect::<TokenStream>()
+ };
+ (!) => { TokenNode::Op('!', Spacing::Alone) };
+ (<) => { TokenNode::Op('<', Spacing::Alone) };
+ (>) => { TokenNode::Op('>', Spacing::Alone) };
+ (_) => { TokenNode::Op('_', Spacing::Alone) };
+ (0) => { TokenNode::Literal(::Literal::integer(0)) };
+ (&) => { TokenNode::Op('&', Spacing::Alone) };
+ ($i:ident) => { TokenNode::Term(Term::intern(stringify!($i))) };
}
macro_rules! quote_tree {
- ((unquote $($t:tt)*)) => { TokenStream::from($($t)*) };
+ ((unquote $($t:tt)*)) => { $($t)* };
((quote $($t:tt)*)) => { ($($t)*).quote() };
- (($($t:tt)*)) => { delimit(token::Paren, quote!($($t)*)) };
- ([$($t:tt)*]) => { delimit(token::Bracket, quote!($($t)*)) };
- ({$($t:tt)*}) => { delimit(token::Brace, quote!($($t)*)) };
+ (($($t:tt)*)) => { TokenNode::Group(Delimiter::Parenthesis, quote!($($t)*)) };
+ ([$($t:tt)*]) => { TokenNode::Group(Delimiter::Bracket, quote!($($t)*)) };
+ ({$($t:tt)*}) => { TokenNode::Group(Delimiter::Brace, quote!($($t)*)) };
(rt) => { quote!(::__internal::__rt) };
- ($t:tt) => { TokenStream::from(TokenTree::Token(span(), quote_tok!($t))) };
-}
-
-fn delimit(delim: token::DelimToken, stream: TokenStream) -> TokenStream {
- TokenTree::Delimited(span(), Delimited { delim: delim, tts: stream.into() }).into()
+ ($t:tt) => { quote_tok!($t) };
}
macro_rules! quote {
() => { TokenStream::empty() };
- ($($t:tt)*) => { [ $( quote_tree!($t), )* ].iter().cloned().collect::<TokenStream>() };
+ ($($t:tt)*) => {
+ [
+ $(TokenStream::from(quote_tree!($t)),)*
+ ].iter().cloned().collect::<TokenStream>()
+ };
}
impl ProcMacro for Quoter {
- fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, _: Span, stream: TokenStream) -> TokenStream {
+ fn expand<'cx>(&self, cx: &'cx mut ExtCtxt,
+ _: ::syntax_pos::Span,
+ stream: tokenstream::TokenStream)
+ -> tokenstream::TokenStream {
let mut info = cx.current_expansion.mark.expn_info().unwrap();
info.callee.allow_internal_unstable = true;
cx.current_expansion.mark.set_expn_info(info);
- ::__internal::set_sess(cx, || quote!(::TokenStream { 0: (quote stream) }))
+ ::__internal::set_sess(cx, || quote!(::TokenStream {
+ 0: (quote TokenStream(stream))
+ }).0)
}
}
impl<T: Quote> Quote for Option<T> {
- fn quote(&self) -> TokenStream {
- match *self {
- Some(ref t) => quote!(Some((quote t))),
+ fn quote(self) -> TokenStream {
+ match self {
+ Some(t) => quote!(Some((quote t))),
None => quote!(None),
}
}
}
impl Quote for TokenStream {
- fn quote(&self) -> TokenStream {
- let mut builder = TokenStreamBuilder::new();
- builder.push(quote!(rt::TokenStreamBuilder::new()));
-
- let mut trees = self.trees();
- loop {
- let (mut tree, mut is_joint) = match trees.next_as_stream() {
- Some(next) => next.as_tree(),
- None => return builder.add(quote!(.build())).build(),
- };
- if let TokenTree::Token(_, Token::Dollar) = tree {
- let (next_tree, next_is_joint) = match trees.next_as_stream() {
- Some(next) => next.as_tree(),
- None => panic!("unexpected trailing `$` in `quote!`"),
- };
- match next_tree {
- TokenTree::Token(_, Token::Ident(..)) => {
- builder.push(quote!(.add(rt::unquote(&(unquote next_tree)))));
- continue
+ fn quote(self) -> TokenStream {
+ let mut after_dollar = false;
+ let stream = iter::once(quote!(rt::TokenStreamBuilder::new()))
+ .chain(self.into_iter().filter_map(|tree| {
+ if after_dollar {
+ after_dollar = false;
+ match tree.kind {
+ TokenNode::Term(_) => {
+ return Some(quote!(.add(rt::unquote(&(unquote tree)))));
+ }
+ TokenNode::Op('$', _) => {}
+ _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
}
- TokenTree::Token(_, Token::Dollar) => {
- tree = next_tree;
- is_joint = next_is_joint;
- }
- _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
+ } else if let TokenNode::Op('$', _) = tree.kind {
+ after_dollar = true;
+ return None;
}
- }
- builder.push(match is_joint {
- true => quote!(.add((quote tree).joint())),
- false => quote!(.add(rt::TokenStream::from((quote tree)))),
- });
+ Some(quote!(.add(rt::TokenStream::from((quote tree)))))
+ }))
+ .chain(iter::once(quote!(.build()))).collect();
+
+ if after_dollar {
+ panic!("unexpected trailing `$` in `quote!`");
}
+
+ stream
}
}
impl Quote for TokenTree {
- fn quote(&self) -> TokenStream {
- match *self {
- TokenTree::Token(span, ref token) => quote! {
- rt::TokenTree::Token((quote span), (quote token))
+ fn quote(self) -> TokenStream {
+ let (op, kind) = match self.kind {
+ TokenNode::Op(op, kind) => (op, kind),
+ TokenNode::Group(delimiter, tokens) => {
+ return quote! {
+ rt::TokenTree::Delimited((quote self.span), rt::Delimited {
+ delim: (quote delimiter),
+ tts: (quote tokens).into()
+ })
+ };
+ },
+ TokenNode::Term(term) => {
+ let variant = if term.as_str().starts_with("'") {
+ quote!(Lifetime)
+ } else {
+ quote!(Ident)
+ };
+ return quote! {
+ rt::TokenTree::Token((quote self.span),
+ rt::token::(unquote variant)(rt::Ident {
+ name: (quote term),
+ ctxt: rt::ctxt()
+ }))
+ };
+ }
+ TokenNode::Literal(lit) => {
+ return quote! {
+ rt::TokenTree::Token((quote self.span), (quote lit))
+ };
+ }
+ };
+
+ let token = match op {
+ '=' => quote!(Eq),
+ '<' => quote!(Lt),
+ '>' => quote!(Gt),
+ '!' => quote!(Not),
+ '~' => quote!(Tilde),
+ '+' => quote!(BinOp(rt::token::BinOpToken::Plus)),
+ '-' => quote!(BinOp(rt::token::BinOpToken::Minus)),
+ '*' => quote!(BinOp(rt::token::BinOpToken::Star)),
+ '/' => quote!(BinOp(rt::token::BinOpToken::Slash)),
+ '%' => quote!(BinOp(rt::token::BinOpToken::Percent)),
+ '^' => quote!(BinOp(rt::token::BinOpToken::Caret)),
+ '&' => quote!(BinOp(rt::token::BinOpToken::And)),
+ '|' => quote!(BinOp(rt::token::BinOpToken::Or)),
+ '@' => quote!(At),
+ '.' => quote!(Dot),
+ ',' => quote!(Comma),
+ ';' => quote!(Semi),
+ ':' => quote!(Colon),
+ '#' => quote!(Pound),
+ '$' => quote!(Dollar),
+ '?' => quote!(Question),
+ '_' => quote!(Underscore),
+ _ => panic!("unsupported character {}", op),
+ };
+
+ match kind {
+ Spacing::Alone => quote! {
+ rt::TokenTree::Token((quote self.span), rt::token::(unquote token))
},
- TokenTree::Delimited(span, ref delimited) => quote! {
- rt::TokenTree::Delimited((quote span), (quote delimited))
+ Spacing::Joint => quote! {
+ rt::TokenTree::Token((quote self.span), rt::token::(unquote token)).joint()
},
}
}
}
-impl Quote for Delimited {
- fn quote(&self) -> TokenStream {
- quote!(rt::Delimited { delim: (quote self.delim), tts: (quote self.stream()).into() })
- }
-}
-
impl<'a> Quote for &'a str {
- fn quote(&self) -> TokenStream {
- TokenTree::Token(span(), Token::Literal(token::Lit::Str_(Symbol::intern(self)), None))
- .into()
+ fn quote(self) -> TokenStream {
+ TokenNode::Literal(Literal::string(self)).into()
}
}
impl Quote for usize {
- fn quote(&self) -> TokenStream {
- let integer_symbol = Symbol::intern(&self.to_string());
- TokenTree::Token(DUMMY_SP, Token::Literal(token::Lit::Integer(integer_symbol), None))
- .into()
- }
-}
-
-impl Quote for Ident {
- fn quote(&self) -> TokenStream {
- quote!(rt::Ident { name: (quote self.name), ctxt: rt::ctxt() })
+ fn quote(self) -> TokenStream {
+ TokenNode::Literal(Literal::integer(self as i128)).into()
}
}
-impl Quote for Symbol {
- fn quote(&self) -> TokenStream {
- quote!(rt::Symbol::intern((quote &*self.as_str())))
+impl Quote for Term {
+ fn quote(self) -> TokenStream {
+ quote!(rt::Symbol::intern((quote self.as_str())))
}
}
impl Quote for Span {
- fn quote(&self) -> TokenStream {
+ fn quote(self) -> TokenStream {
quote!(rt::span())
}
}
-impl Quote for Token {
- fn quote(&self) -> TokenStream {
- macro_rules! gen_match {
- ($($i:ident),*; $($t:tt)*) => {
- match *self {
- $( Token::$i => quote!(rt::token::$i), )*
- $( $t )*
- }
- }
- }
-
- gen_match! {
- Eq, Lt, Le, EqEq, Ne, Ge, Gt, AndAnd, OrOr, Not, Tilde, At, Dot, DotDot, DotDotDot,
- DotDotEq, Comma, Semi, Colon, ModSep, RArrow, LArrow, FatArrow, Pound, Dollar,
- Question, Underscore;
-
- Token::OpenDelim(delim) => quote!(rt::token::OpenDelim((quote delim))),
- Token::CloseDelim(delim) => quote!(rt::token::CloseDelim((quote delim))),
- Token::BinOp(tok) => quote!(rt::token::BinOp((quote tok))),
- Token::BinOpEq(tok) => quote!(rt::token::BinOpEq((quote tok))),
- Token::Ident(ident) => quote!(rt::token::Ident((quote ident))),
- Token::Lifetime(ident) => quote!(rt::token::Lifetime((quote ident))),
- Token::Literal(lit, sfx) => quote!(rt::token::Literal((quote lit), (quote sfx))),
- _ => panic!("Unhandled case!"),
- }
- }
-}
-
-impl Quote for token::BinOpToken {
- fn quote(&self) -> TokenStream {
- macro_rules! gen_match {
- ($($i:ident),*) => {
- match *self {
- $( token::BinOpToken::$i => quote!(rt::token::BinOpToken::$i), )*
- }
- }
- }
-
- gen_match!(Plus, Minus, Star, Slash, Percent, Caret, And, Or, Shl, Shr)
- }
-}
+impl Quote for Literal {
+ fn quote(self) -> TokenStream {
+ let (lit, sfx) = match self.0 {
+ token::Literal(lit, sfx) => (lit, sfx.map(Term)),
+ _ => panic!("unsupported literal {:?}", self.0),
+ };
-impl Quote for Lit {
- fn quote(&self) -> TokenStream {
macro_rules! gen_match {
($($i:ident),*; $($raw:ident),*) => {
- match *self {
- $( Lit::$i(lit) => quote!(rt::token::Lit::$i((quote lit))), )*
- $( Lit::$raw(lit, n) => {
- quote!(::syntax::parse::token::Lit::$raw((quote lit), (quote n)))
- })*
+ match lit {
+ $(token::Lit::$i(lit) => quote! {
+ rt::token::Literal(rt::token::Lit::$i((quote Term(lit))),
+ (quote sfx))
+ },)*
+ $(token::Lit::$raw(lit, n) => quote! {
+ rt::token::Literal(rt::token::Lit::$raw((quote Term(lit)), (quote n)),
+ (quote sfx))
+ },)*
}
}
}
}
}
-impl Quote for token::DelimToken {
- fn quote(&self) -> TokenStream {
+impl Quote for Delimiter {
+ fn quote(self) -> TokenStream {
macro_rules! gen_match {
- ($($i:ident),*) => {
- match *self {
- $(token::DelimToken::$i => { quote!(rt::token::DelimToken::$i) })*
+ ($($i:ident => $j:ident),*) => {
+ match self {
+ $(Delimiter::$i => { quote!(rt::token::DelimToken::$j) })*
}
}
}
- gen_match!(Paren, Bracket, Brace, NoDelim)
+ gen_match!(Parenthesis => Paren, Brace => Brace, Bracket => Bracket, None => NoDelim)
}
}