use std::ops::Neg;
use crate::{Error, Interpreter, Result, lexer::OperatorType};
#[derive(Debug, PartialEq)]
pub enum Expression {
Number(f64),
Variable(String),
Unary {
op: OperatorType,
expr: Box<Expression>,
},
Binary {
left: Box<Expression>,
op: OperatorType,
right: Box<Expression>,
},
Call {
callee: String,
args: Vec<Expression>,
},
}
impl Expression {
fn apply_unary_op(op: &OperatorType, n: f64) -> Result<f64> {
match op {
OperatorType::PLUS => Ok(n.abs()),
OperatorType::MINUS => Ok(n.neg()),
_ => Err(Error::invalid_unary_op(op)),
}
}
fn apply_binary_op(op: &OperatorType, lhs: f64, rhs: f64) -> f64 {
match op {
OperatorType::PLUS => lhs + rhs,
OperatorType::MINUS => lhs - rhs,
OperatorType::MULTIPLY => lhs * rhs,
OperatorType::DIVIDE => lhs / rhs,
OperatorType::MODULO => lhs % rhs,
OperatorType::POWER => lhs.powf(rhs),
}
}
pub(crate) fn eval(&self, ctx: &Interpreter) -> Result<f64> {
match self {
Self::Number(n) => Ok(*n),
Self::Variable(var) => {
let val = ctx.get_var(var);
if let Some(val) = val {
Ok(val)
} else {
panic!("Undefined variable '{}'", var);
}
}
Self::Unary { op, expr } => {
let res = expr.eval(ctx)?;
Ok(Self::apply_unary_op(op, res)?)
}
Self::Binary { left, op, right } => {
let left = left.eval(ctx)?;
let right = right.eval(ctx)?;
Ok(Self::apply_binary_op(op, left, right))
}
Self::Call { callee, args } => {
let mut evaluated_args = Vec::with_capacity(args.len());
for arg in args {
let value = arg.eval(ctx)?;
evaluated_args.push(value);
}
ctx.call_fn(callee.as_str(), evaluated_args.as_slice())
.ok_or(Error::undefined(callee.to_owned()))?
}
}
}
}