diff --git a/edn/src/edn.rustpeg b/edn/src/edn.rustpeg index c09ac224..5b77672d 100644 --- a/edn/src/edn.rustpeg +++ b/edn/src/edn.rustpeg @@ -1,3 +1,4 @@ +/* -*- comment-start: "//"; -*- */ /* vim: set filetype=rust.rustpeg */ // Copyright 2016 Mozilla @@ -24,6 +25,8 @@ use ordered_float::OrderedFloat; use uuid::Uuid; use entities::*; +use query; +use query::FromValue; use symbols::*; use types::{SpannedValue, Span, ValueAndSpan}; @@ -155,12 +158,14 @@ pub symbol -> SpannedValue = ns:( sns:$(symbol_namespace) namespace_separator { sns })? n:$(plain_symbol_name) { SpannedValue::from_symbol(ns, n) } + / #expected("symbol") pub keyword -> SpannedValue = keyword_prefix ns:( sns:$(symbol_namespace) namespace_separator { sns })? n:$(symbol_name) { SpannedValue::from_keyword(ns, n) } + / #expected("keyword") pub list -> SpannedValue = "(" __ v:(value)* __ ")" { SpannedValue::List(LinkedList::from_iter(v)) } @@ -188,6 +193,7 @@ pub value -> ValueAndSpan = span: Span::new(start, end) } } + / #expected("value") atom -> ValueAndSpan = v:value {? if v.is_atom() { Ok(v) } else { Err("expected atom") } } @@ -199,10 +205,29 @@ comment = #quiet<";" [^\r\n]* [\r\n]?> __ = (whitespace / comment)* +// Transaction entity parser starts here. + pub op -> OpType = ":db/add" { OpType::Add } / ":db/retract" { OpType::Retract } +raw_keyword -> Keyword = + keyword_prefix + ns:( sns:$(symbol_namespace) namespace_separator { sns })? + n:$(symbol_name) { + match ns { + Some(ns) => Keyword::namespaced(ns, n), + None => Keyword::plain(n), + } + } + / #expected("keyword") + +raw_forward_keyword -> Keyword + = v:raw_keyword {? if v.is_forward() { Ok(v) } else { Err("expected :forward or :forward/keyword") } } + +raw_backward_keyword -> Keyword + = v:raw_keyword {? if v.is_backward() { Ok(v) } else { Err("expected :_backword or :backward/_keyword") } } + raw_namespaced_keyword -> Keyword = keyword_prefix ns:$(symbol_namespace) namespace_separator n:$(symbol_name) { Keyword::namespaced(ns, n) } / #expected("namespaced keyword") @@ -216,16 +241,20 @@ raw_backward_namespaced_keyword -> Keyword entid -> Entid = v:( raw_basedinteger / raw_hexinteger / raw_octalinteger / raw_integer ) { Entid::Entid(v) } / v:raw_namespaced_keyword { Entid::Ident(v) } + / #expected("entid") forward_entid -> Entid = v:( raw_basedinteger / raw_hexinteger / raw_octalinteger / raw_integer ) { Entid::Entid(v) } / v:raw_forward_namespaced_keyword { Entid::Ident(v) } + / #expected("forward entid") backward_entid -> Entid = v:raw_backward_namespaced_keyword { Entid::Ident(v.to_reversed()) } + / #expected("backward entid") lookup_ref -> LookupRef = "(" __ "lookup-ref" __ a:(entid) __ v:(value) __ ")" { LookupRef { a: AttributePlace::Entid(a), v } } + / #expected("lookup-ref") tx_function -> TxFunction = "(" __ n:$(symbol_name) __ ")" { TxFunction { op: PlainSymbol::plain(n) } } @@ -253,6 +282,210 @@ pub entity -> Entity = __ "[" __ op:(op) __ e:(entity_place) __ a:(forward_entid) __ v:(value_place) __ "]" __ { Entity::AddOrRetract { op, e: e, a: AttributePlace::Entid(a), v: v } } / __ "[" __ op:(op) __ e:(value_place) __ a:(backward_entid) __ v:(entity_place) __ "]" __ { Entity::AddOrRetract { op, e: v, a: AttributePlace::Entid(a), v: e } } / __ map:map_notation __ { Entity::MapNotation(map) } + / #expected("entity") pub entities -> Vec> = __ "[" __ es:(entity*) __ "]" __ { es } + +// Query parser starts here. +// +// We expect every rule except the `raw_*` rules to eat whitespace +// (with `__`) at its start and finish. That means that every string +// pattern (say "[") should be bracketed on either side with either a +// whitespace-eating rule or an explicit whitespace eating `__`. + +query_function -> query::QueryFunction + = __ n:$(symbol_name) __ {? query::QueryFunction::from_symbol(&PlainSymbol::plain(n)).ok_or("expected query function") } + +fn_arg -> query::FnArg + = v:value {? query::FnArg::from_value(&v).ok_or("expected query function argument") } + / __ "[" args:fn_arg+ "]" __ { query::FnArg::Vector(args) } + +find_elem -> query::Element + = __ v:variable __ { query::Element::Variable(v) } + / __ "(" __ "the" v:variable ")" __ { query::Element::Corresponding(v) } + / __ "(" __ "pull" var:variable "[" patterns:pull_attribute+ "]" __ ")" __ { query::Element::Pull(query::Pull { var, patterns }) } + / __ "(" func:query_function args:fn_arg* ")" __ { query::Element::Aggregate(query::Aggregate { func, args }) } + +find_spec -> query::FindSpec + = f:find_elem "." __ { query::FindSpec::FindScalar(f) } + / fs:find_elem+ { query::FindSpec::FindRel(fs) } + / __ "[" f:find_elem __ "..." __ "]" __ { query::FindSpec::FindColl(f) } + / __ "[" fs:find_elem+ "]" __ { query::FindSpec::FindTuple(fs) } + +pull_attribute -> query::PullAttributeSpec + = __ "*" __ { query::PullAttributeSpec::Wildcard } + / __ k:raw_forward_namespaced_keyword __ alias:(":as" __ alias:raw_forward_keyword __ { alias })? { + let attribute = query::PullConcreteAttribute::Ident(::std::rc::Rc::new(k)); + let alias = alias.map(|alias| ::std::rc::Rc::new(alias)); + query::PullAttributeSpec::Attribute( + query::NamedPullAttribute { + attribute, + alias: alias, + }) + } + +limit -> query::Limit + = __ v:variable __ { query::Limit::Variable(v) } + / __ n:(raw_octalinteger / raw_hexinteger / raw_basedinteger / raw_integer) __ {? + if n > 0 { + Ok(query::Limit::Fixed(n as u64)) + } else { + Err("expected positive integer") + } + } + +order -> query::Order + = __ "(" __ "asc" v:variable ")" __ { query::Order(query::Direction::Ascending, v) } + / __ "(" __ "desc" v:variable ")" __ { query::Order(query::Direction::Descending, v) } + / v:variable { query::Order(query::Direction::Ascending, v) } + + +pattern_value_place -> query::PatternValuePlace + = v:value {? query::PatternValuePlace::from_value(&v).ok_or("expected pattern_value_place") } + +pattern_non_value_place -> query::PatternNonValuePlace + = v:value {? query::PatternNonValuePlace::from_value(&v).ok_or("expected pattern_non_value_place") } + +pattern -> query::WhereClause + = __ "[" + src:src_var? + e:pattern_non_value_place + a:pattern_non_value_place + v:pattern_value_place? + tx:pattern_non_value_place? + "]" __ + {? + let v = v.unwrap_or(query::PatternValuePlace::Placeholder); + let tx = tx.unwrap_or(query::PatternNonValuePlace::Placeholder); + + // 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. + query::Pattern::new(src, e, a, v, tx) + .map(query::WhereClause::Pattern) + .ok_or("expected pattern") + } + +// TODO: this shouldn't be checked at parse time. +rule_vars -> BTreeSet + = vs:variable+ {? + let given = vs.len(); + let set: BTreeSet = vs.into_iter().collect(); + if given != set.len() { + Err("expected unique variables") + } else { + Ok(set) + } + } + +or_pattern_clause -> query::OrWhereClause + = clause:where_clause { query::OrWhereClause::Clause(clause) } + +or_and_clause -> query::OrWhereClause + = __ "(" __ "and" clauses:where_clause+ ")" __ { query::OrWhereClause::And(clauses) } + +or_where_clause -> query::OrWhereClause + = or_pattern_clause + / or_and_clause + +or_clause -> query::WhereClause + = __ "(" __ "or" clauses:or_where_clause+ ")" __ { + query::WhereClause::OrJoin(query::OrJoin::new(query::UnifyVars::Implicit, clauses)) + } + +or_join_clause -> query::WhereClause + = __ "(" __ "or-join" __ "[" vars:rule_vars "]" clauses:or_where_clause+ ")" __ { + query::WhereClause::OrJoin(query::OrJoin::new(query::UnifyVars::Explicit(vars), clauses)) + } + +not_clause -> query::WhereClause + = __ "(" __ "not" clauses:where_clause+ ")" __ { + query::WhereClause::NotJoin(query::NotJoin::new(query::UnifyVars::Implicit, clauses)) + } + +not_join_clause -> query::WhereClause + = __ "(" __ "not-join" __ "[" vars:rule_vars "]" clauses:where_clause+ ")" __ { + query::WhereClause::NotJoin(query::NotJoin::new(query::UnifyVars::Explicit(vars), clauses)) + } + +type_annotation -> query::WhereClause + = __ "[" __ "(" __ "type" var:variable __ ty:raw_keyword __ ")" __ "]" __ { + query::WhereClause::TypeAnnotation( + query::TypeAnnotation { + value_type: ty, + variable: var, + }) + } + +pred -> query::WhereClause + = __ "[" __ "(" func:query_function args:fn_arg* ")" __ "]" __ { + query::WhereClause::Pred( + query::Predicate { + operator: func.0, + args: args, + }) + } + +pub where_fn -> query::WhereClause + = __ "[" __ "(" func:query_function args:fn_arg* ")" __ binding:binding "]" __ { + query::WhereClause::WhereFn( + query::WhereFn { + operator: func.0, + args: args, + binding, + }) + } + +where_clause -> query::WhereClause + // Right now we only support patterns and predicates. See #239 for more. + = pattern + / or_join_clause + / or_clause + / not_join_clause + / not_clause + / type_annotation + / pred + / where_fn + +query_part -> query::QueryPart + = __ ":find" fs:find_spec { query::QueryPart::FindSpec(fs) } + / __ ":in" in_vars:variable+ { query::QueryPart::InVars(in_vars) } + / __ ":limit" l:limit { query::QueryPart::Limit(l) } + / __ ":order" os:order+ { query::QueryPart::Order(os) } + / __ ":where" ws:where_clause+ { query::QueryPart::WhereClauses(ws) } + / __ ":with" with_vars:variable+ { query::QueryPart::WithVars(with_vars) } + +pub query -> query::ParsedFindQuery + = __ "[" qps:query_part+ "]" __ {? query::ParsedFindQuery::from_parts(qps) } + +variable -> query::Variable + = v:value {? query::Variable::from_value(&v).ok_or("expected variable") } + +src_var -> query::SrcVar + = v:value {? query::SrcVar::from_value(&v).ok_or("expected src_var") } + +variable_or_placeholder -> query::VariableOrPlaceholder + = v:variable { query::VariableOrPlaceholder::Variable(v) } + / __ "_" __ { query::VariableOrPlaceholder::Placeholder } + +binding -> query::Binding + = __ "[" __ "[" vs:variable_or_placeholder+ "]" __ "]" __ { query::Binding::BindRel(vs) } + / __ "[" v:variable "..." __ "]" __ { query::Binding::BindColl(v) } + / __ "[" vs:variable_or_placeholder+ "]" __ { query::Binding::BindTuple(vs) } + / v:variable { query::Binding::BindScalar(v) } diff --git a/edn/src/query.rs b/edn/src/query.rs index c1c69fd9..418dea2e 100644 --- a/edn/src/query.rs +++ b/edn/src/query.rs @@ -949,6 +949,15 @@ pub struct NotJoin { pub clauses: Vec, } +impl NotJoin { + pub fn new(unify_vars: UnifyVars, clauses: Vec) -> NotJoin { + NotJoin { + unify_vars: unify_vars, + clauses: clauses, + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct TypeAnnotation { pub value_type: Keyword, @@ -981,6 +990,131 @@ pub struct FindQuery { // TODO: in_rules; } +#[allow(dead_code)] +#[derive(Debug, Eq, PartialEq)] +pub struct ParsedFindQuery { + pub find_spec: FindSpec, + pub default_source: SrcVar, + pub with: Vec, + pub in_vars: Vec, + pub in_sources: BTreeSet, + pub limit: Limit, + pub where_clauses: Vec, + pub order: Option>, + // TODO: in_rules; +} + +pub(crate) enum QueryPart { + FindSpec(FindSpec), + WithVars(Vec), + InVars(Vec), + Limit(Limit), + WhereClauses(Vec), + Order(Vec), +} + +impl ParsedFindQuery { + pub(crate) fn from_parts(parts: Vec) -> std::result::Result { + let mut find_spec: Option = None; + let mut with: Option> = None; + let mut in_vars: Option> = None; + let mut limit: Option = None; + let mut where_clauses: Option> = None; + let mut order: Option> = None; + + for part in parts.into_iter() { + match part { + QueryPart::FindSpec(x) => { + if find_spec.is_some() { + return Err("find query has repeated :find"); + } + find_spec = Some(x) + }, + QueryPart::WithVars(x) => { + if with.is_some() { + return Err("find query has repeated :with"); + } + with = Some(x) + }, + QueryPart::InVars(x) => { + if in_vars.is_some() { + return Err("find query has repeated :in"); + } + in_vars = Some(x) + }, + QueryPart::Limit(x) => { + if limit.is_some() { + return Err("find query has repeated :limit"); + } + limit = Some(x) + }, + QueryPart::WhereClauses(x) => { + if where_clauses.is_some() { + return Err("find query has repeated :where"); + } + where_clauses = Some(x) + }, + QueryPart::Order(x) => { + if order.is_some() { + return Err("find query has repeated :order"); + } + order = Some(x) + }, + } + } + + Ok(ParsedFindQuery { + find_spec: find_spec.ok_or("expected :find")?, + default_source: SrcVar::DefaultSrc, + with: with.unwrap_or(vec![]), + in_vars: in_vars.unwrap_or(vec![]), + in_sources: BTreeSet::default(), + limit: limit.unwrap_or(Limit::None), + where_clauses: where_clauses.ok_or("expected :where")?, + order, + }) + } + + + pub fn into_find_query(self: ParsedFindQuery) -> Result { + let in_vars = { + let len = self.in_vars.len(); + let set: BTreeSet = self.in_vars.into_iter().collect(); + if len != set.len() { + return Err("find query has repeated :in variable".into()); + } + set + }; + + let with = { + let len = self.with.len(); + let set: BTreeSet = self.with.into_iter().collect(); + if len != set.len() { + return Err("find query has repeated :with variable".into()); + } + set + }; + + // Make sure that if we have `:limit ?x`, `?x` appears in `:in`. + if let Limit::Variable(ref v) = self.limit { + if !in_vars.contains(v) { + return Err("limit var not present in :in"); + } + } + + Ok(FindQuery { + find_spec: self.find_spec, + default_source: self.default_source, + with, + in_vars, + in_sources: self.in_sources, + limit: self.limit, + where_clauses: self.where_clauses, + order: self.order, + }) + } +} + impl FindQuery { pub fn simple(spec: FindSpec, where_clauses: Vec) -> FindQuery { FindQuery { diff --git a/query-parser/src/lib.rs b/query-parser/src/lib.rs index e5deff3a..2896dc50 100644 --- a/query-parser/src/lib.rs +++ b/query-parser/src/lib.rs @@ -22,7 +22,6 @@ extern crate edn; extern crate mentat_parser_utils; mod errors; -mod parse; pub use errors::{ Error, @@ -31,6 +30,8 @@ pub use errors::{ ResultExt, }; -pub use parse::{ - parse_find_string, -}; +pub fn parse_find_string(string: &str) -> Result { + edn::parse::query(string) + .map_err(|e| e.into()) + .and_then(|parsed| parsed.into_find_query().map_err(|e| e.into())) +} diff --git a/query-parser/src/parse.rs b/query-parser/src/parse.rs deleted file mode 100644 index 0b11d3bf..00000000 --- a/query-parser/src/parse.rs +++ /dev/null @@ -1,1235 +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_parser_utils; -extern crate mentat_query; - -use std; // To refer to std::result::Result. - -use std::collections::BTreeSet; - -use self::combine::{ - eof, - look_ahead, - many, - many1, - optional, - parser, - satisfy, - satisfy_map, - Parser, - ParseResult, - Stream, -}; - -use self::combine::combinator::{any, choice, or, try}; - -use errors::{ - Error, - ErrorKind, - Result, - ResultExt, -}; - -use self::mentat_parser_utils::{ - KeywordMapParser, - ResultParser, - ValueParseError, -}; - -use self::mentat_parser_utils::value_and_span::Stream as ValueStream; -use self::mentat_parser_utils::value_and_span::{ - Item, - OfExactlyParsing, - forward_any_keyword, - forward_namespaced_keyword, - keyword_map, - list, - map, - seq, - vector, -}; - -use self::mentat_query::{ - Aggregate, - Binding, - Direction, - Element, - FindQuery, - FindSpec, - FnArg, - FromValue, - Limit, - Order, - OrJoin, - OrWhereClause, - NamedPullAttribute, - NotJoin, - Pattern, - PatternNonValuePlace, - PatternValuePlace, - Predicate, - Pull, - PullAttributeSpec, - PullConcreteAttribute, - QueryFunction, - SrcVar, - TypeAnnotation, - UnifyVars, - Variable, - VariableOrPlaceholder, - WhereClause, - WhereFn, -}; - -pub struct Query<'a>(std::marker::PhantomData<&'a ()>); - -def_parser!(Query, variable, Variable, { - satisfy_map(Variable::from_value) -}); - -def_parser!(Query, keyword, edn::Keyword, { - satisfy_map(|v: &edn::ValueAndSpan| v.inner.as_keyword().cloned()) -}); - -def_parser!(Query, source_var, SrcVar, { - satisfy_map(SrcVar::from_value) -}); - -// TODO: interning. -def_parser!(Query, query_function, QueryFunction, { - satisfy_map(QueryFunction::from_value) -}); - -def_parser!(Query, fn_arg, FnArg, { - satisfy_map(FnArg::from_value).or(vector().of_exactly(many::, _>(Query::fn_arg())).map(FnArg::Vector)) -}); - -def_parser!(Query, arguments, Vec, { - (many::, _>(Query::fn_arg())) -}); - -def_parser!(Query, direction, Direction, { - satisfy_map(|v: &edn::ValueAndSpan| { - match v.inner { - edn::SpannedValue::PlainSymbol(ref s) => { - let name = s.0.as_str(); - match name { - "asc" => Some(Direction::Ascending), - "desc" => Some(Direction::Descending), - _ => None, - } - }, - _ => None, - } - }) -}); - -def_parser!(Query, order, Order, { - seq().of_exactly((Query::direction(), Query::variable())) - .map(|(d, v)| Order(d, v)) - .or(Query::variable().map(|v| Order(Direction::Ascending, v))) -}); - -def_matches_plain_symbol!(Query, the, "the"); -def_matches_plain_symbol!(Query, pull, "pull"); -def_matches_plain_symbol!(Query, wildcard, "*"); -def_matches_keyword!(Query, alias_as, "as"); - -pub struct Where<'a>(std::marker::PhantomData<&'a ()>); - -def_parser!(Where, pattern_value_place, PatternValuePlace, { - satisfy_map(PatternValuePlace::from_value) -}); - -def_parser!(Query, natural_number, u64, { - any().and_then(|v: &edn::ValueAndSpan| { - match v.inner { - edn::SpannedValue::Integer(x) if (x > 0) => { - Ok(x as u64) - }, - ref spanned => { - let e = Box::new(Error::from_kind(ErrorKind::InvalidLimit(spanned.clone().into()))); - Err(combine::primitives::Error::Other(e)) - }, - } - }) -}); - -def_parser!(Where, pattern_non_value_place, PatternNonValuePlace, { - satisfy_map(PatternNonValuePlace::from_value) -}); - -def_matches_plain_symbol!(Where, and, "and"); - -def_matches_plain_symbol!(Where, or, "or"); - -def_matches_plain_symbol!(Where, or_join, "or-join"); - -def_matches_plain_symbol!(Where, not, "not"); - -def_matches_plain_symbol!(Where, not_join, "not-join"); - -def_matches_plain_symbol!(Where, type_symbol, "type"); - -def_parser!(Where, rule_vars, BTreeSet, { - seq() - .of_exactly(many1(Query::variable()).and_then(unique_vars)) -}); - -def_parser!(Where, or_pattern_clause, OrWhereClause, { - Where::clause().map(|clause| OrWhereClause::Clause(clause)) -}); - -def_parser!(Where, or_and_clause, OrWhereClause, { - seq() - .of_exactly(Where::and() - .with(many1(Where::clause())) - .map(OrWhereClause::And)) -}); - -def_parser!(Where, or_where_clause, OrWhereClause, { - choice([Where::or_pattern_clause(), Where::or_and_clause()]) -}); - -def_parser!(Where, or_clause, WhereClause, { - seq() - .of_exactly(Where::or() - .with(many1(Where::or_where_clause())) - .map(|clauses| { - WhereClause::OrJoin(OrJoin::new(UnifyVars::Implicit, clauses)) - })) -}); - -def_parser!(Where, or_join_clause, WhereClause, { - seq() - .of_exactly(Where::or_join() - .with(Where::rule_vars()) - .and(many1(Where::or_where_clause())) - .map(|(vars, clauses)| { - WhereClause::OrJoin(OrJoin::new(UnifyVars::Explicit(vars), clauses)) - })) -}); - -def_parser!(Where, not_clause, WhereClause, { - seq() - .of_exactly(Where::not() - .with(many1(Where::clause())) - .map(|clauses| { - WhereClause::NotJoin( - NotJoin { - unify_vars: UnifyVars::Implicit, - clauses: clauses, - }) - })) -}); - -def_parser!(Where, not_join_clause, WhereClause, { - seq() - .of_exactly(Where::not_join() - .with(Where::rule_vars()) - .and(many1(Where::clause())) - .map(|(vars, clauses)| { - WhereClause::NotJoin( - NotJoin { - unify_vars: UnifyVars::Explicit(vars), - clauses: clauses, - }) - })) -}); - -def_parser!(Query, func, (QueryFunction, Vec), { - (Query::query_function(), Query::arguments()) -}); - -def_parser!(Query, aggregate, Aggregate, { - Query::func() - .map(|(func, args)| Aggregate { - func, args, - }) -}); - -def_parser!(Query, pull_concrete_attribute_ident, PullConcreteAttribute, { - forward_namespaced_keyword() - .map(|k| PullConcreteAttribute::Ident(::std::rc::Rc::new(k.clone()))) -}); - -def_parser!(Query, pull_concrete_attribute, PullAttributeSpec, { - (Query::pull_concrete_attribute_ident(), - optional(try(Query::alias_as() - .with(forward_any_keyword() - .map(|alias| ::std::rc::Rc::new(alias.clone())))))) - .map(|(attribute, alias)| - PullAttributeSpec::Attribute( - NamedPullAttribute { - attribute, - alias: alias, - })) -}); - -def_parser!(Query, pull_wildcard_attribute, PullAttributeSpec, { - Query::wildcard().map(|_| PullAttributeSpec::Wildcard) -}); - -def_parser!(Query, pull_attribute, PullAttributeSpec, { - choice([ - try(Query::pull_concrete_attribute()), - try(Query::pull_wildcard_attribute()), - // TODO: reversed keywords, entids (with aliases, presumably…). - ]) -}); - -// A wildcard can appear only once. -// If a wildcard appears, only map expressions can be present. -fn validate_attributes<'a, I>(attrs: I) -> std::result::Result<(), &'static str> - where I: IntoIterator { - let mut wildcard_seen = false; - let mut non_map_or_wildcard_seen = false; - for attr in attrs { - match attr { - &PullAttributeSpec::Wildcard => { - if wildcard_seen { - return Err("duplicate wildcard pull attribute"); - } - wildcard_seen = true; - if non_map_or_wildcard_seen { - return Err("wildcard with specified attributes"); - } - }, - // &PullAttributeSpec::LimitedAttribute(_, _) => { - &PullAttributeSpec::Attribute(_) => { - non_map_or_wildcard_seen = true; - if wildcard_seen { - return Err("wildcard with specified attributes"); - } - }, - // TODO: map form. - } - } - Ok(()) -} - -def_parser!(Query, pull_attributes, Vec, { - vector().of_exactly(many1(Query::pull_attribute())) - .and_then(|attrs: Vec| - validate_attributes(&attrs) - .and(Ok(attrs)) - .map_err(|e| combine::primitives::Error::Unexpected(e.into()))) -}); - -/// A vector containing just a parenthesized filter expression. -def_parser!(Where, pred, WhereClause, { - // Accept either a nested list or a nested vector here: - // `[(foo ?x ?y)]` or `[[foo ?x ?y]]` - vector() - .of_exactly(seq() - .of_exactly(Query::func() - .map(|(f, args)| { - WhereClause::Pred( - Predicate { - operator: f.0, - args: args, - }) - }))) -}); - -/// A type annotation. -def_parser!(Where, type_annotation, WhereClause, { - // Accept either a nested list or a nested vector here: - // `[(type ?x :db.type/string)]` or `[[type ?x :db.type/long]]` - vector() - .of_exactly(seq() - .of_exactly((Where::type_symbol(), Query::variable(), Query::keyword()) - .map(|(_, var, ty)| { - WhereClause::TypeAnnotation( - TypeAnnotation { - value_type: ty, - variable: var, - }) - }))) -}); - -/// A vector containing a parenthesized function expression and a binding. -def_parser!(Where, where_fn, WhereClause, { - // Accept either a nested list or a nested vector here: - // `[(foo ?x ?y) binding]` or `[[foo ?x ?y] binding]` - vector() - .of_exactly( - (seq().of_exactly( - Query::func()), - Bind::binding()) - .map(|((f, args), binding)| { - WhereClause::WhereFn( - WhereFn { - operator: f.0, - args: args, - binding: binding, - }) - })) -}); - -def_parser!(Where, pattern, WhereClause, { - vector() - .of_exactly( - // While *technically* Datomic allows you to have a query like: - // [:find … :where [[?x]]] - // We don't -- we require at least e, a. - (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]. - // - // 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_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. - try(Where::or_join_clause()), - try(Where::or_clause()), - try(Where::not_join_clause()), - try(Where::not_clause()), - - try(Where::type_annotation()), - try(Where::pred()), - try(Where::where_fn()), - ]) -}); - -def_parser!(Where, clauses, Vec, { - // Right now we only support patterns and predicates. See #239 for more. - (many1::, _>(Where::clause())) -}); - -pub struct Find<'a>(std::marker::PhantomData<&'a ()>); - -def_matches_plain_symbol!(Find, period, "."); - -def_matches_plain_symbol!(Find, ellipsis, "..."); - -def_matches_plain_symbol!(Find, placeholder, "_"); - -def_parser!(Find, variable_element, Element, { - Query::variable().map(Element::Variable) -}); - -def_parser!(Find, corresponding_element, Element, { - Query::the().with(Query::variable()) - .map(Element::Corresponding) -}); - -def_parser!(Find, aggregate_element, Element, { - Query::aggregate().map(Element::Aggregate) -}); - -def_parser!(Find, pull_element, Element, { - Query::pull().with(Query::variable().and(Query::pull_attributes())) - .map(|(var, attrs)| Element::Pull(Pull { var: var, patterns: attrs })) -}); - -enum ElementType { - Corresponding, - Pull, - Aggregate, -} - -def_parser!(Find, seq_elem, Element, { - let element_parser_for_type = |ty: ElementType| { - match ty { - ElementType::Corresponding => Find::corresponding_element(), - ElementType::Pull => Find::pull_element(), - ElementType::Aggregate => Find::aggregate_element(), - } - }; - - // This slightly tortured phrasing ensures that we don't consume - // when the first item in the list -- the function name -- doesn't - // match, but once we decide what the list is, we go ahead and - // commit to that branch. - seq().of_exactly( - // This comes first because otherwise (the ?x) will match as an aggregate. - look_ahead(Query::the()).map(|_| ElementType::Corresponding) - - // Similarly, we have to parse pull before general functions. - .or(look_ahead(Query::pull()).map(|_| ElementType::Pull)) - .or(look_ahead(Query::func()).map(|_| ElementType::Aggregate)) - .then(element_parser_for_type)) -}); - -def_parser!(Find, elem, Element, { - try(Find::variable_element()) - .or(Find::seq_elem()) -}); - -def_parser!(Find, find_scalar, FindSpec, { - Find::elem().skip(Find::period()) - .map(FindSpec::FindScalar) -}); - -def_parser!(Find, find_coll, FindSpec, { - vector().of_exactly(Find::elem().skip(Find::ellipsis())) - .map(FindSpec::FindColl) -}); - -def_parser!(Find, elements, Vec, { - many1::, _>(Find::elem()) -}); - -def_parser!(Find, find_rel, FindSpec, { - Find::elements().map(FindSpec::FindRel) -}); - -def_parser!(Find, find_tuple, FindSpec, { - vector().of_exactly(Find::elements()) - .map(FindSpec::FindTuple) -}); - -/// Parse a stream 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 -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; 4], _> - ([&mut try(Find::find_scalar()), - &mut try(Find::find_coll()), - &mut try(Find::find_tuple()), - &mut try(Find::find_rel())]) -}); - -fn unique_vars(vars: Vec) -> std::result::Result, combine::primitives::Error> { - let given = vars.len(); - let set: BTreeSet = vars.into_iter().collect(); - if given != set.len() { - // TODO: find out what the variable is! - let e = Box::new(Error::from_kind(ErrorKind::DuplicateVariableError)); - Err(combine::primitives::Error::Other(e)) - } else { - Ok(set) - } -} - -def_parser!(Find, vars, BTreeSet, { - many(Query::variable()).and_then(unique_vars) -}); - -/// 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 find_map = keyword_map_of!( - ("find", Find::spec()), - ("in", Find::vars()), - ("limit", Query::variable().map(Limit::Variable).or(Query::natural_number().map(Limit::Fixed))), - ("order", many1(Query::order())), - ("where", Where::clauses()), - ("with", Find::vars()) // Note: no trailing comma allowed! - ); - - (or(keyword_map(), vector())) - .of_exactly(find_map) - .and_then(|(find_spec, in_vars, limit, order_clauses, where_clauses, with_vars) | -> std::result::Result> { - let limit = limit.unwrap_or(Limit::None); - - // Make sure that if we have `:limit ?x`, `?x` appears in `:in`. - let in_vars = in_vars.unwrap_or(BTreeSet::default()); - if let Limit::Variable(ref v) = limit { - if !in_vars.contains(v) { - let e = Box::new(Error::from_kind(ErrorKind::UnknownLimitVar(v.name()))); - return Err(combine::primitives::Error::Other(e)); - } - } - - Ok(FindQuery { - default_source: SrcVar::DefaultSrc, - find_spec: find_spec.ok_or(combine::primitives::Error::Unexpected("expected :find".into()))?, - in_sources: BTreeSet::default(), // TODO - in_vars: in_vars, - limit: limit, - order: order_clauses, - where_clauses: where_clauses.ok_or(combine::primitives::Error::Unexpected("expected :where".into()))?, - with: with_vars.unwrap_or(BTreeSet::default()), - }) - }) -}); - -pub struct Bind<'a>(std::marker::PhantomData<&'a ()>); - -def_parser!(Bind, bind_scalar, Binding, { - Query::variable() - .skip(eof()) - .map(|var| Binding::BindScalar(var)) -}); - -def_parser!(Bind, variable_or_placeholder, VariableOrPlaceholder, { - Query::variable().map(VariableOrPlaceholder::Variable) - .or(Find::placeholder().map(|_| VariableOrPlaceholder::Placeholder)) -}); - -def_parser!(Bind, bind_coll, Binding, { - vector() - .of_exactly(Query::variable() - .skip(Find::ellipsis())) - .map(Binding::BindColl) -}); - -def_parser!(Bind, bind_rel, Binding, { - vector().of_exactly( - vector().of_exactly( - many1::, _>(Bind::variable_or_placeholder()) - .map(Binding::BindRel))) -}); - -def_parser!(Bind, bind_tuple, Binding, { - vector().of_exactly( - many1::, _>(Bind::variable_or_placeholder()) - .map(Binding::BindTuple)) -}); - -def_parser!(Bind, binding, Binding, { - // Any one of the four binding types might apply, so we combine them with `choice`. Our parsers - // consume input, so we need to wrap them in `try` so that they operate independently. - choice([try(Bind::bind_scalar()), - try(Bind::bind_coll()), - try(Bind::bind_tuple()), - try(Bind::bind_rel())]) -}); - -pub fn parse_find_string(string: &str) -> Result { - let expr = edn::parse::value(string)?; - Find::query() - .parse(expr.atom_stream()) - .map(|x| x.0) - .map_err(|e| Error::from_kind(ErrorKind::FindParseError(e.into()))) -} - -#[cfg(test)] -mod test { - extern crate combine; - extern crate edn; - extern crate mentat_query; - - use std::rc::Rc; - - use self::combine::Parser; - use self::edn::OrderedFloat; - use self::mentat_query::{ - Binding, - Element, - FindSpec, - NonIntegerConstant, - Pattern, - PatternNonValuePlace, - PatternValuePlace, - SrcVar, - Variable, - VariableOrPlaceholder, - }; - - use super::*; - - fn variable(x: edn::PlainSymbol) -> Variable { - Variable(Rc::new(x)) - } - - fn ident_kw(kw: edn::Keyword) -> PatternNonValuePlace { - PatternNonValuePlace::Ident(kw.into()) - } - - fn ident(ns: &str, name: &str) -> PatternNonValuePlace { - ident_kw(edn::Keyword::namespaced(ns, name)) - } - - #[test] - fn test_pattern_mixed() { - let e = edn::PlainSymbol::plain("_"); - let a = edn::Keyword::namespaced("foo", "bar"); - let v = OrderedFloat(99.9); - let tx = edn::PlainSymbol::plain("?tx"); - let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()), - edn::Value::Keyword(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, - attribute: ident_kw(a), - value: PatternValuePlace::Constant(NonIntegerConstant::Float(v)), - tx: PatternNonValuePlace::Variable(variable(tx)), - })); - } - - #[test] - fn test_pattern_vars() { - let s = edn::PlainSymbol::plain("$x"); - let e = edn::PlainSymbol::plain("?e"); - let a = edn::PlainSymbol::plain("?a"); - let v = edn::PlainSymbol::plain("?v"); - let tx = edn::PlainSymbol::plain("?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, WhereClause::Pattern(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::plain("_"); - let a = edn::Keyword::namespaced("foo", "_bar"); - let v = OrderedFloat(99.9); - let tx = edn::PlainSymbol::plain("?tx"); - let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()), - edn::Value::Keyword(a.clone()), - edn::Value::Float(v.clone()), - edn::Value::PlainSymbol(tx.clone()))); - - let input = input.with_spans(); - let mut par = Where::pattern(); - let result = par.parse(input.atom_stream()); - match result { - Err(_) => (), - _ => assert!(false, "Expected a parse error"), - } - } - - #[test] - fn test_pattern_reversed() { - let e = edn::PlainSymbol::plain("_"); - let a = edn::Keyword::namespaced("foo", "_bar"); - let v = edn::PlainSymbol::plain("?v"); - let tx = edn::PlainSymbol::plain("?tx"); - let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()), - edn::Value::Keyword(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, WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(variable(v)), - attribute: ident("foo", "bar"), - value: PatternValuePlace::Placeholder, - tx: PatternNonValuePlace::Variable(variable(tx)), - })); - } - - #[test] - fn test_rule_vars() { - let e = edn::PlainSymbol::plain("?e"); - let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())]); - assert_parses_to!(Where::rule_vars, input, - btreeset!{variable(e.clone())}); - } - - #[test] - fn test_repeated_vars() { - let e = edn::PlainSymbol::plain("?e"); - let f = edn::PlainSymbol::plain("?f"); - let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone()), - edn::Value::PlainSymbol(f.clone()),]); - assert_parses_to!(|| vector().of_exactly(Find::vars()), input, - btreeset!{variable(e.clone()), variable(f.clone())}); - - let g = edn::PlainSymbol::plain("?g"); - let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(g.clone()), - edn::Value::PlainSymbol(g.clone()),]); - - let input = input.with_spans(); - let mut par = vector().of_exactly(Find::vars()); - let result = par.parse(input.atom_stream()) - .map(|x| x.0) - .map_err(|e| if let Some(combine::primitives::Error::Other(x)) = e.errors.into_iter().next() { - // Pattern matching on boxes is rocket science until Rust Nightly features hit - // stable. ErrorKind isn't Clone, so convert to strings. We could pattern match - // for exact comparison here. - x.downcast::().ok().map(|e| e.to_string()) - } else { - None - }); - assert_eq!(result, Err(Some("duplicates in variable list".to_string()))); - } - - #[test] - fn test_or() { - let oj = edn::PlainSymbol::plain("or"); - let e = edn::PlainSymbol::plain("?e"); - let a = edn::PlainSymbol::plain("?a"); - let v = edn::PlainSymbol::plain("?v"); - 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()); - assert_parses_to!(Where::or_clause, input, - WhereClause::OrJoin( - OrJoin::new(UnifyVars::Implicit, - vec![OrWhereClause::Clause( - WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(variable(e)), - attribute: PatternNonValuePlace::Variable(variable(a)), - value: PatternValuePlace::Variable(variable(v)), - tx: PatternNonValuePlace::Placeholder, - }))]))); - } - - #[test] - fn test_or_join() { - let oj = edn::PlainSymbol::plain("or-join"); - let e = edn::PlainSymbol::plain("?e"); - let a = edn::PlainSymbol::plain("?a"); - let v = edn::PlainSymbol::plain("?v"); - 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()); - assert_parses_to!(Where::or_join_clause, input, - WhereClause::OrJoin( - OrJoin::new(UnifyVars::Explicit(btreeset!{variable(e.clone())}), - vec![OrWhereClause::Clause( - WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(variable(e)), - attribute: PatternNonValuePlace::Variable(variable(a)), - value: PatternValuePlace::Variable(variable(v)), - tx: PatternNonValuePlace::Placeholder, - }))]))); - } - - #[test] - fn test_not() { - let e = edn::PlainSymbol::plain("?e"); - let a = edn::PlainSymbol::plain("?a"); - let v = edn::PlainSymbol::plain("?v"); - - assert_edn_parses_to!(Where::not_clause, - "(not [?e ?a ?v])", - WhereClause::NotJoin( - NotJoin { - unify_vars: UnifyVars::Implicit, - clauses: vec![ - WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(variable(e)), - attribute: PatternNonValuePlace::Variable(variable(a)), - value: PatternValuePlace::Variable(variable(v)), - tx: PatternNonValuePlace::Placeholder, - })], - })); - } - - #[test] - fn test_not_join() { - let e = edn::PlainSymbol::plain("?e"); - let a = edn::PlainSymbol::plain("?a"); - let v = edn::PlainSymbol::plain("?v"); - - assert_edn_parses_to!(Where::not_join_clause, - "(not-join [?e] [?e ?a ?v])", - WhereClause::NotJoin( - NotJoin { - unify_vars: UnifyVars::Explicit(btreeset!{variable(e.clone())}), - clauses: vec![WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(variable(e)), - attribute: PatternNonValuePlace::Variable(variable(a)), - value: PatternValuePlace::Variable(variable(v)), - tx: PatternNonValuePlace::Placeholder, - })], - })); - } - - #[test] - fn test_find_sp_variable() { - let sym = edn::PlainSymbol::plain("?x"); - let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone())]); - assert_parses_to!(|| vector().of_exactly(Query::variable()), input, variable(sym)); - } - - #[test] - fn test_find_scalar() { - let sym = edn::PlainSymbol::plain("?x"); - let period = edn::PlainSymbol::plain("."); - let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())]); - assert_parses_to!(|| vector().of_exactly(Find::find_scalar()), - input, - FindSpec::FindScalar(Element::Variable(variable(sym)))); - } - - #[test] - fn test_find_coll() { - let sym = edn::PlainSymbol::plain("?x"); - let period = edn::PlainSymbol::plain("..."); - 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)))); - } - - #[test] - fn test_find_rel() { - let vx = edn::PlainSymbol::plain("?x"); - let vy = edn::PlainSymbol::plain("?y"); - let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())]); - assert_parses_to!(|| vector().of_exactly(Find::find_rel()), - input, - FindSpec::FindRel(vec![Element::Variable(variable(vx)), - Element::Variable(variable(vy))])); - } - - #[test] - fn test_find_tuple() { - let vx = edn::PlainSymbol::plain("?x"); - let vy = edn::PlainSymbol::plain("?y"); - 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_natural_numbers() { - let text = edn::Value::Text("foo".to_string()); - let neg = edn::Value::Integer(-10); - let zero = edn::Value::Integer(0); - let pos = edn::Value::Integer(5); - - // This is terrible, but destructuring errors is frustrating. - let input = text.with_spans(); - let mut par = Query::natural_number(); - let x = par.parse(input.atom_stream()).err().expect("an error").errors; - let result = format!("{:?}", x); - assert_eq!(result, "[Other(Error(InvalidLimit(Text(\"foo\")), State { next_error: None, backtrace: None }))]"); - - let input = neg.with_spans(); - let mut par = Query::natural_number(); - let x = par.parse(input.atom_stream()).err().expect("an error").errors; - let result = format!("{:?}", x); - assert_eq!(result, "[Other(Error(InvalidLimit(Integer(-10)), State { next_error: None, backtrace: None }))]"); - - let input = zero.with_spans(); - let mut par = Query::natural_number(); - let x = par.parse(input.atom_stream()).err().expect("an error").errors; - let result = format!("{:?}", x); - assert_eq!(result, "[Other(Error(InvalidLimit(Integer(0)), State { next_error: None, backtrace: None }))]"); - - let input = pos.with_spans(); - let mut par = Query::natural_number(); - assert_eq!(None, par.parse(input.atom_stream()).err()); - } - - #[test] - fn test_fn_arg_collections() { - let vx = edn::PlainSymbol::plain("?x"); - let vy = edn::PlainSymbol::plain("?y"); - let input = edn::Value::Vector(vec![edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()), - edn::Value::PlainSymbol(vy.clone())])]); - - assert_parses_to!(|| vector().of_exactly(Query::fn_arg()), - input, - FnArg::Vector(vec![FnArg::Variable(variable(vx)), - FnArg::Variable(variable(vy)), - ])); - } - - #[test] - fn test_bind_scalar() { - let vx = edn::PlainSymbol::plain("?x"); - assert_edn_parses_to!(|| list().of_exactly(Bind::binding()), - "(?x)", - Binding::BindScalar(variable(vx))); - } - - #[test] - fn test_bind_coll() { - let vx = edn::PlainSymbol::plain("?x"); - assert_edn_parses_to!(|| list().of_exactly(Bind::binding()), - "([?x ...])", - Binding::BindColl(variable(vx))); - } - - #[test] - fn test_bind_rel() { - let vx = edn::PlainSymbol::plain("?x"); - let vy = edn::PlainSymbol::plain("?y"); - let vw = edn::PlainSymbol::plain("?w"); - assert_edn_parses_to!(|| list().of_exactly(Bind::binding()), - "([[?x ?y _ ?w]])", - Binding::BindRel(vec![VariableOrPlaceholder::Variable(variable(vx)), - VariableOrPlaceholder::Variable(variable(vy)), - VariableOrPlaceholder::Placeholder, - VariableOrPlaceholder::Variable(variable(vw)), - ])); - } - - #[test] - fn test_bind_tuple() { - let vx = edn::PlainSymbol::plain("?x"); - let vy = edn::PlainSymbol::plain("?y"); - let vw = edn::PlainSymbol::plain("?w"); - assert_edn_parses_to!(|| list().of_exactly(Bind::binding()), - "([?x ?y _ ?w])", - Binding::BindTuple(vec![VariableOrPlaceholder::Variable(variable(vx)), - VariableOrPlaceholder::Variable(variable(vy)), - VariableOrPlaceholder::Placeholder, - VariableOrPlaceholder::Variable(variable(vw)), - ])); - } - - #[test] - fn test_the() { - assert_edn_parses_to!(Find::seq_elem, - "(the ?y)", - Element::Corresponding(Variable::from_valid_name("?y"))); - assert_edn_parses_to!(Find::find_tuple, - "[(the ?x) ?y]", - FindSpec::FindTuple(vec![Element::Corresponding(Variable::from_valid_name("?x")), - Element::Variable(Variable::from_valid_name("?y"))])); - assert_edn_parses_to!(Find::spec, - "[(the ?x) ?y]", - FindSpec::FindTuple(vec![Element::Corresponding(Variable::from_valid_name("?x")), - Element::Variable(Variable::from_valid_name("?y"))])); - let expected_query = - FindQuery { - find_spec: FindSpec::FindTuple(vec![Element::Corresponding(Variable::from_valid_name("?x")), - Element::Variable(Variable::from_valid_name("?y"))]), - where_clauses: vec![ - WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")), - attribute: PatternNonValuePlace::Placeholder, - value: PatternValuePlace::Variable(Variable::from_valid_name("?y")), - tx: PatternNonValuePlace::Placeholder, - })], - - default_source: SrcVar::DefaultSrc, - with: Default::default(), - in_vars: Default::default(), - in_sources: Default::default(), - limit: Limit::None, - order: None, - }; - assert_edn_parses_to!(Find::query, - "[:find [(the ?x) ?y] - :where [?x _ ?y]]", - expected_query); - - // If we give a malformed pull expression, we don't fall through to aggregates. - assert_parse_failure_contains!(Find::elem, - "(pull x [])", - r#"errors: [Unexpected(Token(ValueAndSpan { inner: PlainSymbol(PlainSymbol("x")), span: Span(6, 7) })), Expected(Borrowed("variable"))]"#); - } - - #[test] - fn test_where_fn() { - assert_edn_parses_to!(Where::where_fn, - "[(f ?x 1) ?y]", - WhereClause::WhereFn(WhereFn { - operator: edn::PlainSymbol::plain("f"), - args: vec![FnArg::Variable(Variable::from_valid_name("?x")), - FnArg::EntidOrInteger(1)], - binding: Binding::BindScalar(Variable::from_valid_name("?y")), - })); - - assert_edn_parses_to!(Where::where_fn, - "[(f ?x) [?y ...]]", - WhereClause::WhereFn(WhereFn { - operator: edn::PlainSymbol::plain("f"), - args: vec![FnArg::Variable(Variable::from_valid_name("?x"))], - binding: Binding::BindColl(Variable::from_valid_name("?y")), - })); - - assert_edn_parses_to!(Where::where_fn, - "[(f) [?y _]]", - WhereClause::WhereFn(WhereFn { - operator: edn::PlainSymbol::plain("f"), - args: vec![], - binding: Binding::BindTuple(vec![VariableOrPlaceholder::Variable(Variable::from_valid_name("?y")), - VariableOrPlaceholder::Placeholder]), - })); - - assert_edn_parses_to!(Where::where_fn, - "[(f) [[_ ?y]]]", - WhereClause::WhereFn(WhereFn { - operator: edn::PlainSymbol::plain("f"), - args: vec![], - binding: Binding::BindRel(vec![VariableOrPlaceholder::Placeholder, - VariableOrPlaceholder::Variable(Variable::from_valid_name("?y"))]), - })); - } - - #[test] - fn test_type_anno() { - assert_edn_parses_to!(Where::type_annotation, - "[(type ?x :db.type/string)]", - WhereClause::TypeAnnotation(TypeAnnotation { - value_type: edn::Keyword::namespaced("db.type", "string"), - variable: Variable::from_valid_name("?x"), - })); - // We don't check for valid types, or even that the type is namespaced. - assert_edn_parses_to!(Where::clause, - "[[type ?foo :db_type_long]]", - WhereClause::TypeAnnotation(TypeAnnotation { - value_type: edn::Keyword::plain("db_type_long"), - variable: Variable::from_valid_name("?foo"), - })); - - } - - #[test] - fn test_pull() { - assert_edn_parses_to!(Query::pull_attribute, - "*", - PullAttributeSpec::Wildcard); - assert_edn_parses_to!(Query::pull_attributes, - "[*]", - vec![PullAttributeSpec::Wildcard]); - assert_edn_parses_to!(Find::elem, - "(pull ?v [*])", - Element::Pull(Pull { - var: Variable::from_valid_name("?v"), - patterns: vec![PullAttributeSpec::Wildcard], - })); - - let foo_bar = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "bar")); - let foo_baz = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "baz")); - let foo_horse = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "horse")); - let horse = ::std::rc::Rc::new(edn::Keyword::plain("horse")); - assert_edn_parses_to!(Query::pull_concrete_attribute, - ":foo/bar", - PullAttributeSpec::Attribute( - PullConcreteAttribute::Ident(foo_bar.clone()).into())); - assert_edn_parses_to!(Query::pull_attribute, - ":foo/bar", - PullAttributeSpec::Attribute( - PullConcreteAttribute::Ident(foo_bar.clone()).into())); - assert_edn_parses_to!(Find::elem, - "(pull ?v [:foo/bar :as :foo/horse, :foo/baz])", - Element::Pull(Pull { - var: Variable::from_valid_name("?v"), - patterns: vec![ - PullAttributeSpec::Attribute( - NamedPullAttribute { - attribute: PullConcreteAttribute::Ident(foo_bar.clone()), - alias: Some(foo_horse), - }), - PullAttributeSpec::Attribute( - PullConcreteAttribute::Ident(foo_baz.clone()).into()), - ], - })); - assert_edn_parses_to!(Find::elem, - "(pull ?v [:foo/bar :as :horse, :foo/baz])", - Element::Pull(Pull { - var: Variable::from_valid_name("?v"), - patterns: vec![ - PullAttributeSpec::Attribute( - NamedPullAttribute { - attribute: PullConcreteAttribute::Ident(foo_bar.clone()), - alias: Some(horse), - }), - PullAttributeSpec::Attribute( - PullConcreteAttribute::Ident(foo_baz.clone()).into()), - ], - })); - assert_parse_failure_contains!(Find::elem, - "(pull ?x [* :foo/bar])", - r#"errors: [Unexpected(Borrowed("wildcard with specified attributes"))]"#); - } - - #[test] - fn test_query_with_pull() { - let q = "[:find ?x (pull ?x [:foo/bar]) :where [?x _ _]]"; - let expected_query = - FindQuery { - find_spec: FindSpec::FindRel(vec![ - Element::Variable(Variable::from_valid_name("?x")), - Element::Pull(Pull { - var: Variable::from_valid_name("?x"), - patterns: vec![ - PullAttributeSpec::Attribute( - PullConcreteAttribute::Ident( - ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "bar")) - ).into() - ), - ] })]), - where_clauses: vec![ - WhereClause::Pattern(Pattern { - source: None, - entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")), - attribute: PatternNonValuePlace::Placeholder, - value: PatternValuePlace::Placeholder, - tx: PatternNonValuePlace::Placeholder, - })], - - default_source: SrcVar::DefaultSrc, - with: Default::default(), - in_vars: Default::default(), - in_sources: Default::default(), - limit: Limit::None, - order: None, - }; - assert_edn_parses_to!(Find::query, - q, - expected_query); - } -}