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 58d721d..d2d0a18 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 @@ -23,7 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; * * */ @@ -77,16 +77,36 @@ public class CallGenerator implements InstructionGenerator { return; } - // 一维数组整型下标访问 - if ("__index_i".equals(fn)) { - generateIndexInstruction(ins, out, slotMap, true); - return; - } - - // 多维数组返回引用的下标访问 - if ("__index_r".equals(fn)) { - generateIndexInstruction(ins, out, slotMap, false); - return; + // 各种一维数组类型(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; + } } // 普通函数调用 @@ -155,25 +175,38 @@ public class CallGenerator implements InstructionGenerator { } /** - * 生成一维/多维数组的下标访问指令。 + * 生成一维/多维数组的下标访问指令(支持类型分派)。 + *

+ * 本方法用于将 IR 层的数组下标访问表达式(如 arr[idx]),生成对应的 VM 指令序列。 + * 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问。 + *

* - * @param ins 调用指令 - * @param out VM 程序构建器 - * @param slotMap 寄存器到槽位映射 - * @param isInt 是否是一维整型下标 + * + * + * @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, boolean isInt) { + 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"); // 保存返回值到目标寄存器 @@ -187,12 +220,37 @@ public class CallGenerator implements InstructionGenerator { throw new IllegalStateException( "[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest)); } - if (isInt) { - out.emit(OpHelper.opcode("I_STORE") + " " + destSlot); - out.setSlotType(destSlot, 'I'); - } else { - out.emit(OpHelper.opcode("R_STORE") + " " + destSlot); - out.setSlotType(destSlot, 'R'); + + // 按元素类型分派写入 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'); + } } } 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 70e456f..1eefd62 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 @@ -137,7 +137,7 @@ public record ExpressionBuilder(IRContext ctx) { *
  • 否则: *
      *
    • 若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;
    • - *
    • 最后一级用 __index_i 获取实际整型元素值。
    • + *
    • 最后一级用 __index_b/s/i/l/f/d/r,按声明类型智能分派。
    • *
    *
  • * @@ -182,8 +182,28 @@ public record ExpressionBuilder(IRContext ctx) { // 非最末层,下标取“引用” ctx.addInstruction(new CallInstruction(dest, "__index_r", argv)); } else { - // 最末层,下标取实际元素值 - ctx.addInstruction(new CallInstruction(dest, "__index_i", argv)); + // 最末层,下标取实际元素值,按声明类型分派 + 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; 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 e3fb9e7..854a984 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,8 +317,24 @@ public class SyscallCommand implements Command { sel.close(); } - // 数组元素访问:arr[idx] -> 返回元素(当前实现返回 int 或字符串/引用原样) +// 数组元素访问: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; @@ -249,16 +356,32 @@ public class SyscallCommand implements Command { throw new IllegalArgumentException("ARR_GET: not an array/list: " + arrObj); } + // === 按真实类型压栈(byte/short/int/long/float/double/boolean/string/ref)=== if (elem instanceof Number n) { - stack.push(n.intValue()); + 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 "PRINT" -> { Object dataObj = stack.pop(); @@ -279,95 +402,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"; - } }