sunflower/src/expression.rs

154 lines
4.2 KiB
Rust

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<Expression>),
Binary(Box<Expression>, BinaryOperation, Box<Expression>),
}
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::<Vec<_>>()).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<Value> 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)
);
}
}