diff --git a/src/data/expr.rs b/src/data/expr.rs index 568f4b06..1f269fde 100644 --- a/src/data/expr.rs +++ b/src/data/expr.rs @@ -4,21 +4,48 @@ use anyhow::Result; use itertools::Itertools; use crate::data::expr::ExprError::UnexpectedArgs; +use crate::data::keyword::Keyword; +use crate::data::tuple::Tuple; use crate::data::value::DataValue; -use crate::query::compile::Term; #[derive(Debug, thiserror::Error)] pub enum ExprError { #[error("unexpected args for {0}: {1:?}")] UnexpectedArgs(&'static str, Vec), + #[error("unexpected return type: expected {0}, got {1:?}")] + UnexpectedReturnType(String, DataValue), } #[derive(Debug, Clone)] pub enum Expr { - Const(Term), + Binding(Keyword), + BoundIdx(usize), + Const(DataValue), Apply(&'static Op, Box<[Expr]>), } +impl Expr { + pub(crate) fn eval(&self, bindings: &Tuple) -> Result { + match self { + Expr::Binding(_) => { + unreachable!() + } + Expr::BoundIdx(i) => Ok(bindings.0[*i].clone()), + Expr::Const(d) => Ok(d.clone()), + Expr::Apply(op, args) => { + let args: Box<[DataValue]> = args.iter().map(|v| v.eval(bindings)).try_collect()?; + (op.inner)(&args) + } + } + } + pub(crate) fn eval_pred(&self, bindings: &Tuple) -> Result { + match self.eval(bindings)? { + DataValue::Bool(b) => Ok(b), + v => Err(ExprError::UnexpectedReturnType("bool".to_string(), v).into()), + } + } +} + #[derive(Clone)] pub struct Op { pub(crate) name: &'static str, @@ -171,4 +198,4 @@ fn op_not(args: &[DataValue]) -> Result { } else { Err(UnexpectedArgs("not", args.to_vec()).into()) } -} \ No newline at end of file +} diff --git a/src/parse/query.rs b/src/parse/query.rs index 76d5e0d4..d58874c5 100644 --- a/src/parse/query.rs +++ b/src/parse/query.rs @@ -7,7 +7,8 @@ use serde_json::Map; use crate::data::attr::Attribute; use crate::data::expr::{ - Expr, OP_ADD, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LT, OP_MUL, OP_NEQ, OP_SUB, + Expr, OP_ADD, OP_AND, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LT, OP_MUL, OP_NEQ, OP_NOT, OP_OR, + OP_SUB, }; use crate::data::json::JsonValue; use crate::data::keyword::{Keyword, PROG_ENTRY}; @@ -112,6 +113,9 @@ impl SessionTx { "Ge" => &OP_GE, "Lt" => &OP_LT, "Le" => &OP_LE, + "Or" => &OP_OR, + "And" => &OP_AND, + "Not" => &OP_NOT, s => return Err(QueryCompilationError::UnknownOperator(s.to_string()).into()), }; @@ -159,14 +163,14 @@ impl SessionTx { JsonValue::String(s) => { let kw = Keyword::from(s as &str); if kw.is_reserved() { - Ok(Expr::Const(Term::Var(kw))) + Ok(Expr::Binding(kw)) } else { - Ok(Expr::Const(Term::Const(DataValue::String(s.into())))) + Ok(Expr::Const(DataValue::String(s.into()))) } } JsonValue::Object(map) => { if let Some(v) = map.get("const") { - Ok(Expr::Const(Term::Const(v.into()))) + Ok(Expr::Const(v.into())) } else if map.contains_key("pred") { Self::parse_expr(map) } else { @@ -177,7 +181,7 @@ impl SessionTx { .into()) } } - v => Ok(Expr::Const(Term::Const(v.into()))), + v => Ok(Expr::Const(v.into())), } } fn parse_rule_atom(&mut self, payload: &Map, vld: Validity) -> Result {