use std::cmp;
use syntax::ast::*;
use syntax::codemap::{Spanned, DUMMY_SP};
use syntax::ext::base::{ExtCtxt};
use syntax::ext::build::{AstBuilder};
use syntax::ext::quote::rt::{ToTokens};
use syntax::parse::token::{BinOpToken, DelimToken, Token};
use syntax::ptr::{P};
use syntax::tokenstream::{TokenTree};
macro_rules! to_token_trees {
(P<$ty:ty>) => (
impl ToTokenTrees for $ty {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
P(self.clone()).to_tokens(context)
}
}
);
($ty:ty) => (
impl ToTokenTrees for $ty {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
self.to_tokens(context)
}
}
);
}
#[derive(Clone, Debug)]
pub enum SequenceItem {
SingleQuote(Vec<TokenTree>),
MultipleQuote(Vec<Vec<TokenTree>>),
TokenTree(TokenTree),
}
impl SequenceItem {
fn expand(&self, index: usize, tts: &mut Vec<TokenTree>) {
match *self {
SequenceItem::SingleQuote(ref subtts) => tts.extend(subtts.iter().cloned()),
SequenceItem::MultipleQuote(ref subtts) => tts.extend(subtts[index].iter().cloned()),
SequenceItem::TokenTree(ref tt) => tts.push(tt.clone()),
}
}
}
#[derive(Clone, Debug)]
pub struct Sequence {
size: usize,
items: Vec<SequenceItem>,
separator: Option<Token>,
}
#[allow(missing_debug_implementations)]
pub struct SequenceBuilder {
sequence: Sequence,
}
impl SequenceBuilder {
pub fn new() -> SequenceBuilder {
SequenceBuilder { sequence: Sequence { size: 0, items: vec![], separator: None } }
}
pub fn quote<Q: SequenceQuote>(mut self, context: &ExtCtxt, value: &Q) -> SequenceBuilder {
if let Some(size) = value.size() {
self.sequence.size = cmp::max(self.sequence.size, size);
}
self.sequence.items.push(value.to_sequence_item(context));
self
}
pub fn token_tree(mut self, tt: TokenTree) -> SequenceBuilder {
self.sequence.items.push(SequenceItem::TokenTree(tt));
self
}
pub fn separator(mut self, separator: Token) -> SequenceBuilder {
self.sequence.separator = Some(separator);
self
}
pub fn build(self) -> Sequence {
self.sequence
}
}
impl Default for SequenceBuilder {
fn default() -> SequenceBuilder {
SequenceBuilder::new()
}
}
pub trait ToTokenTrees {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree>;
}
impl<T: ToTokenTrees> ToTokenTrees for Option<T> {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
self.as_ref().map_or_else(|| vec![], |t| t.to_token_trees(context))
}
}
impl<T: ToTokenTrees> ToTokenTrees for P<T> {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
(**self).to_token_trees(context)
}
}
impl<'a, T: ToTokenTrees> ToTokenTrees for &'a T {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
(*self).to_token_trees(context)
}
}
impl<T: ToTokenTrees> ToTokenTrees for Spanned<T> {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
self.node.to_token_trees(context)
}
}
impl ToTokenTrees for BareFnTy {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
context.ty(context.call_site(), TyKind::BareFn(P(self.clone()))).to_token_trees(context)
}
}
impl ToTokenTrees for Field {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let cs = context.call_site();
let mut tts = vec![];
tts.push(TokenTree::Token(cs, Token::Ident(self.ident.node)));
tts.push(TokenTree::Token(cs, Token::Colon));
tts.extend(self.expr.to_token_trees(context).into_iter());
tts
}
}
impl ToTokenTrees for FieldPat {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let cs = context.call_site();
let mut tts = vec![];
tts.push(TokenTree::Token(cs, Token::Ident(self.ident)));
tts.push(TokenTree::Token(cs, Token::Colon));
tts.extend(self.pat.to_token_trees(context).into_iter());
tts
}
}
impl ToTokenTrees for FnDecl {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let cs = context.call_site();
let mut tts = vec![];
tts.push(TokenTree::Token(cs, Token::OpenDelim(DelimToken::Paren)));
for arg in &self.inputs {
tts.extend(arg.to_token_trees(context).into_iter());
tts.push(TokenTree::Token(cs, Token::Comma));
}
if self.variadic {
tts.push(TokenTree::Token(cs, Token::DotDotDot));
}
tts.push(TokenTree::Token(cs, Token::CloseDelim(DelimToken::Paren)));
if let FunctionRetTy::Ty(ref ty) = self.output {
tts.push(TokenTree::Token(cs, Token::RArrow));
tts.extend(ty.to_token_trees(context).into_iter());
}
tts
}
}
impl ToTokenTrees for ForeignItem {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let cs = context.call_site();
let mut tts = vec![];
match self.node {
ForeignItemKind::Fn(ref decl, ref generics) => {
tts.push(mk_ident(context, "fn"));
tts.push(TokenTree::Token(cs, Token::Ident(self.ident)));
tts.extend(generics.to_token_trees(context).into_iter());
tts.extend(decl.to_token_trees(context).into_iter());
tts.push(TokenTree::Token(cs, Token::Semi));
},
ForeignItemKind::Static(ref ty, mutable) => {
tts.push(mk_ident(context, "static"));
if mutable {
tts.push(mk_ident(context, "mut"));
}
tts.push(TokenTree::Token(cs, Token::Ident(self.ident)));
tts.push(TokenTree::Token(cs, Token::Colon));
tts.extend(ty.to_token_trees(context).into_iter());
tts.push(TokenTree::Token(cs, Token::Semi));
},
}
tts
}
}
impl ToTokenTrees for ForeignMod {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let item = Item {
ident: context.ident_of(""),
attrs: vec![],
id: DUMMY_NODE_ID,
node: ItemKind::ForeignMod(self.clone()),
vis: Visibility::Inherited,
span: context.call_site(),
};
item.to_token_trees(context)
}
}
impl ToTokenTrees for Lifetime {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let ident = context.ident_of(&self.name.as_str()[..]);
vec![TokenTree::Token(context.call_site(), Token::Lifetime(ident))]
}
}
impl ToTokenTrees for LifetimeDef {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let mut tts = vec![];
tts.extend(self.lifetime.to_token_trees(context).into_iter());
tts.push(TokenTree::Token(context.call_site(), Token::Colon));
let sequence = SequenceBuilder::new()
.quote(context, &self.bounds)
.separator(Token::BinOp(BinOpToken::Plus))
.build();
tts.extend(sequence.to_token_trees(context).into_iter());
tts
}
}
impl ToTokenTrees for Local {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let stmt = Stmt {
id: DUMMY_NODE_ID,
node: StmtKind::Local(P(self.clone())),
span: context.call_site(),
};
stmt.to_token_trees(context)
}
}
impl ToTokenTrees for Sequence {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let mut tts = vec![];
for i in 0..self.size {
for item in &self.items {
item.expand(i, &mut tts);
}
if let Some(ref separator) = self.separator {
if i + 1 < self.size {
tts.push(TokenTree::Token(context.call_site(), separator.clone()));
}
}
}
tts
}
}
impl ToTokenTrees for String {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
(&self[..]).to_token_trees(context)
}
}
impl ToTokenTrees for StructField {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let cs = context.call_site();
let mut tts = vec![];
tts.extend(self.attrs.iter().flat_map(|a| a.to_token_trees(context).into_iter()));
if self.vis == Visibility::Public {
tts.push(mk_ident(context, "pub"));
}
if let Some(ident) = self.ident {
tts.push(TokenTree::Token(cs, Token::Ident(ident)));
tts.push(TokenTree::Token(cs, Token::Colon));
}
tts.extend(self.ty.to_token_trees(context).into_iter());
tts
}
}
impl ToTokenTrees for Token {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
vec![TokenTree::Token(context.call_site(), self.clone())]
}
}
impl ToTokenTrees for Variant_ {
fn to_token_trees(&self, context: &ExtCtxt) -> Vec<TokenTree> {
let cs = context.call_site();
let mut tts = vec![];
tts.extend(self.attrs.iter().flat_map(|a| a.to_token_trees(context).into_iter()));
tts.push(TokenTree::Token(cs, Token::Ident(self.name)));
let data = match self.data {
VariantData::Struct(ref fields, _) => Some((DelimToken::Brace, fields)),
VariantData::Tuple(ref fields, _) => Some((DelimToken::Paren, fields)),
_ => None,
};
if let Some((delim, fields)) = data {
tts.push(TokenTree::Token(cs, Token::OpenDelim(delim)));
let sequence = SequenceBuilder::new()
.quote(context, fields)
.separator(Token::Comma)
.build();
tts.extend(sequence.to_token_trees(context).into_iter());
tts.push(TokenTree::Token(cs, Token::CloseDelim(delim)));
}
if let Some(ref discriminant) = self.disr_expr {
tts.push(TokenTree::Token(cs, Token::Eq));
tts.extend(discriminant.to_token_trees(context).into_iter());
}
tts
}
}
to_token_trees!(());
to_token_trees!(bool);
to_token_trees!(char);
to_token_trees!(i8);
to_token_trees!(i16);
to_token_trees!(i32);
to_token_trees!(i64);
to_token_trees!(isize);
to_token_trees!(u8);
to_token_trees!(u16);
to_token_trees!(u32);
to_token_trees!(u64);
to_token_trees!(usize);
to_token_trees!(str);
to_token_trees!(Arg);
to_token_trees!(Arm);
to_token_trees!(Attribute);
to_token_trees!(Block);
to_token_trees!(P<Expr>);
to_token_trees!(Generics);
to_token_trees!(Ident);
to_token_trees!(ImplItem);
to_token_trees!(P<Item>);
to_token_trees!(Lit);
to_token_trees!(P<MetaItem>);
to_token_trees!(P<Pat>);
to_token_trees!(Path);
to_token_trees!(Stmt);
to_token_trees!(TokenTree);
to_token_trees!(TraitItem);
to_token_trees!(Ty);
to_token_trees!(WhereClause);
pub trait SequenceQuote {
fn size(&self) -> Option<usize>;
fn to_sequence_item(&self, context: &ExtCtxt) -> SequenceItem;
}
impl <T: Clone + ToTokenTrees + 'static> SequenceQuote for T {
fn size(&self) -> Option<usize> {
None
}
fn to_sequence_item(&self, context: &ExtCtxt) -> SequenceItem {
SequenceItem::SingleQuote(self.to_token_trees(context))
}
}
impl <'a, T: Clone + ToTokenTrees + 'static> SequenceQuote for &'a [T] {
fn size(&self) -> Option<usize> {
Some(self.len())
}
fn to_sequence_item(&self, context: &ExtCtxt) -> SequenceItem {
let tts = self.iter().map(|v| v.to_token_trees(context)).collect();
SequenceItem::MultipleQuote(tts)
}
}
impl <T: Clone + ToTokenTrees + 'static> SequenceQuote for Vec<T> {
fn size(&self) -> Option<usize> {
(&self[..]).size()
}
fn to_sequence_item(&self, context: &ExtCtxt) -> SequenceItem {
(&self[..]).to_sequence_item(context)
}
}
fn mk_ident(context: &ExtCtxt, ident: &str) -> TokenTree {
TokenTree::Token(DUMMY_SP, Token::Ident(context.ident_of(ident)))
}