Parse basic :find and :where clauses. (#211) r=nalexander
* Make Variable::from_symbol public. * Implement basic parsing of queries. * Use pinned dependencies the hard way to fix Travis. * Bump ordered-float dependency to 0.4.0. * Error coercions to use ?, and finishing the find interface.
This commit is contained in:
parent
cd5f0d642c
commit
5b770a54cd
11 changed files with 569 additions and 243 deletions
|
@ -7,7 +7,7 @@ error-chain = "0.8.0"
|
||||||
lazy_static = "0.2.2"
|
lazy_static = "0.2.2"
|
||||||
# TODO: don't depend on num and ordered-float; expose helpers in edn abstracting necessary constructors.
|
# TODO: don't depend on num and ordered-float; expose helpers in edn abstracting necessary constructors.
|
||||||
num = "0.1.35"
|
num = "0.1.35"
|
||||||
ordered-float = "0.3.0"
|
ordered-float = "0.4.0"
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
|
@ -11,7 +11,7 @@ readme = "./README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num = "0.1.35"
|
num = "0.1.35"
|
||||||
ordered-float = "0.3.0"
|
ordered-float = "0.4.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
peg = "0.4"
|
peg = "0.4"
|
||||||
|
|
|
@ -4,6 +4,8 @@ version = "0.0.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
combine = "2.1.1"
|
combine = "2.1.1"
|
||||||
|
matches = "0.1"
|
||||||
|
ordered-float = "0.4.0"
|
||||||
|
|
||||||
[dependencies.edn]
|
[dependencies.edn]
|
||||||
path = "../edn"
|
path = "../edn"
|
||||||
|
|
|
@ -1,41 +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 combine;
|
|
||||||
extern crate edn;
|
|
||||||
extern crate mentat_query;
|
|
||||||
|
|
||||||
use self::mentat_query::{FindSpec, FindQuery};
|
|
||||||
|
|
||||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
|
||||||
pub struct NotAVariableError(pub edn::Value);
|
|
||||||
|
|
||||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
|
||||||
pub enum FindParseError {
|
|
||||||
Err,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
|
||||||
pub enum QueryParseError {
|
|
||||||
InvalidInput(edn::Value),
|
|
||||||
EdnParseError(edn::parse::ParseError),
|
|
||||||
MissingField(edn::Keyword),
|
|
||||||
FindParseError(FindParseError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<edn::parse::ParseError> for QueryParseError {
|
|
||||||
fn from(err: edn::parse::ParseError) -> QueryParseError {
|
|
||||||
QueryParseError::EdnParseError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type FindParseResult = Result<FindSpec, FindParseError>;
|
|
||||||
pub type QueryParseResult = Result<FindQuery, QueryParseError>;
|
|
||||||
|
|
|
@ -38,10 +38,36 @@ extern crate mentat_query;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use self::mentat_query::{FindQuery, SrcVar};
|
use self::mentat_query::{
|
||||||
|
FindQuery,
|
||||||
|
FromValue,
|
||||||
|
SrcVar,
|
||||||
|
Variable,
|
||||||
|
};
|
||||||
|
|
||||||
use super::error::{QueryParseError, QueryParseResult};
|
use super::parse::{
|
||||||
use super::util::{values_to_variables, vec_to_keyword_map};
|
NotAVariableError,
|
||||||
|
QueryParseError,
|
||||||
|
QueryParseResult,
|
||||||
|
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>, NotAVariableError> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return Err(NotAVariableError(v.clone()));
|
||||||
|
}
|
||||||
|
return Ok(out);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn parse_find_parts(find: &[edn::Value],
|
fn parse_find_parts(find: &[edn::Value],
|
||||||
|
@ -63,19 +89,27 @@ fn parse_find_parts(find: &[edn::Value],
|
||||||
let source = SrcVar::DefaultSrc;
|
let source = SrcVar::DefaultSrc;
|
||||||
|
|
||||||
// :with is an array of variables. This is simple, so we don't use a parser.
|
// :with is an array of variables. This is simple, so we don't use a parser.
|
||||||
let with_vars = with.map(values_to_variables);
|
let with_vars = if let Some(vals) = with {
|
||||||
|
values_to_variables(vals)?
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
// :wheres is a whole datastructure.
|
// :wheres is a whole datastructure.
|
||||||
|
let where_clauses = clause_seq_to_patterns(wheres)?;
|
||||||
|
|
||||||
super::parse::find_seq_to_find_spec(find)
|
super::parse::find_seq_to_find_spec(find)
|
||||||
.map(|spec| {
|
.map(|spec| {
|
||||||
FindQuery {
|
FindQuery {
|
||||||
find_spec: spec,
|
find_spec: spec,
|
||||||
default_source: source,
|
default_source: source,
|
||||||
|
with: with_vars,
|
||||||
|
in_vars: vec!(), // TODO
|
||||||
|
in_sources: vec!(), // TODO
|
||||||
|
where_clauses: where_clauses,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map_err(QueryParseError::FindParseError)
|
.map_err(QueryParseError::FindParseError)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_find_map(map: BTreeMap<edn::Keyword, Vec<edn::Value>>) -> QueryParseResult {
|
fn parse_find_map(map: BTreeMap<edn::Keyword, Vec<edn::Value>>) -> QueryParseResult {
|
||||||
|
@ -136,3 +170,64 @@ pub fn parse_find(expr: edn::Value) -> QueryParseResult {
|
||||||
}
|
}
|
||||||
return Err(QueryParseError::InvalidInput(expr));
|
return Err(QueryParseError::InvalidInput(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_parse {
|
||||||
|
extern crate edn;
|
||||||
|
|
||||||
|
use self::edn::{NamespacedKeyword, PlainSymbol};
|
||||||
|
use self::edn::types::{to_keyword, to_symbol};
|
||||||
|
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![to_keyword(None, "find")]);
|
||||||
|
assert!(parse_find(truncated_input).is_err());
|
||||||
|
|
||||||
|
let input = edn::Value::Vector(vec![
|
||||||
|
to_keyword(None, "find"),
|
||||||
|
to_symbol(None, "?x"),
|
||||||
|
to_symbol(None, "?y"),
|
||||||
|
to_keyword(None, "where"),
|
||||||
|
edn::Value::Vector(vec![
|
||||||
|
to_symbol(None, "?x"),
|
||||||
|
to_keyword("foo", "bar"),
|
||||||
|
to_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(edn::PlainSymbol::new("?x"))),
|
||||||
|
Element::Variable(Variable(edn::PlainSymbol::new("?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(PlainSymbol::new("?x"))),
|
||||||
|
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
|
||||||
|
value: PatternValuePlace::Variable(Variable(PlainSymbol::new("?y"))),
|
||||||
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
|
})]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,10 +8,14 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate matches;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate mentat_parser_utils;
|
extern crate mentat_parser_utils;
|
||||||
|
|
||||||
mod error;
|
#[macro_use]
|
||||||
|
mod parser_util;
|
||||||
mod util;
|
mod util;
|
||||||
mod parse;
|
mod parse;
|
||||||
pub mod find;
|
pub mod find;
|
||||||
|
|
|
@ -10,156 +10,207 @@
|
||||||
|
|
||||||
extern crate combine;
|
extern crate combine;
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
|
extern crate mentat_parser_utils;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
|
||||||
use self::combine::{eof, many1, parser, satisfy_map, Parser, ParseResult, Stream};
|
use self::mentat_parser_utils::ResultParser;
|
||||||
use self::combine::combinator::{Expected, FnParser, choice, try};
|
use self::combine::{eof, many1, optional, parser, satisfy_map, Parser, ParseResult, Stream};
|
||||||
use self::edn::Value::PlainSymbol;
|
use self::combine::combinator::{choice, try};
|
||||||
use self::mentat_query::{Element, FromValue, FindSpec, Variable};
|
use self::mentat_query::{
|
||||||
|
Element,
|
||||||
|
FindQuery,
|
||||||
|
FindSpec,
|
||||||
|
FromValue,
|
||||||
|
Pattern,
|
||||||
|
PatternNonValuePlace,
|
||||||
|
PatternValuePlace,
|
||||||
|
SrcVar,
|
||||||
|
Variable,
|
||||||
|
WhereClause,
|
||||||
|
};
|
||||||
|
|
||||||
use super::error::{FindParseError, FindParseResult};
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct NotAVariableError(pub edn::Value);
|
||||||
|
|
||||||
pub struct FindSp<I>(::std::marker::PhantomData<fn(I) -> I>);
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
type FindSpParser<O, I> = Expected<FnParser<I, fn(I) -> ParseResult<O, I>>>;
|
pub enum FindParseError {
|
||||||
|
Err,
|
||||||
fn fn_parser<O, I>(f: fn(I) -> ParseResult<O, I>, err: &'static str) -> FindSpParser<O, I>
|
|
||||||
where I: Stream<Item = edn::Value>
|
|
||||||
{
|
|
||||||
parser(f).expected(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `satisfy_unwrap!` makes it a little easier to implement a `satisfy_map`
|
#[allow(dead_code)]
|
||||||
/// body that matches a particular `Value` enum case, otherwise returning `None`.
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
macro_rules! satisfy_unwrap {
|
pub enum WhereParseError {
|
||||||
( $cas: path, $var: ident, $body: block ) => {
|
Err,
|
||||||
satisfy_map(|x: edn::Value| if let $cas($var) = x $body else { None })
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Debug,Eq,PartialEq)]
|
||||||
|
pub enum QueryParseError {
|
||||||
|
InvalidInput(edn::Value),
|
||||||
|
EdnParseError(edn::parse::ParseError),
|
||||||
|
MissingField(edn::Keyword),
|
||||||
|
FindParseError(FindParseError),
|
||||||
|
WhereParseError(WhereParseError),
|
||||||
|
WithParseError(NotAVariableError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<edn::parse::ParseError> for QueryParseError {
|
||||||
|
fn from(err: edn::parse::ParseError) -> QueryParseError {
|
||||||
|
QueryParseError::EdnParseError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> FindSp<I>
|
impl From<WhereParseError> for QueryParseError {
|
||||||
|
fn from(err: WhereParseError) -> QueryParseError {
|
||||||
|
QueryParseError::WhereParseError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NotAVariableError> for QueryParseError {
|
||||||
|
fn from(err: NotAVariableError) -> QueryParseError {
|
||||||
|
QueryParseError::WithParseError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type WhereParseResult = Result<Vec<WhereClause>, WhereParseError>;
|
||||||
|
pub type FindParseResult = Result<FindSpec, FindParseError>;
|
||||||
|
pub type QueryParseResult = Result<FindQuery, QueryParseError>;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Query<I>(::std::marker::PhantomData<fn(I) -> I>);
|
||||||
|
|
||||||
|
impl<I> Query<I>
|
||||||
where I: Stream<Item = edn::Value>
|
where I: Stream<Item = edn::Value>
|
||||||
{
|
{
|
||||||
fn variable() -> FindSpParser<Variable, I> {
|
|
||||||
fn_parser(FindSp::<I>::variable_, "variable")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variable_(input: I) -> ParseResult<Variable, I> {
|
|
||||||
satisfy_map(|x: edn::Value| Variable::from_value(&x)).parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn period() -> FindSpParser<(), I> {
|
|
||||||
fn_parser(FindSp::<I>::period_, "period")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn period_(input: I) -> ParseResult<(), I> {
|
|
||||||
satisfy_map(|x: edn::Value| {
|
|
||||||
if let PlainSymbol(ref s) = x {
|
|
||||||
if s.0.as_str() == "." {
|
|
||||||
return Some(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
})
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ellipsis() -> FindSpParser<(), I> {
|
|
||||||
fn_parser(FindSp::<I>::ellipsis_, "ellipsis")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ellipsis_(input: I) -> ParseResult<(), I> {
|
|
||||||
satisfy_map(|x: edn::Value| {
|
|
||||||
if let PlainSymbol(ref s) = x {
|
|
||||||
if s.0.as_str() == "..." {
|
|
||||||
return Some(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
})
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_scalar() -> FindSpParser<FindSpec, I> {
|
|
||||||
fn_parser(FindSp::<I>::find_scalar_, "find_scalar")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_scalar_(input: I) -> ParseResult<FindSpec, I> {
|
|
||||||
(FindSp::variable(), FindSp::period(), eof())
|
|
||||||
.map(|(var, _, _)| FindSpec::FindScalar(Element::Variable(var)))
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_coll() -> FindSpParser<FindSpec, I> {
|
|
||||||
fn_parser(FindSp::<I>::find_coll_, "find_coll")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_coll_(input: I) -> ParseResult<FindSpec, I> {
|
|
||||||
satisfy_unwrap!(edn::Value::Vector, y, {
|
|
||||||
let mut p = (FindSp::variable(), FindSp::ellipsis(), eof())
|
|
||||||
.map(|(var, _, _)| FindSpec::FindColl(Element::Variable(var)));
|
|
||||||
let r: ParseResult<FindSpec, _> = p.parse_lazy(&y[..]).into();
|
|
||||||
FindSp::to_parsed_value(r)
|
|
||||||
})
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn elements() -> FindSpParser<Vec<Element>, I> {
|
|
||||||
fn_parser(FindSp::<I>::elements_, "elements")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn elements_(input: I) -> ParseResult<Vec<Element>, I> {
|
|
||||||
(many1::<Vec<Variable>, _>(FindSp::variable()), eof())
|
|
||||||
.map(|(vars, _)| {
|
|
||||||
vars.into_iter()
|
|
||||||
.map(Element::Variable)
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_rel() -> FindSpParser<FindSpec, I> {
|
|
||||||
fn_parser(FindSp::<I>::find_rel_, "find_rel")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_rel_(input: I) -> ParseResult<FindSpec, I> {
|
|
||||||
FindSp::elements().map(FindSpec::FindRel).parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_tuple() -> FindSpParser<FindSpec, I> {
|
|
||||||
fn_parser(FindSp::<I>::find_tuple_, "find_tuple")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_tuple_(input: I) -> ParseResult<FindSpec, I> {
|
|
||||||
satisfy_unwrap!(edn::Value::Vector, y, {
|
|
||||||
let r: ParseResult<FindSpec, _> =
|
|
||||||
FindSp::elements().map(FindSpec::FindTuple).parse_lazy(&y[..]).into();
|
|
||||||
FindSp::to_parsed_value(r)
|
|
||||||
})
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find() -> FindSpParser<FindSpec, I> {
|
|
||||||
fn_parser(FindSp::<I>::find_, "find")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_(input: I) -> ParseResult<FindSpec, I> {
|
|
||||||
// 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],
|
|
||||||
_>([&mut try(FindSp::find_scalar()),
|
|
||||||
&mut try(FindSp::find_coll()),
|
|
||||||
&mut try(FindSp::find_tuple()),
|
|
||||||
&mut try(FindSp::find_rel())])
|
|
||||||
.parse_stream(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_parsed_value<T>(r: ParseResult<T, I>) -> Option<T> {
|
fn to_parsed_value<T>(r: ParseResult<T, I>) -> Option<T> {
|
||||||
r.ok().map(|x| x.0)
|
r.ok().map(|x| x.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def_value_satisfy_parser_fn!(Query, variable, Variable, Variable::from_value);
|
||||||
|
def_value_satisfy_parser_fn!(Query, source_var, SrcVar, SrcVar::from_value);
|
||||||
|
|
||||||
|
pub struct Where<I>(::std::marker::PhantomData<fn(I) -> I>);
|
||||||
|
|
||||||
|
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_value_parser_fn!(Where, pattern, Pattern, input, {
|
||||||
|
satisfy_map(|x: edn::Value| {
|
||||||
|
if let edn::Value::Vector(y) = x {
|
||||||
|
// While *technically* Datomic allows you to have a query like:
|
||||||
|
// [:find … :where [[?x]]]
|
||||||
|
// We don't -- we require at list 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
|
||||||
|
eof())
|
||||||
|
.map(|(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)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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<Pattern>, _> = p.parse_lazy(&y[..]).into();
|
||||||
|
let v: Option<Option<Pattern>> = r.ok().map(|x| x.0);
|
||||||
|
v.unwrap_or(None)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).parse_stream(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
def_value_parser_fn!(Where, clauses, Vec<WhereClause>, input, {
|
||||||
|
// Right now we only support patterns. See #239 for more.
|
||||||
|
(many1::<Vec<Pattern>, _>(Where::pattern()), eof())
|
||||||
|
.map(|(patterns, _)| {
|
||||||
|
patterns.into_iter().map(WhereClause::Pattern).collect()
|
||||||
|
})
|
||||||
|
.parse_stream(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct Find<I>(::std::marker::PhantomData<fn(I) -> I>);
|
||||||
|
|
||||||
|
def_value_parser_fn!(Find, period, (), input, {
|
||||||
|
matches_plain_symbol!(".", input)
|
||||||
|
});
|
||||||
|
|
||||||
|
def_value_parser_fn!(Find, ellipsis, (), input, {
|
||||||
|
matches_plain_symbol!("...", input)
|
||||||
|
});
|
||||||
|
|
||||||
|
def_value_parser_fn!(Find, find_scalar, FindSpec, input, {
|
||||||
|
(Query::variable(), Find::period(), 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(), Find::ellipsis(), 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_value_parser_fn!(Find, elements, Vec<Element>, input, {
|
||||||
|
(many1::<Vec<Variable>, _>(Query::variable()), eof())
|
||||||
|
.map(|(vars, _)| {
|
||||||
|
vars.into_iter()
|
||||||
|
.map(Element::Variable)
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.parse_stream(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
def_value_parser_fn!(Find, find_rel, FindSpec, input, {
|
||||||
|
Find::elements().map(FindSpec::FindRel).parse_stream(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
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_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], _>
|
||||||
|
([&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.
|
// 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.
|
// `:find` must be an array of plain var symbols (?foo), pull expressions, and aggregates.
|
||||||
|
@ -172,29 +223,131 @@ impl<I> FindSp<I>
|
||||||
// `?x . ` = FindScalar
|
// `?x . ` = FindScalar
|
||||||
// `[?x ?y ?z]` = FindTuple
|
// `[?x ?y ?z]` = FindTuple
|
||||||
//
|
//
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn find_seq_to_find_spec(find: &[edn::Value]) -> FindParseResult {
|
pub fn find_seq_to_find_spec(find: &[edn::Value]) -> FindParseResult {
|
||||||
FindSp::find()
|
Find::find()
|
||||||
.parse(find)
|
.parse(find)
|
||||||
.map(|x| x.0)
|
.map(|x| x.0)
|
||||||
.map_err(|_| FindParseError::Err)
|
.map_err(|_| FindParseError::Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn clause_seq_to_patterns(clauses: &[edn::Value]) -> WhereParseResult {
|
||||||
|
Where::clauses()
|
||||||
|
.parse(clauses)
|
||||||
|
.map(|x| x.0)
|
||||||
|
.map_err(|_| WhereParseError::Err)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
extern crate combine;
|
extern crate combine;
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
extern crate ordered_float;
|
||||||
|
|
||||||
use self::combine::Parser;
|
use self::combine::Parser;
|
||||||
use self::mentat_query::{Element, FindSpec, Variable};
|
use self::ordered_float::OrderedFloat;
|
||||||
|
use self::mentat_query::{
|
||||||
|
Element,
|
||||||
|
FindSpec,
|
||||||
|
NonIntegerConstant,
|
||||||
|
Pattern,
|
||||||
|
PatternNonValuePlace,
|
||||||
|
PatternValuePlace,
|
||||||
|
SrcVar,
|
||||||
|
Variable,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pattern_mixed() {
|
||||||
|
let e = edn::PlainSymbol::new("_");
|
||||||
|
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())))];
|
||||||
|
assert_parses_to!(Where::pattern, input, Pattern {
|
||||||
|
source: None,
|
||||||
|
entity: PatternNonValuePlace::Placeholder,
|
||||||
|
attribute: PatternNonValuePlace::Ident(a),
|
||||||
|
value: PatternValuePlace::Constant(NonIntegerConstant::Float(v)),
|
||||||
|
tx: PatternNonValuePlace::Variable(Variable(tx)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pattern_vars() {
|
||||||
|
let s = edn::PlainSymbol::new("$x");
|
||||||
|
let e = edn::PlainSymbol::new("?e");
|
||||||
|
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()),
|
||||||
|
edn::Value::PlainSymbol(e.clone()),
|
||||||
|
edn::Value::PlainSymbol(a.clone()),
|
||||||
|
edn::Value::PlainSymbol(v.clone()),
|
||||||
|
edn::Value::PlainSymbol(tx.clone())))];
|
||||||
|
assert_parses_to!(Where::pattern, input, Pattern {
|
||||||
|
source: Some(SrcVar::NamedSrc("x".to_string())),
|
||||||
|
entity: PatternNonValuePlace::Variable(Variable(e)),
|
||||||
|
attribute: PatternNonValuePlace::Variable(Variable(a)),
|
||||||
|
value: PatternValuePlace::Variable(Variable(v)),
|
||||||
|
tx: PatternNonValuePlace::Variable(Variable(tx)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pattern_reversed_invalid() {
|
||||||
|
let e = edn::PlainSymbol::new("_");
|
||||||
|
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 mut par = Where::pattern();
|
||||||
|
let result = par.parse(&input[..]);
|
||||||
|
assert!(matches!(result, Err(_)), "Expected a parse error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pattern_reversed() {
|
||||||
|
let e = edn::PlainSymbol::new("_");
|
||||||
|
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()),
|
||||||
|
edn::Value::NamespacedKeyword(a.clone()),
|
||||||
|
edn::Value::PlainSymbol(v.clone()),
|
||||||
|
edn::Value::PlainSymbol(tx.clone())))];
|
||||||
|
|
||||||
|
// Note that the attribute is no longer reversed, and the entity and value have
|
||||||
|
// switched places.
|
||||||
|
assert_parses_to!(Where::pattern, input, Pattern {
|
||||||
|
source: None,
|
||||||
|
entity: PatternNonValuePlace::Variable(Variable(v)),
|
||||||
|
attribute: PatternNonValuePlace::Ident(edn::NamespacedKeyword::new("foo", "bar")),
|
||||||
|
value: PatternValuePlace::Placeholder,
|
||||||
|
tx: PatternNonValuePlace::Variable(Variable(tx)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_find_sp_variable() {
|
fn test_find_sp_variable() {
|
||||||
let sym = edn::PlainSymbol::new("?x");
|
let sym = edn::PlainSymbol::new("?x");
|
||||||
let input = [edn::Value::PlainSymbol(sym.clone())];
|
let input = [edn::Value::PlainSymbol(sym.clone())];
|
||||||
assert_parses_to!(FindSp::variable, input, Variable(sym));
|
assert_parses_to!(Query::variable, input, Variable(sym));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -202,7 +355,7 @@ mod test {
|
||||||
let sym = edn::PlainSymbol::new("?x");
|
let sym = edn::PlainSymbol::new("?x");
|
||||||
let period = edn::PlainSymbol::new(".");
|
let period = edn::PlainSymbol::new(".");
|
||||||
let input = [edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())];
|
let input = [edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())];
|
||||||
assert_parses_to!(FindSp::find_scalar,
|
assert_parses_to!(Find::find_scalar,
|
||||||
input,
|
input,
|
||||||
FindSpec::FindScalar(Element::Variable(Variable(sym))));
|
FindSpec::FindScalar(Element::Variable(Variable(sym))));
|
||||||
}
|
}
|
||||||
|
@ -213,7 +366,7 @@ mod test {
|
||||||
let period = edn::PlainSymbol::new("...");
|
let period = edn::PlainSymbol::new("...");
|
||||||
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()),
|
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()),
|
||||||
edn::Value::PlainSymbol(period.clone())])];
|
edn::Value::PlainSymbol(period.clone())])];
|
||||||
assert_parses_to!(FindSp::find_coll,
|
assert_parses_to!(Find::find_coll,
|
||||||
input,
|
input,
|
||||||
FindSpec::FindColl(Element::Variable(Variable(sym))));
|
FindSpec::FindColl(Element::Variable(Variable(sym))));
|
||||||
}
|
}
|
||||||
|
@ -223,7 +376,7 @@ mod test {
|
||||||
let vx = edn::PlainSymbol::new("?x");
|
let vx = edn::PlainSymbol::new("?x");
|
||||||
let vy = edn::PlainSymbol::new("?y");
|
let vy = edn::PlainSymbol::new("?y");
|
||||||
let input = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())];
|
let input = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())];
|
||||||
assert_parses_to!(FindSp::find_rel,
|
assert_parses_to!(Find::find_rel,
|
||||||
input,
|
input,
|
||||||
FindSpec::FindRel(vec![Element::Variable(Variable(vx)),
|
FindSpec::FindRel(vec![Element::Variable(Variable(vx)),
|
||||||
Element::Variable(Variable(vy))]));
|
Element::Variable(Variable(vy))]));
|
||||||
|
@ -235,7 +388,7 @@ mod test {
|
||||||
let vy = edn::PlainSymbol::new("?y");
|
let vy = edn::PlainSymbol::new("?y");
|
||||||
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
||||||
edn::Value::PlainSymbol(vy.clone())])];
|
edn::Value::PlainSymbol(vy.clone())])];
|
||||||
assert_parses_to!(FindSp::find_tuple,
|
assert_parses_to!(Find::find_tuple,
|
||||||
input,
|
input,
|
||||||
FindSpec::FindTuple(vec![Element::Variable(Variable(vx)),
|
FindSpec::FindTuple(vec![Element::Variable(Variable(vx)),
|
||||||
Element::Variable(Variable(vy))]));
|
Element::Variable(Variable(vy))]));
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
extern crate combine;
|
extern crate combine;
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
|
|
||||||
|
extern crate mentat_parser_utils;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
|
||||||
/// Generate a `satisfy_map` expression that matches a `PlainSymbol`
|
/// Generate a `satisfy_map` expression that matches a `PlainSymbol`
|
||||||
|
|
|
@ -9,33 +9,9 @@
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
extern crate mentat_query;
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use self::mentat_query::{FromValue, Variable};
|
|
||||||
use super::error::NotAVariableError;
|
|
||||||
|
|
||||||
/// If the provided slice of EDN values are all variables as
|
|
||||||
/// defined by `value_to_variable`, return a Vec of Variables.
|
|
||||||
/// Otherwise, return the unrecognized Value.
|
|
||||||
pub fn values_to_variables(vals: &[edn::Value]) -> Result<Vec<Variable>, NotAVariableError> {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return Err(NotAVariableError(v.clone()));
|
|
||||||
}
|
|
||||||
return Ok(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_values_to_variables() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Take a slice of EDN values, as would be extracted from an
|
/// Take a slice of EDN values, as would be extracted from an
|
||||||
/// `edn::Value::Vector`, and turn it into a map.
|
/// `edn::Value::Vector`, and turn it into a map.
|
||||||
///
|
///
|
||||||
|
|
|
@ -3,8 +3,8 @@ name = "mentat_query"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
[dependencies.num] # For EDN value usage.
|
num = "0.1.35"
|
||||||
[dependencies.ordered-float]
|
ordered-float = "0.4.0"
|
||||||
|
|
||||||
[dependencies.edn]
|
[dependencies.edn]
|
||||||
path = "../edn"
|
path = "../edn"
|
||||||
|
|
187
query/src/lib.rs
187
query/src/lib.rs
|
@ -60,7 +60,7 @@ impl FromValue<Variable> for Variable {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Variable {
|
impl Variable {
|
||||||
fn from_symbol(sym: &PlainSymbol) -> Option<Variable> {
|
pub fn from_symbol(sym: &PlainSymbol) -> Option<Variable> {
|
||||||
if sym.is_var_symbol() {
|
if sym.is_var_symbol() {
|
||||||
Some(Variable(sym.clone()))
|
Some(Variable(sym.clone()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -96,7 +96,7 @@ impl SrcVar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// These are the scalar values representable in EDN.
|
/// These are the scalar values representable in EDN.
|
||||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum NonIntegerConstant {
|
pub enum NonIntegerConstant {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
BigInteger(BigInt),
|
BigInteger(BigInt),
|
||||||
|
@ -118,24 +118,128 @@ pub enum FnArg {
|
||||||
/// This encoding allows us to represent integers that aren't
|
/// This encoding allows us to represent integers that aren't
|
||||||
/// entity IDs. That'll get filtered out in the context of the
|
/// entity IDs. That'll get filtered out in the context of the
|
||||||
/// database.
|
/// database.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum PatternNonValuePlace {
|
pub enum PatternNonValuePlace {
|
||||||
Placeholder,
|
Placeholder,
|
||||||
Variable(Variable),
|
Variable(Variable),
|
||||||
Entid(u64), // Note unsigned. See #190.
|
Entid(i64), // Will always be +ve. See #190.
|
||||||
Ident(NamespacedKeyword),
|
Ident(NamespacedKeyword),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PatternNonValuePlace {
|
||||||
|
// I think we'll want move variants, so let's leave these here for now.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn into_pattern_value_place(self) -> PatternValuePlace {
|
||||||
|
match self {
|
||||||
|
PatternNonValuePlace::Placeholder => PatternValuePlace::Placeholder,
|
||||||
|
PatternNonValuePlace::Variable(x) => PatternValuePlace::Variable(x),
|
||||||
|
PatternNonValuePlace::Entid(x) => PatternValuePlace::EntidOrInteger(x),
|
||||||
|
PatternNonValuePlace::Ident(x) => PatternValuePlace::IdentOrKeyword(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_pattern_value_place(&self) -> PatternValuePlace {
|
||||||
|
match *self {
|
||||||
|
PatternNonValuePlace::Placeholder => PatternValuePlace::Placeholder,
|
||||||
|
PatternNonValuePlace::Variable(ref x) => PatternValuePlace::Variable(x.clone()),
|
||||||
|
PatternNonValuePlace::Entid(x) => PatternValuePlace::EntidOrInteger(x),
|
||||||
|
PatternNonValuePlace::Ident(ref x) => PatternValuePlace::IdentOrKeyword(x.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromValue<PatternNonValuePlace> for PatternNonValuePlace {
|
||||||
|
fn from_value(v: &edn::Value) -> Option<PatternNonValuePlace> {
|
||||||
|
match v {
|
||||||
|
&edn::Value::Integer(x) => if x >= 0 {
|
||||||
|
Some(PatternNonValuePlace::Entid(x))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
&edn::Value::PlainSymbol(ref x) => if x.0.as_str() == "_" {
|
||||||
|
Some(PatternNonValuePlace::Placeholder)
|
||||||
|
} else {
|
||||||
|
if let Some(v) = Variable::from_symbol(x) {
|
||||||
|
Some(PatternNonValuePlace::Variable(v))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&edn::Value::NamespacedKeyword(ref x) =>
|
||||||
|
Some(PatternNonValuePlace::Ident(x.clone())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The `v` part of a pattern can be much broader: it can represent
|
/// The `v` part of a pattern can be much broader: it can represent
|
||||||
/// integers that aren't entity IDs (particularly negative integers),
|
/// integers that aren't entity IDs (particularly negative integers),
|
||||||
/// strings, and all the rest. We group those under `Constant`.
|
/// strings, and all the rest. We group those under `Constant`.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum PatternValuePlace {
|
pub enum PatternValuePlace {
|
||||||
Placeholder,
|
Placeholder,
|
||||||
Variable(Variable),
|
Variable(Variable),
|
||||||
EntidOrInteger(i64),
|
EntidOrInteger(i64),
|
||||||
Ident(NamespacedKeyword),
|
IdentOrKeyword(NamespacedKeyword),
|
||||||
Constant(NonIntegerConstant),
|
Constant(NonIntegerConstant),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromValue<PatternValuePlace> for PatternValuePlace {
|
||||||
|
fn from_value(v: &edn::Value) -> Option<PatternValuePlace> {
|
||||||
|
match v {
|
||||||
|
&edn::Value::Integer(x) =>
|
||||||
|
Some(PatternValuePlace::EntidOrInteger(x)),
|
||||||
|
&edn::Value::PlainSymbol(ref x) if x.0.as_str() == "_" =>
|
||||||
|
Some(PatternValuePlace::Placeholder),
|
||||||
|
&edn::Value::PlainSymbol(ref x) if x.is_var_symbol() =>
|
||||||
|
Some(PatternValuePlace::Variable(Variable(x.clone()))),
|
||||||
|
&edn::Value::NamespacedKeyword(ref x) =>
|
||||||
|
Some(PatternValuePlace::IdentOrKeyword(x.clone())),
|
||||||
|
&edn::Value::Boolean(x) =>
|
||||||
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Boolean(x))),
|
||||||
|
&edn::Value::Float(x) =>
|
||||||
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Float(x))),
|
||||||
|
&edn::Value::BigInteger(ref x) =>
|
||||||
|
Some(PatternValuePlace::Constant(NonIntegerConstant::BigInteger(x.clone()))),
|
||||||
|
&edn::Value::Text(ref x) =>
|
||||||
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Text(x.clone()))),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PatternValuePlace {
|
||||||
|
// I think we'll want move variants, so let's leave these here for now.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn into_pattern_non_value_place(self) -> Option<PatternNonValuePlace> {
|
||||||
|
match self {
|
||||||
|
PatternValuePlace::Placeholder => Some(PatternNonValuePlace::Placeholder),
|
||||||
|
PatternValuePlace::Variable(x) => Some(PatternNonValuePlace::Variable(x)),
|
||||||
|
PatternValuePlace::EntidOrInteger(x) => if x >= 0 {
|
||||||
|
Some(PatternNonValuePlace::Entid(x))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
PatternValuePlace::IdentOrKeyword(x) => Some(PatternNonValuePlace::Ident(x)),
|
||||||
|
PatternValuePlace::Constant(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_pattern_non_value_place(&self) -> Option<PatternNonValuePlace> {
|
||||||
|
match *self {
|
||||||
|
PatternValuePlace::Placeholder => Some(PatternNonValuePlace::Placeholder),
|
||||||
|
PatternValuePlace::Variable(ref x) => Some(PatternNonValuePlace::Variable(x.clone())),
|
||||||
|
PatternValuePlace::EntidOrInteger(x) => if x >= 0 {
|
||||||
|
Some(PatternNonValuePlace::Entid(x))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
PatternValuePlace::IdentOrKeyword(ref x) => Some(PatternNonValuePlace::Ident(x.clone())),
|
||||||
|
PatternValuePlace::Constant(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pub enum PullPattern {
|
pub enum PullPattern {
|
||||||
Constant(Constant),
|
Constant(Constant),
|
||||||
|
@ -212,13 +316,6 @@ pub enum FindSpec {
|
||||||
FindScalar(Element),
|
FindScalar(Element),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct FindQuery {
|
|
||||||
pub find_spec: FindSpec,
|
|
||||||
pub default_source: SrcVar,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the provided `FindSpec` returns at most one result.
|
/// Returns true if the provided `FindSpec` returns at most one result.
|
||||||
pub fn is_unit_limited(spec: &FindSpec) -> bool {
|
pub fn is_unit_limited(spec: &FindSpec) -> bool {
|
||||||
match spec {
|
match spec {
|
||||||
|
@ -255,18 +352,55 @@ pub fn requires_distinct(spec: &FindSpec) -> bool {
|
||||||
// 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
|
||||||
// one direction.
|
// one direction.
|
||||||
#[allow(dead_code)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Pattern {
|
pub struct Pattern {
|
||||||
source: Option<SrcVar>,
|
pub source: Option<SrcVar>,
|
||||||
entity: PatternNonValuePlace,
|
pub entity: PatternNonValuePlace,
|
||||||
attribute: PatternNonValuePlace,
|
pub attribute: PatternNonValuePlace,
|
||||||
value: PatternValuePlace,
|
pub value: PatternValuePlace,
|
||||||
tx: PatternNonValuePlace,
|
pub tx: PatternNonValuePlace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
pub fn new(src: Option<SrcVar>,
|
||||||
|
e: PatternNonValuePlace,
|
||||||
|
a: PatternNonValuePlace,
|
||||||
|
v: PatternValuePlace,
|
||||||
|
tx: PatternNonValuePlace) -> Option<Pattern> {
|
||||||
|
let aa = a.clone(); // Too tired of fighting borrow scope for now.
|
||||||
|
if let PatternNonValuePlace::Ident(ref k) = aa {
|
||||||
|
if k.is_backward() {
|
||||||
|
// e and v have different types; we must convert them.
|
||||||
|
// Not every parseable value is suitable for the entity field!
|
||||||
|
// As such, this is a failable constructor.
|
||||||
|
let e_v = e.to_pattern_value_place();
|
||||||
|
if let Some(v_e) = v.to_pattern_non_value_place() {
|
||||||
|
return Some(Pattern {
|
||||||
|
source: src,
|
||||||
|
entity: v_e,
|
||||||
|
attribute: PatternNonValuePlace::Ident(k.to_reversed()),
|
||||||
|
value: e_v,
|
||||||
|
tx: tx,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Pattern {
|
||||||
|
source: src,
|
||||||
|
entity: e,
|
||||||
|
attribute: a,
|
||||||
|
value: v,
|
||||||
|
tx: tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum WhereClause {
|
pub enum WhereClause {
|
||||||
/*
|
|
||||||
Not,
|
Not,
|
||||||
NotJoin,
|
NotJoin,
|
||||||
Or,
|
Or,
|
||||||
|
@ -274,16 +408,17 @@ pub enum WhereClause {
|
||||||
Pred,
|
Pred,
|
||||||
WhereFn,
|
WhereFn,
|
||||||
RuleExpr,
|
RuleExpr,
|
||||||
*/
|
Pattern(Pattern),
|
||||||
Pattern,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Query {
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
find: FindSpec,
|
pub struct FindQuery {
|
||||||
with: Vec<Variable>,
|
pub find_spec: FindSpec,
|
||||||
in_vars: Vec<Variable>,
|
pub default_source: SrcVar,
|
||||||
in_sources: Vec<SrcVar>,
|
pub with: Vec<Variable>,
|
||||||
where_clauses: Vec<WhereClause>,
|
pub in_vars: Vec<Variable>,
|
||||||
|
pub in_sources: Vec<SrcVar>,
|
||||||
|
pub where_clauses: Vec<WhereClause>,
|
||||||
// TODO: in_rules;
|
// TODO: in_rules;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue