From 6792ef9a9934ee1a1e1ef85156a86405f2f666a6 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 15 Nov 2026 18:28:02 +0800 Subject: [PATCH] docs(compiler): explain scope analysis and wordcode generation --- crates/compiler/src/analysis.rs | 125 ++++++++++++++++++++++++++++++++ crates/compiler/src/codegen.rs | 119 ++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) diff --git a/crates/compiler/src/analysis.rs b/crates/compiler/src/analysis.rs index 9dea769..1c01e32 100644 --- a/crates/compiler/src/analysis.rs +++ b/crates/compiler/src/analysis.rs @@ -1,3 +1,5 @@ +//! compile_ast 调用的名称与确定初始化分析;只构造 Scope/CodeLayout,不分配 VM 对象。 +//! 先登记所有顶层声明,再分析函数体;codegen 根据名称、跨度和绑定索引选择 LoadFast/LoadGlobal。 use std::collections::BTreeSet; use endor_core::CodeLayout; @@ -7,48 +9,72 @@ use frontend::{Diagnostic, DiagnosticKind, Span}; use crate::{CompileOptions, diagnostic}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// 根节点或函数的编译上下文;生成器据此区分局部槽写入与全局环境写入。 pub enum ScopeKind { + // 模块声明入口,执行后通常返回 Nil。 Module, + // 单个表达式入口,直接返回其求值结果。 Expression, + // 单条交互输入,可声明、赋值或返回表达式结果。 Interactive, + // 函数或类方法体,拥有参数、局部槽和 return 语义。 Function, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// expression 解析的读取位置;codegen 使用此索引编码对应的加载指令。 pub enum NameBinding { + // CodeLayout.locals 中的位置,参数也占局部槽。 Local(usize), + // CodeLayout.names 中的位置,运行时先查 globals 再查捕获的 builtins。 Global(usize), } #[derive(Debug, Clone, PartialEq, Eq)] +/// 一次名称表达式的解析结果;名字加源码跨度定位具体使用点,避免同名出现互相混淆。 pub struct NameUse { + // 名称文本;Scope 使用它作为代码名,NameUse 使用它标识被读取的名称。 pub name: String, + // 原源码中的半开字节范围,用于诊断及生成阶段定位。 pub span: Span, + // 名称读取已解析到局部槽或全局名字索引。 pub binding: NameBinding, } #[derive(Debug, Clone)] +/// 一个代码单元的布局与读取记录,由 analyze/function_scope 构造,Emitter 借用。 pub struct Scope { + // 决定赋值存局部还是全局,以及是否允许 return。 pub kind: ScopeKind, + // 名称文本;Scope 使用它作为代码名,NameUse 使用它标识被读取的名称。 pub name: String, + // 原源码中的半开字节范围,用于诊断及生成阶段定位。 pub span: Span, + // VM 代码验证及帧分配需要的参数、局部名和名字表。 pub layout: CodeLayout, + // 每次名称表达式的绑定,按分析访问顺序追加,供生成器按跨度检索。 pub references: Vec, } #[derive(Debug, Clone)] +/// compile_ast 把整个解析结果交给 codegen;模板生成不再自行猜测名称绑定。 pub struct SymbolTable { + // 根代码单元,即模块、表达式或交互输入的作用域。 pub root: Scope, /// Function and class-method scopes in declaration order. Nested declarations /// are rejected until source closure semantics are implemented. + // 顶层函数和类方法按声明顺序展平;生成器以名称和跨度查找对应体。 pub functions: Vec, } +/// 本文件所有拒绝路径共用,把具体节点跨度与说明包装为 Semantic 诊断。 fn error(span: Span, message: impl Into) -> Diagnostic { diagnostic(DiagnosticKind::Semantic, span, message) } +/// analyze 登记顶层声明时调用,统一取得函数或类的名字节点(含诊断位置)。 fn declaration_name(declaration: &Decl) -> &frontend::ast::Name { + // 按声明种类取相同用途的名字,不把类方法当作顶层全局声明。 match &declaration.kind { DeclKind::Function(function) => &function.name, DeclKind::Class(class) => &class.name, @@ -58,7 +84,10 @@ fn declaration_name(declaration: &Decl) -> &frontend::ast::Name { /// Resolve a closed source unit plus explicitly declared host globals. /// Function locals are collected before reading expressions; a later let /// shadows the global for the entire function, but cannot be read early. +/// compile_ast 和直接分析测试调用;options 仅声明额外全局名,返回根及各函数的绑定布局。 +/// 函数体先收集所有局部名,因此后面的 let 也会遮蔽前面的全局读取,但未初始化读取会报错。 pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result { + // 交互模式要求恰好一条语句;空输入或批量输入不是本编译入口支持的单位。 if let AstRoot::Interactive(input) = root && input.statements.len() != 1 { @@ -67,15 +96,19 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result = options.globals.iter().cloned().collect(); + // 在分析任何代码前拒绝空宿主名,避免不可解析的名称混入绑定集合。 if globals.contains("") { return Err(error(root.span(), "host global names must not be empty")); } + // 提取根级函数/类声明,表达式根没有声明;交互输入只取声明语句。 let declarations: Vec<&Decl> = match root { AstRoot::Module(program) => program.declarations.iter().collect(), AstRoot::Interactive(input) => input .statements .iter() + // 交互输入仅声明语句进入预登记列表,其余语句稍后按执行语义分析。 .filter_map(|statement| match &statement.kind { StmtKind::Declaration(declaration) => Some(declaration), _ => None, @@ -83,32 +116,43 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result Vec::new(), }; + // 展平顶层函数及类方法,保留原声明顺序供统一分析。 let functions: Vec<&Function> = declarations .iter() + // 类本身不生成函数体作用域,其 methods 逐个参与分析。 .flat_map(|declaration| match &declaration.kind { DeclKind::Function(function) => vec![function], DeclKind::Class(class) => class.methods.iter().collect(), }) .collect(); + // 按根类型选作用域种类及用于代码诊断的合成名字。 let (kind, name) = match root { AstRoot::Module(_) => (ScopeKind::Module, ""), AstRoot::Expression(_) => (ScopeKind::Expression, ""), AstRoot::Interactive(_) => (ScopeKind::Interactive, ""), }; + // 根作用域记录声明、属性等名字表及根表达式读取,生成器随后借用它。 let mut root_scope = Builder::new(kind, name, root.span()); + // 单独检查本源码顶层重名;宿主同名声明不在这个集合中。 let mut declared = BTreeSet::new(); + // 遍历根级声明;首轮注册名称,次轮检查类结构,保证函数体分析时名字已齐全。 for declaration in &declarations { + // 取得声明的名字和精确跨度,重名错误定位到名字本身。 let name = declaration_name(declaration); declare(&mut declared, &name.text, name.span)?; globals.insert(name.text.clone()); root_scope.intern(&name.text); } + // 遍历根级声明;首轮注册名称,次轮检查类结构,保证函数体分析时名字已齐全。 for declaration in &declarations { + // 类声明额外检查基类名和成员结构,普通函数跳过这一段。 if let DeclKind::Class(class) = &declaration.kind { + // 未指定基类时默认 Object;这里只确认名称合法,实际 Type 在执行时解析。 let base_name = class .base .as_ref() .map_or("Object", |base| base.text.as_str()); + // 基类必须是宿主或源码已声明的名称,不隐式引入未知全局。 if !globals.contains(base_name) { return Err(error( class.span, @@ -116,12 +160,16 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result Result, _>>()?; + // 声明预处理后,按根类型分析真正需要求值的内容。 match root { + // 模块根只含已处理的声明,没有额外顶层语句需要分析。 AstRoot::Module(_) => {} AstRoot::Expression(expression) => root_scope.expression(expression, &globals)?, + // 交互根按唯一语句的种类决定名称及赋值规则。 AstRoot::Interactive(input) => { + // analyze 开头已保证只有一条,仍按列表结构遍历 AST。 for statement in &input.statements { + // 区分绑定、赋值、成员更新与控制流,不能把 return 当成交互结果。 match &statement.kind { + // 新绑定先检查重名并分析右值,完成后才把名称加入可用全局集合。 StmtKind::Binding(binding) => { declare(&mut declared, &binding.name.text, binding.name.span)?; root_scope.expression(&binding.value, &globals)?; globals.insert(binding.name.text.clone()); root_scope.intern(&binding.name.text); } + // 交互赋值先解析右值,再登记写入目标;目标无需预先存在。 StmtKind::Assignment(assignment) => { root_scope.expression(&assignment.value, &globals)?; globals.insert(assignment.name.text.clone()); root_scope.intern(&assignment.name.text); } + // 成员写入分析接收对象与右值,并把属性名加入名字表。 StmtKind::MemberAssignment(assignment) => { root_scope.expression(&assignment.object, &globals)?; root_scope.expression(&assignment.value, &globals)?; root_scope.intern(&assignment.name.text); } + // 声明已经预登记且函数体已统一分析,不能重复分析。 StmtKind::Declaration(_) => {} + // 交互表达式只需验证读取,不创建新绑定。 StmtKind::Expression(expression) => { root_scope.expression(expression, &globals)? } + // 复合语句先收集块内名称到根名字表,再复用语句控制流分析。 StmtKind::If(_) | StmtKind::While(_) => { + // 借一个节点组成切片,复用预收集和控制流分析,不克隆 AST。 let statements = std::slice::from_ref(statement); collect_locals( statements, @@ -177,6 +238,7 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result { return Err(error(statement.span, "return outside a function")); } @@ -190,7 +252,9 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result, name: &str, span: Span) -> Result<(), Diagnostic> { + // 空名或重复插入失败立即报错;成功 insert 同时完成名称登记。 if name.is_empty() || !names.insert(name.to_owned()) { return Err(error( span, @@ -200,9 +264,13 @@ fn declare(names: &mut BTreeSet, name: &str, span: Span) -> Result<(), D Ok(()) } +/// analyze 为每个顶层函数/类方法调用;参数先占局部槽,再收集全体局部名并按控制流检查初始化。 fn function_scope(function: &Function, globals: &BTreeSet) -> Result { + // 新建函数/方法作用域,局部布局及初始化状态彼此独立。 let mut builder = Builder::new(ScopeKind::Function, &function.name.text, function.span); + // 跟踪参数和局部名是否重复,与当前路径是否初始化分开记录。 let mut locals = BTreeSet::new(); + // 参数在函数进入时已有值,按声明顺序同时登记参数表和前部局部槽。 for parameter in &function.parameters { declare(&mut locals, ¶meter.name.text, parameter.name.span)?; builder @@ -226,47 +294,63 @@ fn function_scope(function: &Function, globals: &BTreeSet) -> Result, layout: &mut Vec, ) -> Result<(), Diagnostic> { + // 依源码顺序遍历当前语句序列,递归进入块但不创建块级局部作用域。 for statement in statements { + // 预收集阶段仅关心能引入名称的语句或嵌套声明拒绝规则。 match &statement.kind { + // let 必须是首次声明,即使出现在不同分支也不能重复同一函数级名。 StmtKind::Binding(b) => { declare(locals, &b.name.text, b.name.span)?; layout.push(b.name.text.clone()); } + // 赋值允许写已有名;仅第一次出现时追加一个布局槽。 StmtKind::Assignment(a) => { + // 首次出现的赋值目标才分配槽;后续普通赋值复用原槽。 if locals.insert(a.name.text.clone()) { layout.push(a.name.text.clone()); } } + // 两侧的名称都预分配;这里不决定分支之后是否能读取它们。 StmtKind::If(b) => { collect_locals(&b.body.statements, locals, layout)?; + // 预收集 else 中的局部名,与 then 和外围共享同一函数级布局。 if let Some(other) = &b.otherwise { collect_locals(&other.statements, locals, layout)?; } } + // 循环体也预分配函数级名字,但执行零次的情况由后续初始化分析处理。 StmtKind::While(b) => collect_locals(&b.body.statements, locals, layout)?, + // 源码闭包/嵌套类尚未支持,不生成看似合法但缺捕获语义的布局。 StmtKind::Declaration(_) => { return Err(error( statement.span, "nested declarations are not compiled yet", )); } + // 不引入名字的表达式、返回和成员写入留给后续语句分析。 _ => {} } } Ok(()) } +/// 分析单个代码单元的可变状态;Scope 是输出,initialized 是当前可达路径上确定初始化的名称。 struct Builder { + // 正在构造的作用域,最终移交 SymbolTable。 scope: Scope, + // 仅控制流中保证赋值的局部名;分支取交集,循环出口恢复进入前集合。 initialized: BTreeSet, } impl Builder { + /// analyze/function_scope 创建空分析器;源码身份和布局初始值在这里统一设置。 fn new(kind: ScopeKind, name: &str, span: Span) -> Self { Self { scope: Scope { @@ -280,7 +364,9 @@ impl Builder { } } + /// 分析名称/属性/声明时调用;复用或追加名字表项,保证生成器查索引时必能找到。 fn intern(&mut self, name: &str) -> usize { + // 按文本查找已登记名字;命中时保留稳定索引。 if let Some(index) = self .scope .layout @@ -290,6 +376,7 @@ impl Builder { { index } else { + // 没有现有名字索引才追加,保留此前已记录的索引。 self.scope.layout.names.push(name.to_owned()); self.scope.layout.names.len() - 1 } @@ -297,75 +384,100 @@ impl Builder { /// Function-scoped locals; branches merge definite initialization. A loop /// may execute zero times, so its writes do not initialize later reads. + /// 函数体与复合语句递归调用;返回本语句序列是否所有可达路径都已 return。 fn statements( &mut self, statements: &[frontend::ast::Stmt], globals: &BTreeSet, ) -> Result { + // 表示当前语句序列是否所有可达路径都已返回,供后继不可达检查使用。 let mut returned = false; + // 按源码顺序推进当前可达路径的初始化状态,检查每条后继语句。 for statement in statements { + // 前面的 return 或双侧必返分支已终止所有路径,后继语句报不可达。 if returned { return Err(error( statement.span, "statement after return is unreachable", )); } + // 按控制流检查实际读取与初始化,不重新分配预收集过的局部槽。 match &statement.kind { + // 先分析右值,之后才把本局部标记已初始化,防止 let x = x 读取自身未绑定槽。 StmtKind::Binding(b) => { self.expression(&b.value, globals)?; self.initialized.insert(b.name.text.clone()); + // 根作用域写入全局而非局部槽,需要为 StoreGlobal 登记名字。 if self.scope.kind != ScopeKind::Function { self.intern(&b.name.text); } } + // 普通赋值同样先检查右值,再令目标在当前路径上已初始化。 StmtKind::Assignment(a) => { self.expression(&a.value, globals)?; self.initialized.insert(a.name.text.clone()); + // 根作用域写入全局而非局部槽,需要为 StoreGlobal 登记名字。 if self.scope.kind != ScopeKind::Function { self.intern(&a.name.text); } } + // 检查对象和右值读取,并为后续 StoreAttr 登记属性名字。 StmtKind::MemberAssignment(a) => { self.expression(&a.object, globals)?; self.expression(&a.value, globals)?; self.intern(&a.name.text); } StmtKind::Expression(e) => self.expression(e, globals)?, + // 只有函数可返回;可选返回表达式先接受同样的名称检查。 StmtKind::Return(r) => { + // 只有函数有 return 语义,根控制流块也不能绕过此限制。 if self.scope.kind != ScopeKind::Function { return Err(error(statement.span, "return outside a function")); } + // 无返回表达式由生成器补 Nil;有表达式时先检查其中所有名称读取。 if let Some(e) = &r.value { self.expression(e, globals)?; } returned = true; } + // 先分析条件,再以相同入口初始化集合分别分析两侧。 StmtKind::If(b) => { self.expression(&b.condition, globals)?; + // 保存控制流入口的确定初始化集合,另一分支及循环出口需要恢复它。 let before = self.initialized.clone(); + // 分析 then 分支并取得其是否必定返回的标志。 let yes_return = self.statements(&b.body.statements, globals)?; + // 保存 then 出口状态,恢复入口后再单独分析 else。 let yes = self.initialized.clone(); self.initialized = before; + // 有 else 时分析其状态;无 else 视为存在不返回的空路径。 let no_return = if let Some(other) = &b.otherwise { self.statements(&other.statements, globals)? } else { false }; + // then 已返回则只保留 else 的继续路径;否则按下方规则合并。 self.initialized = if yes_return { self.initialized.clone() } else if no_return { + // 只有 else 返回时保留 then 的继续路径。 yes } else { + // 两边都能继续时,只保留双方都保证初始化的名称。 yes.intersection(&self.initialized).cloned().collect() }; + // 只有两侧都必返时整个 if 才让后继语句不可达。 returned = yes_return && no_return; } + // 检查条件及循环体,但循环可能零次执行,出口恢复入口初始化状态。 StmtKind::While(b) => { self.expression(&b.condition, globals)?; + // 保存控制流入口的确定初始化集合,另一分支及循环出口需要恢复它。 let before = self.initialized.clone(); self.statements(&b.body.statements, globals)?; self.initialized = before; } + // 函数体/复合语句不支持嵌套声明,不能在这里隐式生成闭包作用域。 StmtKind::Declaration(_) => { return Err(error( statement.span, @@ -377,13 +489,17 @@ impl Builder { Ok(returned) } + /// analyze/statements 及自身递归调用;检查名称已声明/已初始化,记录读取绑定并登记属性名字。 fn expression( &mut self, expression: &Expr, globals: &BTreeSet, ) -> Result<(), Diagnostic> { + // 递归覆盖名称读取和子表达式;字面量不需要绑定。 match &expression.kind { + // 局部名优先于全局,即便这个局部尚未初始化也不回退到同名全局。 ExprKind::Name(name) => { + // 先查完整局部布局,未命中才考虑合法全局名,不按声明位置临时改变遮蔽规则。 let binding = if let Some(index) = self .scope .layout @@ -391,6 +507,7 @@ impl Builder { .iter() .position(|local| local == name) { + // 槽已预分配不代表当前路径已经赋值,未确定初始化必须拒绝读取。 if !self.initialized.contains(name) { return Err(error( expression.span, @@ -399,33 +516,41 @@ impl Builder { } NameBinding::Local(index) } else if globals.contains(name) { + // 没有同名局部时,宿主或源码登记过的全局名才能读取。 NameBinding::Global(self.intern(name)) } else { return Err(error(expression.span, format!("undefined name: {name}"))); }; + // 为此名字及源码位置保存绑定,codegen 据此选择 LoadFast 或 LoadGlobal。 self.scope.references.push(NameUse { name: name.clone(), span: expression.span, binding, }); } + // 一元及 await 都只分析操作数;是否允许实际等待由执行器处理。 ExprKind::Unary { operand, .. } | ExprKind::Await(operand) => { self.expression(operand, globals)? } + // 按左右顺序分析所有读取,类型及运算协议由运行时处理。 ExprKind::Binary { left, right, .. } => { self.expression(left, globals)?; self.expression(right, globals)?; } + // 对象表达式接受名称检查,属性本身仅登记为名字表项。 ExprKind::Member { object, name } => { self.expression(object, globals)?; self.intern(&name.text); } + // 调用目标和每个实参都可能读取名称,逐个递归检查。 ExprKind::Call { callee, arguments } => { self.expression(callee, globals)?; + // 按源码顺序检查参数,错误带各自表达式的位置。 for argument in arguments { self.expression(argument, globals)?; } } + // 字面量无变量读取;Symbol 是否可生成运行时值由 codegen 再拒绝。 ExprKind::Number(_) | ExprKind::String(_) | ExprKind::Symbol(_) => {} } Ok(()) diff --git a/crates/compiler/src/codegen.rs b/crates/compiler/src/codegen.rs index 215c762..50b4789 100644 --- a/crates/compiler/src/codegen.rs +++ b/crates/compiler/src/codegen.rs @@ -1,3 +1,5 @@ +//! compile_ast 的字节码生成阶段:消费 analysis 的稳定布局,先产生逻辑指令,再解析物理跳转距离。 +//! 编译产物没有 VM 对象引用;load 才递归分配子代码并替换常量占位,交由宿主执行。 use endor_core::{ CodeLayout, DecodedInstruction, EndorCode, EndorNumber, Instruction, ObjectRef, Opcode, Value, Vm, VmError, @@ -10,30 +12,40 @@ use crate::{NameBinding, Scope, ScopeKind, SymbolTable, diagnostic}; /// Validated, heap-independent executable template. Nested code constants /// remain templates until load(), so no ObjectRef is shared across VM heaps. #[derive(Debug, Clone)] +/// 宿主和编译集成测试取得的已验证模板,可多次加载到不同 VM,不跨堆共享 ObjectRef。 pub struct CompiledCode { + // 当前代码单元,嵌套代码位置先放 Nil;load 时构造目标 VM 专属的常量表。 code: EndorCode, + // (父常量索引, 子模板);Emitter 记录待替换位置,CompiledCode::load 递归装入。 children: Vec<(usize, CompiledCode)>, } impl CompiledCode { + /// 宿主/测试检查已解码指令及偏移,借用模板,不执行代码。 pub fn instructions(&self) -> &[DecodedInstruction] { self.code.instructions() } + /// 宿主/测试检查参数、局部与名字布局,与实际装载代码保持一致。 pub fn layout(&self) -> &CodeLayout { self.code.layout() } + /// 宿主/测试取得分析作用域的代码名,用于显示或诊断。 pub fn name(&self) -> &str { self.code.name() } + /// 宿主/测试检查函数源码类型注解;根模板通常没有函数签名。 pub fn source_signature(&self) -> Option<&endor_core::SourceSignature> { self.code.source_signature() } + /// 宿主/测试查看来自 SourceUnit 的来源标识,装载后用于回溯定位。 pub fn filename(&self) -> &str { self.code.filename() } + /// 宿主/测试查看作用域起点对应的源码行,缺少有效位置时为一。 pub fn first_line(&self) -> usize { self.code.first_line() } + /// 宿主/测试按登记顺序遍历直接子模板,不暴露内部常量占位索引。 pub fn children(&self) -> impl Iterator { self.children.iter().map(|(_, child)| child) } @@ -41,15 +53,23 @@ impl CompiledCode { /// Return one owned code-object root; release it after execution. The /// parent retains its nested code constants. Temporary child roots are /// released on both success and failure, including partial recursive loads. + /// 宿主/测试显式装入目标 VM;成功返回一个拥有代码根,调用者执行完成后负责 release。 + /// 已分配子根无论后续成功或失败都在外层归还,父代码成功分配时已有自己的常量内部边。 pub fn load(&self, vm: &mut Vm) -> Result { + // 记录本次已成功加载的子代码拥有根,外层统一归还,覆盖递归中途失败。 let mut roots = Vec::new(); + // 把可失败步骤放进闭包,使 ? 只退出该阶段,随后仍执行根清理。 let result = (|| { + // 复制模板常量,不修改共享模板本身;子代码位置稍后替换为目标堆句柄。 let mut constants = self.code.constants().to_vec(); + // 递归装入每个子模板,失败时先前成功得到的根仍在 roots 中可清理。 for (index, child) in &self.children { + // 子装载成功返回一个拥有根,父对象尚未分配,必须先临时保存它。 let reference = child.load(vm)?; roots.push(reference); constants[*index] = Value::Object(reference); } + // 用目标 VM 的子代码常量重新构造并验证代码,再恢复原位置元数据。 let mut code = EndorCode::with_layout( self.code.bytecode().to_vec(), constants, @@ -60,11 +80,13 @@ impl CompiledCode { self.code.filename(), self.code.first_line(), ); + // 函数签名随模板一同复制,保证装载后仍有类型注解/方法归属信息。 if let Some(signature) = self.code.source_signature() { code = code.with_source_signature(signature.clone())?; } vm.new_code(code) })(); + // 父代码已取得内部边或装载失败后,临时子根都必须释放一次。 for reference in roots { vm.release(reference) .expect("loader owns each temporary code root"); @@ -73,18 +95,23 @@ impl CompiledCode { } } +/// 生成、装载前验证或编码失败的公共包装,附上当前源码跨度并标为 Compilation。 fn error(span: Span, message: impl Into) -> Diagnostic { diagnostic(DiagnosticKind::Compilation, span, message) } +/// compile_ast 在名称分析成功后调用;按根模式生成最终返回协议并收束为已验证模板。 pub(crate) fn generate( root: &AstRoot, source: &SourceUnit, symbols: &SymbolTable, ) -> Result { + // 根生成器借用已经分析好的根布局,模式分支只决定根的指令及返回值。 let mut emitter = Emitter::new(&symbols.root, source); + // 模块生成声明并返回 Nil,表达式返回其值,交互语句返回表达式或 Nil。 match root { AstRoot::Module(program) => { + // 源码声明依原顺序生成,函数调用的前向名称由执行完模块后的环境提供。 for declaration in &program.declarations { emitter.declaration(declaration, symbols)?; } @@ -96,7 +123,9 @@ pub(crate) fn generate( emitter.simple(Opcode::ReturnValue); } AstRoot::Interactive(input) => { + // analyze 已限制交互输入只有一条;这里复用 AST 列表结构。 for statement in &input.statements { + // 交互表达式保留结果作为根返回值,其他语句执行后补 Nil。 if let StmtKind::Expression(expression) = &statement.kind { emitter.expression(expression)?; } else { @@ -110,16 +139,24 @@ pub(crate) fn generate( emitter.finish() } +/// 单个代码单元的生成状态;借用分析布局与源码,持有逻辑指令、常量及尚未解析的跳转。 struct Emitter<'a> { + // 当前根/函数作用域,提供名字、槽索引及源跨度。 scope: &'a Scope, + // 代码来源和文本快照,finish 据此设置文件身份与起始行。 source: &'a SourceUnit, + // 尚未编码扩展前缀的逻辑指令;索引不能直接当作物理 code-unit 偏移。 instructions: Vec, + // 按出现顺序追加常量;嵌套代码在 load 前使用 Nil 占位。 constants: Vec, + // (跳转逻辑指令索引, 目标逻辑边界索引),finish 根据编码宽度计算相对距离。 jumps: Vec<(usize, usize)>, + // (父常量索引, 子模板);Emitter 记录待替换位置,CompiledCode::load 递归装入。 children: Vec<(usize, CompiledCode)>, } impl<'a> Emitter<'a> { + /// generate/function_value 创建当前代码单元的空生成器,各函数独享指令及常量列表。 fn new(scope: &'a Scope, source: &'a SourceUnit) -> Self { Self { scope, @@ -131,23 +168,29 @@ impl<'a> Emitter<'a> { } } + /// 各生成分支追加带参数指令;usize 索引/数量必须能装入 VM 的 u32 操作数。 fn emit(&mut self, opcode: Opcode, argument: usize) -> Result<(), Diagnostic> { + // 在编码前检查数量或索引宽度,超界映射成当前作用域诊断。 let argument = u32::try_from(argument) .map_err(|_| error(self.scope.span, "bytecode operand exceeds u32"))?; self.instructions.push(Instruction::new(opcode, argument)); Ok(()) } + /// 各分支追加不带操作数指令,统一使用零参数编码。 fn simple(&mut self, opcode: Opcode) { self.instructions.push(Instruction::simple(opcode)); } + /// 表达式、声明和补 Nil 路径共用;追加常量并发出 LoadConst,执行时压入值。 fn constant(&mut self, value: Value) -> Result<(), Diagnostic> { + // 下一常量槽位;函数模板登记和常量加载都必须使用追加前的长度。 let index = self.constants.len(); self.constants.push(value); self.emit(Opcode::LoadConst, index) } + /// 声明、属性和全局写入查分析阶段登记的名字;缺项代表编译器内部阶段不一致。 fn name_index(&self, name: &str) -> usize { self.scope .layout @@ -157,24 +200,31 @@ impl<'a> Emitter<'a> { .expect("analysis interns every emitted name") } + /// generate/statement 调用,把顶层函数或类构造成运行时值并写入对应全局名。 fn declaration(&mut self, declaration: &Decl, symbols: &SymbolTable) -> Result<(), Diagnostic> { + // 函数直接构造值,类还需按 BuildClass 协议压入名字、基类、字段表与方法表。 match &declaration.kind { + // 先创建函数值再 StoreGlobal,函数体此时尚未调用。 DeclKind::Function(function) => { self.function_value(function, symbols, None)?; self.emit(Opcode::StoreGlobal, self.name_index(&function.name.text)) } + // 构造类元数据;父类名来自分析阶段校验过的名字表。 DeclKind::Class(class) => { self.constant(Value::Text(class.name.text.clone()))?; + // 未显式指定时查全局 Object,真正的父类型对象由运行时解析。 let base = class .base .as_ref() .map_or("Object", |base| base.text.as_str()); self.emit(Opcode::LoadGlobal, self.name_index(base))?; + // 每字段压入名称和类型名文本,随后 BuildMap 汇总为字段注解映射。 for field in &class.fields { self.constant(Value::Text(field.name.text.clone()))?; self.constant(Value::Text(field.type_name.text.clone()))?; } self.emit(Opcode::BuildMap, class.fields.len())?; + // 每方法压入名称及源码函数值,签名额外记录当前类名。 for method in &class.methods { self.constant(Value::Text(method.name.text.clone()))?; self.function_value(method, symbols, Some(&class.name.text))?; @@ -186,21 +236,27 @@ impl<'a> Emitter<'a> { } } + /// declaration 为普通函数或类方法调用;生成子模板、附源码类型签名并发出 MakeFunction。 + /// owner 仅类方法传入类名,供运行时签名校验使用;当前源码函数没有默认值与闭包捕获。 fn function_value( &mut self, function: &frontend::ast::Function, symbols: &SymbolTable, owner: Option<&str>, ) -> Result<(), Diagnostic> { + // 按源码跨度及名称定位函数分析结果,避免不同类的同名方法混用布局。 let scope = symbols .functions .iter() .find(|scope| scope.span == function.span && scope.name == function.name.text) .expect("analysis creates each function scope"); + // 函数体使用独立生成器,其常量、指令和跳转不混入外层代码。 let mut body = Emitter::new(scope, self.source); + // 按源码顺序生成函数体语句,语义和不可达错误已由 analysis 检查。 for statement in &function.body.statements { body.statement(statement, symbols)?; } + // 末条语句不是显式 return 时补隐式 Nil 返回;末条 if 即使双侧返回也会补兜底出口。 if !matches!( function .body @@ -212,7 +268,9 @@ impl<'a> Emitter<'a> { body.constant(Value::Nil)?; body.simple(Opcode::ReturnValue); } + // 先完成子代码跳转及验证,再把参数/返回类型注解附到模板。 let mut child = body.finish()?; + // 签名保留源码注解名,运行时在环境中解析类型;类方法额外记录 owner。 child.code = child .code .with_source_signature(endor_core::SourceSignature { @@ -225,16 +283,23 @@ impl<'a> Emitter<'a> { method_owner: owner.map(str::to_owned), }) .map_err(|failure| error(function.span, failure.to_string()))?; + // 下一常量槽位;函数模板登记和常量加载都必须使用追加前的长度。 let index = self.constants.len(); + // 子模板与即将追加的 Nil 常量槽成对保存,load 才替换为实际代码句柄。 self.children.push((index, child)); + // 为子代码保留常量槽,load 时用已分配的托管代码对象替换 Nil。 self.constant(Value::Nil)?; // Replaced by a managed code object during load(). + // 源码函数当前没有默认参数或自由 Cell,按零默认值、零捕获构造。 self.instructions.push(Instruction::make_function(0, 0)); Ok(()) } + /// statement 的 let/赋值共用;先生成右值,再按作用域写局部槽或全局名。 fn store_binding(&mut self, name: &str, value: &Expr) -> Result<(), Diagnostic> { self.expression(value)?; + // 函数级目标写 StoreFast,其余根写 StoreGlobal,右值已经在栈顶。 if self.scope.kind == ScopeKind::Function { + // 从预分配局部表取得目标槽,分析阶段保证所有绑定和赋值目标存在。 let index = self .scope .layout @@ -249,48 +314,65 @@ impl<'a> Emitter<'a> { Ok(()) } + /// generate/function_value 及块递归调用,维护语句栈平衡,并记录待 finish 解析的跳转目标。 fn statement(&mut self, statement: &Stmt, symbols: &SymbolTable) -> Result<(), Diagnostic> { + // 语句需消费自身临时值;return 和控制流分别生成出口或跳转。 match &statement.kind { + // 条件为假跳到 else 起点,then 结束无条件越过 else。 StmtKind::If(branch) => { self.expression(&branch.condition)?; + // 保存条件跳转的逻辑索引,当前先用零操作数占位。 let conditional = self.instructions.len(); self.emit(Opcode::PopJumpIfFalse, 0)?; + // 递归生成 then 语句,嵌套跳转同样追加到当前生成器。 for statement in &branch.body.statements { self.statement(statement, symbols)?; } + // 记录 then 尾部无条件跳转位置,稍后指向整个 if 之后。 let end = self.instructions.len(); self.emit(Opcode::JumpForward, 0)?; self.jumps.push((conditional, self.instructions.len())); + // 存在 else 才生成其代码;无 else 时两个出口可指向同一边界。 if let Some(otherwise) = &branch.otherwise { + // else 同样保持语句栈平衡,结束后接 if 的共同后继。 for statement in &otherwise.statements { self.statement(statement, symbols)?; } } self.jumps.push((end, self.instructions.len())); } + // 每次回到条件开头,条件为假退出;记录两个逻辑跳转后交 finish 解析。 StmtKind::While(loop_) => { + // 保存条件求值起点,回边必须重算条件而非仅回到循环体。 let start = self.instructions.len(); self.expression(&loop_.condition)?; + // 保存条件失败的占位跳转位置。 let exit = self.instructions.len(); self.emit(Opcode::PopJumpIfFalse, 0)?; + // 生成循环体,再生成回到条件开头的后向跳转。 for statement in &loop_.body.statements { self.statement(statement, symbols)?; } + // 回边指令的逻辑位置,用于计算从该指令末端到 start 的距离。 let back = self.instructions.len(); self.emit(Opcode::JumpBackward, 0)?; self.jumps.push((back, start)); self.jumps.push((exit, self.instructions.len())); } StmtKind::Binding(binding) => self.store_binding(&binding.name.text, &binding.value)?, + // 普通赋值与 let 共用存储规则;分析阶段已确定目标槽或全局名。 StmtKind::Assignment(assignment) => { self.store_binding(&assignment.name.text, &assignment.value)? } + // StoreAttr 协议要求先压右值,再压接收对象,最后由指令消费两者。 StmtKind::MemberAssignment(assignment) => { self.expression(&assignment.value)?; self.expression(&assignment.object)?; self.emit(Opcode::StoreAttr, self.name_index(&assignment.name.text))?; } + // 返回表达式或补 Nil,然后 ReturnValue 将值交给调用方。 StmtKind::Return(statement) => { + // 显式返回值正常求值;无表达式的 return 需要先压入 Nil。 if let Some(value) = &statement.value { self.expression(value)?; } else { @@ -298,6 +380,7 @@ impl<'a> Emitter<'a> { } self.simple(Opcode::ReturnValue); } + // 语句位置不保留结果,PopTop 使后续语句开始时栈深恢复。 StmtKind::Expression(expression) => { self.expression(expression)?; self.simple(Opcode::PopTop); @@ -307,40 +390,51 @@ impl<'a> Emitter<'a> { Ok(()) } + /// 根、语句或子表达式递归调用;成功路径把一个值留栈,具体对象协议在 VM 执行时处理。 fn expression(&mut self, expression: &Expr) -> Result<(), Diagnostic> { + // 按 AST 种类发出求值序列,每个成功分支最终留一个值。 match &expression.kind { ExprKind::String(value) => self.constant(Value::Text(value.clone()))?, ExprKind::Number(spelling) => self.number(spelling, expression.span)?, + // 使用分析记录,不能在生成时重新猜测局部与全局遮蔽关系。 ExprKind::Name(name) => { + // 按名字及本次使用的跨度找到具体绑定,不混淆同名的不同读取位置。 let reference = self .scope .references .iter() .find(|reference| reference.span == expression.span && &reference.name == name) .expect("analysis resolves every name expression"); + // 局部槽用 LoadFast,全局名字索引用 LoadGlobal。 let (opcode, index) = match reference.binding { NameBinding::Local(index) => (Opcode::LoadFast, index), NameBinding::Global(index) => (Opcode::LoadGlobal, index), }; self.emit(opcode, index)?; } + // 按 VM 调用布局压被调对象、Null 接收者哨兵及各实参;Call 使用实参数量。 ExprKind::Call { callee, arguments } => { self.expression(callee)?; self.simple(Opcode::PushNull); + // 参数按源码顺序求值,每个结果在栈上保留到 Call 消费。 for argument in arguments { self.expression(argument)?; } self.emit(Opcode::Call, arguments.len())?; } + // 仅求值一次等待对象,再由 Await 指令负责停放/轮询;生成器不实现调度。 ExprKind::Await(operand) => { self.expression(operand)?; self.simple(Opcode::Await); } + // 先求接收对象,再用分析已登记的属性名字索引读取。 ExprKind::Member { object, name } => { self.expression(object)?; self.emit(Opcode::LoadAttr, self.name_index(&name.text))?; } + // 纯取负字面量直接合并精确拼写,否则生成操作数及运行时 UnaryNegate。 ExprKind::Unary { operand, .. } => { + // 只折叠数值及连续取负,避免先把超长十进制转换为浮点。 if let Some(spelling) = signed_literal(expression, false) { self.number(&spelling, expression.span)?; } else { @@ -348,6 +442,7 @@ impl<'a> Emitter<'a> { self.simple(Opcode::UnaryNegate); } } + // 左右操作数依次压栈,再由算术或比较指令消费两个值并返回一个值。 ExprKind::Binary { operation, left, @@ -355,11 +450,13 @@ impl<'a> Emitter<'a> { } => { self.expression(left)?; self.expression(right)?; + // 算术使用 BinaryArithmetic 子操作码;其余已知二元操作编码为比较。 match operation { BinaryOp::Add => self.emit(Opcode::BinaryArithmetic, 0)?, BinaryOp::Subtract => self.emit(Opcode::BinaryArithmetic, 1)?, BinaryOp::Multiply => self.emit(Opcode::BinaryArithmetic, 2)?, BinaryOp::Divide => self.emit(Opcode::BinaryArithmetic, 3)?, + // 算术种类已在前四个分支处理,此分支只应剩余比较操作。 _ => self .instructions .push(Instruction::compare(match operation { @@ -373,6 +470,7 @@ impl<'a> Emitter<'a> { })), } } + // 前端能表示符号字面量,但当前运行时无对应值,明确拒绝生成。 ExprKind::Symbol(_) => { return Err(error( expression.span, @@ -383,7 +481,9 @@ impl<'a> Emitter<'a> { Ok(()) } + /// 数值字面量和已合并符号的字面量共用,直接从十进制拼写构造精确数值,不经过 f64。 fn number(&mut self, spelling: &str, span: Span) -> Result<(), Diagnostic> { + // 根据拼写中的小数点选择精确十进制或整数解析,失败保留字面量跨度。 let value = if spelling.contains('.') { EndorNumber::decimal(spelling) } else { @@ -393,12 +493,17 @@ impl<'a> Emitter<'a> { self.constant(Value::Number(value)) } + /// generate/function_value 结束当前单元时调用;迭代修正扩展指令宽度与相对跳转,再验证编码及布局。 fn finish(mut self) -> Result { // Labels refer to logical instructions. Resolve relative code-unit // offsets after EXTENDED_ARG expansion, to a fixed point. + // 跳转距离变大会改变 EXTENDED_ARG 数量,最多迭代六十四轮求稳定布局。 for attempt in 0..64 { + // 记录每条逻辑指令起点及尾部边界的物理 code-unit 偏移,初始零。 let mut offsets = vec![0_usize]; + // 按当前操作数宽度计算一到四个物理单元,包含必要的扩展前缀。 for i in &self.instructions { + // 操作数每多八位需要一个 EXTENDED_ARG 前缀,最终 opcode 本身占一个单元。 let units = if i.argument <= 0xff { 1 } else if i.argument <= 0xffff { @@ -410,30 +515,38 @@ impl<'a> Emitter<'a> { }; offsets.push(offsets.last().unwrap() + units); } + // 本轮若任一跳转操作数改变,就需要重算可能变化的指令宽度。 let mut changed = false; + // 每个目标是逻辑边界,换算时必须使用本轮物理 offsets。 for &(index, target) in &self.jumps { + // 相对距离以完整跳转指令的末端为基准,后向与前向分别相减。 let distance = if self.instructions[index].opcode == Opcode::JumpBackward { offsets[index + 1] - offsets[target] } else { offsets[target] - offsets[index + 1] }; + // 距离也受 u32 编码限制,超界向宿主返回诊断而非截断。 let operand = u32::try_from(distance) .map_err(|_| error(self.scope.span, "jump exceeds u32"))?; changed |= self.instructions[index].argument != operand; self.instructions[index].argument = operand; } + // 所有跳转操作数稳定时,当前宽度与距离自洽,可结束迭代。 if !changed { break; } + // 达到迭代上限仍变化时拒绝产物,不能输出未收敛的跳转。 if attempt == 63 { return Err(error(self.scope.span, "jump layout did not converge")); } } + // 从作用域跨度取起始行,无法定位时使用第一行作为回溯元数据。 let line = self .scope .span .location(self.source.text()) .map_or(1, |location| location.line); + // 编码并验证控制流、栈和布局,错误归到当前作用域,再附源码身份。 let code = EndorCode::from_instructions( &self.instructions, self.constants, @@ -448,17 +561,23 @@ impl<'a> Emitter<'a> { } } +/// expression 的一元负号分支调用;仅对纯数值和连续取负递归合并符号,其余表达式交运行时求值。 +/// negative 记录累计取负奇偶;每遇到一个 Negate 翻转,返回最终精确数字拼写。 fn signed_literal(expression: &Expr, negative: bool) -> Option { + // 只接受纯数值及连续取负链,调用、名字或其他表达式不能静态合并。 match &expression.kind { + // 遇到数值终点,根据累计取负奇偶返回带或不带负号的精确拼写。 ExprKind::Number(spelling) => Some(if negative { format!("-{spelling}") } else { spelling.clone() }), + // 连续负号每层翻转一次,最后统一生成正确符号的数值拼写。 ExprKind::Unary { operation: UnaryOp::Negate, operand, } => signed_literal(operand, !negative), + // 非字面量链返回 None,由 expression 生成实际运行时操作。 _ => None, } }