use crate::{env::Env, function::FunctionMap, value::Value}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BinaryOperation { Add, Sub, Mul, Div, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, Equal, NotEqual, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expression { Constant(Value), Variable(String), Call(String, Vec), Binary(Box, BinaryOperation, Box), } impl Expression { pub fn eval(&self, e: &Env, f: &FunctionMap) -> Value { match self { Expression::Constant(v) => v.clone(), Expression::Variable(name) => e.get(name).unwrap_or_default(), Expression::Call(name, args) => { f.call(name, e, &args.iter().map(|ex| ex.eval(e, f)).collect::>()).unwrap() }, Expression::Binary(lhs, op, rhs) => { let lhs = lhs.eval(e, f); let rhs = rhs.eval(e, f); match op { BinaryOperation::Add => lhs + rhs, BinaryOperation::Sub => lhs - rhs, BinaryOperation::Mul => lhs * rhs, BinaryOperation::Div => lhs / rhs, BinaryOperation::LessThan => (lhs < rhs).into(), BinaryOperation::GreaterThan => (lhs > rhs).into(), BinaryOperation::LessThanOrEqual => (lhs <= rhs).into(), BinaryOperation::GreaterThanOrEqual => (lhs >= rhs).into(), BinaryOperation::Equal => (lhs == rhs).into(), BinaryOperation::NotEqual => (lhs != rhs).into(), } } } } } impl From for Expression { fn from(v: Value) -> Self { Expression::Constant(v) } } macro_rules! from_impl { ($($type:ty),+) => { $( impl From<$type> for Expression { fn from(v: $type) -> Self { Expression::Constant(Value::from(v)) } } )+ }; } from_impl!(i32, f32, String, bool, char); impl<'s> From<&'s str> for Expression { fn from(s: &'s str) -> Self { Expression::Constant(Value::String(s.to_string())) } } #[cfg(test)] mod tests { use super::*; use crate::env::Env; use crate::value::Value; #[test] fn test_eval() { let f = FunctionMap::new(); let e = Env::new(); e.set("test", 10); assert_eq!( Expression::Variable("test".to_string()).eval(&e, &f), Value::Int(10) ); assert_eq!( Expression::Constant(Value::Int(10)).eval(&e, &f), Value::Int(10) ); assert_eq!( Expression::Binary( Box::new(Expression::Variable("test".to_string())), BinaryOperation::Add, Box::new(Expression::Constant(Value::Int(10))) ) .eval(&e, &f), Value::Int(20) ); } #[test] fn test_eval_unset() { let f = FunctionMap::new(); let e = Env::new(); assert_eq!( Expression::Variable("test".to_string()).eval(&e, &f), Value::Nothing ); assert_eq!( Expression::Constant(Value::Error).eval(&e, &f), Value::Error ); assert_eq!( Expression::Binary( Box::new(Expression::Variable("test".to_string())), BinaryOperation::Add, Box::new(Expression::Constant(Value::Nothing)) ) .eval(&e, &f), Value::Error ); } #[test] fn test_eval_int() { let f = FunctionMap::new(); let e = Env::new(); e.set("test", 10); assert_eq!( Expression::Constant(Value::Int(10)).eval(&e, &f), Value::Int(10) ); assert_eq!( Expression::Binary( Box::new(Expression::Constant(Value::Int(10))), BinaryOperation::Add, Box::new(Expression::Variable("test".to_string())) ) .eval(&e, &f), Value::Int(20) ); } }