docs(compiler): explain compilation entry points and source examples

This commit is contained in:
2026-11-15 18:21:28 +08:00
parent 2ca839c0d8
commit 8937b8f8d2
6 changed files with 45 additions and 2 deletions
+8
View File
@@ -1,13 +1,21 @@
// compiler/tests/pipeline.rs 将此文件作为模块编译,加载后由测试调用 main,检查前向全局函数查找。
// The callee is declared after main. Parameters, locals and calls use wordcode.
// 模块入口由宿主/测试在模块执行完成后调用;先 echo,再把局部结果交给 relay。
function main(): String {
// 直接调用后面声明的 echo,返回值写入当前帧的 message 局部槽。
let message = echo("函数调用已接通")
// 将局部值作为实参传给 relay,返回其结果给宿主。
return relay(message)
}
// main 调用的中转函数;value 是当前帧参数槽,继续调用全局 echo。
function relay(value: String): String {
// 普通函数调用逐层返回相同字符串,验证嵌套调用的参数传递与返回。
return echo(value)
}
// main 和 relay 共用的恒等函数,参数值直接成为调用结果。
function echo(value: String): String {
// 无额外转换,直接把参数值交给调用者。
return value
}
+2 -1
View File
@@ -1,4 +1,5 @@
// cli run crates/compiler/examples/hello.endor → 你好,Endor 🌱
// compiler/tests/pipeline.rs 编译并加载本模块后调用 main,期望返回“你好,Endor 🌱”。
function main(): String {
// 返回 Unicode 文本字面量,测试核对编译及 VM 返回过程保留原文。
return "你好,Endor 🌱"
}
+1 -1
View File
@@ -1,2 +1,2 @@
// cli eval crates/compiler/examples/literal.endor
// compiler/tests/pipeline.rs 以 Expression 模式编译此文件,VM 执行后直接取得文本;不定义 main。
"表达式 → 字节码 → VM"
+4
View File
@@ -1,5 +1,9 @@
// compiler/tests/pipeline.rs 按 Module 模式编译,执行模块后调用 main 并按精确十进制值比较。
// Signed decimal constants retain all digits; no f64 conversion or arithmetic.
// 由测试/宿主调用,验证长十进制带符号常量经过编译与返回仍不丢精度。
function main(): Number {
// 字面量保存所有十进制数字,不先经 f64;源码负号经 signed_literal 合入精确数值拼写。
let value = -123456789012345678901234567890.00100
// 返回局部槽中的数值,用于检查整个管线保持原来的精确值。
return value
}
+8
View File
@@ -1,13 +1,21 @@
// compiler/tests/pipeline.rs 加载模块并调用 main,检查函数值传参及局部名优先于全局声明。
// A parameter can shadow a module function, and functions are ordinary values.
// main 把此全局函数作为普通值传入 invoke;实际调用时返回收到的参数。
function echo(value: String): String {
// 恒等返回,不改变用于确认调用链的文本。
return value
}
// main 调用;参数 echo 遮蔽同名模块函数,value 保存待转发的字符串。
function invoke(echo: Callable, value: String): String {
// 从局部参数 echo 复制函数值,而非再次查找模块的同名全局。
let chosen = echo
// 经局部 chosen 间接调用函数,把 value 作为实参并返回调用结果。
return chosen(value)
}
// 宿主/测试入口,先取得全局 echo 函数值,再传给 invoke 的同名参数。
function main(): String {
// invoke 内部用局部参数解析 echo,最终应原样返回这段文本。
return invoke(echo, "局部名称优先")
}
+22
View File
@@ -1,31 +1,46 @@
//! 宿主与集成测试使用的源码编译门面:输出不依赖 VM 堆的模板,显式加载后才得到代码对象。
//! Minimal source-to-wordcode compiler. Analysis and emission do not allocate
//! VM objects; a compiled unit can be loaded into independent VMs explicitly.
// 名称分析决定局部槽与全局名字表,供下一个字节码生成阶段使用。
mod analysis;
// 生成可跨 VM 复用的代码模板;仅显式 load 时才分配目标 VM 的对象。
mod codegen;
// 对宿主和测试公开名称解析结果,便于检查绑定与源码位置。
pub use analysis::{NameBinding, NameUse, Scope, ScopeKind, SymbolTable, analyze};
// 宿主取得模板后负责选择 VM 并调用 CompiledCode::load。
pub use codegen::CompiledCode;
use frontend::ast::{AstDocument, AstRoot};
use frontend::{Diagnostic, DiagnosticKind, ParseMode, SourceDiagnostic, SourceUnit, Span};
/// 宿主通过 compile_source/compile_ast 提供的编译选项;名称分析据此允许访问宿主全局或内置名。
/// 这里只声明名称,宿主仍需在执行前向 VM 的环境字典提供实际值。
/// Names supplied by the host's globals or builtins dictionary. These are
/// compile-time declarations only; loading code never injects their values.
#[derive(Debug, Clone, Default)]
pub struct CompileOptions {
// 编译期已知名称列表;analyze 与源码声明共同解析可用的全局名,不在此持有 VM 值。
pub globals: Vec<String>,
}
/// 宿主及 compiler 集成测试的源码编译入口:按指定模式解析,再校验 AST、解析名称并生成模板。
/// source 提供文件身份与原始文本;mode 决定模块、表达式或交互根;options 声明额外宿主名称。
/// 解析错误已有源码诊断;后续编译错误在这里附加同一源码的位置和片段,成功不执行任何 VM 指令。
pub fn compile_source(
source: &SourceUnit,
mode: ParseMode,
options: &CompileOptions,
) -> Result<CompiledCode, SourceDiagnostic> {
// 前端根据模式返回对应 AstRoot,解析失败立即把带源码信息的错误传给宿主。
let root = frontend::parse_source_ast(source, mode)?;
// AST 入口返回不含源码快照的 Diagnostic;在此统一补上 SourceUnit 上下文。
compile_ast(&root, source, options).map_err(|error| source.annotate(error))
}
/// compile_source 及已有 AST 的宿主/测试调用;先验证结构与跨度,再分析名称,最后输出代码模板。
/// 传入 source 必须是期望匹配的位置快照;跨度有效只说明位置在范围内,不能证明 AST 与文本语义相同。
/// Compile an AST against its original source snapshot. Span validation does
/// not prove the AST's semantic equivalence to the supplied text.
pub fn compile_ast(
@@ -33,8 +48,10 @@ pub fn compile_ast(
source: &SourceUnit,
options: &CompileOptions,
) -> Result<CompiledCode, Diagnostic> {
// 包装并校验 AST 文档结构,再检查节点跨度对当前 source 文本是否有效;不修改调用方的树。
AstDocument::new(root.clone())
.and_then(|document| document.validate_source(source.text()))
// 文档结构/跨度验证错误归入编译错误,尚无可靠单一节点位置,使用默认跨度。
.map_err(|error| {
diagnostic(
DiagnosticKind::Compilation,
@@ -42,11 +59,16 @@ pub fn compile_ast(
error.to_string(),
)
})?;
// 收集并解析所有作用域的绑定及代码布局;错误会阻止生成阶段继续。
let symbols = analyze(root, options)?;
// 生成器按解析后的槽/名字索引编码,不在此加载模板或分配 VM 堆对象。
codegen::generate(root, source, &symbols)
}
/// 本模块的 AST 验证及 analysis/codegen 的错误辅助函数共用,统一构造不带源码快照的诊断。
/// kind 区分语义/编译错误,span 标记位置,message 接受可转 String 的说明;语法期望项留空。
fn diagnostic(kind: DiagnosticKind, span: Span, message: impl Into<String>) -> Diagnostic {
// 编译器错误没有解析器的“期望 token”列表,源码上下文由上层 compile_source 统一附加。
Diagnostic {
kind,
span,