mod eval;
pub mod index;
pub(crate) mod keywords;
pub use index::Index;
use itertools::Itertools;
use crate::value::Value;
use std::{collections::HashMap, fmt::Display};
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
Value(Value),
Reference(String),
Function(String, Box<Expr>),
Index(Box<Expr>, Index),
If(Box<Expr>, Box<Expr>, Box<Expr>),
Map(HashMap<String, Expr>),
Vec(Vec<Expr>),
Not(Box<Expr>),
Neg(Box<Expr>),
IsSome(Box<Expr>),
IsNone(Box<Expr>),
Int(Box<Expr>),
Float(Box<Expr>),
Dec(Box<Expr>),
DateTime(Box<Expr>),
Duration(Box<Expr>),
Mult(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Equals(Box<Expr>, Box<Expr>),
NotEquals(Box<Expr>, Box<Expr>),
GreaterThan(Box<Expr>, Box<Expr>),
GreaterThanEquals(Box<Expr>, Box<Expr>),
LessThan(Box<Expr>, Box<Expr>),
LessThanEquals(Box<Expr>, Box<Expr>),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
BitAnd(Box<Expr>, Box<Expr>),
BitOr(Box<Expr>, Box<Expr>),
BitXor(Box<Expr>, Box<Expr>),
Contains(Box<Expr>, Box<Expr>),
ToUpper(Box<Expr>),
ToLower(Box<Expr>),
Trim(Box<Expr>),
Floor(Box<Expr>),
Round(Box<Expr>),
Fract(Box<Expr>),
Year(Box<Expr>),
Month(Box<Expr>),
Week(Box<Expr>),
Day(Box<Expr>),
Hour(Box<Expr>),
Minute(Box<Expr>),
Second(Box<Expr>),
}
impl Expr {
pub fn value(value: impl Into<Value>) -> Self {
Expr::Value(value.into())
}
pub fn none() -> Self {
Expr::Value(Value::None)
}
pub fn func(name: impl Into<String>, param: Expr) -> Self {
Expr::Function(name.into(), Box::new(param))
}
pub fn reff(name: impl ToString) -> Self {
Expr::Reference(name.to_string())
}
pub fn index(value: Expr, index: Index) -> Self {
Expr::Index(Box::new(value), index)
}
pub fn iif(swith: impl Into<Expr>, yes: Expr, no: Expr) -> Self {
Expr::If(Box::new(swith.into()), Box::new(yes), Box::new(no))
}
#[allow(clippy::should_implement_trait)]
pub fn not(expr: Expr) -> Self {
Expr::Not(Box::new(expr))
}
#[allow(clippy::should_implement_trait)]
pub fn neg(expr: Expr) -> Self {
Expr::Neg(Box::new(expr))
}
pub fn is_some(expr: Expr) -> Self {
Expr::IsSome(Box::new(expr))
}
pub fn is_none(expr: Expr) -> Self {
Expr::IsNone(Box::new(expr))
}
pub fn int(expr: Expr) -> Self {
Expr::Int(Box::new(expr))
}
pub fn float(expr: Expr) -> Self {
Expr::Float(Box::new(expr))
}
pub fn dec(expr: Expr) -> Self {
Expr::Dec(Box::new(expr))
}
pub fn date_time(expr: Expr) -> Self {
Expr::DateTime(Box::new(expr))
}
pub fn duration(expr: Expr) -> Self {
Expr::Duration(Box::new(expr))
}
pub fn mult(left: Expr, right: Expr) -> Self {
Expr::Mult(Box::new(left), Box::new(right))
}
#[allow(clippy::should_implement_trait)]
pub fn div(left: Expr, right: Expr) -> Self {
Expr::Div(Box::new(left), Box::new(right))
}
#[allow(clippy::should_implement_trait)]
pub fn add(left: Expr, right: Expr) -> Self {
Expr::Add(Box::new(left), Box::new(right))
}
#[allow(clippy::should_implement_trait)]
pub fn sub(left: Expr, right: Expr) -> Self {
Expr::Sub(Box::new(left), Box::new(right))
}
pub fn eq(left: Expr, right: Expr) -> Self {
Expr::Equals(Box::new(left), Box::new(right))
}
pub fn neq(left: Expr, right: Expr) -> Self {
Expr::NotEquals(Box::new(left), Box::new(right))
}
pub fn gt(left: Expr, right: Expr) -> Self {
Expr::GreaterThan(Box::new(left), Box::new(right))
}
pub fn gte(left: Expr, right: Expr) -> Self {
Expr::GreaterThanEquals(Box::new(left), Box::new(right))
}
pub fn lt(left: Expr, right: Expr) -> Self {
Expr::LessThan(Box::new(left), Box::new(right))
}
pub fn lte(left: Expr, right: Expr) -> Self {
Expr::LessThanEquals(Box::new(left), Box::new(right))
}
pub fn and(left: Expr, right: Expr) -> Self {
Expr::And(Box::new(left), Box::new(right))
}
pub fn or(left: Expr, right: Expr) -> Self {
Expr::Or(Box::new(left), Box::new(right))
}
pub fn bitwise_and(left: Expr, right: Expr) -> Self {
Expr::BitAnd(Box::new(left), Box::new(right))
}
pub fn bitwise_or(left: Expr, right: Expr) -> Self {
Expr::BitOr(Box::new(left), Box::new(right))
}
pub fn bitwise_xor(left: Expr, right: Expr) -> Self {
Expr::BitXor(Box::new(left), Box::new(right))
}
pub fn contains(list: Expr, key: Expr) -> Self {
Expr::Contains(Box::new(list), Box::new(key))
}
pub fn to_upper(param: Expr) -> Self {
Expr::ToUpper(Box::new(param))
}
pub fn to_lower(param: Expr) -> Self {
Expr::ToLower(Box::new(param))
}
pub fn trim(param: Expr) -> Self {
Expr::Trim(Box::new(param))
}
pub fn round(param: Expr) -> Self {
Expr::Round(Box::new(param))
}
pub fn floor(param: Expr) -> Self {
Expr::Floor(Box::new(param))
}
pub fn fract(param: Expr) -> Self {
Expr::Fract(Box::new(param))
}
pub fn year(param: Expr) -> Self {
Expr::Year(Box::new(param))
}
pub fn month(param: Expr) -> Self {
Expr::Month(Box::new(param))
}
pub fn week(param: Expr) -> Self {
Expr::Week(Box::new(param))
}
pub fn day(param: Expr) -> Self {
Expr::Day(Box::new(param))
}
pub fn hour(param: Expr) -> Self {
Expr::Hour(Box::new(param))
}
pub fn minute(param: Expr) -> Self {
Expr::Minute(Box::new(param))
}
pub fn second(param: Expr) -> Self {
Expr::Second(Box::new(param))
}
}
impl From<Value> for Expr {
fn from(value: Value) -> Self {
Expr::Value(value)
}
}
impl Display for Expr {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Value(value) => write!(formatter, "{value}"),
Expr::Reference(ident) => write!(formatter, "{ident}"),
Expr::Function(ident, param) => write!(formatter, "{ident}({param})"),
Expr::Index(left, right) => write!(formatter, "({left}.{right})"),
Expr::If(check, true_case, false_case) => {
write!(formatter, "(if {check} then {true_case} else {false_case})")
}
Expr::Vec(values) => {
write!(
formatter,
"[{}]",
values.iter().map(ToString::to_string).join(", ")
)
}
Expr::Map(map) => write!(
formatter,
"{{{}}}",
map.iter()
.map(|(key, value)| format!("{key}: {value}"))
.join(", ")
),
Expr::Not(inner) => write!(formatter, "!({inner})"),
Expr::Neg(inner) => write!(formatter, "-({inner})"),
Expr::IsSome(inner) => write!(formatter, "some({inner})"),
Expr::IsNone(inner) => write!(formatter, "none({inner})"),
Expr::Int(inner) => write!(formatter, "int({inner})"),
Expr::Float(inner) => write!(formatter, "float({inner})"),
Expr::Dec(inner) => write!(formatter, "dec({inner})"),
Expr::DateTime(inner) => write!(formatter, "datetime({inner})"),
Expr::Duration(inner) => write!(formatter, "duration({inner})"),
Expr::Mult(left, right) => write!(formatter, "({left} * {right})"),
Expr::Div(left, right) => write!(formatter, "({left} / {right})"),
Expr::Add(left, right) => write!(formatter, "({left} + {right})"),
Expr::Sub(left, right) => write!(formatter, "({left} - {right})"),
Expr::Equals(left, right) => write!(formatter, "({left} == {right})"),
Expr::NotEquals(left, right) => write!(formatter, "({left} != {right})"),
Expr::GreaterThan(left, right) => write!(formatter, "({left} > {right})"),
Expr::GreaterThanEquals(left, right) => write!(formatter, "({left} >= {right})"),
Expr::LessThan(left, right) => write!(formatter, "({left} < {right})"),
Expr::LessThanEquals(left, right) => write!(formatter, "({left} <= {right})"),
Expr::And(left, right) => write!(formatter, "({left} and {right})"),
Expr::Or(left, right) => write!(formatter, "({left} or {right})"),
Expr::BitAnd(left, right) => write!(formatter, "{left} & {right}"),
Expr::BitOr(left, right) => write!(formatter, "{left} | {right}"),
Expr::BitXor(left, right) => write!(formatter, "{left} ^ {right}"),
Expr::Contains(left, right) => write!(formatter, "({left} contains {right})"),
Expr::ToUpper(param) => write!(formatter, "uppercase({param})"),
Expr::ToLower(param) => write!(formatter, "lowercase({param})"),
Expr::Trim(param) => write!(formatter, "trim({param})"),
Expr::Floor(param) => write!(formatter, "floor({param})"),
Expr::Round(param) => write!(formatter, "round({param})"),
Expr::Fract(param) => write!(formatter, "fract({param})"),
Expr::Year(param) => write!(formatter, "year({param})"),
Expr::Month(param) => write!(formatter, "month({param})"),
Expr::Week(param) => write!(formatter, "week({param})"),
Expr::Day(param) => write!(formatter, "day({param})"),
Expr::Hour(param) => write!(formatter, "hour({param})"),
Expr::Minute(param) => write!(formatter, "minute({param})"),
Expr::Second(param) => write!(formatter, "second({param})"),
}
}
}
#[cfg(test)]
mod when_displaying_expr {
use super::*;
#[test]
fn should_display_value() {
assert_eq!(Expr::value(5).to_string(), "i5");
}
#[test]
fn should_display_mult_div_add_sub() {
assert_eq!(
Expr::mult(
Expr::add(Expr::value(3), Expr::value(4)),
Expr::div(Expr::value(5), Expr::sub(Expr::value(6), Expr::value(7)))
)
.to_string(),
"((i3 + i4) * (i5 / (i6 - i7)))"
);
}
#[test]
fn should_display_eq_neq_gte_gt_lte_lt() {
assert_eq!(
Expr::eq(
Expr::neq(Expr::value(3), Expr::lte(Expr::value(4), Expr::value(9))),
Expr::gte(
Expr::lt(Expr::value(5), Expr::value(8)),
Expr::gt(Expr::value(6), Expr::value(7))
)
)
.to_string(),
"((i3 != (i4 <= i9)) == ((i5 < i8) >= (i6 > i7)))"
);
}
}