if expression

main
Ziyang Hu 2 years ago
parent 0e9edd715f
commit 8484ae33a4

@ -1,9 +1,6 @@
use crate::data::expr::{Expr, StaticExpr};
use crate::data::expr_parser::ExprParseError;
use crate::data::op::{
Op, OpAdd, OpAnd, OpCoalesce, OpDiv, OpEq, OpGe, OpGt, OpIsNull, OpLe, OpLt, OpMinus, OpMod,
OpMul, OpNe, OpNegate, OpNotNull, OpOr, OpPow, OpStrCat, OpSub,
};
use crate::data::op::{Op, OpAdd, OpAnd, OpCoalesce, OpConcat, OpDiv, OpEq, OpGe, OpGt, OpIsNull, OpLe, OpLt, OpMerge, OpMinus, OpMod, OpMul, OpNe, OpNot, OpNotNull, OpOr, OpPow, OpStrCat, OpSub, partial_eval_and, partial_eval_coalesce, partial_eval_if_expr, partial_eval_or, row_eval_and, row_eval_coalesce, row_eval_if_expr, row_eval_or};
use crate::data::tuple_set::{ColId, TableId, TupleSetIdx};
use crate::data::value::{StaticValue, Value};
use std::borrow::Cow;
@ -60,6 +57,15 @@ pub(crate) trait ExprEvalContext {
fn resolve_table_col<'a>(&'a self, binding: &str, col: &str) -> Option<(TableId, ColId)>;
}
impl ExprEvalContext for () {
fn resolve<'a>(&'a self, _key: &str) -> Option<Expr<'a>> {
None
}
fn resolve_table_col<'a>(&'a self, _binding: &str, _col: &str) -> Option<(TableId, ColId)> {
None
}
}
fn extract_optimized_bin_args(args: Vec<Expr>) -> (Expr, Expr) {
let mut args = args.into_iter();
(
@ -150,16 +156,37 @@ impl<'a> Expr<'a> {
}
}
Expr::Apply(op, args) => {
let args = args
.into_iter()
.map(|v| v.partial_eval(ctx))
.collect::<Result<Vec<_>>>()?;
if op.has_side_effect() {
Expr::Apply(op, args)
} else {
match op.partial_eval(args.clone())? {
Some(v) => v,
None => Expr::Apply(op, args),
// special cases
match op.name() {
n if n == OpAnd.name() => partial_eval_and(ctx, args)?,
n if n == OpOr.name() => partial_eval_or(ctx, args)?,
n if n == OpCoalesce.name() => partial_eval_coalesce(ctx, args)?,
_ => {
let mut has_unevaluated = false;
let mut has_null = false;
let args = args
.into_iter()
.map(|v| {
let v = v.partial_eval(ctx);
if !matches!(v, Ok(Expr::Const(_))) {
has_unevaluated = true;
} else if matches!(v, Ok(Expr::Const(Value::Null))) {
has_null = true;
}
v
}).collect::<Result<Vec<_>>>()?;
if has_unevaluated {
Expr::Apply(op, args)
} else {
let args = args.into_iter().map(|v| {
match v {
Expr::Const(v) => v,
_ => unreachable!()
}
}).collect();
op.eval(has_null, args)
.map(Expr::Const)?
}
}
}
}
@ -172,14 +199,11 @@ impl<'a> Expr<'a> {
.into_iter()
.map(|v| v.partial_eval(ctx))
.collect::<Result<Vec<_>>>()?;
if op.has_side_effect() {
Expr::ApplyAgg(op, a_args, args)
} else {
match op.partial_eval(a_args.clone(), args.clone())? {
Some(v) => v,
None => Expr::ApplyAgg(op, a_args, args),
}
}
Expr::ApplyAgg(op, a_args, args)
}
Expr::IfExpr(args) => {
let (cond, if_part, else_part) = *args;
partial_eval_if_expr(ctx, cond, if_part, else_part)?
}
Expr::ApplyZero(_)
| Expr::ApplyOne(_, _)
@ -197,7 +221,7 @@ impl<'a> Expr<'a> {
| Expr::Ge(_)
| Expr::Lt(_)
| Expr::Le(_)
| Expr::Negate(_)
| Expr::Not(_)
| Expr::Minus(_)
| Expr::IsNull(_)
| Expr::NotNull(_)
@ -208,6 +232,7 @@ impl<'a> Expr<'a> {
Ok(res)
}
pub(crate) fn optimize_ops(self) -> Self {
// Note: `and`, `or` and `coalesce` do not short-circuit if not optimized
match self {
Expr::List(l) => Expr::List(l.into_iter().map(|v| v.optimize_ops()).collect()),
Expr::Dict(d) => {
@ -229,8 +254,8 @@ impl<'a> Expr<'a> {
name if name == OpGe.name() => Expr::Ge(extract_optimized_bin_args(args).into()),
name if name == OpLt.name() => Expr::Lt(extract_optimized_bin_args(args).into()),
name if name == OpLe.name() => Expr::Le(extract_optimized_bin_args(args).into()),
name if name == OpNegate.name() => {
Expr::Negate(extract_optimized_u_args(args).into())
name if name == OpNot.name() => {
Expr::Not(extract_optimized_u_args(args).into())
}
name if name == OpMinus.name() => {
Expr::Minus(extract_optimized_u_args(args).into())
@ -286,7 +311,10 @@ impl<'a> Expr<'a> {
),
Expr::FieldAcc(f, arg) => Expr::FieldAcc(f, arg.optimize_ops().into()),
Expr::IdxAcc(i, arg) => Expr::IdxAcc(i, arg.optimize_ops().into()),
Expr::IfExpr(args) => {
let (cond, if_part, else_part) = *args;
Expr::IfExpr((cond.optimize_ops(), if_part.optimize_ops(), else_part.optimize_ops()).into())
},
v @ (Expr::Const(_)
| Expr::Variable(_)
| Expr::TableCol(_, _)
@ -307,7 +335,7 @@ impl<'a> Expr<'a> {
| Expr::Ge(_)
| Expr::Lt(_)
| Expr::Le(_)
| Expr::Negate(_)
| Expr::Not(_)
| Expr::Minus(_)
| Expr::IsNull(_)
| Expr::NotNull(_)
@ -402,6 +430,10 @@ impl<'a> Expr<'a> {
}
v => return Err(EvalError::IndexAccess(*idx, v.to_static())),
},
Expr::IfExpr(args) => {
let (cond, if_part, else_part) = args.as_ref();
row_eval_if_expr(ctx, cond, if_part, else_part)?
}
// optimized implementations, not really necessary
Expr::Add(args) => OpAdd.eval_two_non_null(
match args.as_ref().0.row_eval(ctx)? {
@ -533,8 +565,8 @@ impl<'a> Expr<'a> {
v => v,
},
)?,
Expr::Negate(arg) => {
OpNegate.eval_one_non_null(match arg.as_ref().row_eval(ctx)? {
Expr::Not(arg) => {
OpNot.eval_one_non_null(match arg.as_ref().row_eval(ctx)? {
v @ Value::Null => return Ok(v),
v => v,
})?
@ -545,17 +577,21 @@ impl<'a> Expr<'a> {
})?,
Expr::IsNull(arg) => OpIsNull.eval_one(arg.as_ref().row_eval(ctx)?)?,
Expr::NotNull(arg) => OpNotNull.eval_one(arg.as_ref().row_eval(ctx)?)?,
Expr::Coalesce(args) => OpCoalesce.eval_two(
args.as_ref().0.row_eval(ctx)?,
args.as_ref().1.row_eval(ctx)?,
// These implementations are special in that they short-circuit
Expr::Coalesce(args) => row_eval_coalesce(
ctx,
&args.as_ref().0,
&args.as_ref().1,
)?,
Expr::Or(args) => OpOr.eval_two(
args.as_ref().0.row_eval(ctx)?,
args.as_ref().1.row_eval(ctx)?,
Expr::Or(args) => row_eval_or(
ctx,
&args.as_ref().0,
&args.as_ref().1,
)?,
Expr::And(args) => OpAnd.eval_two(
args.as_ref().0.row_eval(ctx)?,
args.as_ref().1.row_eval(ctx)?,
Expr::And(args) => row_eval_and(
ctx,
&args.as_ref().0,
&args.as_ref().1,
)?,
};
Ok(res)
@ -570,19 +606,39 @@ mod tests {
#[test]
fn evaluations() -> Result<()> {
dbg!(str2expr("123")?.row_eval(&())?);
dbg!(str2expr("123")?.partial_eval(&())?);
dbg!(str2expr("123 + 457")?.row_eval(&())?);
dbg!(str2expr("123 + 457")?.partial_eval(&())?);
dbg!(str2expr("123 + 457.1")?.row_eval(&())?);
dbg!(str2expr("123 + 457.1")?.partial_eval(&())?);
dbg!(str2expr("'123' ++ '457.1'")?.row_eval(&())?);
dbg!(str2expr("'123' ++ '457.1'")?.partial_eval(&())?);
dbg!(str2expr("null ~ null ~ 123 ~ null")?.row_eval(&())?);
dbg!(str2expr("null ~ null ~ 123 ~ null")?.partial_eval(&())?);
dbg!(str2expr("2*3+1/10")?.row_eval(&())?);
dbg!(str2expr("2*3+1/10")?.partial_eval(&())?);
dbg!(str2expr("1>null")?.row_eval(&())?);
dbg!(str2expr("1>null")?.partial_eval(&())?);
dbg!(str2expr("'c'>'d'")?.row_eval(&())?);
dbg!(str2expr("'c'>'d'")?.partial_eval(&())?);
dbg!(str2expr("null && true && null")?.row_eval(&())?);
dbg!(str2expr("null && true && null")?.partial_eval(&())?);
dbg!(str2expr("null && false && null")?.row_eval(&())?);
dbg!(str2expr("null && false && null")?.partial_eval(&())?);
dbg!(str2expr("null || true || null")?.row_eval(&())?);
dbg!(str2expr("null || true || null")?.partial_eval(&())?);
dbg!(str2expr("null || false || null")?.row_eval(&())?);
dbg!(str2expr("null || false || null")?.partial_eval(&())?);
dbg!(str2expr("!true")?.row_eval(&())?);
dbg!(str2expr("!true")?.partial_eval(&())?);
dbg!(str2expr("!null")?.row_eval(&())?);
dbg!(str2expr("!null")?.partial_eval(&())?);
dbg!(str2expr("if null {1} else {2}")?.row_eval(&())?);
dbg!(str2expr("if null {1} else {2}")?.partial_eval(&())?);
dbg!(str2expr("if 1 == 2 {'a'}")?.row_eval(&())?);
dbg!(str2expr("if 1 == 2 {'a'}")?.partial_eval(&())?);
dbg!(str2expr("if 1 == 2 {'a'} else if 3 == 3 {'b'} else {'c'}")?.row_eval(&())?);
dbg!(str2expr("if 1 == 2 {'a'} else if 3 == 3 {'b'} else {'c'}")?.partial_eval(&())?);
Ok(())
}

@ -1,6 +1,6 @@
use crate::data::op::{
AggOp, Op, OpAdd, OpAnd, OpCoalesce, OpDiv, OpEq, OpGe, OpGt, OpIsNull, OpLe, OpLt, OpMinus,
OpMod, OpMul, OpNe, OpNegate, OpNotNull, OpOr, OpPow, OpStrCat, OpSub, UnresolvedOp,
OpMod, OpMul, OpNe, OpNot, OpNotNull, OpOr, OpPow, OpStrCat, OpSub, UnresolvedOp,
};
use crate::data::tuple_set::{ColId, TableId, TupleSetIdx};
use crate::data::value::{StaticValue, Value};
@ -35,6 +35,7 @@ pub(crate) enum Expr<'a> {
ApplyAgg(Arc<dyn AggOp + Send + Sync>, Vec<Expr<'a>>, Vec<Expr<'a>>),
FieldAcc(String, Box<Expr<'a>>),
IdxAcc(usize, Box<Expr<'a>>),
IfExpr(Box<(Expr<'a>, Expr<'a>, Expr<'a>)>),
// optimized
ApplyZero(Arc<dyn Op + Send + Sync>),
ApplyOne(Arc<dyn Op + Send + Sync>, Box<Expr<'a>>),
@ -52,7 +53,7 @@ pub(crate) enum Expr<'a> {
Ge(Box<(Expr<'a>, Expr<'a>)>),
Lt(Box<(Expr<'a>, Expr<'a>)>),
Le(Box<(Expr<'a>, Expr<'a>)>),
Negate(Box<Expr<'a>>),
Not(Box<Expr<'a>>),
Minus(Box<Expr<'a>>),
IsNull(Box<Expr<'a>>),
NotNull(Box<Expr<'a>>),
@ -117,7 +118,7 @@ impl<'a> Debug for Expr<'a> {
Expr::Ge(args) => write!(f, "(`>= {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Lt(args) => write!(f, "(`< {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Le(args) => write!(f, "(`<= {:?} {:?})", args.as_ref().0, args.as_ref().1),
Expr::Negate(arg) => write!(f, "(`! {:?})", arg.as_ref()),
Expr::Not(arg) => write!(f, "(`! {:?})", arg.as_ref()),
Expr::Minus(arg) => write!(f, "(`-- {:?})", arg.as_ref()),
Expr::IsNull(arg) => write!(f, "(`is_null {:?})", arg.as_ref()),
Expr::NotNull(arg) => write!(f, "(`not_null {:?})", arg.as_ref()),
@ -138,6 +139,10 @@ impl<'a> Debug for Expr<'a> {
.collect::<Vec<_>>()
.join(" ")
),
Expr::IfExpr(args) => {
let args = args.as_ref();
write!(f, "(if {:?} {:?} {:?})", args.0, args.1, args.2)
}
Expr::FieldAcc(field, arg) => write!(f, "(.{} {:?})", field, arg),
Expr::IdxAcc(i, arg) => write!(f, "(.{} {:?})", i, arg),
}
@ -379,7 +384,7 @@ impl<'a> From<Expr<'a>> for Value<'a> {
Expr::Ge(arg) => build_value_from_binop(OpGe.name(), *arg),
Expr::Lt(arg) => build_value_from_binop(OpLt.name(), *arg),
Expr::Le(arg) => build_value_from_binop(OpLe.name(), *arg),
Expr::Negate(arg) => build_value_from_uop(OpNegate.name(), *arg),
Expr::Not(arg) => build_value_from_uop(OpNot.name(), *arg),
Expr::Minus(arg) => build_value_from_uop(OpMinus.name(), *arg),
Expr::IsNull(arg) => build_value_from_uop(OpIsNull.name(), *arg),
Expr::NotNull(arg) => build_value_from_uop(OpNotNull.name(), *arg),
@ -394,6 +399,9 @@ impl<'a> From<Expr<'a>> for Value<'a> {
]
.into(),
),
Expr::IfExpr(_) => {
todo!()
},
Expr::ApplyAgg(op, a_args, args) => build_tagged_value(
"ApplyAgg",
vec![

@ -1,7 +1,7 @@
use crate::data::expr::Expr;
use crate::data::op::{
Op, OpAdd, OpAnd, OpCoalesce, OpConcat, OpDiv, OpEq, OpGe, OpGt, OpLe, OpLt, OpMerge, OpMinus,
OpMod, OpMul, OpNe, OpNegate, OpOr, OpPow, OpStrCat, OpSub, UnresolvedOp,
OpMod, OpMul, OpNe, OpNot, OpOr, OpPow, OpStrCat, OpSub, UnresolvedOp,
};
use crate::data::value::Value;
use crate::parser::number::parse_int;
@ -63,6 +63,31 @@ lazy_static! {
};
}
fn build_if_expr(pair: Pair<Rule>) -> Result<Expr> {
let mut if_parts = vec![];
let mut else_part = Expr::Const(Value::Null);
for pair in pair.into_inner() {
if pair.as_rule() == Rule::else_clause {
else_part = Expr::try_from(pair)?;
} else {
if_parts.push(build_if_clause(pair)?)
}
}
Ok(if_parts.into_iter().rev().fold(else_part, |accum, (cond, expr)| {
Expr::IfExpr((cond, expr, accum).into())
}))
}
fn build_if_clause(pair: Pair<Rule>) -> Result<(Expr, Expr)> {
let mut pairs = pair.into_inner();
let cond = pairs.next().unwrap();
let cond = Expr::try_from(cond)?;
let expr = pairs.next().unwrap();
let expr = Expr::try_from(expr)?;
Ok((cond, expr))
}
fn build_expr_primary(pair: Pair<Rule>) -> Result<Expr> {
match pair.as_rule() {
Rule::expr => build_expr_primary(pair.into_inner().next().unwrap()),
@ -101,8 +126,9 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Expr> {
let op = p.as_rule();
let op: Arc<dyn Op + Send + Sync> = match op {
Rule::term => return build_expr_primary(p),
Rule::negate => Arc::new(OpNegate),
Rule::negate => Arc::new(OpNot),
Rule::minus => Arc::new(OpMinus),
Rule::if_expr => return build_if_expr(p),
_ => unreachable!(),
};
let term = build_expr_primary(inner.next().unwrap())?;
@ -335,4 +361,33 @@ pub(crate) mod tests {
dbg!(str2expr("[...a,...b,1,2,...e,3]")?);
Ok(())
}
#[test]
fn conditionals() -> Result<()> {
let s = r#"if a { b + c * d } else if (x) { y } else {z}"#;
dbg!(str2expr(s));
let s = r#"if a { b + c * d }"#;
dbg!(str2expr(s));
let s = r#"(if a { b + c * d } else if (x) { y } else {z})+1"#;
dbg!(str2expr(s));
// let s = r#"cond {
// a > 1 => 1,
// a == 1 => 2,
// true => 3
// }"#;
// let pair = CozoParser::parse(Rule::expr, s).unwrap();
// dbg!(pair);
//
// let s = r#"switch(a) {
// 1 => 1,
// 2 => 2,
// .. => 3
// }"#;
// let pair = CozoParser::parse(Rule::expr, s).unwrap();
// dbg!(pair);
Ok(())
}
}

@ -3,6 +3,7 @@ mod boolean;
mod combine;
mod comparison;
mod text;
mod control;
use crate::data::eval::EvalError;
use crate::data::expr::Expr;
@ -15,6 +16,7 @@ pub(crate) use boolean::*;
pub(crate) use combine::*;
pub(crate) use comparison::*;
pub(crate) use text::*;
pub(crate) use control::*;
type Result<T> = result::Result<T, EvalError>;
@ -97,39 +99,39 @@ pub(crate) trait Op: Send + Sync {
self.name()
)
}
fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> Result<Option<Expr<'a>>> {
// usually those functions that needs specialized implementations are those with arity None
if let Some(arity) = self.arity() {
if arity != args.len() {
return Err(EvalError::ArityMismatch(self.name().to_string(), arity));
}
}
let mut has_null = false;
match args
.iter()
.map(|v| match v {
Expr::Const(v) => {
if *v == Value::Null {
has_null = true;
}
Some(v.clone())
}
_ => None,
})
.collect::<Vec<_>>()
.into_iter()
.collect::<Option<Vec<Value>>>()
{
Some(args) => Ok(Some(Expr::Const(self.eval(has_null, args)?))),
None => {
if self.non_null_args() && has_null {
Ok(Some(Expr::Const(Value::Null)))
} else {
Ok(None)
}
}
}
}
// fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> Result<Option<Expr<'a>>> {
// // usually those functions that needs specialized implementations are those with arity None
// if let Some(arity) = self.arity() {
// if arity != args.len() {
// return Err(EvalError::ArityMismatch(self.name().to_string(), arity));
// }
// }
// let mut has_null = false;
// match args
// .iter()
// .map(|v| match v {
// Expr::Const(v) => {
// if *v == Value::Null {
// has_null = true;
// }
// Some(v.clone())
// }
// _ => None,
// })
// .collect::<Vec<_>>()
// .into_iter()
// .collect::<Option<Vec<Value>>>()
// {
// Some(args) => Ok(Some(Expr::Const(self.eval(has_null, args)?))),
// None => {
// if self.non_null_args() && has_null {
// Ok(Some(Expr::Const(Value::Null)))
// } else {
// Ok(None)
// }
// }
// }
// }
}
pub(crate) trait AggOp: Send + Sync {
@ -143,13 +145,13 @@ pub(crate) trait AggOp: Send + Sync {
false
}
fn name(&self) -> &str;
fn partial_eval<'a>(
&self,
a_args: Vec<Expr<'a>>,
args: Vec<Expr<'a>>,
) -> Result<Option<Expr<'a>>> {
todo!()
}
// fn partial_eval<'a>(
// &self,
// a_args: Vec<Expr<'a>>,
// args: Vec<Expr<'a>>,
// ) -> Result<Option<Expr<'a>>> {
// todo!()
// }
}
pub(crate) struct UnresolvedOp(pub String);
@ -169,20 +171,3 @@ impl AggOp for UnresolvedOp {
&self.0
}
}
pub(crate) struct OpNegate;
impl Op for OpNegate {
fn name(&self) -> &str {
"!"
}
fn eval_one_non_null<'a>(&self, arg: Value<'a>) -> Result<Value<'a>> {
match arg {
Value::Bool(b) => Ok((!b).into()),
v => Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![v.to_static()],
)),
}
}
}

@ -1,9 +1,10 @@
use crate::data::eval::EvalError;
use crate::data::eval::{EvalError, ExprEvalContext, RowEvalContext};
use crate::data::expr::Expr;
use crate::data::op::Op;
use crate::data::typing::Typing;
use crate::data::value::{StaticValue, Value};
use std::result;
use std::sync::Arc;
type Result<T> = result::Result<T, EvalError>;
@ -41,37 +42,6 @@ impl Op for OpNotNull {
}
}
pub(crate) struct OpCoalesce;
impl Op for OpCoalesce {
fn arity(&self) -> Option<usize> {
None
}
fn name(&self) -> &str {
"~~"
}
fn non_null_args(&self) -> bool {
false
}
fn eval<'a>(&self, _has_null: bool, args: Vec<Value<'a>>) -> Result<Value<'a>> {
for arg in args {
if arg != Value::Null {
return Ok(arg);
}
}
Ok(Value::Null)
}
fn eval_two<'a>(&self, left: Value<'a>, right: Value<'a>) -> Result<Value<'a>> {
match (left, right) {
(Value::Null, v) => Ok(v),
(l, _r) => Ok(l),
}
}
fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> crate::data::op::Result<Option<Expr<'a>>> {
todo!()
}
}
pub(crate) struct OpOr;
impl Op for OpOr {
@ -94,7 +64,7 @@ impl Op for OpOr {
return Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![v.to_static()],
))
));
}
}
}
@ -104,21 +74,61 @@ impl Op for OpOr {
Ok(Value::Bool(false))
}
}
fn eval_two<'a>(&self, left: Value<'a>, right: Value<'a>) -> Result<Value<'a>> {
match (left, right) {
(Value::Null, Value::Bool(true)) => Ok(true.into()),
(Value::Null, Value::Bool(false)) => Ok(Value::Null),
(Value::Bool(true), Value::Null) => Ok(true.into()),
(Value::Bool(false), Value::Null) => Ok(Value::Null),
(Value::Bool(l), Value::Bool(r)) => Ok((l || r).into()),
(l, r) => Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![l.to_static(), r.to_static()],
)),
}
pub(crate) fn partial_eval_or<'a, T: ExprEvalContext + 'a>(ctx: &'a T, args: Vec<Expr<'a>>) -> Result<Expr<'a>> {
let mut collected = vec![];
let mut has_null = false;
for arg in args {
match arg.partial_eval(ctx)? {
Expr::Const(Value::Null) => has_null = true,
Expr::Const(Value::Bool(b)) => {
if b {
return Ok(Expr::Const(Value::Bool(true)));
}
}
Expr::Const(v) => {
return Err(EvalError::OpTypeMismatch(
OpOr.name().to_string(),
vec![v.to_static()],
));
}
Expr::Apply(op, mut args) if op.name() == OpOr.name() => {
if args.last() == Some(&Expr::Const(Value::Null)) {
has_null = true;
args.pop();
}
collected.extend(args);
}
expr => collected.push(expr)
}
}
fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> crate::data::op::Result<Option<Expr<'a>>> {
todo!()
if has_null {
collected.push(Expr::Const(Value::Null));
}
Ok(match collected.len() {
0 => Expr::Const(Value::Bool(false)),
1 => collected.pop().unwrap(),
_ => Expr::Apply(Arc::new(OpOr), collected)
})
}
pub(crate) fn row_eval_or<'a, T: RowEvalContext + 'a>(ctx: &'a T, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Result<Value<'a>> {
let left = left.row_eval(ctx)?;
if left == Value::Bool(true) {
return Ok(Value::Bool(true));
}
let right = right.row_eval(ctx)?;
match (left, right) {
(Value::Null, Value::Bool(true)) => Ok(true.into()),
(Value::Null, Value::Bool(false)) => Ok(Value::Null),
(Value::Bool(false), Value::Null) => Ok(Value::Null),
(Value::Bool(false), Value::Bool(r)) => Ok(r.into()),
(l, r) => Err(EvalError::OpTypeMismatch(
OpOr.name().to_string(),
vec![l.to_static(), r.to_static()],
)),
}
}
@ -144,7 +154,7 @@ impl Op for OpAnd {
return Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![v.to_static()],
))
));
}
}
}
@ -154,20 +164,77 @@ impl Op for OpAnd {
Ok(Value::Bool(true))
}
}
fn eval_two<'a>(&self, left: Value<'a>, right: Value<'a>) -> Result<Value<'a>> {
match (left, right) {
(Value::Null, Value::Bool(false)) => Ok(false.into()),
(Value::Null, Value::Bool(true)) => Ok(Value::Null),
(Value::Bool(false), Value::Null) => Ok(false.into()),
(Value::Bool(true), Value::Null) => Ok(Value::Null),
(Value::Bool(l), Value::Bool(r)) => Ok((l && r).into()),
(l, r) => Err(EvalError::OpTypeMismatch(
}
pub(crate) fn partial_eval_and<'a, T: ExprEvalContext + 'a>(ctx: &'a T, args: Vec<Expr<'a>>) -> Result<Expr<'a>> {
let mut collected = vec![];
let mut has_null = false;
for arg in args {
match arg.partial_eval(ctx)? {
Expr::Const(Value::Null) => has_null = true,
Expr::Const(Value::Bool(b)) => {
if !b {
return Ok(Expr::Const(Value::Bool(false)));
}
}
Expr::Const(v) => {
return Err(EvalError::OpTypeMismatch(
OpAnd.name().to_string(),
vec![v.to_static()],
));
}
Expr::Apply(op, mut args) if op.name() == OpAnd.name() => {
if args.last() == Some(&Expr::Const(Value::Null)) {
has_null = true;
args.pop();
}
collected.extend(args);
}
expr => collected.push(expr)
}
}
if has_null {
collected.push(Expr::Const(Value::Null));
}
Ok(match collected.len() {
0 => Expr::Const(Value::Bool(true)),
1 => collected.pop().unwrap(),
_ => Expr::Apply(Arc::new(OpAnd), collected)
})
}
pub(crate) fn row_eval_and<'a, T: RowEvalContext + 'a>(ctx: &'a T, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Result<Value<'a>> {
let left = left.row_eval(ctx)?;
if left == Value::Bool(false) {
return Ok(Value::Bool(false));
}
let right = right.row_eval(ctx)?;
match (left, right) {
(Value::Null, Value::Bool(false)) => Ok(false.into()),
(Value::Null, Value::Bool(true)) => Ok(Value::Null),
(Value::Bool(true), Value::Null) => Ok(Value::Null),
(Value::Bool(true), Value::Bool(r)) => Ok(r.into()),
(l, r) => Err(EvalError::OpTypeMismatch(
OpAnd.name().to_string(),
vec![l.to_static(), r.to_static()],
)),
}
}
pub(crate) struct OpNot;
impl Op for OpNot {
fn name(&self) -> &str {
"!"
}
fn eval_one_non_null<'a>(&self, arg: Value<'a>) -> Result<Value<'a>> {
match arg {
Value::Bool(b) => Ok((!b).into()),
v => Err(EvalError::OpTypeMismatch(
self.name().to_string(),
vec![l.to_static(), r.to_static()],
vec![v.to_static()],
)),
}
}
fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> crate::data::op::Result<Option<Expr<'a>>> {
todo!()
}
}

@ -1,4 +1,4 @@
use crate::data::eval::EvalError;
use crate::data::eval::{EvalError, ExprEvalContext};
use crate::data::expr::Expr;
use crate::data::op::Op;
use crate::data::typing::Typing;
@ -36,9 +36,6 @@ impl Op for OpConcat {
}
Ok(coll.into())
}
fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> crate::data::op::Result<Option<Expr<'a>>> {
todo!()
}
}
pub(crate) struct OpMerge;
@ -53,7 +50,7 @@ impl Op for OpMerge {
fn non_null_args(&self) -> bool {
false
}
fn eval<'a>(&self, has_null: bool, args: Vec<Value<'a>>) -> Result<Value<'a>> {
fn eval<'a>(&self, _has_null: bool, args: Vec<Value<'a>>) -> Result<Value<'a>> {
let mut coll = BTreeMap::new();
for v in args.into_iter() {
match v {
@ -69,7 +66,4 @@ impl Op for OpMerge {
}
Ok(coll.into())
}
fn partial_eval<'a>(&self, args: Vec<Expr<'a>>) -> crate::data::op::Result<Option<Expr<'a>>> {
todo!()
}
}
}

@ -0,0 +1,92 @@
use std::result;
use std::sync::Arc;
use crate::data::eval::{EvalError, ExprEvalContext, RowEvalContext};
use crate::data::expr::Expr;
use crate::data::op::Op;
use crate::data::value::Value;
type Result<T> = result::Result<T, EvalError>;
pub(crate) struct OpCond;
pub(crate) struct OpCoalesce;
impl Op for OpCoalesce {
fn arity(&self) -> Option<usize> {
None
}
fn name(&self) -> &str {
"~~"
}
fn non_null_args(&self) -> bool {
false
}
fn eval<'a>(&self, _has_null: bool, args: Vec<Value<'a>>) -> Result<Value<'a>> {
for arg in args {
if arg != Value::Null {
return Ok(arg);
}
}
Ok(Value::Null)
}
}
pub(crate) fn row_eval_coalesce<'a, T: RowEvalContext + 'a>(ctx: &'a T, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Result<Value<'a>> {
let left = left.row_eval(ctx)?;
if left != Value::Null {
return Ok(left);
}
right.row_eval(ctx)
}
const IF_NAME: &str = "if";
pub(crate) fn partial_eval_coalesce<'a, T: ExprEvalContext + 'a>(ctx: &'a T, args: Vec<Expr<'a>>) -> Result<Expr<'a>> {
let mut collected = vec![];
for arg in args {
match arg.partial_eval(ctx)? {
Expr::Const(Value::Null) => {}
Expr::Apply(op, args) if op.name() == OpCoalesce.name() => {
collected.extend(args);
}
expr => collected.push(expr)
}
}
Ok(match collected.len() {
0 => Expr::Const(Value::Null),
1 => collected.pop().unwrap(),
_ => Expr::Apply(Arc::new(OpCoalesce), collected)
})
}
pub(crate) fn row_eval_if_expr<'a, T: RowEvalContext + 'a>(ctx: &'a T, cond: &'a Expr<'a>, if_part: &'a Expr<'a>, else_part: &'a Expr<'a>) -> Result<Value<'a>> {
let cond = cond.row_eval(ctx)?;
match cond {
Value::Bool(b) => {
Ok(if b {
if_part.row_eval(ctx)?
} else {
else_part.row_eval(ctx)?
})
}
Value::Null => Ok(Value::Null),
v => Err(EvalError::OpTypeMismatch(IF_NAME.to_string(), vec![v.to_static()]))
}
}
pub(crate) fn partial_eval_if_expr<'a, T: ExprEvalContext + 'a>(ctx: &'a T, cond: Expr<'a>, if_part: Expr<'a>, else_part: Expr<'a>) -> Result<Expr<'a>> {
let cond = cond.partial_eval(ctx)?;
match cond {
Expr::Const(Value::Null) => Ok(Expr::Const(Value::Null)),
Expr::Const(Value::Bool(b)) => {
Ok(if b {
if_part.partial_eval(ctx)?
} else {
else_part.partial_eval(ctx)?
})
}
cond => {
Ok(Expr::IfExpr((cond, if_part.partial_eval(ctx)?, else_part.partial_eval(ctx)?).into()))
}
}
}

@ -103,7 +103,7 @@ op_ge = { ">=" }
op_le = { "<=" }
op_pow = { "^" }
unary = { (unary_op ~ unary) | term }
unary = { cond_expr | switch_expr | if_expr | (unary_op ~ unary) | term }
unary_op = _{ minus | negate }
minus = { "-" }
negate = { "!" }
@ -117,6 +117,16 @@ kw_arg = {ident ~ "=" ~ expr}
pos_arg = { expr }
grouping = { "(" ~ expr ~ ")" }
if_expr = { if_clause ~ ("else" ~ if_clause)* ~ else_clause? }
if_clause = { "if" ~ expr ~ "{" ~ expr ~ "}"}
else_clause = { "else" ~ "{" ~ expr ~ "}" }
switch_expr = { "switch" ~ expr ~ "{" ~ switch_pattern ~ ("," ~ switch_pattern)* ~ ("," ~ default_pattern)? ~ ","? ~ "}" }
switch_pattern = { expr ~ "=>" ~ expr }
default_pattern = { ".." ~ "=>" ~ expr }
cond_expr = { "cond" ~ "{" ~ switch_pattern ~ ("," ~ switch_pattern)* ~ ","? ~ "}" }
list = { "[" ~ (list_el ~ ",")* ~ list_el? ~ "]" }
list_el = _{spreading | expr}
spreading = {"..." ~ term}

Loading…
Cancel
Save