#[macro_export]
macro_rules! htype {
($holmes:ident, [$t:tt]) => {
::holmes::pg::dyn::types::List::new(htype!($holmes, $t))
};
($holmes:ident, ($($t:tt),*)) => {
::holmes::pg::dyn::types::Tuple::new(vec![$(htype!($holmes, $t)),*])
};
($holmes:ident, $i:ident) => {
$holmes.get_type(stringify!($i))
.expect(&format!("Type not present in database: {}", stringify!($i)))
};
}
#[macro_export]
macro_rules! holmes_exec {
($holmes:ident, { $( $action:expr );* }) => {{
$( try!($action($holmes)); );*
$holmes.nop()
}};
}
#[macro_export]
macro_rules! field {
($holmes:ident, [$name:ident $t:tt $descr:expr]) => {{::holmes::engine::types::Field {
name: Some(stringify!($name).to_string()),
description: Some($descr.to_string()),
type_: htype!($holmes, $t)
}}};
($holmes:ident, [$name:ident $t:tt]) => {{::holmes::engine::types::Field {
name: Some(stringify!($name).to_string()),
description: None,
type_: htype!($holmes, $t)
}}};
($holmes:ident, $t:tt) => {{::holmes::engine::types::Field {
name: None,
description: None,
type_: htype!($holmes, $t)
}}};
}
#[macro_export]
macro_rules! predicate {
($holmes:ident, $pred_name:ident($($t:tt),*), $descr:expr) => {{
let fields = vec![$(field!($holmes, $t),)*];
$holmes.new_predicate(&::holmes::engine::types::Predicate {
name: stringify!($pred_name).to_string(),
description: Some($descr.to_string()),
fields: fields
})
}};
($holmes:ident, $pred_name:ident($($t:tt),*)) => {{
let fields = vec![$(field!($holmes, $t),)*];
$holmes.new_predicate(&::holmes::engine::types::Predicate {
name: stringify!($pred_name).to_string(),
description: None,
fields: fields
})
}};
($pred_name:ident($($t:tt),*) : $descr:expr) => { |holmes: &mut ::holmes::Engine<_,_>| {
predicate!(holmes, $pred_name($($t),*), $descr)
}};
($pred_name:ident($($t:tt),*)) => { |holmes: &mut ::holmes::Engine<_,_>| {
predicate!(holmes, $pred_name($($t),*))
}};
}
#[macro_export]
macro_rules! fact {
($holmes:ident, $pred_name:ident($($a:expr),*)) => {
$holmes.new_fact(&::holmes::engine::types::Fact {
pred_name : stringify!($pred_name).to_string(),
args : vec![$(::holmes::pg::dyn::values::ToValue::to_value($a)),*]
})
};
($pred_name:ident($($a:expr),*)) => { |holmes: &mut ::holmes::Engine<_,_>| {
fact!(holmes, $pred_name($($a),*))
}};
}
#[macro_export]
macro_rules! clause {
($holmes:ident, $vars:ident, $next:ident, $pred_name:ident($($m:tt),*)) => {{
let mut b = 0;
::holmes::engine::types::Clause {
pred_name: stringify!($pred_name).to_string(),
args: vec![$(clause_match!($vars, b, $next, $m)),*]
}
}};
($holmes:ident, $vars:ident, $next:ident, $pred_name:ident{$($field:ident = $m:tt),*}) => {{
use std::collections::HashMap;
let pred_name = stringify!($pred_name).to_string();
let pred = $holmes.get_predicate(&pred_name)?.unwrap();
let mut matches = HashMap::new();
let mut b = 0;
let _ = {
$(matches.insert(stringify!($field).to_string(), clause_match!($vars, b, $next, $m)));*
};
let args: Vec<_> = pred.fields.iter().enumerate().map(|(idx, field)| {
let slot = ::holmes::engine::types::Projection::Slot(idx);
match field.name {
Some(ref name) => match matches.remove(name) {
Some(cm) => (slot, cm.1),
None => (slot, ::holmes::engine::types::MatchExpr::Unbound)
},
None => (slot, ::holmes::engine::types::MatchExpr::Unbound),
}
}).collect();
::holmes::engine::types::Clause {
pred_name: pred_name,
args: args
}
}};
}
#[macro_export]
macro_rules! query {
($holmes:ident, $($pred_name:ident $inner:tt)&*) => {{
use std::collections::HashMap;
let mut vars : HashMap<String, ::holmes::engine::types::Var> = HashMap::new();
let mut n : ::holmes::engine::types::Var = 0;
let query = vec![$(clause!($holmes, vars, n, $pred_name $inner)),*];
$holmes.derive(&query)
}}
}
#[macro_export]
macro_rules! rule {
($holmes:ident, $head_name:ident $head_inner:tt <= $($body_name:ident $body_inner:tt)&*,
{$(let $bind:tt = $hexpr:tt);*}) => {{
use std::collections::HashMap;
let mut vars : HashMap<String, ::holmes::engine::types::Var> = HashMap::new();
let mut n : ::holmes::engine::types::Var = 0;
let body = vec![$(clause!($holmes, vars, n, $body_name $body_inner)),*];
let head = clause!($holmes, vars, n, $head_name $head_inner);
$holmes.new_rule(&::holmes::engine::types::Rule {
body: body,
head: head,
wheres : vec! [$(::holmes::engine::types::WhereClause {
lhs : bind_match!(vars, n, $bind),
rhs : hexpr!(vars, n, $hexpr)
}),*]
})
}};
($holmes:ident, $($head_name:ident $head_inner:tt),* <= $($body_name:ident $inner:tt)&*) => {
rule!($holmes, $($head_name $head_inner),* <= $($body_name $inner)&*, {})
};
($($head_name:ident $head_inner:tt),* <= $($body_name:ident $inner:tt)&*) => {
|holmes: &mut ::holmes::Engine<_,_>| {
rule!(holmes, $($head_name $head_inner),* <= $($body_name $inner)&*, {})
}
};
($($head_name:ident $head_inner:tt),* <=
$($body_name:ident $inner:tt)&*, {$(let $bind:tt = $hexpr:tt);*}) => {
|holmes: &mut ::holmes::Engine<_,_>| {
rule!(holmes, $($head_name $head_inner),* <=
$($body_name $inner)&*, {$(let $bind = $hexpr);*})
}
};
}
#[macro_export]
macro_rules! func {
($holmes:ident, let $name:ident : $src:tt -> $dst:tt = $body:expr) => {{
let src = htype!($holmes, $src);
let dst = htype!($holmes, $dst);
$holmes.reg_func(stringify!($name).to_string(),
::holmes::engine::types::Func {
input_type: src,
output_type: dst,
run: Box::new(|v : ::holmes::pg::dyn::Value| {
::holmes::pg::dyn::values::ToValue::to_value($body(typed_unpack!(v, $src)))
})})
}};
(let $name:ident : $src:tt -> $dst:tt = $body:expr) => {
|holmes: &mut ::holmes::Engine<_,_>| {
func!(holmes, let $name : $src -> $dst = $body)
}
};
}
pub mod internal {
#[macro_export]
macro_rules! typed_unpack {
($val:expr, [$typ:tt]) => {
$val.get().downcast_ref::<Vec<::holmes::pg::dyn::Value>>()
.expect("Dynamic list unpack failed")
.into_iter().map(|v| {
typed_unpack!(v, $typ)
}).collect::<Vec<_>>()
};
($val:expr, ($($typ:tt),*)) => {{
let mut pack = $val.get().downcast_ref::<Vec<::holmes::pg::dyn::Value>>()
.expect("Dynamic tuple unpack failed").into_iter();
($(typed_unpack!(pack.next().expect("Dynamic tuple too short"), $typ)),*)
}};
($val:expr, $name:ident) => {
$val.get().downcast_ref()
.expect(concat!("Dynamic base type unpack failed for ",
stringify!($name)))
};
}
#[macro_export]
macro_rules! bind_match {
($vars:ident, $n:ident, [ $bm:tt ]) => {
::holmes::engine::types::BindExpr::Iterate(
Box::new(bind_match!($vars, $n, $bm)))
};
($vars:ident, $n:ident, {$($bm:tt),*}) => {
::holmes::engine::types::BindExpr::Destructure(
vec![$(bind_match!($vars, $n, $bm)),*])
};
($vars:ident, $n:ident, $cm:tt) => {{
let mut b = 0;
::holmes::engine::types::BindExpr::Normal(
clause_match!($vars, b, $n, $cm).1)
}};
}
#[macro_export]
macro_rules! hexpr {
($vars:ident, $n:ident, [$hexpr_name:ident]) => {{
let mut b = 0;
match clause_match!($vars, b, $n, $hexpr_name).1 {
::holmes::engine::types::MatchExpr::Var(var_no) =>
::holmes::engine::types::Expr::Var(var_no),
_ => panic!("clause_match! returned non-var for var input")
}
}};
($vars:ident, $n:ident, ($hexpr:expr)) => {
::holmes::engine::types::Expr::Val(
::holmes::pg::dyn::values::ToValue::to_value($hexpr))
};
($vars:ident, $n:ident, {$hexpr_func:ident($($hexpr_arg:tt),*)}) => {
::holmes::engine::types::Expr::App(
stringify!($hexpr_func).to_string(),
vec![$(hexpr!($vars, $n, $hexpr_arg)),*])
};
}
#[macro_export]
macro_rules! db_expr {
($vars:ident, ($v:expr)) => {{
::holmes::engine::types::Projection::U64($v)
}};
($vars:ident, [$n:ident]) => {{
match $vars.get(stringify!($n)) {
Some(varnum) => ::holmes::engine::types::Projection::Var(*varnum),
None => panic!("Referenced undefined variable in substring clause")
}
}};
}
#[macro_export]
macro_rules! clause_match {
($vars:ident, $m:ident, $n:ident, {$start:tt, $end:tt, $v:ident}) => {{
use std::collections::hash_map::Entry::*;
match $vars.entry(stringify!($v).to_string()) {
Occupied(_) => (),
Vacant(entry) => {
$n = $n + 1;
entry.insert($n - 1);
}
}
$m = $m + 1;
let col = ::holmes::engine::types::Projection::Slot($m - 1);
(::holmes::engine::types::Projection::SubStr {
buf: Box::new(col),
start_idx: Box::new(db_expr!($vars, $start)),
end_idx: Box::new(db_expr!($vars, $end))},
::holmes::engine::types::MatchExpr::Var(
*$vars.get(stringify!($v)).unwrap()))
}};
($vars:ident, $m:ident, $n:ident, [_]) => {{
$m = $m + 1;
let col = ::holmes::engine::types::Projection::Slot($m - 1);
(col, ::holmes::engine::types::MatchExpr::Unbound)
}};
($vars:ident, $m:ident, $n:ident, ($v:expr)) => {{
$m = $m + 1;
let col = ::holmes::engine::types::Projection::Slot($m - 1);
(col, ::holmes::engine::types::MatchExpr::Const(
::holmes::pg::dyn::values::ToValue::to_value($v)))
}};
($vars:ident, $b:ident, $n:ident, $m:ident) => {{
use std::collections::hash_map::Entry::*;
use ::holmes::engine::types::MatchExpr::*;
$b = $b + 1;
let col = ::holmes::engine::types::Projection::Slot($b - 1);
(col, match $vars.entry(stringify!($m).to_string()) {
Occupied(entry) => Var(*entry.get()),
Vacant(entry) => {
$n = $n + 1;
entry.insert($n - 1);
Var($n - 1)
}
})
}};
}
}