feat: 改进函数调用和模块处理机制
- 补充模块名以生成全限定函数名,增强模块间调用的准确性 - 重构 IRProgramBuilder 以更清晰地处理模块和函数 - 优化 VMCodeGenerator 和 VMProgramBuilder 以支持新的全限定名机制
This commit is contained in:
parent
15cd43a7d5
commit
cadace165a
@ -5,96 +5,89 @@ import org.jcnc.snow.compiler.backend.utils.OpHelper;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 虚拟机代码生成器(VMCodeGenerator)
|
||||
* VMCodeGenerator 负责将中间表示(IR)函数转换为目标虚拟机(VM)的指令序列。
|
||||
* <p>
|
||||
* 本类作为指令生成器调度中心,不负责任何具体 IR 指令到 VM 指令的转换实现,
|
||||
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。
|
||||
* </p>
|
||||
* <p>
|
||||
* 工作流程简述:
|
||||
* <ol>
|
||||
* <li>接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。</li>
|
||||
* <li>遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。</li>
|
||||
* <li>生成流程以函数为单位(beginFunction/endFunction)。</li>
|
||||
* </ol>
|
||||
* 每个 IR 指令根据类型由对应的 InstructionGenerator 处理,并将结果输出到 VMProgramBuilder。
|
||||
* 该类通过注册表(registry)实现 IR 到 VM 指令生成器的快速分发。
|
||||
*/
|
||||
public final class VMCodeGenerator {
|
||||
|
||||
/**
|
||||
* 指令类型到生成器的注册表(调度表)。
|
||||
* <p>
|
||||
* 键: IR 指令类型(Class对象),
|
||||
* 值: 对应的指令生成器实例。
|
||||
* </p>
|
||||
* IR 指令类型到指令生成器的映射。
|
||||
* 每种 IRInstruction 都有对应的 InstructionGenerator 处理。
|
||||
*/
|
||||
private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry;
|
||||
|
||||
/**
|
||||
* 虚拟寄存器到槽号的映射表,由 RegisterAllocator 负责生成。
|
||||
* 虚拟寄存器到 VM 局部槽位的映射表。
|
||||
* 用于寄存器分配与指令生成。
|
||||
*/
|
||||
private final Map<IRVirtualRegister, Integer> slotMap;
|
||||
|
||||
/**
|
||||
* 虚拟机程序构建器,用于输出 VM 指令。
|
||||
* 输出目标 VM 程序的构建器。
|
||||
* 提供 emit、beginFunction、endFunction 等接口。
|
||||
*/
|
||||
private final VMProgramBuilder out;
|
||||
|
||||
/**
|
||||
* 当前处理的函数名,用于部分指令生成逻辑(如主函数判断等)。
|
||||
* 当前正在处理的函数名。
|
||||
* 用于区分是否为 main 函数等用途。
|
||||
*/
|
||||
private String currentFn;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* 构造 VMCodeGenerator。
|
||||
*
|
||||
* @param slotMap 虚拟寄存器到槽号的映射
|
||||
* @param out 虚拟机程序构建器
|
||||
* @param generators 各类 IR 指令生成器集合,需预先构建
|
||||
* @param slotMap 虚拟寄存器到 VM 局部槽位的分配表
|
||||
* @param out 输出 VM 程序的 builder
|
||||
* @param generators 可用的 IR 指令生成器列表,每个类型只应有一个
|
||||
*/
|
||||
public VMCodeGenerator(Map<IRVirtualRegister, Integer> slotMap,
|
||||
VMProgramBuilder out,
|
||||
List<InstructionGenerator<? extends IRInstruction>> generators) {
|
||||
this.slotMap = slotMap;
|
||||
this.out = out;
|
||||
// 按类型注册各 IR 指令生成器,建立不可变类型-生成器映射表
|
||||
// 构建不可变的类型到生成器的注册表
|
||||
this.registry = generators.stream()
|
||||
.collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g));
|
||||
}
|
||||
|
||||
/**
|
||||
* 为一个 IR 函数生成虚拟机指令
|
||||
* 将 IRFunction 生成对应 VM 代码,并写入输出。
|
||||
*
|
||||
* @param fn 待生成的 IR 函数对象
|
||||
* @throws IllegalStateException 若遇到不支持的 IR 指令类型
|
||||
* <ol>
|
||||
* <li>调用 {@code out.beginFunction} 标记函数起始。</li>
|
||||
* <li>遍历函数体的每条 IR 指令,查找对应 InstructionGenerator 并生成目标代码。</li>
|
||||
* <li>对 main/main.xxx 函数追加 HALT 指令,其它函数追加 RET。</li>
|
||||
* <li>调用 {@code out.endFunction} 结束函数。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param fn 需要生成 VM 代码的 IRFunction
|
||||
* @throws IllegalStateException 如果遇到不支持的 IR 指令类型
|
||||
*/
|
||||
public void generate(IRFunction fn) {
|
||||
this.currentFn = fn.name();
|
||||
|
||||
/* 登记函数入口地址 —— 解决 CALL 未解析符号问题 */
|
||||
out.beginFunction(currentFn);
|
||||
|
||||
/* 逐条分发 IR 指令给对应的生成器 */
|
||||
for (IRInstruction ins : fn.body()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
// 查找合适的指令生成器
|
||||
InstructionGenerator<IRInstruction> gen =
|
||||
(InstructionGenerator<IRInstruction>) registry.get(ins.getClass());
|
||||
if (gen == null) {
|
||||
throw new IllegalStateException("Unsupported IR: " + ins);
|
||||
}
|
||||
// 调用生成器生成对应的 VM 指令
|
||||
gen.generate(ins, out, slotMap, currentFn);
|
||||
}
|
||||
|
||||
/* 强制补上函数结尾的返回/终止指令 */
|
||||
String retOpcode = "main".equals(currentFn) ? "HALT" : "RET";
|
||||
// 结尾指令:main 函数统一用 HALT,其他函数用 RET
|
||||
String retOpcode = ("main".equals(currentFn) || currentFn.endsWith(".main")) ? "HALT" : "RET";
|
||||
out.emit(OpHelper.opcode(retOpcode));
|
||||
|
||||
/* 结束函数 */
|
||||
out.endFunction();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,109 +1,109 @@
|
||||
package org.jcnc.snow.compiler.backend.builder;
|
||||
|
||||
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* VMProgramBuilder: 构建线性 VM 程序(即按顺序存放所有 VM 指令)。
|
||||
* VMProgramBuilder 用于构建虚拟机(VM)的最终指令列表。
|
||||
* <p>
|
||||
* 本类用于编译器后端,将所有生成的 VM 指令(包括分支和调用指令)统一存储、管理。
|
||||
* 支持符号(如函数入口、标签地址)的延迟解析与回填(fix-up)机制,
|
||||
* 可在目标尚未定义时提前生成分支或调用指令,定义后自动修正。
|
||||
* </p>
|
||||
* <p>
|
||||
* 常用于处理跨函数、跨标签的 CALL/JUMP 等复杂控制流,确保最终生成的 VM 指令地址一致正确。
|
||||
* </p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>维护代码指令序列和符号地址表</li>
|
||||
* <li>支持跨函数、标签跳转的延后修补(Call/Branch Fixup)</li>
|
||||
* <li>支持虚拟机本地槽位类型的管理(如 I/F...)</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class VMProgramBuilder {
|
||||
/** 未解析目标的 CALL 指令信息(待修补) */
|
||||
|
||||
/**
|
||||
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)。
|
||||
* @param index CALL 指令在 code 列表中的位置
|
||||
* @param target 目标函数的全名
|
||||
* @param nArgs 参数个数
|
||||
*/
|
||||
private record CallFix(int index, String target, int nArgs) {}
|
||||
|
||||
/** 未解析目标的分支指令(JUMP/IC_* 等待修补) */
|
||||
/**
|
||||
* 未知目标的分支指令修补记录(待目标标签确定后修正)。
|
||||
* @param index 分支指令在 code 列表中的位置
|
||||
* @param label 跳转目标标签名
|
||||
*/
|
||||
private record BranchFix(int index, String label) {}
|
||||
|
||||
/** 占位符: 用于表示尚未确定的符号地址 */
|
||||
/** 未解析地址的占位符,便于后期批量修补 */
|
||||
private static final String PLACEHOLDER = "-1";
|
||||
|
||||
/** 按顺序存放的 VM 指令文本 */
|
||||
/** VM 指令列表 */
|
||||
private final List<String> code = new ArrayList<>();
|
||||
|
||||
// 虚拟机槽位编号到数据类型前缀的映射(如 0 -> 'I', 1 -> 'D' 等)
|
||||
/** 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化) */
|
||||
private final Map<Integer, Character> slotType = new HashMap<>();
|
||||
|
||||
/** 符号(如函数名、标签名)到其首地址(即指令序号/偏移量)的映射表
|
||||
* 主要用于跳转和调用,定位具体的代码位置 */
|
||||
/** 符号(函数名/标签)到指令序号的映射表 */
|
||||
private final Map<String, Integer> addr = new HashMap<>();
|
||||
|
||||
/** 所有待回填(fix-up)的 CALL 调用指令记录
|
||||
* 由于被调用目标地址在编译时可能尚未确定,需要先记录,最终统一回填 */
|
||||
/** 所有待修补的 CALL 指令集合 */
|
||||
private final List<CallFix> callFixes = new ArrayList<>();
|
||||
|
||||
/** 所有待回填(fix-up)的分支跳转指令记录
|
||||
* 与 CALL 类似,分支指令的目标地址也可能需要编译后期再补充 */
|
||||
/** 所有待修补的分支指令集合 */
|
||||
private final List<BranchFix> branchFixes = new ArrayList<>();
|
||||
|
||||
/** 程序计数器(Program Counter),表示下一个生成指令将插入的位置 */
|
||||
/** 当前代码指针(已生成指令的数量/下一个指令的位置) */
|
||||
private int pc = 0;
|
||||
|
||||
/**
|
||||
* 设置某个槽位对应的数据类型前缀
|
||||
* 设置槽位(局部变量/虚拟寄存器)的类型前缀。
|
||||
*
|
||||
* @param slot 槽位编号
|
||||
* @param prefix 类型前缀(如 'I' 表示 int,'D' 表示 double 等)
|
||||
* @param prefix 类型前缀(如 'I', 'F')
|
||||
*/
|
||||
public void setSlotType(int slot, char prefix) {
|
||||
slotType.put(slot, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个槽位对应的数据类型前缀
|
||||
* 若未指定则返回默认类型 'I'(int)
|
||||
* 获取槽位的类型前缀,默认为 'I'(整数类型)。
|
||||
*
|
||||
* @param slot 槽位编号
|
||||
* @return 类型前缀(如 'I', 'D' 等)
|
||||
* @return 类型前缀字符
|
||||
*/
|
||||
public char getSlotType(int slot) {
|
||||
return slotType.getOrDefault(slot, 'I');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 标记函数入口或标签,并尝试修补所有等候该符号的指令。
|
||||
* @param name 符号名(函数名/标签名)
|
||||
* 标记一个函数或标签的起始位置。
|
||||
* <p>
|
||||
* 1. 记录符号到当前 pc 的映射;
|
||||
* 2. 立即尝试修补之前所有针对该符号的延后调用和分支。
|
||||
*
|
||||
* @param name 函数名或标签名(全限定名)
|
||||
*/
|
||||
public void beginFunction(String name) {
|
||||
addr.put(name, pc); // 记录当前地址为入口
|
||||
patchCallFixes(name); // 修补所有待该符号的 CALL
|
||||
patchBranchFixes(name); // 修补所有待该符号的分支
|
||||
addr.put(name, pc);
|
||||
patchCallFixes(name);
|
||||
patchBranchFixes(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束函数(当前实现为空,方便 API 统一)。
|
||||
*/
|
||||
public void endFunction() { /* no-op */ }
|
||||
/** 函数结尾的处理(占位,无需特殊处理)。 */
|
||||
public void endFunction() {}
|
||||
|
||||
/**
|
||||
* 写入一条 VM 指令或标签。
|
||||
* <ul>
|
||||
* <li>如果以冒号结尾,视为标签,仅登记其地址,不写入指令流,也不递增 pc。</li>
|
||||
* <li>否则写入实际指令,并自增 pc。</li>
|
||||
* </ul>
|
||||
* @param line 一行 VM 指令文本或标签名(结尾有冒号)
|
||||
* 添加一条指令或标签到代码列表。
|
||||
*
|
||||
* @param line 指令字符串或标签字符串(若以冒号结尾为标签)
|
||||
*/
|
||||
public void emit(String line) {
|
||||
if (line.endsWith(":")) { // 是标签定义行
|
||||
if (line.endsWith(":")) {
|
||||
// 标签定义
|
||||
String label = line.substring(0, line.length() - 1);
|
||||
addr.put(label, pc); // 记录标签地址
|
||||
patchBranchFixes(label); // 修补所有以该标签为目标的分支指令
|
||||
return; // 标签行不写入 code,不递增 pc
|
||||
addr.put(label, pc);
|
||||
patchBranchFixes(label);
|
||||
return;
|
||||
}
|
||||
code.add(line); // 普通指令写入 code
|
||||
code.add(line);
|
||||
pc++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 CALL 指令。
|
||||
* 支持延迟修补: 若目标已知,直接写入地址;否则写入占位并登记 fix-up。
|
||||
* @param target 目标函数名
|
||||
* 添加一条 CALL 指令,若目标未定义则延后修补。
|
||||
*
|
||||
* @param target 目标函数全名
|
||||
* @param nArgs 参数个数
|
||||
*/
|
||||
public void emitCall(String target, int nArgs) {
|
||||
@ -117,10 +117,10 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成分支(JUMP 或 IC_*)指令。
|
||||
* 支持延迟修补机制。
|
||||
* @param opcode 指令名
|
||||
* @param label 目标标签名
|
||||
* 添加一条分支指令(如 JMP/BR/BEQ),若目标未定义则延后修补。
|
||||
*
|
||||
* @param opcode 指令操作码
|
||||
* @param label 跳转目标标签名
|
||||
*/
|
||||
public void emitBranch(String opcode, String label) {
|
||||
Integer a = resolve(label);
|
||||
@ -133,13 +133,12 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建最终 VM 代码文本列表。
|
||||
* <ul>
|
||||
* <li>若存在未解析符号(CALL 或分支),则抛出异常。</li>
|
||||
* <li>否则返回不可变指令流。</li>
|
||||
* </ul>
|
||||
* @return 完整 VM 指令流
|
||||
* @throws IllegalStateException 若有未修补的符号引用
|
||||
* 完成代码生成,输出最终 VM 指令序列。
|
||||
* <p>
|
||||
* 如果存在未修补的调用或分支,将抛出异常。
|
||||
*
|
||||
* @return 指令序列(不可变)
|
||||
* @throws IllegalStateException 如果存在未修补符号
|
||||
*/
|
||||
public List<String> build() {
|
||||
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) {
|
||||
@ -151,27 +150,26 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析符号地址。若全限定名找不到则降级尝试简单名。
|
||||
* @param sym 符号名
|
||||
* @return 地址或 null(未定义)
|
||||
* 解析符号地址,仅支持全名精准匹配。
|
||||
*
|
||||
* @param sym 符号全名
|
||||
* @return 地址(指令序号),未找到返回 null
|
||||
*/
|
||||
private Integer resolve(String sym) {
|
||||
Integer a = addr.get(sym);
|
||||
if (a == null && sym.contains(".")) {
|
||||
a = addr.get(sym.substring(sym.lastIndexOf('.') + 1));
|
||||
}
|
||||
return a;
|
||||
return addr.get(sym);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修补所有以 name 为目标的 CALL 占位符。
|
||||
* @param name 新定义的函数名
|
||||
* 修补所有等待目标函数 name 的 CALL 指令。
|
||||
* <p>
|
||||
* 只支持全名精确修补,不做模糊查找或短名回退。
|
||||
*
|
||||
* @param name 目标函数全名
|
||||
*/
|
||||
private void patchCallFixes(String name) {
|
||||
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) {
|
||||
CallFix f = it.next();
|
||||
// 目标函数名完全匹配或后缀匹配(兼容全限定名)
|
||||
if (f.target.equals(name) || f.target.endsWith("." + name)) {
|
||||
if (f.target.equals(name)) {
|
||||
code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
|
||||
it.remove();
|
||||
}
|
||||
@ -179,8 +177,9 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修补所有以 label 为目标的分支占位符。
|
||||
* @param label 新定义的标签名
|
||||
* 修补所有等待目标 label 的分支指令。
|
||||
*
|
||||
* @param label 目标标签
|
||||
*/
|
||||
private void patchBranchFixes(String label) {
|
||||
for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext();) {
|
||||
|
||||
@ -157,7 +157,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建函数或方法调用表达式。
|
||||
* 构建函数或方法调用表达式。模块内未限定调用会自动补全当前模块名。
|
||||
*
|
||||
* @param call AST 调用表达式节点
|
||||
* @return 存储调用结果的虚拟寄存器
|
||||
@ -171,8 +171,18 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
// 成员方法调用,例如 obj.foo()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id
|
||||
-> id.name() + "." + m.member();
|
||||
// 普通函数调用
|
||||
case IdentifierNode id -> id.name();
|
||||
// 普通函数调用,如果未指定模块,自动补全当前模块名
|
||||
case IdentifierNode id -> {
|
||||
String current = ctx.getFunction().name();
|
||||
int dot = current.lastIndexOf('.');
|
||||
if (dot > 0) {
|
||||
// 当前处于模块内函数(Module.func),补全为同模块下的全限定名
|
||||
yield current.substring(0, dot) + "." + id.name();
|
||||
} else {
|
||||
// 顶层/脚本函数等不含模块前缀,保持原样
|
||||
yield id.name();
|
||||
}
|
||||
}
|
||||
// 其它情况暂不支持
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
|
||||
@ -12,21 +12,19 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram。
|
||||
*
|
||||
* <p>主要职责:
|
||||
* IRProgramBuilder 负责将 AST 根节点(如模块、函数、顶层语句)转换为可执行的 IRProgram 实例。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>遍历输入的顶层节点,识别 ModuleNode、FunctionNode 及脚本式顶层 StatementNode;</li>
|
||||
* <li>对 ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram;</li>
|
||||
* <li>对单独的 FunctionNode 节点直接构建并纳入 IRProgram;</li>
|
||||
* <li>对顶层脚本式 StatementNode 自动封装为名称固定的“_start”函数,再行构建并纳入 IRProgram;</li>
|
||||
* <li>对不支持的节点类型抛出 IllegalStateException,以确保编译流程严谨。</li>
|
||||
* <li>遍历 AST 根节点,根据类型分别处理(模块、函数、顶层语句)。</li>
|
||||
* <li>对模块内的函数添加全限定名,并在函数体前注入全局变量声明。</li>
|
||||
* <li>将单独的顶层语句封装为特殊的 "_start" 函数。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class IRProgramBuilder {
|
||||
|
||||
/**
|
||||
* 构建完整的 IRProgram 实例。
|
||||
* 将解析生成的 AST 根节点列表转换为 IRProgram。
|
||||
*
|
||||
* @param roots 含 ModuleNode、FunctionNode 或 StatementNode 的顶层 AST 根节点列表
|
||||
* @return 包含所有转换后 IRFunction 的 IRProgram 对象
|
||||
@ -36,17 +34,20 @@ public final class IRProgramBuilder {
|
||||
IRProgram irProgram = new IRProgram();
|
||||
for (Node node : roots) {
|
||||
switch (node) {
|
||||
case ModuleNode moduleNode ->
|
||||
// 对每个模块,所有函数均自动注入 globals
|
||||
moduleNode.functions().forEach(f -> irProgram.add(buildFunctionWithGlobals(moduleNode, f)));
|
||||
case ModuleNode moduleNode -> {
|
||||
// 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名
|
||||
for (FunctionNode f : moduleNode.functions()) {
|
||||
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
|
||||
}
|
||||
}
|
||||
case FunctionNode functionNode ->
|
||||
// 顶层函数节点: 直接构建并添加
|
||||
// 处理顶层函数节点:直接构建为 IRFunction 并加入
|
||||
irProgram.add(buildFunction(functionNode));
|
||||
case StatementNode statementNode ->
|
||||
// 脚本式顶层语句: 封装为“_start”函数后构建并添加
|
||||
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
||||
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
||||
default ->
|
||||
// 严格校验节点类型,遇不支持者立即失败
|
||||
// 遇到未知类型节点,抛出异常
|
||||
throw new IllegalStateException("Unsupported top-level node: " + node);
|
||||
}
|
||||
}
|
||||
@ -54,21 +55,27 @@ public final class IRProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建带有模块全局声明“注入”的函数。
|
||||
* 为了在当前 IR 设计下能访问全局变量,将模块的 globals 作为前置声明
|
||||
* 追加到函数体最前面,使其在函数内被注册到 IR 作用域中。
|
||||
* 构建带有模块全局声明“注入”的函数,并将函数名加上模块前缀,保证模块内函数名唯一。
|
||||
* <p>
|
||||
* 如果模块有全局声明,则这些声明会被插入到函数体前部。
|
||||
*
|
||||
* @param moduleNode 当前模块节点
|
||||
* @param functionNode 模块中的函数节点
|
||||
* @return 包含全局声明、已加前缀函数名的 IRFunction
|
||||
*/
|
||||
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
|
||||
// 拼接模块名和函数名,生成全限定名
|
||||
String qualifiedName = moduleNode.name() + "." + functionNode.name();
|
||||
// 若无全局声明,仅重命名后直接构建
|
||||
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
|
||||
return buildFunction(functionNode);
|
||||
return buildFunction(renameFunction(functionNode, qualifiedName));
|
||||
}
|
||||
// 重建函数体:先放 globals 的声明,再放原来的语句
|
||||
// 若有全局声明,插入到函数体最前面
|
||||
List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
|
||||
newBody.addAll(moduleNode.globals());
|
||||
newBody.addAll(functionNode.body());
|
||||
|
||||
FunctionNode wrapped = new FunctionNode(
|
||||
functionNode.name(),
|
||||
qualifiedName,
|
||||
functionNode.parameters(),
|
||||
functionNode.returnType(),
|
||||
newBody,
|
||||
@ -78,28 +85,39 @@ public final class IRProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用 FunctionBuilder 将 FunctionNode 转换为 IRFunction。
|
||||
* 生成一个重命名的 FunctionNode(只修改函数名,其他属性保持不变)。
|
||||
*
|
||||
* @param functionNode 待构建的 AST FunctionNode
|
||||
* @return 构建完成的 IRFunction 实例
|
||||
* @param fn 原始函数节点
|
||||
* @param newName 新的函数名(通常为全限定名)
|
||||
* @return 重命名后的 FunctionNode
|
||||
*/
|
||||
private FunctionNode renameFunction(FunctionNode fn, String newName) {
|
||||
return new FunctionNode(
|
||||
newName,
|
||||
fn.parameters(),
|
||||
fn.returnType(),
|
||||
fn.body(),
|
||||
fn.context()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 IRFunction。
|
||||
*
|
||||
* @param functionNode 要转换的函数节点
|
||||
* @return 转换结果 IRFunction
|
||||
*/
|
||||
private IRFunction buildFunction(FunctionNode functionNode) {
|
||||
return new FunctionBuilder().build(functionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个脚本式顶层 StatementNode 封装为名称固定的“_start”函数节点。
|
||||
* 将顶层语句节点封装成特殊的 "_start" 函数。
|
||||
* <p>
|
||||
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)。
|
||||
*
|
||||
* <p>封装规则:
|
||||
* <ul>
|
||||
* <li>函数名固定为“_start”;</li>
|
||||
* <li>返回类型设为 null,由后续流程处理;</li>
|
||||
* <li>参数列表为空;</li>
|
||||
* <li>函数主体仅包含传入的单条语句。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param stmt 待封装的顶层脚本语句节点
|
||||
* @return 生成的 FunctionNode,用于后续 IRFunction 构建
|
||||
* @param stmt 要封装的顶层语句
|
||||
* @return 包装成 FunctionNode 的 "_start" 函数
|
||||
*/
|
||||
private FunctionNode wrapTopLevel(StatementNode stmt) {
|
||||
return new FunctionNode(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user