From fa6ea512b3cadfe52254c6b23a587ca8c54274a8 Mon Sep 17 00:00:00 2001 From: 0264408 Date: Fri, 26 Jun 2026 10:31:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20VM-only=20architecture=20=E2=80=94?= =?UTF-8?q?=20remove=20tree-walking=20interpreter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate dual-engine architecture (Interpreter + Vm) in favor of a single bytecode VM executor. - Extract shared types (Value, FunctionProto, Closure, UpvalueObj, Runtime trait, NativeFn) into new module - Consolidate builtin registration in — single used by VM, eliminates 40-line duplicate - Delete tree-walker: exec.rs, eval.rs, interpreter.rs, env.rs, module.rs - Change Value::Function(Rc) → Value::Function(Rc) eliminating the closures HashMap pointer-key hack - Fix VM semantic gaps found during migration: * Structural equality for Object/Array in is_equal * CompoundAssignGlobal opcode (global compound assigns were broken) * 9 string methods added to VM get_property * ForInInit error on non-iterable values - Switch run_file/run_repl to VM; remove --vm CLI flag - Move 327 tests from interpreter/ to vm/ — all pass Co-Authored-By: Claude --- aster-core/src/interpreter/builtins/mod.rs | 52 -- aster-core/src/interpreter/env.rs | 50 -- aster-core/src/interpreter/eval.rs | 624 ------------------ aster-core/src/interpreter/exec.rs | 192 ------ aster-core/src/interpreter/interpreter.rs | 55 -- aster-core/src/interpreter/mod.rs | 136 +--- aster-core/src/interpreter/module.rs | 143 ---- aster-core/src/lib.rs | 46 +- .../{interpreter => runtime}/builtins/core.rs | 2 +- .../{interpreter => runtime}/builtins/io.rs | 2 +- aster-core/src/runtime/builtins/mod.rs | 45 ++ .../{interpreter => runtime}/builtins/os.rs | 2 +- .../builtins/string.rs | 2 +- aster-core/src/runtime/mod.rs | 191 ++++++ aster-core/src/vm/compiler.rs | 68 +- aster-core/src/vm/opcode.rs | 7 +- aster-core/src/{interpreter => vm}/tests.rs | 30 +- aster-core/src/vm/vm.rs | 240 ++++--- aster/src/main.rs | 16 +- 19 files changed, 432 insertions(+), 1471 deletions(-) delete mode 100644 aster-core/src/interpreter/builtins/mod.rs delete mode 100644 aster-core/src/interpreter/env.rs delete mode 100644 aster-core/src/interpreter/eval.rs delete mode 100644 aster-core/src/interpreter/exec.rs delete mode 100644 aster-core/src/interpreter/interpreter.rs delete mode 100644 aster-core/src/interpreter/module.rs rename aster-core/src/{interpreter => runtime}/builtins/core.rs (98%) rename aster-core/src/{interpreter => runtime}/builtins/io.rs (93%) create mode 100644 aster-core/src/runtime/builtins/mod.rs rename aster-core/src/{interpreter => runtime}/builtins/os.rs (88%) rename aster-core/src/{interpreter => runtime}/builtins/string.rs (99%) create mode 100644 aster-core/src/runtime/mod.rs rename aster-core/src/{interpreter => vm}/tests.rs (98%) diff --git a/aster-core/src/interpreter/builtins/mod.rs b/aster-core/src/interpreter/builtins/mod.rs deleted file mode 100644 index a429f6c..0000000 --- a/aster-core/src/interpreter/builtins/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! 内置函数模块:按功能分组注册,便于扩展和维护。 - -pub mod core; -pub mod io; -pub mod os; -pub mod string; - -use crate::interpreter::{Env, Value}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; - -/// 向全局环境注册所有标准库(io、os 等) -pub fn register_all(env: &Rc>) { - let mut e = env.borrow_mut(); - - // io - let mut io = HashMap::new(); - io.insert("print".into(), Value::NativeFunction(Rc::new(io::print))); - io.insert("input".into(), Value::NativeFunction(Rc::new(io::input))); - e.define("io".into(), Value::Object(Rc::new(RefCell::new(io))), true); - // input and print can be used as global functions for convenience - e.define("print".into(), Value::NativeFunction(Rc::new(io::print)), true); - e.define("input".into(), Value::NativeFunction(Rc::new(io::input)), true); - - // os - let mut os = HashMap::new(); - os.insert("clock".into(), Value::NativeFunction(Rc::new(os::clock))); - e.define("os".into(), Value::Object(Rc::new(RefCell::new(os))), true); - // clock can also be used as a global function for convenience - e.define("clock".into(), Value::NativeFunction(Rc::new(os::clock)), true); - - // core — len, typeof, push, pop - e.define("len".into(), Value::NativeFunction(Rc::new(core::len)), true); - e.define("typeof".into(), Value::NativeFunction(Rc::new(core::typeof_fn)), true); - e.define("push".into(), Value::NativeFunction(Rc::new(core::push)), true); - e.define("pop".into(), Value::NativeFunction(Rc::new(core::pop)), true); - - // require — module loader - e.define("require".into(), Value::NativeFunction(Rc::new(super::module::require_fn)), true); - - // string — split, trim, substring, replace, contains, upper, lower, starts_with, ends_with - e.define("split".into(), Value::NativeFunction(Rc::new(string::split)), true); - e.define("trim".into(), Value::NativeFunction(Rc::new(string::trim)), true); - e.define("substring".into(), Value::NativeFunction(Rc::new(string::substring)), true); - e.define("replace".into(), Value::NativeFunction(Rc::new(string::replace)), true); - e.define("contains".into(), Value::NativeFunction(Rc::new(string::contains)), true); - e.define("upper".into(), Value::NativeFunction(Rc::new(string::upper)), true); - e.define("lower".into(), Value::NativeFunction(Rc::new(string::lower)), true); - e.define("starts_with".into(), Value::NativeFunction(Rc::new(string::starts_with)), true); - e.define("ends_with".into(), Value::NativeFunction(Rc::new(string::ends_with)), true); -} diff --git a/aster-core/src/interpreter/env.rs b/aster-core/src/interpreter/env.rs deleted file mode 100644 index 194da3c..0000000 --- a/aster-core/src/interpreter/env.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::Value; -use std::collections::HashMap; -use std::rc::Rc; -use std::cell::RefCell; - -#[derive(Debug)] -pub struct Env { - /// (value, is_mutable) — `is_mutable` is true for `let`, false for `const`. - pub values: HashMap, - pub parent: Option>>, -} - -impl Env { - pub fn new(parent: Option>>) -> Self { - Self { - values: HashMap::new(), - parent, - } - } - - pub fn define(&mut self, name: String, val: Value, mutable: bool) { - self.values.insert(name, (val, mutable)); - } - - /// Assign a new value to an existing binding. Returns `Err(msg)` if the - /// binding exists but was declared with `const` (immutable). - pub fn assign(&mut self, name: &str, val: Value) -> Result { - if let Some((_, false)) = self.values.get(name) { - return Err(format!("Cannot reassign constant '{}'", name)); - } - if self.values.contains_key(name) { - self.values.insert(name.to_string(), (val, true)); - Ok(true) - } else if let Some(parent) = &self.parent { - parent.borrow_mut().assign(name, val) - } else { - Ok(false) - } - } - - pub fn get(&self, name: &str) -> Option { - if let Some((val, _)) = self.values.get(name) { - Some(val.clone()) - } else if let Some(parent) = &self.parent { - parent.borrow().get(name) - } else { - None - } - } -} diff --git a/aster-core/src/interpreter/eval.rs b/aster-core/src/interpreter/eval.rs deleted file mode 100644 index c159214..0000000 --- a/aster-core/src/interpreter/eval.rs +++ /dev/null @@ -1,624 +0,0 @@ -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, - }) - } - } -} diff --git a/aster-core/src/interpreter/exec.rs b/aster-core/src/interpreter/exec.rs deleted file mode 100644 index 73bac95..0000000 --- a/aster-core/src/interpreter/exec.rs +++ /dev/null @@ -1,192 +0,0 @@ -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/aster-core/src/interpreter/interpreter.rs b/aster-core/src/interpreter/interpreter.rs deleted file mode 100644 index 829c07a..0000000 --- a/aster-core/src/interpreter/interpreter.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::ast::*; -use crate::error::RuntimeError; -use super::{Env, Value}; -use std::collections::HashMap; -use std::rc::Rc; -use std::cell::RefCell; - -pub struct Interpreter { - /// 当前作用域(用户代码所在 env,parent 指向 builtins_env) - pub env: Rc>, - /// 内置函数根作用域(parent = None,所有内置函数注册在此) - pub builtins_env: Rc>, - /// 模块缓存:规范路径 → exports 对象 - pub module_cache: RefCell>, - /// 当前执行文件的目录,用于 require() 解析相对路径 - pub current_dir: String, -} - -impl Interpreter { - pub fn new() -> Self { - // 根层:仅包含内置函数 - let builtins_env = Rc::new(RefCell::new(Env::new(None))); - super::builtins::register_all(&builtins_env); - - // 用户层:parent 指向 builtins_env,用户定义的变量都在这层 - let script_env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&builtins_env))))); - - Self { - env: script_env, - builtins_env, - module_cache: RefCell::new(HashMap::new()), - current_dir: std::env::current_dir() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|_| ".".to_string()), - } - } - - /// 指定当前目录的构造器,用于 `run_file` 时设置脚本所在目录 - pub fn with_current_dir(dir: String) -> Self { - let mut interp = Self::new(); - interp.current_dir = dir; - interp - } - - pub fn interpret(&mut self, statements: Vec) -> Result<(), RuntimeError> { - for stmt in statements { - self.execute(stmt)?; - } - Ok(()) - } -} - -#[cfg(test)] -#[path = "tests.rs"] -mod tests; diff --git a/aster-core/src/interpreter/mod.rs b/aster-core/src/interpreter/mod.rs index 866b1f4..d2c07f3 100644 --- a/aster-core/src/interpreter/mod.rs +++ b/aster-core/src/interpreter/mod.rs @@ -1,133 +1,3 @@ -pub mod builtins; -pub mod env; -pub mod eval; -pub mod exec; -pub mod interpreter; -pub mod module; - -pub use env::Env; -pub use interpreter::Interpreter; - -use std::collections::HashMap; -use std::rc::Rc; -use std::cell::RefCell; -use std::fmt; - -/// Trait abstracting runtime services that native functions may need. -/// Both the tree-walking `Interpreter` and the bytecode `Vm` implement this. -pub trait Runtime { - /// Load and execute a module, returning its exports object. - /// Implementations differ: tree-walker interprets directly, - /// VM compiles to bytecode then executes. - fn require(&mut self, path: &str) -> Result; -} - -pub type NativeFn = Rc) -> Result>; - -#[derive(Clone)] -pub enum Value { - Number(f64), - String(String), - Bool(bool), - Nil, - Object(Rc>>), - Array(Rc>>), - Function(Rc), - NativeFunction(NativeFn), -} - -pub enum Signal { - None, // 正常执行 - Return(Value), // return 语句携带的返回值 - Break, // break 信号 - Continue, // continue 信号 -} - -impl Value { - pub fn get(&self, key: &str) -> Option { - match self { - Value::Object(obj) => obj.borrow().get(key).cloned(), - _ => None, - } - } - - pub fn set(&self, key: &str, val: Value) -> Result<(), crate::error::RuntimeError> { - match self { - Value::Object(obj) => { - obj.borrow_mut().insert(key.to_string(), val); - Ok(()) - }, - _ => Err(crate::error::RuntimeError::RuntimeError { - message: "Only objects have properties".to_string(), - token: None, - }), - } - } -} - -impl std::fmt::Debug for Value { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Value::Number(n) => write!(f, "Number({:?})", n), - Value::String(s) => write!(f, "String({:?})", s), - Value::Bool(b) => write!(f, "Bool({:?})", b), - Value::Nil => write!(f, "Nil"), - Value::Object(_) => write!(f, "Object(...)"), - Value::Array(_) => write!(f, "Array(...)"), - Value::Function(_) => write!(f, "Function(...)"), - Value::NativeFunction(_) => write!(f, "NativeFunction(...)"), - } - } -} - -impl Value { - /// 容器内值的格式化:字符串加引号以区分类型,其余类型用 Display - fn fmt_element(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Value::String(s) => write!(f, "\"{}\"", s), - other => write!(f, "{}", other), - } - } -} - -impl fmt::Display for Value { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Value::Number(n) => write!(f, "{}", n), - Value::String(s) => write!(f, "{}", s), - Value::Bool(b) => write!(f, "{}", b), - Value::Nil => write!(f, "nil"), - Value::Object(obj) => { - let obj = obj.borrow(); - write!(f, "{{ ")?; - for (key, value) in obj.iter() { - write!(f, "{}: ", key)?; - value.fmt_element(f)?; - write!(f, ", ")?; - } - write!(f, "}}") - }, - Value::Array(arr) => { - let arr = arr.borrow(); - write!(f, "[")?; - for (i, val) in arr.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - val.fmt_element(f)?; - } - write!(f, "]") - }, - Value::Function(_) => write!(f, ""), - Value::NativeFunction(_) => write!(f, ""), - } - } -} - -#[derive(Debug, Clone)] -pub struct Function { - pub params: Vec, - pub body: Vec, - pub env: Rc>, // 闭包捕获环境 - pub name: Option, -} +// Re-export shared types from runtime (backward compatibility) +pub use crate::runtime::{Value, NativeFn, Runtime}; +pub use crate::runtime::builtins; diff --git a/aster-core/src/interpreter/module.rs b/aster-core/src/interpreter/module.rs deleted file mode 100644 index 33be480..0000000 --- a/aster-core/src/interpreter/module.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! 模块系统:require() 加载器 -//! -//! `require("path/to/module.ast")` 加载并执行指定的 Aster 文件, -//! 返回一个包含模块所有顶层定义的 `Value::Object`。 -//! 模块在自己的作用域中执行,只能访问内置函数,无法访问调用者的变量。 -//! 第二次 require 同一文件会返回缓存的对象。 - -use crate::error::RuntimeError; -use crate::lexer::Lexer; -use crate::parser::Parser; -use super::{Interpreter, Env, Value, Signal, Runtime}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::path::Path; -use std::rc::Rc; - -impl Runtime for Interpreter { - fn require(&mut self, path: &str) -> Result { - require_impl(self, path) - } -} - -/// require() 的内置函数实现 — 薄包装,委托给 Runtime::require -pub fn require_fn(runtime: &mut dyn Runtime, args: Vec) -> Result { - let path_str = match args.first() { - Some(Value::String(s)) => s.clone(), - Some(other) => return Err(runtime_error(format!( - "require() expects a string argument, got {}", other))), - None => return Err(runtime_error( - "require() expects 1 argument (string path)")), - }; - runtime.require(&path_str) -} - -/// require() 的内部实现 (供 Interpreter::require 使用) -fn require_impl(interp: &mut Interpreter, path_str: &str) -> Result { - // 1. 路径解析 - let resolved = resolve_path(&interp.current_dir, path_str)?; - - // 2. 缓存查找 - if let Some(cached) = interp.module_cache.borrow().get(&resolved) { - return Ok(cached.clone()); - } - - // 3. 读取文件 - let src = std::fs::read_to_string(&resolved) - .map_err(|e| runtime_error(format!( - "Module '{}' not found: {}", path_str, e)))?; - - // 4. 词法分析 - let (tokens, lex_errors) = Lexer::new(&src).tokenize(); - if !lex_errors.is_empty() { - return Err(runtime_error(format!( - "Lex error in module '{}': {}", path_str, lex_errors[0]))); - } - - // 5. 语法分析 - let mut parser = Parser::new(tokens); - let (stmts, parse_errors) = parser.parse(); - if !parse_errors.is_empty() { - return Err(runtime_error(format!( - "Parse error in module '{}': {}", path_str, parse_errors[0]))); - } - - // 6. 创建隔离的模块 env(父级 = builtins_env,看不到调用者的变量) - let module_env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&interp.builtins_env))))); - - // 7. 在缓存中插入占位符(支持循环 require) - let exports_map = Rc::new(RefCell::new(HashMap::new())); - let exports = Value::Object(Rc::clone(&exports_map)); - interp.module_cache.borrow_mut().insert(resolved.clone(), exports.clone()); - - // 8. 保存调用者状态 - let previous_env = Rc::clone(&interp.env); - let previous_dir = interp.current_dir.clone(); - - // 9. 切换到模块上下文 - interp.env = module_env.clone(); - interp.current_dir = module_dir(&resolved); - - // 10. 执行模块 - for stmt in &stmts { - match interp.execute(stmt.clone()) { - Ok(Signal::Break) | Ok(Signal::Continue) => { - interp.env = previous_env; - interp.current_dir = previous_dir; - interp.module_cache.borrow_mut().remove(&resolved); - return Err(runtime_error( - "break/continue outside of loop in module")); - } - Err(e) => { - interp.env = previous_env; - interp.current_dir = previous_dir; - interp.module_cache.borrow_mut().remove(&resolved); - return Err(e); - } - Ok(Signal::None) | Ok(Signal::Return(_)) => {} - } - } - - // 11. 恢复调用者状态 - interp.env = previous_env; - interp.current_dir = previous_dir; - - // 12. 收集 exports(模块 env 中的所有直接绑定) - for (name, (val, _mutable)) in module_env.borrow().values.clone() { - exports_map.borrow_mut().insert(name, val); - } - - Ok(exports) -} - -// ============================================================================ -// Helpers -// ============================================================================ - -fn resolve_path(current_dir: &str, path_str: &str) -> Result { - let path = Path::new(path_str); - let resolved = if path.is_absolute() { - path.to_path_buf() - } else { - Path::new(current_dir).join(path) - }; - std::fs::canonicalize(&resolved) - .map(|p| p.to_string_lossy().to_string()) - .map_err(|_| runtime_error(format!( - "Module '{}' not found (resolved to '{}')", - path_str, resolved.display()))) -} - -fn module_dir(resolved: &str) -> String { - Path::new(resolved) - .parent() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| ".".to_string()) -} - -fn runtime_error(msg: impl Into) -> RuntimeError { - RuntimeError::RuntimeError { - message: msg.into(), - token: None, - } -} diff --git a/aster-core/src/lib.rs b/aster-core/src/lib.rs index 4766e23..344922d 100644 --- a/aster-core/src/lib.rs +++ b/aster-core/src/lib.rs @@ -1,6 +1,7 @@ pub mod lexer; pub mod ast; pub mod parser; +pub mod runtime; pub mod interpreter; pub mod vm; pub mod error; @@ -8,7 +9,6 @@ pub mod analysis; use lexer::Lexer; use parser::Parser; -use interpreter::Interpreter; use vm::compiler::Compiler; use vm::vm::Vm; use error::RuntimeError; @@ -37,33 +37,6 @@ pub fn run_file(filename: &str, src: String) { std::process::exit(65); } - let script_dir = std::path::Path::new(filename) - .parent() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| ".".to_string()); - - let mut interpreter = Interpreter::with_current_dir(script_dir); - if let Err(e) = interpreter.interpret(stmts) { - eprintln!("Error: {}", e); - std::process::exit(70); - } -} - -/// Run a file using the bytecode VM (for performance comparison). -pub fn run_file_vm(filename: &str, src: String) { - let (tokens, lex_errors) = Lexer::new(&src).tokenize(); - if !lex_errors.is_empty() { - print_errors(&lex_errors); - std::process::exit(65); - } - - let mut parser = Parser::new(tokens); - let (stmts, parse_errors) = parser.parse(); - if !parse_errors.is_empty() { - print_errors(&parse_errors); - std::process::exit(65); - } - let script_dir = std::path::Path::new(filename) .parent() .map(|p| p.to_string_lossy().to_string()) @@ -87,9 +60,9 @@ pub fn run_file_vm(filename: &str, src: String) { pub fn run_repl() { println!("Welcome to Aster REPL!"); println!("Type ':exit' to quit."); - println!("Type ':reset' to reset the interpreter state."); + println!("Type ':reset' to reset the VM state."); - let mut interpreter = Interpreter::new(); + let mut vm = Vm::new(); let mut line = String::new(); loop { @@ -113,8 +86,8 @@ pub fn run_repl() { break; } ":reset" => { - interpreter = Interpreter::new(); - println!("Interpreter reset."); + vm = Vm::new(); + println!("VM reset."); continue; } _ => {} @@ -133,7 +106,14 @@ pub fn run_repl() { continue; } - if let Err(e) = interpreter.interpret(stmts) { + let proto = match Compiler::compile(&stmts) { + Ok(p) => p, + Err(e) => { + eprintln!("Compile Error: {}", e); + continue; + } + }; + if let Err(e) = vm.run(std::rc::Rc::new(proto)) { eprintln!("Error: {}", e); } } diff --git a/aster-core/src/interpreter/builtins/core.rs b/aster-core/src/runtime/builtins/core.rs similarity index 98% rename from aster-core/src/interpreter/builtins/core.rs rename to aster-core/src/runtime/builtins/core.rs index f7283e7..c669166 100644 --- a/aster-core/src/interpreter/builtins/core.rs +++ b/aster-core/src/runtime/builtins/core.rs @@ -1,7 +1,7 @@ //! 标准库:core(len, typeof, push, pop) use crate::error::RuntimeError; -use crate::interpreter::{Runtime, Value}; +use crate::runtime::{Runtime, Value}; pub fn len(_runtime: &mut dyn Runtime, args: Vec) -> Result { if args.is_empty() { diff --git a/aster-core/src/interpreter/builtins/io.rs b/aster-core/src/runtime/builtins/io.rs similarity index 93% rename from aster-core/src/interpreter/builtins/io.rs rename to aster-core/src/runtime/builtins/io.rs index b90626f..4409aa4 100644 --- a/aster-core/src/interpreter/builtins/io.rs +++ b/aster-core/src/runtime/builtins/io.rs @@ -1,7 +1,7 @@ //! 标准库:io(print, input) use crate::error::RuntimeError; -use crate::interpreter::{Runtime, Value}; +use crate::runtime::{Runtime, Value}; pub fn print(_interp: &mut dyn Runtime, args: Vec) -> Result { for arg in args.iter() { diff --git a/aster-core/src/runtime/builtins/mod.rs b/aster-core/src/runtime/builtins/mod.rs new file mode 100644 index 0000000..19b85f5 --- /dev/null +++ b/aster-core/src/runtime/builtins/mod.rs @@ -0,0 +1,45 @@ +//! Builtin function modules. + +pub mod core; +pub mod io; +pub mod os; +pub mod string; + +use crate::runtime::Value; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +/// Register all standard library functions into a HashMap. +pub fn register_all(map: &mut HashMap) { + // io + let mut io_map = HashMap::new(); + io_map.insert("print".into(), Value::NativeFunction(Rc::new(io::print))); + io_map.insert("input".into(), Value::NativeFunction(Rc::new(io::input))); + map.insert("io".into(), Value::Object(Rc::new(RefCell::new(io_map)))); + map.insert("print".into(), Value::NativeFunction(Rc::new(io::print))); + map.insert("input".into(), Value::NativeFunction(Rc::new(io::input))); + + // os + let mut os_map = HashMap::new(); + os_map.insert("clock".into(), Value::NativeFunction(Rc::new(os::clock))); + map.insert("os".into(), Value::Object(Rc::new(RefCell::new(os_map)))); + map.insert("clock".into(), Value::NativeFunction(Rc::new(os::clock))); + + // core + map.insert("len".into(), Value::NativeFunction(Rc::new(core::len))); + map.insert("typeof".into(), Value::NativeFunction(Rc::new(core::typeof_fn))); + map.insert("push".into(), Value::NativeFunction(Rc::new(core::push))); + map.insert("pop".into(), Value::NativeFunction(Rc::new(core::pop))); + + // string + map.insert("split".into(), Value::NativeFunction(Rc::new(string::split))); + map.insert("trim".into(), Value::NativeFunction(Rc::new(string::trim))); + map.insert("substring".into(), Value::NativeFunction(Rc::new(string::substring))); + map.insert("replace".into(), Value::NativeFunction(Rc::new(string::replace))); + map.insert("contains".into(), Value::NativeFunction(Rc::new(string::contains))); + map.insert("upper".into(), Value::NativeFunction(Rc::new(string::upper))); + map.insert("lower".into(), Value::NativeFunction(Rc::new(string::lower))); + map.insert("starts_with".into(), Value::NativeFunction(Rc::new(string::starts_with))); + map.insert("ends_with".into(), Value::NativeFunction(Rc::new(string::ends_with))); +} diff --git a/aster-core/src/interpreter/builtins/os.rs b/aster-core/src/runtime/builtins/os.rs similarity index 88% rename from aster-core/src/interpreter/builtins/os.rs rename to aster-core/src/runtime/builtins/os.rs index d53e863..6d36c38 100644 --- a/aster-core/src/interpreter/builtins/os.rs +++ b/aster-core/src/runtime/builtins/os.rs @@ -1,7 +1,7 @@ //! 标准库:os(clock) use crate::error::RuntimeError; -use crate::interpreter::{Runtime, Value}; +use crate::runtime::{Runtime, Value}; pub fn clock(_interp: &mut dyn Runtime, _args: Vec) -> Result { Ok(Value::Number( diff --git a/aster-core/src/interpreter/builtins/string.rs b/aster-core/src/runtime/builtins/string.rs similarity index 99% rename from aster-core/src/interpreter/builtins/string.rs rename to aster-core/src/runtime/builtins/string.rs index 484eed2..b2c6db2 100644 --- a/aster-core/src/interpreter/builtins/string.rs +++ b/aster-core/src/runtime/builtins/string.rs @@ -1,7 +1,7 @@ //! 标准库:string(split, trim, substring, replace, contains, upper, lower, starts_with, ends_with) use crate::error::RuntimeError; -use crate::interpreter::{Runtime, Value}; +use crate::runtime::{Runtime, Value}; use std::cell::RefCell; use std::rc::Rc; diff --git a/aster-core/src/runtime/mod.rs b/aster-core/src/runtime/mod.rs new file mode 100644 index 0000000..5bf6bce --- /dev/null +++ b/aster-core/src/runtime/mod.rs @@ -0,0 +1,191 @@ +pub mod builtins; + +use std::collections::HashMap; +use std::rc::Rc; +use std::cell::RefCell; +use std::fmt; + +/// Trait abstracting runtime services that native functions may need. +pub trait Runtime { + fn require(&mut self, path: &str) -> Result; +} + +pub type NativeFn = Rc) -> Result>; + +// ============================================================================ +// Compiled function types +// ============================================================================ + +#[derive(Debug, Clone)] +pub struct FunctionProto { + pub name: Option, + pub arity: u8, + pub code: Vec, + pub constants: Vec, + pub protos: Vec>, + pub upvalue_count: u8, + pub upvalues: Vec<(bool, u8)>, // (is_local, index) + pub lines: Vec<(usize, usize)>, // (bytecode_offset, source_line) +} + +impl FunctionProto { + pub fn new(name: Option) -> Self { + Self { + name, + arity: 0, + code: Vec::new(), + constants: Vec::new(), + protos: Vec::new(), + upvalue_count: 0, + upvalues: Vec::new(), + lines: Vec::new(), + } + } + + pub fn add_constant(&mut self, val: Value) -> u16 { + for (i, c) in self.constants.iter().enumerate() { + if values_eq(c, &val) { + return i as u16; + } + } + let idx = self.constants.len(); + self.constants.push(val); + idx as u16 + } +} + +fn values_eq(a: &Value, b: &Value) -> bool { + match (a, b) { + (Value::Number(x), Value::Number(y)) => (x - y).abs() < f64::EPSILON, + (Value::String(x), Value::String(y)) => x == y, + (Value::Bool(x), Value::Bool(y)) => x == y, + (Value::Nil, Value::Nil) => true, + _ => false, + } +} + +/// An upvalue — a reference to a local variable in an enclosing function. +#[derive(Debug, Clone)] +pub struct UpvalueObj { + pub location: usize, + pub closed: Option, +} + +/// A runtime closure: compiled function proto + captured upvalues. +#[derive(Debug, Clone)] +pub struct Closure { + pub proto: Rc, + pub upvalues: Vec>>, +} + +// ============================================================================ +// Value +// ============================================================================ + +#[derive(Clone)] +pub enum Value { + Number(f64), + String(String), + Bool(bool), + Nil, + Object(Rc>>), + Array(Rc>>), + Function(Rc), + NativeFunction(NativeFn), +} + +impl Value { + pub fn get(&self, key: &str) -> Option { + match self { + Value::Object(obj) => obj.borrow().get(key).cloned(), + _ => None, + } + } + + pub fn set(&self, key: &str, val: Value) -> Result<(), crate::error::RuntimeError> { + match self { + Value::Object(obj) => { + obj.borrow_mut().insert(key.to_string(), val); + Ok(()) + }, + _ => Err(crate::error::RuntimeError::RuntimeError { + message: "Only objects have properties".to_string(), + token: None, + }), + } + } +} + +impl std::fmt::Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Value::Number(n) => write!(f, "Number({:?})", n), + Value::String(s) => write!(f, "String({:?})", s), + Value::Bool(b) => write!(f, "Bool({:?})", b), + Value::Nil => write!(f, "Nil"), + Value::Object(_) => write!(f, "Object(...)"), + Value::Array(_) => write!(f, "Array(...)"), + Value::Function(_) => write!(f, "Function(...)"), + Value::NativeFunction(_) => write!(f, "NativeFunction(...)"), + } + } +} + +impl Value { + fn fmt_element(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Value::String(s) => write!(f, "\"{}\"", s), + other => write!(f, "{}", other), + } + } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Value::Number(n) => write!(f, "{}", n), + Value::String(s) => write!(f, "{}", s), + Value::Bool(b) => write!(f, "{}", b), + Value::Nil => write!(f, "nil"), + Value::Object(obj) => { + let obj = obj.borrow(); + write!(f, "{{ ")?; + for (key, value) in obj.iter() { + write!(f, "{}: ", key)?; + value.fmt_element(f)?; + write!(f, ", ")?; + } + write!(f, "}}") + }, + Value::Array(arr) => { + let arr = arr.borrow(); + write!(f, "[")?; + for (i, val) in arr.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + val.fmt_element(f)?; + } + write!(f, "]") + }, + Value::Function(_) => write!(f, ""), + Value::NativeFunction(_) => write!(f, ""), + } + } +} + +/// `require()` builtin — thin wrapper that delegates to `Runtime::require`. +pub fn require_fn(runtime: &mut dyn Runtime, args: Vec) -> Result { + let path_str = match args.first() { + Some(Value::String(s)) => s.clone(), + Some(other) => return Err(crate::error::RuntimeError::RuntimeError { + message: format!("require() expects a string argument, got {}", other), + token: None, + }), + None => return Err(crate::error::RuntimeError::RuntimeError { + message: "require() expects 1 argument (string path)".into(), + token: None, + }), + }; + runtime.require(&path_str) +} diff --git a/aster-core/src/vm/compiler.rs b/aster-core/src/vm/compiler.rs index c58de13..fe7d748 100644 --- a/aster-core/src/vm/compiler.rs +++ b/aster-core/src/vm/compiler.rs @@ -7,67 +7,12 @@ use crate::ast::*; use crate::ast::expr::{Literal, UnaryOp, BinaryOp, LogicalOp, AssignOp}; use crate::error::RuntimeError; -use crate::interpreter::Value; +use crate::runtime::{Value, FunctionProto}; use super::opcode::*; use std::rc::Rc; use std::cell::RefCell; -// ============================================================================ -// FunctionProto — compiled function blueprint -// ============================================================================ - -#[derive(Debug, Clone)] -pub struct FunctionProto { - pub name: Option, - pub arity: u8, - pub code: Vec, - pub constants: Vec, - /// Nested function protos (for closures and function declarations) - pub protos: Vec>, - pub upvalue_count: u8, - /// Upvalue descriptors for this function (for Closure opcode emission) - pub upvalues: Vec<(bool, u8)>, // (is_local, index) - pub lines: Vec<(usize, usize)>, // (bytecode_offset, source_line) -} - -impl FunctionProto { - pub fn new(name: Option) -> Self { - Self { - name, - arity: 0, - code: Vec::new(), - constants: Vec::new(), - protos: Vec::new(), - upvalue_count: 0, - upvalues: Vec::new(), - lines: Vec::new(), - } - } - - fn add_constant(&mut self, val: Value) -> u16 { - // Check for existing identical constant - for (i, c) in self.constants.iter().enumerate() { - if values_eq(c, &val) { - return i as u16; - } - } - let idx = self.constants.len(); - self.constants.push(val); - idx as u16 - } -} - -fn values_eq(a: &Value, b: &Value) -> bool { - match (a, b) { - (Value::Number(x), Value::Number(y)) => (x - y).abs() < f64::EPSILON, - (Value::String(x), Value::String(y)) => x == y, - (Value::Bool(x), Value::Bool(y)) => x == y, - (Value::Nil, Value::Nil) => true, - _ => false, - } -} - // ============================================================================ // Compiler // ============================================================================ @@ -536,7 +481,7 @@ impl Compiler { self.function.code.push(compound_op as u8); } else { let name_idx = self.add_string_constant(name); - emit_u16(&mut self.function.code, OpCode::CompoundAssignProp, name_idx); + emit_u16(&mut self.function.code, OpCode::CompoundAssignGlobal, name_idx); self.function.code.push(compound_op as u8); } } @@ -558,16 +503,11 @@ impl Compiler { emit_u16(&mut self.function.code, OpCode::SetProperty, name_idx); } else { // Compound property set: object.name op= value - // Strategy: load object, dup, get property as current value, - // load rhs, apply op, set property + // CompoundAssignProp handler does get_property internally self.compile_expr(object)?; - self.emit_op(OpCode::Dup); - let name_idx = self.add_string_constant(name); - emit_u16(&mut self.function.code, OpCode::GetProperty, name_idx); // current value self.compile_expr(value)?; // rhs let compound_op = assign_op_to_compound(op); - self.function.code.push(compound_op as u8); - // Now stack: object, current_val, rhs → set property + let name_idx = self.add_string_constant(name); emit_u16(&mut self.function.code, OpCode::CompoundAssignProp, name_idx); self.function.code.push(compound_op as u8); } diff --git a/aster-core/src/vm/opcode.rs b/aster-core/src/vm/opcode.rs index 6c4eabc..3f379ff 100644 --- a/aster-core/src/vm/opcode.rs +++ b/aster-core/src/vm/opcode.rs @@ -79,12 +79,13 @@ pub enum OpCode { CompoundAssignProp = 40, // operand: u16 name idx + u8 compound-op tag CompoundAssignIndex = 41, // operand: u8 compound-op tag CompoundAssignUpvalue = 44, // operand: u8 upvalue idx + u8 compound-op tag + CompoundAssignGlobal = 45, // operand: u16 name idx + u8 compound-op tag } impl OpCode { pub fn from_u8(byte: u8) -> Option { match byte { - 0..=41 | 44 => Some(unsafe { std::mem::transmute::(byte) }), + 0..=41 | 44..=45 => Some(unsafe { std::mem::transmute::(byte) }), 42 => Some(OpCode::LoadUpvalue), 43 => Some(OpCode::StoreUpvalue), _ => None, @@ -92,7 +93,7 @@ impl OpCode { } /// Total number of distinct opcodes - pub const COUNT: usize = 45; + pub const COUNT: usize = 46; } /// Compound assignment operator tags (used as operand byte after @@ -171,3 +172,5 @@ pub const SIZE_OP: usize = 1; pub const SIZE_U8: usize = 2; /// Size in bytes of an opcode with a u16/i16 operand. pub const SIZE_U16: usize = 3; +/// Size in bytes of an opcode with a u16 + u8 operand. +pub const SIZE_U16_PLUS1: usize = 4; diff --git a/aster-core/src/interpreter/tests.rs b/aster-core/src/vm/tests.rs similarity index 98% rename from aster-core/src/interpreter/tests.rs rename to aster-core/src/vm/tests.rs index 4e5d48f..5b4bf79 100644 --- a/aster-core/src/interpreter/tests.rs +++ b/aster-core/src/vm/tests.rs @@ -1,8 +1,10 @@ -use crate::interpreter::{Interpreter, Value}; +use crate::runtime::Value; use crate::lexer::Lexer; use crate::parser::Parser; +use crate::vm::compiler::Compiler; +use crate::vm::vm::Vm; -/// Full pipeline: source → tokens → ast → interpret → last expression value. +/// Full pipeline: source → tokens → ast → compile → vm → last expression value. /// Wraps in `let __result = ;` so we can read the value back. fn eval_expr(input: &str) -> Value { let wrapped = format!("let __result = {};", input); @@ -10,25 +12,27 @@ fn eval_expr(input: &str) -> Value { let mut parser = Parser::new(tokens); let (stmts, errors) = parser.parse(); assert!(errors.is_empty(), "Parse errors: {:?}", errors); - let mut interpreter = Interpreter::new(); - interpreter.interpret(stmts).expect("Runtime error"); - interpreter.env.borrow().get("__result").expect("No __result in env") + let proto = Compiler::compile(&stmts).expect("Compile error"); + let mut vm = Vm::new(); + vm.run(std::rc::Rc::new(proto)).expect("Runtime error"); + vm.get_global("__result").expect("No __result in globals") } -/// Full pipeline for multiple statements. Returns the interpreter for env inspection. -fn run(input: &str) -> Interpreter { +/// Full pipeline for multiple statements. Returns the VM for state inspection. +fn run(input: &str) -> Vm { let (tokens, _) = Lexer::new(input).tokenize(); let mut parser = Parser::new(tokens); let (stmts, errors) = parser.parse(); assert!(errors.is_empty(), "Parse errors: {:?}", errors); - let mut interpreter = Interpreter::new(); - interpreter.interpret(stmts).expect("Runtime error"); - interpreter + let proto = Compiler::compile(&stmts).expect("Compile error"); + let mut vm = Vm::new(); + vm.run(std::rc::Rc::new(proto)).expect("Runtime error"); + vm } -/// Helper: get a variable from the interpreter's environment. -fn get_var(interp: &Interpreter, name: &str) -> Value { - interp.env.borrow().get(name).unwrap_or(Value::Nil) +/// Helper: get a variable from the VM's globals. +fn get_var(vm: &Vm, name: &str) -> Value { + vm.get_global(name).unwrap_or(Value::Nil) } /// Helper: assert a number value. diff --git a/aster-core/src/vm/vm.rs b/aster-core/src/vm/vm.rs index d464892..e0ed9f8 100644 --- a/aster-core/src/vm/vm.rs +++ b/aster-core/src/vm/vm.rs @@ -4,11 +4,11 @@ //! call frames. Implements the `Runtime` trait for native function support. use crate::error::RuntimeError; -use crate::interpreter::{Value, Runtime}; +use crate::runtime::{Value, Runtime, FunctionProto, Closure, UpvalueObj}; use crate::lexer::Lexer; use crate::parser::Parser; use super::opcode::*; -use super::compiler::{Compiler, FunctionProto}; +use super::compiler::Compiler; use std::cell::RefCell; use std::collections::HashMap; @@ -18,22 +18,6 @@ use std::rc::Rc; // VM data structures // ============================================================================ -/// An upvalue — a reference to a local variable in an enclosing function. -#[derive(Debug, Clone)] -struct UpvalueObj { - /// Stack index where the value lives, or usize::MAX if closed - location: usize, - /// The value, if it has been moved off the stack (closed) - closed: Option, -} - -/// A runtime closure: compiled function proto + captured upvalues. -#[derive(Debug, Clone)] -struct Closure { - proto: Rc, - upvalues: Vec>>, -} - /// A call frame on the VM stack. struct CallFrame { closure: Rc, @@ -62,9 +46,6 @@ pub struct Vm { /// Open upvalues (tracked so closures share the same upvalue object) open_upvalues: Vec>>, - - /// VM-compiled closures: maps Function Rc pointer → Closure data - closures: RefCell>>, } impl Vm { @@ -85,7 +66,6 @@ impl Vm { .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|_| ".".to_string()), open_upvalues: Vec::new(), - closures: RefCell::new(HashMap::new()), } } @@ -101,6 +81,12 @@ impl Vm { self.run(Rc::new(proto)) } + /// Look up a global variable by name (for test inspection). + pub fn get_global(&self, name: &str) -> Option { + self.globals.borrow().get(name).map(|(v, _)| v.clone()) + .or_else(|| self.builtins.borrow().get(name).cloned()) + } + /// Execute a compiled FunctionProto. pub fn run(&mut self, proto: Rc) -> Result<(), RuntimeError> { let closure = Rc::new(Closure { @@ -496,15 +482,7 @@ impl Vm { } let closure = Rc::new(Closure { proto, upvalues }); - let func = Rc::new(crate::interpreter::Function { - params: Vec::new(), - body: Vec::new(), - env: Rc::new(RefCell::new(crate::interpreter::Env::new(None))), - name: None, - }); - let key = Rc::as_ptr(&func) as *const crate::interpreter::Function; - self.closures.borrow_mut().insert(key, closure); - self.stack.push(Value::Function(func)); + self.stack.push(Value::Function(closure)); self.advance_ip_to(off); } @@ -527,7 +505,15 @@ impl Vm { // --- For-in --- OpCode::ForInInit => { let iterable = self.stack.pop().unwrap(); - let items = self.for_in_items(&iterable); + let items = match &iterable { + 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: format!("for-in requires an array, object, or string, got {}", iterable), + token: None, + }), + }; self.stack.push(Value::Array(Rc::new(RefCell::new(items)))); self.advance_ip(SIZE_OP); } @@ -632,6 +618,33 @@ impl Vm { self.stack.push(result); self.advance_ip(SIZE_U8 + 1); } + OpCode::CompoundAssignGlobal => { + let name_idx = read_u16(code, ip) as usize; + let op_tag = code[ip + 3]; + let compound_op = CompoundOp::from_u8(op_tag).unwrap(); + let rhs = self.stack.pop().unwrap(); + let name = proto_string(&proto, name_idx)?; + let lhs = self.globals.borrow().get(&name) + .map(|(v, _)| v.clone()) + .or_else(|| self.builtins.borrow().get(&name).cloned()) + .ok_or_else(|| RuntimeError::RuntimeError { + message: format!("Undefined variable '{}'", name), + token: None, + })?; + let result = self.apply_compound_op(lhs, rhs, compound_op)?; + // Check const flag (globals tuple stores (value, mutable)) + if let Some((_, mutable)) = self.globals.borrow().get(&name) { + if !*mutable { + return Err(RuntimeError::RuntimeError { + message: format!("Cannot reassign constant '{}'", name), + token: None, + }); + } + } + self.globals.borrow_mut().insert(name, (result.clone(), true)); + self.stack.push(result); + self.advance_ip(SIZE_U16_PLUS1); + } } } } @@ -678,22 +691,13 @@ impl Vm { self.stack.push(result); self.advance_ip(SIZE_U8); } - Value::Function(_) => { - // Look up the VM-compiled closure - let func_ptr = match &self.stack[callee_idx] { - Value::Function(f) => Rc::as_ptr(f) as *const crate::interpreter::Function, - _ => unreachable!(), - }; - let closure = self.closures.borrow().get(&func_ptr).cloned().ok_or_else(|| RuntimeError::RuntimeError { - message: "VM: call to non-VM function (tree-walker function not supported in VM)".into(), - token: None, - })?; - + Value::Function(closure) => { + let closure = Rc::clone(closure); // Advance caller's IP past the Call instruction before pushing new frame self.frame_mut().ip += SIZE_U8; // Callee is at callee_idx, args start at callee_idx+1 - let base = callee_idx + 1; // first param is here + let base = callee_idx + 1; self.frames.push(CallFrame { closure, ip: 0, @@ -748,6 +752,78 @@ impl Vm { }, Value::String(s) => match name { "length" => Ok(Value::Number(s.chars().count() as f64)), + "upper" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::upper(runtime, all_args) + }))) + } + "lower" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::lower(runtime, all_args) + }))) + } + "trim" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::trim(runtime, all_args) + }))) + } + "substring" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::substring(runtime, all_args) + }))) + } + "replace" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::replace(runtime, all_args) + }))) + } + "contains" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::contains(runtime, all_args) + }))) + } + "starts_with" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::starts_with(runtime, all_args) + }))) + } + "ends_with" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::ends_with(runtime, all_args) + }))) + } + "split" => { + let s = s.clone(); + Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn Runtime, args: Vec| { + let mut all_args = vec![Value::String(s.clone())]; + all_args.extend(args); + crate::runtime::builtins::string::split(runtime, all_args) + }))) + } _ => Err(RuntimeError::RuntimeError { message: format!("String has no property '{}'", name), token: None, @@ -922,7 +998,23 @@ impl Vm { (Value::Bool(x), Value::Bool(y)) => x == y, (Value::Number(x), Value::Number(y)) => x == y, (Value::String(x), Value::String(y)) => x == y, - _ => false, // Simplified — full structural equality omitted for now + (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, } } @@ -988,14 +1080,6 @@ impl Vm { // For-in helper // ======================================================================== - fn for_in_items(&self, collection: &Value) -> Vec { - match collection { - 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(), - _ => Vec::new(), - } - } } impl Runtime for Vm { @@ -1080,12 +1164,7 @@ impl Runtime for Vm { } } - // 11. Transfer closures from module VM to parent (so exported fns are callable) - for (key, closure) in module_vm.closures.borrow().iter() { - self.closures.borrow_mut().insert(*key, Rc::clone(closure)); - } - - // 12. Cache in parent + // 11. Cache in parent self.module_cache.borrow_mut().insert(resolved, exports_obj.clone()); Ok(exports_obj) @@ -1107,43 +1186,16 @@ fn proto_string(proto: &FunctionProto, idx: usize) -> Result>>) { let mut m = map.borrow_mut(); - - // io - let mut io = HashMap::new(); - io.insert("print".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::io::print))); - io.insert("input".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::io::input))); - m.insert("io".into(), Value::Object(Rc::new(RefCell::new(io)))); - m.insert("print".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::io::print))); - m.insert("input".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::io::input))); - - // os - let mut os = HashMap::new(); - os.insert("clock".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::os::clock))); - m.insert("os".into(), Value::Object(Rc::new(RefCell::new(os)))); - m.insert("clock".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::os::clock))); - - // core - m.insert("len".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::core::len))); - m.insert("typeof".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::core::typeof_fn))); - m.insert("push".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::core::push))); - m.insert("pop".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::core::pop))); - - // require (VM version) - m.insert("require".into(), Value::NativeFunction(Rc::new(crate::interpreter::module::require_fn))); - - // string - m.insert("split".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::split))); - m.insert("trim".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::trim))); - m.insert("substring".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::substring))); - m.insert("replace".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::replace))); - m.insert("contains".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::contains))); - m.insert("upper".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::upper))); - m.insert("lower".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::lower))); - m.insert("starts_with".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::starts_with))); - m.insert("ends_with".into(), Value::NativeFunction(Rc::new(crate::interpreter::builtins::string::ends_with))); + crate::runtime::builtins::register_all(&mut *m); + // require is VM-specific + m.insert("require".into(), Value::NativeFunction(Rc::new(crate::runtime::require_fn))); } + +#[cfg(test)] +#[path = "tests.rs"] +mod tests; diff --git a/aster/src/main.rs b/aster/src/main.rs index d992125..17c5021 100644 --- a/aster/src/main.rs +++ b/aster/src/main.rs @@ -3,21 +3,13 @@ use std::fs; fn main() { let args: Vec = env::args().collect(); - let use_vm = args.iter().any(|a| a == "--vm"); - let file_args: Vec<&String> = args.iter().filter(|a| *a != "--vm").collect(); - match file_args.len() { + match args.len() { 1 => aster_core::run_repl(), 2 => { - let filename = file_args[1]; + let filename = &args[1]; match fs::read_to_string(filename) { - Ok(src) => { - if use_vm { - aster_core::run_file_vm(filename, src) - } else { - aster_core::run_file(filename, src) - } - } + Ok(src) => aster_core::run_file(filename, src), Err(e) => { eprintln!("Error reading file '{}': {}", filename, e); std::process::exit(1); @@ -25,7 +17,7 @@ fn main() { } } _ => { - eprintln!("Usage: aster [--vm] [script]"); + eprintln!("Usage: aster [script]"); std::process::exit(1); } }