#![feature(plugin, plugin_registrar, rustc_private, quote)]
#![warn(missing_copy_implementations, missing_debug_implementations, missing_docs)]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(clippy))]
extern crate rustc_plugin;
extern crate syntax;
use std::rc::{Rc};
use rustc_plugin::{Registry};
use syntax::ast::*;
use syntax::codemap::{Span, DUMMY_SP};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult, MacroExpanderFn};
use syntax::ext::build::{AstBuilder};
use syntax::parse::token::{BinOpToken, DelimToken, Token};
use syntax::ptr::{P};
use syntax::tokenstream::{Delimited, TokenTree};
mod expr;
pub use expr::{ToExpr};
mod parse;
pub use parse::*;
mod tt;
pub use tt::{Sequence, SequenceBuilder, SequenceQuote, ToTokenTrees};
macro_rules! open { ($delim:ident) => (Token::OpenDelim(DelimToken::$delim)); }
macro_rules! close { ($delim:ident) => (Token::CloseDelim(DelimToken::$delim)); }
macro_rules! ident { ($ident:expr) => (Token::Ident(Ident::from_str($ident))); }
macro_rules! quote {
($registry:expr, $name:ident, $parse:expr) => (
quote!($registry, $name, $parse, [], [])
);
($f:expr, $name:ident, $parse:expr, [$($left:expr), *], [$($right:expr), *]) => (
fn $name(
context: &mut ExtCtxt, span: Span, arguments: &[TokenTree]
) -> Box<MacResult + 'static> {
let left = &[$($left), *];
let right = &[$($right), *];
expand_quote(context, span, arguments, $parse, left, right)
}
$f(&stringify!($name)[7..], $name);
);
}
#[derive(Debug)]
enum ExpandedTokenTree {
TokenTree(P<Expr>),
Vec(P<Expr>),
}
#[derive(Copy, Clone, Debug)]
enum Parse {
Method(&'static str),
Function(&'static str),
}
impl Parse {
fn to_expr(&self, context: &ExtCtxt, span: Span, tts: P<Expr>, source: P<Expr>) -> P<Expr> {
let tts = context.expr_addr_of(span, tts);
match *self {
Parse::Method(method) => {
let arguments = match &method[..] {
"parse_attribute" | "parse_fn_decl" => vec![context.expr_bool(span, true)],
"parse_path" => {
let path = &["syntax", "parse", "parser", "PathStyle", "Type"];
vec![expr::mk_expr_path(context, span, path)]
},
_ => vec![],
};
let p = context.ident_of("p");
let pident = context.expr_ident(span, p);
let call = expr::mk_expr_method_call(context, span, pident, method, arguments);
let block = context.expr_block(context.block_expr(call));
let lambda = context.lambda(span, vec![p], block);
let arguments = vec![source, span.to_expr(context, DUMMY_SP), tts, lambda];
let expr = expr::mk_expr_call(context, span, &["synthax", "parse_tts"], arguments);
expr::mk_expr_method_call(context, span, expr, "unwrap", vec![])
},
Parse::Function(function) => {
let arguments = vec![source, span.to_expr(context, DUMMY_SP), tts];
expr::mk_expr_call(context, span, &["synthax", function], arguments)
},
}
}
}
fn mk_expr_tt(context: &ExtCtxt, span: Span, tt: &TokenTree) -> P<Expr> {
let source = context.expr_ident(span, context.ident_of("_source"));
let expr = expr::mk_expr_method_call(context, span, source, "call_site", vec![]);
expr::mk_expr_tt(context, span, tt, expr)
}
fn expand_etts(
context: &ExtCtxt, span: Span, etts: &[ExpandedTokenTree], source: &P<Expr>
) -> P<Expr> {
let mut stmts = vec![];
let source = context.expr_addr_of(span, source.clone());
stmts.push(context.stmt_let(span, false, context.ident_of("_source"), source));
let expr = expr::mk_expr_call(context, span, &["std", "vec", "Vec", "new"], vec![]);
stmts.push(context.stmt_let(span, true, context.ident_of("_tts"), expr));
let expr = context.expr_ident(span, context.ident_of("_tts"));
for ett in etts {
let (method, argument) = match *ett {
ExpandedTokenTree::TokenTree(ref tt) => ("push", tt.clone()),
ExpandedTokenTree::Vec(ref vec) =>
("extend", expr::mk_expr_method_call(context, span, vec.clone(), "into_iter", vec![])),
};
let expr = expr::mk_expr_method_call(context, span, expr.clone(), method, vec![argument]);
stmts.push(context.stmt_expr(expr));
}
stmts.push(context.stmt_expr(expr));
context.expr_block(context.block(span, stmts))
}
fn expand_var_sequence_tts(
context: &ExtCtxt, span: Span, tts: &[TokenTree], mut expr: P<Expr>
) -> P<Expr> {
macro_rules! token_tree {
($tt:expr) => ({
let arguments = vec![$tt.to_expr(context, span)];
expr = expr::mk_expr_method_call(context, span, expr, "token_tree", arguments);
});
}
let mut tts = tts.iter();
while let Some(tt) = tts.next() {
match *tt {
TokenTree::Token(_, Token::Dollar) => {
let subexpr = expand_var(context, span, &mut tts);
let source = context.expr_ident(span, context.ident_of("_source"));
let arguments = vec![source, context.expr_addr_of(span, subexpr)];
expr = expr::mk_expr_method_call(context, span, expr, "quote", arguments);
},
TokenTree::Delimited(_, ref delimited) => {
token_tree!(delimited.open_tt(DUMMY_SP));
expr = expand_var_sequence_tts(context, span, &delimited.tts, expr);
token_tree!(delimited.close_tt(DUMMY_SP));
},
_ => token_tree!(tt),
}
}
expr
}
fn expand_var_sequence_separator<'a, I: Iterator<Item=&'a TokenTree>>(
context: &ExtCtxt, span: Span, tts: &mut I, expr: P<Expr>
) -> P<Expr> {
let separator = match tts.next() {
Some(&TokenTree::Token(_, Token::BinOp(BinOpToken::Star))) => None,
Some(&TokenTree::Token(_, ref token)) => {
match tts.next() {
Some(&TokenTree::Token(_, Token::BinOp(BinOpToken::Star))) => { },
invalid => {
context.span_err(invalid.map_or(span, |tt| tt.span()), "expected `*`");
panic!();
},
}
Some(token)
},
invalid => {
context.span_err(invalid.map_or(span, |tt| tt.span()), "expected separator or `*`");
panic!();
},
};
if let Some(separator) = separator {
let arguments = vec![separator.to_expr(context, span)];
expr::mk_expr_method_call(context, span, expr, "separator", arguments)
} else {
expr
}
}
fn expand_var_sequence<'a, I: Iterator<Item=&'a TokenTree>>(
context: &ExtCtxt, span: Span, tts: &mut I, subtts: &[TokenTree]
) -> P<Expr> {
let mut expr = expr::mk_expr_call(context, span, &["synthax", "SequenceBuilder", "new"], vec![]);
expr = expand_var_sequence_tts(context, span, subtts, expr);
expr = expand_var_sequence_separator(context, span, tts, expr);
expr::mk_expr_method_call(context, span, expr, "build", vec![])
}
fn expand_var<'a, I: Iterator<Item=&'a TokenTree>>(
context: &ExtCtxt, span: Span, tts: &mut I
) -> P<Expr> {
match tts.next() {
Some(&TokenTree::Token(_, Token::Ident(ident))) => context.expr_ident(span, ident),
Some(&TokenTree::Delimited(_, ref delimited)) if delimited.delim == DelimToken::Brace =>
parse_expr(context, span, &delimited.tts).unwrap(),
Some(&TokenTree::Delimited(_, ref delimited)) if delimited.delim == DelimToken::Paren =>
expand_var_sequence(context, span, tts, &delimited.tts),
invalid => {
context.span_err(invalid.map_or(span, |tt| tt.span()), "expected identifier or `{`");
panic!();
},
}
}
fn expand_delimited(
context: &ExtCtxt, span: Span, delimited: &Rc<Delimited>, source: &P<Expr>
) -> Vec<ExpandedTokenTree> {
let mut etts = vec![];
let tt = mk_expr_tt(context, span, &delimited.open_tt(DUMMY_SP));
etts.push(ExpandedTokenTree::TokenTree(tt));
etts.push(ExpandedTokenTree::Vec(expand_tts(context, span, &delimited.tts, source)));
let tt = mk_expr_tt(context, span, &delimited.close_tt(DUMMY_SP));
etts.push(ExpandedTokenTree::TokenTree(tt));
etts
}
fn expand_tts(context: &ExtCtxt, span: Span, tts: &[TokenTree], source: &P<Expr>) -> P<Expr> {
let mut tts = tts.iter();
let mut etts = vec![];
while let Some(tt) = tts.next() {
match *tt {
TokenTree::Token(_, Token::Dollar) => {
let idents = &["synthax", "ToTokenTrees", "to_token_trees"];
let expr = context.expr_addr_of(span, expand_var(context, span, &mut tts));
let arguments = vec![expr, source.clone()];
etts.push(ExpandedTokenTree::Vec(expr::mk_expr_call(context, span, idents, arguments)));
},
TokenTree::Delimited(_, ref delimited) =>
etts.extend(expand_delimited(context, span, delimited, source).into_iter()),
_ => etts.push(ExpandedTokenTree::TokenTree(mk_expr_tt(context, span, tt))),
}
}
expand_etts(context, span, &etts, source)
}
fn parse_arguments<'tt>(
context: &mut ExtCtxt, span: Span, arguments: &'tt [TokenTree]
) -> Option<(P<Expr>, &'tt [TokenTree])> {
let comma = match arguments.iter().position(|tt| tt.eq_token(Token::Comma)) {
Some(comma) => comma,
None => {
context.span_err(span, "expected `,`");
return None;
},
};
parse_expr(context, span, &arguments[..comma]).map(|e| (e, &arguments[(comma + 1)..]))
}
fn expand_quote_token_trees(
context: &mut ExtCtxt, span: Span, arguments: &[TokenTree]
) -> Box<MacResult + 'static> {
let (source, arguments) = match parse_arguments(context, span, arguments) {
Some(some) => some,
None => return DummyResult::any(span),
};
MacEager::expr(expand_tts(context, span, arguments, &source))
}
fn expand_quote(
context: &mut ExtCtxt,
span: Span,
arguments: &[TokenTree],
parse: Parse,
left: &[Token],
right: &[Token],
) -> Box<MacResult + 'static> {
let (source, arguments) = match parse_arguments(context, span, arguments) {
Some(some) => some,
None => return DummyResult::any(span),
};
let tts = if !left.is_empty() || !right.is_empty() {
let mut tts = vec![];
tts.extend(left.iter().map(|t| TokenTree::Token(span, t.clone())));
tts.extend(arguments.iter().cloned());
tts.extend(right.iter().map(|t| TokenTree::Token(span, t.clone())));
expand_tts(context, span, &tts, &source)
} else {
expand_tts(context, span, arguments, &source)
};
MacEager::expr(parse.to_expr(context, span, tts, source))
}
fn register_plugins<F: FnMut(&str, MacroExpanderFn)>(mut f: F) {
f("quote_token_trees", expand_quote_token_trees);
quote!(f, expand_quote_arg, Parse::Method("parse_arg"));
quote!(f, expand_quote_arm, Parse::Method("parse_arm"));
quote!(f, expand_quote_attribute, Parse::Method("parse_attribute"));
quote!(f, expand_quote_bare_fn_ty, Parse::Function("parse_bare_fn_ty"));
quote!(f, expand_quote_block, Parse::Method("parse_block"));
quote!(f, expand_quote_expr, Parse::Method("parse_expr"));
quote!(f, expand_quote_field, Parse::Method("parse_field"));
quote!(f, expand_quote_field_pat, Parse::Function("parse_field_pat"),
[ident!("Struct"), open!(Brace)], [close!(Brace)]);
quote!(f, expand_quote_fn_decl, Parse::Method("parse_fn_decl"));
quote!(f, expand_quote_foreign_item, Parse::Function("parse_foreign_item"),
[ident!("extern"), open!(Brace)], [close!(Brace)]);
quote!(f, expand_quote_foreign_mod, Parse::Function("parse_foreign_mod"));
quote!(f, expand_quote_generics, Parse::Method("parse_generics"));
quote!(f, expand_quote_impl_item, Parse::Method("parse_impl_item"));
quote!(f, expand_quote_item, Parse::Method("parse_item"));
quote!(f, expand_quote_lifetime, Parse::Function("parse_lifetime"));
quote!(f, expand_quote_lit, Parse::Method("parse_lit"));
quote!(f, expand_quote_local, Parse::Function("parse_local"));
quote!(f, expand_quote_meta_item, Parse::Method("parse_meta_item"));
quote!(f, expand_quote_pat, Parse::Method("parse_pat"));
quote!(f, expand_quote_path, Parse::Method("parse_path"));
quote!(f, expand_quote_stmt, Parse::Method("parse_stmt"));
quote!(f, expand_quote_struct_field, Parse::Function("parse_struct_field"),
[ident!("struct"), ident!("Struct"), open!(Brace)], [close!(Brace)]);
quote!(f, expand_quote_trait_item, Parse::Method("parse_trait_item"));
quote!(f, expand_quote_ty, Parse::Method("parse_ty"));
quote!(f, expand_quote_variant, Parse::Function("parse_variant"),
[ident!("enum"), ident!("Enum"), open!(Brace)], [close!(Brace)]);
quote!(f, expand_quote_where_clause, Parse::Method("parse_where_clause"));
}
#[cfg(feature="syntex")]
pub fn plugin_registrar(registry: &mut Registry) {
register_plugins(|n, v| registry.add_macro(n, v));
}
#[cfg(feature="syntex")]
pub fn expand<S: AsRef<std::path::Path>, D: AsRef<std::path::Path>>(
source: S, destination: D
) -> Result<(), rustc_plugin::Error> {
let mut registry = Registry::new();
plugin_registrar(&mut registry);
registry.expand("", source.as_ref(), destination.as_ref())
}
#[cfg(not(feature="syntex"))]
#[doc(hidden)]
#[plugin_registrar]
pub fn plugin_registrar(registry: &mut Registry) {
register_plugins(|n, v| registry.register_macro(n, v));
}