#![crate_type="dylib"]
#![crate_name="bassert"]
#![feature(plugin_registrar, quote)]
#![doc(html_logo_url = "https://avatars.io/gravatar/d0ad9c6f37bb5aceac2d7ac95ba82607?size=large",
html_favicon_url="https://avatars.io/gravatar/d0ad9c6f37bb5aceac2d7ac95ba82607?size=small")]
extern crate syntax;
extern crate rustc;
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ast::{self, TokenTree};
use syntax::ptr::P;
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacExpr};
use rustc::plugin::Registry;
#[macro_export]
macro_rules! bassert{
($e:expr) => ({
bassert!($e,"");
});
($e:expr, $fmt:expr) => ({
bassert!($e,"{}",$e);
});
($e:expr, $fmt:expr, $($args:tt)*) => ({
let f = internal_bassert!($e); f(&format_args!(concat!("\n",$fmt), $($args)*));
});
}
#[macro_export]
macro_rules! debug_bassert{
($e:expr) => ({
if cfg!(not(ndebug)) { bassert!($e) }
});
($e:expr, $fmt:expr) => ({
if cfg!(not(ndebug)) { bassert!($e, $fmt) }
});
($e:expr, $fmt:expr, $($args:tt)*) => ({
if cfg!(not(ndebug)) { bassert!($e, $fmt, $($args)*) }
});
}
fn expand_bassert(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
-> Box<MacResult + 'static> {
let mut parser = cx.new_parser_from_tts(args);
let expr: P<ast::Expr> = parser.parse_expr();
if parser.token != token::Eof {
cx.span_err(sp, "Non terminated internal bassert macro!");
return DummyResult::any(sp);
}
MacExpr::new(get_fmt_meth(cx, expr))
}
fn get_fmt_meth(cx: &mut ExtCtxt, expr: P<ast::Expr>) -> P<ast::Expr> {
let inner_check = match expr.node {
ast::ExprBinary(cmp, ref given, ref expected) => {
let chk_fun = get_check_func(cx, cmp);
quote_expr!(cx,
match (&($given), &($expected)) {
(given_val, expected_val) => {
let chk = $chk_fun;
if !chk(given_val, expected_val) {
panic!("assertion failed: {}:\n\
left: `{}` = `{:?}`\n\
right: `{}` = `{:?}`{}",
stringify!($expr), stringify!($given), *given_val,
stringify!($expected), *expected_val, rest);
}
}
}
)
},
ast::ExprCall(ref fun, ref args) if args.len() > 0 && args.len() < 7 => {
match args.len() {
1 => {
let a = &args[0];
quote_expr!(cx,
match (&($a),) {
(a,) => {
if !($fun(*a)) {
panic!("assertion failed: {}:\n\
{} = {:?}{}",
stringify!($expr), stringify!($a), *a, rest);
};
}
};
)
}
2 => {
let a = &args[0]; let b = &args[1];
quote_expr!(cx,
match (&($a), &($b)) {
(a, b) => {
if !($fun(*a, *b)) {
panic!("assertion failed: {}:\n\
{} = {:?}\n\
{} = {:?}{}",
stringify!($expr), stringify!($a), *a, stringify!($b),
*b, rest);
};
}
};
)
}
3 => {
let a = &args[0]; let b = &args[1]; let c = &args[2];
quote_expr!(cx,
match (&($a), &($b), &($c)) {
(a, b, c) => {
if !($fun(*a, *b, *c)) {
panic!("assertion failed: {}:\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}{}",
stringify!($expr), stringify!($a), *a, stringify!($b),
*b, stringify!($c), *c, rest);
};
}
};
)
}
4 => {
let a = &args[0]; let b = &args[1]; let c = &args[2]; let d = &args[3];
quote_expr!(cx,
match (&($a), &($b), &($c), &($d)) {
(a, b, c, d) => {
if !($fun(*a, *b, *c, *d)) {
panic!("assertion failed: {}:\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}{}",
stringify!($expr), stringify!($a), *a, stringify!($b),
*b, stringify!($c), *c, stringify!($d), *d, rest);
};
}
};
)
}
5 => {
let a = &args[0]; let b = &args[1]; let c = &args[2]; let d = &args[3]; let e = &args[4];
quote_expr!(cx,
match (&($a), &($b), &($c), &($d), &($e)) {
(a, b, c, d, e) => {
if !($fun(*a, *b, *c, *d, *e)) {
panic!("assertion failed: {}:\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}{}",
stringify!($expr), stringify!($a), *a, stringify!($b),
*b, stringify!($c), *c, stringify!($d), *d,
stringify!($e), *e, rest);
};
}
};
)
}
6 => {
let a = &args[0]; let b = &args[1]; let c = &args[2]; let d = &args[3]; let e = &args[4]; let f = &args[5];
quote_expr!(cx,
match (&($a), &($b), &($c), &($d), &($e), &($f)) {
(a, b, c, d, e, f) => {
if !($fun(*a, *b, *c, *d, *e, *f)) {
panic!("assertion failed: {}:\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}\n\
{} = {:?}{}",
stringify!($expr),
stringify!($a), *a, stringify!($b), *b, stringify!($c),
*c, stringify!($d), *d, stringify!($e), *e,
stringify!($f), *f, rest);
};
}
};
)
}
_ => { unreachable!() }
}
},
_ => {
quote_expr!(cx,
if !($expr) {
panic!(concat!("assertion failed: ", stringify!($expr), "{}"), rest);
}
)
}
};
quote_expr!(cx, |: rest: &::std::fmt::Arguments| { $inner_check } )
}
fn get_check_func(cx: &mut ExtCtxt, cmp: ast::BinOp) -> P<ast::Expr> {
match cmp {
ast::BiEq => {
quote_expr!(cx, { fn chk<T: Eq>(x: &T, y: &T) -> bool { *x == *y }; chk })
},
ast::BiNe => {
quote_expr!(cx, { fn chk<T: Eq>(x: &T, y: &T) -> bool { *x != *y }; chk })
},
ast::BiLe => {
quote_expr!(cx, { fn chk<T: Ord>(x: &T, y: &T) -> bool { *x <= *y }; chk })
},
ast::BiGe => {
quote_expr!(cx, { fn chk<T: Ord>(x: &T, y: &T) -> bool { *x >= *y }; chk })
},
ast::BiLt => {
quote_expr!(cx, { fn chk<T: Ord>(x: &T, y: &T) -> bool { *x < *y }; chk })
},
ast::BiGt => {
quote_expr!(cx, { fn chk<T: Ord>(x: &T, y: &T) -> bool { *x > *y }; chk })
},
ast::BiOr => {
quote_expr!(cx, { fn chk(x: &bool, y: &bool) -> bool { *x || *y }; chk })
},
ast::BiAnd => {
quote_expr!(cx, { fn chk(x: &bool, y: &bool) -> bool { *x && *y }; chk })
},
ast::BiAdd => {
quote_expr!(cx, { fn chk<R, L: Add<R, bool>>(x: &L, y: &R) -> bool { *x + *y }; chk })
},
ast::BiSub => {
quote_expr!(cx, { fn chk<R, L: Sub<R, bool>>(x: &L, y: &R) -> bool { *x - *y }; chk })
},
ast::BiMul => {
quote_expr!(cx, { fn chk<R, L: Mul<R, bool>>(x: &L, y: &R) -> bool { *x * *y }; chk })
},
ast::BiDiv => {
quote_expr!(cx, { fn chk<R, L: Div<R, bool>>(x: &L, y: &R) -> bool { *x / *y }; chk })
},
ast::BiRem => {
quote_expr!(cx, { fn chk<R, L: Rem<R, bool>>(x: &L, y: &R) -> bool { *x % *y }; chk })
},
ast::BiShr => {
quote_expr!(cx, { fn chk<R, L: Shr<R, bool>>(x: &L, y: &R) -> bool { *x >> *y }; chk })
},
ast::BiShl => {
quote_expr!(cx, { fn chk<R, L: Shl<R, bool>>(x: &L, y: &R) -> bool { *x << *y }; chk })
},
ast::BiBitOr => {
quote_expr!(cx, { fn chk<R, L: BitOr<R, bool>>(x: &L, y: &R) -> bool { *x | *y }; chk })
},
ast::BiBitAnd => {
quote_expr!(cx, { fn chk<R, L: BitAnd<R, bool>>(x: &L, y: &R)-> bool { *x & *y }; chk })
},
ast::BiBitXor => {
quote_expr!(cx, { fn chk<R, L: BitXor<R, bool>>(x: &L, y: &R)-> bool { *x ^ *y }; chk })
},
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) { reg.register_macro("internal_bassert", expand_bassert); }