diff --git a/src/interpreter/eval.rs b/src/interpreter/eval.rs new file mode 100644 index 0000000..6c70aa3 --- /dev/null +++ b/src/interpreter/eval.rs @@ -0,0 +1,519 @@ +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(map) => { + match map.borrow().get(&name) { + Some(val) => Ok(val.clone()), + None => Err(RuntimeError::RuntimeError { + message: format!("Undefined 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(map) => { + let val = match op { + AssignOp::Equal => rhs, + _ => { + let map_borrow = map.borrow(); + let current = map_borrow.get(&name).ok_or_else(|| RuntimeError::RuntimeError { + message: format!("Property '{}' does not exist", name), + token: None, + })?; + self.apply_assign_op(current.clone(), rhs, op)? + } + }; + map.borrow_mut().insert(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(map) = &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 map.borrow().get(&key).cloned().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(map) = &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 map_borrow = map.borrow(); + let current = map_borrow.get(&key).ok_or_else(|| RuntimeError::RuntimeError { + message: format!("Property '{}' does not exist", key), + token: None, + })?; + self.apply_assign_op(current.clone(), rhs, op)? + } + }; + map.borrow_mut().insert(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(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)) + }) + } + _ => 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, + }) + } + } +} diff --git a/src/interpreter/exec.rs b/src/interpreter/exec.rs new file mode 100644 index 0000000..73bac95 --- /dev/null +++ b/src/interpreter/exec.rs @@ -0,0 +1,192 @@ +use crate::ast::*; +use crate::error::RuntimeError; +use crate::interpreter::Signal; +use super::{Value, Env, Function}; +use std::rc::Rc; +use std::cell::RefCell; + +impl super::Interpreter { + // ======================================================================== + // execute — dispatcher + // ======================================================================== + + pub fn execute(&mut self, stmt: Stmt) -> Result { + match stmt { + Stmt::Let { name, initializer, mutable } => self.exec_let(name, initializer, mutable), + Stmt::ExprStmt(expr) => self.exec_expr_stmt(expr), + Stmt::Block(stmts) => self.exec_block(stmts), + Stmt::If { condition, then_branch, else_branch } => { + self.exec_if(condition, *then_branch, else_branch.map(|b| *b)) + } + Stmt::While { condition, body } => self.exec_while(condition, *body), + Stmt::For { initializer, condition, step, body } => { + self.exec_for(initializer, condition, step, *body) + } + Stmt::ForIn { var_name, iterable, body } => self.exec_for_in(var_name, iterable, *body), + Stmt::Function { name, params, body } => self.exec_function(name, params, body), + Stmt::Return(expr_opt) => self.exec_return(expr_opt), + Stmt::Break => Ok(Signal::Break), + Stmt::Continue => Ok(Signal::Continue), + } + } + + // ======================================================================== + // exec_* methods + // ======================================================================== + + fn exec_let(&mut self, name: String, initializer: Expr, mutable: bool) -> Result { + let val = self.evaluate(initializer)?; + self.env.borrow_mut().define(name, val, mutable); + Ok(Signal::None) + } + + fn exec_expr_stmt(&mut self, expr: Expr) -> Result { + self.evaluate(expr)?; + Ok(Signal::None) + } + + fn exec_block(&mut self, stmts: Vec) -> Result { + let previous = Rc::clone(&self.env); + self.env = Rc::new(RefCell::new(Env::new(Some(previous)))); + let mut signal = Signal::None; + for s in stmts { + signal = self.execute(s)?; + if !matches!(signal, Signal::None) { + break; + } + } + let parent = self.env.borrow().parent.as_ref().unwrap().clone(); + self.env = parent; + Ok(signal) + } + + fn exec_if( + &mut self, + condition: Expr, + then_branch: Stmt, + else_branch: Option, + ) -> Result { + let cond_val = self.evaluate(condition)?; + if self.is_truthy(&cond_val) { + self.execute(then_branch) + } else if let Some(else_branch) = else_branch { + self.execute(else_branch) + } else { + Ok(Signal::None) + } + } + + fn exec_while(&mut self, condition: Expr, body: Stmt) -> Result { + loop { + let cond_val = self.evaluate(condition.clone())?; + if !self.is_truthy(&cond_val) { + break; + } + match self.execute(body.clone())? { + Signal::Break => break, + Signal::Continue => continue, + sig @ Signal::Return(_) => return Ok(sig), + Signal::None => {} + } + } + Ok(Signal::None) + } + + fn exec_for( + &mut self, + initializer: Option>, + condition: Option, + step: Option, + body: Stmt, + ) -> Result { + if let Some(init) = initializer { + self.execute(*init)?; + } + loop { + if let Some(cond) = &condition { + let cond_val = self.evaluate(cond.clone())?; + if !self.is_truthy(&cond_val) { + break; + } + } + match self.execute(body.clone())? { + Signal::Break => break, + Signal::Continue => { + if let Some(step) = &step { + self.evaluate(step.clone())?; + } + continue; + } + sig @ Signal::Return(_) => return Ok(sig), + Signal::None => {} + } + if let Some(step) = &step { + self.evaluate(step.clone())?; + } + } + Ok(Signal::None) + } + + fn exec_for_in( + &mut self, + var_name: String, + iterable: Expr, + body: Stmt, + ) -> Result { + let iter_val = self.evaluate(iterable)?; + let items: Vec = match &iter_val { + Value::Array(arr) => arr.borrow().clone(), + Value::Object(obj) => obj.borrow().keys().map(|k| Value::String(k.clone())).collect(), + Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(), + _ => { + return Err(RuntimeError::RuntimeError { + message: "for-in requires an array, object, or string".into(), + token: None, + }); + } + }; + + let mut signal = Signal::None; + for item in items { + let previous = Rc::clone(&self.env); + self.env = Rc::new(RefCell::new(Env::new(Some(previous)))); + self.env.borrow_mut().define(var_name.clone(), item, true); + + signal = self.execute(body.clone())?; + let parent = self.env.borrow().parent.as_ref().unwrap().clone(); + self.env = parent; + + match signal { + Signal::Break => { signal = Signal::None; break; } + Signal::Continue => { signal = Signal::None; continue; } + sig @ Signal::Return(_) => return Ok(sig), + Signal::None => {} + } + } + Ok(signal) + } + + fn exec_function( + &mut self, + name: String, + params: Vec, + body: Vec, + ) -> Result { + let func = Value::Function(Rc::new(Function { + params, + body, + env: Rc::clone(&self.env), + name: Some(name.clone()), + })); + self.env.borrow_mut().define(name, func, true); + Ok(Signal::None) + } + + fn exec_return(&mut self, expr_opt: Option) -> Result { + if let Some(expr) = expr_opt { + Ok(Signal::Return(self.evaluate(expr)?)) + } else { + Ok(Signal::Return(Value::Nil)) + } + } +} diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index dd2775b..8d89cb7 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -1,8 +1,6 @@ use crate::ast::*; use crate::error::RuntimeError; -use crate::interpreter::Signal; -use super::{Value, Env, Function}; -use std::collections::HashMap; +use super::{Env}; use std::rc::Rc; use std::cell::RefCell; @@ -23,588 +21,6 @@ impl Interpreter { } Ok(()) } - - fn execute(&mut self, stmt: Stmt) -> Result { - match stmt { - Stmt::Let { name, initializer, mutable } => { - let val = self.evaluate(initializer)?; - self.env.borrow_mut().define(name, val, mutable); - Ok(Signal::None) - } - Stmt::ExprStmt(expr) => { - self.evaluate(expr)?; - Ok(Signal::None) - } - Stmt::Block(stmts) => { - let previous = Rc::clone(&self.env); - self.env = Rc::new(RefCell::new(Env::new(Some(previous)))); - let mut signal = Signal::None; - for s in stmts { - signal = self.execute(s)?; - if !matches!(signal, Signal::None) { - break; - } - } - let parent = self.env.borrow().parent.as_ref().unwrap().clone(); - self.env = parent; - Ok(signal) - } - Stmt::If { condition, then_branch, else_branch } => { - let cond_val = self.evaluate(condition)?; - if self.is_truthy(&cond_val) { - self.execute(*then_branch) - } else if let Some(else_branch) = else_branch { - self.execute(*else_branch) - } else { - Ok(Signal::None) - } - } - Stmt::While { condition, body } => { - loop { - let cond_val = self.evaluate(condition.clone())?; - if !self.is_truthy(&cond_val) { - break; - } - match self.execute(*body.clone())? { - Signal::Break => break, - Signal::Continue => continue, - sig @ Signal::Return(_) => return Ok(sig), - Signal::None => {} - } - } - Ok(Signal::None) - } - Stmt::For { initializer, condition, step, body } => { - if let Some(init) = initializer { - self.execute(*init)?; - } - loop { - if let Some(cond) = &condition { - let cond_val = self.evaluate(cond.clone())?; - if !self.is_truthy(&cond_val) { - break; - } - } - match self.execute(*body.clone())? { - Signal::Break => break, - Signal::Continue => { - if let Some(step) = &step { - self.evaluate(step.clone())?; - } - continue; - } - sig @ Signal::Return(_) => return Ok(sig), - Signal::None => {} - } - if let Some(step) = &step { - self.evaluate(step.clone())?; - } - } - Ok(Signal::None) - } - Stmt::ForIn { var_name, iterable, body } => { - let iter_val = self.evaluate(iterable)?; - let items: Vec = match &iter_val { - Value::Array(arr) => arr.borrow().clone(), - Value::Object(obj) => obj.borrow().keys().map(|k| Value::String(k.clone())).collect(), - Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(), - _ => { - return Err(RuntimeError::RuntimeError { - message: "for-in requires an array, object, or string".into(), - token: None, - }); - } - }; - - let mut signal = Signal::None; - for item in items { - // Create a new scope for each iteration so the loop variable - // is isolated and break/continue clean up correctly. - let previous = Rc::clone(&self.env); - self.env = Rc::new(RefCell::new(Env::new(Some(previous)))); - self.env.borrow_mut().define(var_name.clone(), item, true); - - signal = self.execute(*body.clone())?; - // Restore parent scope before checking signal - let parent = self.env.borrow().parent.as_ref().unwrap().clone(); - self.env = parent; - - match signal { - Signal::Break => { signal = Signal::None; break; } - Signal::Continue => { signal = Signal::None; continue; } - sig @ Signal::Return(_) => return Ok(sig), - Signal::None => {} - } - } - Ok(signal) - } - Stmt::Function { name, params, body } => { - let func = Value::Function(Rc::new(Function { - params, - body, - env: Rc::clone(&self.env), - name: Some(name.clone()), - })); - self.env.borrow_mut().define(name, func, true); - Ok(Signal::None) - } - Stmt::Return(expr_opt) => { - if let Some(expr) = expr_opt { - Ok(Signal::Return(self.evaluate(expr)?)) - } else { - Ok(Signal::Return(Value::Nil)) - } - } - Stmt::Break => Ok(Signal::Break), - Stmt::Continue => Ok(Signal::Continue), - } - } - - fn evaluate(&mut self, expr: Expr) -> Result { - match expr { - Expr::Literal(lit) => Ok(match lit { - crate::ast::expr::Literal::Number(n) => Value::Number(n), - crate::ast::expr::Literal::String(s) => Value::String(s), - crate::ast::expr::Literal::Bool(b) => Value::Bool(b), - crate::ast::expr::Literal::Nil => Value::Nil, - }), - Expr::Variable(name) => { - match self.env.borrow().get(&name) { - Some(val) => Ok(val), - None => Err(RuntimeError::RuntimeError { - message: format!("Undefined variable '{}'", name), - token: None, - }), - } - } - Expr::Assign { name, op, value } => { - 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) - } - Expr::Get { object, name } => { - let obj = self.evaluate(*object)?; - match obj { - Value::Object(map) => { - match map.borrow().get(&name) { - Some(val) => Ok(val.clone()), - None => Err(RuntimeError::RuntimeError { - message: format!("Undefined property '{}'", name), - token: None, - }), - } - } - _ => Err(RuntimeError::RuntimeError { - message: "Only objects have properties".to_string(), - token: None, - }), - } - } - Expr::Set { object, name, op, value } => { - let obj = self.evaluate(*object)?; - let rhs = self.evaluate(*value)?; - match obj { - Value::Object(map) => { - let val = match op { - AssignOp::Equal => rhs, - _ => { - let map_borrow = map.borrow(); - let current = map_borrow.get(&name).ok_or_else(|| RuntimeError::RuntimeError { message: format!("Property '{}' does not exist", name), token: None })?; - self.apply_assign_op(current.clone(), rhs, op)? - } - }; - map.borrow_mut().insert(name, val.clone()); - Ok(val) - } - _ => Err(RuntimeError::RuntimeError { - message: "Only objects have properties".to_string(), - token: None, - }), - } - } - Expr::IndexGet { array, index } => { - let target = self.evaluate(*array)?; - let idx_val = self.evaluate(*index)?; - // Object index access: obj["key"] - if let Value::Object(map) = &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 map.borrow().get(&key).cloned().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, - }), - } - } - Expr::IndexSet { array, index, op, value } => { - 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(map) = &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 map_borrow = map.borrow(); - let current = map_borrow.get(&key).ok_or_else(|| RuntimeError::RuntimeError { - message: format!("Property '{}' does not exist", key), - token: None, - })?; - self.apply_assign_op(current.clone(), rhs, op)? - } - }; - map.borrow_mut().insert(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, - }), - } - } - Expr::ObjectLiteral { properties } => { - 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)))) - } - Expr::ArrayLiteral { elements } => { - let mut arr = Vec::new(); - for e in elements { - arr.push(self.evaluate(e)?); - } - Ok(Value::Array(Rc::new(RefCell::new(arr)))) - } - Expr::Unary { op, right } => { - let val = self.evaluate(*right)?; - match op { - crate::ast::expr::UnaryOp::Negate => match val { - Value::Number(n) => Ok(Value::Number(-n)), - _ => Err(RuntimeError::RuntimeError { - message: "Unary '-' on non-number".to_string(), - token: None, - }), - }, - crate::ast::expr::UnaryOp::Not => Ok(Value::Bool(!self.is_truthy(&val))), - } - } - Expr::Binary { left, op, right } => { - let l = self.evaluate(*left)?; - let r = self.evaluate(*right)?; - match op { - crate::ast::expr::BinaryOp::Add => { - // If either operand is a string, coerce both to string and concatenate. - 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, - }), - } - } - } - crate::ast::expr::BinaryOp::Sub => match (l,r) { - (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a-b)), - _ => Err(RuntimeError::RuntimeError { - message: "Invalid '-' operands".to_string(), - token: None, - }), - }, - crate::ast::expr::BinaryOp::Mul => match (l,r) { - (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a*b)), - _ => Err(RuntimeError::RuntimeError { - message: "Invalid '*' operands".to_string(), - token: None, - }), - }, - crate::ast::expr::BinaryOp::Div => 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, - }), - }, - crate::ast::expr::BinaryOp::Mod => 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, - }), - }, - crate::ast::expr::BinaryOp::Greater => Ok(Value::Bool(self.as_number(&l)? > self.as_number(&r)?)), - crate::ast::expr::BinaryOp::GreaterEqual => Ok(Value::Bool(self.as_number(&l)? >= self.as_number(&r)?)), - crate::ast::expr::BinaryOp::Less => Ok(Value::Bool(self.as_number(&l)? < self.as_number(&r)?)), - crate::ast::expr::BinaryOp::LessEqual => Ok(Value::Bool(self.as_number(&l)? <= self.as_number(&r)?)), - crate::ast::expr::BinaryOp::Equal => Ok(Value::Bool(self.is_equal(&l,&r))), - crate::ast::expr::BinaryOp::NotEqual => Ok(Value::Bool(!self.is_equal(&l,&r))), - } - } - Expr::Logical { left, op, right } => { - let l = self.evaluate(*left)?; - match op { - crate::ast::expr::LogicalOp::And => { - Ok(if !self.is_truthy(&l) { l } else { self.evaluate(*right)? }) - } - crate::ast::expr::LogicalOp::Or => { - Ok(if self.is_truthy(&l) { l } else { self.evaluate(*right)? }) - } - } - } - Expr::Ternary { condition, then_branch, else_branch } => { - let cond = self.evaluate(*condition)?; - if self.is_truthy(&cond) { - self.evaluate(*then_branch) - } else { - self.evaluate(*else_branch) - } - } - Expr::Call { callee, arguments } => { - let func = self.evaluate(*callee)?; - let mut args = Vec::new(); - for e in arguments { - args.push(self.evaluate(e)?); - } - self.call_function(func, args) - } - Expr::Lambda { params, body } => { - Ok(Value::Function(Rc::new(Function { - params, - body, - env: Rc::clone(&self.env), - name: None, - }))) - } - } - } - - fn call_function(&mut self, func_val: Value, args: Vec) -> Result { - match func_val { - Value::NativeFunction(native_fn) => { - native_fn(args) - } - Value::Function(f) => { - 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) - } - _ => { - Err(RuntimeError::RuntimeError { - message: "Attempt to call non-function".to_string(), - token: None, - }) - } - } - } - - fn is_truthy(&self, val: &Value) -> bool { - match val { - Value::Nil => false, - Value::Bool(b) => *b, - _ => true, - } - } - - 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)) - }) - } - _ => false, - } - } - - 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)) - } - } - } - - 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, - }) - } - } - - 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, - }) - } - } } #[cfg(test)] diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c696f9e..ec28685 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,5 +1,7 @@ pub mod builtins; pub mod env; +pub mod eval; +pub mod exec; pub mod interpreter; pub use env::Env; diff --git a/src/interpreter/tests.rs b/src/interpreter/tests.rs index b2fe298..06ebf2a 100644 --- a/src/interpreter/tests.rs +++ b/src/interpreter/tests.rs @@ -1,4 +1,4 @@ -use super::*; +use crate::interpreter::{Interpreter, Value}; use crate::lexer::Lexer; use crate::parser::Parser;