Part 3: Use value_and_span
apparatus in query-parser/.
This commit is contained in:
parent
e947a32c59
commit
ff136b2546
6 changed files with 346 additions and 749 deletions
|
@ -1,261 +0,0 @@
|
|||
// Copyright 2016 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
/// ! This module defines the interface and implementation for parsing an EDN
|
||||
/// ! input into a structured Datalog query.
|
||||
/// !
|
||||
/// ! The query types are defined in the `query` crate, because they
|
||||
/// ! are shared between the parser (EDN -> query), the translator
|
||||
/// ! (query -> SQL), and the executor (query, SQL -> running code).
|
||||
/// !
|
||||
/// ! The query input can be in two forms: a 'flat' human-oriented
|
||||
/// ! sequence:
|
||||
/// !
|
||||
/// ! ```clojure
|
||||
/// ! [:find ?y :in $ ?x :where [?x :foaf/knows ?y]]
|
||||
/// ! ```
|
||||
/// !
|
||||
/// ! or a more programmatically generable map:
|
||||
/// !
|
||||
/// ! ```clojure
|
||||
/// ! {:find [?y]
|
||||
/// ! :in [$]
|
||||
/// ! :where [[?x :foaf/knows ?y]]}
|
||||
/// ! ```
|
||||
/// !
|
||||
/// ! We parse by expanding the array format into four parts, treating them as the four
|
||||
/// ! parts of the map.
|
||||
|
||||
extern crate edn;
|
||||
extern crate mentat_parser_utils;
|
||||
extern crate mentat_query;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use self::mentat_query::{
|
||||
FindQuery,
|
||||
FnArg,
|
||||
FromValue,
|
||||
Predicate,
|
||||
PredicateFn,
|
||||
SrcVar,
|
||||
Variable,
|
||||
};
|
||||
|
||||
use self::mentat_parser_utils::ValueParseError;
|
||||
|
||||
use super::parse::{
|
||||
ErrorKind,
|
||||
QueryParseResult,
|
||||
Result,
|
||||
clause_seq_to_patterns,
|
||||
};
|
||||
|
||||
use super::util::vec_to_keyword_map;
|
||||
|
||||
/// If the provided slice of EDN values are all variables as
|
||||
/// defined by `value_to_variable`, return a `Vec` of `Variable`s.
|
||||
/// Otherwise, return the unrecognized Value in a `NotAVariableError`.
|
||||
fn values_to_variables(vals: &[edn::Value]) -> Result<Vec<Variable>> {
|
||||
let mut out: Vec<Variable> = Vec::with_capacity(vals.len());
|
||||
for v in vals {
|
||||
if let Some(var) = Variable::from_value(v) {
|
||||
out.push(var);
|
||||
continue;
|
||||
}
|
||||
bail!(ErrorKind::NotAVariableError(v.clone()));
|
||||
}
|
||||
return Ok(out);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn parse_find_parts(find: &[edn::Value],
|
||||
ins: Option<&[edn::Value]>,
|
||||
with: Option<&[edn::Value]>,
|
||||
wheres: &[edn::Value])
|
||||
-> QueryParseResult {
|
||||
// :find must be an array of plain var symbols (?foo), pull expressions, and aggregates.
|
||||
// For now we only support variables and the annotations necessary to declare which
|
||||
// flavor of :find we want:
|
||||
// ?x ?y ?z = FindRel
|
||||
// [?x ...] = FindColl
|
||||
// ?x . = FindScalar
|
||||
// [?x ?y ?z] = FindTuple
|
||||
//
|
||||
// :in must be an array of sources ($), rules (%), and vars (?). For now we only support the
|
||||
// default source. :in can be omitted, in which case the default is equivalent to `:in $`.
|
||||
// TODO: process `ins`.
|
||||
let source = SrcVar::DefaultSrc;
|
||||
|
||||
// :with is an array of variables. This is simple, so we don't use a parser.
|
||||
let with_vars = if let Some(vals) = with {
|
||||
values_to_variables(vals)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// :wheres is a whole datastructure.
|
||||
let where_clauses = clause_seq_to_patterns(wheres)?;
|
||||
|
||||
super::parse::find_seq_to_find_spec(find)
|
||||
.map(|spec| {
|
||||
FindQuery {
|
||||
find_spec: spec,
|
||||
default_source: source,
|
||||
with: with_vars,
|
||||
in_vars: vec![], // TODO
|
||||
in_sources: vec![], // TODO
|
||||
where_clauses: where_clauses,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_find_map(map: BTreeMap<edn::Keyword, Vec<edn::Value>>) -> QueryParseResult {
|
||||
// Eagerly awaiting `const fn`.
|
||||
let kw_find = edn::Keyword::new("find");
|
||||
let kw_in = edn::Keyword::new("in");
|
||||
let kw_with = edn::Keyword::new("with");
|
||||
let kw_where = edn::Keyword::new("where");
|
||||
|
||||
// Oh, if only we had `guard`.
|
||||
if let Some(find) = map.get(&kw_find) {
|
||||
if let Some(wheres) = map.get(&kw_where) {
|
||||
parse_find_parts(find,
|
||||
map.get(&kw_in).map(|x| x.as_slice()),
|
||||
map.get(&kw_with).map(|x| x.as_slice()),
|
||||
wheres)
|
||||
} else {
|
||||
bail!(ErrorKind::MissingFieldError(kw_where))
|
||||
}
|
||||
} else {
|
||||
bail!(ErrorKind::MissingFieldError(kw_find))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_find_edn_map(map: BTreeMap<edn::Value, edn::Value>) -> QueryParseResult {
|
||||
// Every key must be a Keyword. Every value must be a Vec.
|
||||
let mut m = BTreeMap::new();
|
||||
|
||||
if map.is_empty() {
|
||||
return parse_find_map(m);
|
||||
}
|
||||
|
||||
for (k, v) in map {
|
||||
if let edn::Value::Keyword(kw) = k {
|
||||
if let edn::Value::Vector(vec) = v {
|
||||
m.insert(kw, vec);
|
||||
continue;
|
||||
} else {
|
||||
bail!(ErrorKind::InvalidInputError(v))
|
||||
}
|
||||
} else {
|
||||
bail!(ErrorKind::InvalidInputError(k))
|
||||
}
|
||||
}
|
||||
|
||||
parse_find_map(m)
|
||||
}
|
||||
|
||||
pub fn parse_find_string(string: &str) -> QueryParseResult {
|
||||
let expr = edn::parse::value(string)?;
|
||||
parse_find(expr.without_spans())
|
||||
}
|
||||
|
||||
pub fn parse_find(expr: edn::Value) -> QueryParseResult {
|
||||
// No `match` because scoping and use of `expr` in error handling is nuts.
|
||||
if let edn::Value::Map(m) = expr {
|
||||
return parse_find_edn_map(m);
|
||||
}
|
||||
if let edn::Value::Vector(ref v) = expr {
|
||||
if let Some(m) = vec_to_keyword_map(v) {
|
||||
return parse_find_map(m);
|
||||
}
|
||||
}
|
||||
bail!(ErrorKind::InvalidInputError(expr))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_parse {
|
||||
extern crate edn;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use self::edn::{NamespacedKeyword, PlainSymbol};
|
||||
use self::edn::types::Value;
|
||||
use super::mentat_query::{
|
||||
Element,
|
||||
FindSpec,
|
||||
Pattern,
|
||||
PatternNonValuePlace,
|
||||
PatternValuePlace,
|
||||
SrcVar,
|
||||
Variable,
|
||||
WhereClause,
|
||||
};
|
||||
use super::*;
|
||||
|
||||
// TODO: when #224 lands, fix to_keyword to be variadic.
|
||||
#[test]
|
||||
fn test_parse_find() {
|
||||
let truncated_input = edn::Value::Vector(vec![Value::from_keyword(None, "find")]);
|
||||
assert!(parse_find(truncated_input).is_err());
|
||||
|
||||
let input =
|
||||
edn::Value::Vector(vec![Value::from_keyword(None, "find"),
|
||||
Value::from_symbol(None, "?x"),
|
||||
Value::from_symbol(None, "?y"),
|
||||
Value::from_keyword(None, "where"),
|
||||
edn::Value::Vector(vec![Value::from_symbol(None, "?x"),
|
||||
Value::from_keyword("foo", "bar"),
|
||||
Value::from_symbol(None, "?y")])]);
|
||||
|
||||
let parsed = parse_find(input).unwrap();
|
||||
if let FindSpec::FindRel(elems) = parsed.find_spec {
|
||||
assert_eq!(2, elems.len());
|
||||
assert_eq!(vec![Element::Variable(Variable::from_valid_name("?x")),
|
||||
Element::Variable(Variable::from_valid_name("?y"))],
|
||||
elems);
|
||||
} else {
|
||||
panic!("Expected FindRel.");
|
||||
}
|
||||
|
||||
assert_eq!(SrcVar::DefaultSrc, parsed.default_source);
|
||||
assert_eq!(parsed.where_clauses,
|
||||
vec![
|
||||
WhereClause::Pattern(Pattern {
|
||||
source: None,
|
||||
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
|
||||
attribute: PatternNonValuePlace::Ident(Rc::new(NamespacedKeyword::new("foo", "bar"))),
|
||||
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
|
||||
tx: PatternNonValuePlace::Placeholder,
|
||||
})]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_predicate() {
|
||||
let input = "[:find ?x :where [?x :foo/bar ?y] [[< ?y 10]]]";
|
||||
let parsed = parse_find_string(input).unwrap();
|
||||
assert_eq!(parsed.where_clauses,
|
||||
vec![
|
||||
WhereClause::Pattern(Pattern {
|
||||
source: None,
|
||||
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
|
||||
attribute: PatternNonValuePlace::Ident(Rc::new(NamespacedKeyword::new("foo", "bar"))),
|
||||
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
|
||||
tx: PatternNonValuePlace::Placeholder,
|
||||
}),
|
||||
WhereClause::Pred(Predicate {
|
||||
operator: PlainSymbol::new("<"),
|
||||
args: vec![FnArg::Variable(Variable::from_valid_name("?y")),
|
||||
FnArg::EntidOrInteger(10)],
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -21,19 +21,12 @@ extern crate edn;
|
|||
#[macro_use]
|
||||
extern crate mentat_parser_utils;
|
||||
|
||||
mod util;
|
||||
mod parse;
|
||||
pub mod find;
|
||||
|
||||
pub use find::{
|
||||
parse_find,
|
||||
parse_find_string,
|
||||
};
|
||||
|
||||
pub use parse::{
|
||||
Error,
|
||||
ErrorKind,
|
||||
QueryParseResult,
|
||||
Result,
|
||||
ResultExt,
|
||||
parse_find_string,
|
||||
};
|
||||
|
|
|
@ -13,14 +13,27 @@ extern crate edn;
|
|||
extern crate mentat_parser_utils;
|
||||
extern crate mentat_query;
|
||||
|
||||
use self::combine::{eof, many, many1, optional, parser, satisfy_map, Parser, ParseResult, Stream};
|
||||
use self::combine::combinator::{choice, try};
|
||||
use std; // To refer to std::result::Result.
|
||||
|
||||
use self::combine::{eof, many, many1, optional, parser, satisfy, satisfy_map, Parser, ParseResult, Stream};
|
||||
use self::combine::combinator::{choice, or, try};
|
||||
|
||||
use self::mentat_parser_utils::{
|
||||
ResultParser,
|
||||
ValueParseError,
|
||||
};
|
||||
|
||||
use self::mentat_parser_utils::value_and_span::Stream as ValueStream;
|
||||
use self::mentat_parser_utils::value_and_span::{
|
||||
Item,
|
||||
OfParsing,
|
||||
keyword_map,
|
||||
list,
|
||||
map,
|
||||
seq,
|
||||
vector,
|
||||
};
|
||||
|
||||
use self::mentat_query::{
|
||||
Element,
|
||||
FindQuery,
|
||||
|
@ -50,17 +63,17 @@ error_chain! {
|
|||
}
|
||||
|
||||
errors {
|
||||
NotAVariableError(value: edn::Value) {
|
||||
NotAVariableError(value: edn::ValueAndSpan) {
|
||||
description("not a variable")
|
||||
display("not a variable: '{}'", value)
|
||||
}
|
||||
|
||||
FindParseError(e: ValueParseError) {
|
||||
FindParseError(e: combine::ParseError<ValueStream>) {
|
||||
description(":find parse error")
|
||||
display(":find parse error")
|
||||
}
|
||||
|
||||
WhereParseError(e: ValueParseError) {
|
||||
WhereParseError(e: combine::ParseError<ValueStream>) {
|
||||
description(":where parse error")
|
||||
display(":where parse error")
|
||||
}
|
||||
|
@ -83,321 +96,349 @@ error_chain! {
|
|||
}
|
||||
}
|
||||
|
||||
pub type WhereParseResult = Result<Vec<WhereClause>>;
|
||||
pub type FindParseResult = Result<FindSpec>;
|
||||
pub type QueryParseResult = Result<FindQuery>;
|
||||
pub struct Query;
|
||||
|
||||
pub struct Query<I>(::std::marker::PhantomData<fn(I) -> I>);
|
||||
def_parser!(Query, variable, Variable, {
|
||||
satisfy_map(Variable::from_value)
|
||||
});
|
||||
|
||||
impl<I> Query<I>
|
||||
where I: Stream<Item = edn::Value>
|
||||
{
|
||||
fn to_parsed_value<T>(r: ParseResult<T, I>) -> Option<T> {
|
||||
r.ok().map(|x| x.0)
|
||||
}
|
||||
}
|
||||
def_parser!(Query, source_var, SrcVar, {
|
||||
satisfy_map(SrcVar::from_value)
|
||||
});
|
||||
|
||||
// TODO: interning.
|
||||
def_value_satisfy_parser_fn!(Query, variable, Variable, Variable::from_value);
|
||||
def_value_satisfy_parser_fn!(Query, source_var, SrcVar, SrcVar::from_value);
|
||||
def_value_satisfy_parser_fn!(Query, predicate_fn, PredicateFn, PredicateFn::from_value);
|
||||
def_value_satisfy_parser_fn!(Query, fn_arg, FnArg, FnArg::from_value);
|
||||
def_parser!(Query, predicate_fn, PredicateFn, {
|
||||
satisfy_map(PredicateFn::from_value)
|
||||
});
|
||||
|
||||
pub struct Where<I>(::std::marker::PhantomData<fn(I) -> I>);
|
||||
def_parser!(Query, fn_arg, FnArg, {
|
||||
satisfy_map(FnArg::from_value)
|
||||
});
|
||||
|
||||
def_value_satisfy_parser_fn!(Where,
|
||||
pattern_value_place,
|
||||
PatternValuePlace,
|
||||
PatternValuePlace::from_value);
|
||||
def_value_satisfy_parser_fn!(Where,
|
||||
pattern_non_value_place,
|
||||
PatternNonValuePlace,
|
||||
PatternNonValuePlace::from_value);
|
||||
def_parser!(Query, arguments, Vec<FnArg>, {
|
||||
(many::<Vec<FnArg>, _>(Query::fn_arg()))
|
||||
});
|
||||
|
||||
fn seq<T: Into<Option<edn::Value>>>(x: T) -> Option<Vec<edn::Value>> {
|
||||
match x.into() {
|
||||
Some(edn::Value::List(items)) => Some(items.into_iter().collect()),
|
||||
Some(edn::Value::Vector(items)) => Some(items),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub struct Where;
|
||||
|
||||
/// Take a vector Value containing one vector Value, and return the `Vec` inside the inner vector.
|
||||
/// Also accepts an inner list, returning it as a `Vec`.
|
||||
fn unwrap_nested(x: edn::Value) -> Option<Vec<edn::Value>> {
|
||||
match x {
|
||||
edn::Value::Vector(mut v) => {
|
||||
seq(v.pop())
|
||||
def_parser!(Where, pattern_value_place, PatternValuePlace, {
|
||||
satisfy_map(PatternValuePlace::from_value)
|
||||
});
|
||||
|
||||
def_parser!(Where, pattern_non_value_place, PatternNonValuePlace, {
|
||||
satisfy_map(PatternNonValuePlace::from_value)
|
||||
});
|
||||
|
||||
|
||||
def_parser!(Where, and, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
s.0.as_str() == "and"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
def_value_parser_fn!(Where, and, (), input, {
|
||||
matches_plain_symbol!("and", input)
|
||||
})
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or, (), input, {
|
||||
matches_plain_symbol!("or", input)
|
||||
def_parser!(Where, or, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
s.0.as_str() == "or"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or_join, (), input, {
|
||||
matches_plain_symbol!("or-join", input)
|
||||
def_parser!(Where, or_join, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
s.0.as_str() == "or-join"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, rule_vars, Vec<Variable>, input, {
|
||||
satisfy_map(|x: edn::Value| {
|
||||
seq(x).and_then(|items| {
|
||||
let mut p = many1(Query::variable()).skip(eof());
|
||||
Query::to_parsed_value(p.parse_lazy(&items[..]).into())
|
||||
})}).parse_stream(input)
|
||||
def_parser!(Where, rule_vars, Vec<Variable>, {
|
||||
seq()
|
||||
.of(many1(Query::variable()))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or_pattern_clause, OrWhereClause, input, {
|
||||
Where::clause().map(|clause| OrWhereClause::Clause(clause)).parse_stream(input)
|
||||
def_parser!(Where, or_pattern_clause, OrWhereClause, {
|
||||
Where::clause().map(|clause| OrWhereClause::Clause(clause))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or_and_clause, OrWhereClause, input, {
|
||||
satisfy_map(|x: edn::Value| {
|
||||
seq(x).and_then(|items| {
|
||||
let mut p = Where::and()
|
||||
.with(many1(Where::clause()))
|
||||
.skip(eof())
|
||||
.map(OrWhereClause::And);
|
||||
let r: ParseResult<OrWhereClause, _> = p.parse_lazy(&items[..]).into();
|
||||
Query::to_parsed_value(r)
|
||||
})
|
||||
}).parse_stream(input)
|
||||
def_parser!(Where, or_and_clause, OrWhereClause, {
|
||||
seq()
|
||||
.of(Where::and()
|
||||
.with(many1(Where::clause()))
|
||||
.map(OrWhereClause::And))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or_where_clause, OrWhereClause, input, {
|
||||
choice([Where::or_pattern_clause(), Where::or_and_clause()]).parse_stream(input)
|
||||
def_parser!(Where, or_where_clause, OrWhereClause, {
|
||||
choice([Where::or_pattern_clause(), Where::or_and_clause()])
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or_clause, WhereClause, input, {
|
||||
satisfy_map(|x: edn::Value| {
|
||||
seq(x).and_then(|items| {
|
||||
let mut p = Where::or()
|
||||
.with(many1(Where::or_where_clause()))
|
||||
.skip(eof())
|
||||
.map(|clauses| {
|
||||
WhereClause::OrJoin(
|
||||
OrJoin {
|
||||
unify_vars: UnifyVars::Implicit,
|
||||
clauses: clauses,
|
||||
})
|
||||
});
|
||||
let r: ParseResult<WhereClause, _> = p.parse_lazy(&items[..]).into();
|
||||
Query::to_parsed_value(r)
|
||||
})
|
||||
}).parse_stream(input)
|
||||
def_parser!(Where, or_clause, WhereClause, {
|
||||
seq()
|
||||
.of(Where::or()
|
||||
.with(many1(Where::or_where_clause()))
|
||||
.map(|clauses| {
|
||||
WhereClause::OrJoin(
|
||||
OrJoin {
|
||||
unify_vars: UnifyVars::Implicit,
|
||||
clauses: clauses,
|
||||
})
|
||||
}))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, or_join_clause, WhereClause, input, {
|
||||
satisfy_map(|x: edn::Value| {
|
||||
seq(x).and_then(|items| {
|
||||
let mut p = Where::or_join()
|
||||
.with(Where::rule_vars())
|
||||
.and(many1(Where::or_where_clause()))
|
||||
.skip(eof())
|
||||
.map(|(vars, clauses)| {
|
||||
WhereClause::OrJoin(
|
||||
OrJoin {
|
||||
unify_vars: UnifyVars::Explicit(vars),
|
||||
clauses: clauses,
|
||||
})
|
||||
});
|
||||
let r: ParseResult<WhereClause, _> = p.parse_lazy(&items[..]).into();
|
||||
Query::to_parsed_value(r)
|
||||
})
|
||||
}).parse_stream(input)
|
||||
def_parser!(Where, or_join_clause, WhereClause, {
|
||||
seq()
|
||||
.of(Where::or_join()
|
||||
.with(Where::rule_vars())
|
||||
.and(many1(Where::or_where_clause()))
|
||||
.map(|(vars, clauses)| {
|
||||
WhereClause::OrJoin(
|
||||
OrJoin {
|
||||
unify_vars: UnifyVars::Explicit(vars),
|
||||
clauses: clauses,
|
||||
})
|
||||
}))
|
||||
});
|
||||
|
||||
/// A vector containing just a parenthesized filter expression.
|
||||
def_value_parser_fn!(Where, pred, WhereClause, input, {
|
||||
satisfy_map(|x: edn::Value| {
|
||||
// Accept either a list or a vector here:
|
||||
// `[(foo ?x ?y)]` or `[[foo ?x ?y]]`
|
||||
unwrap_nested(x).and_then(|items| {
|
||||
let mut p = (Query::predicate_fn(), Query::arguments())
|
||||
.skip(eof())
|
||||
.map(|(f, args)| {
|
||||
WhereClause::Pred(
|
||||
Predicate {
|
||||
operator: f.0,
|
||||
args: args,
|
||||
})
|
||||
});
|
||||
let r: ParseResult<WhereClause, _> = p.parse_lazy(&items[..]).into();
|
||||
Query::to_parsed_value(r)
|
||||
})
|
||||
}).parse_stream(input)
|
||||
def_parser!(Where, pred, WhereClause, {
|
||||
// Accept either a nested list or a nested vector here:
|
||||
// `[(foo ?x ?y)]` or `[[foo ?x ?y]]`
|
||||
vector()
|
||||
.of(seq()
|
||||
.of((Query::predicate_fn(), Query::arguments())
|
||||
.map(|(f, args)| {
|
||||
WhereClause::Pred(
|
||||
Predicate {
|
||||
operator: f.0,
|
||||
args: args,
|
||||
})
|
||||
})))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, pattern, WhereClause, input, {
|
||||
satisfy_map(|x: edn::Value| {
|
||||
if let edn::Value::Vector(y) = x {
|
||||
def_parser!(Where, pattern, WhereClause, {
|
||||
vector()
|
||||
.of(
|
||||
// While *technically* Datomic allows you to have a query like:
|
||||
// [:find … :where [[?x]]]
|
||||
// We don't -- we require at least e, a.
|
||||
let mut p = (optional(Query::source_var()), // src
|
||||
Where::pattern_non_value_place(), // e
|
||||
Where::pattern_non_value_place(), // a
|
||||
optional(Where::pattern_value_place()), // v
|
||||
optional(Where::pattern_non_value_place())) // tx
|
||||
.skip(eof())
|
||||
.map(|(src, e, a, v, tx)| {
|
||||
let v = v.unwrap_or(PatternValuePlace::Placeholder);
|
||||
let tx = tx.unwrap_or(PatternNonValuePlace::Placeholder);
|
||||
(optional(Query::source_var()), // src
|
||||
Where::pattern_non_value_place(), // e
|
||||
Where::pattern_non_value_place(), // a
|
||||
optional(Where::pattern_value_place()), // v
|
||||
optional(Where::pattern_non_value_place())) // tx
|
||||
.and_then(|(src, e, a, v, tx)| {
|
||||
let v = v.unwrap_or(PatternValuePlace::Placeholder);
|
||||
let tx = tx.unwrap_or(PatternNonValuePlace::Placeholder);
|
||||
|
||||
// Pattern::new takes care of reversal of reversed
|
||||
// attributes: [?x :foo/_bar ?y] turns into
|
||||
// [?y :foo/bar ?x].
|
||||
Pattern::new(src, e, a, v, tx).map(WhereClause::Pattern)
|
||||
});
|
||||
|
||||
// This is a bit messy: the inner conversion to a Pattern can
|
||||
// fail if the input is something like
|
||||
//
|
||||
// ```edn
|
||||
// [?x :foo/_reversed 23.4]
|
||||
// ```
|
||||
//
|
||||
// because
|
||||
//
|
||||
// ```edn
|
||||
// [23.4 :foo/reversed ?x]
|
||||
// ```
|
||||
//
|
||||
// is nonsense. That leaves us with nested optionals; we unwrap them here.
|
||||
let r: ParseResult<Option<WhereClause>, _> = p.parse_lazy(&y[..]).into();
|
||||
let v: Option<Option<WhereClause>> = Query::to_parsed_value(r);
|
||||
v.unwrap_or(None)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).parse_stream(input)
|
||||
// Pattern::new takes care of reversal of reversed
|
||||
// attributes: [?x :foo/_bar ?y] turns into
|
||||
// [?y :foo/bar ?x].
|
||||
//
|
||||
// This is a bit messy: the inner conversion to a Pattern can
|
||||
// fail if the input is something like
|
||||
//
|
||||
// ```edn
|
||||
// [?x :foo/_reversed 23.4]
|
||||
// ```
|
||||
//
|
||||
// because
|
||||
//
|
||||
// ```edn
|
||||
// [23.4 :foo/reversed ?x]
|
||||
// ```
|
||||
//
|
||||
// is nonsense. That leaves us with a nested optional, which we unwrap here.
|
||||
Pattern::new(src, e, a, v, tx)
|
||||
.map(WhereClause::Pattern)
|
||||
.ok_or(combine::primitives::Error::Expected("pattern".into()))
|
||||
}))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Query, arguments, Vec<FnArg>, input, {
|
||||
(many::<Vec<FnArg>, _>(Query::fn_arg()))
|
||||
.skip(eof())
|
||||
.parse_stream(input)
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, clause, WhereClause, input, {
|
||||
choice([Where::pattern(),
|
||||
Where::pred(),
|
||||
def_parser!(Where, clause, WhereClause, {
|
||||
choice([try(Where::pattern()),
|
||||
// It's either
|
||||
// (or-join [vars] clauses…)
|
||||
// or
|
||||
// (or clauses…)
|
||||
// We don't yet handle source vars.
|
||||
Where::or_join_clause(),
|
||||
Where::or_clause(),
|
||||
]).parse_stream(input)
|
||||
try(Where::or_join_clause()),
|
||||
try(Where::or_clause()),
|
||||
|
||||
try(Where::pred()),
|
||||
])
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Where, clauses, Vec<WhereClause>, input, {
|
||||
def_parser!(Where, clauses, Vec<WhereClause>, {
|
||||
// Right now we only support patterns and predicates. See #239 for more.
|
||||
(many1::<Vec<WhereClause>, _>(Where::clause()))
|
||||
.skip(eof())
|
||||
.parse_stream(input)
|
||||
});
|
||||
|
||||
pub struct Find<I>(::std::marker::PhantomData<fn(I) -> I>);
|
||||
pub struct Find;
|
||||
|
||||
def_value_parser_fn!(Find, period, (), input, {
|
||||
matches_plain_symbol!(".", input)
|
||||
/// TODO: extract macro for matching these `PlainSymbol` instances.
|
||||
def_parser!(Find, period, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
s.0.as_str() == "."
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, ellipsis, (), input, {
|
||||
matches_plain_symbol!("...", input)
|
||||
def_parser!(Find, ellipsis, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
s.0.as_str() == "..."
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, find_scalar, FindSpec, input, {
|
||||
def_parser!(Find, find_scalar, FindSpec, {
|
||||
Query::variable()
|
||||
.map(|var| FindSpec::FindScalar(Element::Variable(var)))
|
||||
.skip(Find::period())
|
||||
.skip(eof())
|
||||
.map(|var| FindSpec::FindScalar(Element::Variable(var)))
|
||||
.parse_stream(input)
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, find_coll, FindSpec, input, {
|
||||
satisfy_unwrap!(edn::Value::Vector, y, {
|
||||
let mut p = Query::variable()
|
||||
.skip(Find::ellipsis())
|
||||
.skip(eof())
|
||||
.map(|var| FindSpec::FindColl(Element::Variable(var)));
|
||||
let r: ParseResult<FindSpec, _> = p.parse_lazy(&y[..]).into();
|
||||
Query::to_parsed_value(r)
|
||||
})
|
||||
.parse_stream(input)
|
||||
def_parser!(Find, find_coll, FindSpec, {
|
||||
vector()
|
||||
.of(Query::variable()
|
||||
.map(|var| FindSpec::FindColl(Element::Variable(var)))
|
||||
.skip(Find::ellipsis()))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, elements, Vec<Element>, input, {
|
||||
many1::<Vec<Variable>, _>(Query::variable()).skip(eof())
|
||||
.map(|vars| {
|
||||
vars.into_iter()
|
||||
.map(Element::Variable)
|
||||
.collect()
|
||||
})
|
||||
.parse_stream(input)
|
||||
def_parser!(Find, elements, Vec<Element>, {
|
||||
many1::<Vec<Element>, _>(Query::variable().map(Element::Variable))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, find_rel, FindSpec, input, {
|
||||
Find::elements().map(FindSpec::FindRel).parse_stream(input)
|
||||
def_parser!(Find, find_rel, FindSpec, {
|
||||
Find::elements().map(FindSpec::FindRel)
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, find_tuple, FindSpec, input, {
|
||||
satisfy_unwrap!(edn::Value::Vector, y, {
|
||||
let r: ParseResult<FindSpec, _> =
|
||||
Find::elements().map(FindSpec::FindTuple).parse_lazy(&y[..]).into();
|
||||
Query::to_parsed_value(r)
|
||||
})
|
||||
.parse_stream(input)
|
||||
def_parser!(Find, find_tuple, FindSpec, {
|
||||
vector()
|
||||
.of(Find::elements().map(FindSpec::FindTuple))
|
||||
});
|
||||
|
||||
def_value_parser_fn!(Find, find, FindSpec, input, {
|
||||
// Any one of the four specs 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::<[&mut Parser<Input = I, Output = FindSpec>; 4], _>
|
||||
/// Parse a stream values into one of four find specs.
|
||||
///
|
||||
/// `:find` must be an array of plain var symbols (?foo), pull expressions, and aggregates. For now
|
||||
/// we only support variables and the annotations necessary to declare which flavor of :find we
|
||||
/// want:
|
||||
///
|
||||
///
|
||||
/// `?x ?y ?z ` = FindRel
|
||||
/// `[?x ...] ` = FindColl
|
||||
/// `?x . ` = FindScalar
|
||||
/// `[?x ?y ?z]` = FindTuple
|
||||
def_parser!(Find, spec, FindSpec, {
|
||||
// Any one of the four specs 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::<[&mut Parser<Input = _, Output = FindSpec>; 4], _>
|
||||
([&mut try(Find::find_scalar()),
|
||||
&mut try(Find::find_coll()),
|
||||
&mut try(Find::find_tuple()),
|
||||
&mut try(Find::find_rel())])
|
||||
.parse_stream(input)
|
||||
});
|
||||
|
||||
// Parse a sequence of values into one of four find specs.
|
||||
//
|
||||
// `:find` must be an array of plain var symbols (?foo), pull expressions, and aggregates.
|
||||
// For now we only support variables and the annotations necessary to declare which
|
||||
// flavor of :find we want:
|
||||
//
|
||||
//
|
||||
// `?x ?y ?z ` = FindRel
|
||||
// `[?x ...] ` = FindColl
|
||||
// `?x . ` = FindScalar
|
||||
// `[?x ?y ?z]` = FindTuple
|
||||
//
|
||||
#[allow(dead_code)]
|
||||
pub fn find_seq_to_find_spec(find: &[edn::Value]) -> FindParseResult {
|
||||
Find::find()
|
||||
.parse(find)
|
||||
.map(|x| x.0)
|
||||
.map_err::<ValueParseError, _>(|e| e.translate_position(find).into())
|
||||
.map_err(|e| Error::from_kind(ErrorKind::FindParseError(e)))
|
||||
/// TODO: extract macro for matching these `Keyword` instances.
|
||||
def_parser!(Find, literal_find, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::Keyword(ref s) = v.inner {
|
||||
s.0.as_str() == "find"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
def_parser!(Find, literal_with, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::Keyword(ref s) = v.inner {
|
||||
s.0.as_str() == "with"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
def_parser!(Find, literal_where, edn::ValueAndSpan, {
|
||||
satisfy(|v: edn::ValueAndSpan| {
|
||||
if let edn::SpannedValue::Keyword(ref s) = v.inner {
|
||||
s.0.as_str() == "where"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/// Express something close to a builder pattern for a `FindQuery`.
|
||||
enum FindQueryPart {
|
||||
FindSpec(FindSpec),
|
||||
With(Vec<Variable>),
|
||||
WhereClauses(Vec<WhereClause>),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clause_seq_to_patterns(clauses: &[edn::Value]) -> WhereParseResult {
|
||||
Where::clauses()
|
||||
.parse(clauses)
|
||||
/// This is awkward, but will do for now. We use `keyword_map()` to optionally accept vector find
|
||||
/// queries, then we use `FindQueryPart` to collect parts that have heterogeneous types; and then we
|
||||
/// construct a `FindQuery` from them.
|
||||
def_parser!(Find, query, FindQuery, {
|
||||
let p_find_spec = Find::literal_find()
|
||||
.with(vector().of(Find::spec().map(FindQueryPart::FindSpec)));
|
||||
|
||||
let p_with_vars = Find::literal_with()
|
||||
.with(vector().of(many(Query::variable()).map(FindQueryPart::With)));
|
||||
|
||||
let p_where_clauses = Find::literal_where()
|
||||
.with(vector().of(Where::clauses().map(FindQueryPart::WhereClauses))).expected(":where clauses");
|
||||
|
||||
(or(map(), keyword_map()))
|
||||
.of(many(choice::<[&mut Parser<Input = ValueStream, Output = FindQueryPart>; 3], _>([
|
||||
&mut try(p_find_spec),
|
||||
&mut try(p_with_vars),
|
||||
&mut try(p_where_clauses),
|
||||
])))
|
||||
.and_then(|parts: Vec<FindQueryPart>| -> std::result::Result<FindQuery, combine::primitives::Error<edn::ValueAndSpan, edn::ValueAndSpan>> {
|
||||
let mut find_spec = None;
|
||||
let mut with_vars = None;
|
||||
let mut where_clauses = None;
|
||||
|
||||
for part in parts {
|
||||
match part {
|
||||
FindQueryPart::FindSpec(x) => find_spec = Some(x),
|
||||
FindQueryPart::With(x) => with_vars = Some(x),
|
||||
FindQueryPart::WhereClauses(x) => where_clauses = Some(x),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(FindQuery {
|
||||
find_spec: find_spec.clone().ok_or(combine::primitives::Error::Unexpected("expected :find".into()))?,
|
||||
default_source: SrcVar::DefaultSrc,
|
||||
with: with_vars.unwrap_or(vec![]),
|
||||
in_vars: vec![], // TODO
|
||||
in_sources: vec![], // TODO
|
||||
where_clauses: where_clauses.ok_or(combine::primitives::Error::Unexpected("expected :where".into()))?,
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
pub fn parse_find_string(string: &str) -> Result<FindQuery> {
|
||||
let expr = edn::parse::value(string)?;
|
||||
Find::query()
|
||||
.parse(expr.into_atom_stream())
|
||||
.map(|x| x.0)
|
||||
.map_err::<ValueParseError, _>(|e| e.translate_position(clauses).into())
|
||||
.map_err(|e| Error::from_kind(ErrorKind::WhereParseError(e)))
|
||||
.map_err(|e| Error::from_kind(ErrorKind::FindParseError(e)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -441,10 +482,10 @@ mod test {
|
|||
let a = edn::NamespacedKeyword::new("foo", "bar");
|
||||
let v = OrderedFloat(99.9);
|
||||
let tx = edn::PlainSymbol::new("?tx");
|
||||
let input = [edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::NamespacedKeyword(a.clone()),
|
||||
edn::Value::Float(v.clone()),
|
||||
edn::Value::PlainSymbol(tx.clone())))];
|
||||
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::NamespacedKeyword(a.clone()),
|
||||
edn::Value::Float(v.clone()),
|
||||
edn::Value::PlainSymbol(tx.clone())));
|
||||
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
|
||||
source: None,
|
||||
entity: PatternNonValuePlace::Placeholder,
|
||||
|
@ -461,11 +502,11 @@ mod test {
|
|||
let a = edn::PlainSymbol::new("?a");
|
||||
let v = edn::PlainSymbol::new("?v");
|
||||
let tx = edn::PlainSymbol::new("?tx");
|
||||
let input = [edn::Value::Vector(vec!(edn::Value::PlainSymbol(s.clone()),
|
||||
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(s.clone()),
|
||||
edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::PlainSymbol(a.clone()),
|
||||
edn::Value::PlainSymbol(v.clone()),
|
||||
edn::Value::PlainSymbol(tx.clone())))];
|
||||
edn::Value::PlainSymbol(tx.clone())));
|
||||
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
|
||||
source: Some(SrcVar::NamedSrc("x".to_string())),
|
||||
entity: PatternNonValuePlace::Variable(variable(e)),
|
||||
|
@ -481,13 +522,13 @@ mod test {
|
|||
let a = edn::NamespacedKeyword::new("foo", "_bar");
|
||||
let v = OrderedFloat(99.9);
|
||||
let tx = edn::PlainSymbol::new("?tx");
|
||||
let input = [edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
||||
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::NamespacedKeyword(a.clone()),
|
||||
edn::Value::Float(v.clone()),
|
||||
edn::Value::PlainSymbol(tx.clone())))];
|
||||
edn::Value::PlainSymbol(tx.clone())));
|
||||
|
||||
let mut par = Where::pattern();
|
||||
let result = par.parse(&input[..]);
|
||||
let result = par.parse(input.with_spans().into_atom_stream());
|
||||
assert!(matches!(result, Err(_)), "Expected a parse error.");
|
||||
}
|
||||
|
||||
|
@ -497,10 +538,10 @@ mod test {
|
|||
let a = edn::NamespacedKeyword::new("foo", "_bar");
|
||||
let v = edn::PlainSymbol::new("?v");
|
||||
let tx = edn::PlainSymbol::new("?tx");
|
||||
let input = [edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
||||
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::NamespacedKeyword(a.clone()),
|
||||
edn::Value::PlainSymbol(v.clone()),
|
||||
edn::Value::PlainSymbol(tx.clone())))];
|
||||
edn::Value::PlainSymbol(tx.clone())));
|
||||
|
||||
// Note that the attribute is no longer reversed, and the entity and value have
|
||||
// switched places.
|
||||
|
@ -516,7 +557,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_rule_vars() {
|
||||
let e = edn::PlainSymbol::new("?e");
|
||||
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())])];
|
||||
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())]);
|
||||
assert_parses_to!(Where::rule_vars, input,
|
||||
vec![variable(e.clone())]);
|
||||
}
|
||||
|
@ -527,11 +568,11 @@ mod test {
|
|||
let e = edn::PlainSymbol::new("?e");
|
||||
let a = edn::PlainSymbol::new("?a");
|
||||
let v = edn::PlainSymbol::new("?v");
|
||||
let input = [edn::Value::List(
|
||||
let input = edn::Value::List(
|
||||
vec![edn::Value::PlainSymbol(oj),
|
||||
edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::PlainSymbol(a.clone()),
|
||||
edn::Value::PlainSymbol(v.clone())])].into_iter().collect())];
|
||||
edn::Value::PlainSymbol(v.clone())])].into_iter().collect());
|
||||
assert_parses_to!(Where::or_clause, input,
|
||||
WhereClause::OrJoin(
|
||||
OrJoin {
|
||||
|
@ -553,12 +594,12 @@ mod test {
|
|||
let e = edn::PlainSymbol::new("?e");
|
||||
let a = edn::PlainSymbol::new("?a");
|
||||
let v = edn::PlainSymbol::new("?v");
|
||||
let input = [edn::Value::List(
|
||||
let input = edn::Value::List(
|
||||
vec![edn::Value::PlainSymbol(oj),
|
||||
edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())]),
|
||||
edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone()),
|
||||
edn::Value::PlainSymbol(a.clone()),
|
||||
edn::Value::PlainSymbol(v.clone())])].into_iter().collect())];
|
||||
edn::Value::PlainSymbol(v.clone())])].into_iter().collect());
|
||||
assert_parses_to!(Where::or_join_clause, input,
|
||||
WhereClause::OrJoin(
|
||||
OrJoin {
|
||||
|
@ -577,16 +618,16 @@ mod test {
|
|||
#[test]
|
||||
fn test_find_sp_variable() {
|
||||
let sym = edn::PlainSymbol::new("?x");
|
||||
let input = [edn::Value::PlainSymbol(sym.clone())];
|
||||
assert_parses_to!(Query::variable, input, variable(sym));
|
||||
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone())]);
|
||||
assert_parses_to!(|| vector().of(Query::variable()), input, variable(sym));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_scalar() {
|
||||
let sym = edn::PlainSymbol::new("?x");
|
||||
let period = edn::PlainSymbol::new(".");
|
||||
let input = [edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())];
|
||||
assert_parses_to!(Find::find_scalar,
|
||||
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())]);
|
||||
assert_parses_to!(|| vector().of(Find::find_scalar()),
|
||||
input,
|
||||
FindSpec::FindScalar(Element::Variable(variable(sym))));
|
||||
}
|
||||
|
@ -595,8 +636,8 @@ mod test {
|
|||
fn test_find_coll() {
|
||||
let sym = edn::PlainSymbol::new("?x");
|
||||
let period = edn::PlainSymbol::new("...");
|
||||
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()),
|
||||
edn::Value::PlainSymbol(period.clone())])];
|
||||
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()),
|
||||
edn::Value::PlainSymbol(period.clone())]);
|
||||
assert_parses_to!(Find::find_coll,
|
||||
input,
|
||||
FindSpec::FindColl(Element::Variable(variable(sym))));
|
||||
|
@ -606,8 +647,8 @@ mod test {
|
|||
fn test_find_rel() {
|
||||
let vx = edn::PlainSymbol::new("?x");
|
||||
let vy = edn::PlainSymbol::new("?y");
|
||||
let input = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())];
|
||||
assert_parses_to!(Find::find_rel,
|
||||
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())]);
|
||||
assert_parses_to!(|| vector().of(Find::find_rel()),
|
||||
input,
|
||||
FindSpec::FindRel(vec![Element::Variable(variable(vx)),
|
||||
Element::Variable(variable(vy))]));
|
||||
|
@ -617,37 +658,11 @@ mod test {
|
|||
fn test_find_tuple() {
|
||||
let vx = edn::PlainSymbol::new("?x");
|
||||
let vy = edn::PlainSymbol::new("?y");
|
||||
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
||||
edn::Value::PlainSymbol(vy.clone())])];
|
||||
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
||||
edn::Value::PlainSymbol(vy.clone())]);
|
||||
assert_parses_to!(Find::find_tuple,
|
||||
input,
|
||||
FindSpec::FindTuple(vec![Element::Variable(variable(vx)),
|
||||
Element::Variable(variable(vy))]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_processing() {
|
||||
let vx = edn::PlainSymbol::new("?x");
|
||||
let vy = edn::PlainSymbol::new("?y");
|
||||
let ellipsis = edn::PlainSymbol::new("...");
|
||||
let period = edn::PlainSymbol::new(".");
|
||||
|
||||
let scalar = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(period.clone())];
|
||||
let tuple = [edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
||||
edn::Value::PlainSymbol(vy.clone())])];
|
||||
let coll = [edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
||||
edn::Value::PlainSymbol(ellipsis.clone())])];
|
||||
let rel = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())];
|
||||
|
||||
assert_eq!(FindSpec::FindScalar(Element::Variable(variable(vx.clone()))),
|
||||
find_seq_to_find_spec(&scalar).unwrap());
|
||||
assert_eq!(FindSpec::FindTuple(vec![Element::Variable(variable(vx.clone())),
|
||||
Element::Variable(variable(vy.clone()))]),
|
||||
find_seq_to_find_spec(&tuple).unwrap());
|
||||
assert_eq!(FindSpec::FindColl(Element::Variable(variable(vx.clone()))),
|
||||
find_seq_to_find_spec(&coll).unwrap());
|
||||
assert_eq!(FindSpec::FindRel(vec![Element::Variable(variable(vx.clone())),
|
||||
Element::Variable(variable(vy.clone()))]),
|
||||
find_seq_to_find_spec(&rel).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
// Copyright 2016 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
extern crate edn;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Take a slice of EDN values, as would be extracted from an
|
||||
/// `edn::Value::Vector`, and turn it into a map.
|
||||
///
|
||||
/// The slice must consist of subsequences of an initial plain
|
||||
/// keyword, followed by one or more non-plain-keyword values.
|
||||
///
|
||||
/// The plain keywords are used as keys into the resulting map.
|
||||
/// The values are accumulated into vectors.
|
||||
///
|
||||
/// Invalid input causes this function to return `None`.
|
||||
///
|
||||
/// TODO: this function can be generalized to take an arbitrary
|
||||
/// destructuring/break function, yielding a map with a custom
|
||||
/// key type and splitting in the right places.
|
||||
pub fn vec_to_keyword_map(vec: &[edn::Value]) -> Option<BTreeMap<edn::Keyword, Vec<edn::Value>>> {
|
||||
let mut m = BTreeMap::new();
|
||||
|
||||
if vec.is_empty() {
|
||||
return Some(m);
|
||||
}
|
||||
|
||||
if vec.len() == 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Turn something like
|
||||
//
|
||||
// `[:foo 1 2 3 :bar 4 5 6]`
|
||||
//
|
||||
// into
|
||||
//
|
||||
// `Some((:foo, [1 2 3]))`
|
||||
fn step(slice: &[edn::Value]) -> Option<(edn::Keyword, Vec<edn::Value>)> {
|
||||
// [:foo 1 2 3 :bar] is invalid: nothing follows `:bar`.
|
||||
if slice.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The first item must be a keyword.
|
||||
if let edn::Value::Keyword(ref k) = slice[0] {
|
||||
|
||||
// The second can't be: [:foo :bar 1 2 3] is invalid.
|
||||
if slice[1].is_keyword() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Accumulate items until we reach the next keyword.
|
||||
let mut acc = Vec::new();
|
||||
for v in &slice[1..] {
|
||||
if v.is_keyword() {
|
||||
break;
|
||||
}
|
||||
acc.push(v.clone());
|
||||
}
|
||||
return Some((k.clone(), acc));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
let mut bits = vec;
|
||||
while !bits.is_empty() {
|
||||
match step(bits) {
|
||||
Some((k, v)) => {
|
||||
bits = &bits[(v.len() + 1)..];
|
||||
|
||||
// Duplicate keys aren't allowed.
|
||||
if m.contains_key(&k) {
|
||||
return None;
|
||||
}
|
||||
m.insert(k, v);
|
||||
},
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
return Some(m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_to_keyword_map() {
|
||||
let foo = edn::symbols::Keyword("foo".to_string());
|
||||
let bar = edn::symbols::Keyword("bar".to_string());
|
||||
let baz = edn::symbols::Keyword("baz".to_string());
|
||||
|
||||
// [:foo 1 2 3 :bar 4]
|
||||
let input = vec!(edn::Value::Keyword(foo.clone()),
|
||||
edn::Value::Integer(1),
|
||||
edn::Value::Integer(2),
|
||||
edn::Value::Integer(3),
|
||||
edn::Value::Keyword(bar.clone()),
|
||||
edn::Value::Integer(4));
|
||||
|
||||
let m = vec_to_keyword_map(&input).unwrap();
|
||||
|
||||
assert!(m.contains_key(&foo));
|
||||
assert!(m.contains_key(&bar));
|
||||
assert!(!m.contains_key(&baz));
|
||||
|
||||
let onetwothree = vec!(edn::Value::Integer(1),
|
||||
edn::Value::Integer(2),
|
||||
edn::Value::Integer(3));
|
||||
let four = vec!(edn::Value::Integer(4));
|
||||
|
||||
assert_eq!(m.get(&foo).unwrap(), &onetwothree);
|
||||
assert_eq!(m.get(&bar).unwrap(), &four);
|
||||
|
||||
// Trailing keywords aren't allowed.
|
||||
assert_eq!(None,
|
||||
vec_to_keyword_map(&vec!(edn::Value::Keyword(foo.clone()))));
|
||||
assert_eq!(None,
|
||||
vec_to_keyword_map(&vec!(edn::Value::Keyword(foo.clone()),
|
||||
edn::Value::Integer(2),
|
||||
edn::Value::Keyword(bar.clone()))));
|
||||
|
||||
// Duplicate keywords aren't allowed.
|
||||
assert_eq!(None,
|
||||
vec_to_keyword_map(&vec!(edn::Value::Keyword(foo.clone()),
|
||||
edn::Value::Integer(2),
|
||||
edn::Value::Keyword(foo.clone()),
|
||||
edn::Value::Integer(1))));
|
||||
|
||||
// Starting with anything but a keyword isn't allowed.
|
||||
assert_eq!(None,
|
||||
vec_to_keyword_map(&vec!(edn::Value::Integer(2),
|
||||
edn::Value::Keyword(foo.clone()),
|
||||
edn::Value::Integer(1))));
|
||||
|
||||
// Consecutive keywords aren't allowed.
|
||||
assert_eq!(None,
|
||||
vec_to_keyword_map(&vec!(edn::Value::Keyword(foo.clone()),
|
||||
edn::Value::Keyword(bar.clone()),
|
||||
edn::Value::Integer(1))));
|
||||
|
||||
// Empty lists return an empty map.
|
||||
assert_eq!(BTreeMap::new(), vec_to_keyword_map(&vec!()).unwrap());
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ fn can_parse_simple_or() {
|
|||
#[test]
|
||||
fn can_parse_unit_or_join() {
|
||||
let s = "[:find ?x . :where (or-join [?x] [?x _ 15])]";
|
||||
let p = parse_find_string(s).unwrap();
|
||||
let p = parse_find_string(s).expect("to be able to parse find");
|
||||
|
||||
assert_eq!(p.find_spec,
|
||||
FindSpec::FindScalar(Element::Variable(Variable::from_valid_name("?x"))));
|
||||
|
|
|
@ -67,15 +67,15 @@ impl Variable {
|
|||
}
|
||||
|
||||
pub trait FromValue<T> {
|
||||
fn from_value(v: &edn::Value) -> Option<T>;
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<T>;
|
||||
}
|
||||
|
||||
/// If the provided EDN value is a PlainSymbol beginning with '?', return
|
||||
/// it wrapped in a Variable. If not, return None.
|
||||
/// TODO: intern strings. #398.
|
||||
impl FromValue<Variable> for Variable {
|
||||
fn from_value(v: &edn::Value) -> Option<Variable> {
|
||||
if let edn::Value::PlainSymbol(ref s) = *v {
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<Variable> {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
Variable::from_symbol(s)
|
||||
} else {
|
||||
None
|
||||
|
@ -112,8 +112,8 @@ impl fmt::Debug for Variable {
|
|||
pub struct PredicateFn(pub PlainSymbol);
|
||||
|
||||
impl FromValue<PredicateFn> for PredicateFn {
|
||||
fn from_value(v: &edn::Value) -> Option<PredicateFn> {
|
||||
if let edn::Value::PlainSymbol(ref s) = *v {
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<PredicateFn> {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
PredicateFn::from_symbol(s)
|
||||
} else {
|
||||
None
|
||||
|
@ -135,8 +135,8 @@ pub enum SrcVar {
|
|||
}
|
||||
|
||||
impl FromValue<SrcVar> for SrcVar {
|
||||
fn from_value(v: &edn::Value) -> Option<SrcVar> {
|
||||
if let edn::Value::PlainSymbol(ref s) = *v {
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<SrcVar> {
|
||||
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
||||
SrcVar::from_symbol(s)
|
||||
} else {
|
||||
None
|
||||
|
@ -184,16 +184,17 @@ pub enum FnArg {
|
|||
}
|
||||
|
||||
impl FromValue<FnArg> for FnArg {
|
||||
fn from_value(v: &edn::Value) -> Option<FnArg> {
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<FnArg> {
|
||||
// TODO: support SrcVars.
|
||||
Variable::from_value(v)
|
||||
Variable::from_value(v.clone()) // TODO: don't clone!
|
||||
.and_then(|v| Some(FnArg::Variable(v)))
|
||||
.or_else(||
|
||||
match v {
|
||||
&edn::Value::Integer(i) => Some(FnArg::EntidOrInteger(i)),
|
||||
&edn::Value::Float(f) => Some(FnArg::Constant(NonIntegerConstant::Float(f))),
|
||||
.or_else(|| {
|
||||
println!("from_value {}", v.inner);
|
||||
match v.inner {
|
||||
edn::SpannedValue::Integer(i) => Some(FnArg::EntidOrInteger(i)),
|
||||
edn::SpannedValue::Float(f) => Some(FnArg::Constant(NonIntegerConstant::Float(f))),
|
||||
_ => unimplemented!(),
|
||||
})
|
||||
}})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,14 +235,14 @@ impl PatternNonValuePlace {
|
|||
}
|
||||
|
||||
impl FromValue<PatternNonValuePlace> for PatternNonValuePlace {
|
||||
fn from_value(v: &edn::Value) -> Option<PatternNonValuePlace> {
|
||||
match v {
|
||||
&edn::Value::Integer(x) => if x >= 0 {
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<PatternNonValuePlace> {
|
||||
match v.inner {
|
||||
edn::SpannedValue::Integer(x) => if x >= 0 {
|
||||
Some(PatternNonValuePlace::Entid(x))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
&edn::Value::PlainSymbol(ref x) => if x.0.as_str() == "_" {
|
||||
edn::SpannedValue::PlainSymbol(ref x) => if x.0.as_str() == "_" {
|
||||
Some(PatternNonValuePlace::Placeholder)
|
||||
} else {
|
||||
if let Some(v) = Variable::from_symbol(x) {
|
||||
|
@ -250,7 +251,7 @@ impl FromValue<PatternNonValuePlace> for PatternNonValuePlace {
|
|||
None
|
||||
}
|
||||
},
|
||||
&edn::Value::NamespacedKeyword(ref x) =>
|
||||
edn::SpannedValue::NamespacedKeyword(ref x) =>
|
||||
Some(PatternNonValuePlace::Ident(Rc::new(x.clone()))),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -276,23 +277,23 @@ pub enum PatternValuePlace {
|
|||
}
|
||||
|
||||
impl FromValue<PatternValuePlace> for PatternValuePlace {
|
||||
fn from_value(v: &edn::Value) -> Option<PatternValuePlace> {
|
||||
match v {
|
||||
&edn::Value::Integer(x) =>
|
||||
fn from_value(v: edn::ValueAndSpan) -> Option<PatternValuePlace> {
|
||||
match v.inner {
|
||||
edn::SpannedValue::Integer(x) =>
|
||||
Some(PatternValuePlace::EntidOrInteger(x)),
|
||||
&edn::Value::PlainSymbol(ref x) if x.0.as_str() == "_" =>
|
||||
edn::SpannedValue::PlainSymbol(ref x) if x.0.as_str() == "_" =>
|
||||
Some(PatternValuePlace::Placeholder),
|
||||
&edn::Value::PlainSymbol(ref x) =>
|
||||
edn::SpannedValue::PlainSymbol(ref x) =>
|
||||
Variable::from_symbol(x).map(PatternValuePlace::Variable),
|
||||
&edn::Value::NamespacedKeyword(ref x) =>
|
||||
edn::SpannedValue::NamespacedKeyword(ref x) =>
|
||||
Some(PatternValuePlace::IdentOrKeyword(Rc::new(x.clone()))),
|
||||
&edn::Value::Boolean(x) =>
|
||||
edn::SpannedValue::Boolean(x) =>
|
||||
Some(PatternValuePlace::Constant(NonIntegerConstant::Boolean(x))),
|
||||
&edn::Value::Float(x) =>
|
||||
edn::SpannedValue::Float(x) =>
|
||||
Some(PatternValuePlace::Constant(NonIntegerConstant::Float(x))),
|
||||
&edn::Value::BigInteger(ref x) =>
|
||||
edn::SpannedValue::BigInteger(ref x) =>
|
||||
Some(PatternValuePlace::Constant(NonIntegerConstant::BigInteger(x.clone()))),
|
||||
&edn::Value::Text(ref x) =>
|
||||
edn::SpannedValue::Text(ref x) =>
|
||||
// TODO: intern strings. #398.
|
||||
Some(PatternValuePlace::Constant(NonIntegerConstant::Text(Rc::new(x.clone())))),
|
||||
_ => None,
|
||||
|
@ -685,4 +686,4 @@ impl ContainsVariables for Pattern {
|
|||
acc_ref(acc, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue