docs(compiler): explain scope analysis and wordcode generation
This commit is contained in:
@@ -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<NameUse>,
|
||||
}
|
||||
|
||||
#[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<Scope>,
|
||||
}
|
||||
|
||||
/// 本文件所有拒绝路径共用,把具体节点跨度与说明包装为 Semantic 诊断。
|
||||
fn error(span: Span, message: impl Into<String>) -> 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<SymbolTable, Diagnostic> {
|
||||
// 交互模式要求恰好一条语句;空输入或批量输入不是本编译入口支持的单位。
|
||||
if let AstRoot::Interactive(input) = root
|
||||
&& input.statements.len() != 1
|
||||
{
|
||||
@@ -67,15 +96,19 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result<SymbolTable,
|
||||
"interactive input must contain exactly one statement",
|
||||
));
|
||||
}
|
||||
// 宿主名称先去重,后续加入源码声明;这里只声明合法名称,不持有实际 VM 值。
|
||||
let mut globals: BTreeSet<String> = 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<SymbolTable,
|
||||
.collect(),
|
||||
AstRoot::Expression(_) => 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, "<module>"),
|
||||
AstRoot::Expression(_) => (ScopeKind::Expression, "<expression>"),
|
||||
AstRoot::Interactive(_) => (ScopeKind::Interactive, "<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<SymbolTable,
|
||||
));
|
||||
}
|
||||
root_scope.intern(base_name);
|
||||
// 字段与方法共用类内去重集合,不能跨两类成员重复名称。
|
||||
let mut members = BTreeSet::new();
|
||||
// 字段名参与类成员去重;类型名留给后续代码签名/运行时类型协议处理。
|
||||
for field in &class.fields {
|
||||
declare(&mut members, &field.name.text, field.span)?;
|
||||
}
|
||||
// 方法名也参与去重,且必须显式接收第一个 self 参数。
|
||||
for method in &class.methods {
|
||||
declare(&mut members, &method.name.text, method.span)?;
|
||||
// 检查第一个参数确实存在且名为 self,不自动插入接收者。
|
||||
if method
|
||||
.parameters
|
||||
.first()
|
||||
@@ -137,37 +185,50 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result<SymbolTable,
|
||||
}
|
||||
// Analyze all bodies after registering all functions, enabling forward
|
||||
// references and recursion without instantiating function objects here.
|
||||
// 所有顶层名注册完才解析函数体,因此允许函数前向引用及递归;任一错误中止整体。
|
||||
let scopes = functions
|
||||
.iter()
|
||||
.map(|function| function_scope(function, &globals))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
// 声明预处理后,按根类型分析真正需要求值的内容。
|
||||
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<SymbolTable,
|
||||
globals.extend(root_scope.scope.layout.names.iter().cloned());
|
||||
root_scope.statements(statements, &globals)?;
|
||||
}
|
||||
// 根交互语句没有函数帧,拒绝 return。
|
||||
StmtKind::Return(_) => {
|
||||
return Err(error(statement.span, "return outside a function"));
|
||||
}
|
||||
@@ -190,7 +252,9 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result<SymbolTable,
|
||||
})
|
||||
}
|
||||
|
||||
/// 顶层、参数、局部及类成员共用的去重检查;仅向传入集合登记,不分配布局索引。
|
||||
fn declare(names: &mut BTreeSet<String>, 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<String>, name: &str, span: Span) -> Result<(), D
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// analyze 为每个顶层函数/类方法调用;参数先占局部槽,再收集全体局部名并按控制流检查初始化。
|
||||
fn function_scope(function: &Function, globals: &BTreeSet<String>) -> Result<Scope, Diagnostic> {
|
||||
// 新建函数/方法作用域,局部布局及初始化状态彼此独立。
|
||||
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<String>) -> Result<Sco
|
||||
Ok(builder.scope)
|
||||
}
|
||||
|
||||
/// function_scope 或交互复合语句调用;递归预分配函数级名称,不在此推断执行后是否已初始化。
|
||||
/// let 重复声明报错,普通赋值首次出现才追加槽,if/while 块共享同一组名称。
|
||||
fn collect_locals(
|
||||
statements: &[frontend::ast::Stmt],
|
||||
locals: &mut BTreeSet<String>,
|
||||
layout: &mut Vec<String>,
|
||||
) -> 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<String>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
) -> Result<bool, Diagnostic> {
|
||||
// 表示当前语句序列是否所有可达路径都已返回,供后继不可达检查使用。
|
||||
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<String>,
|
||||
) -> 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(())
|
||||
|
||||
@@ -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<Item = &CompiledCode> {
|
||||
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<ObjectRef, VmError> {
|
||||
// 记录本次已成功加载的子代码拥有根,外层统一归还,覆盖递归中途失败。
|
||||
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<String>) -> Diagnostic {
|
||||
diagnostic(DiagnosticKind::Compilation, span, message)
|
||||
}
|
||||
|
||||
/// compile_ast 在名称分析成功后调用;按根模式生成最终返回协议并收束为已验证模板。
|
||||
pub(crate) fn generate(
|
||||
root: &AstRoot,
|
||||
source: &SourceUnit,
|
||||
symbols: &SymbolTable,
|
||||
) -> Result<CompiledCode, Diagnostic> {
|
||||
// 根生成器借用已经分析好的根布局,模式分支只决定根的指令及返回值。
|
||||
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<Instruction>,
|
||||
// 按出现顺序追加常量;嵌套代码在 load 前使用 Nil 占位。
|
||||
constants: Vec<Value>,
|
||||
// (跳转逻辑指令索引, 目标逻辑边界索引),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<CompiledCode, Diagnostic> {
|
||||
// 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<String> {
|
||||
// 只接受纯数值及连续取负链,调用、名字或其他表达式不能静态合并。
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user