Part 1: Parse functions in where clauses.

This commit is contained in:
Nick Alexander 2017-04-03 16:46:11 -07:00 committed by Richard Newman
parent c6e933c396
commit b9cbf92205
2 changed files with 207 additions and 2 deletions

View file

@ -38,6 +38,7 @@ use self::mentat_parser_utils::value_and_span::{
};
use self::mentat_query::{
Binding,
Direction,
Element,
FindQuery,
@ -57,7 +58,9 @@ use self::mentat_query::{
SrcVar,
UnifyVars,
Variable,
VariableOrPlaceholder,
WhereClause,
WhereFn,
};
error_chain! {
@ -279,6 +282,25 @@ def_parser!(Where, pred, WhereClause, {
})))
});
/// A vector containing a parenthesized function expression and a binding.
def_parser!(Where, where_fn, WhereClause, {
// Accept either a nested list or a nested vector here:
// `[(foo ?x ?y) binding]` or `[[foo ?x ?y] binding]`
vector()
.of_exactly(
(seq().of_exactly(
(Query::predicate_fn(), Query::arguments())),
Bind::binding())
.map(|((f, args), binding)| {
WhereClause::WhereFn(
WhereFn {
operator: f.0,
args: args,
binding: binding,
})
}))
});
def_parser!(Where, pattern, WhereClause, {
vector()
.of_exactly(
@ -331,6 +353,7 @@ def_parser!(Where, clause, WhereClause, {
try(Where::not_clause()),
try(Where::pred()),
try(Where::where_fn()),
])
});
@ -345,6 +368,8 @@ def_matches_plain_symbol!(Find, period, ".");
def_matches_plain_symbol!(Find, ellipsis, "...");
def_matches_plain_symbol!(Find, placeholder, "_");
def_parser!(Find, find_scalar, FindSpec, {
Query::variable()
.skip(Find::period())
@ -448,6 +473,48 @@ def_parser!(Find, query, FindQuery, {
})
});
pub struct Bind<'a>(std::marker::PhantomData<&'a ()>);
def_parser!(Bind, bind_scalar, Binding, {
Query::variable()
.skip(eof())
.map(|var| Binding::BindScalar(var))
});
def_parser!(Bind, variable_or_placeholder, VariableOrPlaceholder, {
Query::variable().map(VariableOrPlaceholder::Variable)
.or(Find::placeholder().map(|_| VariableOrPlaceholder::Placeholder))
});
def_parser!(Bind, bind_coll, Binding, {
vector()
.of_exactly(Query::variable()
.skip(Find::ellipsis()))
.map(Binding::BindColl)
});
def_parser!(Bind, bind_rel, Binding, {
vector().of_exactly(
vector().of_exactly(
many1::<Vec<VariableOrPlaceholder>, _>(Bind::variable_or_placeholder())
.map(Binding::BindRel)))
});
def_parser!(Bind, bind_tuple, Binding, {
vector().of_exactly(
many1::<Vec<VariableOrPlaceholder>, _>(Bind::variable_or_placeholder())
.map(Binding::BindTuple))
});
def_parser!(Bind, binding, Binding, {
// Any one of the four binding types might apply, so we combine them with `choice`. Our parsers
// consume input, so we need to wrap them in `try` so that they operate independently.
choice([try(Bind::bind_scalar()),
try(Bind::bind_coll()),
try(Bind::bind_tuple()),
try(Bind::bind_rel())])
});
pub fn parse_find_string(string: &str) -> Result<FindQuery> {
let expr = edn::parse::value(string)?;
Find::query()
@ -467,6 +534,7 @@ mod test {
use self::combine::Parser;
use self::edn::OrderedFloat;
use self::mentat_query::{
Binding,
Element,
FindSpec,
NonIntegerConstant,
@ -475,6 +543,7 @@ mod test {
PatternValuePlace,
SrcVar,
Variable,
VariableOrPlaceholder,
};
use super::*;
@ -793,4 +862,86 @@ mod test {
FnArg::Variable(variable(vy)),
]));
}
#[test]
fn test_bind_scalar() {
let vx = edn::PlainSymbol::new("?x");
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
"(?x)",
Binding::BindScalar(variable(vx)));
}
#[test]
fn test_bind_coll() {
let vx = edn::PlainSymbol::new("?x");
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
"([?x ...])",
Binding::BindColl(variable(vx)));
}
#[test]
fn test_bind_rel() {
let vx = edn::PlainSymbol::new("?x");
let vy = edn::PlainSymbol::new("?y");
let vw = edn::PlainSymbol::new("?w");
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
"([[?x ?y _ ?w]])",
Binding::BindRel(vec![VariableOrPlaceholder::Variable(variable(vx)),
VariableOrPlaceholder::Variable(variable(vy)),
VariableOrPlaceholder::Placeholder,
VariableOrPlaceholder::Variable(variable(vw)),
]));
}
#[test]
fn test_bind_tuple() {
let vx = edn::PlainSymbol::new("?x");
let vy = edn::PlainSymbol::new("?y");
let vw = edn::PlainSymbol::new("?w");
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
"([?x ?y _ ?w])",
Binding::BindTuple(vec![VariableOrPlaceholder::Variable(variable(vx)),
VariableOrPlaceholder::Variable(variable(vy)),
VariableOrPlaceholder::Placeholder,
VariableOrPlaceholder::Variable(variable(vw)),
]));
}
#[test]
fn test_where_fn() {
assert_edn_parses_to!(Where::where_fn,
"[(f ?x 1) ?y]",
WhereClause::WhereFn(WhereFn {
operator: edn::PlainSymbol::new("f"),
args: vec![FnArg::Variable(Variable::from_valid_name("?x")),
FnArg::EntidOrInteger(1)],
binding: Binding::BindScalar(Variable::from_valid_name("?y")),
}));
assert_edn_parses_to!(Where::where_fn,
"[(f ?x) [?y ...]]",
WhereClause::WhereFn(WhereFn {
operator: edn::PlainSymbol::new("f"),
args: vec![FnArg::Variable(Variable::from_valid_name("?x"))],
binding: Binding::BindColl(Variable::from_valid_name("?y")),
}));
assert_edn_parses_to!(Where::where_fn,
"[(f) [?y _]]",
WhereClause::WhereFn(WhereFn {
operator: edn::PlainSymbol::new("f"),
args: vec![],
binding: Binding::BindTuple(vec![VariableOrPlaceholder::Variable(Variable::from_valid_name("?y")),
VariableOrPlaceholder::Placeholder]),
}));
assert_edn_parses_to!(Where::where_fn,
"[(f) [[_ ?y]]]",
WhereClause::WhereFn(WhereFn {
operator: edn::PlainSymbol::new("f"),
args: vec![],
binding: Binding::BindRel(vec![VariableOrPlaceholder::Placeholder,
VariableOrPlaceholder::Variable(Variable::from_valid_name("?y"))]),
}));
}
}

View file

@ -534,6 +534,25 @@ impl FindSpec {
}
}
// Datomic accepts variable or placeholder. DataScript accepts recursive bindings. Mentat sticks
// to the non-recursive form Datomic accepts, which is much simpler to process.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum VariableOrPlaceholder {
Placeholder,
Variable(Variable),
}
#[derive(Clone,Debug,Eq,PartialEq)]
pub enum Binding {
BindRel(Vec<VariableOrPlaceholder>),
BindColl(Variable),
BindTuple(Vec<VariableOrPlaceholder>),
BindScalar(Variable),
}
// Note that the "implicit blank" rule applies.
// A pattern with a reversed attribute — :foo/_bar — is reversed
// at the point of parsing. These `Pattern` instances only represent
@ -589,6 +608,13 @@ pub struct Predicate {
pub args: Vec<FnArg>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WhereFn {
pub operator: PlainSymbol,
pub args: Vec<FnArg>,
pub binding: Binding,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnifyVars {
/// `Implicit` means the variables in an `or` or `not` are derived from the enclosed pattern.
@ -664,7 +690,7 @@ pub enum WhereClause {
NotJoin(NotJoin),
OrJoin(OrJoin),
Pred(Predicate),
WhereFn,
WhereFn(WhereFn),
RuleExpr,
Pattern(Pattern),
}
@ -730,7 +756,7 @@ impl ContainsVariables for WhereClause {
&Pred(ref p) => p.accumulate_mentioned_variables(acc),
&Pattern(ref p) => p.accumulate_mentioned_variables(acc),
&NotJoin(ref n) => n.accumulate_mentioned_variables(acc),
&WhereFn => (),
&WhereFn(ref f) => f.accumulate_mentioned_variables(acc),
&RuleExpr => (),
}
}
@ -794,6 +820,34 @@ impl ContainsVariables for Predicate {
}
}
impl ContainsVariables for Binding {
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
match self {
&Binding::BindScalar(ref v) | &Binding::BindColl(ref v) => {
acc_ref(acc, v)
},
&Binding::BindRel(ref vs) | &Binding::BindTuple(ref vs) => {
for v in vs {
if let &VariableOrPlaceholder::Variable(ref v) = v {
acc_ref(acc, v);
}
}
},
}
}
}
impl ContainsVariables for WhereFn {
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
for arg in &self.args {
if let &FnArg::Variable(ref v) = arg {
acc_ref(acc, v)
}
}
self.binding.accumulate_mentioned_variables(acc);
}
}
fn acc_ref<T: Clone + Ord>(acc: &mut BTreeSet<T>, v: &T) {
// Roll on, reference entries!
if !acc.contains(v) {