Files
aster/README.md
0264408 deab894864 doc: 修正README中upvalue捕获层数限制的表述
实际机制是增量逐层建立,通过enclosing_locals合并链
理论上不限深度;仅一次性链创建受限于3层穿透

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-26 10:49:41 +08:00

185 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Aster
Aster 是一门使用 Rust 编写的动态类型脚本语言,采用**基于栈的字节码 VM** 执行。无外部依赖,纯标准库。
## 快速开始
```bash
# 构建
cargo build
# 启动 REPL(交互式)
cargo run
# 执行脚本文件
cargo run -- examples/script.ast
# 运行测试
cargo test
```
## 语言特性
### 变量与赋值
```js
let x = 42; // 可变绑定
const y = 100; // 不可变绑定(不能重新赋值)
let name = "aster";
x = x + 1;
x += 2; // 复合赋值:+= -= *= /= %=
// y = 200; // ✗ 运行时错误:不能对 const 重新赋值
```
`const` 阻止对绑定的重新赋值,但不阻止对象属性或数组元素的修改。
### 数据类型
```js
let n = 3.14; // 数字(f64
let s = "hello\nworld"; // 字符串(支持 \n \t \r \" \\ 转义)
let b = true; // 布尔值
let empty = nil; // nil
let arr = [1, 2, 3]; // 数组
let obj = { name: "aster", v:1 }; // 对象
```
### 运算符
```
算术: + - * / %
比较: == != < > <= >=
逻辑: && || !
```
- `+` 同时支持数字加法和字符串拼接
- `==``!=` 对数组和对象进行深度比较
- `&&``||` 短路求值
- 除零和取模零会产生运行时错误
### 控制流
```js
// if / else
if (x > 0) {
print("positive");
} else {
print("non-positive");
}
// while 循环
let i = 0;
while (i < 10) {
print(i);
i = i + 1;
}
// for 循环(C 风格)
for (let i = 0; i < 10; i = i + 1) {
print(i);
}
// break / continue
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 同一文件会返回缓存的对象(支持循环引用)
```
### 函数与闭包
```js
// 函数声明
fn add(a, b) {
return a + b;
}
// 递归
fn fib(n) {
if (n <= 1) { return n; }
return fib(n - 1) + fib(n - 2);
}
// 闭包
fn make_adder(x) {
return fn(y) { return x + y; };
}
let add5 = make_adder(5);
print(add5(10)); // 15
```
### 数组与对象
```js
let arr = [10, 20, 30];
print(arr[0]); // 索引访问
arr[1] = 99; // 索引赋值
arr[0] += 5; // 复合索引赋值
let obj = { name: "aster", version: 1 };
print(obj.name); // 属性访问
obj.version = 2; // 属性赋值
```
### 内置函数
```js
// I/O
print("hello", 42); // 打印,自动换行
let name = input("name: "); // 读取输入(可选提示符)
let name = io.input("name:"); // 模块化调用
// 时间
let t = clock(); // Unix 时间戳(秒)
let t = os.clock(); // 模块化调用
```
## REPL 命令
| 命令 | 说明 |
|------|------|
| `:exit` | 退出 REPL |
| `:reset` | 重置解释器状态(清除所有变量和函数) |
## 架构
```
源码文本 → Lexer → Tokens → Parser → AST → Compiler → Bytecode → VM → 输出
```
| 模块 | 职责 |
|------|------|
| `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 |
| `runtime/` | 运行时类型:`Value` 枚举、`FunctionProto`(编译后的函数蓝图)、`Closure`(函数原型 + 捕获的 upvalue)、`Runtime` trait、`NativeFn` 类型。`builtins/` 按功能分组注册原生函数 |
| `vm/` | 字节码 VM。`Compiler` 将 AST 编译为基于栈的字节码(44 条指令),`Vm` 执行字节码。支持闭包 upvalue 捕获、3 层嵌套闭包、`require()` 模块加载 |
| `error/` | 三种错误:`LexError`(行列号)、`ParseError`Token)、`RuntimeError`(可选 Token |
### 关键设计决策
- **零外部依赖** — 全部基于 Rust 标准库构建
- **基于栈的字节码 VM** — AST 先编译为字节码再执行,单一执行路径,无语义漂移
- **错误容忍解析** — 词法分析器和解析器均将错误收集到 `Vec` 中,单次运行可报告多个诊断信息,而非在第一个错误处就中止
- **`Rc<RefCell<>>` 共享所有权** — 用于 `Value::Object``Value::Array``UpvalueObj`,提供动态可变语义。闭包通过 upvalue 机制捕获外层局部变量
- **词法作用域闭包** — 编译时解析 upvalue 捕获,通过增量链路支持任意深度嵌套;`resolve_upvalue` 中显式处理 3 层穿透(本地→父级→祖级),更深层级通过逐层编译自然建立
## 许可证
MIT