use crate::convert::ToValue;
use crate::ops::Operation;
use crate::runtime::eval_value;
use crate::value::Value;
use crate::Result;
#[derive(Clone)]
pub enum Expr {
Col(String),
Lit(Value),
Op(Operation, Vec<Expr>),
Call(String, Vec<Expr>),
}
pub trait IntoExpr {
fn into_expr(self) -> Expr;
}
impl IntoExpr for Expr {
fn into_expr(self) -> Expr {
self
}
}
impl IntoExpr for &Expr {
fn into_expr(self) -> Expr {
self.clone()
}
}
impl IntoExpr for Value {
fn into_expr(self) -> Expr {
Expr::Lit(self)
}
}
macro_rules! into_expr_lit {
($($t:ty),*) => {$(
impl IntoExpr for $t {
fn into_expr(self) -> Expr { Expr::Lit(self.to_value()) }
}
)*};
}
into_expr_lit!(bool, u8, i16, i32, i64, f32, f64);
impl IntoExpr for &str {
fn into_expr(self) -> Expr {
Expr::Lit(Value::string(self))
}
}
impl IntoExpr for String {
fn into_expr(self) -> Expr {
Expr::Lit(Value::string(&self))
}
}
impl<S: AsRef<str>> IntoExpr for crate::Str<S> {
fn into_expr(self) -> Expr {
Expr::Lit(self.to_value())
}
}
pub fn col(name: impl Into<String>) -> Expr {
Expr::Col(name.into())
}
pub fn lit(v: impl ToValue) -> Expr {
Expr::Lit(v.to_value())
}
impl Expr {
pub fn op(op: Operation, operands: Vec<Expr>) -> Expr {
Expr::Op(op, operands)
}
fn binary(self, op: Operation, rhs: impl IntoExpr) -> Expr {
Expr::Op(op, vec![self, rhs.into_expr()])
}
fn unary(self, op: Operation) -> Expr {
Expr::Op(op, vec![self])
}
pub fn eq(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::Equals, rhs)
}
pub fn ne(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::NotEquals, rhs)
}
pub fn lt(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::LessThan, rhs)
}
pub fn le(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::LessEqual, rhs)
}
pub fn gt(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::GreaterThan, rhs)
}
pub fn ge(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::GreaterEqual, rhs)
}
pub fn like(self, pattern: impl IntoExpr) -> Expr {
self.binary(Operation::Like, pattern)
}
pub fn is_in(self, set: impl IntoExpr) -> Expr {
self.binary(Operation::In, set)
}
pub fn within(self, range: impl IntoExpr) -> Expr {
self.binary(Operation::Within, range)
}
pub fn and(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::And, rhs)
}
pub fn or(self, rhs: impl IntoExpr) -> Expr {
self.binary(Operation::Or, rhs)
}
pub fn sum(self) -> Expr {
self.unary(Operation::Sum)
}
pub fn avg(self) -> Expr {
self.unary(Operation::Avg)
}
pub fn count(self) -> Expr {
self.unary(Operation::Count)
}
pub fn min(self) -> Expr {
self.unary(Operation::Min)
}
pub fn max(self) -> Expr {
self.unary(Operation::Max)
}
pub fn first(self) -> Expr {
self.unary(Operation::First)
}
pub fn last(self) -> Expr {
self.unary(Operation::Last)
}
pub fn median(self) -> Expr {
self.unary(Operation::Median)
}
pub fn deviation(self) -> Expr {
self.unary(Operation::Deviation)
}
pub fn std(self) -> Expr {
self.unary(Operation::Stddev)
}
pub fn distinct(self) -> Expr {
self.unary(Operation::Distinct)
}
pub fn ceil(self) -> Expr {
self.unary(Operation::Ceil)
}
pub fn floor(self) -> Expr {
self.unary(Operation::Floor)
}
pub fn round(self) -> Expr {
self.unary(Operation::Round)
}
pub fn filter(self, mask: impl IntoExpr) -> Expr {
self.binary(Operation::Filter, mask)
}
pub fn compile(&self) -> Value {
match self {
Expr::Col(name) => Value::name_ref(name),
Expr::Lit(v) => v.clone(),
Expr::Op(op, operands) => {
let mut items = Vec::with_capacity(operands.len() + 1);
items.push(Value::name_ref(op.as_str()));
for o in operands {
items.push(o.compile());
}
Value::list(&items)
}
Expr::Call(name, operands) => {
let mut items = Vec::with_capacity(operands.len() + 1);
items.push(Value::name_ref(name));
for o in operands {
items.push(o.compile());
}
Value::list(&items)
}
}
}
pub fn execute(&self) -> Result<Value> {
eval_value(&self.compile())
}
}
macro_rules! bin_op {
($trait:ident, $method:ident, $op:expr) => {
impl<R: IntoExpr> std::ops::$trait<R> for Expr {
type Output = Expr;
fn $method(self, rhs: R) -> Expr {
Expr::Op($op, vec![self, rhs.into_expr()])
}
}
};
}
bin_op!(Add, add, Operation::Add);
bin_op!(Sub, sub, Operation::Subtract);
bin_op!(Mul, mul, Operation::Multiply);
bin_op!(Div, div, Operation::Divide);
bin_op!(Rem, rem, Operation::Modulo);
bin_op!(BitAnd, bitand, Operation::And);
bin_op!(BitOr, bitor, Operation::Or);
impl std::ops::Neg for Expr {
type Output = Expr;
fn neg(self) -> Expr {
self.unary(Operation::Negate)
}
}
impl std::ops::Not for Expr {
type Output = Expr;
fn not(self) -> Expr {
self.unary(Operation::Not)
}
}
macro_rules! free_agg {
($name:ident, $op:expr) => {
pub fn $name(e: impl IntoExpr) -> Expr {
Expr::Op($op, vec![e.into_expr()])
}
};
}
free_agg!(sum, Operation::Sum);
free_agg!(avg, Operation::Avg);
free_agg!(count, Operation::Count);
free_agg!(min, Operation::Min);
free_agg!(max, Operation::Max);
free_agg!(first, Operation::First);
free_agg!(last, Operation::Last);
free_agg!(median, Operation::Median);
free_agg!(distinct, Operation::Distinct);