154 lines
4.2 KiB
Rust
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)
|
|
);
|
|
}
|
|
}
|