From a9a82ea1a77e880310b071a02520d22a644018d5 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Mon, 17 Apr 2017 18:46:40 -0700 Subject: [PATCH] Part 1: parse :in. We also at this point switch from using `Vec` to `BTreeSet`. This allows us to guarantee no duplicates later; we'll reject duplicates at parse time. --- query-parser/src/parse.rs | 42 ++++++++++++++++++++++++++++++++------- query/src/lib.rs | 21 ++++++++++++++------ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/query-parser/src/parse.rs b/query-parser/src/parse.rs index 5b4d879d..2952afdc 100644 --- a/query-parser/src/parse.rs +++ b/query-parser/src/parse.rs @@ -15,6 +15,8 @@ extern crate mentat_query; use std; // To refer to std::result::Result. +use std::collections::BTreeSet; + use self::combine::{eof, many, many1, optional, parser, satisfy, satisfy_map, Parser, ParseResult, Stream}; use self::combine::combinator::{choice, or, try}; @@ -65,6 +67,11 @@ error_chain! { } errors { + DuplicateVariableError { + description("duplicate variable") + display("duplicates in variable list") + } + NotAVariableError(value: edn::ValueAndSpan) { description("not a variable") display("not a variable: '{}'", value) @@ -328,6 +335,8 @@ def_parser!(Find, spec, FindSpec, { def_matches_keyword!(Find, literal_find, "find"); +def_matches_keyword!(Find, literal_in, "in"); + def_matches_keyword!(Find, literal_with, "with"); def_matches_keyword!(Find, literal_where, "where"); @@ -337,11 +346,26 @@ def_matches_keyword!(Find, literal_order, "order"); /// Express something close to a builder pattern for a `FindQuery`. enum FindQueryPart { FindSpec(FindSpec), - With(Vec), + With(BTreeSet), + In(BTreeSet), WhereClauses(Vec), Order(Vec), } +def_parser!(Find, vars, BTreeSet, { + vector().of_exactly(many(Query::variable()).map(|vars: Vec| { + let given = vars.len(); + let set: BTreeSet = vars.into_iter().collect(); + if given != set.len() { + // TODO: find out what the variable is! + // TODO: figure out how to use `and_then` to return an error here. + panic!(Error::from_kind(ErrorKind::DuplicateVariableError)); + } else { + set + } + })) +}); + /// 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. @@ -349,8 +373,9 @@ def_parser!(Find, query, FindQuery, { let p_find_spec = Find::literal_find() .with(vector().of_exactly(Find::spec().map(FindQueryPart::FindSpec))); - let p_with_vars = Find::literal_with() - .with(vector().of_exactly(many(Query::variable()).map(FindQueryPart::With))); + let p_in_vars = Find::literal_in().with(Find::vars().map(FindQueryPart::In)); + + let p_with_vars = Find::literal_with().with(Find::vars().map(FindQueryPart::With)); let p_where_clauses = Find::literal_where() .with(vector().of_exactly(Where::clauses().map(FindQueryPart::WhereClauses))).expected(":where clauses"); @@ -359,14 +384,16 @@ def_parser!(Find, query, FindQuery, { .with(vector().of_exactly(many1(Query::order()).map(FindQueryPart::Order))); (or(map(), keyword_map())) - .of_exactly(many(choice::<[&mut Parser; 4], _>([ + .of_exactly(many(choice::<[&mut Parser; 5], _>([ &mut try(p_find_spec), + &mut try(p_in_vars), &mut try(p_with_vars), &mut try(p_where_clauses), &mut try(p_order_clauses), ]))) .and_then(|parts: Vec| -> std::result::Result> { let mut find_spec = None; + let mut in_vars = None; let mut with_vars = None; let mut where_clauses = None; let mut order_clauses = None; @@ -375,6 +402,7 @@ def_parser!(Find, query, FindQuery, { match part { FindQueryPart::FindSpec(x) => find_spec = Some(x), FindQueryPart::With(x) => with_vars = Some(x), + FindQueryPart::In(x) => in_vars = Some(x), FindQueryPart::WhereClauses(x) => where_clauses = Some(x), FindQueryPart::Order(x) => order_clauses = Some(x), } @@ -383,9 +411,9 @@ def_parser!(Find, query, FindQuery, { Ok(FindQuery { find_spec: find_spec.clone().ok_or(combine::primitives::Error::Unexpected("expected :find".into()))?, default_source: SrcVar::DefaultSrc, - with: with_vars.unwrap_or(vec![]), - in_vars: vec![], // TODO - in_sources: vec![], // TODO + with: with_vars.unwrap_or(BTreeSet::default()), + in_vars: in_vars.unwrap_or(BTreeSet::default()), + in_sources: BTreeSet::default(), // TODO order: order_clauses, where_clauses: where_clauses.ok_or(combine::primitives::Error::Unexpected("expected :where".into()))?, }) diff --git a/query/src/lib.rs b/query/src/lib.rs index 3252c447..3a1c501a 100644 --- a/query/src/lib.rs +++ b/query/src/lib.rs @@ -33,12 +33,21 @@ extern crate edn; extern crate mentat_core; -use std::collections::BTreeSet; +use std::collections::{ + BTreeMap, + BTreeSet, +}; + use std::fmt; use std::rc::Rc; + use edn::{BigInt, OrderedFloat}; pub use edn::{NamespacedKeyword, PlainSymbol}; -use mentat_core::TypedValue; + +use mentat_core::{ + TypedValue, + ValueType, +}; pub type SrcVarName = String; // Do not include the required syntactic '$'. @@ -138,7 +147,7 @@ pub enum Direction { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Order(pub Direction, pub Variable); // Future: Element instead of Variable? -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum SrcVar { DefaultSrc, NamedSrc(SrcVarName), @@ -600,9 +609,9 @@ pub enum WhereClause { pub struct FindQuery { pub find_spec: FindSpec, pub default_source: SrcVar, - pub with: Vec, - pub in_vars: Vec, - pub in_sources: Vec, + pub with: BTreeSet, + pub in_vars: BTreeSet, + pub in_sources: BTreeSet, pub where_clauses: Vec, pub order: Option>, // TODO: in_rules;