use crate::ast::*; use crate::ast::expr::{Literal, UnaryOp, BinaryOp, LogicalOp, AssignOp}; use crate::error::RuntimeError; use crate::interpreter::Signal; use super::{Value, Env, Function}; use std::collections::HashMap; use std::rc::Rc; use std::cell::RefCell; impl super::Interpreter { // ======================================================================== // evaluate — dispatcher // ======================================================================== pub fn evaluate(&mut self, expr: Expr) -> Result { match expr { Expr::Literal(lit) => self.eval_literal(lit), Expr::Variable(name) => self.eval_variable(name), Expr::Assign { name, op, value } => self.eval_assign(name, op, *value), Expr::Get { object, name } => self.eval_get(*object, name), Expr::Set { object, name, op, value } => self.eval_set(*object, name, op, *value), Expr::IndexGet { array, index } => self.eval_index_get(*array, *index), Expr::IndexSet { array, index, op, value } => self.eval_index_set(*array, *index, op, *value), Expr::ObjectLiteral { properties } => self.eval_object_literal(properties), Expr::ArrayLiteral { elements } => self.eval_array_literal(elements), Expr::Unary { op, right } => self.eval_unary(op, *right), Expr::Binary { left, op, right } => self.eval_binary(*left, op, *right), Expr::Logical { left, op, right } => self.eval_logical(*left, op, *right), Expr::Ternary { condition, then_branch, else_branch } => { self.eval_ternary(*condition, *then_branch, *else_branch) } Expr::Call { callee, arguments } => self.eval_call(*callee, arguments), Expr::Lambda { params, body } => self.eval_lambda(params, body), } } // ======================================================================== // eval_* methods // ======================================================================== fn eval_literal(&mut self, lit: Literal) -> Result { Ok(match lit { Literal::Number(n) => Value::Number(n), Literal::String(s) => Value::String(s), Literal::Bool(b) => Value::Bool(b), Literal::Nil => Value::Nil, }) } fn eval_variable(&mut self, name: String) -> Result { match self.env.borrow().get(&name) { Some(val) => Ok(val), None => Err(RuntimeError::RuntimeError { message: format!("Undefined variable '{}'", name), token: None, }), } } fn eval_assign(&mut self, name: String, op: AssignOp, value: Expr) -> Result { let rhs = self.evaluate(value)?; let val = match op { AssignOp::Equal => rhs, _ => { let current = self.env.borrow().get(&name).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Undefined variable '{}'", name), token: None, })?; self.apply_assign_op(current.clone(), rhs, op)? } }; match self.env.borrow_mut().assign(&name, val.clone()) { Ok(true) => {} Ok(false) => { return Err(RuntimeError::RuntimeError { message: format!("Undefined variable '{}'", name), token: None, }); } Err(msg) => { return Err(RuntimeError::RuntimeError { message: msg, token: None, }); } } Ok(val) } fn eval_get(&mut self, object: Expr, name: String) -> Result { let obj = self.evaluate(object)?; match obj { Value::Object(_) => { match obj.get(&name) { Some(val) => Ok(val), None => Err(RuntimeError::RuntimeError { message: format!("Undefined property '{}'", name), token: None, }), } } Value::Array(arr) => match name.as_str() { "length" => Ok(Value::Number(arr.borrow().len() as f64)), "push" => { let arr = Rc::clone(&arr); Ok(Value::NativeFunction(Rc::new(move |_runtime: &mut dyn super::Runtime, mut args: Vec| { let val = args.pop().unwrap_or(Value::Nil); arr.borrow_mut().push(val.clone()); Ok(val) }))) } "pop" => { let arr = Rc::clone(&arr); Ok(Value::NativeFunction(Rc::new(move |_runtime: &mut dyn super::Runtime, _args: Vec| { arr.borrow_mut() .pop() .ok_or_else(|| RuntimeError::RuntimeError { message: "pop() on empty array".into(), token: None, }) }))) } _ => Err(RuntimeError::RuntimeError { message: format!("Array has no property '{}'", name), token: None, }), }, Value::String(s) => match name.as_str() { "length" => Ok(Value::Number(s.chars().count() as f64)), "upper" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::upper(runtime, all_args) }))) } "lower" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::lower(runtime, all_args) }))) } "trim" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::trim(runtime, all_args) }))) } "substring" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::substring(runtime, all_args) }))) } "replace" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::replace(runtime, all_args) }))) } "contains" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::contains(runtime, all_args) }))) } "starts_with" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::starts_with(runtime, all_args) }))) } "ends_with" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::ends_with(runtime, all_args) }))) } "split" => { let s = s.clone(); Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec| { let mut all_args = vec![Value::String(s.clone())]; all_args.extend(args); super::builtins::string::split(runtime, all_args) }))) } _ => Err(RuntimeError::RuntimeError { message: format!("String has no property '{}'", name), token: None, }), }, _ => Err(RuntimeError::RuntimeError { message: "Only objects have properties".to_string(), token: None, }), } } fn eval_set(&mut self, object: Expr, name: String, op: AssignOp, value: Expr) -> Result { let obj = self.evaluate(object)?; let rhs = self.evaluate(value)?; match obj { Value::Object(_) => { let val = match op { AssignOp::Equal => rhs, _ => { let current = obj.get(&name).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Property '{}' does not exist", name), token: None, })?; self.apply_assign_op(current, rhs, op)? } }; obj.set(&name, val.clone())?; Ok(val) } _ => Err(RuntimeError::RuntimeError { message: "Only objects have properties".to_string(), token: None, }), } } fn eval_index_get(&mut self, array: Expr, index: Expr) -> Result { let target = self.evaluate(array)?; let idx_val = self.evaluate(index)?; // Object index access: obj["key"] if let Value::Object(_) = &target { let key = match &idx_val { Value::String(s) => s.clone(), _ => return Err(RuntimeError::RuntimeError { message: "Object index must be a string".into(), token: None, }), }; return target.get(&key).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Undefined property '{}'", key), token: None, }); } let i = self.as_array_index(&idx_val)?; match target { Value::Array(vec) => { let vec = vec.borrow(); vec.get(i).cloned().ok_or_else(|| RuntimeError::RuntimeError { message: format!("Index {} out of bounds (len {})", i, vec.len()), token: None, }) } Value::String(s) => { let chars: Vec = s.chars().collect(); chars.get(i).map(|&c| Value::String(c.to_string())).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Index {} out of bounds (len {})", i, chars.len()), token: None, }) } _ => Err(RuntimeError::RuntimeError { message: "Index access on non-array, non-string, non-object value".to_string(), token: None, }), } } fn eval_index_set(&mut self, array: Expr, index: Expr, op: AssignOp, value: Expr) -> Result { let target = self.evaluate(array)?; let idx_val = self.evaluate(index)?; let rhs = self.evaluate(value)?; // Object index assignment: obj["key"] = value if let Value::Object(_) = &target { let key = match &idx_val { Value::String(s) => s.clone(), _ => return Err(RuntimeError::RuntimeError { message: "Object index must be a string".into(), token: None, }), }; let val = match op { AssignOp::Equal => rhs, _ => { let current = target.get(&key).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Property '{}' does not exist", key), token: None, })?; self.apply_assign_op(current, rhs, op)? } }; target.set(&key, val.clone())?; return Ok(val); } let i = self.as_array_index(&idx_val)?; match target { Value::Array(vec) => { let mut vec = vec.borrow_mut(); if i >= vec.len() { return Err(RuntimeError::RuntimeError { message: format!("Index {} out of bounds (len {})", i, vec.len()), token: None, }); } let val = match op { AssignOp::Equal => rhs, _ => { let current = vec[i].clone(); self.apply_assign_op(current, rhs, op)? } }; vec[i] = val.clone(); Ok(val) } _ => Err(RuntimeError::RuntimeError { message: "Index assignment on non-array, non-object value".to_string(), token: None, }), } } fn eval_object_literal(&mut self, properties: Vec<(String, Expr)>) -> Result { let mut map = HashMap::new(); for (key, value_expr) in properties { let value = self.evaluate(value_expr)?; map.insert(key, value); } Ok(Value::Object(Rc::new(RefCell::new(map)))) } fn eval_array_literal(&mut self, elements: Vec) -> Result { let mut arr = Vec::new(); for e in elements { arr.push(self.evaluate(e)?); } Ok(Value::Array(Rc::new(RefCell::new(arr)))) } fn eval_unary(&mut self, op: UnaryOp, right: Expr) -> Result { let val = self.evaluate(right)?; match op { UnaryOp::Negate => match val { Value::Number(n) => Ok(Value::Number(-n)), _ => Err(RuntimeError::RuntimeError { message: "Unary '-' on non-number".to_string(), token: None, }), }, UnaryOp::Not => Ok(Value::Bool(!self.is_truthy(&val))), } } fn eval_binary(&mut self, left: Expr, op: BinaryOp, right: Expr) -> Result { let l = self.evaluate(left)?; let r = self.evaluate(right)?; match op { BinaryOp::Add => self.eval_binary_add(l, r), BinaryOp::Sub => self.eval_binary_arith(l, r, |a, b| a - b, "-"), BinaryOp::Mul => self.eval_binary_arith(l, r, |a, b| a * b, "*"), BinaryOp::Div => self.eval_binary_div(l, r), BinaryOp::Mod => self.eval_binary_mod(l, r), BinaryOp::Greater => Ok(Value::Bool(self.as_number(&l)? > self.as_number(&r)?)), BinaryOp::GreaterEqual => Ok(Value::Bool(self.as_number(&l)? >= self.as_number(&r)?)), BinaryOp::Less => Ok(Value::Bool(self.as_number(&l)? < self.as_number(&r)?)), BinaryOp::LessEqual => Ok(Value::Bool(self.as_number(&l)? <= self.as_number(&r)?)), BinaryOp::Equal => Ok(Value::Bool(self.is_equal(&l, &r))), BinaryOp::NotEqual => Ok(Value::Bool(!self.is_equal(&l, &r))), } } fn eval_binary_add(&mut self, l: Value, r: Value) -> Result { if matches!(l, Value::String(_)) || matches!(r, Value::String(_)) { Ok(Value::String(format!("{}{}", l, r))) } else { match (l, r) { (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)), _ => Err(RuntimeError::RuntimeError { message: "Invalid '+' operands".to_string(), token: None, }), } } } fn eval_binary_arith( &mut self, l: Value, r: Value, op_fn: fn(f64, f64) -> f64, name: &str, ) -> Result { match (l, r) { (Value::Number(a), Value::Number(b)) => Ok(Value::Number(op_fn(a, b))), _ => Err(RuntimeError::RuntimeError { message: format!("Invalid '{}' operands", name), token: None, }), } } fn eval_binary_div(&mut self, l: Value, r: Value) -> Result { match (l, r) { (Value::Number(_), Value::Number(b)) if b == 0.0 => Err(RuntimeError::RuntimeError { message: "Division by zero".to_string(), token: None, }), (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a / b)), _ => Err(RuntimeError::RuntimeError { message: "Invalid '/' operands".to_string(), token: None, }), } } fn eval_binary_mod(&mut self, l: Value, r: Value) -> Result { match (l, r) { (Value::Number(_), Value::Number(b)) if b == 0.0 => Err(RuntimeError::RuntimeError { message: "Modulo by zero".to_string(), token: None, }), (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a % b)), _ => Err(RuntimeError::RuntimeError { message: "Invalid '%' operands".to_string(), token: None, }), } } fn eval_logical(&mut self, left: Expr, op: LogicalOp, right: Expr) -> Result { let l = self.evaluate(left)?; match op { LogicalOp::And => { Ok(if !self.is_truthy(&l) { l } else { self.evaluate(right)? }) } LogicalOp::Or => { Ok(if self.is_truthy(&l) { l } else { self.evaluate(right)? }) } } } fn eval_ternary(&mut self, condition: Expr, then_branch: Expr, else_branch: Expr) -> Result { let cond = self.evaluate(condition)?; if self.is_truthy(&cond) { self.evaluate(then_branch) } else { self.evaluate(else_branch) } } fn eval_call(&mut self, callee: Expr, arguments: Vec) -> Result { let func = self.evaluate(callee)?; let mut args = Vec::new(); for e in arguments { args.push(self.evaluate(e)?); } self.call_function(func, args) } fn eval_lambda(&mut self, params: Vec, body: Vec) -> Result { Ok(Value::Function(Rc::new(Function { params, body, env: Rc::clone(&self.env), name: None, }))) } // ======================================================================== // call_function // ======================================================================== pub fn call_function(&mut self, func_val: Value, args: Vec) -> Result { match func_val { Value::NativeFunction(native_fn) => native_fn(self as &mut dyn super::Runtime, args), Value::Function(f) => self.call_user_function(f, args), _ => Err(RuntimeError::RuntimeError { message: "Attempt to call non-function".to_string(), token: None, }), } } fn call_user_function(&mut self, f: Rc, args: Vec) -> Result { let env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&f.env))))); if let Some(name) = &f.name { env.borrow_mut().define(name.clone(), Value::Function(Rc::clone(&f)), true); } for (i, param) in f.params.iter().enumerate() { let val = args.get(i).cloned().unwrap_or(Value::Nil); env.borrow_mut().define(param.clone(), val, true); } let previous = Rc::clone(&self.env); self.env = env; let mut ret = Value::Nil; for stmt in &f.body { match self.execute(stmt.clone())? { Signal::Return(val) => { ret = val; break; } Signal::None => {} Signal::Break | Signal::Continue => { return Err(RuntimeError::RuntimeError { message: "break/continue outside of loop".to_string(), token: None, }); } } } self.env = previous; Ok(ret) } // ======================================================================== // Helpers // ======================================================================== pub fn is_truthy(&self, val: &Value) -> bool { match val { Value::Nil => false, Value::Bool(b) => *b, _ => true, } } pub fn is_equal(&self, a: &Value, b: &Value) -> bool { match (a, b) { (Value::Nil, Value::Nil) => true, (Value::Bool(x), Value::Bool(y)) => x == y, (Value::Number(x), Value::Number(y)) => x == y, (Value::String(x), Value::String(y)) => x == y, (Value::Array(x), Value::Array(y)) => { let x = x.borrow(); let y = y.borrow(); if x.len() != y.len() { return false; } x.iter().zip(y.iter()).all(|(a, b)| self.is_equal(a, b)) } (Value::Object(x), Value::Object(y)) => { let x = x.borrow(); let y = y.borrow(); if x.len() != y.len() { return false; } x.iter().all(|(k, v)| { y.get(k).map_or(false, |yv| self.is_equal(v, yv)) }) } (Value::Function(x), Value::Function(y)) => Rc::ptr_eq(x, y), (Value::NativeFunction(x), Value::NativeFunction(y)) => Rc::ptr_eq(x, y), _ => false, } } pub fn apply_assign_op(&self, left: Value, right: Value, op: AssignOp) -> Result { match op { AssignOp::Equal => Ok(right), AssignOp::PlusEqual => { if let Value::String(s) = &left { Ok(Value::String(format!("{}{}", s, right))) } else { let left_num = self.as_number(&left)?; let right_num = self.as_number(&right)?; Ok(Value::Number(left_num + right_num)) } } AssignOp::MinusEqual => { let left_num = self.as_number(&left)?; let right_num = self.as_number(&right)?; Ok(Value::Number(left_num - right_num)) } AssignOp::StarEqual => { let left_num = self.as_number(&left)?; let right_num = self.as_number(&right)?; Ok(Value::Number(left_num * right_num)) } AssignOp::SlashEqual => { let left_num = self.as_number(&left)?; let right_num = self.as_number(&right)?; Ok(Value::Number(left_num / right_num)) } AssignOp::PercentEqual => { let left_num = self.as_number(&left)?; let right_num = self.as_number(&right)?; Ok(Value::Number(left_num % right_num)) } } } pub fn as_number(&self, val: &Value) -> Result { if let Value::Number(n) = val { Ok(*n) } else { Err(RuntimeError::RuntimeError { message: "Expected number".to_string(), token: None, }) } } pub fn as_array_index(&self, val: &Value) -> Result { if let Value::Number(n) = val { if *n < 0.0 || n.fract() != 0.0 { return Err(RuntimeError::RuntimeError { message: format!("Index must be a non-negative integer, got {}", n), token: None, }); } Ok(*n as usize) } else { Err(RuntimeError::RuntimeError { message: "Index must be a number".to_string(), token: None, }) } } }