refactor: 优化代码组织

This commit is contained in:
0264408
2026-06-16 19:33:16 +08:00
parent e4272943aa
commit a7bedf6fa1
5 changed files with 715 additions and 586 deletions
+519
View File
@@ -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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<char> = 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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Expr>) -> Result<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Expr>) -> Result<Value, RuntimeError> {
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<String>, body: Vec<Stmt>) -> Result<Value, RuntimeError> {
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<Value>) -> Result<Value, RuntimeError> {
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<Function>, args: Vec<Value>) -> Result<Value, RuntimeError> {
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<Value, RuntimeError> {
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<f64, RuntimeError> {
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<usize, RuntimeError> {
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,
})
}
}
}
+192
View File
@@ -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<Signal, RuntimeError> {
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<Signal, RuntimeError> {
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<Signal, RuntimeError> {
self.evaluate(expr)?;
Ok(Signal::None)
}
fn exec_block(&mut self, stmts: Vec<Stmt>) -> Result<Signal, RuntimeError> {
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<Stmt>,
) -> Result<Signal, RuntimeError> {
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<Signal, RuntimeError> {
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<Box<Stmt>>,
condition: Option<Expr>,
step: Option<Expr>,
body: Stmt,
) -> Result<Signal, RuntimeError> {
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<Signal, RuntimeError> {
let iter_val = self.evaluate(iterable)?;
let items: Vec<Value> = 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<String>,
body: Vec<Stmt>,
) -> Result<Signal, RuntimeError> {
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<Expr>) -> Result<Signal, RuntimeError> {
if let Some(expr) = expr_opt {
Ok(Signal::Return(self.evaluate(expr)?))
} else {
Ok(Signal::Return(Value::Nil))
}
}
}
+1 -585
View File
@@ -1,8 +1,6 @@
use crate::ast::*; use crate::ast::*;
use crate::error::RuntimeError; use crate::error::RuntimeError;
use crate::interpreter::Signal; use super::{Env};
use super::{Value, Env, Function};
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
@@ -23,588 +21,6 @@ impl Interpreter {
} }
Ok(()) Ok(())
} }
fn execute(&mut self, stmt: Stmt) -> Result<Signal, RuntimeError> {
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<Value> = 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<Value, RuntimeError> {
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<char> = 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<Value>) -> Result<Value, RuntimeError> {
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<Value, RuntimeError> {
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<f64, RuntimeError> {
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<usize, RuntimeError> {
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)] #[cfg(test)]
+2
View File
@@ -1,5 +1,7 @@
pub mod builtins; pub mod builtins;
pub mod env; pub mod env;
pub mod eval;
pub mod exec;
pub mod interpreter; pub mod interpreter;
pub use env::Env; pub use env::Env;
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*; use crate::interpreter::{Interpreter, Value};
use crate::lexer::Lexer; use crate::lexer::Lexer;
use crate::parser::Parser; use crate::parser::Parser;