refactor: VM-only architecture — remove tree-walking interpreter

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<Function>) → Value::Function(Rc<Closure>)
  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 <noreply@anthropic.com>
This commit is contained in:
0264408
2026-06-26 10:31:32 +08:00
co-authored by Claude
parent abe0844816
commit fa6ea512b3
19 changed files with 432 additions and 1471 deletions
+4 -64
View File
@@ -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<String>,
pub arity: u8,
pub code: Vec<u8>,
pub constants: Vec<Value>,
/// Nested function protos (for closures and function declarations)
pub protos: Vec<Rc<FunctionProto>>,
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<String>) -> 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);
}