use std::fmt::Display; use crate::error::EvalError; use crate::parser::expr::{BinaryOp, Expr, Literal, UnaryOp}; pub type EvalResult = Result; /*====================================================================================================================*/ pub fn evaluate(expr: Expr) -> EvalResult { expr.eval() } /*====================================================================================================================*/ #[derive(Debug, PartialEq)] pub enum Primitive { String(String), Number(f64), Bool(bool), Nil, } impl Primitive { fn is_truthy(&self) -> bool { match self { Primitive::Bool(false) | Primitive::Nil => false, _ => true, } } } impl Display for Primitive { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Primitive::String(s) => write!(f, "\"{s}\""), Primitive::Number(num) => write!(f, "{num}"), Primitive::Bool(b) => write!(f, "{b}"), Primitive::Nil => write!(f, "nil"), } } } /*====================================================================================================================*/ trait Eval { fn eval(self) -> EvalResult; } impl Eval for Literal { fn eval(self) -> EvalResult { match self { Literal::String(s) => Ok(Primitive::String(s)), Literal::Number(num) => Ok(Primitive::Number(num)), Literal::Bool(b) => Ok(Primitive::Bool(b)), Literal::Nil => Ok(Primitive::Nil), } } } impl Eval for Expr { fn eval(self) -> EvalResult { match self { Expr::Literal(literal) => literal.eval(), Expr::Unary(op, expr) => { let arg = expr.eval()?; match (op, arg) { (UnaryOp::Negate, Primitive::Number(num)) => Ok(Primitive::Number(-num)), (UnaryOp::Not, Primitive::Bool(b)) => Ok(Primitive::Bool(!b)), (UnaryOp::Not, primitive) => Ok(Primitive::Bool(!primitive.is_truthy())), (op, arg) => Err(EvalError::UnaryOpInvalidArgument { op, arg }), } } Expr::Binary(left_expr, op, right_expr) => { use Primitive::{Bool, Number, String}; let left = left_expr.eval()?; let right = right_expr.eval()?; match (left, op, right) { (Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)), (Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)), (Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)), (Number(left), BinaryOp::Divide, Number(right)) => { if right == 0.0 { return Err(EvalError::DivisionByZero); } Ok(Number(left / right)) } (String(left), BinaryOp::Add, String(right)) => Ok(String(left + &right)), (left, BinaryOp::Equal, right) => Ok(Bool(left == right)), (left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)), (Number(left), BinaryOp::Less, Number(right)) => Ok(Bool(left < right)), (Number(left), BinaryOp::LessEqual, Number(right)) => Ok(Bool(left <= right)), (Number(left), BinaryOp::Greater, Number(right)) => Ok(Bool(left > right)), (Number(left), BinaryOp::GreaterEqual, Number(right)) => Ok(Bool(left >= right)), (String(left), BinaryOp::Less, String(right)) => Ok(Bool(left < right)), (String(left), BinaryOp::LessEqual, String(right)) => Ok(Bool(left <= right)), (String(left), BinaryOp::Greater, String(right)) => Ok(Bool(left > right)), (String(left), BinaryOp::GreaterEqual, String(right)) => Ok(Bool(left >= right)), (left, op, right) => Err(EvalError::BinaryOpInvalidArguments { left, op, right }), } } Expr::Grouping(expr) => expr.eval(), } } }