!51 feat: 支持模块内函数调用自动补全为当前模块前缀

Merge pull request !51 from Luke/bugfix/fix-function-call
This commit is contained in:
Luke 2025-07-31 06:35:25 +00:00 committed by Gitee
commit 736e088a91
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
16 changed files with 268 additions and 168 deletions

11
.run/Bug4.run.xml Normal file
View File

@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Bug4" type="Application" factoryName="Application" folderName="BugFarm">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/BugFarm/Bug4 -o target/Bug4 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

11
.run/Bug5.run.xml Normal file
View File

@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Bug5" type="Application" factoryName="Application" folderName="BugFarm">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/BugFarm/Bug5 -o target/Bug5 " />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -0,0 +1,9 @@
module: Main
import: ModuleB
function: main
return_type: int
body:
return ModuleB.fun()
end body
end function
end module

View File

@ -0,0 +1,8 @@
module: ModuleA
function: fun
return_type: int
body:
return 123
end body
end function
end module

View File

@ -0,0 +1,9 @@
module: ModuleB
import: ModuleA
function: fun
return_type: int
body:
return ModuleA.fun() + ModuleA.fun()
end body
end function
end module

View File

@ -0,0 +1,16 @@
module: Main
import: os
globals:
declare sum: int = 0
function: main
parameter:
return_type: int
body:
sum = 20
os.print(sum)
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -3,7 +3,7 @@ module: Main
function: main function: main
return_type: void return_type: void
body: body:
print(222) os.print(222)
end body end body
end function end function
end module end module

View File

@ -1,13 +1,20 @@
module: Main module: Main
import: os import: os
globals: globals:
declare num1:int=10
declare num2:int=10 declare num2:int=10
function: main function: main
return_type: void return_type: void
body: body:
declare num1:int=11 declare num1:int=11
print(num1+num2) os.print(num1+num2+abc())
end body end body
end function end function
function: abc
return_type: int
body:
return 1
end body
end function
end module end module

View File

@ -12,7 +12,7 @@ module: Main
i = i + 1 i = i + 1
body: body:
if i % 2 == 0 then if i % 2 == 0 then
print(i) os.print(i)
end if end if
end body end body
end loop end loop

View File

@ -12,7 +12,7 @@ module: Main
i = i + 1 i = i + 1
body: body:
if i % 2 == 0 then if i % 2 == 0 then
print(i) os.print(i)
break break
end if end if
end body end body

View File

@ -5,96 +5,83 @@ import org.jcnc.snow.compiler.backend.utils.OpHelper;
import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.core.IRInstruction; import org.jcnc.snow.compiler.ir.core.IRInstruction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 虚拟机代码生成器VMCodeGenerator * VMCodeGenerator 负责将中间表示IR函数转换为目标虚拟机VM的指令序列
* <p> * <p>
* 本类作为指令生成器调度中心不负责任何具体 IR 指令到 VM 指令的转换实现 * 每个 IR 指令根据类型由对应的 InstructionGenerator 处理并将结果输出到 VMProgramBuilder
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成 * 该类通过注册表registry实现 IR VM 指令生成器的快速分发
* </p>
* <p>
* 工作流程简述:
* <ol>
* <li>接收一组已注册的 IR 指令生成器并建立类型到生成器的映射表</li>
* <li>遍历 IR 函数体的每条指令根据类型找到对应的生成器调用其 generate 方法生成 VM 指令</li>
* <li>生成流程以函数为单位beginFunction/endFunction</li>
* </ol>
*/ */
public final class VMCodeGenerator { public final class VMCodeGenerator {
/** /**
* 指令类型到生成器的注册表调度表 * IR 指令类型到指令生成器的映射
* <p> * 每种 IRInstruction 都有对应的 InstructionGenerator 处理
* : IR 指令类型Class对象
* : 对应的指令生成器实例
* </p>
*/ */
private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry; private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry;
/** /**
* 虚拟寄存器到槽号的映射表 RegisterAllocator 负责生成 * 虚拟寄存器到 VM 局部槽位的映射表
* 用于寄存器分配与指令生成
*/ */
private final Map<IRVirtualRegister, Integer> slotMap; private final Map<IRVirtualRegister, Integer> slotMap;
/** /**
* 虚拟机程序构建器用于输出 VM 指令 * 输出目标 VM 程序的构建器
* 提供 emitbeginFunctionendFunction 等接口
*/ */
private final VMProgramBuilder out; private final VMProgramBuilder out;
/** /**
* 当前处理的函数名用于部分指令生成逻辑如主函数判断等 * 构造 VMCodeGenerator
*/
private String currentFn;
/**
* 构造方法
* *
* @param slotMap 虚拟寄存器到槽号的映射 * @param slotMap 虚拟寄存器到 VM 局部槽位的分配表
* @param out 虚拟机程序构建器 * @param out 输出 VM 程序的 builder
* @param generators 各类 IR 指令生成器集合需预先构建 * @param generators 可用的 IR 指令生成器列表每个类型只应有一个
*/ */
public VMCodeGenerator(Map<IRVirtualRegister, Integer> slotMap, public VMCodeGenerator(Map<IRVirtualRegister, Integer> slotMap,
VMProgramBuilder out, VMProgramBuilder out,
List<InstructionGenerator<? extends IRInstruction>> generators) { List<InstructionGenerator<? extends IRInstruction>> generators) {
this.slotMap = slotMap; this.slotMap = slotMap;
this.out = out; this.out = out;
// 按类型注册各 IR 指令生成器建立不可变类型-生成器映射 // 构建不可变的类型到生成器的注册
this.registry = generators.stream() this.registry = generators.stream()
.collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g)); .collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g));
} }
/** /**
* 为一个 IR 函数生成虚拟机指令 * IRFunction 生成对应 VM 代码并写入输出
* *
* @param fn 待生成的 IR 函数对象 * <ol>
* @throws IllegalStateException 若遇到不支持的 IR 指令类型 * <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) { public void generate(IRFunction fn) {
this.currentFn = fn.name(); String currentFn = fn.name();
/* 登记函数入口地址 —— 解决 CALL 未解析符号问题 */
out.beginFunction(currentFn); out.beginFunction(currentFn);
/* 逐条分发 IR 指令给对应的生成器 */
for (IRInstruction ins : fn.body()) { for (IRInstruction ins : fn.body()) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// 查找合适的指令生成器
InstructionGenerator<IRInstruction> gen = InstructionGenerator<IRInstruction> gen =
(InstructionGenerator<IRInstruction>) registry.get(ins.getClass()); (InstructionGenerator<IRInstruction>) registry.get(ins.getClass());
if (gen == null) { if (gen == null) {
throw new IllegalStateException("Unsupported IR: " + ins); throw new IllegalStateException("Unsupported IR: " + ins);
} }
// 调用生成器生成对应的 VM 指令
gen.generate(ins, out, slotMap, currentFn); gen.generate(ins, out, slotMap, currentFn);
} }
// 结尾指令main 函数统一用 HALT其他函数用 RET
/* 强制补上函数结尾的返回/终止指令 */ String retOpcode = ("main".equals(currentFn) || currentFn.endsWith(".main")) ? "HALT" : "RET";
String retOpcode = "main".equals(currentFn) ? "HALT" : "RET";
out.emit(OpHelper.opcode(retOpcode)); out.emit(OpHelper.opcode(retOpcode));
/* 结束函数 */
out.endFunction(); out.endFunction();
} }
} }

View File

@ -1,109 +1,109 @@
package org.jcnc.snow.compiler.backend.builder; package org.jcnc.snow.compiler.backend.builder;
import org.jcnc.snow.vm.engine.VMOpCode; import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*; import java.util.*;
/** /**
* VMProgramBuilder: 构建线性 VM 程序即按顺序存放所有 VM 指令 * VMProgramBuilder 用于构建虚拟机(VM)的最终指令列表
* <p> * <p>
* 本类用于编译器后端将所有生成的 VM 指令包括分支和调用指令统一存储管理 * 主要职责
* 支持符号如函数入口标签地址的延迟解析与回填fix-up机制 * <ul>
* 可在目标尚未定义时提前生成分支或调用指令定义后自动修正 * <li>维护代码指令序列和符号地址表</li>
* </p> * <li>支持跨函数标签跳转的延后修补(Call/Branch Fixup)</li>
* <p> * <li>支持虚拟机本地槽位类型的管理( I/F...)</li>
* 常用于处理跨函数跨标签的 CALL/JUMP 等复杂控制流确保最终生成的 VM 指令地址一致正确 * </ul>
* </p>
*/ */
public final class VMProgramBuilder { public final class VMProgramBuilder {
/** 未解析目标的 CALL 指令信息(待修补) */
/**
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)
* @param index CALL 指令在 code 列表中的位置
* @param target 目标函数的全名
* @param nArgs 参数个数
*/
private record CallFix(int index, String target, int 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 record BranchFix(int index, String label) {}
/** 占位符: 用于表示尚未确定的符号地址 */ /** 未解析地址的占位符,便于后期批量修补 */
private static final String PLACEHOLDER = "-1"; private static final String PLACEHOLDER = "-1";
/** 按顺序存放的 VM 指令文本 */ /** VM 指令列表 */
private final List<String> code = new ArrayList<>(); private final List<String> code = new ArrayList<>();
/** 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化) */
// 虚拟机槽位编号到数据类型前缀的映射 0 -> 'I', 1 -> 'D'
private final Map<Integer, Character> slotType = new HashMap<>(); private final Map<Integer, Character> slotType = new HashMap<>();
/** 符号(函数名/标签)到指令序号的映射表 */
/** 符号如函数名标签名到其首地址即指令序号/偏移量的映射表
* 主要用于跳转和调用定位具体的代码位置 */
private final Map<String, Integer> addr = new HashMap<>(); private final Map<String, Integer> addr = new HashMap<>();
/** 所有待修补的 CALL 指令集合 */
/** 所有待回填fix-up CALL 调用指令记录
* 由于被调用目标地址在编译时可能尚未确定需要先记录最终统一回填 */
private final List<CallFix> callFixes = new ArrayList<>(); private final List<CallFix> callFixes = new ArrayList<>();
/** 所有待修补的分支指令集合 */
/** 所有待回填fix-up的分支跳转指令记录
* CALL 类似分支指令的目标地址也可能需要编译后期再补充 */
private final List<BranchFix> branchFixes = new ArrayList<>(); private final List<BranchFix> branchFixes = new ArrayList<>();
/** 当前代码指针(已生成指令的数量/下一个指令的位置) */
/** 程序计数器Program Counter表示下一个生成指令将插入的位置 */
private int pc = 0; private int pc = 0;
/** /**
* 设置某个槽位对应的数据类型前缀 * 设置槽位(局部变量/虚拟寄存器)的类型前缀
*
* @param slot 槽位编号 * @param slot 槽位编号
* @param prefix 类型前缀 'I' 表示 int'D' 表示 double * @param prefix 类型前缀( 'I', 'F')
*/ */
public void setSlotType(int slot, char prefix) { public void setSlotType(int slot, char prefix) {
slotType.put(slot, prefix); slotType.put(slot, prefix);
} }
/** /**
* 获取某个槽位对应数据类型前缀 * 获取槽位的类型前缀默认为 'I'(整数类型)
* 若未指定则返回默认类型 'I'int *
* @param slot 槽位编号 * @param slot 槽位编号
* @return 类型前缀 'I', 'D' * @return 类型前缀字符
*/ */
public char getSlotType(int slot) { public char getSlotType(int slot) {
return slotType.getOrDefault(slot, 'I'); return slotType.getOrDefault(slot, 'I');
} }
/** /**
* 标记函数入口或标签并尝试修补所有等候该符号的指令 * 标记一个函数或标签的起始位置
* @param name 符号名函数名/标签名 * <p>
* 1. 记录符号到当前 pc 的映射
* 2. 立即尝试修补之前所有针对该符号的延后调用和分支
*
* @param name 函数名或标签名(全限定名)
*/ */
public void beginFunction(String name) { public void beginFunction(String name) {
addr.put(name, pc); // 记录当前地址为入口 addr.put(name, pc);
patchCallFixes(name); // 修补所有待该符号的 CALL patchCallFixes(name);
patchBranchFixes(name); // 修补所有待该符号的分支 patchBranchFixes(name);
} }
/** /** 函数结尾的处理(占位,无需特殊处理)。 */
* 结束函数当前实现为空方便 API 统一 public void endFunction() {}
*/
public void endFunction() { /* no-op */ }
/** /**
* 写入一条 VM 指令或标签 * 添加一条指令或标签到代码列表
* <ul> *
* <li>如果以冒号结尾视为标签仅登记其地址不写入指令流也不递增 pc</li> * @param line 指令字符串或标签字符串(若以冒号结尾为标签)
* <li>否则写入实际指令并自增 pc</li>
* </ul>
* @param line 一行 VM 指令文本或标签名结尾有冒号
*/ */
public void emit(String line) { public void emit(String line) {
if (line.endsWith(":")) { // 是标签定义行 if (line.endsWith(":")) {
// 标签定义
String label = line.substring(0, line.length() - 1); String label = line.substring(0, line.length() - 1);
addr.put(label, pc); // 记录标签地址 addr.put(label, pc);
patchBranchFixes(label); // 修补所有以该标签为目标的分支指令 patchBranchFixes(label);
return; // 标签行不写入 code不递增 pc return;
} }
code.add(line); // 普通指令写入 code code.add(line);
pc++; pc++;
} }
/** /**
* 生成 CALL 指令 * 添加一条 CALL 指令若目标未定义则延后修补
* 支持延迟修补: 若目标已知直接写入地址否则写入占位并登记 fix-up *
* @param target 目标函数 * @param target 目标函数
* @param nArgs 参数个数 * @param nArgs 参数个数
*/ */
public void emitCall(String target, int nArgs) { public void emitCall(String target, int nArgs) {
@ -117,10 +117,10 @@ public final class VMProgramBuilder {
} }
/** /**
* 生成分支JUMP IC_*指令 * 添加一条分支指令( JMP/BR/BEQ)若目标未定义则延后修补
* 支持延迟修补机制 *
* @param opcode 指令 * @param opcode 指令操作码
* @param label 目标标签名 * @param label 跳转目标标签名
*/ */
public void emitBranch(String opcode, String label) { public void emitBranch(String opcode, String label) {
Integer a = resolve(label); Integer a = resolve(label);
@ -133,13 +133,12 @@ public final class VMProgramBuilder {
} }
/** /**
* 构建最终 VM 代码文本列表 * 完成代码生成输出最终 VM 指令序列
* <ul> * <p>
* <li>若存在未解析符号CALL 或分支则抛出异常</li> * 如果存在未修补的调用或分支将抛出异常
* <li>否则返回不可变指令流</li> *
* </ul> * @return 指令序列(不可变)
* @return 完整 VM 指令流 * @throws IllegalStateException 如果存在未修补符号
* @throws IllegalStateException 若有未修补的符号引用
*/ */
public List<String> build() { public List<String> build() {
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) { 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) { private Integer resolve(String sym) {
Integer a = addr.get(sym); return addr.get(sym);
if (a == null && sym.contains(".")) {
a = addr.get(sym.substring(sym.lastIndexOf('.') + 1));
}
return a;
} }
/** /**
* 修补所有以 name 为目标的 CALL 占位符 * 修补所有等待目标函数 name CALL 指令
* @param name 新定义的函数名 * <p>
* 只支持全名精确修补不做模糊查找或短名回退
*
* @param name 目标函数全名
*/ */
private void patchCallFixes(String name) { private void patchCallFixes(String name) {
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) { for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) {
CallFix f = it.next(); CallFix f = it.next();
// 目标函数名完全匹配或后缀匹配兼容全限定名 if (f.target.equals(name)) {
if (f.target.equals(name) || f.target.endsWith("." + name)) {
code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs); code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
it.remove(); it.remove();
} }
@ -179,8 +177,9 @@ public final class VMProgramBuilder {
} }
/** /**
* 修补所有以 label 为目标的分支占位符 * 修补所有等待目标 label 的分支指令
* @param label 新定义的标签名 *
* @param label 目标标签
*/ */
private void patchBranchFixes(String label) { private void patchBranchFixes(String label) {
for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext();) { for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext();) {

View File

@ -157,7 +157,7 @@ public record ExpressionBuilder(IRContext ctx) {
} }
/** /**
* 构建函数或方法调用表达式 * 构建函数或方法调用表达式模块内未限定调用会自动补全当前模块名
* *
* @param call AST 调用表达式节点 * @param call AST 调用表达式节点
* @return 存储调用结果的虚拟寄存器 * @return 存储调用结果的虚拟寄存器
@ -171,8 +171,18 @@ public record ExpressionBuilder(IRContext ctx) {
// 成员方法调用例如 obj.foo() // 成员方法调用例如 obj.foo()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id case MemberExpressionNode m when m.object() instanceof IdentifierNode id
-> id.name() + "." + m.member(); -> 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()); default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
}; };

View File

@ -12,21 +12,19 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram * IRProgramBuilder 负责将 AST 根节点(如模块函数顶层语句)转换为可执行的 IRProgram 实例
* * <p>
* <p>主要职责: * 主要职责:
* <ul> * <ul>
* <li>遍历输入的顶层节点识别 ModuleNodeFunctionNode 及脚本式顶层 StatementNode</li> * <li>遍历 AST 根节点根据类型分别处理(模块函数顶层语句)</li>
* <li> ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram</li> * <li>对模块内的函数添加全限定名并在函数体前注入全局变量声明</li>
* <li>对单独的 FunctionNode 节点直接构建并纳入 IRProgram</li> * <li>将单独的顶层语句封装为特殊的 "_start" 函数</li>
* <li>对顶层脚本式 StatementNode 自动封装为名称固定的_start函数再行构建并纳入 IRProgram</li>
* <li>对不支持的节点类型抛出 IllegalStateException以确保编译流程严谨</li>
* </ul> * </ul>
*/ */
public final class IRProgramBuilder { public final class IRProgramBuilder {
/** /**
* 构建完整的 IRProgram 实例 * 将解析生成的 AST 根节点列表转换为 IRProgram
* *
* @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表 * @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表
* @return 包含所有转换后 IRFunction IRProgram 对象 * @return 包含所有转换后 IRFunction IRProgram 对象
@ -36,17 +34,20 @@ public final class IRProgramBuilder {
IRProgram irProgram = new IRProgram(); IRProgram irProgram = new IRProgram();
for (Node node : roots) { for (Node node : roots) {
switch (node) { switch (node) {
case ModuleNode moduleNode -> case ModuleNode moduleNode -> {
// 对每个模块所有函数均自动注入 globals // 处理模块节点:遍历其中所有函数统一用模块名.函数名作为全限定名
moduleNode.functions().forEach(f -> irProgram.add(buildFunctionWithGlobals(moduleNode, f))); for (FunctionNode f : moduleNode.functions()) {
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
}
}
case FunctionNode functionNode -> case FunctionNode functionNode ->
// 顶层函数节点: 直接构建并添加 // 处理顶层函数节点:直接构建 IRFunction 并加
irProgram.add(buildFunction(functionNode)); irProgram.add(buildFunction(functionNode));
case StatementNode statementNode -> case StatementNode statementNode ->
// 脚本式顶层语句: 封装为_start函数后构建并添加 // 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode))); irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default -> default ->
// 严格校验节点类型遇不支持者立即失败 // 遇到未知类型节点抛出异常
throw new IllegalStateException("Unsupported top-level node: " + node); throw new IllegalStateException("Unsupported top-level node: " + node);
} }
} }
@ -54,21 +55,27 @@ public final class IRProgramBuilder {
} }
/** /**
* 构建带有模块全局声明注入的函数 * 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一
* 为了在当前 IR 设计下能访问全局变量将模块的 globals 作为前置声明 * <p>
* 追加到函数体最前面使其在函数内被注册到 IR 作用域中 * 如果模块有全局声明则这些声明会被插入到函数体前部
*
* @param moduleNode 当前模块节点
* @param functionNode 模块中的函数节点
* @return 包含全局声明已加前缀函数名的 IRFunction
*/ */
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) { private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
// 拼接模块名和函数名生成全限定名
String qualifiedName = moduleNode.name() + "." + functionNode.name();
// 若无全局声明仅重命名后直接构建
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) { 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()); List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
newBody.addAll(moduleNode.globals()); newBody.addAll(moduleNode.globals());
newBody.addAll(functionNode.body()); newBody.addAll(functionNode.body());
FunctionNode wrapped = new FunctionNode( FunctionNode wrapped = new FunctionNode(
functionNode.name(), qualifiedName,
functionNode.parameters(), functionNode.parameters(),
functionNode.returnType(), functionNode.returnType(),
newBody, newBody,
@ -78,28 +85,39 @@ public final class IRProgramBuilder {
} }
/** /**
* 利用 FunctionBuilder FunctionNode 转换为 IRFunction * 生成一个重命名的 FunctionNode(只修改函数名其他属性保持不变)
* *
* @param functionNode 待构建的 AST FunctionNode * @param fn 原始函数节点
* @return 构建完成的 IRFunction 实例 * @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) { private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode); return new FunctionBuilder().build(functionNode);
} }
/** /**
* 将单个脚本式顶层 StatementNode 封装为名称固定的_start函数节点 * 将顶层语句节点封装成特殊的 "_start" 函数
* <p>
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)
* *
* <p>封装规则: * @param stmt 要封装的顶层语句
* <ul> * @return 包装成 FunctionNode "_start" 函数
* <li>函数名固定为_start</li>
* <li>返回类型设为 null由后续流程处理</li>
* <li>参数列表为空</li>
* <li>函数主体仅包含传入的单条语句</li>
* </ul>
*
* @param stmt 待封装的顶层脚本语句节点
* @return 生成的 FunctionNode用于后续 IRFunction 构建
*/ */
private FunctionNode wrapTopLevel(StatementNode stmt) { private FunctionNode wrapTopLevel(StatementNode stmt) {
return new FunctionNode( return new FunctionNode(

View File

@ -1,7 +1,11 @@
package org.jcnc.snow.vm.commands.flow.control; package org.jcnc.snow.vm.commands.flow.control;
import org.jcnc.snow.vm.interfaces.Command; import org.jcnc.snow.vm.interfaces.Command;
import org.jcnc.snow.vm.module.*; import org.jcnc.snow.vm.module.CallStack;
import org.jcnc.snow.vm.module.LocalVariableStore;
import org.jcnc.snow.vm.module.OperandStack;
import org.jcnc.snow.vm.module.StackFrame;
import org.jcnc.snow.vm.utils.LoggingUtils;
import static org.jcnc.snow.common.SnowConfig.print; import static org.jcnc.snow.common.SnowConfig.print;
@ -37,7 +41,7 @@ public class RetCommand implements Command {
/* ----- Root frame: do NOT pop, just end program ----- */ /* ----- Root frame: do NOT pop, just end program ----- */
if (topFrame.getReturnAddress() == PROGRAM_END) { if (topFrame.getReturnAddress() == PROGRAM_END) {
System.out.println("Return <root>"); LoggingUtils.logInfo("", "\nReturn <root>");
return PROGRAM_END; // VM main loop should break return PROGRAM_END; // VM main loop should break
} }