Files
endorphin-aster-vm/docs/runtime/compiler.md
T

61 lines
7.4 KiB
Markdown
Raw 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.
# 最小源码编译与执行链路
当前已连接 reader → lexer → PEG parser → AST → 名称/作用域分析 → wordcode → 代码对象 → VM。实现位于独立 [compiler](../../crates/compiler/README.md);frontend 只负责语法,objects 拥有指令/代码数据与验证,core 解释字节码。用户无需手写指令,也不需要先补全 Python 基础类。
## 接口与阶段
| 接口 | 行为 |
| --- | --- |
| analyze(&AstRoot, &CompileOptions) | 返回 SymbolTable:根与函数作用域、CodeLayout、每次名称读取的位置和 Local/Global 绑定 |
| compile_ast(&AstRoot, &SourceUnit, &CompileOptions) | 校验节点与原文范围、分析并生成 CompiledCode;不验证 AST 与原文的语义等价性 |
| compile_source(&SourceUnit, ParseMode, &CompileOptions) | 先调用 frontend 解析,再编译;错误携带原始文件/流/URI 与修订来源及一基行列 |
| CompiledCode::load(&mut Vm) | 将模板及子模板装载为该 VM 的代码对象,返回一个拥有根的 ObjectRef |
解析错误沿用 Lexical/Syntax,名称错误为 Semantic,不支持的执行特性或代码验证错误为 Compilation。编译不修改 VM 或全局字典;词法动作也不执行语言代码。模板提供 instructions/layout/name/filename/first_line/children 只读检查入口。生成时使用已有 EndorCode 验证器检查槽位、操作数和栈平衡;装载再次验证真实常量。
## 名称与作用域
首版是模块作用域和函数作用域。模块先登记全部函数/类名,再分析函数体,因此支持前向引用、自递归和函数作为普通值传递。函数参数占据最前面的局部槽,let 与首次赋值目标按出现顺序分配后续槽;引用优先使用函数局部,其次使用已声明全局。
函数内先收集全部局部名称,再按语句顺序检查初始化。后面声明的 let 会遮蔽整个函数中的同名全局,但提前读取报错;let x = x 也会报未初始化。参数、局部绑定和同一模块的函数不允许在各自作用域内重复声明;参数或局部可以遮蔽模块函数。裸名称赋值 `x = expression` 可在交互全局创建或更新名字;函数内赋值复用已有参数/局部槽,不存在时分配局部槽。赋值右侧先求值再写入,失败不会覆盖该目标;函数内赋值目标仍遮蔽同名全局,首次初始化前读取报错。`let` 的重复声明检查不变。嵌套函数/闭包分析和块级作用域尚未实现。
CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称,默认空。不认识的名称在编译时报错;已声明的宿主名称仍必须在执行时存在,否则 VM 返回 NameNotFound。编译器不自动注入 print、默认基础类或标准库,也不读取这些名字的值。类型注解只保留语法,不参与名称绑定、运行时求值或类型检查。
## 指令与代码对象
| 源结构 | 生成方式 |
| --- | --- |
| 字符串、整数、十进制常量 | 放入常量表并 LoadConst;数字经现有 EndorNumber 解析,不转换为 f64 |
| 数字字面量前的一个或多个负号 | 折叠符号为精确常量;不是任意表达式的一元算术 |
| 局部读取/let/赋值 | LoadFast/StoreFast;参数和局部名称在 CodeLayout 中 |
| 全局读取/赋值 | LoadGlobal/StoreGlobal,使用该代码对象的 names 表 |
| 函数声明 | 子代码模板 → LoadConst(code) → MakeFunction(0, 0) → StoreGlobal |
| 普通调用 | 求值 callee → PushNull → 从左到右求值位置参数 → Call(argc) |
| 成员读取 | 求值对象 → LoadAttr;沿用 VM 属性协议;类型方法返回持有接收者的绑定方法 |
| 属性赋值 | 求值右侧,再求值目标对象,使用 StoreAttr 维护托管引用与变更 |
| 类声明 | 名称、基类、字段映射、MakeFunction 构造的方法映射 → BuildClass → StoreGlobal |
| return | 求值结果 → ReturnValue;无值 return 或 AST 函数末尾无 return 时生成 nil |
源码 parser 接受绑定/名称及属性赋值/表达式语句与可选末尾带值 return;空 return 仍仅由 AST 调用方构造。模块初始化发布函数/类并返回 nil,不自动调用 main。Lyra 宿主选择入口属于宿主行为。
二元算术、非字面量的一元负号和符号值 .name 目前产生明确 Compilation 诊断,不伪造运行结果。导入、类、Agent、条件/循环、关键字参数等仍未实现。函数参数个数和对象是否可调用由 VM 在运行时检查。
## 装载、执行与所有权
CompiledCode 是纯 Rust 模板,嵌套代码常量在模板内部使用占位槽,装载时替换为当前 VM 的子代码对象;模板不含跨 VM 句柄,可复用到多个 VM。父代码对象成功分配后持有子代码,装载成功或失败都会释放临时子根。返回的根代码对象由调用方 release。
宿主为模块提供 globals 字典,调用 vm.execute(code, globals) 发布函数,然后从字典借用函数句柄并调用 vm.call。函数共享装载后的代码,反复调用不重新编译;execute/call 的对象结果持有根,用后 release_value。函数指向 globals,globals 保存函数,因此宿主需保留命名空间期间的根,并在销毁时清空字典或释放根后显式 collect_cycles。编译器不清空宿主字典。
Expression 模式返回表达式值;Interactive 模式严格编译一次声明、let、裸赋值或表达式。声明/绑定/赋值返回 nil,表达式返回值;宿主可以跨提交复用 globals,并在下一次 CompileOptions 中声明可见名称。Lyra 当前没有 REPL、续行或自动会话管理。
代码对象保存来源字符串、函数名和定义起始行,现有 VM traceback 保存该位置及指令偏移;没有每条指令对应的精确源码行/列映射。现有 ExecutionLimits 帧数/指令数限制可由嵌入方继续使用;本链路不构成不可信代码沙箱。
## Lyra 宿主与检查
lyra run [--entry name] <file|-> 编译模块、执行初始化、调用无参数入口(默认 main),输出返回值一行。lyra eval <file|-> 编译执行一个表达式。字符串输出原始内容,数字输出无损表示,对象使用 VM str;失败仅输出 stderr 并返回非零退出码。文件和 stdin 严格 UTF-8,stdin 到 EOF 后才处理。Lyra 在成功或失败后释放代码/结果并清空自己创建的命名空间。
可执行文件见 [示例目录](../../crates/compiler/examples/README.md),API/所有权与错误路径见 [pipeline.rs](../../crates/compiler/tests/pipeline.rs),进程入口见 [CLI 检查](https://gitea.entroia.com/Zayden-Jung/endorphin-lyra-cli/src/branch/main/crates/cli/tests/README.md)。引擎和进程入口检查由独立 Lyra CI 分别显式执行;本地只做静态检查,不据此宣称实际执行通过,见 [验证约定](../operations/validation.md)。返回 [运行时索引](README.md)。
类方法使用显式首参数 self;编译器检查本类重复字段/方法和缺少 self。方法代码仍是共享的普通代码对象,参数类型名目前不实施运行时类型检查。BuildClass 在 VM 中检查继承字段冲突和原生分配边界;构造失败不发布类型到目标全局名。默认基类 Object 须由宿主通过 CompileOptions 声明并在运行全局中提供,编译器不自行引导类型。普通类构造和继承分派见[类型机制](types.md)。