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();