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 15957a1..58d721d 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,42 +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; /** * {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。 *

- * 支持: - *

- *

- * 对于 syscall 子命令,支持常量字符串和字符串寄存器两种来源,并支持寄存器-字符串常量池注册机制。 + * 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。 *

+ * + * */ public class CallGenerator implements InstructionGenerator { /** * 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。 + *
+ * 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。 */ - private static final Map STRING_CONST_POOL = new HashMap<>(); + private static final Map STRING_CONST_POOL = new ConcurrentHashMap<>(); /** * 注册一个字符串常量,绑定到虚拟寄存器 id。 * - * @param regId 虚拟寄存器 id - * @param value 字符串常量 + * @param regId 虚拟寄存器 id + * @param value 字符串常量 */ public static void registerStringConst(int regId, String value) { STRING_CONST_POOL.put(regId, value); } /** - * 返回当前指令生成器支持的 IR 指令类型(即 {@link CallInstruction})。 + * 返回本生成器支持的 IR 指令类型。 * - * @return {@code CallInstruction.class} + * @return {@link CallInstruction} 类型的 Class 对象 */ @Override public Class supportedClass() { @@ -53,122 +57,234 @@ public class CallGenerator implements InstructionGenerator { } /** - * 生成 VM 指令序列,实现函数调用/特殊 syscall/数组索引等 IR 指令的转换。 + * 生成指定的调用指令,包括 syscall、数组下标、普通函数。 * - * @param ins 当前函数调用 IR 指令 - * @param out VM 指令输出构建器 - * @param slotMap IR 虚拟寄存器 → VM 槽位映射表 - * @param currentFn 当前函数名 + * @param ins IR 层的调用指令 + * @param out VM 程序构建器 + * @param slotMap 寄存器到槽位的映射表 + * @param currentFn 当前函数名 */ @Override public void generate(CallInstruction ins, 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) { // 来自寄存器的字符串 - String s = STRING_CONST_POOL.get(vr.id()); - if (s == null) - throw new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr); - subcmd = s.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 无返回值,直接返回 + // 特殊处理 syscall 调用 + if ("syscall".equals(fn) || fn.endsWith(".syscall")) { + generateSyscall(ins, out, slotMap, fn); + return; } - /* ========== 特殊处理内部索引函数:__index_i(arr, idx) ========== */ - if ("__index_i".equals(ins.getFunctionName())) { - // 加载参数(arr 为引用类型,idx 为整型) - if (ins.getArguments().size() != 2) { - throw new IllegalStateException("__index_i 需要两个参数(arr, idx)"); + // 一维数组整型下标访问 + if ("__index_i".equals(fn)) { + generateIndexInstruction(ins, out, slotMap, true); + return; + } + + // 多维数组返回引用的下标访问 + if ("__index_r".equals(fn)) { + generateIndexInstruction(ins, out, slotMap, false); + return; + } + + // 普通函数调用 + generateNormalCall(ins, out, slotMap, fn); + } + + // ========== 私有辅助方法 ========== + + /** + * 解析 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); } - IRVirtualRegister arr = (IRVirtualRegister) ins.getArguments().get(0); - IRVirtualRegister idx = (IRVirtualRegister) ins.getArguments().get(1); + 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) + ); + } + } - int arrSlot = slotMap.get(arr); - int idxSlot = slotMap.get(idx); + /** + * 加载一个参数到栈,支持指定默认类型。如果未设置类型则采用默认类型。 + * + * @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); + } - char arrT = out.getSlotType(arrSlot); - if (arrT == '\0') arrT = 'R'; // 默认为引用类型 - out.emit(OpHelper.opcode(arrT + "_LOAD") + " " + arrSlot); + /** + * 生成一维/多维数组的下标访问指令。 + * + * @param ins 调用指令 + * @param out VM 程序构建器 + * @param slotMap 寄存器到槽位映射 + * @param isInt 是否是一维整型下标 + */ + private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map slotMap, boolean isInt) { + String fn = ins.getFunctionName(); + List args = ins.getArguments(); + if (args.size() != 2) { + throw new IllegalStateException( + "[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args)); + } + // 加载数组参数 + loadArgument(out, slotMap, args.get(0), 'R', fn); + // 加载下标参数 + loadArgument(out, slotMap, args.get(1), 'I', fn); - char idxT = out.getSlotType(idxSlot); - if (idxT == '\0') idxT = 'I'; // 默认为整型 - out.emit(OpHelper.opcode(idxT + "_LOAD") + " " + idxSlot); + out.emit(VMOpCode.SYSCALL + " " + "ARR_GET"); - // 调用 SYSCALL ARR_GET,让 VM 取出数组元素并压回栈顶 - out.emit(VMOpCode.SYSCALL + " " + "ARR_GET"); - - // 取回返回值并保存(当前仅支持 int 元素) - int destSlot = slotMap.get(ins.getDest()); + // 保存返回值到目标寄存器 + 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)); + } + if (isInt) { out.emit(OpHelper.opcode("I_STORE") + " " + destSlot); out.setSlotType(destSlot, 'I'); - return; + } else { + out.emit(OpHelper.opcode("R_STORE") + " " + destSlot); + out.setSlotType(destSlot, 'R'); + } + } + + /** + * 生成 syscall 指令分支逻辑。 + *
    + *
  1. 解析 syscall 子命令
  2. + *
  3. 压栈剩余参数
  4. + *
  5. 发出 SYSCALL 指令
  6. + *
  7. 若有返回值则保存至目标槽位
  8. + *
+ * + * @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. 推断返回值类型(非 void 返回时用) ---------- - char retType = 'I'; // 默认为整型 - if (!ins.getArguments().isEmpty()) { - // 简化:根据第一个参数类型推断返回类型,或者通过全局表拿到返回类型 - String ret = GlobalFunctionTable.getReturnType(ins.getFunctionName()); - if (ret != null) { - retType = Character.toUpperCase(ret.charAt(0)); + // 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'); } + } - // ---------- 2. 压栈所有参数 ---------- + /** + * 生成普通函数调用指令: + *
    + *
  1. 推断返回值类型
  2. + *
  3. 压栈所有参数
  4. + *
  5. 生成 CALL 指令
  6. + *
  7. 保存返回值(若非 void)
  8. + *
+ * + * @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()) { - IRVirtualRegister vr = (IRVirtualRegister) arg; - int slotId = slotMap.get(vr); - char t = out.getSlotType(slotId); - if (t == '\0') t = 'I'; - out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId); + loadArgument(out, slotMap, arg, 'R', fn); } - // ---------- 3. 发出 CALL 指令 ---------- - out.emitCall(ins.getFunctionName(), ins.getArguments().size()); + // 3. 发出 CALL 指令 + out.emitCall(fn, ins.getArguments().size()); - // ---------- 3.5 如果为 void 返回直接结束 ---------- - if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) { + // 3.5 void 返回直接结束 + if ("void".equals(retTypeName)) { return; } - // ---------- 4. 保存返回值 ---------- - int destSlot = slotMap.get(ins.getDest()); + // 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/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java index 4a5d525..70e456f 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 @@ -12,7 +12,8 @@ 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; /** * {@code ExpressionBuilder} 表达式 → IR 构建器。 @@ -24,17 +25,13 @@ import java.util.*; *
  • 为每种表达式类型生成对应 IR 指令
  • *
  • 支持表达式嵌套的递归构建
  • *
  • 支持写入指定目标寄存器,避免冗余的 move 指令
  • - *
  • 支持 IndexExpressionNode 的编译期折叠(arr[2]),并自动降级为运行时调用 __index_i
  • + *
  • 支持 IndexExpressionNode 的编译期折叠(arr[2]),并在运行时降级为 __index_i/__index_r
  • * */ public record ExpressionBuilder(IRContext ctx) { /** - * 构建表达式,返回存储其结果的虚拟寄存器。 - * - * @param expr 要生成 IR 的表达式节点 - * @return 存储表达式值的虚拟寄存器 - * @throws IllegalStateException 不支持的表达式类型或未定义标识符 + * 构建表达式,返回结果寄存器。 */ public IRVirtualRegister build(ExpressionNode expr) { return switch (expr) { @@ -43,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()); @@ -74,7 +71,7 @@ public record ExpressionBuilder(IRContext ctx) { *
      *
    • 字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器
    • *
    • 变量标识符:查表获取源寄存器,并 move 到目标寄存器
    • - *
    • 二元表达式与下标表达式:递归生成子表达式结果,并写入目标寄存器
    • + *
    • 二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器
    • *
    • 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
    • *
    *

    @@ -85,22 +82,23 @@ public record ExpressionBuilder(IRContext ctx) { */ 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)); - case ArrayLiteralNode arr -> - InstructionFactory.loadConstInto(ctx, dest, buildArrayConstant(arr)); + // 布尔字面量:转换为 int 1/0,生成 loadConst 指令写入目标寄存器 + case BoolLiteralNode b -> InstructionFactory.loadConstInto( + ctx, dest, new IRConstant(b.getValue() ? 1 : 0)); + + // 数组字面量:生成数组常量并写入目标寄存器 + case ArrayLiteralNode arr -> InstructionFactory.loadConstInto( + ctx, dest, buildArrayConstant(arr)); + + // 变量标识符:查表获得源寄存器,move 到目标寄存器 case IdentifierNode id -> { IRVirtualRegister src = ctx.getScope().lookup(id.name()); if (src == null) @@ -108,12 +106,22 @@ public record ExpressionBuilder(IRContext ctx) { InstructionFactory.move(ctx, src, dest); } - // 二元表达式:递归生成并写入目标寄存器 + // 二元表达式:递归生成左右子表达式,并将结果写入目标寄存器 case BinaryExpressionNode bin -> buildBinaryInto(bin, dest); + + // 下标表达式:递归生成索引结果,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); @@ -121,80 +129,182 @@ public record ExpressionBuilder(IRContext ctx) { } } + /** - * 下标访问表达式处理。支持编译期常量折叠(数组和下标均为常量时直接求值), - * 否则生成运行时调用 __index_i(由 VM 降级为 ARR_GET)。 + * 构建下标访问表达式(IndexExpressionNode)。 + *
      + *
    • 若数组和下标均为编译期常量,则直接进行常量折叠(直接返回目标常量寄存器);
    • + *
    • 否则: + *
        + *
      • 若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;
      • + *
      • 最后一级用 __index_i 获取实际整型元素值。
      • + *
      + *
    • + *
    * - * @param node 下标访问表达式 - * @return 存储结果的虚拟寄存器 + * @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; } - IRVirtualRegister arrReg = build(node.array()); + + // 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()); - IRVirtualRegister dest = ctx.newRegister(); + + // 4. 创建目标寄存器 + IRVirtualRegister dest = ctx.newRegister(); + + // 5. 准备参数 List argv = new ArrayList<>(); argv.add(arrReg); argv.add(idxReg); - ctx.addInstruction(new CallInstruction(dest, "__index_i", argv)); + + // 6. 选择调用指令 + if (node.array() instanceof IndexExpressionNode) { + // 非最末层,下标取“引用” + ctx.addInstruction(new CallInstruction(dest, "__index_r", argv)); + } else { + // 最末层,下标取实际元素值 + ctx.addInstruction(new CallInstruction(dest, "__index_i", argv)); + } + return dest; } /** - * 尝试将表达式折叠为编译期常量(支持嵌套)。 - * 支持数字、字符串、布尔、数组、常量标识符。 + * 构建中间层下标访问表达式(返回引用)。 + *

    + * 用于多维数组的链式下标访问(如 arr[1][2]),保证中间结果是“可被再次下标”的引用。 + *

      + *
    • 若数组和下标均为编译期常量,则直接常量折叠,返回目标常量寄存器;
    • + *
    • 否则,递归处理 array,生成“引用”指令(__index_r)。
    • + *
    + *

    * - * @param expr 要折叠的表达式节点 - * @return 常量对象(如数字、字符串、List),否则返回 null + * @param node 下标访问表达式节点 + * @return 存放引用结果的虚拟寄存器 + */ + private 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); - } - return Integer.parseInt(s); + if (s.contains(".") || s.contains("e") || s.contains("E")) + return Double.parseDouble(s); // 带小数或科学计数法为 double + return Integer.parseInt(s); // 否则为 int } catch (NumberFormatException e) { - return null; + return null; // 无法解析为数字 } } - if (expr instanceof StringLiteralNode s) { - return s.value(); - } - if (expr instanceof BoolLiteralNode b) { - return b.getValue() ? 1 : 0; - } + + // 字符串字面量:直接返回字符串 + 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) { - java.util.List list = new java.util.ArrayList<>(); + List list = new ArrayList<>(); for (ExpressionNode e : arr.elements()) { Object v = tryFoldConst(e); - if (v == null) return null; + if (v == null) return null; // 有一项无法折叠则整体失败 list.add(v); } - return java.util.List.copyOf(list); + return List.copyOf(list); } + + // 标识符:尝试查找作用域中的常量值 if (expr instanceof IdentifierNode id) { Object v = null; try { v = ctx.getScope().getConstValue(id.name()); - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + // 查不到常量或异常都视为无法折叠 + } return v; } + + // 其它类型:不支持折叠,返回 null return null; } + /** * 一元表达式构建 * @@ -207,62 +317,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; } + /** * 二元表达式构建,结果存储到新寄存器。 *
    @@ -277,44 +395,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); } } + /* ───────────────── 字面量辅助方法 ───────────────── */ /** @@ -324,8 +452,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; } @@ -337,8 +468,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; } @@ -350,8 +484,11 @@ 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; } @@ -363,33 +500,58 @@ public record ExpressionBuilder(IRContext ctx) { * @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 数组 IRConstant - * @throws IllegalStateException 若含有非常量元素 + * @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()); - case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0); + // 布尔字面量,转成 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/vm/commands/ref/control/RPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java index 3223860..5fc661b 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,6 +5,10 @@ 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. @@ -14,75 +18,34 @@ import org.jcnc.snow.vm.module.OperandStack; *

    * *

    Instruction format: {@code R_PUSH }

    - *
      - *
    • {@code }: The reference value (e.g., string, boolean, integer, floating-point, or array literal) - * to be pushed onto the stack. If the literal contains spaces, all arguments after {@code R_PUSH} are joined with spaces.
    • - *
    - * - *

    Behavior:

    - *
      - *
    • Ensures that at least one parameter is provided after the operator; otherwise, throws {@code IllegalStateException}.
    • - *
    • Concatenates all parameters after {@code R_PUSH} into a single string, separated by spaces.
    • - *
    • If the resulting string is an array literal (i.e., surrounded by square brackets), splits and parses its elements using {@link #getObject(String)} for primitive type support.
    • - *
    • Otherwise, pushes the literal as a string reference onto the operand stack.
    • - *
    • Increments the program counter to advance to the next instruction.
    • - *
    * *

    - * Supported element types in array literals include integers, floating-point numbers, booleans, - * and quoted strings (surrounded by double quotes). + * Example usages: + *

      + *
    • {@code R_PUSH "hello"} → push the string {@code "hello"} onto stack
    • + *
    • {@code R_PUSH [1,2,3]} → push an (unmodifiable) {@code List} onto stack
    • + *
    • {@code R_PUSH [[1,2,3],[4,5,6]]} → push a nested (unmodifiable) {@code List>} onto stack
    • + *
    + *

    + * + *

    + * Supported element types in array literals include integers, floating-point numbers (parsed as {@code Double}), + * booleans ({@code true}/{@code false} → {@code 1}/{@code 0}), quoted strings (surrounded by double quotes), + * and further nested arrays. *

    */ public final class RPushCommand implements Command { - /** - * Parses a string element into its corresponding Java object. - *
      - *
    • If enclosed in double quotes, treats as a string literal (quotes are removed).
    • - *
    • If "true" or "false" (case-insensitive), returns 1 or 0 respectively (as integer representation).
    • - *
    • If numeric (integer or floating-point), parses accordingly.
    • - *
    • Otherwise, returns the string as-is.
    • - *
    - * - * @param e The string to parse. - * @return The parsed object (String, Integer, Double, or Integer for boolean). - */ - private static Object getObject(String e) { - String x = e.trim(); - Object v; - if (x.startsWith("\"") && x.endsWith("\"") && x.length() >= 2) { - // String literal (remove surrounding double quotes) - v = x.substring(1, x.length() - 1); - } else if ("true".equalsIgnoreCase(x) || "false".equalsIgnoreCase(x)) { - // Boolean value: true → 1, false → 0 - v = Boolean.parseBoolean(x) ? 1 : 0; - } else { - try { - // Attempt to parse as floating-point or integer number - if (x.contains(".") || x.contains("e") || x.contains("E")) { - v = Double.parseDouble(x); - } else { - v = Integer.parseInt(x); - } - } catch (NumberFormatException ex) { - // Fallback: treat as plain string - v = x; - } - } - return v; + // ======== Parsing helpers ======== + + private static final class Cursor { + final String s; + int i; + Cursor(String s) { this.s = s; this.i = 0; } + boolean end() { return i >= s.length(); } + char ch() { return s.charAt(i); } } - /** - * Executes the {@code R_PUSH} instruction: pushes a reference value onto the operand stack. - * - * @param parts The instruction split into its components. - * @param pc The current program counter. - * @param stack The operand stack. - * @param lvs The local variable store (unused). - * @param cs The call stack (unused). - * @return The next program counter value. - * @throws IllegalStateException If no parameter is supplied after {@code R_PUSH}. - */ @Override public int execute(String[] parts, int pc, OperandStack stack, @@ -102,22 +65,135 @@ public final class RPushCommand implements Command { // If the literal is an array (e.g., [1, 2, "foo"]), parse elements and push as an unmodifiable list. if (literal.startsWith("[") && literal.endsWith("]")) { - String inside = literal.substring(1, literal.length() - 1).trim(); - java.util.List list = new java.util.ArrayList<>(); - if (!inside.isEmpty()) { - // Split by comma, support element parsing (numbers, booleans, quoted strings) - String[] elems = inside.split(","); - for (String e : elems) { - Object v = getObject(e); - list.add(v); - } + Object parsed = parseValue(new Cursor(literal)); + if (!(parsed instanceof List list)) { + // Should never happen for a bracketed value, but keep a guard. + stack.push(parsed); + } else { + stack.push(deepUnmodifiable(list)); } - // Push as an unmodifiable list to prevent further modifications. - stack.push(java.util.Collections.unmodifiableList(list)); } else { // Otherwise, push the string literal as-is. stack.push(literal); } return pc + 1; } + + /** Deeply wrap lists as unmodifiable; leave scalars intact. */ + private static Object deepUnmodifiableObject(Object v) { + if (v instanceof List l) { + return deepUnmodifiable(l); + } + return v; + } + + private static List deepUnmodifiable(List l) { + List out = new ArrayList<>(l.size()); + for (Object v : l) out.add(deepUnmodifiableObject(v)); + return Collections.unmodifiableList(out); + } + + // ======== Recursive-descent parser for array literals ======== + + private static 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); + } + + private static List parseArray(Cursor c) { + // assumes current char is '[' + expect(c, '['); + skipWs(c); + List values = new ArrayList<>(); + if (!peek(c, ']')) { + while (true) { + Object v = parseValue(c); + values.add(v); + skipWs(c); + if (peek(c, ',')) { + c.i++; // consume ',' + skipWs(c); + continue; + } + break; + } + } + expect(c, ']'); + return values; + } + + private static String parseQuoted(Cursor c) { + // assumes current char is '"' + expect(c, '\"'); + StringBuilder sb = new StringBuilder(); + while (!c.end()) { + char ch = c.ch(); + c.i++; + if (ch == '\\') { // escape + if (c.end()) break; + char nxt = c.ch(); + c.i++; + switch (nxt) { + case 'n' -> sb.append('\n'); + case 'r' -> sb.append('\r'); + case 't' -> sb.append('\t'); + case '\"' -> sb.append('\"'); + case '\\' -> sb.append('\\'); + default -> sb.append(nxt); + } + } else if (ch == '\"') { + // end quote + return sb.toString(); + } else { + sb.append(ch); + } + } + // Unclosed string: return what we have + return sb.toString(); + } + + private static Object parseAtom(Cursor c) { + int start = c.i; + while (!c.end()) { + char ch = c.ch(); + if (ch == ',' || ch == ']' ) break; + if (Character.isWhitespace(ch)) break; + c.i++; + } + String token = c.s.substring(start, c.i).trim(); + if (token.isEmpty()) return ""; + // booleans + if ("true".equalsIgnoreCase(token)) return 1; + if ("false".equalsIgnoreCase(token)) return 0; + // number (int or double) + try { + if (token.contains(".") || token.contains("e") || token.contains("E")) { + return Double.parseDouble(token); + } else { + return Integer.parseInt(token); + } + } catch (NumberFormatException ex) { + // fallback: raw string + return token; + } + } + + private static void skipWs(Cursor c) { + while (!c.end() && Character.isWhitespace(c.ch())) c.i++; + } + + private static boolean peek(Cursor c, char ch) { + return !c.end() && c.ch() == ch; + } + + private static void expect(Cursor c, char ch) { + if (c.end() || c.ch() != ch) + throw new IllegalArgumentException("R_PUSH array literal parse error: expected '" + ch + "' at position " + c.i); + c.i++; // consume + skipWs(c); + } }