diff --git a/.run/Demo19.run.xml b/.run/Demo19.run.xml
new file mode 100644
index 0000000..0896751
--- /dev/null
+++ b/.run/Demo19.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo20.run.xml b/.run/Demo20.run.xml
new file mode 100644
index 0000000..bdf690b
--- /dev/null
+++ b/.run/Demo20.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo21.run.xml b/.run/Demo21.run.xml
new file mode 100644
index 0000000..2076c8c
--- /dev/null
+++ b/.run/Demo21.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/测试.run.xml b/.run/测试.run.xml
index 097c514..dc6fb89 100644
--- a/.run/测试.run.xml
+++ b/.run/测试.run.xml
@@ -9,7 +9,10 @@
+
+
+
diff --git a/playground/Demo/Demo18/Main.snow b/playground/Demo/Demo18/Main.snow
index e41610c..107f100 100644
--- a/playground/Demo/Demo18/Main.snow
+++ b/playground/Demo/Demo18/Main.snow
@@ -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
diff --git a/playground/Demo/Demo19/Main.snow b/playground/Demo/Demo19/Main.snow
new file mode 100644
index 0000000..5bcef84
--- /dev/null
+++ b/playground/Demo/Demo19/Main.snow
@@ -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
diff --git a/playground/Demo/Demo19/OS.snow b/playground/Demo/Demo19/OS.snow
new file mode 100644
index 0000000..6026d43
--- /dev/null
+++ b/playground/Demo/Demo19/OS.snow
@@ -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
\ No newline at end of file
diff --git a/playground/Demo/Demo20/Main.snow b/playground/Demo/Demo20/Main.snow
new file mode 100644
index 0000000..c3ba2e4
--- /dev/null
+++ b/playground/Demo/Demo20/Main.snow
@@ -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
diff --git a/playground/Demo/Demo20/OS.snow b/playground/Demo/Demo20/OS.snow
new file mode 100644
index 0000000..6026d43
--- /dev/null
+++ b/playground/Demo/Demo20/OS.snow
@@ -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
\ No newline at end of file
diff --git a/playground/Demo/Demo21/Main.snow b/playground/Demo/Demo21/Main.snow
new file mode 100644
index 0000000..f3b11ce
--- /dev/null
+++ b/playground/Demo/Demo21/Main.snow
@@ -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
diff --git a/playground/Demo/Demo21/OS.snow b/playground/Demo/Demo21/OS.snow
new file mode 100644
index 0000000..6026d43
--- /dev/null
+++ b/playground/Demo/Demo21/OS.snow
@@ -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
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
index 0f7c9c8..04dfaa1 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
@@ -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;
/**
- * CallGenerator - 将 IR {@code CallInstruction} 生成 VM 指令
- *
+ * {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
*
- * 本类负责将中间表示(IR)中的函数调用指令 {@link CallInstruction} 转换为虚拟机(VM)指令。
- * 支持普通函数调用和特殊的 syscall 指令转换。
+ * 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
*
*
- *
- * 能力说明:
*
- * 支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量,直接降级为 {@code SYSCALL <SUBCMD>} 指令。
- * 对普通函数,完成参数加载、调用、返回值保存等指令生成。
+ * syscall: 支持字符串常量与寄存器到子命令的绑定与解析
+ * 数组下标访问: 支持所有主流基础类型 “__index_b/s/i/l/f/d/r”
+ * 普通函数: 支持自动推断返回值类型与槽位类型
*
- *
*/
public class CallGenerator implements InstructionGenerator {
/**
- * <虚拟寄存器 id, 对应的字符串常量>
- * 用于记录虚拟寄存器与其绑定字符串常量的映射。由 {@link LoadConstGenerator} 在编译期间填充。
+ * 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
+ *
+ * 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
*/
- private static final Map STRING_CONST_POOL = new HashMap<>();
+ private static final Map STRING_CONST_POOL = new ConcurrentHashMap<>();
/**
- * 注册字符串常量到虚拟寄存器
- * 供 {@link LoadConstGenerator} 在加载字符串常量时调用。
+ * 注册一个字符串常量,绑定到虚拟寄存器 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 supportedClass() {
@@ -56,11 +57,11 @@ public class CallGenerator implements InstructionGenerator {
}
/**
- * 生成 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 {
VMProgramBuilder out,
Map slotMap,
String currentFn) {
+ String fn = ins.getFunctionName();
- /* ========== 特殊处理 syscall 调用 ========== */
- if ("syscall".equals(ins.getFunctionName()) ||
- ins.getFunctionName().endsWith(".syscall")) {
-
- List 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),无返回值。
+ *
+ * 调用栈压栈顺序为:arr (引用类型, R),idx (整型, I),value (元素类型, T)。
+ * 执行 SYSCALL ARR_SET 指令以完成数组赋值操作。
+ *
+ *
+ * @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 slotMap,
+ char valType) {
+ List 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 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);
+ }
+
+ /**
+ * 生成一维/多维数组的下标访问指令(支持类型分派)。
+ *
+ * 本方法用于将 IR 层的数组下标访问表达式(如 arr[idx]),生成对应的 VM 指令序列。
+ * 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问。
+ *
+ *
+ *
+ * 1. 依次加载数组参数(arr)和下标参数(idx)到操作数栈
+ * 2. 发出 ARR_GET 系统调用指令(SYSCALL ARR_GET),通过 VM 访问对应元素
+ * 3. 根据元素声明类型,将结果写入目标槽位,支持类型分派(B/S/I/L/F/D/R)
+ * 4. 若参数或返回寄存器缺失,则抛出异常提示
+ *
+ *
+ * @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 slotMap, char retType) {
+ String fn = ins.getFunctionName();
+ List 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 指令分支逻辑。
+ *
+ * 解析 syscall 子命令
+ * 压栈剩余参数
+ * 发出 SYSCALL 指令
+ * 若有返回值则保存至目标槽位
+ *
+ *
+ * @param ins 调用指令
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param fn 当前函数名
+ */
+ private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map slotMap, String fn) {
+ List 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');
+ }
+ }
+
+ /**
+ * 生成普通函数调用指令:
+ *
+ * 推断返回值类型
+ * 压栈所有参数
+ * 生成 CALL 指令
+ * 保存返回值(若非 void)
+ *
+ *
+ * @param ins 调用指令
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param fn 当前函数名
+ */
+ private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map 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);
}
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
index a24f6d9..84d2e7e 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
@@ -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;
/**
- * LoadConstGenerator - 将 IR {@code LoadConstInstruction} 生成 VM 指令
+ * LoadConstGenerator - Generates VM instructions from IR {@code LoadConstInstruction}
*
*
- * 本类负责将 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.
+ *
+ *
+ *
+ * Fix: When the constant is an array (List), type information is preserved in R_PUSH payload:
+ *
+ * Float is output with f suffix (e.g., 0.1f);
+ * Long is output with L suffix (e.g., 123L);
+ * Double/Integer are output in their default format (e.g., 1.0, 42);
+ * Supports recursive serialization of nested arrays.
+ *
+ * This prevents float values from being misinterpreted as double on the VM side,
+ * and avoids Double→Float cast exceptions in later F_STORE operations.
*
*/
public class LoadConstGenerator implements InstructionGenerator {
/**
- * 指定本生成器支持的 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 supportedClass() {
@@ -29,12 +101,16 @@ public class LoadConstGenerator implements InstructionGenerator
+ * 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.
+ *
*
- * @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 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("未知的常量类型: "
- + (value != null ? value.getClass() : null));
+ 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);
}
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java
index 977c85e..2b5f8c8 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java
@@ -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());
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
index eab2d75..47bb4a4 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
@@ -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;
/**
- * ExpressionBuilder - 表达式 → IR 构建器
- *
+ * {@code ExpressionBuilder} 表达式 → IR 构建器。
*
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。
- * 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。
- *
- *
- *
- * 主要功能:
+ * 支持字面量、标识符、二元表达式、一元表达式、函数调用、数组下标等多种类型表达式。
*
- * 将表达式节点映射为虚拟寄存器
- * 为每种表达式类型生成对应 IR 指令
- * 支持表达式嵌套的递归构建
- * 支持写入指定目标寄存器,避免冗余的 move 指令
+ * 将表达式节点映射为虚拟寄存器
+ * 为每种表达式类型生成对应 IR 指令
+ * 支持表达式嵌套的递归构建
+ * 支持写入指定目标寄存器,避免冗余的 move 指令
+ * 支持 IndexExpressionNode 的编译期折叠(arr[2]),并在运行时降级为 __index_i/__index_r
*
- *
*/
public record ExpressionBuilder(IRContext ctx) {
- /* ───────────────── 顶层入口 ───────────────── */
-
/**
- * 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。
- *
- *
- * 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。
- *
- *
- * @param expr 任意 AST 表达式节点
- * @return 存储该表达式结果的虚拟寄存器
- * @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
+ * 构建表达式,返回结果寄存器。
*/
public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) {
@@ -53,22 +40,22 @@ public record ExpressionBuilder(IRContext ctx) {
// 字符串字面量,例如 "abc"
case StringLiteralNode s -> buildStringLiteral(s.value());
// 布尔字面量,例如 true / false
- case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
+ case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
// 标识符(变量名),如 a、b
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)
case BinaryExpressionNode bin -> buildBinary(bin);
// 函数/方法调用表达式
- case CallExpressionNode call -> buildCall(call);
+ case CallExpressionNode call -> buildCall(call);
// 一元表达式(如 -a, !a)
- case UnaryExpressionNode un -> buildUnary(un);
-
+ 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}。
*
- * 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。
+ * 按表达式类型分派处理,包括:
+ *
+ * 字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器
+ * 变量标识符:查表获取源寄存器,并 move 到目标寄存器
+ * 二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器
+ * 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
+ *
*
*
- * @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(
- ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
+ // 数字字面量:生成 loadConst 指令,将数值常量写入目标寄存器
+ case NumberLiteralNode n -> InstructionFactory.loadConstInto(
+ ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
- // 字符串字面量:生成 loadConst 指令写入目标寄存器
- case StringLiteralNode s ->
- InstructionFactory.loadConstInto(
- ctx, dest, new IRConstant(s.value()));
+ // 字符串字面量:生成 loadConst 指令,将字符串常量写入目标寄存器
+ case StringLiteralNode s -> InstructionFactory.loadConstInto(
+ ctx, dest, new IRConstant(s.value()));
- // 布尔字面量:转换为 int 1/0,生成 loadConst
- case BoolLiteralNode b ->
- InstructionFactory.loadConstInto(
- ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
+ // 布尔字面量:转换为 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)。
+ *
+ * 若数组和下标均为编译期常量,则直接进行常量折叠(直接返回目标常量寄存器);
+ * 否则:
+ *
+ * 若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;
+ * 最后一级用 __index_b/s/i/l/f/d/r,按声明类型智能分派。
+ *
+ *
+ *
+ *
+ * @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 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;
+ }
+
+ /**
+ * 构建中间层下标访问表达式(返回引用)。
+ *
+ * 用于多维数组的链式下标访问(如 arr[1][2]),保证中间结果是“可被再次下标”的引用。
+ *
+ * 若数组和下标均为编译期常量,则直接常量折叠,返回目标常量寄存器;
+ * 否则,递归处理 array,生成“引用”指令(__index_r)。
+ *
+ *
+ *
+ * @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 argv = new ArrayList<>();
+ argv.add(arrReg);
+ argv.add(idxReg);
+
+ // 6. 生成 __index_r 调用指令(总是返回引用)
+ ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
+
+ return dest;
+ }
+
+ /**
+ * 常量折叠工具(支持嵌套数组)。
+ *
+ * 尝试将表达式节点 {@code expr} 折叠为常量值,用于编译期计算/优化。
+ *
+ * 数字字面量:返回 int 或 double。
+ * 字符串字面量:返回字符串。
+ * 布尔字面量:返回 1(true)或 0(false)。
+ * 数组字面量:递归折叠所有元素为 List,如果有一项不能折叠则返回 null。
+ * 标识符:尝试从作用域查找编译期常量值。
+ * 其它类型:无法折叠,返回 null。
+ *
+ *
+ *
+ * @param expr 需要折叠的表达式节点
+ * @return 编译期常量值(支持 int、double、String、List),否则返回 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 返回 1,false 返回 0
+ if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
+
+ // 数组字面量:递归折叠所有元素
+ if (expr instanceof ArrayLiteralNode arr) {
+ List 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;
+ }
+
/**
* 一元表达式构建
@@ -137,62 +337,70 @@ public record ExpressionBuilder(IRContext ctx) {
*/
private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
// 递归生成操作数的寄存器
- IRVirtualRegister src = build(un.operand());
+ 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;
}
+
/**
- * 构建函数或方法调用表达式。模块内未限定调用会自动补全当前模块名。
+ * 构建函数或方法调用表达式。
+ *
+ * 支持普通函数调用(foo(a, b))与成员方法调用(obj.method(a, b))。
+ *
+ * 首先递归生成所有参数的虚拟寄存器列表。
+ * 根据 callee 类型区分成员访问或直接标识符调用,并规范化方法名(如加前缀)。
+ * 为返回值分配新寄存器,生成 Call 指令。
+ *
+ *
*
- * @param call AST 调用表达式节点
- * @return 存储调用结果的虚拟寄存器
+ * @param call 函数/方法调用表达式节点
+ * @return 存放调用结果的虚拟寄存器
*/
private IRVirtualRegister buildCall(CallExpressionNode call) {
- // 递归生成所有参数(实参)对应的寄存器
+ // 1. 递归生成所有参数的寄存器
List 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;
}
+
/**
* 二元表达式构建,结果存储到新寄存器。
*
@@ -207,44 +415,54 @@ 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);
}
/**
* 二元表达式构建,结果直接写入目标寄存器(用于赋值左值等优化场景)。
*
- * @param bin 二元表达式节点
+ * @param bin 二元表达式节点
* @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;
+ }
+
+ /**
+ * 构建支持嵌套的数组常量表达式。
+ *
+ * 遍历并递归处理数组字面量的所有元素:
+ *
+ * 数字字面量:根据内容生成 int 或 double 常量
+ * 字符串字面量:直接存储字符串内容
+ * 布尔字面量:转换为 1(true)或 0(false)存储
+ * 数组字面量:递归构建,允许多层嵌套,最终生成嵌套的 List
+ *
+ * 若包含非常量元素,则抛出异常。
+ *
+ *
+ * @param arr 数组字面量节点
+ * @return 封装所有常量元素(支持嵌套 List)的 {@link IRConstant}
+ * @throws IllegalStateException 如果数组中存在非常量元素
+ */
+
+ private IRConstant buildArrayConstant(ArrayLiteralNode arr) {
+ List 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));
+ }
+
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java
index 209b4fa..774c883 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java
@@ -7,109 +7,143 @@ import java.util.HashMap;
import java.util.Map;
/**
- * IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。
- *
- * 主要功能包括:
+ * {@code IRBuilderScope} 用于管理单个函数作用域内的变量名与虚拟寄存器的映射关系。
+ *
+ * 主要职责:
*
- * 维护在当前作用域中已声明变量的寄存器分配信息;
- * 支持将已有虚拟寄存器与变量名重新绑定;
- * 根据变量名查找对应的虚拟寄存器实例或类型。
+ * 维护当前作用域内所有已声明变量的寄存器和类型信息
+ * 支持变量与虚拟寄存器的重新绑定与查找
+ * 支持变量的类型信息记录与查询
+ * 支持变量的编译期常量值记录与查询(便于常量折叠等优化)
*
*/
final class IRBuilderScope {
/**
- * 存储变量名到对应 IRVirtualRegister 的映射。
- * 变量名为键,虚拟寄存器对象为值,用于查找和更新。
+ * 变量名到虚拟寄存器的映射表。
+ * 用于跟踪所有声明和分配的变量。
*/
private final Map vars = new HashMap<>();
/**
- * 存储变量名到对应类型的映射。
- *
- * 变量名为键,变量类型为值,用于变量类型提升。
+ * 变量名到类型字符串的映射表。
+ * 用于类型分析与推断。
*/
private final Map varTypes = new HashMap<>();
-
/**
- * 当前作用域所绑定的 IRFunction 对象,用于申请新的虚拟寄存器。
+ * 变量名到编译期常量值的映射表。
+ * 用于常量折叠优化(如 int、string、数组等常量)。
+ */
+ private final Map 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 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);
+ }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
index b735213..e95d132 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
@@ -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 指令序列的构建器。
+ * StatementBuilder —— AST 语句节点 ({@link StatementNode}) 到 IR 指令序列的构建器。
*
- * 负责将各种语句节点(循环、分支、表达式、赋值、声明、返回等)生成对应的 IR 指令,并管理作用域和控制流标签。
+ * 负责将各类语句(如循环、分支、表达式、赋值、声明、返回、break、continue 等)
+ * 转换为对应的 IR 指令,并自动管理作用域、虚拟寄存器分配以及控制流标签(如 break/continue 目标)。
*
+ *
+ *
+ * 支持多种语句类型的分发与转换。
+ * 与 {@link ExpressionBuilder} 协作完成表达式相关 IR 生成。
+ * 负责控制流跳转(分支、循环)的标签分配与维护。
+ * 在变量赋值和声明时自动常量折叠和登记。
+ *
+ *
+ * @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 breakTargets = new ArrayDeque<>();
+
/**
- * continue 目标标签栈(保存每层循环的 step 起始标签)
+ * continue 目标标签栈(保存每层循环的 step 起始标签,用于 continue 跳转)
*/
private final ArrayDeque 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 指令序列。
+ *
* 根据节点类型分发到对应的处理方法。
+ * 支持:循环、分支、表达式语句、赋值、声明、返回、break、continue。
+ * 不支持的语句类型会抛出异常。
+ *
*
- * @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 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 stmts) {
for (StatementNode s : stmts) build(s);
}
/**
- * 构建循环语句(for/while)。
- * 处理流程: 初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。
+ * 构建循环语句(for/while),包括初始语句、条件判断、循环体、更新语句、跳回判断等 IR 指令。
+ *
+ * 自动维护 break/continue 的目标标签。
+ *
*
- * @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 分支(可选)。
+ *
+ * 包括:条件判断、then 分支、else 分支(可选)、结束标签等。
+ *
*
- * @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。
+ *
+ * 对于二元比较表达式,会选择恰当的比较指令。
+ * 其他类型表达式,等价于 (cond == 0) 时跳转。
+ *
*
- * @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)。
+ *
+ * 该方法会根据表达式类型进行常量求值:
+ *
+ * 如果是数字字面量,解析为 Integer 或 Double。
+ * 如果是字符串字面量,直接返回字符串内容。
+ * 如果是布尔字面量,返回 1(true)或 0(false)。
+ * 如果是数组字面量,会递归所有元素全部是常量时,返回包含常量元素的 List。
+ * 如果是标识符节点,尝试从作用域查找是否登记为常量,如果找到则返回。
+ * 其余类型或无法确定常量时,返回 null。
+ *
+ * 用于全局/局部常量传播优化与类型推断。
+ *
+ * @param expr 待折叠的表达式节点,允许为 null
+ * @return 如果可折叠则返回其常量值(如 Integer、Double、String、List),否则返回 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 返回 1,false 返回 0
+ if (expr instanceof BoolLiteralNode b) {
+ return b.getValue() ? 1 : 0;
+ }
+
+ // 5. 数组字面量:递归所有元素做常量折叠,只有全为常量时才返回 List
+ if (expr instanceof ArrayLiteralNode arr) {
+ java.util.List 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;
+ }
+
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java
index 2085fef..3810de8 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/SymbolTokenScanner.java
@@ -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);
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
index 554d48c..07b6561 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
@@ -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,
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ArrayLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ArrayLiteralNode.java
new file mode 100644
index 0000000..a2a8e7e
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ArrayLiteralNode.java
@@ -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} 表示数组字面量表达式节点。
+ *
+ * 例如:[1, 2, 3] 或 [[1, 2], [3, 4]] 这样的语法结构,均对应本节点类型。
+ *
+ *
+ *
+ * {@link #elements()} 保存所有元素表达式节点。
+ * {@link #context()} 表示该节点在源代码中的上下文信息(如位置、父节点等)。
+ *
+ */
+public record ArrayLiteralNode(
+ /*
+ 数组字面量中的所有元素表达式(按顺序)。
+ */
+ List elements,
+
+ /*
+ 节点的上下文信息(如源码位置等)。
+ */
+ NodeContext context
+) implements ExpressionNode {
+
+ /**
+ * 返回字符串形式,如 {@code Array[1, 2, 3]}。
+ *
+ * @return 表示该数组字面量节点的字符串
+ */
+ @Override
+ public String toString() {
+ return "Array" + elements;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/IndexAssignmentNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/IndexAssignmentNode.java
new file mode 100644
index 0000000..9dc8333
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/IndexAssignmentNode.java
@@ -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}。
+ *
+ * 对于多维数组,{@code target} 可以是嵌套的 {@link IndexExpressionNode}。
+ *
+ *
+ * @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;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/IndexExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/IndexExpressionNode.java
new file mode 100644
index 0000000..a33caa9
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/IndexExpressionNode.java
@@ -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]}。
+ *
+ * 支持多维数组嵌套(如 arr[i][j])。
+ *
+ *
+ * @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 + "]";
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/ArrayLiteralParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/ArrayLiteralParselet.java
new file mode 100644
index 0000000..6a9a483
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/ArrayLiteralParselet.java
@@ -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} 用于解析数组字面量表达式。
+ *
+ * 支持语法形如 {@code [a, b, c]} 或 {@code [ [1,2], [3,4] ]},
+ * 允许元素及逗号前后出现换行符,便于多行书写。
+ *
+ *
+ * 语法结构为:
+ *
[ (element (',' element)*)? ]
+ *
+ */
+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 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();
+ }
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/IndexParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/IndexParselet.java
new file mode 100644
index 0000000..a32e35c
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/IndexParselet.java
@@ -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} 负责解析数组或集合的下标访问表达式,语法结构为:
+ *
+ * primary '[' expr ']'
+ *
+ * 例如 {@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;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index df2ee36..f18813f 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -25,45 +25,80 @@ import java.util.Map;
*/
public class PrattExpressionParser implements ExpressionParser {
- /** 前缀解析器注册表(按 Token 类型名索引) */
+ /**
+ * 前缀解析器注册表(按 Token 类型名或词素索引)。
+ *
+ * 用于存储所有支持的前缀表达式解析器,例如字面量、变量、分组、数组、一元运算等。
+ * 支持通过 TokenType 的名称和特定词素(如 "(", "[")两种方式索引。
+ *
+ */
private static final Map prefixes = new HashMap<>();
- /** 中缀解析器注册表(按运算符词素索引) */
+
+ /**
+ * 中缀解析器注册表(按运算符词素索引)。
+ *
+ * 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。
+ * 仅通过词素索引(如 "+", "-", "(", "[" 等)。
+ *
+ */
private static final Map 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());
}
/**
- * 表达式解析统一入口。
- * 以最低优先级启动递归下降,适配任意表达式复杂度。
+ * 解析任意表达式的统一入口。
+ *
+ * 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。
+ *
*
- * @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 算法核心)。
*
- * 先根据当前 Token 类型查找前缀解析器进行初始解析,
- * 然后根据优先级不断递归处理中缀运算符和右侧表达式。
+ * 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。
+ * 2. 然后循环检测是否有更高优先级的中缀操作符,
+ * 若有则递归处理右侧表达式并组合为新的表达式节点。
+ *
+ *
+ * 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。
*
*
* @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 算法关键)。
+ *
+ * 用于决定当前是否需要递归处理更高优先级的中缀操作。
+ *
*
* @param ctx 当前解析上下文
- * @return 下一个中缀运算符的优先级序号;若无解析器则为 -1
+ * @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
index 61f4dab..e47a5aa 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
@@ -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
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
index 5cbcc40..5e0470b 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
@@ -8,39 +8,29 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
- * {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。
+ * {@code DeclarationStatementParser} 负责解析变量声明语句节点。
*
- * 本解析器支持以下两种形式的声明语法:
+ * 支持以下两种语法结构:
*
{@code
* declare myVar:Integer
* declare myVar:Integer = 42 + 3
* }
- * 其中:
- *
- * {@code myVar} 为变量名(必须为标识符类型);
- * {@code Integer} 为类型标注(必须为类型标记);
- * 可选的初始化表达式由 {@link PrattExpressionParser} 解析;
- * 每条声明语句必须以换行符({@code NEWLINE})结束。
- *
- * 若语法不满足上述结构,将在解析过程中抛出异常。
+ * 解析器能够识别多维数组类型(如 {@code int[]}, {@code string[][]}),并支持可选初始化表达式。
+ *
+ * 每个声明语句均要求以换行符结尾,语法不合法时会抛出异常。
+ *
*/
public class DeclarationStatementParser implements StatementParser {
/**
- * 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。
+ * 解析一条 {@code declare} 语句,生成对应的抽象语法树节点 {@link DeclarationNode}。
*
- * 解析流程如下:
- *
- * 匹配关键字 {@code declare};
- * 读取变量名称(标识符类型);
- * 读取类型标注(在冒号后,要求为 {@code TYPE} 类型);
- * 若存在 {@code =},则继续解析其后的表达式作为初始化值;
- * 最终必须匹配 {@code NEWLINE} 表示语句结束。
- *
- * 若遇到非法语法结构,将触发异常并中断解析过程。
+ * 支持类型标注和可选初始化表达式。类型部分自动拼接数组维度(如 int[][])。
+ *
*
- * @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()
- .expectType(TokenType.TYPE)
- .getLexeme();
+ StringBuilder type = new StringBuilder(
+ ctx.getTokens()
+ .expectType(TokenType.TYPE)
+ .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));
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
index 7e80851..955baa7 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
@@ -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 lhs = new PrattExpressionParser().parse(ctx);
+ if ("=".equals(ts.peek().getLexeme())) {
+ ts.next(); // consume '='
+ ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
+ ts.expectType(TokenType.NEWLINE);
+ // 根据左值类型构造具体赋值节点
+ 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);
+ }
+ }
// 普通表达式语句
- ExpressionNode expr = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
- return new ExpressionStatementNode(expr, new NodeContext(line, column, file));
+ return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java
index d61496c..5f0bc30 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java
@@ -12,68 +12,64 @@ import java.util.Map;
/**
* {@code AnalyzerRegistry} 是语义分析器的注册与分发中心。
*
- * 它负责根据 AST 节点的类型,查找并返回相应的 {@link StatementAnalyzer} 或 {@link ExpressionAnalyzer} 实例。
- * 同时支持注册自定义分析器,并在未找到对应表达式分析器时提供默认兜底处理器。
+ * 该类维护了 AST 节点类型与相应 {@link StatementAnalyzer}、
+ * {@link ExpressionAnalyzer} 实例的映射关系。调用者可以通过节点类型注册自定义的分析器,
+ * 并在后续通过节点对象高效获取对应分析器,实现语义分析分发。
+ *
+ *
+ * 对于表达式分析器的获取({@link #getExpressionAnalyzer(ExpressionNode)}),
+ * 支持“最近父类匹配”查找机制:若找不到节点的精确类型分析器,则向上递归查找已注册的最近父类类型分析器;
+ * 若依然未找到,则自动 fallback 到默认的 {@link UnsupportedExpressionAnalyzer},确保分析流程健壮性。
+ *
*
- * 主要职责:
- *
- * 支持注册语句和表达式节点类型对应的分析器;
- * 在语义分析阶段,根据 AST 节点动态查找对应的分析器;
- * 为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer};
- * 不为语句提供默认兜底分析器,未注册类型将返回 {@code null}。
- *
*/
public class AnalyzerRegistry {
- /** Statement 节点类型 → 对应语义分析器映射表 */
- private final Map, StatementAnalyzer>> stmtAnalyzers = new HashMap<>();
-
- /** Expression 节点类型 → 对应语义分析器映射表 */
- private final Map, ExpressionAnalyzer>> exprAnalyzers = new HashMap<>();
-
- /** 默认兜底表达式分析器,用于处理未注册的表达式类型 */
- private final ExpressionAnalyzer defaultUnsupported =
- new UnsupportedExpressionAnalyzer<>();
-
- // ========================= 注册方法 =========================
+ /**
+ * Statement 节点类型 → 对应语义分析器映射表
+ */
+ private final Map, StatementAnalyzer>> stmtAnalyzers = new HashMap<>();
/**
- * 注册一个 {@link StatementAnalyzer} 实例,用于处理指定类型的语句节点。
- *
- * @param cls 要注册的语句节点类型(Class 对象)
- * @param analyzer 与该类型匹配的分析器实例
- * @param {@link StatementNode} 的具体子类
+ * Expression 节点类型 → 对应语义分析器映射表
*/
- public void registerStatementAnalyzer(
- Class cls,
- StatementAnalyzer analyzer
- ) {
- stmtAnalyzers.put(cls, analyzer);
+ private final Map, ExpressionAnalyzer>> exprAnalyzers = new HashMap<>();
+
+ /**
+ * 默认表达式兜底分析器:用于未注册或不能识别的表达式类型
+ */
+ private final ExpressionAnalyzer defaultUnsupported = new UnsupportedExpressionAnalyzer<>();
+
+ /**
+ * 注册 Statement 类型的语义分析器。
+ *
+ * @param clazz AST 语句节点类型(class对象),例如 {@code IfStatementNode.class}
+ * @param analyzer 针对该节点类型的语义分析器实例
+ * @param 语句节点类型参数,必须是 {@link StatementNode} 的子类
+ */
+ public void registerStatementAnalyzer(Class clazz, StatementAnalyzer analyzer) {
+ stmtAnalyzers.put(clazz, analyzer);
}
/**
- * 注册一个 {@link ExpressionAnalyzer} 实例,用于处理指定类型的表达式节点。
+ * 注册 Expression 类型的语义分析器。
*
- * @param cls 要注册的表达式节点类型(Class 对象)
- * @param analyzer 与该类型匹配的分析器实例
- * @param {@link ExpressionNode} 的具体子类
+ * @param clazz AST 表达式节点类型(class对象),例如 {@code BinaryExprNode.class}
+ * @param analyzer 针对该节点类型的语义分析器实例
+ * @param 表达式节点类型参数,必须是 {@link ExpressionNode} 的子类
*/
- public void registerExpressionAnalyzer(
- Class cls,
- ExpressionAnalyzer analyzer
- ) {
- exprAnalyzers.put(cls, analyzer);
+ public void registerExpressionAnalyzer(Class clazz, ExpressionAnalyzer analyzer) {
+ exprAnalyzers.put(clazz, analyzer);
}
- // ========================= 获取方法 =========================
-
/**
- * 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}。
+ * 获取指定语句节点对象的分析器实例。
*
- * 若节点类型未注册,返回 {@code null}。
+ * 只支持“精确类型匹配”,即仅当注册过该节点 class 时才返回分析器,否则返回 null。
+ *
*
- * @param stmt 要分析的语句节点实例
- * @param 语句类型(推断自参数)
- * @return 与该节点类型对应的分析器,若未注册则为 {@code null}
+ * @param stmt 语句节点对象
+ * @param 节点类型,需为 {@link StatementNode} 子类
+ * @return 匹配的 {@link StatementAnalyzer},若未注册则返回 null
*/
@SuppressWarnings("unchecked")
public StatementAnalyzer getStatementAnalyzer(S stmt) {
@@ -81,17 +77,34 @@ public class AnalyzerRegistry {
}
/**
- * 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}。
+ * 获取指定表达式节点对象的分析器实例。
*
- * 若节点类型未注册,返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}。
+ * 首先尝试节点类型的精确匹配;若未注册,向上递归查找其最近的已注册父类类型分析器;
+ * 若依然未命中,则返回默认的 {@link UnsupportedExpressionAnalyzer},保证分析器始终有返回值。
+ *
*
- * @param expr 要分析的表达式节点实例
- * @param 表达式类型(推断自参数)
- * @return 与该节点类型对应的分析器,或默认兜底分析器
+ * @param expr 表达式节点对象
+ * @param 节点类型,需为 {@link ExpressionNode} 子类
+ * @return 匹配的 {@link ExpressionAnalyzer} 实例;若未注册,则返回兜底分析器
*/
@SuppressWarnings("unchecked")
public ExpressionAnalyzer getExpressionAnalyzer(E expr) {
- return (ExpressionAnalyzer)
- exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
+ Class> cls = expr.getClass();
+ // 精确匹配
+ ExpressionAnalyzer> analyzer = exprAnalyzers.get(cls);
+ if (analyzer != null) {
+ return (ExpressionAnalyzer) analyzer;
+ }
+ // 向上遍历父类尝试匹配
+ Class> current = cls.getSuperclass();
+ while (current != null && ExpressionNode.class.isAssignableFrom(current)) {
+ analyzer = exprAnalyzers.get(current);
+ if (analyzer != null) {
+ return (ExpressionAnalyzer) analyzer;
+ }
+ current = current.getSuperclass();
+ }
+ // fallback
+ return (ExpressionAnalyzer) defaultUnsupported;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java
new file mode 100644
index 0000000..752cc61
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java
@@ -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)。
+ *
+ * 主要负责:
+ *
+ * 推断数组字面量的元素类型,生成对应的 {@link ArrayType}。
+ * 检查所有元素类型是否一致,不一致时报错并降级为 {@code int[]}。
+ * 若数组为空,默认类型为 {@code int[]},并产生相应语义错误。
+ *
+ *
+ */
+public class ArrayLiteralAnalyzer implements ExpressionAnalyzer {
+ /**
+ * 分析数组字面量表达式,推断类型,并检查元素类型一致性。
+ *
+ * @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 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);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IndexExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IndexExpressionAnalyzer.java
new file mode 100644
index 0000000..a0a890e
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IndexExpressionAnalyzer.java
@@ -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]})的类型推断和检查。
+ *
+ * 语义规则:
+ *
+ * 先分析 array 的类型,必须为 {@link ArrayType}
+ * 再分析 index 的类型,必须为数值类型
+ * 表达式结果类型为 array 的元素类型;若 array 非数组类型,报错并返回 int 类型兜底
+ *
+ */
+public class IndexExpressionAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 分析并类型检查下标访问表达式。
+ *
+ * @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;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java
new file mode 100644
index 0000000..4990c79
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java
@@ -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} 用于分析和类型检查数组下标赋值语句。
+ *
+ * 主要职责:
+ *
+ * 检查左侧是否为数组类型
+ * 检查下标是否为数值类型
+ * 检查右值(被赋值表达式)类型是否与数组元素类型兼容
+ * 如有类型不符,将语义错误写入 {@link Context#getErrors()}
+ *
+ */
+public class IndexAssignmentAnalyzer implements StatementAnalyzer {
+
+ /**
+ * 分析并类型检查数组下标赋值语句,如 {@code arr[i] = v}。
+ *
+ * 左侧必须是数组类型
+ * 下标必须为数值类型
+ * 右侧赋值类型需与数组元素类型一致
+ * 任何不合法情况均会产生 {@link SemanticError}
+ *
+ *
+ * @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()));
+ }
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
index 0e5e318..f0b301a 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
@@ -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());
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
index 881f083..6a69b67 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
@@ -2,42 +2,54 @@ 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} 表示语义分析阶段的全局上下文环境。
*
- * 它贯穿整个语义分析流程,用于维护并提供以下核心服务:
+ * 该类贯穿整个语义分析流程,集中管理以下内容:
*
- * 模块信息管理: 包含所有已加载模块(源模块与内置模块);
- * 错误收集: 集中存储语义分析期间产生的 {@link SemanticError};
- * 日志控制: 支持按需输出详细调试日志;
- * 分析器调度: 通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发。
+ * 模块信息管理(所有已加载模块,包括源模块和内置模块);
+ * 错误收集(集中存储语义分析期间产生的 {@link SemanticError});
+ * 日志输出控制(可选,支持调试信息);
+ * 分析器调度(通过 {@link AnalyzerRegistry} 分发对应分析器);
*
+ *
+ * 提供便捷的 getter 方法和类型解析工具方法。
+ *
*/
public class Context {
- /** 模块表: 模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */
+ /**
+ * 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用。
+ */
private final Map modules;
- /** 错误列表: 语义分析过程中收集的所有 {@link SemanticError} */
+ /**
+ * 错误列表:语义分析过程中收集的所有 {@link SemanticError}。
+ */
private final List errors;
- /** 日志开关: 若为 true,将启用 {@link #log(String)} 输出日志信息 */
+ /**
+ * 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息。
+ */
private final boolean verbose;
- /** 语义分析器注册表: 用于按节点类型动态调度分析器 */
+ /**
+ * 语义分析器注册表:用于按节点类型动态调度分析器。
+ */
private final AnalyzerRegistry registry;
/**
* 构造语义分析上下文对象。
*
- * @param modules 已注册的模块信息集合
- * @param errors 错误收集器,分析器将所有语义错误写入此列表
- * @param verbose 是否启用调试日志输出
- * @param registry 分析器注册表,提供类型到分析器的映射与调度能力
+ * @param modules 已注册的模块信息集合
+ * @param errors 错误收集器,分析器将所有语义错误写入此列表
+ * @param verbose 是否启用调试日志输出
+ * @param registry 分析器注册表,提供类型到分析器的映射与调度能力
*/
public Context(Map modules,
List errors,
@@ -54,13 +66,17 @@ public class Context {
/**
* 获取所有模块信息映射表。
*
- * @return 模块名 → 模块信息 {@link ModuleInfo} 的映射
+ * @return 模块名到模块信息({@link ModuleInfo})的映射表
*/
public Map getModules() {
return modules;
}
- /** @return 模块信息(快捷方式) */
+ /**
+ * 模块信息 getter(快捷方式)。
+ *
+ * @return 模块名到模块信息({@link ModuleInfo})的映射表
+ */
public Map modules() {
return modules;
}
@@ -76,7 +92,11 @@ public class Context {
return errors;
}
- /** @return 错误列表(快捷方式) */
+ /**
+ * 错误列表 getter(快捷方式)。
+ *
+ * @return 错误列表
+ */
public List 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} 实例。
+ * 将类型名称字符串解析为对应的类型实例(支持多维数组后缀)。
*
- * 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在,则返回对应类型;
- * 否则返回 {@code null},调用方可据此决定是否降级处理。
+ * 例如,"int" → int 类型,"int[][]" → 二维整型数组类型。
+ *
*
- * @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;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java
new file mode 100644
index 0000000..9b7bd41
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java
@@ -0,0 +1,73 @@
+package org.jcnc.snow.compiler.semantic.type;
+
+import java.util.Objects;
+
+/**
+ * {@code ArrayType} 表示数组类型,每个数组类型包含其元素类型。
+ *
+ * 例如,int[]、string[] 等均可用本类表示。内部通过 {@link #elementType()} 字段保存元素类型。
+ *
+ */
+public record ArrayType(
+ /*
+ 数组元素的类型。
+ */
+ Type elementType
+) implements Type {
+
+ /**
+ * 判断当前数组类型能否与另一类型兼容(主要用于类型检查)。
+ *
+ * 只有当 {@code other} 也是 ArrayType,且元素类型兼容时返回 true。
+ *
+ *
+ * @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() + "[]";
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
index fc7798f..209e578 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
@@ -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(); }
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
index 1386018..6c12299 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
@@ -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.
*
*
- * 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:
+ *
+ * String literals
+ * Array literals (e.g., {@code [1, 2, 3]}), including nested arrays
+ *
*
*
- * Instruction format: {@code R_PUSH }
- *
- * {@code }: 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.
- *
- *
- * Behavior:
- *
- * Checks that the instruction has at least one parameter after the operator.
- * Concatenates all parameters after {@code R_PUSH} into a single string (separated by spaces).
- * Pushes the resulting string as a reference object onto the operand stack.
- * Increments the program counter to the next instruction.
- * Throws an {@code IllegalStateException} if the instruction is missing required parameters.
- *
+ *
+ * For array literals, a nested list structure is constructed. In this implementation,
+ * array literals are pushed as mutable {@link java.util.ArrayList} structures,
+ * so that subsequent system calls such as {@code ARR_SET} can modify elements in-place.
+ *
*/
-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 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 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 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;
+ }
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
index 90caf8b..80428fa 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
@@ -46,6 +46,97 @@ import static java.nio.file.StandardOpenOption.*;
*/
public class SyscallCommand implements Command {
+ /**
+ * 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
+ *
+ * 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
+ * 以支持文件读、写、创建、截断、追加等多种访问场景。
+ * 常用于 SYSCALL 的 OPEN 子命令。
+ *
+ *
+ * @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
+ * @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
+ */
+ private static Set flagsToOptions(int flags) {
+ Set 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 代表本次系统调用失败。
+ *
+ * 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
+ * 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
+ *
+ *
+ * @param stack 操作数栈,将失败信号写入此栈
+ * @param e 抛出的异常对象,可在调试时输出日志
+ */
+ private static void pushErr(OperandStack stack, Exception e) {
+ stack.push(-1);
+ System.err.println("Syscall exception: " + e);
+ }
+
+ /**
+ * 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
+ *
+ * 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
+ * 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
+ *
+ *
+ * @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);
+ }
+
+ /**
+ * 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
+ *
+ * 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
+ * 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
+ * 若为不支持的类型,返回通用提示字符串。
+ *
+ *
+ * @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],并将对应元素以真实类型压入操作数栈。
+
+ 支持 List 与任意原生数组类型(int[]、double[] 等);
+ idx 参数支持 Number/String 类型,自动转 int;
+ 下标越界将抛出异常,非数组类型将报错;
+ 返回结果保持类型精度:byte/short/int/long/float/double/boolean/string/object;
+ boolean 元素以 1/0 压栈,string/引用直接压栈;
+
+
+ 异常与出错行为:
+
+ 索引类型非法、目标非数组/列表,将抛 IllegalArgumentException;
+ 索引越界,将抛 IndexOutOfBoundsException;
+
+ */
+ 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[], ...)
+ 参数顺序:栈顶 value、idx、arr
+ 不返回值(成功/失败由异常控制)
+ */
+ 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 mlist = (java.util.List) 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} 集合。
- *
- * 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
- * 以支持文件读、写、创建、截断、追加等多种访问场景。
- * 常用于 SYSCALL 的 OPEN 子命令。
- *
- *
- * @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
- * @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
- */
- private static Set flagsToOptions(int flags) {
- Set 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 代表本次系统调用失败。
- *
- * 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
- * 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
- *
- *
- * @param stack 操作数栈,将失败信号写入此栈
- * @param e 抛出的异常对象,可在调试时输出日志
- */
- private static void pushErr(OperandStack stack, Exception e) {
- stack.push(-1);
- System.err.println("Syscall exception: " + e);
- }
-
- /**
- * 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
- *
- * 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
- * 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
- *
- *
- * @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);
- }
-
- /**
- * 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
- *
- * 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
- * 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
- * 若为不支持的类型,返回通用提示字符串。
- *
- *
- * @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";
- }
}
diff --git a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java
index 704822b..aa6d227 100644
--- a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java
+++ b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java
@@ -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();