183 lines
5.1 KiB
Rust
183 lines
5.1 KiB
Rust
use std::str::FromStr;
|
|
|
|
use crate::{env::Env, function::FunctionMap, value::Value};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum BinaryOperation {
|
|
Add,
|
|
Sub,
|
|
Mul,
|
|
Div,
|
|
Mod,
|
|
LessThan,
|
|
GreaterThan,
|
|
LessThanOrEqual,
|
|
GreaterThanOrEqual,
|
|
Equal,
|
|
NotEqual,
|
|
}
|
|
|
|
impl FromStr for BinaryOperation {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"+" => Ok(BinaryOperation::Add),
|
|
"-" => Ok(BinaryOperation::Sub),
|
|
"*" => Ok(BinaryOperation::Mul),
|
|
"/" => Ok(BinaryOperation::Div),
|
|
"%" => Ok(BinaryOperation::Mod),
|
|
"<" => Ok(BinaryOperation::LessThan),
|
|
">" => Ok(BinaryOperation::GreaterThan),
|
|
"<=" => Ok(BinaryOperation::LessThanOrEqual),
|
|
">=" => Ok(BinaryOperation::GreaterThanOrEqual),
|
|
"==" => Ok(BinaryOperation::Equal),
|
|
"!=" => Ok(BinaryOperation::NotEqual),
|
|
_ => Err("Invalid binary operation"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[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::Mod => 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)
|
|
);
|
|
}
|
|
}
|