79 lines
4.3 KiB
Rust
79 lines
4.3 KiB
Rust
//! 宿主与集成测试使用的源码编译门面:输出不依赖 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(
|
|
root: &AstRoot,
|
|
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,
|
|
Span::default(),
|
|
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,
|
|
message: message.into(),
|
|
expected: Vec::new(),
|
|
}
|
|
}
|