Part 1: Parse functions in where clauses.
This commit is contained in:
parent
c6e933c396
commit
b9cbf92205
2 changed files with 207 additions and 2 deletions
|
@ -38,6 +38,7 @@ use self::mentat_parser_utils::value_and_span::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::mentat_query::{
|
use self::mentat_query::{
|
||||||
|
Binding,
|
||||||
Direction,
|
Direction,
|
||||||
Element,
|
Element,
|
||||||
FindQuery,
|
FindQuery,
|
||||||
|
@ -57,7 +58,9 @@ use self::mentat_query::{
|
||||||
SrcVar,
|
SrcVar,
|
||||||
UnifyVars,
|
UnifyVars,
|
||||||
Variable,
|
Variable,
|
||||||
|
VariableOrPlaceholder,
|
||||||
WhereClause,
|
WhereClause,
|
||||||
|
WhereFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
error_chain! {
|
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, {
|
def_parser!(Where, pattern, WhereClause, {
|
||||||
vector()
|
vector()
|
||||||
.of_exactly(
|
.of_exactly(
|
||||||
|
@ -331,6 +353,7 @@ def_parser!(Where, clause, WhereClause, {
|
||||||
try(Where::not_clause()),
|
try(Where::not_clause()),
|
||||||
|
|
||||||
try(Where::pred()),
|
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, ellipsis, "...");
|
||||||
|
|
||||||
|
def_matches_plain_symbol!(Find, placeholder, "_");
|
||||||
|
|
||||||
def_parser!(Find, find_scalar, FindSpec, {
|
def_parser!(Find, find_scalar, FindSpec, {
|
||||||
Query::variable()
|
Query::variable()
|
||||||
.skip(Find::period())
|
.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> {
|
pub fn parse_find_string(string: &str) -> Result<FindQuery> {
|
||||||
let expr = edn::parse::value(string)?;
|
let expr = edn::parse::value(string)?;
|
||||||
Find::query()
|
Find::query()
|
||||||
|
@ -467,6 +534,7 @@ mod test {
|
||||||
use self::combine::Parser;
|
use self::combine::Parser;
|
||||||
use self::edn::OrderedFloat;
|
use self::edn::OrderedFloat;
|
||||||
use self::mentat_query::{
|
use self::mentat_query::{
|
||||||
|
Binding,
|
||||||
Element,
|
Element,
|
||||||
FindSpec,
|
FindSpec,
|
||||||
NonIntegerConstant,
|
NonIntegerConstant,
|
||||||
|
@ -475,6 +543,7 @@ mod test {
|
||||||
PatternValuePlace,
|
PatternValuePlace,
|
||||||
SrcVar,
|
SrcVar,
|
||||||
Variable,
|
Variable,
|
||||||
|
VariableOrPlaceholder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -793,4 +862,86 @@ mod test {
|
||||||
FnArg::Variable(variable(vy)),
|
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"))]),
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
// Note that the "implicit blank" rule applies.
|
||||||
// A pattern with a reversed attribute — :foo/_bar — is reversed
|
// A pattern with a reversed attribute — :foo/_bar — is reversed
|
||||||
// at the point of parsing. These `Pattern` instances only represent
|
// at the point of parsing. These `Pattern` instances only represent
|
||||||
|
@ -589,6 +608,13 @@ pub struct Predicate {
|
||||||
pub args: Vec<FnArg>,
|
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)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum UnifyVars {
|
pub enum UnifyVars {
|
||||||
/// `Implicit` means the variables in an `or` or `not` are derived from the enclosed pattern.
|
/// `Implicit` means the variables in an `or` or `not` are derived from the enclosed pattern.
|
||||||
|
@ -664,7 +690,7 @@ pub enum WhereClause {
|
||||||
NotJoin(NotJoin),
|
NotJoin(NotJoin),
|
||||||
OrJoin(OrJoin),
|
OrJoin(OrJoin),
|
||||||
Pred(Predicate),
|
Pred(Predicate),
|
||||||
WhereFn,
|
WhereFn(WhereFn),
|
||||||
RuleExpr,
|
RuleExpr,
|
||||||
Pattern(Pattern),
|
Pattern(Pattern),
|
||||||
}
|
}
|
||||||
|
@ -730,7 +756,7 @@ impl ContainsVariables for WhereClause {
|
||||||
&Pred(ref p) => p.accumulate_mentioned_variables(acc),
|
&Pred(ref p) => p.accumulate_mentioned_variables(acc),
|
||||||
&Pattern(ref p) => p.accumulate_mentioned_variables(acc),
|
&Pattern(ref p) => p.accumulate_mentioned_variables(acc),
|
||||||
&NotJoin(ref n) => n.accumulate_mentioned_variables(acc),
|
&NotJoin(ref n) => n.accumulate_mentioned_variables(acc),
|
||||||
&WhereFn => (),
|
&WhereFn(ref f) => f.accumulate_mentioned_variables(acc),
|
||||||
&RuleExpr => (),
|
&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) {
|
fn acc_ref<T: Clone + Ord>(acc: &mut BTreeSet<T>, v: &T) {
|
||||||
// Roll on, reference entries!
|
// Roll on, reference entries!
|
||||||
if !acc.contains(v) {
|
if !acc.contains(v) {
|
||||||
|
|
Loading…
Reference in a new issue