From d3a85a24bf47fe1bf0ff8841e64100ccb06b7b57 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 3 Aug 2025 00:08:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E8=B5=8B=E5=80=BC=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 __setindex_x 系列内置函数,用于数组元素赋值 - 实现了对 byte、short、int、long、float、double、boolean 和引用类型数组的支持 - 修改了 ExpressionBuilder 和 StatementBuilder以支持数组赋值语法 - 更新了 VirtualMachineEngine 和 SyscallCommand 以支持新的 ARR_SET系统调用 --- .../backend/generator/CallGenerator.java | 79 ++- .../ir/builder/ExpressionBuilder.java | 18 +- .../compiler/ir/builder/StatementBuilder.java | 51 ++ .../vm/commands/ref/control/RPushCommand.java | 493 ++++++++---------- .../system/control/SyscallCommand.java | 34 ++ .../snow/vm/engine/VirtualMachineEngine.java | 3 - 6 files changed, 383 insertions(+), 295 deletions(-) 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 d2d0a18..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 @@ -77,7 +77,7 @@ public class CallGenerator implements InstructionGenerator { return; } - // 各种一维数组类型(byte/short/int/long/float/double/boolean) + // 各种一维数组类型(byte/short/int/long/float/double/boolean)读取 switch (fn) { case "__index_b" -> { generateIndexInstruction(ins, out, slotMap, 'B'); @@ -107,6 +107,35 @@ public class CallGenerator implements InstructionGenerator { 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; + } } // 普通函数调用 @@ -115,6 +144,54 @@ public class CallGenerator implements InstructionGenerator { // ========== 私有辅助方法 ========== + /** + * 生成数组元素赋值指令(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 子命令(第一个参数),支持字符串常量与已绑定字符串的虚拟寄存器。 * 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 1eefd62..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 @@ -191,15 +191,15 @@ public record ExpressionBuilder(IRContext ctx) { 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 "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"; + case "string" -> func = "__index_r"; // 字符串/其它未识别类型均走引用 + default -> func = "__index_r"; } } } @@ -222,7 +222,7 @@ public record ExpressionBuilder(IRContext ctx) { * @param node 下标访问表达式节点 * @return 存放引用结果的虚拟寄存器 */ - private IRVirtualRegister buildIndexRef(IndexExpressionNode node) { + public IRVirtualRegister buildIndexRef(IndexExpressionNode node) { // 1. 常量折叠:如果 array 和 index 都是编译期常量,直接取值 Object arrConst = tryFoldConst(node.array()); Object idxConst = tryFoldConst(node.index()); 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 6fd26a7..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 @@ -103,6 +103,57 @@ public class StatementBuilder { 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()) { 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 0f1cd2a..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 @@ -14,292 +14,35 @@ import java.util.List; * and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine. * *

- * This instruction pushes a reference value—typically a string literal or an array literal—onto the operand stack. - *

- * - *

Instruction format: {@code R_PUSH }

- * - *

- * Example usages: + * This instruction pushes a reference-type value onto the operand stack. + * The input is parsed from the textual instruction form, which can represent: *

    - *
  • {@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
  • + *
  • String literals
  • + *
  • Array literals (e.g., {@code [1, 2, 3]}), including nested arrays
  • *
*

* *

- * 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. + * 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 { - - // ======== Parsing helpers ======== +public class RPushCommand implements Command { /** - * Deeply wraps lists as unmodifiable; leaves scalars unchanged. + * Executes the R_PUSH command. * - * @param v input object - * @return deeply unmodifiable version of the object - */ - private static Object deepUnmodifiableObject(Object v) { - if (v instanceof List l) { - return deepUnmodifiable(l); - } - return v; - } - - /** - * Recursively wraps all nested lists as unmodifiable. - * - * @param l input list - * @return deeply unmodifiable list - */ - private static List deepUnmodifiable(List l) { - List out = new ArrayList<>(l.size()); - for (Object v : l) out.add(deepUnmodifiableObject(v)); - return Collections.unmodifiableList(out); - } - - /** - * Parses a value starting from the cursor. - * Skips whitespace, and delegates to the appropriate sub-parser depending on the character: - * array, quoted string, or atomic value. - * - * @param c cursor - * @return parsed value (Object) - */ - 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); - } - - /** - * Parses an array literal from the cursor, supporting nested structures. - * Assumes the current character is '['. - * - * @param c cursor - * @return List of parsed objects - */ - 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; - } - - // ======== Recursive-descent parser for array literals ======== - - /** - * Parses a string literal wrapped in double quotes (supports common escape sequences). - *

- * Assumes the cursor currently points to the starting quote character ("), and consumes the opening quote. - * Parses the string content from the cursor, stopping at the closing quote ("). - * Supported escape sequences: - *

    - *
  • \n newline
  • - *
  • \r carriage return
  • - *
  • \t tab
  • - *
  • \" double quote itself
  • - *
  • \\ backslash
  • - *
  • Other characters are output as-is
  • - *
- * If the string is not closed properly (i.e., no closing quote is found before the end), returns the currently parsed content. - * - * @param c cursor object (must support ch() for current char, i for index, end() for boundary check) - * @return parsed string content - */ - private static String parseQuoted(Cursor c) { - // Assume current position is the opening quote; consume it - expect(c, '\"'); - StringBuilder sb = new StringBuilder(); - - // Traverse until the end or an unclosed string - while (!c.end()) { - char ch = c.ch(); - c.i++; - if (ch == '\\') { // handle escape sequences - if (c.end()) break; // nothing after escape char - char nxt = c.ch(); - c.i++; - // Common escapes - switch (nxt) { - case 'n' -> sb.append('\n'); // newline - case 'r' -> sb.append('\r'); // carriage return - case 't' -> sb.append('\t'); // tab - case '\"' -> sb.append('\"'); // double quote - case '\\' -> sb.append('\\'); // backslash - default -> sb.append(nxt); // any other char as-is - } - } else if (ch == '\"') { - // Found closing quote; end of string - return sb.toString(); - } else { - // Regular character - sb.append(ch); - } - } - // Unclosed string, return parsed content - return sb.toString(); - } - - /** - * Parses an atomic constant ("atom"), supporting type-suffixed numbers and booleans. - *

- * Examples: 0.1f, 123L, 3.14d, 100, true, false
- * Parsing rules: - *

    - *
  • Supports float(f/F), long(l/L), double(d/D), short(s/S), byte(b/B) type suffixes
  • - *
  • Supports boolean true/false (case-insensitive, converted to 1/0)
  • - *
  • Decimals without suffix parsed as double; integers without suffix as int
  • - *
  • If parsing fails, returns the original string
  • - *
- * - * @param c cursor, must support ch() for current char, i for index, end() for boundary check, s for the original string - * @return parsed Object - */ - private static Object parseAtom(Cursor c) { - int start = c.i; - // Read until a comma, ']' or whitespace - while (!c.end()) { - char ch = c.ch(); - if (ch == ',' || ch == ']') break; - if (Character.isWhitespace(ch)) break; - c.i++; - } - // Extract current token - String token = c.s.substring(start, c.i).trim(); - if (token.isEmpty()) return ""; - // Boolean parsing (case-insensitive, convert to 1/0) - if ("true".equalsIgnoreCase(token)) return 1; - if ("false".equalsIgnoreCase(token)) return 0; - // Handle numeric type suffixes - try { - char last = token.charAt(token.length() - 1); - switch (last) { - case 'f': - case 'F': - // float suffix - return Float.parseFloat(token.substring(0, token.length() - 1)); - case 'l': - case 'L': - // long suffix - return Long.parseLong(token.substring(0, token.length() - 1)); - case 'd': - case 'D': - // double suffix - return Double.parseDouble(token.substring(0, token.length() - 1)); - case 's': - case 'S': - // short suffix - return Short.parseShort(token.substring(0, token.length() - 1)); - case 'b': - case 'B': - // byte suffix - return Byte.parseByte(token.substring(0, token.length() - 1)); - default: - // No suffix, check for floating point or integer - if (token.contains(".") || token.contains("e") || token.contains("E")) { - return Double.parseDouble(token); - } else { - return Integer.parseInt(token); - } - } - } catch (NumberFormatException ex) { - // Parsing failed, fall back to original string (e.g. identifiers) - return token; - } - } - - - /** - * Skips all whitespace characters at the current cursor position until a non-whitespace or end of text is reached. - *

- * The cursor index is automatically incremented, so it will point to the next non-whitespace character (or end of text). - * - * @param c cursor object (must support ch() for current char, i for index, end() for boundary check) - */ - private static void skipWs(Cursor c) { - // Increment cursor while not at end and is whitespace - while (!c.end() && Character.isWhitespace(c.ch())) { - c.i++; - } - } - - /** - * Checks if the current cursor position matches the specified character. - * - * @param c cursor object - * @param ch expected character - * @return true if not at end and character matches ch, otherwise false - */ - private static boolean peek(Cursor c, char ch) { - return !c.end() && c.ch() == ch; - } - - /** - * Asserts that the current cursor position is the specified character; throws if not. - * If it matches, skips the character and any following whitespace. - * - * @param c cursor object - * @param ch expected character - * @throws IllegalArgumentException if current position is not the expected character - */ - 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 current character - skipWs(c); // Skip any subsequent whitespace - } - - - /** - * Executes the R_PUSH instruction: pushes a constant or array constant onto the operand stack. - *

- * Processing steps: - *

    - *
  • 1. Checks parameter count, throws if insufficient.
  • - *
  • 2. Concatenates all arguments (except opcode) into a raw literal string.
  • - *
  • 3. Checks if the literal is an array (starts with [ and ends with ]).
  • - *
  • 4. If array, recursively parses and pushes as a read-only List onto the operand stack.
  • - *
  • 5. Otherwise, pushes the literal string as-is.
  • - *
- * - * @param parts instruction and parameter strings (parts[0] is the opcode, others are params) - * @param pc current instruction index - * @param stack operand stack - * @param lvs local variable store - * @param cs call stack - * @return next instruction index + * @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) { - - // Check parameter count + public int execute(String[] parts, int pc, OperandStack stack, LocalVariableStore local, CallStack callStack) { if (parts.length < 2) throw new IllegalStateException("R_PUSH missing parameter"); @@ -318,8 +61,8 @@ public final class RPushCommand implements Command { // Should not happen in theory; safety fallback stack.push(parsed); } else { - // Convert to read-only List before pushing to prevent modification - stack.push(deepUnmodifiable(list)); + // Push a deep-mutable copy so ARR_SET can modify elements in-place + stack.push(deepMutable(list)); } } else { // Regular string, push as-is @@ -331,19 +74,29 @@ public final class RPushCommand implements Command { /** * A simple string cursor, supporting index increment and character reading, for use by the parser. */ - private static final class Cursor { - final String s; // Original string - int i; // Current index + 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; } /** - * Checks if the cursor is at the end of the string. - * - * @return true if at end + * 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(); @@ -353,10 +106,186 @@ public final class RPushCommand implements Command { * 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 aecb2e2..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 @@ -381,6 +381,40 @@ public class SyscallCommand implements Command { } } + 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" -> { 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();