Merge branch 'dev' into bugfix/fix-global-redeclare-error

This commit is contained in:
Luke 2025-08-03 00:49:18 +08:00
commit 4eb1aff553
39 changed files with 2503 additions and 563 deletions

10
.run/Demo19.run.xml Normal file
View File

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

10
.run/Demo20.run.xml Normal file
View File

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

10
.run/Demo21.run.xml Normal file
View File

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

View File

@ -9,7 +9,10 @@
<toRun name="Demo15" type="Application" />
<toRun name="Demo16" type="Application" />
<toRun name="Demo17" type="Application" />
<toRun name="Demo18" type="Application" />
<toRun name="Demo19" type="Application" />
<toRun name="Demo2" type="Application" />
<toRun name="Demo20" type="Application" />
<toRun name="Demo3" type="Application" />
<toRun name="Demo4" type="Application" />
<toRun name="Demo6" type="Application" />

View File

@ -31,8 +31,8 @@ module: Main
if j % 2 == 0 then
continue
end if
print(i)
print(j)
os.print(i)
os.print(j)
end body
end loop
end body

View File

@ -0,0 +1,26 @@
module: Main
import: os
function: main
return_type: void
body:
declare n: int[][][][] = [
[
[ [17, 18], [19, 20] ],
[ [21, 22], [23, 24] ]
],
[
[ [25, 26], [27, 28] ],
[ [29, 30], [31, 32] ]
]
]
declare i: int = 0
declare j: int = 1
declare k: int = 0
declare l: int = 0
declare x: int = n[i][j][k][l]
os.print(x)
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

@ -0,0 +1,12 @@
module: Main
import:os
function: main
return_type: void
body:
declare arr: int[] = [1, 2, 3]
arr[0] = 5
os.print(arr[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

@ -0,0 +1,35 @@
module: Main
import: os
globals:
declare sum: int = 123
function: main
parameter:
return_type: int
body:
declare arr: int[][][][][][][][][][][][][][][][][][][][][][] = [[[[[[[[[[[[[[[[[[[[[[1], [2], [3]]]]]]]]]]]]]]]]]]]]]]
loop:
init:
declare i: int = 0
cond:
i < 3
step:
i = i + 1
body:
arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] = arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] + 1
end body
end loop
loop:
init:
declare i: int = 0
cond:
i < 3
step:
i = i + 1
body:
os.print(arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0])
end body
end loop
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

@ -10,45 +10,46 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <b>CallGenerator - IR {@code CallInstruction} 生成 VM 指令</b>
*
* {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令
* <p>
* 本类负责将中间表示IR中的函数调用指令 {@link CallInstruction} 转换为虚拟机VM指令
* 支持普通函数调用和特殊的 syscall 指令转换
* 支持 syscall普通函数调用一维/多维数组下标访问以及字符串常量池绑定等功能
* </p>
*
* <p>
* <b>能力说明</b>
* <ul>
* <li>支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量直接降级为 {@code SYSCALL &lt;SUBCMD&gt;} 指令</li>
* <li>对普通函数完成参数加载调用返回值保存等指令生成</li>
* <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
* <li>数组下标访问: 支持所有主流基础类型 __index_b/s/i/l/f/d/r</li>
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
* </ul>
* </p>
*/
public class CallGenerator implements InstructionGenerator<CallInstruction> {
/**
* <虚拟寄存器 id, 对应的字符串常量>
* <p>用于记录虚拟寄存器与其绑定字符串常量的映射 {@link LoadConstGenerator} 在编译期间填充</p>
* 字符串常量池用于绑定虚拟寄存器 id 到字符串值 syscall 子命令使用
* <br>
* 使用 ConcurrentHashMap 保证并发安全所有 registerStringConst 的注册和读取都是线程安全的
*/
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>();
private static final Map<Integer, String> STRING_CONST_POOL = new ConcurrentHashMap<>();
/**
* 注册字符串常量到虚拟寄存器
* <p> {@link LoadConstGenerator} 在加载字符串常量时调用</p>
* 注册一个字符串常量绑定到虚拟寄存器 id
*
* @param regId 虚拟寄存器 id
* @param value 字符串常量
* @param value 字符串常量
*/
public static void registerStringConst(int regId, String value) {
STRING_CONST_POOL.put(regId, value);
}
/**
* 返回本生成器支持的 IR 指令类型CallInstruction
* 返回本生成器支持的 IR 指令类型
*
* @return {@link CallInstruction} 类型的 Class 对象
*/
@Override
public Class<CallInstruction> supportedClass() {
@ -56,11 +57,11 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
}
/**
* 生成 VM 指令的主逻辑
* 生成指定的调用指令包括 syscall数组下标普通函数
*
* @param ins 当前 IR 指令函数调用
* @param out 指令输出构建器
* @param slotMap IR 虚拟寄存器与物理槽位映射
* @param ins IR 层的调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位的映射表
* @param currentFn 当前函数名
*/
@Override
@ -68,79 +69,357 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
String currentFn) {
String fn = ins.getFunctionName();
/* ========== 特殊处理 syscall 调用 ========== */
if ("syscall".equals(ins.getFunctionName()) ||
ins.getFunctionName().endsWith(".syscall")) {
List<IRValue> args = ins.getArguments();
if (args.isEmpty()) {
throw new IllegalStateException("syscall 需要子命令参数");
}
// ---------- 0. 解析 syscall 子命令 ----------
// 子命令支持 IRConstant直接字面量或虚拟寄存器需已绑定字符串
String subcmd;
IRValue first = args.getFirst();
if (first instanceof IRConstant(Object value)) { // 直接字面量
if (!(value instanceof String s))
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
subcmd = s.toUpperCase(Locale.ROOT);
} else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器
// 从常量池中查找是否已绑定字符串
subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id()))
.orElseThrow(() ->
new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr));
subcmd = subcmd.toUpperCase(Locale.ROOT);
} else {
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
}
// ---------- 1. 压栈其余 syscall 参数index 1 开始 ----------
for (int i = 1; i < args.size(); i++) {
IRVirtualRegister vr = (IRVirtualRegister) args.get(i);
int slotId = slotMap.get(vr);
char t = out.getSlotType(slotId);
if (t == '\0') t = 'I'; // 默认整型
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
}
// ---------- 2. 生成 SYSCALL 指令 ----------
out.emit(VMOpCode.SYSCALL + " " + subcmd);
return; // syscall 无返回值直接返回
}
/* ========== 普通函数调用 ========== */
// ---------- 1. 推断返回值类型 void 返回时用 ----------
char retType = 'I'; // 默认为整型
if (!ins.getArguments().isEmpty()) {
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
retType = out.getSlotType(firstSlot);
if (retType == '\0') retType = 'I';
}
// ---------- 2. 加载全部实参 ----------
for (var arg : ins.getArguments()) {
int slotId = slotMap.get((IRVirtualRegister) arg);
char t = out.getSlotType(slotId);
if (t == '\0') t = 'I'; // 默认整型
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
}
// ---------- 3. 发出 CALL 指令 ----------
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
// ---------- 3.5 如果为 void 返回直接结束 ----------
if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) {
// 特殊处理 syscall 调用
if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
generateSyscall(ins, out, slotMap, fn);
return;
}
// ---------- 4. 保存返回值 ----------
int destSlot = slotMap.get(ins.getDest());
// 各种一维数组类型byte/short/int/long/float/double/boolean读取
switch (fn) {
case "__index_b" -> {
generateIndexInstruction(ins, out, slotMap, 'B');
return;
}
case "__index_s" -> {
generateIndexInstruction(ins, out, slotMap, 'S');
return;
}
case "__index_i" -> {
generateIndexInstruction(ins, out, slotMap, 'I');
return;
}
case "__index_l" -> {
generateIndexInstruction(ins, out, slotMap, 'L');
return;
}
case "__index_f" -> {
generateIndexInstruction(ins, out, slotMap, 'F');
return;
}
case "__index_d" -> {
generateIndexInstruction(ins, out, slotMap, 'D');
return;
}
case "__index_r" -> {
generateIndexInstruction(ins, out, slotMap, 'R');
return;
}
case "__setindex_b" -> {
generateSetIndexInstruction(ins, out, slotMap, 'B');
return;
}
case "__setindex_s" -> {
generateSetIndexInstruction(ins, out, slotMap, 'S');
return;
}
case "__setindex_i" -> {
generateSetIndexInstruction(ins, out, slotMap, 'I');
return;
}
case "__setindex_l" -> {
generateSetIndexInstruction(ins, out, slotMap, 'L');
return;
}
case "__setindex_f" -> {
generateSetIndexInstruction(ins, out, slotMap, 'F');
return;
}
case "__setindex_d" -> {
generateSetIndexInstruction(ins, out, slotMap, 'D');
return;
}
case "__setindex_r" -> {
generateSetIndexInstruction(ins, out, slotMap, 'R');
return;
}
}
// 普通函数调用
generateNormalCall(ins, out, slotMap, fn);
}
// ========== 私有辅助方法 ==========
/**
* 生成数组元素赋值指令arr[idx] = value无返回值
* <p>
* 调用栈压栈顺序为arr (引用类型, R)idx (整型, I)value (元素类型, T)
* 执行 SYSCALL ARR_SET 指令以完成数组赋值操作
* </p>
*
* @param ins 当前的调用指令包含参数信息
* @param out VM 指令生成器用于输出 VM 指令
* @param slotMap 虚拟寄存器与槽位的映射表
* @param valType 待写入的 value 的类型标识'B': byte, 'S': short, 'I': int, 'L': long, 'F': float, 'D': double, 其余为引用类型'R'
* @throws IllegalStateException 如果参数数量不为3则抛出异常
*/
private void generateSetIndexInstruction(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
char valType) {
List<IRValue> args = ins.getArguments();
if (args.size() != 3) {
// 参数数量错误抛出异常并输出实际参数列表
throw new IllegalStateException(
"[CallGenerator] __setindex_* 需要三个参数(arr, idx, value),实际: " + args);
}
// 第一个参数为数组对象压入引用类型寄存器'R' arr
loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName());
// 第二个参数为索引值压入整型寄存器'I' idx
loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName());
// 第三个参数为待赋值元素根据元素类型压入相应类型寄存器
// 支持类型'B'(byte), 'S'(short), 'I'(int), 'L'(long), 'F'(float), 'D'(double)
// 其他情况如引用类型'R'处理
switch (valType) {
case 'B' -> loadArgument(out, slotMap, args.get(2), 'B', ins.getFunctionName());
case 'S' -> loadArgument(out, slotMap, args.get(2), 'S', ins.getFunctionName());
case 'I' -> loadArgument(out, slotMap, args.get(2), 'I', ins.getFunctionName());
case 'L' -> loadArgument(out, slotMap, args.get(2), 'L', ins.getFunctionName());
case 'F' -> loadArgument(out, slotMap, args.get(2), 'F', ins.getFunctionName());
case 'D' -> loadArgument(out, slotMap, args.get(2), 'D', ins.getFunctionName());
default -> loadArgument(out, slotMap, args.get(2), 'R', ins.getFunctionName());
}
// 输出 VM 指令SYSCALL ARR_SET完成数组元素写入操作
out.emit(VMOpCode.SYSCALL + " " + "ARR_SET");
}
/**
* 解析 syscall 子命令第一个参数支持字符串常量与已绑定字符串的虚拟寄存器
*
* @param arg 子命令参数应为字符串常量或寄存器
* @param fn 当前函数名仅用于报错信息
* @return 子命令大写字符串
* @throws IllegalStateException 如果参数不是字符串常量或已绑定寄存器
*/
private String resolveSyscallSubcmd(IRValue arg, String fn) {
switch (arg) {
case IRConstant(String s) -> {
return s.toUpperCase(Locale.ROOT);
}
case IRConstant(Object value) -> throw new IllegalStateException(
"[CallGenerator] syscall 第一个参数必须是字符串常量 (function: %s, value: %s)"
.formatted(fn, value)
);
case IRVirtualRegister(int id) -> {
String s = STRING_CONST_POOL.get(id);
if (s == null) {
throw new IllegalStateException(
"[CallGenerator] 未找到 syscall 字符串常量绑定 (function: %s, regId: %d)"
.formatted(fn, id)
);
}
return s.toUpperCase(Locale.ROOT);
}
case null, default -> throw new IllegalStateException(
"[CallGenerator] syscall 第一个参数必须是字符串常量或已绑定字符串的寄存器 (function: %s, arg: %s)"
.formatted(fn, arg)
);
}
}
/**
* 加载一个参数到栈支持指定默认类型如果未设置类型则采用默认类型
*
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位的映射
* @param arg 参数值应为虚拟寄存器
* @param defaultType 默认类型
* @param fn 当前函数名用于错误提示
* @throws IllegalStateException 如果参数不是虚拟寄存器或槽位未找到
*/
private void loadArgument(VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, IRValue arg, char defaultType, String fn) {
if (!(arg instanceof IRVirtualRegister vr)) {
throw new IllegalStateException(
"[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
}
Integer slot = slotMap.get(vr);
if (slot == null) {
throw new IllegalStateException(
"[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
}
char t = out.getSlotType(slot);
if (t == '\0') t = defaultType;
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
}
/**
* 生成一维/多维数组的下标访问指令支持类型分派
* <p>
* 本方法用于将 IR 层的数组下标访问表达式 arr[idx]生成对应的 VM 指令序列
* 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问
* </p>
*
* <ul>
* <li>1. 依次加载数组参数arr和下标参数idx到操作数栈</li>
* <li>2. 发出 ARR_GET 系统调用指令SYSCALL ARR_GET通过 VM 访问对应元素</li>
* <li>3. 根据元素声明类型将结果写入目标槽位支持类型分派B/S/I/L/F/D/R</li>
* <li>4. 若参数或返回寄存器缺失则抛出异常提示</li>
* </ul>
*
* @param ins 下标访问对应的 IR 调用指令函数名通常为 __index_x
* @param out VM 程序构建器用于发出 VM 指令
* @param slotMap IR 虚拟寄存器到 VM 槽位的映射表
* @param retType 元素类型标识'B' byte, 'S' short, 'I' int, 'L' long, 'F' float, 'D' double, 'R' ref/obj
* @throws IllegalStateException 参数个数不符缺少目标寄存器未找到槽位等情况
*/
private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, char retType) {
String fn = ins.getFunctionName();
List<IRValue> args = ins.getArguments();
if (args.size() != 2) {
throw new IllegalStateException(
"[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args));
}
// 加载数组参数寄存器类型按 R/ref 处理默认对象槽位
loadArgument(out, slotMap, args.get(0), 'R', fn);
// 加载下标参数寄存器类型按 I/int 处理
loadArgument(out, slotMap, args.get(1), 'I', fn);
// 发出 ARR_GET 系统调用元素访问由 VM 完成类型分派
out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
// 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
throw new IllegalStateException(
"[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
}
// 按元素类型分派写入 VM 槽位
switch (retType) {
case 'B' -> {
out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'B');
}
case 'S' -> {
out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'S');
}
case 'I' -> {
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
}
case 'L' -> {
out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'L');
}
case 'F' -> {
out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'F');
}
case 'D' -> {
out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'D');
}
default -> {
out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'R');
}
}
}
/**
* 生成 syscall 指令分支逻辑
* <ol>
* <li>解析 syscall 子命令</li>
* <li>压栈剩余参数</li>
* <li>发出 SYSCALL 指令</li>
* <li>若有返回值则保存至目标槽位</li>
* </ol>
*
* @param ins 调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位映射
* @param fn 当前函数名
*/
private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
List<IRValue> args = ins.getArguments();
if (args.isEmpty()) {
throw new IllegalStateException(
"[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
}
// 0. 解析 syscall 子命令第一个参数
String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
// 1. 压栈其余 syscall 参数 index 1 开始
for (int i = 1; i < args.size(); i++) {
loadArgument(out, slotMap, args.get(i), 'R', fn);
}
// 2. 生成 SYSCALL 指令
out.emit(VMOpCode.SYSCALL + " " + subcmd);
// 3. 有返回值则保存到目标槽位
IRVirtualRegister dest = ins.getDest();
if (dest != null) {
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] syscall 未找到目标寄存器的槽位映射 (function: %s, dest: %s)"
.formatted(fn, dest));
}
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
}
}
/**
* 生成普通函数调用指令
* <ol>
* <li>推断返回值类型</li>
* <li>压栈所有参数</li>
* <li>生成 CALL 指令</li>
* <li>保存返回值若非 void</li>
* </ol>
*
* @param ins 调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位映射
* @param fn 当前函数名
*/
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
// 1. 推断返回值类型首字母大写缺省为 'I'
String retTypeName = GlobalFunctionTable.getReturnType(fn);
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
// 2. 压栈所有参数
for (IRValue arg : ins.getArguments()) {
loadArgument(out, slotMap, arg, 'R', fn);
}
// 3. 发出 CALL 指令
out.emitCall(fn, ins.getArguments().size());
// 3.5 void 返回直接结束
if ("void".equals(retTypeName)) {
return;
}
// 4. 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
throw new IllegalStateException(
"[CallGenerator] 普通函数调用未找到目标寄存器 (function: %s)".formatted(fn));
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
}
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
out.setSlotType(destSlot, retType);
}

View File

@ -7,21 +7,93 @@ import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* <b>LoadConstGenerator - IR {@code LoadConstInstruction} 生成 VM 指令</b>
* <b>LoadConstGenerator - Generates VM instructions from IR {@code LoadConstInstruction}</b>
*
* <p>
* 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令
* 额外支持如果常量类型为 {@code String}会同步登记到
* {@link CallGenerator} 的字符串常量池方便 syscall 降级场景使用
* This class is responsible for converting IR-level {@link LoadConstInstruction} into corresponding VM instructions.
* If the constant is a {@code String}, it will also be registered in the
* {@link CallGenerator} string constant pool to support syscall downgrade scenarios.
* </p>
*
* <p>
* Fix: When the constant is an array (List), type information is preserved in R_PUSH payload:
* <ul>
* <li>Float is output with <code>f</code> suffix (e.g., 0.1f);</li>
* <li>Long is output with <code>L</code> suffix (e.g., 123L);</li>
* <li>Double/Integer are output in their default format (e.g., 1.0, 42);</li>
* <li>Supports recursive serialization of nested arrays.</li>
* </ul>
* This prevents float values from being misinterpreted as double on the VM side,
* and avoids DoubleFloat cast exceptions in later F_STORE operations.
* </p>
*/
public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> {
/**
* 指定本生成器支持的 IR 指令类型LoadConstInstruction
* Formats a constant value as a string for use as a VM payload.
* Lists are recursively serialized, and Float/Long types include suffixes to preserve type information.
*
* @param v The constant value to format.
* @return The formatted string for use in VM code.
*/
private static String formatConst(Object v) {
return formatConst(v, false);
}
/**
* Internal helper for recursively formatting constant values (including nested arrays)
* with appropriate type suffixes for array payloads.
*
* @param v The constant value to format.
* @param insideArray True if currently formatting inside an array context; affects whether type suffixes are applied.
* @return The formatted string for use in VM code.
*/
private static String formatConst(Object v, boolean insideArray) {
if (v instanceof List<?> list) {
// Recursively process each element in the list
return "[" + list.stream()
.map(x -> formatConst(x, true))
.collect(Collectors.joining(", ")) + "]";
}
if (v instanceof String s) {
return s;
}
if (v instanceof Float f) {
// Always keep .0 for integer values
float fv = f;
String s = (fv == (long) fv) ? String.format("%.1f", fv) : f.toString();
return insideArray ? (s + "f") : s;
}
if (v instanceof Long l) {
return insideArray ? (l + "L") : l.toString();
}
if (v instanceof Double d) {
double dv = d;
// Always keep .0 for integer values
return (dv == (long) dv) ? String.format("%.1f", dv) : Double.toString(dv);
}
if (v instanceof Short s) {
return insideArray ? (s + "s") : Short.toString(s);
}
if (v instanceof Byte b) {
return insideArray ? (b + "b") : Byte.toString(b);
}
if (v instanceof Boolean b) {
return b ? "1" : "0";
}
return String.valueOf(v);
}
/**
* Specifies the type of IR instruction supported by this generator.
*
* @return The class object representing {@link LoadConstInstruction}.
*/
@Override
public Class<LoadConstInstruction> supportedClass() {
@ -29,12 +101,16 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
}
/**
* 生成 VM 指令主流程
* Generates the VM instructions for a given {@link LoadConstInstruction}.
* <p>
* This includes formatting the constant value, emitting the corresponding PUSH and STORE instructions,
* marking the local slot type for later operations, and registering string constants if necessary.
* </p>
*
* @param ins 当前常量加载指令
* @param out 指令输出构建器
* @param slotMap 虚拟寄存器与物理槽位映射
* @param currentFn 当前函数名
* @param ins The {@link LoadConstInstruction} to generate code for.
* @param out The {@link VMProgramBuilder} used to collect the generated instructions.
* @param slotMap A mapping from {@link IRVirtualRegister} to physical slot indices.
* @param currentFn The name of the current function.
*/
@Override
public void generate(LoadConstInstruction ins,
@ -42,34 +118,35 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
Map<IRVirtualRegister, Integer> slotMap,
String currentFn) {
/* 1. 获取常量值 */
// 1. Get the constant value
IRConstant constant = (IRConstant) ins.operands().getFirst();
Object value = constant.value();
/* 2. 生成 PUSH 指令,将常量值入栈 */
out.emit(OpHelper.pushOpcodeFor(value) + " " + value);
// 2. Generate PUSH instruction (array constants use type-aware formatting)
String payload = formatConst(value);
out.emit(OpHelper.pushOpcodeFor(value) + " " + payload);
/* 3. STORE 到目标槽位 */
// 3. STORE the result to the destination slot
int slot = slotMap.get(ins.dest());
out.emit(OpHelper.storeOpcodeFor(value) + " " + slot);
/* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */
// 4. Mark the slot's data type for later inference and instruction selection
char prefix = switch (value) {
case Integer _ -> 'I'; // 整型
case Long _ -> 'L'; // 长整型
case Short _ -> 'S'; // 短整型
case Byte _ -> 'B'; // 字节型
case Double _ -> 'D'; // 双精度
case Float _ -> 'F'; // 单精度
case Boolean _ -> 'I'; // 布尔类型用 I 处理
case String _ -> 'R'; // 字符串常量
case null, default -> // 其它类型异常
throw new IllegalStateException("未知的常量类型: "
case Integer _ -> 'I'; // Integer
case Long _ -> 'L'; // Long
case Short _ -> 'S'; // Short
case Byte _ -> 'B'; // Byte
case Double _ -> 'D'; // Double
case Float _ -> 'F'; // Float
case Boolean _ -> 'I'; // Boolean handled as Integer (typically lowered to 1/0)
case String _ -> 'R'; // String constant
case java.util.List<?> _ -> 'R'; // Reference type (arrays, etc.)
case null, default -> throw new IllegalStateException("Unknown constant type: "
+ (value != null ? value.getClass() : null));
};
out.setSlotType(slot, prefix);
/* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */
// 5. If the constant is a string, register it for the CallGenerator string pool
if (value instanceof String s) {
CallGenerator.registerStringConst(ins.dest().id(), s);
}

View File

@ -234,6 +234,7 @@ public final class OpHelper {
if (v instanceof Double) return "D";
if (v instanceof Float) return "F";
if (v instanceof String) return "R"; //引用类型
if (v instanceof java.util.List) return "R"; // 引用类型数组等
throw new IllegalStateException("Unknown const type: " + v.getClass());
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.core.IROpCode;
import org.jcnc.snow.compiler.ir.core.IRValue;
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
@ -11,40 +12,26 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
/**
* <b>ExpressionBuilder - 表达式 IR 构建器</b>
*
* {@code ExpressionBuilder} 表达式 IR 构建器
* <p>
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作并生成对应的 IR 指令序列
* 支持字面量标识符二元表达式一元表达式函数调用等多种类型表达式
* </p>
*
* <p>
* 主要功能
* 支持字面量标识符二元表达式一元表达式函数调用数组下标等多种类型表达式
* <ul>
* <li>将表达式节点映射为虚拟寄存器</li>
* <li>为每种表达式类型生成对应 IR 指令</li>
* <li>支持表达式嵌套的递归构建</li>
* <li>支持写入指定目标寄存器避免冗余的 move 指令</li>
* <li>支持 IndexExpressionNode 的编译期折叠arr[2]并在运行时降级为 __index_i/__index_r</li>
* </ul>
* </p>
*/
public record ExpressionBuilder(IRContext ctx) {
/* ───────────────── 顶层入口 ───────────────── */
/**
* 构建任意 AST 表达式节点自动为其分配一个新的虚拟寄存器并返回该寄存器
*
* <p>
* 这是表达式 IR 生成的核心入口它会根据不同的表达式类型进行分派递归构建 IR 指令
* </p>
*
* @param expr 任意 AST 表达式节点
* @return 存储该表达式结果的虚拟寄存器
* @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
* 构建表达式返回结果寄存器
*/
public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) {
@ -58,8 +45,7 @@ public record ExpressionBuilder(IRContext ctx) {
case IdentifierNode id -> {
// 查找当前作用域中的变量寄存器
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg == null)
throw new IllegalStateException("未定义标识符: " + id.name());
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
yield reg;
}
// 二元表达式 a+b, x==y
@ -68,7 +54,8 @@ public record ExpressionBuilder(IRContext ctx) {
case CallExpressionNode call -> buildCall(call);
// 一元表达式 -a, !a
case UnaryExpressionNode un -> buildUnary(un);
case IndexExpressionNode idx -> buildIndex(idx);
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
// 默认分支遇到未知表达式类型则直接抛异常
default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName());
@ -78,33 +65,40 @@ public record ExpressionBuilder(IRContext ctx) {
/* ───────────────── 写入指定寄存器 ───────────────── */
/**
* 生成表达式并将其结果直接写入目标寄存器避免冗余的 move 操作
*
* 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}
* <p>
* 某些简单表达式如字面量变量名可以直接写入目标寄存器复杂表达式则会先 build 到新寄存器 move 到目标寄存器
* 按表达式类型分派处理包括
* <ul>
* <li>字面量数字字符串布尔数组生成 loadConst 指令直接写入目标寄存器</li>
* <li>变量标识符查表获取源寄存器 move 到目标寄存器</li>
* <li>二元表达式下标调用表达式递归生成子表达式结果并写入目标寄存器</li>
* <li>其它类型统一先 build 到临时寄存器 move 到目标寄存器</li>
* </ul>
* </p>
*
* @param node 要生成的表达式节点
* @param dest 目标虚拟寄存器用于存储结果
* @param node 要求值的表达式节点
* @param dest 结果目标虚拟寄存器
* @throws IllegalStateException 若标识符未定义如变量未声明时引用
*/
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) {
// 数字字面量生成 loadConst 指令写入目标寄存器
case NumberLiteralNode n ->
InstructionFactory.loadConstInto(
// 数字字面量生成 loadConst 指令将数值常量写入目标寄存器
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
// 字符串字面量生成 loadConst 指令写入目标寄存器
case StringLiteralNode s ->
InstructionFactory.loadConstInto(
// 字符串字面量生成 loadConst 指令将字符串常量写入目标寄存器
case StringLiteralNode s -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(s.value()));
// 布尔字面量转换为 int 1/0生成 loadConst
case BoolLiteralNode b ->
InstructionFactory.loadConstInto(
// 布尔字面量转换为 int 1/0生成 loadConst 指令写入目标寄存器
case BoolLiteralNode b -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
// 标识符查表获取原寄存器然后 move 到目标寄存器
// 数组字面量生成数组常量并写入目标寄存器
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
ctx, dest, buildArrayConstant(arr));
// 变量标识符查表获得源寄存器move 到目标寄存器
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null)
@ -112,10 +106,22 @@ public record ExpressionBuilder(IRContext ctx) {
InstructionFactory.move(ctx, src, dest);
}
// 二元表达式递归生成并写入目标寄存器
// 二元表达式递归生成左右子表达式将结果写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
// 其它复杂情况 build 到新寄存器 move 到目标寄存器
// 下标表达式递归生成索引结果move 到目标寄存器
case IndexExpressionNode idx -> {
IRVirtualRegister tmp = buildIndex(idx);
InstructionFactory.move(ctx, tmp, dest);
}
// 调用表达式递归生成调用结果move 到目标寄存器
case CallExpressionNode call -> {
IRVirtualRegister tmp = buildCall(call);
InstructionFactory.move(ctx, tmp, dest);
}
// 其它类型统一先 build 到临时寄存器 move 到目标寄存器
default -> {
IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest);
@ -123,7 +129,201 @@ public record ExpressionBuilder(IRContext ctx) {
}
}
/* ───────────────── 具体表达式类型 ───────────────── */
/**
* 构建下标访问表达式IndexExpressionNode
* <ul>
* <li>若数组和下标均为编译期常量则直接进行常量折叠直接返回目标常量寄存器</li>
* <li>否则
* <ul>
* <li>若数组表达式本身是下一个下标的中间值即多维数组链式下标则先用 __index_r 获取引用</li>
* <li>最后一级用 __index_b/s/i/l/f/d/r按声明类型智能分派</li>
* </ul>
* </li>
* </ul>
*
* @param node 下标访问表达式节点
* @return 存放结果的虚拟寄存器
*/
private IRVirtualRegister buildIndex(IndexExpressionNode node) {
// 1. 常量折叠如果 array index 都是编译期常量直接取值
Object arrConst = tryFoldConst(node.array());
Object idxConst = tryFoldConst(node.index());
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
int i = num.intValue();
// 越界检查
if (i < 0 || i >= list.size())
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
Object elem = list.get(i);
IRVirtualRegister r = ctx.newRegister();
// 加载常量元素到新寄存器
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
return r;
}
// 2. 处理多级下标 arr[1][2]中间层用 __index_r 返回引用
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
? buildIndexRef(inner) // 递归获取引用
: build(node.array()); // 否则直接生成 array 的值
// 3. 生成下标值
IRVirtualRegister idxReg = build(node.index());
// 4. 创建目标寄存器
IRVirtualRegister dest = ctx.newRegister();
// 5. 准备参数
List<IRValue> argv = new ArrayList<>();
argv.add(arrReg);
argv.add(idxReg);
// 6. 选择调用指令
if (node.array() instanceof IndexExpressionNode) {
// 非最末层下标取引用
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
} else {
// 最末层下标取实际元素值按声明类型分派
String func = "__index_i"; // 默认整型
if (node.array() instanceof IdentifierNode id) {
String declType = ctx.getScope().lookupType(id.name()); // "double[]""int[]"
if (declType != null) {
String base = declType.toLowerCase();
int p = base.indexOf('[');
if (p > 0) base = base.substring(0, p); // 基本类型
switch (base) {
case "byte" -> func = "__index_b";
case "short" -> func = "__index_s";
case "int" -> func = "__index_i";
case "long" -> func = "__index_l";
case "float" -> func = "__index_f";
case "double" -> func = "__index_d";
case "boolean" -> func = "__index_i"; // 布尔型用 int 通道返回 1/0
case "string" -> func = "__index_r"; // 字符串/其它未识别类型均走引用
default -> func = "__index_r";
}
}
}
ctx.addInstruction(new CallInstruction(dest, func, argv));
}
return dest;
}
/**
* 构建中间层下标访问表达式返回引用
* <p>
* 用于多维数组的链式下标访问 arr[1][2]保证中间结果是可被再次下标的引用
* <ul>
* <li>若数组和下标均为编译期常量则直接常量折叠返回目标常量寄存器</li>
* <li>否则递归处理 array生成引用指令__index_r</li>
* </ul>
* </p>
*
* @param node 下标访问表达式节点
* @return 存放引用结果的虚拟寄存器
*/
public IRVirtualRegister buildIndexRef(IndexExpressionNode node) {
// 1. 常量折叠如果 array index 都是编译期常量直接取值
Object arrConst = tryFoldConst(node.array());
Object idxConst = tryFoldConst(node.index());
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
int i = num.intValue();
// 越界检查
if (i < 0 || i >= list.size())
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
Object elem = list.get(i);
IRVirtualRegister r = ctx.newRegister();
// 加载常量元素到新寄存器
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
return r;
}
// 2. 递归生成 array 引用用于支持链式多级下标
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
? buildIndexRef(inner) // 递归向下返回引用
: build(node.array()); // 基础数组直接 build
// 3. 生成下标值
IRVirtualRegister idxReg = build(node.index());
// 4. 创建目标寄存器
IRVirtualRegister dest = ctx.newRegister();
// 5. 组织参数列表
List<IRValue> argv = new ArrayList<>();
argv.add(arrReg);
argv.add(idxReg);
// 6. 生成 __index_r 调用指令总是返回引用
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
return dest;
}
/**
* 常量折叠工具支持嵌套数组
* <p>
* 尝试将表达式节点 {@code expr} 折叠为常量值用于编译期计算/优化
* <ul>
* <li>数字字面量返回 int double</li>
* <li>字符串字面量返回字符串</li>
* <li>布尔字面量返回 1true 0false</li>
* <li>数组字面量递归折叠所有元素为 List如果有一项不能折叠则返回 null</li>
* <li>标识符尝试从作用域查找编译期常量值</li>
* <li>其它类型无法折叠返回 null</li>
* </ul>
* </p>
*
* @param expr 需要折叠的表达式节点
* @return 编译期常量值支持 intdoubleStringList否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
if (expr == null) return null;
// 数字字面量尝试解析为 int double
if (expr instanceof NumberLiteralNode n) {
String s = n.value();
try {
if (s.contains(".") || s.contains("e") || s.contains("E"))
return Double.parseDouble(s); // 带小数或科学计数法为 double
return Integer.parseInt(s); // 否则为 int
} catch (NumberFormatException e) {
return null; // 无法解析为数字
}
}
// 字符串字面量直接返回字符串
if (expr instanceof StringLiteralNode s) return s.value();
// 布尔字面量true 返回 1false 返回 0
if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
// 数组字面量递归折叠所有元素
if (expr instanceof ArrayLiteralNode arr) {
List<Object> list = new ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e);
if (v == null) return null; // 有一项无法折叠则整体失败
list.add(v);
}
return List.copyOf(list);
}
// 标识符尝试查找作用域中的常量值
if (expr instanceof IdentifierNode id) {
Object v = null;
try {
v = ctx.getScope().getConstValue(id.name());
} catch (Throwable ignored) {
// 查不到常量或异常都视为无法折叠
}
return v;
}
// 其它类型不支持折叠返回 null
return null;
}
/**
* 一元表达式构建
@ -140,59 +340,67 @@ public record ExpressionBuilder(IRContext ctx) {
IRVirtualRegister src = build(un.operand());
// 分配目标寄存器
IRVirtualRegister dest = ctx.newRegister();
switch (un.operator()) {
// 算术负号生成取负指令
// 算术负号生成取负指令例如-a
case "-" -> ctx.addInstruction(
new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
// 逻辑非等价于 a == 0生成比较指令
// 逻辑非等价于 a == 0生成整数等于比较指令!a
case "!" -> {
// 生成常量0的寄存器
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
// 比较 src 是否等于0等价于逻辑非
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
}
// 其它一元运算符不支持异常
// 其它一元运算符不支持异常
default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
}
return dest;
}
/**
* 构建函数或方法调用表达式模块内未限定调用会自动补全当前模块名
* 构建函数或方法调用表达式
* <p>
* 支持普通函数调用foo(a, b)与成员方法调用obj.method(a, b)
* <ul>
* <li>首先递归生成所有参数的虚拟寄存器列表</li>
* <li>根据 callee 类型区分成员访问或直接标识符调用并规范化方法名如加前缀</li>
* <li>为返回值分配新寄存器生成 Call 指令</li>
* </ul>
* </p>
*
* @param call AST 调用表达式节点
* @return 存储调用结果的虚拟寄存器
* @param call 函数/方法调用表达式节点
* @return 调用结果的虚拟寄存器
*/
private IRVirtualRegister buildCall(CallExpressionNode call) {
// 递归生成所有参数实参对应的寄存器
// 1. 递归生成所有参数的寄存器
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
// 解析被调用目标名支持普通函数/成员方法
// 2. 规范化被调用方法名区分成员方法与普通函数
String callee = switch (call.callee()) {
// 成员方法调用例如 obj.foo()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id
-> id.name() + "." + m.member();
// 普通函数调用如果未指定模块自动补全当前模块名
// 成员方法调用 obj.method()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
// 普通函数调用或处理命名空间前缀如当前方法名为 namespace.func
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();
if (dot > 0)
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
yield id.name(); // 全局函数调用
}
}
// 其它情况暂不支持
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
// 其它类型不支持
default -> throw new IllegalStateException(
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
};
// 为返回值分配新寄存器生成 Call 指令
// 3. 分配用于存放返回值的新寄存器生成 Call 指令
IRVirtualRegister dest = ctx.newRegister();
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
return dest;
}
/**
* 二元表达式构建结果存储到新寄存器
* <br>
@ -207,18 +415,21 @@ public record ExpressionBuilder(IRContext ctx) {
IRVirtualRegister b = build(bin.right());
String op = bin.operator();
// 比较运算符==!=>< 需要生成条件跳转或布尔值寄存器
// 判断是否为比较运算符==!=><
if (ComparisonUtils.isComparisonOperator(op)) {
// 生成比较操作返回布尔值寄存器
return InstructionFactory.binOp(
ctx,
// 通过比较工具获得合适的 IR 操作码
// 根据运算符和操作数类型获得合适的 IR 操作码
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
a, b);
}
// 其它算术/位运算
// 其它二元运算算术位运算等
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
if (code == null)
throw new IllegalStateException("不支持的运算符: " + op);
// 生成二元操作指令
return InstructionFactory.binOp(ctx, code, a, b);
}
@ -229,22 +440,29 @@ public record ExpressionBuilder(IRContext ctx) {
* @param dest 目标虚拟寄存器
*/
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
// 递归生成左右操作数的寄存器
IRVirtualRegister a = build(bin.left());
IRVirtualRegister b = build(bin.right());
String op = bin.operator();
// 处理比较运算符==!=><
if (ComparisonUtils.isComparisonOperator(op)) {
InstructionFactory.binOpInto(
ctx,
// 选择对应类型和符号的比较操作码
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
a, b, dest);
} else {
// 算术或位运算符
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
if (code == null)
throw new IllegalStateException("不支持的运算符: " + op);
// 生成二元操作指令写入目标寄存器
InstructionFactory.binOpInto(ctx, code, a, b, dest);
}
}
/* ───────────────── 字面量辅助方法 ───────────────── */
/**
@ -254,8 +472,11 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 存储该字面量的寄存器
*/
private IRVirtualRegister buildNumberLiteral(String value) {
// 解析数字常量
IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c));
return r;
}
@ -267,8 +488,11 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 存储该字符串的寄存器
*/
private IRVirtualRegister buildStringLiteral(String value) {
// 构建字符串常量
IRConstant c = new IRConstant(value);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c));
return r;
}
@ -280,9 +504,74 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 存储 1/0 的寄存器
*/
private IRVirtualRegister buildBoolLiteral(boolean v) {
// 转换为 1 0 的常量
IRConstant c = new IRConstant(v ? 1 : 0);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c));
return r;
}
/**
* 构建数组字面量表达式元素均为常量时
*
* @param arr 数组字面量节点
* @return 存储该数组的寄存器
*/
private IRVirtualRegister buildArrayLiteral(ArrayLiteralNode arr) {
// 递归生成支持嵌套的数组常量
IRConstant c = buildArrayConstant(arr);
// 分配新寄存器
IRVirtualRegister r = ctx.newRegister();
// 生成 LoadConst 指令
ctx.addInstruction(new LoadConstInstruction(r, c));
return r;
}
/**
* 构建支持嵌套的数组常量表达式
* <p>
* 遍历并递归处理数组字面量的所有元素
* <ul>
* <li>数字字面量根据内容生成 int double 常量</li>
* <li>字符串字面量直接存储字符串内容</li>
* <li>布尔字面量转换为 1true 0false存储</li>
* <li>数组字面量递归构建允许多层嵌套最终生成嵌套的 List</li>
* </ul>
* 若包含非常量元素则抛出异常
* </p>
*
* @param arr 数组字面量节点
* @return 封装所有常量元素支持嵌套 List {@link IRConstant}
* @throws IllegalStateException 如果数组中存在非常量元素
*/
private IRConstant buildArrayConstant(ArrayLiteralNode arr) {
List<Object> list = new ArrayList<>();
for (ExpressionNode e : arr.elements()) {
switch (e) {
// 数字字面量解析并加入
case NumberLiteralNode n -> {
IRConstant num = ExpressionUtils.buildNumberConstant(ctx, n.value());
list.add(num.value());
}
// 字符串字面量直接加入
case StringLiteralNode s -> list.add(s.value());
// 布尔字面量转成 1/0
case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0);
// 嵌套数组递归生成并加入
case ArrayLiteralNode inner -> {
IRConstant innerConst = buildArrayConstant(inner);
list.add(innerConst.value());
}
// 其它类型暂不支持
default -> throw new IllegalStateException(
"暂不支持含非常量元素的数组字面量: " + e.getClass().getSimpleName());
}
}
// 返回不可变的 List 封装为 IRConstant
return new IRConstant(List.copyOf(list));
}
}

View File

@ -7,109 +7,143 @@ import java.util.HashMap;
import java.util.Map;
/**
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系
*
* <p>主要功能包括:
* {@code IRBuilderScope} 用于管理单个函数作用域变量名与虚拟寄存器的映射关系
* <p>
* 主要职责
* <ul>
* <li>维护在当前作用域中已声明变量的寄存器分配信息</li>
* <li>支持将已有虚拟寄存器与变量名重新绑定</li>
* <li>根据变量名查找对应的虚拟寄存器实例或类型</li>
* <li>维护当前作用域内所有已声明变量的寄存器和类型信息</li>
* <li>支持变量与虚拟寄存器的重新绑定与查找</li>
* <li>支持变量的类型信息记录与查询</li>
* <li>支持变量的编译期常量值记录与查询便于常量折叠等优化</li>
* </ul>
*/
final class IRBuilderScope {
/**
* 存储变量名到对应 IRVirtualRegister 的映射
* 变量名为键虚拟寄存器对象为值用于查找和更新
* 变量名到虚拟寄存器的映射表
* 用于跟踪所有声明和分配的变量
*/
private final Map<String, IRVirtualRegister> vars = new HashMap<>();
/**
* 存储变量名到对应类型的映射
* <br>
* 变量名为键变量类型为值用于变量类型提升
* 变量名到类型字符串的映射表
* 用于类型分析与推断
*/
private final Map<String, String> varTypes = new HashMap<>();
/**
* 当前作用域所绑定的 IRFunction 对象用于申请新的虚拟寄存器
* 变量名到编译期常量值的映射表
* 用于常量折叠优化 intstring数组等常量
*/
private final Map<String, Object> varConstValues = new HashMap<>();
/**
* 当前作用域所绑定的 IRFunction 实例
* 用于申请新的虚拟寄存器
*/
private IRFunction fn;
/**
* 将指定的 IRFunction 关联到当前作用域以便后续声明变量时能够
* 调用该函数的 newRegister() 方法生成新的寄存器
* 绑定当前作用域到指定 IRFunction
*
* @param fn 要绑定到本作用域的 IRFunction 实例
* @param fn 目标 IRFunction
*/
void attachFunction(IRFunction fn) {
this.fn = fn;
}
/**
* 在当前作用域中声明一个新变量并为其分配一个新的虚拟寄存器
* 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中
* 声明一个新变量并分配新的虚拟寄存器
*
* @param name 变量名称作为映射键使用
* @param type 变量类型
* @param name 变量名称
* @param type 变量类型
*/
void declare(String name, String type) {
IRVirtualRegister reg = fn.newRegister();
vars.put(name, reg);
varTypes.put(name, type);
varConstValues.remove(name);
}
/**
* 在当前作用域中声明或导入一个已有的虚拟寄存器并将其与指定变量名绑定
* 该方法可用于将外部或前一作用域的寄存器导入到本作用域
* 声明新变量并绑定到指定的寄存器
*
* @param name 变量名称作为映射键使用
* @param type 变量类型
* @param reg 要绑定到该名称的 IRVirtualRegister 实例
* @param name 变量名称
* @param type 变量类型
* @param reg 绑定的虚拟寄存器
*/
void declare(String name, String type, IRVirtualRegister reg) {
vars.put(name, reg);
varTypes.put(name, type);
varConstValues.remove(name);
}
/**
* 更新已存在变量的虚拟寄存器绑定关系若变量已声明则替换其对应的寄存器
* 若尚未声明则等同于声明新变量
* 更新变量的虚拟寄存器绑定如变量已存在则覆盖否则等同于新声明
*
* @param name 变量名称作为映射键使用
* @param reg 新的 IRVirtualRegister 实例用于替换旧绑定
* @param name 变量名称
* @param reg 新的虚拟寄存器
*/
void put(String name, IRVirtualRegister reg) {
vars.put(name, reg);
}
/**
* 根据变量名称在当前作用域中查找对应的虚拟寄存器
* 查找变量名对应的虚拟寄存器
*
* @param name 需要查询的变量名称
* @return 如果该名称已绑定寄存器则返回对应的 IRVirtualRegister
* 如果未声明则返回 null
* @param name 变量名
* @return 已绑定的虚拟寄存器若未声明则返回 null
*/
IRVirtualRegister lookup(String name) {
return vars.get(name);
}
/**
* 根据变量名称在当前作用域中查找对应的类型
* 查找变量名对应的类型名
*
* @param name 需要查询的变量名称
* @return 如果该名称已声明则返回对应的类型
* 如果未声明则返回 null
* @param name 变量名
* @return 已声明类型字符串若未声明则返回 null
*/
String lookupType(String name) {
return varTypes.get(name);
}
/**
* 获取 变量->类型的映射 的不可变副本
* @return 变量->类型的映射 的不可变副本
* 获取变量名到类型名映射的不可变副本
*
* @return 变量名类型名映射的只读视图
*/
Map<String, String> getVarTypes() {
return Map.copyOf(varTypes);
}
// ---------------- 编译期常量相关接口 ----------------
/**
* 设置变量的编译期常量值
*
* @param name 变量名称
* @param value 常量值null 表示清除
*/
void setConstValue(String name, Object value) {
if (value == null) varConstValues.remove(name);
else varConstValues.put(name, value);
}
/**
* 获取变量的编译期常量值如没有则返回 null
*
* @param name 变量名称
* @return 编译期常量值 null
*/
Object getConstValue(String name) {
return varConstValues.get(name);
}
/**
* 清除变量的编译期常量值绑定
*
* @param name 变量名称
*/
void clearConstValue(String name) {
varConstValues.remove(name);
}
}

View File

@ -10,13 +10,23 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.ArrayDeque;
import java.util.Locale;
/**
* StatementBuilder AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器
* <b>StatementBuilder</b> AST 语句节点 ({@link StatementNode}) IR 指令序列的构建器
* <p>
* 负责将各种语句节点循环分支表达式赋值声明返回等生成对应的 IR 指令并管理作用域和控制流标签
* 负责将各类语句如循环分支表达式赋值声明返回breakcontinue
* 转换为对应的 IR 指令并自动管理作用域虚拟寄存器分配以及控制流标签 break/continue 目标
* </p>
*
* <ul>
* <li>支持多种语句类型的分发与转换</li>
* <li> {@link ExpressionBuilder} 协作完成表达式相关 IR 生成</li>
* <li>负责控制流跳转分支循环的标签分配与维护</li>
* <li>在变量赋值和声明时自动常量折叠和登记</li>
* </ul>
*
* @author [你的名字]
* @since 1.0
*/
public class StatementBuilder {
@ -24,79 +34,126 @@ public class StatementBuilder {
* 当前 IR 上下文包含作用域指令序列等信息
*/
private final IRContext ctx;
/**
* 表达式 IR 构建器用于将表达式节点转为 IR 指令
*/
private final ExpressionBuilder expr;
/**
* break 目标标签栈保存每层循环的结束标签
* break 目标标签栈保存每层循环的结束标签用于 break 跳转
*/
private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
/**
* continue 目标标签栈保存每层循环的 step 起始标签
* continue 目标标签栈保存每层循环的 step 起始标签用于 continue 跳转
*/
private final ArrayDeque<String> continueTargets = new ArrayDeque<>();
/**
* 构造方法
* 构造方法初始化 StatementBuilder
*
* @param ctx IR 编译上下文环境
* @param ctx IR 编译上下文环境包含作用域标签指令等信息
*/
public StatementBuilder(IRContext ctx) {
this.ctx = ctx;
this.expr = new ExpressionBuilder(ctx);
}
private static char typeSuffixFromType(String type) {
if (type == null) return '\0';
return switch (type.toLowerCase(Locale.ROOT)) {
case "byte" -> 'b';
case "short" -> 's';
case "long" -> 'l';
case "float" -> 'f';
case "double" -> 'd';
default -> '\0'; // 其余默认按 32-bit 整型处理
};
}
/**
* 将一个 AST 语句节点转为 IR 指令序列
* <p>
* 根据节点类型分发到对应的处理方法
* 支持循环分支表达式语句赋值声明返回breakcontinue
* 不支持的语句类型会抛出异常
* </p>
*
* @param stmt 待转换的语句节点
* @param stmt 待转换的语句节点不能为空
* @throws IllegalStateException 若遇到不支持的语句类型 break/continue 不在循环体中
*/
public void build(StatementNode stmt) {
if (stmt instanceof LoopNode loop) {
// 循环语句
buildLoop(loop);
return;
}
if (stmt instanceof IfNode ifNode) {
// 分支if-else语句
buildIf(ifNode);
return;
}
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) {
// 纯表达式语句 foo();
expr.build(exp);
return;
}
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
// 赋值语句 a = b + 1;
final String type = ctx.getScope().lookupType(var);
// 1. 设置声明变量的类型
ctx.setVarType(type);
IRVirtualRegister target = getOrDeclareRegister(var, type);
expr.buildInto(rhs, target);
// 2. 清除变量声明
// 赋值时尝试记录/清除常量
try {
Object constVal = tryFoldConst(rhs);
if (constVal != null)
ctx.getScope().setConstValue(var, constVal);
else
ctx.getScope().clearConstValue(var);
} catch (Throwable ignored) {
}
ctx.clearVarType();
return;
}
// ==== 支持数组下标赋值 arr[idx] = value ====
if (stmt instanceof IndexAssignmentNode idxAssign) {
IndexExpressionNode target = idxAssign.target();
// 1. 目标数组寄存器多维时用 buildIndexRef 拿引用
IRVirtualRegister arrReg = (target.array() instanceof IndexExpressionNode inner)
? expr.buildIndexRef(inner)
: expr.build(target.array());
// 2. 下标与右值
IRVirtualRegister idxReg = expr.build(target.index());
IRVirtualRegister valReg = expr.build(idxAssign.value());
// 3. 选择内置函数名 __setindex_x根据元素类型分派
String func = "__setindex_r";
org.jcnc.snow.compiler.parser.ast.base.ExpressionNode base = target.array();
while (base instanceof IndexExpressionNode innerIdx) base = innerIdx.array();
if (base instanceof IdentifierNode id) {
String arrType = ctx.getScope().lookupType(id.name());
if (arrType != null) {
String elemType = arrType.endsWith("[]") ? arrType.substring(0, arrType.length() - 2) : arrType;
switch (elemType) {
case "byte" -> func = "__setindex_b";
case "short" -> func = "__setindex_s";
case "int" -> func = "__setindex_i";
case "long" -> func = "__setindex_l";
case "float" -> func = "__setindex_f";
case "double" -> func = "__setindex_d";
case "boolean" -> func = "__setindex_i";
case "string" -> func = "__setindex_r";
default -> func = "__setindex_r";
}
}
}
// 4. 生成 CALL 指令
java.util.List<org.jcnc.snow.compiler.ir.core.IRValue> argv =
java.util.List.of(arrReg, idxReg, valReg);
ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(null, func, argv));
// 5. 赋值后清理常量绑定
try {
if (base instanceof IdentifierNode id2) {
ctx.getScope().clearConstValue(id2.name());
}
} catch (Throwable ignored) {}
return;
}
if (stmt instanceof DeclarationNode decl) {
// 变量声明语句 int a = 1;
if (decl.getInitializer().isPresent()) {
@ -113,16 +170,21 @@ public class StatementBuilder {
// 即使初始值是某个已存在变量 outer_i这里是值的拷贝
expr.buildInto(decl.getInitializer().get(), dest);
// 4. 清理类型设置防止影响后续变量声明
ctx.clearVarType();
// 声明赋初值时登记常量
try {
Object constVal = tryFoldConst(decl.getInitializer().get());
if (constVal != null)
ctx.getScope().setConstValue(decl.getName(), constVal);
else
ctx.getScope().clearConstValue(decl.getName());
} catch (Throwable ignored) {
}
// 5. 在作用域内将变量名与新分配的寄存器进行绑定
// 这样后续对该变量的任何操作都只会影响 dest不会反向影响初值表达式中的源变量
ctx.clearVarType();
ctx.getScope().declare(decl.getName(), decl.getType(), dest);
} else {
// 仅声明变量无初值 int a;
// 在作用域内声明并分配新寄存器但不进行初始化
ctx.getScope().declare(decl.getName(), decl.getType());
ctx.getScope().clearConstValue(decl.getName());
}
return;
}
@ -159,10 +221,11 @@ public class StatementBuilder {
}
/**
* 获取变量名对应的寄存器不存在则声明一个新的
* 获取变量名对应的寄存器如果尚未声明则新声明一个并返回
*
* @param name 变量名
* @return 变量对应的虚拟寄存器
* @param name 变量名不能为空
* @param type 变量类型不能为空
* @return 变量对应的虚拟寄存器 {@link IRVirtualRegister}
*/
private IRVirtualRegister getOrDeclareRegister(String name, String type) {
IRVirtualRegister reg = ctx.getScope().lookup(name);
@ -174,19 +237,21 @@ public class StatementBuilder {
}
/**
* 批量构建一组语句节点顺序处理每个语句
* 批量构建一组语句节点顺序依次处理
*
* @param stmts 语句节点集合
* @param stmts 语句节点集合不可为 null
*/
private void buildStatements(Iterable<StatementNode> stmts) {
for (StatementNode s : stmts) build(s);
}
/**
* 构建循环语句for/while
* 处理流程: 初始语句 条件判断 循环体 更新语句 跳回条件
* 构建循环语句for/while包括初始语句条件判断循环体更新语句跳回判断等 IR 指令
* <p>
* 自动维护 break/continue 的目标标签
* </p>
*
* @param loop 循环节点
* @param loop 循环节点不能为空
*/
private void buildLoop(LoopNode loop) {
if (loop.init() != null) build(loop.init());
@ -222,9 +287,11 @@ public class StatementBuilder {
/**
* 构建分支语句if/else
* 处理流程: 条件判断 then 分支 else 分支可选
* <p>
* 包括条件判断then 分支else 分支可选结束标签等
* </p>
*
* @param ifNode if 语句节点
* @param ifNode if 语句节点不能为空
*/
private void buildIf(IfNode ifNode) {
String lblElse = ctx.newLabel();
@ -245,11 +312,14 @@ public class StatementBuilder {
}
/**
* 条件跳转指令的生成
* 如果是二元比较表达式直接使用对应比较操作码否则等价于与 0 比较
* 发射条件跳转指令如果 cond 不成立则跳转到 falseLabel
* <p>
* 对于二元比较表达式会选择恰当的比较指令
* 其他类型表达式等价于 (cond == 0) 时跳转
* </p>
*
* @param cond 条件表达式
* @param falseLabel 条件不成立时跳转到的标签
* @param cond 条件表达式节点不可为 null
* @param falseLabel 条件不成立时跳转的标签不可为 null
*/
private void emitConditionalJump(ExpressionNode cond, String falseLabel) {
if (cond instanceof BinaryExpressionNode(
@ -271,7 +341,79 @@ public class StatementBuilder {
} else {
IRVirtualRegister condReg = expr.build(cond);
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
InstructionFactory.cmpJump(ctx, IROpCode.CMP_IEQ, condReg, zero, falseLabel);
InstructionFactory.cmpJump(ctx, org.jcnc.snow.compiler.ir.core.IROpCode.CMP_IEQ, condReg, zero, falseLabel);
}
}
/**
* 递归尝试对表达式做常量折叠constant folding
* <p>
* 该方法会根据表达式类型进行常量求值
* <ul>
* <li>如果是数字字面量解析为 Integer Double</li>
* <li>如果是字符串字面量直接返回字符串内容</li>
* <li>如果是布尔字面量返回 1true 0false</li>
* <li>如果是数组字面量会递归所有元素全部是常量时返回包含常量元素的 List</li>
* <li>如果是标识符节点尝试从作用域查找是否登记为常量如果找到则返回</li>
* <li>其余类型或无法确定常量时返回 null</li>
* </ul>
* 用于全局/局部常量传播优化与类型推断
*
* @param expr 待折叠的表达式节点允许为 null
* @return 如果可折叠则返回其常量值 IntegerDoubleStringList否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
// 1. 空节点直接返回 null
if (expr == null) return null;
// 2. 数字字面量尝试解析为 Integer Double
if (expr instanceof NumberLiteralNode n) {
String s = n.value(); // 获取文本内容
try {
// 判断是否为浮点型包含 . e/E 科学计数法
if (s.contains(".") || s.contains("e") || s.contains("E")) {
return Double.parseDouble(s); // 解析为 Double
}
return Integer.parseInt(s); // 否则解析为 Integer
} catch (NumberFormatException e) {
// 解析失败返回 null
return null;
}
}
// 3. 字符串字面量直接返回字符串内容
if (expr instanceof StringLiteralNode s) {
return s.value();
}
// 4. 布尔字面量true 返回 1false 返回 0
if (expr instanceof BoolLiteralNode b) {
return b.getValue() ? 1 : 0;
}
// 5. 数组字面量递归所有元素做常量折叠只有全为常量时才返回 List
if (expr instanceof ArrayLiteralNode arr) {
java.util.List<Object> list = new java.util.ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e); // 递归折叠每个元素
if (v == null) return null; // 只要有一个不是常量则整个数组不是常量
list.add(v);
}
// 所有元素均为常量返回只读 List
return java.util.List.copyOf(list);
}
// 6. 标识符尝试查找该变量在当前作用域是否登记为常量
if (expr instanceof IdentifierNode id) {
try {
Object v = ctx.getScope().getConstValue(id.name());
if (v != null) return v; // 查到常量则返回
} catch (Throwable ignored) {
}
}
// 7. 其他情况均视为不可折叠返回 null
return null;
}
}

View File

@ -28,7 +28,7 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
return ":,().+-*/".indexOf(c) >= 0;
return ":,().+-*/[]".indexOf(c) >= 0;
}
/**
@ -53,6 +53,8 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
case '/' -> TokenType.DIVIDE;
case '(' -> TokenType.LPAREN;
case ')' -> TokenType.RPAREN;
case '[' -> TokenType.LBRACKET;
case ']' -> TokenType.RBRACKET;
default -> TokenType.UNKNOWN;
};
return new Token(type, String.valueOf(c), line, col);

View File

@ -14,10 +14,10 @@ public enum TokenType {
/** 普通标识符,如变量名、函数名等 */
IDENTIFIER,
/** 语言保留关键字(如 if、return、module 等) */
/** 关键字declare、if、else、loop、break、continue、return 等) */
KEYWORD,
/** 内置类型名称(如 int、string、bool 等) */
/** 内置类型名byte、short、int、long、float、double、string、boolean、void 等) */
TYPE,
/* ---------- 字面量 ---------- */
@ -67,6 +67,12 @@ public enum TokenType {
/** 右括号 ')' */
RPAREN,
/** 左中括号 '[' */
LBRACKET,
/** 右中括号 ']' */
RBRACKET,
/** 相等比较符号 '==' */
DOUBLE_EQUALS,

View File

@ -0,0 +1,40 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.List;
/**
* {@code ArrayLiteralNode} 表示数组字面量表达式节点
* <p>
* 例如[1, 2, 3] [[1, 2], [3, 4]] 这样的语法结构均对应本节点类型
* </p>
*
* <ul>
* <li>{@link #elements()} 保存所有元素表达式节点</li>
* <li>{@link #context()} 表示该节点在源代码中的上下文信息如位置父节点等</li>
* </ul>
*/
public record ArrayLiteralNode(
/*
数组字面量中的所有元素表达式按顺序
*/
List<ExpressionNode> elements,
/*
节点的上下文信息如源码位置等
*/
NodeContext context
) implements ExpressionNode {
/**
* 返回字符串形式 {@code Array[1, 2, 3]}
*
* @return 表示该数组字面量节点的字符串
*/
@Override
public String toString() {
return "Array" + elements;
}
}

View File

@ -0,0 +1,42 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
/**
* {@code IndexAssignmentNode} 表示数组元素赋值语句节点例如 {@code arr[i] = value}
* <p>
* 对于多维数组{@code target} 可以是嵌套的 {@link IndexExpressionNode}
* </p>
*
* @param target 要赋值的数组元素支持多维数组下标
* @param value 右侧赋值表达式
* @param context 该节点的源代码上下文信息
*/
public record IndexAssignmentNode(
/*
数组元素目标支持多维数组下标 arr[i][j]
*/
IndexExpressionNode target,
/*
被赋的右侧表达式
*/
ExpressionNode value,
/*
节点的上下文信息如源码位置等
*/
NodeContext context
) implements StatementNode {
/**
* 返回赋值语句的字符串表示 "arr[i] = value"
*
* @return 字符串形式
*/
@Override
public String toString() {
return target + " = " + value;
}
}

View File

@ -0,0 +1,41 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code IndexExpressionNode} 表示数组/集合的下标访问表达式节点例如 {@code arr[i]}
* <p>
* 支持多维数组嵌套 arr[i][j]
* </p>
*
* @param array 被访问的目标表达式通常是数组集合或嵌套下标表达式
* @param index 下标表达式必须为数值类型
* @param context 源码上下文信息如位置
*/
public record IndexExpressionNode(
/*
下标访问的目标表达式 arr
*/
ExpressionNode array,
/*
下标表达式 i
*/
ExpressionNode index,
/*
源码上下文信息如行号文件名等
*/
NodeContext context
) implements ExpressionNode {
/**
* 返回形如 "arr[i]" 的字符串表示
* @return 表达式的字符串形式
*/
@Override
public String toString() {
return array + "[" + index + "]";
}
}

View File

@ -0,0 +1,80 @@
package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
import java.util.ArrayList;
import java.util.List;
/**
* {@code ArrayLiteralParselet} 用于解析数组字面量表达式
* <p>
* 支持语法形如 {@code [a, b, c]} {@code [ [1,2], [3,4] ]}
* 允许元素及逗号前后出现换行符便于多行书写
* </p>
* <p>
* 语法结构为<br>
* <pre>[ (element (',' element)*)? ]</pre>
* </p>
*/
public class ArrayLiteralParselet implements PrefixParselet {
/**
* 解析数组字面量表达式
*
* @param ctx 解析上下文包含词法流源文件信息等
* @param token 已消费的左中括号LBRACKET用于定位节点源信息
* @return 解析得到的 {@link ArrayLiteralNode} 节点
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
// token 为已消费的 LBRACKET使用其位置生成 NodeContext
int line = token.getLine();
int col = token.getCol();
String file = ctx.getSourceName();
TokenStream ts = ctx.getTokens();
skipNewlines(ts);
List<ExpressionNode> elements = new ArrayList<>();
// 空数组: 直接遇到 RBRACKET
if (ts.peek().getType() != TokenType.RBRACKET) {
while (true) {
// 解析一个元素
ExpressionNode elem = new PrattExpressionParser().parse(ctx);
elements.add(elem);
skipNewlines(ts);
// 逗号继续右中括号结束
if (ts.peek().getType() == TokenType.COMMA) {
ts.next();
skipNewlines(ts);
continue;
}
break;
}
}
// 期望并消费右中括号
ts.expectType(TokenType.RBRACKET);
return new ArrayLiteralNode(elements, new NodeContext(line, col, file));
}
/**
* 跳过词法流中连续的换行符允许数组元素跨多行书写
*
* @param ts 词法流
*/
private static void skipNewlines(TokenStream ts) {
while (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
}
}

View File

@ -0,0 +1,51 @@
package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
/**
* {@code IndexParselet} 负责解析数组或集合的下标访问表达式语法结构为
* <pre>
* primary '[' expr ']'
* </pre>
* 例如 {@code arr[expr]}
*/
public class IndexParselet implements InfixParselet {
/**
* 解析下标访问表达式
*
* @param ctx 解析上下文包含词法流错误信息等
* @param left 已解析的 primary 表达式如数组名表达式等
* @return {@link IndexExpressionNode} 下标访问表达式节点
*/
@Override
public ExpressionNode parse(ParserContext ctx, ExpressionNode left) {
int line = left.context().line();
int col = left.context().column();
String file = left.context().file();
// 消耗左中括号 '['
ctx.getTokens().expectType(TokenType.LBRACKET);
// 解析索引表达式
ExpressionNode index = new PrattExpressionParser().parse(ctx);
// 消耗右中括号 ']'
ctx.getTokens().expectType(TokenType.RBRACKET);
// 构造下标访问表达式节点
return new IndexExpressionNode(left, index, new NodeContext(line, col, file));
}
/**
* 下标访问优先级与函数调用一致
*
* @return {@link Precedence#CALL} 优先级
*/
@Override
public Precedence precedence() {
return Precedence.CALL;
}
}

View File

@ -25,45 +25,80 @@ import java.util.Map;
*/
public class PrattExpressionParser implements ExpressionParser {
/** 前缀解析器注册表(按 Token 类型名索引) */
/**
* 前缀解析器注册表 Token 类型名或词素索引
* <p>
* 用于存储所有支持的前缀表达式解析器例如字面量变量分组数组一元运算等
* 支持通过 TokenType 的名称和特定词素 "(", "["两种方式索引
* </p>
*/
private static final Map<String, PrefixParselet> prefixes = new HashMap<>();
/** 中缀解析器注册表(按运算符词素索引) */
/**
* 中缀解析器注册表按运算符词素索引
* <p>
* 用于存储所有支持的中缀表达式解析器如二元运算函数调用下标成员访问等
* 仅通过词素索引 "+", "-", "(", "["
* </p>
*/
private static final Map<String, InfixParselet> infixes = new HashMap<>();
static {
// 前缀解析器注册
// -------- 前缀解析器注册 --------
// 注册数字字面量解析
prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
// 注册标识符变量名解析
prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
// 注册字符串字面量解析
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
// 注册布尔字面量解析
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
// 一元前缀运算符
// 支持括号分组数组字面量
prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
// 兼容直接以词素注册 '(' '['
prefixes.put("(", new GroupingParselet());
prefixes.put("[", new ArrayLiteralParselet());
// 一元前缀运算符负号逻辑非
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
prefixes.put("-", new UnaryOperatorParselet());
prefixes.put("!", new UnaryOperatorParselet());
// 中缀解析器注册
// -------- 中缀解析器注册 --------
// 注册常见二元运算符加减乘除取模
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true));
// 比较运算符
infixes.put(">", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("<", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put(">=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("<=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
// 相等性
infixes.put("==", new BinaryOperatorParselet(Precedence.EQUALITY, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true));
// 逻辑与或
infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true));
infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
// 调用索引成员访问
infixes.put("(", new CallParselet());
infixes.put("[", new IndexParselet());
infixes.put(".", new MemberParselet());
}
/**
* 表达式解析统一入口
* 以最低优先级启动递归下降适配任意表达式复杂度
* 解析任意表达式的统一入口
* <p>
* 该方法将以最低优先级启动表达式递归解析能够自动适配和处理多层嵌套或复杂组合表达式
* </p>
*
* @param ctx 当前解析上下文
* @return 解析的表达式 AST 节点
* @param ctx 当前解析上下文对象持有 token 流等信息
* @return 解析得到的表达式 AST 节点对象
*/
@Override
public ExpressionNode parse(ParserContext ctx) {
@ -71,21 +106,32 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
* 按指定优先级解析表达式Pratt 算法主循环
* 按指定优先级解析表达式Pratt 算法核心
* <p>
* 先根据当前 Token 类型查找前缀解析器进行初始解析
* 然后根据优先级不断递归处理中缀运算符和右侧表达式
* 1. 先取当前 token查找对应的前缀解析器进行初始解析构建表达式左侧如字面量变量等
* 2. 然后循环检测是否有更高优先级的中缀操作符
* 若有则递归处理右侧表达式并组合为新的表达式节点
* </p>
* <p>
* 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常
* </p>
*
* @param ctx 解析上下文
* @param prec 当前运算符优先级阈值
* @return 构建完成的表达式节点
* @throws UnsupportedFeature 遇到未注册的前缀或中缀解析器
* @param prec 当前运算符优先级用于控制递归层级
* @return 解析构建好的表达式节点
* @throws UnsupportedFeature 遇到未注册的解析器时抛出
*/
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
// 取下一个 token 作为本轮前缀表达式起始
Token token = ctx.getTokens().next();
// 查找前缀解析器先按类型名再按词素
PrefixParselet prefix = prefixes.get(token.getType().name());
if (prefix == null) {
prefix = prefixes.get(token.getLexeme());
}
if (prefix == null) {
// 未找到前缀解析器则报错
throw new UnsupportedFeature(
"没有为该 Token 类型注册前缀解析器: " + token.getType(),
token.getLine(),
@ -93,13 +139,17 @@ public class PrattExpressionParser implements ExpressionParser {
);
}
// 执行前缀解析获得左侧表达式
ExpressionNode left = prefix.parse(ctx, token);
// 不断尝试查找优先级更高的中缀运算符递归处理表达式链
while (!ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) {
// 查看下一个 token 词素查找中缀解析器
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
if (infix == null) {
// 若未注册中缀解析器则直接抛异常常见于语法错误
Token t = ctx.getTokens().peek();
throw new UnsupportedFeature(
"没有为该运算符注册中缀解析器: '" + lex + "'",
@ -107,16 +157,21 @@ public class PrattExpressionParser implements ExpressionParser {
t.getCol()
);
}
// 使用中缀解析器处理表达式组合
left = infix.parse(ctx, left);
}
// 返回本层递归已解析的表达式节点
return left;
}
/**
* 获取下一个中缀解析器的优先级Pratt 算法核心
* 获取下一个 token 词素对应的中缀运算符优先级Pratt 算法关键
* <p>
* 用于决定当前是否需要递归处理更高优先级的中缀操作
* </p>
*
* @param ctx 当前解析上下文
* @return 下一个中缀运算符的优先级序号若无解析器则为 -1
* @return 下一个中缀运算符的优先级序号若无注册解析器则返回 -1
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());

View File

@ -9,26 +9,30 @@ package org.jcnc.snow.compiler.parser.expression;
*/
public enum Precedence {
/**
* 最低优先级通常用于整个表达式解析的起始入口
*/
/** 最低优先级,通常用于整个表达式解析的起始入口。 */
LOWEST,
/**
* 加法和减法的优先级例如 +-
*/
/** 逻辑或(|| */
OR,
/** 逻辑与(&& */
AND,
/** 相等/不等(==, != */
EQUALITY,
/** 大小比较(<, >, <=, >= */
COMPARISON,
/** 加法和减法(+、- */
SUM,
/**
* 乘法除法取模等更高优先级的二元运算符例如 */%
*/
/** 乘法、除法、取模(*、/、% */
PRODUCT,
/** 一元前缀(-x !x */
UNARY,
/**
* 函数调用成员访问等最强绑定例如 foo()obj.prop
*/
/** 函数调用、成员访问等最强绑定foo()、obj.prop */
CALL
}

View File

@ -8,39 +8,29 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
* {@code DeclarationStatementParser} 类负责解析变量声明语句是语句级解析器的一部分
* {@code DeclarationStatementParser} 负责解析变量声明语句节点
* <p>
* 本解析器支持以下两种形式的声明语法:
* 支持以下两种语法结构
* <pre>{@code
* declare myVar:Integer
* declare myVar:Integer = 42 + 3
* }</pre>
* 其中:
* <ul>
* <li>{@code myVar} 为变量名必须为标识符类型</li>
* <li>{@code Integer} 为类型标注必须为类型标记</li>
* <li>可选的初始化表达式由 {@link PrattExpressionParser} 解析</li>
* <li>每条声明语句必须以换行符{@code NEWLINE}结束</li>
* </ul>
* 若语法不满足上述结构将在解析过程中抛出异常
* 解析器能够识别多维数组类型 {@code int[]}, {@code string[][]}并支持可选初始化表达式
* <p>
* 每个声明语句均要求以换行符结尾语法不合法时会抛出异常
* </p>
*/
public class DeclarationStatementParser implements StatementParser {
/**
* 解析一条 {@code declare} 声明语句并返回对应的抽象语法树节点 {@link DeclarationNode}
* 解析一条 {@code declare} 语句生成对应的抽象语法树节点 {@link DeclarationNode}
* <p>
* 解析流程如下:
* <ol>
* <li>匹配关键字 {@code declare}</li>
* <li>读取变量名称标识符类型</li>
* <li>读取类型标注在冒号后要求为 {@code TYPE} 类型</li>
* <li>若存在 {@code =}则继续解析其后的表达式作为初始化值</li>
* <li>最终必须匹配 {@code NEWLINE} 表示语句结束</li>
* </ol>
* 若遇到非法语法结构将触发异常并中断解析过程
* 支持类型标注和可选初始化表达式类型部分自动拼接数组维度 int[][]
* </p>
*
* @param ctx 当前语法解析上下文包含词法流错误信息等
* @return 返回一个 {@link DeclarationNode} 节点表示解析完成的声明语法结构
* @param ctx 当前语法解析上下文包含词法流错误信息等
* @return {@link DeclarationNode} 表示声明语句结构
* @throws RuntimeException 语法不合法时抛出
*/
@Override
public DeclarationNode parse(ParserContext ctx) {
@ -61,11 +51,19 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expect(":");
// 获取变量类型类型标识符
String type = ctx.getTokens()
StringBuilder type = new StringBuilder(
ctx.getTokens()
.expectType(TokenType.TYPE)
.getLexeme();
.getLexeme()
);
// 可选的初始化表达式若存在 "="则解析等号右侧表达式
// 消费多维数组类型后缀 "[]"
while (ctx.getTokens().match("[")) {
ctx.getTokens().expectType(TokenType.RBRACKET); // 必须配对
type.append("[]"); // 类型名称拼接 [] int[][]
}
// 可选初始化表达式=号右侧
ExpressionNode init = null;
if (ctx.getTokens().match("=")) {
init = new PrattExpressionParser().parse(ctx);
@ -75,6 +73,6 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expectType(TokenType.NEWLINE);
// 返回构建好的声明语法树节点
return new DeclarationNode(name, type, init, new NodeContext(line, column, file));
return new DeclarationNode(name, type.toString(), init, new NodeContext(line, column, file));
}
}

View File

@ -51,7 +51,7 @@ public class ExpressionStatementParser implements StatementParser {
int column = ts.peek().getCol();
String file = ctx.getSourceName();
// 赋值语句: IDENTIFIER = expr
// 简单形式: IDENTIFIER = expr
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
String varName = ts.next().getLexeme();
ts.expect("=");
@ -60,9 +60,23 @@ public class ExpressionStatementParser implements StatementParser {
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
}
// 普通表达式语句
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
// 尝试解析更通用的左值形式支持下标: <expr> = <expr>
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
if ("=".equals(ts.peek().getLexeme())) {
ts.next(); // consume '='
ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(expr, new NodeContext(line, column, file));
// 根据左值类型构造具体赋值节点
if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
} else {
throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
}
}
// 普通表达式语句
ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
}
}

View File

@ -12,68 +12,64 @@ import java.util.Map;
/**
* {@code AnalyzerRegistry} 是语义分析器的注册与分发中心
* <p>
* 它负责根据 AST 节点的类型查找并返回相应的 {@link StatementAnalyzer} {@link ExpressionAnalyzer} 实例
* 同时支持注册自定义分析器并在未找到对应表达式分析器时提供默认兜底处理器
* 该类维护了 AST 节点类型与相应 {@link StatementAnalyzer}
* {@link ExpressionAnalyzer} 实例的映射关系调用者可以通过节点类型注册自定义的分析器
* 并在后续通过节点对象高效获取对应分析器实现语义分析分发
* </p>
* <p>
* 对于表达式分析器的获取{@link #getExpressionAnalyzer(ExpressionNode)}
* 支持最近父类匹配查找机制若找不到节点的精确类型分析器则向上递归查找已注册的最近父类类型分析器
* 若依然未找到则自动 fallback 到默认的 {@link UnsupportedExpressionAnalyzer}确保分析流程健壮性
* </p>
* <p>
* 主要职责:
* <ul>
* <li>支持注册语句和表达式节点类型对应的分析器</li>
* <li>在语义分析阶段根据 AST 节点动态查找对应的分析器</li>
* <li>为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer}</li>
* <li>不为语句提供默认兜底分析器未注册类型将返回 {@code null}</li>
* </ul>
*/
public class AnalyzerRegistry {
/** Statement 节点类型 → 对应语义分析器映射表 */
private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
/** Expression 节点类型 → 对应语义分析器映射表 */
private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
/** 默认兜底表达式分析器,用于处理未注册的表达式类型 */
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported =
new UnsupportedExpressionAnalyzer<>();
// ========================= 注册方法 =========================
/**
* Statement 节点类型 对应语义分析器映射表
*/
private final Map<Class<? extends StatementNode>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
/**
* 注册一个 {@link StatementAnalyzer} 实例用于处理指定类型的语句节点
*
* @param cls 要注册的语句节点类型Class 对象
* @param analyzer 与该类型匹配的分析器实例
* @param <S> {@link StatementNode} 的具体子类
* Expression 节点类型 对应语义分析器映射表
*/
public <S extends StatementNode> void registerStatementAnalyzer(
Class<S> cls,
StatementAnalyzer<S> analyzer
) {
stmtAnalyzers.put(cls, analyzer);
private final Map<Class<? extends ExpressionNode>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
/**
* 默认表达式兜底分析器用于未注册或不能识别的表达式类型
*/
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported = new UnsupportedExpressionAnalyzer<>();
/**
* 注册 Statement 类型的语义分析器
*
* @param clazz AST 语句节点类型class对象例如 {@code IfStatementNode.class}
* @param analyzer 针对该节点类型的语义分析器实例
* @param <S> 语句节点类型参数必须是 {@link StatementNode} 的子类
*/
public <S extends StatementNode> void registerStatementAnalyzer(Class<S> clazz, StatementAnalyzer<S> analyzer) {
stmtAnalyzers.put(clazz, analyzer);
}
/**
* 注册一个 {@link ExpressionAnalyzer} 实例用于处理指定类型的表达式节点
* 注册 Expression 类型的语义分析器
*
* @param cls 要注册的表达式节点类型Class 对象
* @param analyzer 与该类型匹配的分析器实例
* @param <E> {@link ExpressionNode} 的具体子类
* @param clazz AST 表达式节点类型class对象例如 {@code BinaryExprNode.class}
* @param analyzer 针对该节点类型的语义分析器实例
* @param <E> 表达式节点类型参数必须是 {@link ExpressionNode} 子类
*/
public <E extends ExpressionNode> void registerExpressionAnalyzer(
Class<E> cls,
ExpressionAnalyzer<E> analyzer
) {
exprAnalyzers.put(cls, analyzer);
public <E extends ExpressionNode> void registerExpressionAnalyzer(Class<E> clazz, ExpressionAnalyzer<E> analyzer) {
exprAnalyzers.put(clazz, analyzer);
}
// ========================= 获取方法 =========================
/**
* 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}
* 获取指定语句节点对象的分析器实例
* <p>
* 若节点类型未注册返回 {@code null}
* 只支持精确类型匹配即仅当注册过该节点 class 时才返回分析器否则返回 null
* </p>
*
* @param stmt 要分析的语句节点实例
* @param <S> 语句类型推断自参数
* @return 与该节点类型对应的分析器若未注册则为 {@code null}
* @param stmt 语句节点对象
* @param <S> 节点类型需为 {@link StatementNode} 子类
* @return 匹配的 {@link StatementAnalyzer}若未注册则返回 null
*/
@SuppressWarnings("unchecked")
public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) {
@ -81,17 +77,34 @@ public class AnalyzerRegistry {
}
/**
* 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}
* 获取指定表达式节点对象的分析器实例
* <p>
* 若节点类型未注册返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}
* 首先尝试节点类型的精确匹配若未注册向上递归查找其最近的已注册父类类型分析器
* 若依然未命中则返回默认的 {@link UnsupportedExpressionAnalyzer}保证分析器始终有返回值
* </p>
*
* @param expr 要分析的表达式节点实例
* @param <E> 表达式类型推断自参数
* @return 与该节点类型对应的分析器或默认兜底分析器
* @param expr 表达式节点对象
* @param <E> 节点类型需为 {@link ExpressionNode} 子类
* @return 匹配的 {@link ExpressionAnalyzer} 实例若未注册则返回兜底分析器
*/
@SuppressWarnings("unchecked")
public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) {
return (ExpressionAnalyzer<E>)
exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
Class<?> cls = expr.getClass();
// 精确匹配
ExpressionAnalyzer<?> analyzer = exprAnalyzers.get(cls);
if (analyzer != null) {
return (ExpressionAnalyzer<E>) analyzer;
}
// 向上遍历父类尝试匹配
Class<?> current = cls.getSuperclass();
while (current != null && ExpressionNode.class.isAssignableFrom(current)) {
analyzer = exprAnalyzers.get(current);
if (analyzer != null) {
return (ExpressionAnalyzer<E>) analyzer;
}
current = current.getSuperclass();
}
// fallback
return (ExpressionAnalyzer<E>) defaultUnsupported;
}
}

View File

@ -0,0 +1,63 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.List;
/**
* {@code ArrayLiteralAnalyzer} 用于分析数组字面量表达式ArrayLiteralNode
* <p>
* 主要负责
* <ul>
* <li>推断数组字面量的元素类型生成对应的 {@link ArrayType}</li>
* <li>检查所有元素类型是否一致不一致时报错并降级为 {@code int[]}</li>
* <li>若数组为空默认类型为 {@code int[]}并产生相应语义错误</li>
* </ul>
* </p>
*/
public class ArrayLiteralAnalyzer implements ExpressionAnalyzer<ArrayLiteralNode> {
/**
* 分析数组字面量表达式推断类型并检查元素类型一致性
*
* @param ctx 全局/当前语义分析上下文
* @param mi 所属模块信息
* @param fn 当前分析的函数节点
* @param locals 当前作用域符号表
* @param expr 当前数组字面量节点
* @return 推断出的数组类型若类型冲突或无法推断则为 {@code int[]}
*/
@Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, ArrayLiteralNode expr) {
List<ExpressionNode> elems = expr.elements();
if (elems.isEmpty()) {
ctx.getErrors().add(new SemanticError(expr, "空数组字面量的类型无法推断,已默认为 int[]"));
return new ArrayType(BuiltinType.INT);
}
// 以第一个元素为基准
Type first = ctx.getRegistry()
.getExpressionAnalyzer(elems.getFirst())
.analyze(ctx, mi, fn, locals, elems.getFirst());
for (int i = 1; i < elems.size(); i++) {
ExpressionNode e = elems.get(i);
Type t = ctx.getRegistry()
.getExpressionAnalyzer(e)
.analyze(ctx, mi, fn, locals, e);
if (!t.equals(first)) {
ctx.getErrors().add(new SemanticError(e, "数组元素类型不一致: 期望 " + first.name() + ",实际 " + t.name()));
return new ArrayType(BuiltinType.INT);
}
}
return new ArrayType(first);
}
}

View File

@ -0,0 +1,59 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* {@code IndexExpressionAnalyzer} 负责下标访问表达式 {@code array[index]}的类型推断和检查
* <p>
* 语义规则
* <ol>
* <li>先分析 array 的类型必须为 {@link ArrayType}</li>
* <li>再分析 index 的类型必须为数值类型</li>
* <li>表达式结果类型为 array 的元素类型 array 非数组类型报错并返回 int 类型兜底</li>
* </ol>
*/
public class IndexExpressionAnalyzer implements ExpressionAnalyzer<IndexExpressionNode> {
/**
* 分析并类型检查下标访问表达式
*
* @param ctx 当前语义分析上下文
* @param mi 当前模块信息
* @param fn 当前函数节点
* @param locals 当前作用域符号表
* @param node 要分析的下标访问表达式节点
* @return array 合法则返回元素类型否则兜底为 int 类型
*/
@Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexExpressionNode node) {
// 先分析被下标访问的数组表达式
Type arrType = ctx.getRegistry()
.getExpressionAnalyzer(node.array())
.analyze(ctx, mi, fn, locals, node.array());
if (arrType instanceof ArrayType(Type elementType)) {
// 再分析下标表达式
Type idxType = ctx.getRegistry()
.getExpressionAnalyzer(node.index())
.analyze(ctx, mi, fn, locals, node.index());
if (!idxType.isNumeric()) {
ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
}
// array[index] 的类型是数组元素类型
return elementType;
}
ctx.getErrors().add(new SemanticError(node, "仅数组类型支持下标访问"));
return BuiltinType.INT;
}
}

View File

@ -0,0 +1,72 @@
package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode;
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* {@code IndexAssignmentAnalyzer} 用于分析和类型检查数组下标赋值语句
* <p>
* 主要职责
* <ul>
* <li>检查左侧是否为数组类型</li>
* <li>检查下标是否为数值类型</li>
* <li>检查右值被赋值表达式类型是否与数组元素类型兼容</li>
* <li>如有类型不符将语义错误写入 {@link Context#getErrors()}</li>
* </ul>
*/
public class IndexAssignmentAnalyzer implements StatementAnalyzer<IndexAssignmentNode> {
/**
* 分析并类型检查数组下标赋值语句 {@code arr[i] = v}
* <ul>
* <li>左侧必须是数组类型</li>
* <li>下标必须为数值类型</li>
* <li>右侧赋值类型需与数组元素类型一致</li>
* <li>任何不合法情况均会产生 {@link SemanticError}</li>
* </ul>
*
* @param ctx 当前语义分析上下文
* @param mi 当前模块信息
* @param fn 所属函数节点
* @param locals 局部符号表
* @param node 赋值语句 AST 节点
*/
@Override
public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexAssignmentNode node) {
IndexExpressionNode target = node.target();
// 分析左侧类型必须为数组类型
Type arrT = ctx.getRegistry()
.getExpressionAnalyzer(target.array())
.analyze(ctx, mi, fn, locals, target.array());
if (!(arrT instanceof ArrayType(Type elementType))) {
ctx.getErrors().add(new SemanticError(node, "左侧不是数组,无法进行下标赋值"));
return;
}
// 分析下标类型必须为数值类型
Type idxT = ctx.getRegistry()
.getExpressionAnalyzer(target.index())
.analyze(ctx, mi, fn, locals, target.index());
if (!idxT.isNumeric()) {
ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
}
// 分析右值类型必须与元素类型一致
Type rhsT = ctx.getRegistry()
.getExpressionAnalyzer(node.value())
.analyze(ctx, mi, fn, locals, node.value());
if (!rhsT.equals(elementType)) {
ctx.getErrors().add(new SemanticError(node,
"数组元素赋值类型不匹配: 期望 " + elementType.name() + ",实际 " + rhsT.name()));
}
}
}

View File

@ -57,6 +57,11 @@ public final class AnalyzerRegistrar {
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
registry.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
registry.registerExpressionAnalyzer(IndexExpressionNode.class,new IndexExpressionAnalyzer()); // 关键行
registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
// ---------- 注册一元表达式分析器 ----------
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());

View File

@ -2,33 +2,45 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.List;
import java.util.Map;
/**
* {@code Context} 表示语义分析阶段的共享上下文环境
* {@code Context} 表示语义分析阶段的全局上下文环境
* <p>
* 它贯穿整个语义分析流程用于维护并提供以下核心服务:
* 该类贯穿整个语义分析流程集中管理以下内容
* <ul>
* <li>模块信息管理: 包含所有已加载模块源模块与内置模块</li>
* <li>错误收集: 集中存储语义分析期间产生的 {@link SemanticError}</li>
* <li>日志控制: 支持按需输出详细调试日志</li>
* <li>分析器调度: 通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发</li>
* <li>模块信息管理所有已加载模块包括源模块和内置模块</li>
* <li>错误收集集中存储语义分析期间产生的 {@link SemanticError}</li>
* <li>日志输出控制可选支持调试信息</li>
* <li>分析器调度通过 {@link AnalyzerRegistry} 分发对应分析器</li>
* </ul>
* <p>
* 提供便捷的 getter 方法和类型解析工具方法
* </p>
*/
public class Context {
/** 模块表: 模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */
/**
* 模块表模块名 {@link ModuleInfo}用于模块查找与跨模块引用
*/
private final Map<String, ModuleInfo> modules;
/** 错误列表: 语义分析过程中收集的所有 {@link SemanticError} */
/**
* 错误列表语义分析过程中收集的所有 {@link SemanticError}
*/
private final List<SemanticError> errors;
/** 日志开关: 若为 true将启用 {@link #log(String)} 输出日志信息 */
/**
* 日志开关若为 true将启用 {@link #log(String)} 输出日志信息
*/
private final boolean verbose;
/** 语义分析器注册表: 用于按节点类型动态调度分析器 */
/**
* 语义分析器注册表用于按节点类型动态调度分析器
*/
private final AnalyzerRegistry registry;
/**
@ -54,13 +66,17 @@ public class Context {
/**
* 获取所有模块信息映射表
*
* @return 模块名 模块信息 {@link ModuleInfo} 的映射
* @return 模块名到模块信息{@link ModuleInfo}的映射表
*/
public Map<String, ModuleInfo> getModules() {
return modules;
}
/** @return 模块信息(快捷方式) */
/**
* 模块信息 getter快捷方式
*
* @return 模块名到模块信息{@link ModuleInfo}的映射表
*/
public Map<String, ModuleInfo> modules() {
return modules;
}
@ -76,7 +92,11 @@ public class Context {
return errors;
}
/** @return 错误列表(快捷方式) */
/**
* 错误列表 getter快捷方式
*
* @return 错误列表
*/
public List<SemanticError> errors() {
return errors;
}
@ -92,7 +112,11 @@ public class Context {
return registry;
}
/** @return 注册表(快捷方式) */
/**
* 注册表 getter快捷方式
*
* @return {@link AnalyzerRegistry} 实例
*/
public AnalyzerRegistry registry() {
return registry;
}
@ -113,15 +137,26 @@ public class Context {
// ------------------ 工具函数 ------------------
/**
* 将类型名称字符串解析为对应的内置 {@link Type} 实例
* 将类型名称字符串解析为对应的类型实例支持多维数组后缀
* <p>
* 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在则返回对应类型
* 否则返回 {@code null}调用方可据此决定是否降级处理
* 例如"int" int 类型"int[][]" 二维整型数组类型
* </p>
*
* @param name 类型名称 "int", "float", "void", "string"
* @return 匹配的 {@link Type}若无匹配项则返回 {@code null}
* @param name 类型名称支持 "[]" 数组后缀
* @return 对应的 {@link Type} 实例若无法识别返回 null
*/
public Type parseType(String name) {
return BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
int dims = 0;
while (name.endsWith("[]")) {
name = name.substring(0, name.length() - 2);
dims++;
}
Type base = BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
if (base == null) return null;
Type t = base;
for (int i = 0; i < dims; i++) {
t = new ArrayType(t);
}
return t;
}
}

View File

@ -0,0 +1,73 @@
package org.jcnc.snow.compiler.semantic.type;
import java.util.Objects;
/**
* {@code ArrayType} 表示数组类型每个数组类型包含其元素类型
* <p>
* 例如int[]string[] 等均可用本类表示内部通过 {@link #elementType()} 字段保存元素类型
* </p>
*/
public record ArrayType(
/*
数组元素的类型
*/
Type elementType
) implements Type {
/**
* 判断当前数组类型能否与另一类型兼容主要用于类型检查
* <p>
* 只有当 {@code other} 也是 ArrayType且元素类型兼容时返回 true
* </p>
*
* @param other 需判断的类型
* @return 类型兼容性结果
*/
@Override
public boolean isCompatible(Type other) {
if (!(other instanceof ArrayType(Type type))) return false;
return elementType.isCompatible(type);
}
/**
* 数组类型不是数值类型直接调用父接口默认实现
*
* @return 总为 false
*/
@Override
public boolean isNumeric() {
return Type.super.isNumeric();
}
/**
* 判断两个 ArrayType 是否等价元素类型完全一致即视为等价
*
* @param o 比较对象
* @return 是否等价
*/
@Override
public boolean equals(Object o) {
return o instanceof ArrayType(Type type) && Objects.equals(elementType, type);
}
/**
* 返回数组类型的字符串描述 "int[]"
*
* @return 类型名称
*/
@Override
public String toString() {
return name();
}
/**
* 获取数组类型的名称描述 "int[]"
*
* @return 类型名称
*/
@Override
public String name() {
return elementType.name() + "[]";
}
}

View File

@ -47,4 +47,6 @@ public interface Type {
int ia = order.indexOf(a), ib = order.indexOf(b);
return order.get(Math.max(ia, ib));
}
/** 类型名字符串(如 int、double[] */
default String name() { return toString(); }
}

View File

@ -5,59 +5,287 @@ import org.jcnc.snow.vm.module.CallStack;
import org.jcnc.snow.vm.module.LocalVariableStore;
import org.jcnc.snow.vm.module.OperandStack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The {@code RPushCommand} class implements the {@link Command} interface and represents the
* reference push instruction ({@code R_PUSH}) in the virtual machine.
* The {@code RPushCommand} class implements the {@link Command} interface
* and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine.
*
* <p>
* This instruction pushes a reference object, such as a String literal, onto the operand stack.
* This instruction pushes a reference-type value onto the operand stack.
* The input is parsed from the textual instruction form, which can represent:
* <ul>
* <li>String literals</li>
* <li>Array literals (e.g., {@code [1, 2, 3]}), including nested arrays</li>
* </ul>
* </p>
*
* <p>Instruction format: {@code R_PUSH <literal>}</p>
* <ul>
* <li>{@code <literal>}: The reference value (e.g., string) to be pushed onto the stack.
* If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.</li>
* </ul>
*
* <p>Behavior:</p>
* <ul>
* <li>Checks that the instruction has at least one parameter after the operator.</li>
* <li>Concatenates all parameters after {@code R_PUSH} into a single string (separated by spaces).</li>
* <li>Pushes the resulting string as a reference object onto the operand stack.</li>
* <li>Increments the program counter to the next instruction.</li>
* <li>Throws an {@code IllegalStateException} if the instruction is missing required parameters.</li>
* </ul>
* <p>
* For array literals, a nested list structure is constructed. In this implementation,
* array literals are pushed as <b>mutable</b> {@link java.util.ArrayList} structures,
* so that subsequent system calls such as {@code ARR_SET} can modify elements in-place.
* </p>
*/
public final class RPushCommand implements Command {
public class RPushCommand implements Command {
/**
* Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal)
* onto the operand stack.
* Executes the R_PUSH command.
*
* @param parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"),
* {@code parts[1..]} are the parts of the literal to be concatenated and pushed.
* @param pc The current program counter value, indicating the instruction address being executed.
* @param stack The operand stack manager. The literal will be pushed onto this stack.
* @param lvs The local variable store. (Not used in this instruction.)
* @param cs The call stack manager. (Not used in this instruction.)
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
* @throws IllegalStateException if the instruction is missing required parameters.
* @param parts The parts of the instruction, where {@code parts[1..n]} are concatenated as the literal.
* @param pc The current program counter.
* @param stack The operand stack where the result will be pushed.
* @param local The local variable store (unused in this instruction).
* @param callStack The call stack (unused in this instruction).
* @return The new program counter (typically {@code pc+1}).
* @throws IllegalStateException if no literal parameter is provided.
*/
@Override
public int execute(String[] parts, int pc,
OperandStack stack,
LocalVariableStore lvs,
CallStack cs) {
public int execute(String[] parts, int pc, OperandStack stack, LocalVariableStore local, CallStack callStack) {
if (parts.length < 2)
throw new IllegalStateException("R_PUSH missing parameter");
// Join all arguments into a complete literal string
StringBuilder sb = new StringBuilder();
for (int i = 1; i < parts.length; i++) {
if (i > 1) sb.append(' ');
sb.append(parts[i]);
}
stack.push(sb.toString());
String literal = sb.toString().trim();
// Check if this is an array literal
if (literal.startsWith("[") && literal.endsWith("]")) {
Object parsed = parseValue(new Cursor(literal));
if (!(parsed instanceof List<?> list)) {
// Should not happen in theory; safety fallback
stack.push(parsed);
} else {
// Push a deep-mutable copy so ARR_SET can modify elements in-place
stack.push(deepMutable(list));
}
} else {
// Regular string, push as-is
stack.push(literal);
}
return pc + 1;
}
/**
* A simple string cursor, supporting index increment and character reading, for use by the parser.
*/
static class Cursor {
final String s;
int i;
/**
* Constructs a new {@code Cursor} for the given string.
*
* @param s The string to parse.
*/
Cursor(String s) {
this.s = s;
this.i = 0;
}
/**
* Advances the cursor by one character.
*/
void skip() {
i++;
}
/**
* @return {@code true} if the cursor has reached the end of the string.
*/
boolean end() {
return i >= s.length();
}
/**
* Gets the character at the current cursor position.
*
* @return current character
* @throws StringIndexOutOfBoundsException if at end of string
*/
char ch() {
return s.charAt(i);
}
}
/**
* Parses a value from the input string at the current cursor position.
* This can be an array literal, a quoted string, or a simple atom (number, word).
*
* @param c The cursor for parsing.
* @return The parsed value (could be List, String, Number).
*/
Object parseValue(Cursor c) {
skipWs(c);
if (c.end()) return "";
char ch = c.ch();
if (ch == '[') return parseArray(c);
if (ch == '\"') return parseQuoted(c);
return parseAtom(c);
}
/**
* Skips whitespace characters in the input string.
*
* @param c The cursor to advance.
*/
private static void skipWs(Cursor c) {
while (!c.end()) {
char ch = c.ch();
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') c.skip();
else break;
}
}
/**
* Parses an array literal from the input, including nested arrays.
*
* @param c The cursor (positioned at '[' at entry).
* @return A List representing the parsed array.
*/
private Object parseArray(Cursor c) {
// assumes current char is '['
c.skip(); // skip '['
List<Object> out = new ArrayList<>();
skipWs(c);
while (!c.end()) {
if (c.ch() == ']') {
c.skip(); // skip ']'
break;
}
Object v = parseValue(c);
out.add(v);
skipWs(c);
if (!c.end() && c.ch() == ',') {
c.skip();
skipWs(c);
}
}
return out;
}
/**
* Parses a quoted string literal, handling escape characters.
*
* @param c The cursor (positioned at '"' at entry).
* @return The parsed string value.
*/
private static String parseQuoted(Cursor c) {
// assumes current char is '"'
c.skip(); // skip opening quote
StringBuilder sb = new StringBuilder();
while (!c.end()) {
char ch = c.ch();
c.skip();
if (ch == '\\') {
if (c.end()) break;
char esc = c.ch();
c.skip();
switch (esc) {
case 'n' -> sb.append('\n');
case 'r' -> sb.append('\r');
case 't' -> sb.append('\t');
case '\"' -> sb.append('\"');
case '\\' -> sb.append('\\');
default -> sb.append(esc);
}
} else if (ch == '\"') {
break;
} else {
sb.append(ch);
}
}
return sb.toString();
}
/**
* Parses an atom (number, hexadecimal, binary, or plain string token).
*
* @param c The cursor.
* @return An Integer, Double, or String, depending on the content.
*/
private static Object parseAtom(Cursor c) {
StringBuilder sb = new StringBuilder();
while (!c.end()) {
char ch = c.ch();
if (ch == ',' || ch == ']' || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') break;
sb.append(ch);
c.skip();
}
String token = sb.toString();
// try number
try {
if (token.startsWith("0x") || token.startsWith("0X")) {
return Integer.parseInt(token.substring(2), 16);
}
if (token.startsWith("0b") || token.startsWith("0B")) {
return Integer.parseInt(token.substring(2), 2);
}
if (token.contains(".")) {
return Double.parseDouble(token);
}
return Integer.parseInt(token);
} catch (NumberFormatException e) {
// fallback as string
return token;
}
}
// ---------------------- helpers for immutability/mutability ----------------------
/**
* Recursively creates an unmodifiable copy of a list, with all nested lists also unmodifiable.
*
* @param l The list to make unmodifiable.
* @return An unmodifiable deep copy of the list.
*/
List<?> deepUnmodifiable(List<?> l) {
List<Object> out = new ArrayList<>(l.size());
for (Object v : l) out.add(deepUnmodifiableObject(v));
return Collections.unmodifiableList(out);
}
/**
* Helper method for {@link #deepUnmodifiable(List)}. Recursively processes each element.
*
* @param v The object to process.
* @return Unmodifiable list if input is a list, otherwise the value itself.
*/
Object deepUnmodifiableObject(Object v) {
if (v instanceof List<?> l) {
return deepUnmodifiable(l);
}
return v;
}
/**
* Create a deep mutable copy of a nested List structure, preserving element values.
* Nested lists are turned into {@link java.util.ArrayList} so they can be modified by ARR_SET.
*
* @param l The source list.
* @return Deep mutable copy of the list.
*/
private static java.util.List<?> deepMutable(java.util.List<?> l) {
java.util.List<Object> out = new java.util.ArrayList<>(l.size());
for (Object v : l) out.add(deepMutableObject(v));
return out;
}
/**
* Helper method for {@link #deepMutable(List)}. Recursively processes each element.
*
* @param v The object to process.
* @return Mutable list if input is a list, otherwise the value itself.
*/
private static Object deepMutableObject(Object v) {
if (v instanceof java.util.List<?> l) {
return deepMutable(l);
}
return v;
}
}

View File

@ -46,6 +46,97 @@ import static java.nio.file.StandardOpenOption.*;
*/
public class SyscallCommand implements Command {
/**
* 根据传入的文件打开标志构造 NIO {@link OpenOption} 集合
* <p>
* 本方法负责将底层虚拟机传递的 flags 整数型位域转换为 Java NIO 标准的文件打开选项集合
* 以支持文件读创建截断追加等多种访问场景
* 常用于 SYSCALL OPEN 子命令
* </p>
*
* @param flags 文件打开模式标志各标志可组合使用具体含义请参见虚拟机文档
* @return 转换后的 OpenOption 集合可直接用于 FileChannel.open NIO 方法
*/
private static Set<OpenOption> flagsToOptions(int flags) {
Set<OpenOption> opts = new HashSet<>();
// 如果有写入标志则添加WRITE否则默认为READ
if ((flags & 0x1) != 0) opts.add(WRITE);
else opts.add(READ);
// 如果包含创建标志允许创建文件
if ((flags & 0x40) != 0) opts.add(CREATE);
// 包含截断标志打开时清空内容
if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
// 包含追加标志文件写入时追加到末尾
if ((flags & 0x400) != 0) opts.add(APPEND);
return opts;
}
/**
* 捕获所有异常并统一处理操作数栈压入 -1 代表本次系统调用失败
* <p>
* 本方法是全局错误屏障任何命令异常都会转换为虚拟机通用的失败信号
* 保证上层调用逻辑不会被异常打断实际应用中可拓展错误码机制
* </p>
*
* @param stack 操作数栈将失败信号写入此栈
* @param e 抛出的异常对象可在调试时输出日志
*/
private static void pushErr(OperandStack stack, Exception e) {
stack.push(-1);
System.err.println("Syscall exception: " + e);
}
/**
* 控制台输出通用方法支持基本类型字节数组任意数组对象等
* <p>
* 该方法用于 SYSCALL PRINT/PRINTLN将任意类型对象转为易读字符串输出到标准输出流
* 字节数组自动按 UTF-8 解码其它原生数组按格式化字符串输出
* </p>
*
* @param obj 待输出的内容可以为任何类型如基本类型byte[]数组对象等
* @param newline 是否自动换行如果为 true则在输出后换行否则直接输出
*/
private static void output(Object obj, boolean newline) {
String str;
if (obj == null) {
str = "null";
} else if (obj instanceof byte[] bytes) {
// 字节数组作为文本输出
str = new String(bytes);
} else if (obj.getClass().isArray()) {
// 其它数组格式化输出
str = arrayToString(obj);
} else {
str = obj.toString();
}
if (newline) System.out.println(str);
else System.out.print(str);
}
/**
* 将各种原生数组和对象数组转换为可读字符串便于控制台输出和调试
* <p>
* 本方法针对 intlongdoublefloatshortcharbyteboolean 等所有原生数组类型
* 以及对象数组都能正确格式化统一输出格式风格避免显示为类型 hashCode
* 若为不支持的类型返回通用提示字符串
* </p>
*
* @param array 任意原生数组或对象数组
* @return 该数组的可读字符串表示
*/
private static String arrayToString(Object array) {
if (array instanceof int[] a) return Arrays.toString(a);
if (array instanceof long[] a) return Arrays.toString(a);
if (array instanceof double[] a) return Arrays.toString(a);
if (array instanceof float[] a) return Arrays.toString(a);
if (array instanceof short[] a) return Arrays.toString(a);
if (array instanceof char[] a) return Arrays.toString(a);
if (array instanceof byte[] a) return Arrays.toString(a);
if (array instanceof boolean[] a) return Arrays.toString(a);
if (array instanceof Object[] a) return Arrays.deepToString(a);
return "Unsupported array";
}
/**
* 分发并执行 SYSCALL 子命令根据子命令类型从操作数栈取出参数操作底层资源并将结果压回栈顶
*
@ -226,6 +317,105 @@ public class SyscallCommand implements Command {
sel.close();
}
// 数组元素访问arr[idx] 保留所有类型精度byte/short/int/long/float/double/boolean/string/ref
case "ARR_GET" -> {
/*
执行数组下标访问操作 arr[idx]并将对应元素以真实类型压入操作数栈
<ul>
<li>支持 List 与任意原生数组类型int[]double[] </li>
<li>idx 参数支持 Number/String 类型自动转 int</li>
<li>下标越界将抛出异常非数组类型将报错</li>
<li>返回结果保持类型精度byte/short/int/long/float/double/boolean/string/object;</li>
<li>boolean 元素以 1/0 压栈string/引用直接压栈</li>
</ul>
异常与出错行为
<ul>
<li>索引类型非法目标非数组/列表将抛 IllegalArgumentException</li>
<li>索引越界将抛 IndexOutOfBoundsException</li>
</ul>
*/
Object idxObj = stack.pop();
Object arrObj = stack.pop();
int idx;
if (idxObj instanceof Number n) idx = n.intValue();
else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
else throw new IllegalArgumentException("ARR_GET: invalid index type " + idxObj);
Object elem;
if (arrObj instanceof java.util.List<?> list) {
if (idx < 0 || idx >= list.size())
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + list.size() + ")");
elem = list.get(idx);
} else if (arrObj != null && arrObj.getClass().isArray()) {
int len = java.lang.reflect.Array.getLength(arrObj);
if (idx < 0 || idx >= len)
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
elem = java.lang.reflect.Array.get(arrObj, idx);
} else {
throw new IllegalArgumentException("ARR_GET: not an array/list: " + arrObj);
}
// === 按真实类型压栈byte/short/int/long/float/double/boolean/string/ref===
if (elem instanceof Number n) {
if (elem instanceof Double) {
stack.push(n.doubleValue());
} else if (elem instanceof Float) {
stack.push(n.floatValue());
} else if (elem instanceof Long) {
stack.push(n.longValue());
} else if (elem instanceof Integer) {
stack.push(n.intValue());
} else if (elem instanceof Short) {
stack.push(n.shortValue());
} else if (elem instanceof Byte) {
stack.push(n.byteValue());
} else {
stack.push(n.intValue()); // 兜底
}
} else if (elem instanceof Boolean b) {
stack.push(b ? 1 : 0);
} else {
// string 或其它引用类型直接返回
stack.push(elem);
}
}
case "ARR_SET" -> {
/*
arr[idx] = value
支持 List 和所有原生数组类型int[], double[], ...
参数顺序栈顶 valueidxarr
不返回值成功/失败由异常控制
*/
Object value = stack.pop();
Object idxObj = stack.pop();
Object arrObj = stack.pop();
int idx;
if (idxObj instanceof Number n) idx = n.intValue();
else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
else throw new IllegalArgumentException("ARR_SET: invalid index type " + idxObj);
if (arrObj instanceof java.util.List<?> list) {
// 必须是可变 List
@SuppressWarnings("unchecked")
java.util.List<Object> mlist = (java.util.List<Object>) list;
if (idx < 0 || idx >= mlist.size())
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
mlist.set(idx, value);
} else if (arrObj != null && arrObj.getClass().isArray()) {
int len = java.lang.reflect.Array.getLength(arrObj);
if (idx < 0 || idx >= len)
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
java.lang.reflect.Array.set(arrObj, idx, value);
} else {
throw new IllegalArgumentException("ARR_SET: not an array/list: " + arrObj);
}
// 操作成功push 0 作为 ok 信号不需要返回时可省略
stack.push(0);
}
// 控制台输出
case "PRINT" -> {
Object dataObj = stack.pop();
@ -246,95 +436,4 @@ public class SyscallCommand implements Command {
return pc + 1;
}
/**
* 根据传入的文件打开标志构造 NIO {@link OpenOption} 集合
* <p>
* 本方法负责将底层虚拟机传递的 flags 整数型位域转换为 Java NIO 标准的文件打开选项集合
* 以支持文件读创建截断追加等多种访问场景
* 常用于 SYSCALL OPEN 子命令
* </p>
*
* @param flags 文件打开模式标志各标志可组合使用具体含义请参见虚拟机文档
* @return 转换后的 OpenOption 集合可直接用于 FileChannel.open NIO 方法
*/
private static Set<OpenOption> flagsToOptions(int flags) {
Set<OpenOption> opts = new HashSet<>();
// 如果有写入标志则添加WRITE否则默认为READ
if ((flags & 0x1) != 0) opts.add(WRITE);
else opts.add(READ);
// 如果包含创建标志允许创建文件
if ((flags & 0x40) != 0) opts.add(CREATE);
// 包含截断标志打开时清空内容
if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
// 包含追加标志文件写入时追加到末尾
if ((flags & 0x400) != 0) opts.add(APPEND);
return opts;
}
/**
* 捕获所有异常并统一处理操作数栈压入 -1 代表本次系统调用失败
* <p>
* 本方法是全局错误屏障任何命令异常都会转换为虚拟机通用的失败信号
* 保证上层调用逻辑不会被异常打断实际应用中可拓展错误码机制
* </p>
*
* @param stack 操作数栈将失败信号写入此栈
* @param e 抛出的异常对象可在调试时输出日志
*/
private static void pushErr(OperandStack stack, Exception e) {
stack.push(-1);
System.err.println("Syscall exception: " + e);
}
/**
* 控制台输出通用方法支持基本类型字节数组任意数组对象等
* <p>
* 该方法用于 SYSCALL PRINT/PRINTLN将任意类型对象转为易读字符串输出到标准输出流
* 字节数组自动按 UTF-8 解码其它原生数组按格式化字符串输出
* </p>
*
* @param obj 待输出的内容可以为任何类型如基本类型byte[]数组对象等
* @param newline 是否自动换行如果为 true则在输出后换行否则直接输出
*/
private static void output(Object obj, boolean newline) {
String str;
if (obj == null) {
str = "null";
} else if (obj instanceof byte[] bytes) {
// 字节数组作为文本输出
str = new String(bytes);
} else if (obj.getClass().isArray()) {
// 其它数组格式化输出
str = arrayToString(obj);
} else {
str = obj.toString();
}
if (newline) System.out.println(str);
else System.out.print(str);
}
/**
* 将各种原生数组和对象数组转换为可读字符串便于控制台输出和调试
* <p>
* 本方法针对 intlongdoublefloatshortcharbyteboolean 等所有原生数组类型
* 以及对象数组都能正确格式化统一输出格式风格避免显示为类型 hashCode
* 若为不支持的类型返回通用提示字符串
* </p>
*
* @param array 任意原生数组或对象数组
* @return 该数组的可读字符串表示
*/
private static String arrayToString(Object array) {
if (array instanceof int[] a) return Arrays.toString(a);
if (array instanceof long[] a) return Arrays.toString(a);
if (array instanceof double[] a) return Arrays.toString(a);
if (array instanceof float[] a) return Arrays.toString(a);
if (array instanceof short[] a) return Arrays.toString(a);
if (array instanceof char[] a) return Arrays.toString(a);
if (array instanceof byte[] a) return Arrays.toString(a);
if (array instanceof boolean[] a) return Arrays.toString(a);
if (array instanceof Object[] a) return Arrays.deepToString(a);
return "Unsupported array";
}
}

View File

@ -1,6 +1,5 @@
package org.jcnc.snow.vm.engine;
import org.jcnc.snow.common.Mode;
import org.jcnc.snow.vm.execution.CommandExecutionHandler;
import org.jcnc.snow.vm.module.*;
@ -51,8 +50,6 @@ public class VirtualMachineEngine {
/**
* Builds a VM engine with fresh runtime structures.
*
* @param vmMode execution mode (DEBUG / RUN)
*/
public VirtualMachineEngine() {
this.operandStack = new OperandStack();