use std::fmt::Display; use crate::error::RuntimeError; use crate::parser::{BinaryOp, Expr, Literal, Stmt, UnaryOp}; use super::environment::Environment; pub type EvalResult = Result; /*====================================================================================================================*/ pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> { statement.eval(env) } /*====================================================================================================================*/ #[derive(Debug, Clone, PartialEq)] pub enum Value { String(String), Number(f64), Bool(bool), Nil, } impl Value { fn is_truthy(&self) -> bool { match self { Value::Bool(false) | Value::Nil => false, _ => true, } } } impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Value::String(s) => write!(f, "\"{s}\""), Value::Number(num) => write!(f, "{num}"), Value::Bool(b) => write!(f, "{b}"), Value::Nil => write!(f, "nil"), } } } /*====================================================================================================================*/ /* trait Eval { fn eval(self, env: &mut Environment) -> EvalResult; } */ impl Literal { fn eval(self, _env: &mut Environment) -> EvalResult { match self { Literal::String(s) => Ok(Value::String(s)), Literal::Number(num) => Ok(Value::Number(num)), Literal::Bool(b) => Ok(Value::Bool(b)), Literal::Nil => Ok(Value::Nil), } } } impl Expr { fn eval(self, env: &mut Environment) -> EvalResult { match self { Expr::Literal { literal } => literal.eval(env), Expr::Unary { op, expr } => { let arg = expr.eval(env)?; match (op, arg) { (UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)), (UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)), (UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())), (op, arg) => Err(RuntimeError::UnaryOpInvalidArgument { op, arg }), } } Expr::Binary { left, op, right } => { use Value::{Bool, Number, String}; let left = left.eval(env)?; let right = right.eval(env)?; 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(RuntimeError::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(RuntimeError::BinaryOpInvalidArguments { left, op, right }), } } Expr::Grouping { expr } => expr.eval(env), Expr::Variable { name } => env.get(&name).cloned(), Expr::Assignment { name, value } => { let value = value.eval(env)?; env.assign(&name, value.clone())?; Ok(value) } } } } impl Stmt { fn eval(self, env: &mut Environment) -> EvalResult<()> { match self { Stmt::ExprStmt { expr } => { // expr.eval(env)?; // Ok(Value::Nil) expr.eval(env)?; Ok(()) } Stmt::Print { expr } => { match expr.eval(env)? { // special case: when printing a string, drop the surrounding "" Value::String(s) => println!("{s}"), val => println!("{val}"), } Ok(()) } Stmt::VarDecl { name, initializer } => { let initializer = initializer.eval(env)?; env.define(name, initializer); Ok(()) } Stmt::Block { statements } => { env.enter_scope(); for statement in statements { // on error the current frame gets destroyed anyways, so no need to exit scope statement.eval(env)?; } env.exit_scope(); Ok(()) } } } }