Compare commits
12 Commits
bdffe9e3c5
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ced4a9d060 | |||
| abe0844816 | |||
| 61ce3466a9 | |||
| bb0cf8c558 | |||
| 1cfa31abcc | |||
| 7b9bded5ee | |||
| 9a193fd7ca | |||
| ddee395f52 | |||
| 9c9a17b149 | |||
| 299003a718 | |||
| d854b22006 | |||
| 89952139c5 |
@@ -1,6 +1,6 @@
|
||||
# Aster
|
||||
|
||||
Aster 是一门使用 Rust 编写的动态类型脚本语言解释器,基于树遍历(tree-walker)实现。无外部依赖,纯标准库。
|
||||
Aster 是一门使用 Rust 编写的动态类型脚本语言解释器,支持两种执行模式:**树遍历(tree-walker)** 和 **字节码 VM**。无外部依赖,纯标准库。
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -11,9 +11,12 @@ cargo build
|
||||
# 启动 REPL(交互式)
|
||||
cargo run
|
||||
|
||||
# 执行脚本文件
|
||||
# 执行脚本文件(树遍历模式)
|
||||
cargo run -- examples/script.ast
|
||||
|
||||
# 执行脚本文件(字节码 VM 模式,性能更优)
|
||||
cargo run -- --vm examples/script.ast
|
||||
|
||||
# 运行测试
|
||||
cargo test
|
||||
```
|
||||
@@ -84,6 +87,21 @@ while (true) {
|
||||
if (condition) { break; }
|
||||
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()` 跳过至下一条语句边界继续解析 |
|
||||
| `ast/` | AST 节点定义。`Expr`(表达式)涵盖字面量、变量、赋值、属性/索引访问、一元/二元/逻辑运算、函数调用、lambda 和对象/数组字面量。`Stmt`(语句)涵盖 let、表达式语句、块、if/while/for、函数、return/break/continue |
|
||||
| `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) |
|
||||
|
||||
### 关键设计决策
|
||||
|
||||
- **零外部依赖** — 全部基于 Rust 标准库构建
|
||||
- **双执行模式** — 树遍历模式(默认,适合交互和调试)和字节码 VM 模式(`--vm`,约 40% 性能提升),共享同一 AST 和运行时语义
|
||||
- **错误容忍解析** — 词法分析器和解析器均将错误收集到 `Vec` 中,单次运行可报告多个诊断信息,而非在第一个错误处就中止
|
||||
- **`Rc<RefCell<>>` 共享所有权** — 用于 `Env` 链、`Value::Object`、`Value::Array` 和 `Value::Function`,提供动态可变语义
|
||||
- **词法作用域闭包** — `Function` 在定义时捕获 `Env`,lambda 表达式同理
|
||||
- **词法作用域闭包** — `Function` 在定义时捕获 `Env`,lambda 表达式同理;VM 通过 upvalue 机制支持最多 3 层传递式捕获
|
||||
|
||||
## 许可证
|
||||
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
// ============================================================================
|
||||
// Aster 性能基准测试
|
||||
// 运行: cargo run -- examples/benchmark.ast
|
||||
// 目的: 用于树遍历解释器 vs VM 的性能对比
|
||||
//
|
||||
// 注: debug 模式下总耗时约 30-60s,release 模式会快 10-50x
|
||||
// ============================================================================
|
||||
|
||||
// --- 高阶工具函数 (被后续 benchmark 使用) ---
|
||||
|
||||
fn map(arr, func) {
|
||||
let result = []
|
||||
for (x in arr) {
|
||||
push(result, func(x))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fn filter(arr, pred) {
|
||||
let result = []
|
||||
for (x in arr) {
|
||||
if (pred(x)) {
|
||||
push(result, x)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fn reduce(arr, func, init) {
|
||||
let acc = init
|
||||
for (x in arr) {
|
||||
acc = func(acc, x)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
fn sort(arr) {
|
||||
let n = len(arr)
|
||||
let i = 0
|
||||
while (i < n - 1) {
|
||||
let j = 0
|
||||
while (j < n - i - 1) {
|
||||
if (arr[j] > arr[j + 1]) {
|
||||
let tmp = arr[j]
|
||||
arr[j] = arr[j + 1]
|
||||
arr[j + 1] = tmp
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// --- 基准测试框架 ---
|
||||
|
||||
fn bench(name, thunk) {
|
||||
let t0 = clock()
|
||||
let result = thunk()
|
||||
let t1 = clock()
|
||||
let elapsed = t1 - t0
|
||||
print(name + ": " + result + " \ttime=" + elapsed + "s")
|
||||
return result
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 1. 算术运算压测 — 测试基本运算 + 循环调度开销
|
||||
// ============================================================================
|
||||
|
||||
print("=== 1. Arithmetic ===")
|
||||
|
||||
bench("1a. int loop 1M", fn() {
|
||||
let sum = 0
|
||||
let i = 0
|
||||
while (i < 1000000) {
|
||||
sum = sum + i
|
||||
i = i + 1
|
||||
}
|
||||
return sum
|
||||
})
|
||||
|
||||
bench("1b. FP mul 500K", fn() {
|
||||
let x = 1.0
|
||||
let i = 0
|
||||
while (i < 500000) {
|
||||
x = x * 1.00001
|
||||
i = i + 1
|
||||
}
|
||||
return x
|
||||
})
|
||||
|
||||
bench("1c. compound assign 1M", fn() {
|
||||
let a = 0
|
||||
let i = 0
|
||||
while (i < 1000000) {
|
||||
a += i
|
||||
i += 1
|
||||
}
|
||||
return a
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 2. 函数调用开销 — 测试各种调用模式
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 2. Function Calls ===")
|
||||
|
||||
fn empty() { return 42 }
|
||||
|
||||
bench("2a. call empty fn 200K", fn() {
|
||||
let i = 0
|
||||
while (i < 200000) {
|
||||
empty()
|
||||
i = i + 1
|
||||
}
|
||||
return empty()
|
||||
})
|
||||
|
||||
fn add(a, b) { return a + b }
|
||||
|
||||
bench("2b. call add(a,b) 200K", fn() {
|
||||
let s = 0
|
||||
let i = 0
|
||||
while (i < 200000) {
|
||||
s = add(s, i)
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
fn fact(n) {
|
||||
if (n <= 1) { return 1 }
|
||||
return n * fact(n - 1)
|
||||
}
|
||||
|
||||
bench("2c. recursion fact(10) 10K", fn() {
|
||||
let i = 0
|
||||
while (i < 10000) {
|
||||
fact(10)
|
||||
i = i + 1
|
||||
}
|
||||
return fact(10)
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 3. 闭包压测 — 闭包创建与调用
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 3. Closures ===")
|
||||
|
||||
fn make_mult(k) {
|
||||
return fn(x) { return x * k }
|
||||
}
|
||||
|
||||
bench("3a. create + call closure 100K", fn() {
|
||||
let m = make_mult(7)
|
||||
let s = 0
|
||||
let i = 0
|
||||
while (i < 100000) {
|
||||
s = s + m(i)
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
bench("3b. inline lambda map 10K", fn() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
let i = 0
|
||||
let s = 0
|
||||
while (i < 10000) {
|
||||
let doubled = map(arr, fn(x) { return x * 2 })
|
||||
s = s + doubled[0]
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 4. 数组操作
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 4. Array ===")
|
||||
|
||||
bench("4a. array index get 1M", fn() {
|
||||
let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
|
||||
let s = 0
|
||||
let i = 0
|
||||
while (i < 1000000) {
|
||||
s = s + arr[i % 10]
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
bench("4b. array index set 500K", fn() {
|
||||
let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
let i = 0
|
||||
while (i < 500000) {
|
||||
arr[i % 10] = i
|
||||
i = i + 1
|
||||
}
|
||||
return arr[0] + arr[9]
|
||||
})
|
||||
|
||||
bench("4c. push + pop 100K", fn() {
|
||||
let arr = []
|
||||
let i = 0
|
||||
while (i < 100000) {
|
||||
push(arr, i)
|
||||
i = i + 1
|
||||
}
|
||||
while (len(arr) > 0) {
|
||||
pop(arr)
|
||||
}
|
||||
return len(arr)
|
||||
})
|
||||
|
||||
bench("4d. for-in array 10K items", fn() {
|
||||
let arr = []
|
||||
let i = 0
|
||||
while (i < 10000) {
|
||||
push(arr, i)
|
||||
i = i + 1
|
||||
}
|
||||
let s = 0
|
||||
for (x in arr) {
|
||||
s = s + x
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 5. 对象操作
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 5. Object ===")
|
||||
|
||||
bench("5a. object field read 200K", fn() {
|
||||
let obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }
|
||||
let s = 0
|
||||
let i = 0
|
||||
while (i < 200000) {
|
||||
s = s + obj.a + obj.b + obj.c
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
bench("5b. object field write 200K", fn() {
|
||||
let obj = { x: 0, y: 0 }
|
||||
let i = 0
|
||||
while (i < 200000) {
|
||||
obj.x = i
|
||||
obj.y = obj.x + 1
|
||||
i = i + 1
|
||||
}
|
||||
return obj.x + obj.y
|
||||
})
|
||||
|
||||
bench("5c. object bracket get 200K", fn() {
|
||||
let obj = { name: "test", age: 25 }
|
||||
let s = ""
|
||||
let i = 0
|
||||
while (i < 200000) {
|
||||
s = obj["name"]
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 6. 字符串操作
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 6. String ===")
|
||||
|
||||
bench("6a. string concat 50K", fn() {
|
||||
let s = ""
|
||||
let i = 0
|
||||
while (i < 50000) {
|
||||
s = s + "a"
|
||||
i = i + 1
|
||||
}
|
||||
return len(s)
|
||||
})
|
||||
|
||||
bench("6b. split + join pattern 10K", fn() {
|
||||
let csv = "alice,bob,carol,dave,eve"
|
||||
let i = 0
|
||||
let c = 0
|
||||
while (i < 10000) {
|
||||
let parts = split(csv, ",")
|
||||
c = c + len(parts)
|
||||
i = i + 1
|
||||
}
|
||||
return c
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 7. 高阶函数链 — map/filter/reduce
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 7. Higher-order ===")
|
||||
|
||||
bench("7a. map+filter+reduce chain 1000", fn() {
|
||||
let data = []
|
||||
let i = 0
|
||||
while (i < 100) {
|
||||
push(data, i)
|
||||
i = i + 1
|
||||
}
|
||||
let n = 0
|
||||
while (n < 1000) {
|
||||
let r = reduce(
|
||||
map(
|
||||
filter(data, fn(x) { return x % 2 == 0 }),
|
||||
fn(x) { return x * x }
|
||||
),
|
||||
fn(a, b) { return a + b },
|
||||
0
|
||||
)
|
||||
n = n + 1
|
||||
}
|
||||
return n
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 8. 复杂算法 — 冒泡排序
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 8. Algorithms ===")
|
||||
|
||||
bench("8a. bubble sort 100 items x 50", fn() {
|
||||
let template = [42, 17, 8, 99, 3, 56, 23, 91, 5, 67,
|
||||
34, 12, 78, 1, 55, 88, 19, 73, 44, 28,
|
||||
66, 37, 81, 14, 50, 95, 7, 63, 29, 41,
|
||||
85, 21, 52, 9, 76, 33, 68, 15, 47, 92,
|
||||
25, 58, 4, 71, 39, 83, 11, 54, 97, 30,
|
||||
64, 18, 49, 2, 87, 36, 74, 22, 59, 45,
|
||||
90, 6, 53, 27, 80, 13, 48, 94, 31, 69,
|
||||
16, 57, 43, 89, 24, 61, 8, 75, 38, 46,
|
||||
100, 20, 65, 10, 79, 35, 72, 26, 82, 51,
|
||||
98, 32, 77, 40, 84, 62, 93, 3, 70, 70]
|
||||
let i = 0
|
||||
while (i < 50) {
|
||||
let arr = []
|
||||
let j = 0
|
||||
while (j < 100) {
|
||||
push(arr, template[j])
|
||||
j = j + 1
|
||||
}
|
||||
sort(arr)
|
||||
i = i + 1
|
||||
}
|
||||
return i
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 9. 斐波那契 — 递归 vs 迭代对比
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 9. Fibonacci ===")
|
||||
|
||||
fn fib_rec(n) {
|
||||
if (n <= 1) { return n }
|
||||
return fib_rec(n - 1) + fib_rec(n - 2)
|
||||
}
|
||||
|
||||
bench("9a. fib_rec(25) x 10", fn() {
|
||||
let i = 0
|
||||
let r = 0
|
||||
while (i < 10) {
|
||||
r = fib_rec(25)
|
||||
i = i + 1
|
||||
}
|
||||
return r
|
||||
})
|
||||
|
||||
fn fib_iter(n) {
|
||||
if (n <= 1) { return n }
|
||||
let a = 0
|
||||
let b = 1
|
||||
let i = 2
|
||||
while (i <= n) {
|
||||
let tmp = a + b
|
||||
a = b
|
||||
b = tmp
|
||||
i = i + 1
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
bench("9b. fib_iter(100) x 10K", fn() {
|
||||
let i = 0
|
||||
while (i < 10000) {
|
||||
fib_iter(100)
|
||||
i = i + 1
|
||||
}
|
||||
return fib_iter(100)
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 10. 变量访问 — 全局 vs 局部 vs 深层闭包
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 10. Variable Access ===")
|
||||
|
||||
let g = 100
|
||||
|
||||
fn use_global() {
|
||||
return g + 1
|
||||
}
|
||||
|
||||
bench("10a. global var read 200K", fn() {
|
||||
let i = 0
|
||||
let s = 0
|
||||
while (i < 200000) {
|
||||
s = s + g
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
bench("10b. local var read 200K", fn() {
|
||||
let local = 100
|
||||
let i = 0
|
||||
let s = 0
|
||||
while (i < 200000) {
|
||||
s = s + local
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
fn deep_scope() {
|
||||
let a = 1
|
||||
let b = 2
|
||||
let c = 3
|
||||
let d = 4
|
||||
let e = 5
|
||||
return fn() {
|
||||
return fn() {
|
||||
return fn() {
|
||||
return a + b + c + d + e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bench("10c. nested closure call 100K", fn() {
|
||||
let get = deep_scope()()()
|
||||
let i = 0
|
||||
let s = 0
|
||||
while (i < 100000) {
|
||||
s = s + get()
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 11. 分支判断
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 11. Conditionals ===")
|
||||
|
||||
bench("11a. if-else chain 500K", fn() {
|
||||
let s = 0
|
||||
let i = 0
|
||||
while (i < 500000) {
|
||||
if (i % 3 == 0) {
|
||||
s = s + 1
|
||||
} else if (i % 3 == 1) {
|
||||
s = s + 2
|
||||
} else {
|
||||
s = s + 3
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 12. 综合混合负载
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== 12. Mixed Workload ===")
|
||||
|
||||
bench("12a. mixed (arith+obj+array+call)", fn() {
|
||||
let s = 0
|
||||
let i = 0
|
||||
while (i < 50000) {
|
||||
s = s + i * 2 - i / 2 + i % 7
|
||||
|
||||
let pt = { x: i, y: i + 1, z: i + 2 }
|
||||
s = s + pt.x + pt.y - pt.z
|
||||
|
||||
let arr = [i, i + 1, i + 2, i + 3, i + 4]
|
||||
s = s + arr[0] + arr[i % 5]
|
||||
|
||||
s = s + add(pt.x, arr[1])
|
||||
|
||||
i = i + 1
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// 13. 总结
|
||||
// ============================================================================
|
||||
|
||||
print("")
|
||||
print("=== Benchmark Complete ===")
|
||||
@@ -130,7 +130,7 @@ pub enum LogicalOp {
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AssignOp {
|
||||
Equal, // =
|
||||
PlusEqual, // +=
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 标准库:core(len, typeof, push, pop)
|
||||
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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()))
|
||||
}
|
||||
|
||||
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 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "pop() expects 1 argument (array)".into(),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 标准库:io(print, input)
|
||||
|
||||
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() {
|
||||
print!("{}", arg);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ pub fn print(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, Runti
|
||||
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) {
|
||||
print!("{}", prompt);
|
||||
std::io::Write::flush(&mut std::io::stdout()).unwrap();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! 内置函数模块:按功能分组注册,便于扩展和维护。
|
||||
|
||||
mod core;
|
||||
mod io;
|
||||
mod os;
|
||||
pub mod core;
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
pub mod string;
|
||||
|
||||
use crate::interpreter::{Env, Value};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 标准库:os(clock)
|
||||
|
||||
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(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! 标准库:string(split, trim, substring, replace, contains, upper, lower, starts_with, ends_with)
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
use crate::interpreter::{Interpreter, Value};
|
||||
use crate::interpreter::{Runtime, Value};
|
||||
use std::cell::RefCell;
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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)
|
||||
// ============================================================================
|
||||
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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)
|
||||
// ============================================================================
|
||||
|
||||
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() {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
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 {
|
||||
return Err(RuntimeError::RuntimeError {
|
||||
message: "ends_with() expects 2 arguments (string, suffix)".into(),
|
||||
|
||||
@@ -103,7 +103,7 @@ impl super::Interpreter {
|
||||
"length" => Ok(Value::Number(arr.borrow().len() as f64)),
|
||||
"push" => {
|
||||
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);
|
||||
arr.borrow_mut().push(val.clone());
|
||||
Ok(val)
|
||||
@@ -111,7 +111,7 @@ impl super::Interpreter {
|
||||
}
|
||||
"pop" => {
|
||||
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()
|
||||
.pop()
|
||||
.ok_or_else(|| RuntimeError::RuntimeError {
|
||||
@@ -129,74 +129,74 @@ impl super::Interpreter {
|
||||
"length" => Ok(Value::Number(s.chars().count() as f64)),
|
||||
"upper" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::upper(_interp, all_args)
|
||||
super::builtins::string::upper(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"lower" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::lower(_interp, all_args)
|
||||
super::builtins::string::lower(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"trim" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::trim(_interp, all_args)
|
||||
super::builtins::string::trim(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"substring" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::substring(_interp, all_args)
|
||||
super::builtins::string::substring(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"replace" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::replace(_interp, all_args)
|
||||
super::builtins::string::replace(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"contains" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::contains(_interp, all_args)
|
||||
super::builtins::string::contains(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"starts_with" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::starts_with(_interp, all_args)
|
||||
super::builtins::string::starts_with(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"ends_with" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::ends_with(_interp, all_args)
|
||||
super::builtins::string::ends_with(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
"split" => {
|
||||
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())];
|
||||
all_args.extend(args);
|
||||
super::builtins::string::split(_interp, all_args)
|
||||
super::builtins::string::split(runtime, all_args)
|
||||
})))
|
||||
}
|
||||
_ => 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> {
|
||||
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),
|
||||
_ => Err(RuntimeError::RuntimeError {
|
||||
message: "Attempt to call non-function".to_string(),
|
||||
|
||||
@@ -13,7 +13,16 @@ use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
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)]
|
||||
pub enum Value {
|
||||
|
||||
@@ -8,15 +8,20 @@
|
||||
use crate::error::RuntimeError;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::Parser;
|
||||
use super::{Interpreter, Env, Value, Signal};
|
||||
use super::{Interpreter, Env, Value, Signal, Runtime};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// require() 的内置函数实现
|
||||
pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
// 1. 参数验证
|
||||
impl Runtime for Interpreter {
|
||||
fn require(&mut self, path: &str) -> Result<Value, RuntimeError> {
|
||||
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() {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
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(
|
||||
"require() expects 1 argument (string path)")),
|
||||
};
|
||||
runtime.require(&path_str)
|
||||
}
|
||||
|
||||
// 2. 路径解析
|
||||
let resolved = resolve_path(&interp.current_dir, &path_str)?;
|
||||
/// require() 的内部实现 (供 Interpreter::require 使用)
|
||||
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) {
|
||||
return Ok(cached.clone());
|
||||
}
|
||||
|
||||
// 4. 读取文件
|
||||
// 3. 读取文件
|
||||
let src = std::fs::read_to_string(&resolved)
|
||||
.map_err(|e| runtime_error(format!(
|
||||
"Module '{}' not found: {}", path_str, e)))?;
|
||||
|
||||
// 5. 词法分析
|
||||
// 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])));
|
||||
}
|
||||
|
||||
// 6. 语法分析
|
||||
// 5. 语法分析
|
||||
let mut parser = Parser::new(tokens);
|
||||
let (stmts, parse_errors) = parser.parse();
|
||||
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])));
|
||||
}
|
||||
|
||||
// 7. 创建隔离的模块 env(父级 = builtins_env,看不到调用者的变量)
|
||||
// 6. 创建隔离的模块 env(父级 = 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 = Value::Object(Rc::clone(&exports_map));
|
||||
interp.module_cache.borrow_mut().insert(resolved.clone(), exports.clone());
|
||||
|
||||
// 9. 保存调用者状态
|
||||
// 8. 保存调用者状态
|
||||
let previous_env = Rc::clone(&interp.env);
|
||||
let previous_dir = interp.current_dir.clone();
|
||||
|
||||
// 10. 切换到模块上下文
|
||||
// 9. 切换到模块上下文
|
||||
interp.env = module_env.clone();
|
||||
interp.current_dir = module_dir(&resolved);
|
||||
|
||||
// 11. 执行模块
|
||||
// 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);
|
||||
@@ -81,23 +89,20 @@ pub fn require_fn(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, R
|
||||
"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(_)) => {
|
||||
// 正常执行或顶层 return,继续
|
||||
}
|
||||
Ok(Signal::None) | Ok(Signal::Return(_)) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// 12. 恢复调用者状态
|
||||
// 11. 恢复调用者状态
|
||||
interp.env = previous_env;
|
||||
interp.current_dir = previous_dir;
|
||||
|
||||
// 13. 收集 exports(模块 env 中的所有直接绑定)
|
||||
// 12. 收集 exports(模块 env 中的所有直接绑定)
|
||||
for (name, (val, _mutable)) in module_env.borrow().values.clone() {
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
/// 解析模块路径:相对路径基于 current_dir,用 canonicalize 规范化
|
||||
fn resolve_path(current_dir: &str, path_str: &str) -> Result<String, RuntimeError> {
|
||||
let path = Path::new(path_str);
|
||||
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())))
|
||||
}
|
||||
|
||||
/// 提取模块文件所在目录
|
||||
fn module_dir(resolved: &str) -> String {
|
||||
Path::new(resolved)
|
||||
.parent()
|
||||
|
||||
@@ -2,12 +2,15 @@ pub mod lexer;
|
||||
pub mod ast;
|
||||
pub mod parser;
|
||||
pub mod interpreter;
|
||||
pub mod vm;
|
||||
pub mod error;
|
||||
pub mod analysis;
|
||||
|
||||
use lexer::Lexer;
|
||||
use parser::Parser;
|
||||
use interpreter::Interpreter;
|
||||
use vm::compiler::Compiler;
|
||||
use vm::vm::Vm;
|
||||
use error::RuntimeError;
|
||||
|
||||
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() {
|
||||
println!("Welcome to Aster REPL!");
|
||||
println!("Type ':exit' to quit.");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
pub mod opcode;
|
||||
pub mod compiler;
|
||||
pub mod vm;
|
||||
@@ -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
@@ -3,13 +3,21 @@ use std::fs;
|
||||
|
||||
fn main() {
|
||||
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(),
|
||||
2 => {
|
||||
let filename = &args[1];
|
||||
let filename = file_args[1];
|
||||
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) => {
|
||||
eprintln!("Error reading file '{}': {}", filename, e);
|
||||
std::process::exit(1);
|
||||
@@ -17,7 +25,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Usage: aster [script]");
|
||||
eprintln!("Usage: aster [--vm] [script]");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user