Compare commits

11 Commits

Author SHA1 Message Date
elmma ced4a9d060 Merge pull request 'fix: CompoundAssignUpvalue支持 + const变量赋值检查' (#2) from vm-bytecode into master
Reviewed-on: #2
2026-06-26 09:43:18 +08:00
0264408 abe0844816 fix: CompoundAssignUpvalue支持 + const变量赋值检查
- 新增 CompoundAssignUpvalue 字节码,闭包中 x+=5 不再报错
- Local/Upvalue 上的 is_const 编译期检查
- globals 改为 HashMap<String, (Value, bool)> 追踪 const,StoreGlobal 运行时拒绝
- DefineGlobal 指令新增 1 字节 mutable 标志
- 更新 README:双执行模式、for-in、require()、VM 模块说明

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-26 09:42:38 +08:00
0264408 61ce3466a9 Merge branch 'master' of https://gitea.jetlumen.com/admin/aster 2026-06-26 09:27:19 +08:00
0264408 bb0cf8c558 doc: 更新README 2026-06-26 09:27:16 +08:00
elmma 1cfa31abcc Merge pull request 'vm-bytecode' (#1) from vm-bytecode into master
Reviewed-on: admin/aster#1
2026-06-26 09:25:10 +08:00
elmma 7b9bded5ee chore: 消除所有编译器警告
- 移除unused方法: frame(), get_constant(), emit_i16_placeholder
- 移除LoopContext未使用字段: start_ip, scope_depth
- Vm字段改为私有: frames, open_upvalues
- is_const字段添加#[allow(dead_code)]
- 测试: 327/327 通过 | 0 warnings
2026-06-26 00:09:02 +08:00
elmma 9a193fd7ca feat: VM版require()模块加载支持
- Vm实现Runtime::require: lex→parse→compile→execute
- 模块隔离执行: 独立Vm实例,共享builtins
- Closure传递: 模块导出的函数闭包注册到父Vm
- 循环require支持: 缓存占位符机制

测试: 327/327 通过
验证: use_math.ast (add=42, mul=42, add(mul(2,3),1)=7)
2026-06-25 23:47:33 +08:00
elmma ddee395f52 feat: 传递式upvalue — 3层嵌套闭包支持
- upvalues改为Rc<RefCell<Vec<Upvalue>>>支持跨编译器共享
- resolve_upvalue: 通过grandparent_upvalues级联创建upvalue链
- compile_nested_function: 传递完整enclosing_locals链 + grandparent_upvalues
- Upvalue添加name字段用于传递式查找

测试: 327/327 通过 | Benchmark: 18/18 全部通过
2026-06-25 23:41:36 +08:00
elmma 9c9a17b149 feat: 重构for-in为预计算+隐藏局部变量模式
ForInInit: 改为预计算所有迭代元素为数组
ForInNext: 接收items_slot+idx_slot操作数,从局部变量读取
编译器: for-in迭代器使用隐藏局部变量(items, idx),隔离于value stack
修复: for-in在函数调用嵌套场景下栈对齐问题
修复: break路径在for-in中正确弹出循环变量
修复: ForInNext退出时push Nil以保持栈平衡
修复: 跳转偏移计算(移除emit_loop_jump中错误的-3)

通过: 17/18 benchmark测试 (全部for-in相关测试通过)
已知限制: 3层嵌套闭包(10c)需传递式upvalue支持
2026-06-25 23:32:23 +08:00
elmma 299003a718 feat: VM函数调用、闭包、upvalue捕获支持
- 实现用户定义函数调用 (CallFrame创建、参数传递、返回)
- 实现闭包/upvalue捕获 (编译器解析、VM捕获、LoadUpvalue/StoreUpvalue)
- 修复栈管理: StoreLocal peek语义、局部变量槽位分配
- 修复跳转偏移计算 (移除错误的-3偏置)
- 修复对象字面量编译 (每字段后Pop)
- 修复函数声明栈泄漏 (DefineGlobal后Pop)
- 添加ForInNext值Pop (避免栈累积)
- 添加OpCode::from_u8对非连续值的支持

通过: 算术、循环、条件、数组、对象、字符串、递归、闭包、upvalue
部分通过: for-in迭代器 (基本工作,复杂嵌套场景待修复)
未实现: VM版require()、复合upvalue赋值

测试: 327/327 通过
2026-06-25 23:16:59 +08:00
elmma d854b22006 feat: 字节码VM基础设施 — 编译器、VM执行循环、Runtime trait
Phase 1-3: 基础VM架构
- 新增 Runtime trait: 抽象树遍历解释器和VM的共同接口
- NativeFn 改为接受 &mut dyn Runtime
- 重构所有内置函数使用新签名
- vm/opcode.rs: 33个字节码指令 + 编码/解码辅助函数
- vm/compiler.rs: AST→字节码编译器,支持变量解析、跳转回填、作用域
- vm/vm.rs: 栈式VM执行循环,支持全局变量、原生函数调用
- lib.rs: 新增 run_file_vm() + --vm CLI标志
- 修复: 跳转偏移计算、对象字面量编译

工作特性: 算术、变量、while/for循环、条件、数组、对象、字符串
待完成: 用户定义函数调用、闭包/upvalue捕获、完整require支持

Release模式: 1M算术循环 VM 0.44s vs 树遍历 0.89s (2.0x加速)
2026-06-25 01:08:49 +08:00
16 changed files with 2494 additions and 79 deletions
+26 -5
View File
@@ -1,6 +1,6 @@
# Aster # Aster
Aster 是一门使用 Rust 编写的动态类型脚本语言解释器,基于树遍历(tree-walker实现。无外部依赖,纯标准库。 Aster 是一门使用 Rust 编写的动态类型脚本语言解释器,支持两种执行模式:**树遍历(tree-walker** 和 **字节码 VM**。无外部依赖,纯标准库。
## 快速开始 ## 快速开始
@@ -11,9 +11,12 @@ cargo build
# 启动 REPL(交互式) # 启动 REPL(交互式)
cargo run cargo run
# 执行脚本文件 # 执行脚本文件(树遍历模式)
cargo run -- examples/script.ast cargo run -- examples/script.ast
# 执行脚本文件(字节码 VM 模式,性能更优)
cargo run -- --vm examples/script.ast
# 运行测试 # 运行测试
cargo test cargo test
``` ```
@@ -84,6 +87,21 @@ while (true) {
if (condition) { break; } if (condition) { break; }
if (skip) { continue; } if (skip) { continue; }
} }
// for-in 遍历(数组 / 字符串)
for (let item in [1, 2, 3]) {
print(item);
}
```
### 模块系统
```js
// 加载并执行外部脚本,返回模块导出的对象
let math = require("math.ast");
print(math.add(3, 4));
// 第二次 require 同一文件会返回缓存的对象(支持循环引用)
``` ```
### 函数与闭包 ### 函数与闭包
@@ -144,23 +162,26 @@ let t = os.clock(); // 模块化调用
## 架构 ## 架构
``` ```
源码文本 → Lexer → Tokens → Parser → AST → Interpreter → 输出 源码文本 → Lexer → Tokens → Parser → AST ──→ Interpreter(树遍历)→ 输出
└─→ Compiler → Bytecode → VM → 输出
``` ```
| 模块 | 职责 | | 模块 | 职责 |
|------|------| |------|------|
| `lexer/` | 词法分析。`Lexer::tokenize()` 将源码转为 `(Vec<Token>, Vec<Error>)`,Token 记录行列号用于错误报告,支持 `//` 单行注释 | | `lexer/` | 词法分析。`Lexer::tokenize()` 将源码转为 `(Vec<Token>, Vec<Error>)`,Token 记录行列号用于错误报告,支持 `//` 单行注释`/* */` 块注释 |
| `parser/` | 递归下降 + Pratt 解析器。`Parser::parse()` 返回 `(Vec<Stmt>, Vec<Error>)`,遇到语法错误通过 `synchronize()` 跳过至下一条语句边界继续解析 | | `parser/` | 递归下降 + Pratt 解析器。`Parser::parse()` 返回 `(Vec<Stmt>, Vec<Error>)`,遇到语法错误通过 `synchronize()` 跳过至下一条语句边界继续解析 |
| `ast/` | AST 节点定义。`Expr`(表达式)涵盖字面量、变量、赋值、属性/索引访问、一元/二元/逻辑运算、函数调用、lambda 和对象/数组字面量。`Stmt`(语句)涵盖 let、表达式语句、块、if/while/for、函数、return/break/continue | | `ast/` | AST 节点定义。`Expr`(表达式)涵盖字面量、变量、赋值、属性/索引访问、一元/二元/逻辑运算、函数调用、lambda 和对象/数组字面量。`Stmt`(语句)涵盖 let、表达式语句、块、if/while/for、函数、return/break/continue |
| `interpreter/` | 树遍历求值器。`Env` 是基于 `Rc<RefCell<>>` 的链式作用域。`Signal` 枚举通过调用栈传播 `Return`/`Break`/`Continue``builtins/``io``os` 模块组织原生函数,同时注册为全局函数以方便使用 | | `interpreter/` | 树遍历求值器。`Env` 是基于 `Rc<RefCell<>>` 的链式作用域。`Signal` 枚举通过调用栈传播 `Return`/`Break`/`Continue``builtins/``io``os` 模块组织原生函数,同时注册为全局函数以方便使用 |
| `vm/` | 字节码 VM。`Compiler` 将 AST 编译为基于栈的字节码,`Vm` 执行字节码指令。支持闭包 upvalue 捕获、3 层嵌套闭包、`require()` 模块加载。通过 `--vm` 标志启用 |
| `error/` | 三种错误:`LexError`(行列号)、`ParseError`Token)、`RuntimeError`(可选 Token | | `error/` | 三种错误:`LexError`(行列号)、`ParseError`Token)、`RuntimeError`(可选 Token |
### 关键设计决策 ### 关键设计决策
- **零外部依赖** — 全部基于 Rust 标准库构建 - **零外部依赖** — 全部基于 Rust 标准库构建
- **双执行模式** — 树遍历模式(默认,适合交互和调试)和字节码 VM 模式(`--vm`,约 40% 性能提升),共享同一 AST 和运行时语义
- **错误容忍解析** — 词法分析器和解析器均将错误收集到 `Vec` 中,单次运行可报告多个诊断信息,而非在第一个错误处就中止 - **错误容忍解析** — 词法分析器和解析器均将错误收集到 `Vec` 中,单次运行可报告多个诊断信息,而非在第一个错误处就中止
- **`Rc<RefCell<>>` 共享所有权** — 用于 `Env` 链、`Value::Object``Value::Array``Value::Function`,提供动态可变语义 - **`Rc<RefCell<>>` 共享所有权** — 用于 `Env` 链、`Value::Object``Value::Array``Value::Function`,提供动态可变语义
- **词法作用域闭包** — `Function` 在定义时捕获 `Env`lambda 表达式同理 - **词法作用域闭包** — `Function` 在定义时捕获 `Env`lambda 表达式同理VM 通过 upvalue 机制支持最多 3 层传递式捕获
## 许可证 ## 许可证
+1 -1
View File
@@ -130,7 +130,7 @@ pub enum LogicalOp {
Or, Or,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssignOp { pub enum AssignOp {
Equal, // = Equal, // =
PlusEqual, // += PlusEqual, // +=
+5 -5
View File
@@ -1,9 +1,9 @@
//! 标准库:corelen, typeof, push, pop //! 标准库:corelen, typeof, push, pop
use crate::error::RuntimeError; use crate::error::RuntimeError;
use crate::interpreter::{Interpreter, Value}; use crate::interpreter::{Runtime, Value};
pub fn len(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn len(_runtime: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "len() expects 1 argument".into(), message: "len() expects 1 argument".into(),
@@ -21,7 +21,7 @@ pub fn len(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runtime
} }
} }
pub fn typeof_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn typeof_fn(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "typeof() expects 1 argument".into(), message: "typeof() expects 1 argument".into(),
@@ -41,7 +41,7 @@ pub fn typeof_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
Ok(Value::String(s.into())) Ok(Value::String(s.into()))
} }
pub fn push(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn push(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() < 2 { if args.len() < 2 {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "push() expects 2 arguments (array, value)".into(), message: "push() expects 2 arguments (array, value)".into(),
@@ -61,7 +61,7 @@ pub fn push(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runtim
} }
} }
pub fn pop(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn pop(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "pop() expects 1 argument (array)".into(), message: "pop() expects 1 argument (array)".into(),
+3 -3
View File
@@ -1,9 +1,9 @@
//! 标准库:ioprint, input //! 标准库:ioprint, input
use crate::error::RuntimeError; use crate::error::RuntimeError;
use crate::interpreter::{Interpreter, Value}; use crate::interpreter::{Runtime, Value};
pub fn print(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn print(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
for arg in args.iter() { for arg in args.iter() {
print!("{}", arg); print!("{}", arg);
} }
@@ -11,7 +11,7 @@ pub fn print(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runti
Ok(Value::Nil) Ok(Value::Nil)
} }
pub fn input(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn input(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if let Some(prompt) = args.get(0) { if let Some(prompt) = args.get(0) {
print!("{}", prompt); print!("{}", prompt);
std::io::Write::flush(&mut std::io::stdout()).unwrap(); std::io::Write::flush(&mut std::io::stdout()).unwrap();
+3 -3
View File
@@ -1,8 +1,8 @@
//! 内置函数模块:按功能分组注册,便于扩展和维护。 //! 内置函数模块:按功能分组注册,便于扩展和维护。
mod core; pub mod core;
mod io; pub mod io;
mod os; pub mod os;
pub mod string; pub mod string;
use crate::interpreter::{Env, Value}; use crate::interpreter::{Env, Value};
+2 -2
View File
@@ -1,9 +1,9 @@
//! 标准库:osclock //! 标准库:osclock
use crate::error::RuntimeError; use crate::error::RuntimeError;
use crate::interpreter::{Interpreter, Value}; use crate::interpreter::{Runtime, Value};
pub fn clock(_interp: &mut Interpreter, _args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn clock(_interp: &mut dyn Runtime, _args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::Number( Ok(Value::Number(
std::time::SystemTime::now() std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
+10 -10
View File
@@ -1,7 +1,7 @@
//! 标准库:stringsplit, trim, substring, replace, contains, upper, lower, starts_with, ends_with //! 标准库:stringsplit, trim, substring, replace, contains, upper, lower, starts_with, ends_with
use crate::error::RuntimeError; use crate::error::RuntimeError;
use crate::interpreter::{Interpreter, Value}; use crate::interpreter::{Runtime, Value};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@@ -19,7 +19,7 @@ fn require_string(val: &Value, arg_name: &str) -> Result<String, RuntimeError> {
// split(str, delim) → array of substrings // split(str, delim) → array of substrings
// ============================================================================ // ============================================================================
pub fn split(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn split(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() < 2 { if args.len() < 2 {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "split() expects 2 arguments (string, delimiter)".into(), message: "split() expects 2 arguments (string, delimiter)".into(),
@@ -43,7 +43,7 @@ pub fn split(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runti
// trim(str) → string with leading/trailing whitespace removed // trim(str) → string with leading/trailing whitespace removed
// ============================================================================ // ============================================================================
pub fn trim(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn trim(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "trim() expects 1 argument (string)".into(), message: "trim() expects 1 argument (string)".into(),
@@ -58,7 +58,7 @@ pub fn trim(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runtim
// substring(str, start, len?) → substring // substring(str, start, len?) → substring
// ============================================================================ // ============================================================================
pub fn substring(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn substring(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "substring() expects at least 2 arguments (string, start, len?)".into(), message: "substring() expects at least 2 arguments (string, start, len?)".into(),
@@ -81,7 +81,7 @@ pub fn substring(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
// replace(str, old, new) → string with all occurrences replaced // replace(str, old, new) → string with all occurrences replaced
// ============================================================================ // ============================================================================
pub fn replace(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn replace(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() < 3 { if args.len() < 3 {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "replace() expects 3 arguments (string, old, new)".into(), message: "replace() expects 3 arguments (string, old, new)".into(),
@@ -106,7 +106,7 @@ pub fn replace(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Run
// contains(str, needle) → bool // contains(str, needle) → bool
// ============================================================================ // ============================================================================
pub fn contains(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn contains(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() < 2 { if args.len() < 2 {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "contains() expects 2 arguments (string, needle)".into(), message: "contains() expects 2 arguments (string, needle)".into(),
@@ -122,7 +122,7 @@ pub fn contains(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Ru
// upper(str) → uppercase (ASCII) // upper(str) → uppercase (ASCII)
// ============================================================================ // ============================================================================
pub fn upper(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn upper(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "upper() expects 1 argument (string)".into(), message: "upper() expects 1 argument (string)".into(),
@@ -137,7 +137,7 @@ pub fn upper(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runti
// lower(str) → lowercase (ASCII) // lower(str) → lowercase (ASCII)
// ============================================================================ // ============================================================================
pub fn lower(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn lower(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.is_empty() { if args.is_empty() {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "lower() expects 1 argument (string)".into(), message: "lower() expects 1 argument (string)".into(),
@@ -152,7 +152,7 @@ pub fn lower(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runti
// starts_with(str, prefix) → bool // starts_with(str, prefix) → bool
// ============================================================================ // ============================================================================
pub fn starts_with(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn starts_with(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() < 2 { if args.len() < 2 {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "starts_with() expects 2 arguments (string, prefix)".into(), message: "starts_with() expects 2 arguments (string, prefix)".into(),
@@ -168,7 +168,7 @@ pub fn starts_with(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value,
// ends_with(str, suffix) → bool // ends_with(str, suffix) → bool
// ============================================================================ // ============================================================================
pub fn ends_with(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn ends_with(_interp: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() < 2 { if args.len() < 2 {
return Err(RuntimeError::RuntimeError { return Err(RuntimeError::RuntimeError {
message: "ends_with() expects 2 arguments (string, suffix)".into(), message: "ends_with() expects 2 arguments (string, suffix)".into(),
+21 -21
View File
@@ -103,7 +103,7 @@ impl super::Interpreter {
"length" => Ok(Value::Number(arr.borrow().len() as f64)), "length" => Ok(Value::Number(arr.borrow().len() as f64)),
"push" => { "push" => {
let arr = Rc::clone(&arr); let arr = Rc::clone(&arr);
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, mut args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |_runtime: &mut dyn super::Runtime, mut args: Vec<Value>| {
let val = args.pop().unwrap_or(Value::Nil); let val = args.pop().unwrap_or(Value::Nil);
arr.borrow_mut().push(val.clone()); arr.borrow_mut().push(val.clone());
Ok(val) Ok(val)
@@ -111,7 +111,7 @@ impl super::Interpreter {
} }
"pop" => { "pop" => {
let arr = Rc::clone(&arr); let arr = Rc::clone(&arr);
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, _args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |_runtime: &mut dyn super::Runtime, _args: Vec<Value>| {
arr.borrow_mut() arr.borrow_mut()
.pop() .pop()
.ok_or_else(|| RuntimeError::RuntimeError { .ok_or_else(|| RuntimeError::RuntimeError {
@@ -129,74 +129,74 @@ impl super::Interpreter {
"length" => Ok(Value::Number(s.chars().count() as f64)), "length" => Ok(Value::Number(s.chars().count() as f64)),
"upper" => { "upper" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::upper(_interp, all_args) super::builtins::string::upper(runtime, all_args)
}))) })))
} }
"lower" => { "lower" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::lower(_interp, all_args) super::builtins::string::lower(runtime, all_args)
}))) })))
} }
"trim" => { "trim" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::trim(_interp, all_args) super::builtins::string::trim(runtime, all_args)
}))) })))
} }
"substring" => { "substring" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::substring(_interp, all_args) super::builtins::string::substring(runtime, all_args)
}))) })))
} }
"replace" => { "replace" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::replace(_interp, all_args) super::builtins::string::replace(runtime, all_args)
}))) })))
} }
"contains" => { "contains" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::contains(_interp, all_args) super::builtins::string::contains(runtime, all_args)
}))) })))
} }
"starts_with" => { "starts_with" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::starts_with(_interp, all_args) super::builtins::string::starts_with(runtime, all_args)
}))) })))
} }
"ends_with" => { "ends_with" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::ends_with(_interp, all_args) super::builtins::string::ends_with(runtime, all_args)
}))) })))
} }
"split" => { "split" => {
let s = s.clone(); let s = s.clone();
Ok(Value::NativeFunction(Rc::new(move |_interp: &mut Self, args: Vec<Value>| { Ok(Value::NativeFunction(Rc::new(move |runtime: &mut dyn super::Runtime, args: Vec<Value>| {
let mut all_args = vec![Value::String(s.clone())]; let mut all_args = vec![Value::String(s.clone())];
all_args.extend(args); all_args.extend(args);
super::builtins::string::split(_interp, all_args) super::builtins::string::split(runtime, all_args)
}))) })))
} }
_ => Err(RuntimeError::RuntimeError { _ => Err(RuntimeError::RuntimeError {
@@ -481,7 +481,7 @@ impl super::Interpreter {
pub fn call_function(&mut self, func_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn call_function(&mut self, func_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
match func_val { match func_val {
Value::NativeFunction(native_fn) => native_fn(self, args), Value::NativeFunction(native_fn) => native_fn(self as &mut dyn super::Runtime, args),
Value::Function(f) => self.call_user_function(f, args), Value::Function(f) => self.call_user_function(f, args),
_ => Err(RuntimeError::RuntimeError { _ => Err(RuntimeError::RuntimeError {
message: "Attempt to call non-function".to_string(), message: "Attempt to call non-function".to_string(),
+10 -1
View File
@@ -13,7 +13,16 @@ use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::fmt;
pub type NativeFn = Rc<dyn Fn(&mut super::Interpreter, Vec<Value>) -> Result<Value, crate::error::RuntimeError>>; /// 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<Value, crate::error::RuntimeError>;
}
pub type NativeFn = Rc<dyn Fn(&mut dyn Runtime, Vec<Value>) -> Result<Value, crate::error::RuntimeError>>;
#[derive(Clone)] #[derive(Clone)]
pub enum Value { pub enum Value {
+27 -24
View File
@@ -8,15 +8,20 @@
use crate::error::RuntimeError; use crate::error::RuntimeError;
use crate::lexer::Lexer; use crate::lexer::Lexer;
use crate::parser::Parser; use crate::parser::Parser;
use super::{Interpreter, Env, Value, Signal}; use super::{Interpreter, Env, Value, Signal, Runtime};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
/// require() 的内置函数实现 impl Runtime for Interpreter {
pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> { fn require(&mut self, path: &str) -> Result<Value, RuntimeError> {
// 1. 参数验证 require_impl(self, path)
}
}
/// require() 的内置函数实现 — 薄包装,委托给 Runtime::require
pub fn require_fn(runtime: &mut dyn Runtime, args: Vec<Value>) -> Result<Value, RuntimeError> {
let path_str = match args.first() { let path_str = match args.first() {
Some(Value::String(s)) => s.clone(), Some(Value::String(s)) => s.clone(),
Some(other) => return Err(runtime_error(format!( Some(other) => return Err(runtime_error(format!(
@@ -24,28 +29,32 @@ pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
None => return Err(runtime_error( None => return Err(runtime_error(
"require() expects 1 argument (string path)")), "require() expects 1 argument (string path)")),
}; };
runtime.require(&path_str)
}
// 2. 路径解析 /// require() 的内部实现 (供 Interpreter::require 使用)
let resolved = resolve_path(&interp.current_dir, &path_str)?; fn require_impl(interp: &mut Interpreter, path_str: &str) -> Result<Value, RuntimeError> {
// 1. 路径解析
let resolved = resolve_path(&interp.current_dir, path_str)?;
// 3. 缓存查找 // 2. 缓存查找
if let Some(cached) = interp.module_cache.borrow().get(&resolved) { if let Some(cached) = interp.module_cache.borrow().get(&resolved) {
return Ok(cached.clone()); return Ok(cached.clone());
} }
// 4. 读取文件 // 3. 读取文件
let src = std::fs::read_to_string(&resolved) let src = std::fs::read_to_string(&resolved)
.map_err(|e| runtime_error(format!( .map_err(|e| runtime_error(format!(
"Module '{}' not found: {}", path_str, e)))?; "Module '{}' not found: {}", path_str, e)))?;
// 5. 词法分析 // 4. 词法分析
let (tokens, lex_errors) = Lexer::new(&src).tokenize(); let (tokens, lex_errors) = Lexer::new(&src).tokenize();
if !lex_errors.is_empty() { if !lex_errors.is_empty() {
return Err(runtime_error(format!( return Err(runtime_error(format!(
"Lex error in module '{}': {}", path_str, lex_errors[0]))); "Lex error in module '{}': {}", path_str, lex_errors[0])));
} }
// 6. 语法分析 // 5. 语法分析
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
let (stmts, parse_errors) = parser.parse(); let (stmts, parse_errors) = parser.parse();
if !parse_errors.is_empty() { if !parse_errors.is_empty() {
@@ -53,27 +62,26 @@ pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
"Parse error in module '{}': {}", path_str, parse_errors[0]))); "Parse error in module '{}': {}", path_str, parse_errors[0])));
} }
// 7. 创建隔离的模块 env(父级 = builtins_env,看不到调用者的变量) // 6. 创建隔离的模块 env(父级 = builtins_env,看不到调用者的变量)
let module_env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&interp.builtins_env))))); let module_env = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&interp.builtins_env)))));
// 8. 在缓存中插入占位符(支持循环 require) // 7. 在缓存中插入占位符(支持循环 require)
let exports_map = Rc::new(RefCell::new(HashMap::new())); let exports_map = Rc::new(RefCell::new(HashMap::new()));
let exports = Value::Object(Rc::clone(&exports_map)); let exports = Value::Object(Rc::clone(&exports_map));
interp.module_cache.borrow_mut().insert(resolved.clone(), exports.clone()); interp.module_cache.borrow_mut().insert(resolved.clone(), exports.clone());
// 9. 保存调用者状态 // 8. 保存调用者状态
let previous_env = Rc::clone(&interp.env); let previous_env = Rc::clone(&interp.env);
let previous_dir = interp.current_dir.clone(); let previous_dir = interp.current_dir.clone();
// 10. 切换到模块上下文 // 9. 切换到模块上下文
interp.env = module_env.clone(); interp.env = module_env.clone();
interp.current_dir = module_dir(&resolved); interp.current_dir = module_dir(&resolved);
// 11. 执行模块 // 10. 执行模块
for stmt in &stmts { for stmt in &stmts {
match interp.execute(stmt.clone()) { match interp.execute(stmt.clone()) {
Ok(Signal::Break) | Ok(Signal::Continue) => { Ok(Signal::Break) | Ok(Signal::Continue) => {
// 恢复状态后返回错误
interp.env = previous_env; interp.env = previous_env;
interp.current_dir = previous_dir; interp.current_dir = previous_dir;
interp.module_cache.borrow_mut().remove(&resolved); interp.module_cache.borrow_mut().remove(&resolved);
@@ -81,23 +89,20 @@ pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
"break/continue outside of loop in module")); "break/continue outside of loop in module"));
} }
Err(e) => { Err(e) => {
// 恢复状态后传播错误
interp.env = previous_env; interp.env = previous_env;
interp.current_dir = previous_dir; interp.current_dir = previous_dir;
interp.module_cache.borrow_mut().remove(&resolved); interp.module_cache.borrow_mut().remove(&resolved);
return Err(e); return Err(e);
} }
Ok(Signal::None) | Ok(Signal::Return(_)) => { Ok(Signal::None) | Ok(Signal::Return(_)) => {}
// 正常执行或顶层 return,继续
}
} }
} }
// 12. 恢复调用者状态 // 11. 恢复调用者状态
interp.env = previous_env; interp.env = previous_env;
interp.current_dir = previous_dir; interp.current_dir = previous_dir;
// 13. 收集 exports(模块 env 中的所有直接绑定) // 12. 收集 exports(模块 env 中的所有直接绑定)
for (name, (val, _mutable)) in module_env.borrow().values.clone() { for (name, (val, _mutable)) in module_env.borrow().values.clone() {
exports_map.borrow_mut().insert(name, val); exports_map.borrow_mut().insert(name, val);
} }
@@ -109,7 +114,6 @@ pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
// Helpers // Helpers
// ============================================================================ // ============================================================================
/// 解析模块路径:相对路径基于 current_dir,用 canonicalize 规范化
fn resolve_path(current_dir: &str, path_str: &str) -> Result<String, RuntimeError> { fn resolve_path(current_dir: &str, path_str: &str) -> Result<String, RuntimeError> {
let path = Path::new(path_str); let path = Path::new(path_str);
let resolved = if path.is_absolute() { let resolved = if path.is_absolute() {
@@ -124,7 +128,6 @@ fn resolve_path(current_dir: &str, path_str: &str) -> Result<String, RuntimeErro
path_str, resolved.display()))) path_str, resolved.display())))
} }
/// 提取模块文件所在目录
fn module_dir(resolved: &str) -> String { fn module_dir(resolved: &str) -> String {
Path::new(resolved) Path::new(resolved)
.parent() .parent()
+38
View File
@@ -2,12 +2,15 @@ pub mod lexer;
pub mod ast; pub mod ast;
pub mod parser; pub mod parser;
pub mod interpreter; pub mod interpreter;
pub mod vm;
pub mod error; pub mod error;
pub mod analysis; pub mod analysis;
use lexer::Lexer; use lexer::Lexer;
use parser::Parser; use parser::Parser;
use interpreter::Interpreter; use interpreter::Interpreter;
use vm::compiler::Compiler;
use vm::vm::Vm;
use error::RuntimeError; use error::RuntimeError;
use std::io::Write; use std::io::Write;
@@ -46,6 +49,41 @@ pub fn run_file(filename: &str, src: String) {
} }
} }
/// 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())
.unwrap_or_else(|| ".".to_string());
let proto = match Compiler::compile(&stmts) {
Ok(p) => p,
Err(e) => {
eprintln!("Compile Error: {}", e);
std::process::exit(70);
}
};
let mut vm = Vm::with_current_dir(script_dir);
if let Err(e) = vm.run(std::rc::Rc::new(proto)) {
eprintln!("Error: {}", e);
std::process::exit(70);
}
}
pub fn run_repl() { pub fn run_repl() {
println!("Welcome to Aster REPL!"); println!("Welcome to Aster REPL!");
println!("Type ':exit' to quit."); println!("Type ':exit' to quit.");
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
pub mod opcode;
pub mod compiler;
pub mod vm;
+173
View File
@@ -0,0 +1,173 @@
//! Bytecode opcode definitions for the Aster VM.
//!
//! Each instruction is encoded as a 1-byte opcode tag followed by
//! variable-length operands (u8 for local slots, u16 for constant pool
//! indices, i16 for relative jump offsets).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum OpCode {
// --- Stack manipulation ---
Pop = 0,
Dup = 1,
// --- Constants (operand: u16 index into constant pool) ---
LoadConst = 2,
LoadNil = 3,
LoadTrue = 4,
LoadFalse = 5,
// --- Local variables (operand: u8 slot index) ---
LoadLocal = 6,
StoreLocal = 7,
LoadUpvalue = 42, // operand: u8 upvalue index
StoreUpvalue = 43, // operand: u8 upvalue index
// --- Global variables (operand: u16 index into constant pool for name) ---
LoadGlobal = 8,
StoreGlobal = 9,
DefineGlobal = 10,
// --- Property access (operand: u16 index into constant pool for name) ---
GetProperty = 11,
SetProperty = 12,
// --- Index access (operands on stack) ---
GetIndex = 13,
SetIndex = 14,
// --- Binary arithmetic ---
Add = 15,
Sub = 16,
Mul = 17,
Div = 18,
Mod = 19,
// --- Unary ---
Negate = 20,
Not = 21,
// --- Comparison ---
Equal = 22,
NotEqual = 23,
Greater = 24,
GreaterEqual = 25,
Less = 26,
LessEqual = 27,
// --- Control flow (operand: i16 relative jump offset) ---
Jump = 28,
JumpIfFalse = 29,
JumpIfTrue = 30,
PopJumpIfFalse = 31,
// --- Functions ---
Call = 32, // operand: u8 arg count
Return = 33,
Closure = 34, // operand: u16 proto idx, then N*(u8 is_local, u8 index)
// --- Object/Array construction ---
NewObject = 35,
NewArray = 36, // operand: u16 element count
// --- For-in support ---
ForInInit = 37,
ForInNext = 38, // operand: i16 done jump offset
// --- Compound assignment ---
CompoundAssignLocal = 39, // operand: u8 slot + u8 compound-op tag
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
}
impl OpCode {
pub fn from_u8(byte: u8) -> Option<Self> {
match byte {
0..=41 | 44 => Some(unsafe { std::mem::transmute::<u8, OpCode>(byte) }),
42 => Some(OpCode::LoadUpvalue),
43 => Some(OpCode::StoreUpvalue),
_ => None,
}
}
/// Total number of distinct opcodes
pub const COUNT: usize = 45;
}
/// Compound assignment operator tags (used as operand byte after
/// CompoundAssignLocal/Prop/Index).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CompoundOp {
PlusEqual = 0,
MinusEqual = 1,
StarEqual = 2,
SlashEqual = 3,
PercentEqual = 4,
}
impl CompoundOp {
pub fn from_u8(byte: u8) -> Option<Self> {
if byte <= 4 {
Some(unsafe { std::mem::transmute::<u8, CompoundOp>(byte) })
} else {
None
}
}
}
// ============================================================================
// Instruction encoding helpers (for compiler use)
// ============================================================================
/// Write a u8 operand after the opcode byte.
pub fn emit_u8(code: &mut Vec<u8>, op: OpCode, operand: u8) {
code.push(op as u8);
code.push(operand);
}
/// Write a u16 operand after the opcode byte (little-endian).
pub fn emit_u16(code: &mut Vec<u8>, op: OpCode, operand: u16) {
code.push(op as u8);
code.push((operand & 0xFF) as u8);
code.push(((operand >> 8) & 0xFF) as u8);
}
/// Write an i16 operand after the opcode byte (little-endian).
pub fn emit_i16(code: &mut Vec<u8>, op: OpCode, offset: i16) {
code.push(op as u8);
code.push((offset & 0xFF) as u8);
code.push(((offset >> 8) & 0xFF) as u8);
}
/// Write a standalone opcode byte.
pub fn emit_op(code: &mut Vec<u8>, op: OpCode) {
code.push(op as u8);
}
// ============================================================================
// Instruction decoding helpers (for VM use)
// ============================================================================
/// Read a u8 at position `ip + 1` (right after the opcode byte).
pub fn read_u8(code: &[u8], ip: usize) -> u8 {
code[ip + 1]
}
/// Read a u16 at position `ip + 1` (little-endian, right after the opcode byte).
pub fn read_u16(code: &[u8], ip: usize) -> u16 {
(code[ip + 1] as u16) | ((code[ip + 2] as u16) << 8)
}
/// Read an i16 at position `ip + 1` (little-endian, right after the opcode byte).
pub fn read_i16(code: &[u8], ip: usize) -> i16 {
((code[ip + 1] as u16) | ((code[ip + 2] as u16) << 8)) as i16
}
/// Size in bytes of an opcode with no operand.
pub const SIZE_OP: usize = 1;
/// Size in bytes of an opcode with a u8 operand.
pub const SIZE_U8: usize = 2;
/// Size in bytes of an opcode with a u16/i16 operand.
pub const SIZE_U16: usize = 3;
File diff suppressed because it is too large Load Diff
+12 -4
View File
@@ -3,13 +3,21 @@ use std::fs;
fn main() { fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = 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 args.len() { match file_args.len() {
1 => aster_core::run_repl(), 1 => aster_core::run_repl(),
2 => { 2 => {
let filename = &args[1]; let filename = file_args[1];
match fs::read_to_string(filename) { match fs::read_to_string(filename) {
Ok(src) => aster_core::run_file(filename, src), Ok(src) => {
if use_vm {
aster_core::run_file_vm(filename, src)
} else {
aster_core::run_file(filename, src)
}
}
Err(e) => { Err(e) => {
eprintln!("Error reading file '{}': {}", filename, e); eprintln!("Error reading file '{}': {}", filename, e);
std::process::exit(1); std::process::exit(1);
@@ -17,7 +25,7 @@ fn main() {
} }
} }
_ => { _ => {
eprintln!("Usage: aster [script]"); eprintln!("Usage: aster [--vm] [script]");
std::process::exit(1); std::process::exit(1);
} }
} }