From 3758e3da40069a3fd878d534611b84079a39a94e Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 17 Jul 2025 17:03:49 +0800 Subject: [PATCH 01/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20SyscallComma?= =?UTF-8?q?nd=20=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 CommandFactory 中为 VMOpCode.SYSCALL 添加了 SyscallCommand 实现 - 解除了对 SYSCALL 命令的注释,使其可以被使用 --- src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java index 4054a11..dbdab0f 100644 --- a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java +++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java @@ -1,5 +1,6 @@ package org.jcnc.snow.vm.factories; +import org.jcnc.snow.vm.commands.system.control.SyscallCommand; import org.jcnc.snow.vm.commands.type.control.byte8.*; import org.jcnc.snow.vm.commands.type.control.double64.*; import org.jcnc.snow.vm.commands.type.control.float32.*; @@ -262,7 +263,7 @@ public class CommandFactory { // region System Control (0x0400-0x04FF) COMMANDS[VMOpCode.HALT] = new HaltCommand(); -// COMMANDS[VMOpCode.SYSCALL] = new SyscallCommand(); + COMMANDS[VMOpCode.SYSCALL] = new SyscallCommand(); // COMMANDS[VMOpCode.DEBUG_TRAP] = new DebugTrapCommand(); // endregion From bf9190dec658f9e503c83e9d356a455c0e653ab0 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 17 Jul 2025 17:04:12 +0800 Subject: [PATCH 02/36] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E6=8C=87=E4=BB=A4=20DEBUG=5FTRAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除了 OpHelper 类中 DEBUG_TRAP 指令的映射,以减少不必要的代码。这个修改对现有的功能没有影响,只是为了简化代码结构。 --- .../java/org/jcnc/snow/compiler/backend/utils/OpHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java index 42c29ff..d006802 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java @@ -176,7 +176,7 @@ public final class OpHelper { map.put("MOV", Integer.toString(VMOpCode.MOV)); map.put("HALT", Integer.toString(VMOpCode.HALT)); map.put("SYSCALL", Integer.toString(VMOpCode.SYSCALL)); - map.put("DEBUG_TRAP", Integer.toString(VMOpCode.DEBUG_TRAP)); +// map.put("DEBUG_TRAP", Integer.toString(VMOpCode.DEBUG_TRAP)); OPCODE_MAP = Collections.unmodifiableMap(map); Map revmap = new HashMap<>(); // reverse map From 23c6de3601a4039b8bd5700d20687ead9eae9a11 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 17 Jul 2025 17:04:22 +0800 Subject: [PATCH 03/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20SyscallComma?= =?UTF-8?q?nd=20=E7=B1=BB=E5=AE=9E=E7=8E=B0=E7=B3=BB=E7=BB=9F=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SyscallCommand 类,用于处理系统调用命令(opcode =0x0401) --- .../system/control/SyscallCommand.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java 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 new file mode 100644 index 0000000..017d561 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java @@ -0,0 +1,72 @@ +package org.jcnc.snow.vm.commands.system.control; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +import java.util.Arrays; + +/** + * SyscallCommand ―― 统一的系统调用入口(opcode = 0x0401)。 + * + *

当前支持子命令: + *

    + *
  • PRINT —— 打印,不换行
  • + *
  • PRINTLN —— 打印并换行
  • + *
+ * + *

用法示例(VM 指令):

+ *
+ *   1025 PRINT "Hello, Snow!"
+ *   I_PUSH 42
+ *   1025 PRINTLN
+ * 
+ */ +public class SyscallCommand implements Command { + + @Override + public int execute(String[] parts, int currentPC, + OperandStack operandStack, + LocalVariableStore localVariableStore, + CallStack callStack) { + + if (parts.length < 2) + throw new IllegalArgumentException("SYSCALL requires a sub-command"); + + String subCmd = parts[1].toUpperCase(); + + switch (subCmd) { + case "PRINT": + case "PRINTLN": { + boolean newline = subCmd.equals("PRINTLN"); + String output; + + // 指令里直接带字符串常量 + if (parts.length > 2) { + output = String.join(" ", Arrays.copyOfRange(parts, 2, parts.length)); + if (output.length() >= 2 && + output.startsWith("\"") && + output.endsWith("\"")) { + output = output.substring(1, output.length() - 1); // 去掉首尾引号 + } + } + // 没带常量,则弹栈打印 + else { + Object value = operandStack.pop(); + output = String.valueOf(value); + } + + if (newline) System.out.println(output); + else System.out.print(output); + break; + } + + default: + throw new UnsupportedOperationException("Unsupported SYSCALL: " + subCmd); + } + + // 下一条指令 + return currentPC + 1; + } +} From 59aef0c6616e8963e090444355c6393b62009213 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 17 Jul 2025 17:05:48 +0800 Subject: [PATCH 04/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20SYSCALL=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=A0=81=E7=9A=84=E8=AF=A6=E7=BB=86=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E5=B9=B6=E7=A7=BB=E9=99=A4=E6=9C=AA=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=20DEBUG=5FTRAP=20=E6=93=8D=E4=BD=9C=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 SYSCALL 操作码添加了详细的注释,说明了其执行步骤和实现类- 注释掉了未使用的 DEBUG_TRAP 操作码 - 引入了 SyscallCommand 类以实现 SYSCALL 操作码的功能 --- src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java index 73d8ed5..8e1a880 100644 --- a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java +++ b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java @@ -1,5 +1,6 @@ package org.jcnc.snow.vm.engine; +import org.jcnc.snow.vm.commands.system.control.SyscallCommand; import org.jcnc.snow.vm.commands.type.control.byte8.*; import org.jcnc.snow.vm.commands.type.control.double64.*; import org.jcnc.snow.vm.commands.type.control.float32.*; @@ -2646,8 +2647,18 @@ public class VMOpCode { * */ public static final int HALT = 0x0400; + /** + * SYSCALL Opcode: Represents a system call operation that invokes a system-level function or service. + *

This opcode is implemented by the {@link SyscallCommand} class, which defines its specific execution logic.

+ * + *

Execution Steps:

+ *
    + *
  1. Parses the system call identifier from the instruction parameters.
  2. + *
  3. Invokes the corresponding system-level function or service based on the system call identifier.
  4. + *
  5. Returns the result of the system call operation.
  6. + */ public static final int SYSCALL = 0x0401; - public static final int DEBUG_TRAP = 0x0402; +// public static final int DEBUG_TRAP = 0x0402; // endregion /** From 08cfc1ffb964d5563709e7c0f473f3a0602797d2 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 17 Jul 2025 17:06:39 +0800 Subject: [PATCH 05/36] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9=20Bug1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/BugFarm/Bug1/Main.snow | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/playground/BugFarm/Bug1/Main.snow b/playground/BugFarm/Bug1/Main.snow index f4fb369..3c4df88 100644 --- a/playground/BugFarm/Bug1/Main.snow +++ b/playground/BugFarm/Bug1/Main.snow @@ -2,8 +2,7 @@ module: Main function: main return_type: void body: - declare abc!:int =1 - + declare abc:string ="0" end body end function end module From dad69eabfba04b799a28e5d2dac6357cb3865af5 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 18 Jul 2025 18:06:19 +0800 Subject: [PATCH 06/36] =?UTF-8?q?feat(vm):=20=E5=AE=9E=E7=8E=B0=20UNIX?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E7=9A=84=E6=96=87=E4=BB=B6=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E7=AC=A6=20I/O=20=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 FDTable 类,用于管理文件描述符和 NIO Channel 的映射 - 重构 SyscallCommand 类,实现多个 I/O 相关的 syscall 子命令 - 支持文件操作(open、close、read、write、seek)、管道、网络套接字、I/O 多路复用等功能 - 优化异常处理机制,统一处理 syscall内部异常 --- .../system/control/SyscallCommand.java | 400 ++++++++++++++++-- .../java/org/jcnc/snow/vm/io/FDTable.java | 62 +++ 2 files changed, 422 insertions(+), 40 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/vm/io/FDTable.java 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 017d561..b45754e 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 @@ -1,72 +1,392 @@ package org.jcnc.snow.vm.commands.system.control; import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.io.FDTable; import org.jcnc.snow.vm.module.CallStack; import org.jcnc.snow.vm.module.LocalVariableStore; import org.jcnc.snow.vm.module.OperandStack; -import java.util.Arrays; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.file.OpenOption; +import java.nio.file.Paths; +import java.util.*; + +import static java.nio.file.StandardOpenOption.*; /** - * SyscallCommand ―― 统一的系统调用入口(opcode = 0x0401)。 + * {@code SyscallCommand} —— I/O syscall 子命令集合指令。 * - *

    当前支持子命令: + *

    + * 封装类 UNIX 文件描述符(File Descriptor, FD)操作、文件/网络 I/O、管道、fd 复制、多路复用(select)等系统调用能力, + * 基于 Java NIO 统一实现,供虚拟机指令集以 SYSCALL 形式扩展使用。 + *

    + * + * 栈操作约定: *
      - *
    • PRINT —— 打印,不换行
    • - *
    • PRINTLN —— 打印并换行
    • + *
    • 参数按“右值先入栈、左值后入栈”的顺序推入操作数栈,执行 {@code SYSCALL } 后出栈。
    • + *
    • 即:调用 SYSCALL OPEN path flags mode,入栈顺序为 path(先入)→ flags → mode(后入),出栈时 mode→flags→path 弹出。
    • *
    * - *

    用法示例(VM 指令):

    - *
    - *   1025 PRINT "Hello, Snow!"
    - *   I_PUSH 42
    - *   1025 PRINTLN
    - * 
    + * 返回值说明: + *
      + *
    • 成功:压入正值(如 FD、实际数据、字节数、0 表示成功)。
    • + *
    • 失败:统一压入 -1,后续可扩展为 errno 机制。
    • + *
    + * + * 支持的子命令(部分): + *
      + *
    • PRINT / PRINTLN —— 控制台输出(TODO: 代码未实现
    • + *
    • OPEN / CLOSE / READ / WRITE / SEEK —— 文件 I/O 操作
    • + *
    • PIPE / DUP —— 管道和 FD 复制
    • + *
    • SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络套接字
    • + *
    • SELECT —— I/O 多路复用
    • + *
    */ public class SyscallCommand implements Command { + /*------------------------------------ 工具方法 ------------------------------------*/ + + /** + * POSIX open 标志到 Java NIO OpenOption 的映射 + *

    + * 将 Linux/UNIX 的 open 调用 flags 参数,转换为 Java NIO 的 OpenOption 集合。 + * 目前仅支持 WRITE(0x1)、READ、CREATE(0x40)、TRUNCATE(0x200)、APPEND(0x400)等常用标志。 + *

    + * + * @param flags POSIX 风格 open 标志(如 O_WRONLY=0x1, O_CREAT=0x40 等) + * @return 映射后的 OpenOption 集合 + */ + private static Set flagsToOptions(int flags) { + Set opts = new HashSet<>(); + // 0x1 = 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; + } + + /** + * 统一异常处理: + *

    + * 捕获 syscall 内部所有异常,将 -1 压入操作数栈,表示系统调用失败(暂不区分错误类型)。 + * 常见异常如文件不存在、权限不足、通道类型不符、网络故障等。 + *

    + * + * @param stack 当前操作数栈 + * @param e 捕获的异常对象 + */ + private static void pushErr(OperandStack stack, Exception e) { + stack.push(-1); // 目前统一用 -1,后续可按异常类型/errno 映射 + } + + /*--------------------------------------------------------------------------------*/ + + /** + * 执行 SYSCALL 子命令: + *

    + * 按照 parts[1] 指定的 SYSCALL 子命令,结合虚拟机栈与资源表完成对应的系统调用模拟。 + * 支持基础文件、网络、管道等 I/O 操作,并对异常统一处理。 + *

    + * + * @param parts 指令字符串数组,parts[1] 为子命令 + * @param pc 当前指令计数器 + * @param stack 操作数栈 + * @param locals 局部变量表 + * @param callStack 调用栈 + * @return 下一条指令的 pc 值(默认 pc+1) + */ @Override - public int execute(String[] parts, int currentPC, - OperandStack operandStack, - LocalVariableStore localVariableStore, + public int execute(String[] parts, int pc, + OperandStack stack, + LocalVariableStore locals, CallStack callStack) { if (parts.length < 2) - throw new IllegalArgumentException("SYSCALL requires a sub-command"); + throw new IllegalArgumentException("SYSCALL needs sub-command"); - String subCmd = parts[1].toUpperCase(); + String cmd = parts[1].toUpperCase(Locale.ROOT); - switch (subCmd) { - case "PRINT": - case "PRINTLN": { - boolean newline = subCmd.equals("PRINTLN"); - String output; + try { + switch (cmd) { - // 指令里直接带字符串常量 - if (parts.length > 2) { - output = String.join(" ", Arrays.copyOfRange(parts, 2, parts.length)); - if (output.length() >= 2 && - output.startsWith("\"") && - output.endsWith("\"")) { - output = output.substring(1, output.length() - 1); // 去掉首尾引号 + /*==================== 文件 / 目录 ====================*/ + + /* + * OPEN —— 打开文件,返回文件描述符(File Descriptor, FD)。 + * 入栈(push)顺序: path(文件路径, String), flags(int), mode(int,文件权限,暂未用) + * 出栈(pop)顺序: mode → flags → path + * 成功返回 fd,失败返回 -1。 + */ + case "OPEN" -> { + int mode = (Integer) stack.pop(); // 目前未用 + int flags = (Integer) stack.pop(); + String path = String.valueOf(stack.pop()); + FileChannel fc = FileChannel.open(Paths.get(path), flagsToOptions(flags)); + stack.push(FDTable.register(fc)); + } + /* + * CLOSE —— 关闭文件描述符。 + * 入栈顺序: fd(int) + * 出栈顺序: fd + * 返回 0 成功,-1 失败 + */ + case "CLOSE" -> { + int fd = (Integer) stack.pop(); + FDTable.close(fd); + stack.push(0); + } + /* + * READ —— 从 fd 读取指定字节数。 + * 入栈顺序: fd(int), count(int,字节数) + * 出栈顺序: count → fd + * 返回:byte[](实际读取的数据,EOF 时长度为 0) + */ + case "READ" -> { + int count = (Integer) stack.pop(); + int fd = (Integer) stack.pop(); + Channel ch = FDTable.get(fd); + if (!(ch instanceof ReadableByteChannel rch)) { + stack.push(new byte[0]); + break; } + ByteBuffer buf = ByteBuffer.allocate(count); + int n = rch.read(buf); + if (n < 0) n = 0; + buf.flip(); + byte[] out = new byte[n]; + buf.get(out); + stack.push(out); } - // 没带常量,则弹栈打印 - else { - Object value = operandStack.pop(); - output = String.valueOf(value); + /* + * WRITE —— 向 fd 写数据。 + * 入栈顺序: fd(int), data(byte[] 或 String) + * 出栈顺序: data → fd + * 返回写入字节数,失败 -1 + */ + case "WRITE" -> { + Object dataObj = stack.pop(); + int fd = (Integer) stack.pop(); + byte[] data = (dataObj instanceof byte[] b) + ? b + : String.valueOf(dataObj).getBytes(); + Channel ch = FDTable.get(fd); + if (!(ch instanceof WritableByteChannel wch)) { + stack.push(-1); + break; + } + int written = wch.write(ByteBuffer.wrap(data)); + stack.push(written); + } + /* + * SEEK —— 文件定位,移动文件读写指针。 + * 入栈顺序: fd(int), offset(long/int), whence(int, 0=SET,1=CUR,2=END) + * 出栈顺序: whence → offset → fd + * 返回新位置(long),失败 -1 + */ + case "SEEK" -> { + int whence = (Integer) stack.pop(); + long off = ((Number) stack.pop()).longValue(); + int fd = (Integer) stack.pop(); + Channel ch = FDTable.get(fd); + if (!(ch instanceof SeekableByteChannel sbc)) { + stack.push(-1); + break; + } + SeekableByteChannel newPos = switch (whence) { + case 0 -> sbc.position(off); + case 1 -> sbc.position(sbc.position() + off); + case 2 -> sbc.position(sbc.size() + off); + default -> throw new IllegalArgumentException("bad whence"); + }; + stack.push(newPos); } - if (newline) System.out.println(output); - else System.out.print(output); - break; + /*==================== 管道 / FD 相关 ====================*/ + + /* + * PIPE —— 创建匿名管道。 + * 入栈顺序: (无) + * 出栈顺序: (无) + * 返回顺序: write fd(先压栈),read fd(后压栈) + */ + case "PIPE" -> { + Pipe p = Pipe.open(); + stack.push(FDTable.register(p.sink())); // write fd + stack.push(FDTable.register(p.source())); // read fd + } + /* + * DUP —— 复制一个已存在的 fd(dup)。 + * 入栈顺序: oldfd(int) + * 出栈顺序: oldfd + * 返回新的 fd,失败 -1 + */ + case "DUP" -> { + int oldfd = (Integer) stack.pop(); + stack.push(FDTable.dup(oldfd)); + } + + /*==================== 网络相关 ====================*/ + + /* + * SOCKET —— 创建套接字,支持 stream/dgram。 + * 入栈顺序: domain(int, AF_INET=2等), type(int, 1=STREAM,2=DGRAM), protocol(int, 预留) + * 出栈顺序: protocol → type → domain + * 返回 fd + */ + case "SOCKET" -> { + int proto = (Integer) stack.pop(); // 预留,暂不用 + int type = (Integer) stack.pop(); // 1=STREAM,2=DGRAM + int domain = (Integer) stack.pop(); // AF_INET=2…… + Channel ch = (type == 1) + ? SocketChannel.open() + : DatagramChannel.open(); + stack.push(FDTable.register(ch)); + } + /* + * CONNECT —— 发起 TCP 连接。 + * 入栈顺序: fd(int), host(String), port(int) + * 出栈顺序: port → host → fd + * 返回 0 成功,-1 失败 + */ + case "CONNECT" -> { + int port = (Integer) stack.pop(); + String host = String.valueOf(stack.pop()); + int fd = (Integer) stack.pop(); + Channel ch = FDTable.get(fd); + if (ch instanceof SocketChannel sc) { + sc.connect(new InetSocketAddress(host, port)); + stack.push(0); + } else stack.push(-1); + } + /* + * BIND —— 绑定端口。 + * 入栈顺序: fd(int), host(String), port(int) + * 出栈顺序: port → host → fd + * 返回 0 成功,-1 失败 + */ + case "BIND" -> { + int port = (Integer) stack.pop(); + String host = String.valueOf(stack.pop()); + int fd = (Integer) stack.pop(); + Channel ch = FDTable.get(fd); + if (ch instanceof ServerSocketChannel ssc) { + ssc.bind(new InetSocketAddress(host, port)); + stack.push(0); + } else stack.push(-1); + } + /* + * LISTEN —— 监听 socket,兼容 backlog。 + * 入栈顺序: fd(int), backlog(int) + * 出栈顺序: backlog → fd + * 返回 0 成功,-1 失败 + * 注意:Java NIO 打开 ServerSocketChannel 已自动监听,无 backlog 处理,行为和 UNIX 有区别。 + */ + case "LISTEN" -> { + int backlog = (Integer) stack.pop(); + int fd = (Integer) stack.pop(); + Channel ch = FDTable.get(fd); + if (ch instanceof ServerSocketChannel) stack.push(0); + else stack.push(-1); + } + /* + * ACCEPT —— 接收连接。 + * 入栈顺序: fd(int) + * 出栈顺序: fd + * 返回新连接 fd,失败 -1 + */ + case "ACCEPT" -> { + int fd = (Integer) stack.pop(); + Channel ch = FDTable.get(fd); + if (ch instanceof ServerSocketChannel ssc) { + SocketChannel cli = ssc.accept(); + stack.push(FDTable.register(cli)); + } else stack.push(-1); + } + + /*==================== 多路复用 ====================*/ + + /* + * SELECT —— I/O 多路复用,监视多个 fd 是否可读写。 + * 入栈顺序: fds(List), timeout_ms(long) + * 出栈顺序: timeout_ms → fds + * 返回: 就绪 fd 列表(List) + */ + case "SELECT" -> { + long timeout = ((Number) stack.pop()).longValue(); + @SuppressWarnings("unchecked") + List fds = (List) stack.pop(); + + Selector sel = Selector.open(); + for (int fd : fds) { + Channel c = FDTable.get(fd); + if (c instanceof SelectableChannel sc) { + sc.configureBlocking(false); + int ops = (c instanceof ReadableByteChannel ? SelectionKey.OP_READ : 0) + | (c instanceof WritableByteChannel ? SelectionKey.OP_WRITE : 0); + sc.register(sel, ops, fd); + } + } + int ready = sel.select(timeout); + List readyFds = new ArrayList<>(); + if (ready > 0) { + for (SelectionKey k : sel.selectedKeys()) + readyFds.add((Integer) k.attachment()); + } + stack.push(readyFds); + sel.close(); + } + + + /*==================== 控制台输出 ====================*/ + + /* + * PRINT —— 控制台输出(无换行)。 + * 入栈顺序: data(String 或 byte[]) + * 出栈顺序: data + * 返回 0 + */ + case "PRINT" -> { + Object dataObj = stack.pop(); + if (dataObj instanceof byte[] b) { + System.out.print(new String(b)); + } else { + System.out.print(dataObj); + } + stack.push(0); // 表示成功 + } + /* + * PRINTLN —— 控制台输出(自动换行)。 + * 入栈顺序: data(String 或 byte[]) + * 出栈顺序: data + * 返回 0 + */ + case "PRINTLN" -> { + Object dataObj = stack.pop(); + if (dataObj instanceof byte[] b) { + System.out.println(new String(b)); + } else { + System.out.println(dataObj); + } + stack.push(0); // 表示成功 + } + + /*==================== 其它未实现/扩展命令 ====================*/ + + /* + * TODO: PRINT / PRINTLN / 其它自定义 syscall 子命令未实现 + */ + default -> throw new UnsupportedOperationException("Unsupported SYSCALL: " + cmd); } - - default: - throw new UnsupportedOperationException("Unsupported SYSCALL: " + subCmd); + } catch (Exception e) { + // 统一异常处理,异常时压入 -1 + pushErr(stack, e); } - // 下一条指令 - return currentPC + 1; + // 默认:下一条指令 + return pc + 1; } } diff --git a/src/main/java/org/jcnc/snow/vm/io/FDTable.java b/src/main/java/org/jcnc/snow/vm/io/FDTable.java new file mode 100644 index 0000000..98d6385 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/io/FDTable.java @@ -0,0 +1,62 @@ +package org.jcnc.snow.vm.io; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.nio.channels.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 维护 “虚拟 fd → Java NIO Channel” 的全局映射表。 + * + *
    + *   0  → stdin   (ReadableByteChannel)
    + *   1  → stdout  (WritableByteChannel)
    + *   2  → stderr  (WritableByteChannel)
    + *   3+ → 运行期动态分配
    + * 
    + */ +public final class FDTable { + + private FDTable() {} + + /** 下一次可用 fd(0‒2 保留给标准流) */ + private static final AtomicInteger NEXT_FD = new AtomicInteger(3); + /** 主映射表:fd → Channel */ + private static final ConcurrentHashMap MAP = new ConcurrentHashMap<>(); + + static { + // JVM 标准流包装成 NIO Channel 后放入表中 + MAP.put(0, Channels.newChannel(new BufferedInputStream(System.in))); + MAP.put(1, Channels.newChannel(new BufferedOutputStream(System.out))); + MAP.put(2, Channels.newChannel(new BufferedOutputStream(System.err))); + } + + /** 注册新 Channel,返回分配到的虚拟 fd */ + public static int register(Channel ch) { + int fd = NEXT_FD.getAndIncrement(); + MAP.put(fd, ch); + return fd; + } + + /** 取得 Channel;如果 fd 不存在则返回 null */ + public static Channel get(int fd) { + return MAP.get(fd); + } + + /** 关闭并移除 fd(0‒2 忽略) */ + public static void close(int fd) throws IOException { + if (fd <= 2) return; // 标准流交由宿主 JVM 维护 + Channel ch = MAP.remove(fd); + if (ch != null && ch.isOpen()) ch.close(); + } + + /** 类似 dup(oldfd) —— 返回指向同一 Channel 的新 fd */ + public static int dup(int oldfd) { + Channel ch = MAP.get(oldfd); + if (ch == null) + throw new IllegalArgumentException("Bad fd: " + oldfd); + return register(ch); // 多个 fd 引用同一 Channel + } +} From 8ec5120e544cb6e1632000c7614fbbf6892bf953 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 18 Jul 2025 23:33:13 +0800 Subject: [PATCH 07/36] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E5=AD=90?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E6=94=AF=E6=8C=81=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 PRINT / PRINTLN 命令的 TODO 注释 - 修正其它自定义 syscall 子命令的 TODO 注释 --- .../jcnc/snow/vm/commands/system/control/SyscallCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b45754e..39e63e7 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 @@ -37,7 +37,7 @@ import static java.nio.file.StandardOpenOption.*; * * 支持的子命令(部分): *
      - *
    • PRINT / PRINTLN —— 控制台输出(TODO: 代码未实现
    • + *
    • PRINT / PRINTLN —— 控制台输出
    • *
    • OPEN / CLOSE / READ / WRITE / SEEK —— 文件 I/O 操作
    • *
    • PIPE / DUP —— 管道和 FD 复制
    • *
    • SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络套接字
    • @@ -377,7 +377,7 @@ public class SyscallCommand implements Command { /*==================== 其它未实现/扩展命令 ====================*/ /* - * TODO: PRINT / PRINTLN / 其它自定义 syscall 子命令未实现 + * 其它自定义 syscall 子命令未实现 */ default -> throw new UnsupportedOperationException("Unsupported SYSCALL: " + cmd); } From 4e3de185b83f795cbfe61f1d23bc3fc732081927 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 19 Jul 2025 00:09:17 +0800 Subject: [PATCH 08/36] =?UTF-8?q?refactor:=E9=87=8D=E6=9E=84=20PRINT=20?= =?UTF-8?q?=E5=92=8C=20PRINTLN=20=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=BE=93=E5=87=BA=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构了 PRINT 和 PRINTLN 指令的实现,支持更多数据类型输出 - 新增通用的 output辅助方法,统一处理各种类型的输出 - 扩展了输出功能,支持基本类型、数组和任意对象的打印 - 优化了代码结构,提高了可读性和可维护性 --- .../system/control/SyscallCommand.java | 149 +++++++++++------- 1 file changed, 91 insertions(+), 58 deletions(-) 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 39e63e7..eebcd28 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,42 +46,7 @@ import static java.nio.file.StandardOpenOption.*; */ public class SyscallCommand implements Command { - /*------------------------------------ 工具方法 ------------------------------------*/ - /** - * POSIX open 标志到 Java NIO OpenOption 的映射 - *

      - * 将 Linux/UNIX 的 open 调用 flags 参数,转换为 Java NIO 的 OpenOption 集合。 - * 目前仅支持 WRITE(0x1)、READ、CREATE(0x40)、TRUNCATE(0x200)、APPEND(0x400)等常用标志。 - *

      - * - * @param flags POSIX 风格 open 标志(如 O_WRONLY=0x1, O_CREAT=0x40 等) - * @return 映射后的 OpenOption 集合 - */ - private static Set flagsToOptions(int flags) { - Set opts = new HashSet<>(); - // 0x1 = 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; - } - - /** - * 统一异常处理: - *

      - * 捕获 syscall 内部所有异常,将 -1 压入操作数栈,表示系统调用失败(暂不区分错误类型)。 - * 常见异常如文件不存在、权限不足、通道类型不符、网络故障等。 - *

      - * - * @param stack 当前操作数栈 - * @param e 捕获的异常对象 - */ - private static void pushErr(OperandStack stack, Exception e) { - stack.push(-1); // 目前统一用 -1,后续可按异常类型/errno 映射 - } /*--------------------------------------------------------------------------------*/ @@ -92,10 +57,10 @@ public class SyscallCommand implements Command { * 支持基础文件、网络、管道等 I/O 操作,并对异常统一处理。 *

      * - * @param parts 指令字符串数组,parts[1] 为子命令 - * @param pc 当前指令计数器 - * @param stack 操作数栈 - * @param locals 局部变量表 + * @param parts 指令字符串数组,parts[1] 为子命令 + * @param pc 当前指令计数器 + * @param stack 操作数栈 + * @param locals 局部变量表 * @param callStack 调用栈 * @return 下一条指令的 pc 值(默认 pc+1) */ @@ -345,35 +310,24 @@ public class SyscallCommand implements Command { /* * PRINT —— 控制台输出(无换行)。 - * 入栈顺序: data(String 或 byte[]) + * 入栈顺序: data(任意基本类型或其包装类型、String、byte[] 等) * 出栈顺序: data * 返回 0 */ case "PRINT" -> { Object dataObj = stack.pop(); - if (dataObj instanceof byte[] b) { - System.out.print(new String(b)); - } else { - System.out.print(dataObj); - } - stack.push(0); // 表示成功 + output(dataObj, false); + stack.push(0); // success } - /* - * PRINTLN —— 控制台输出(自动换行)。 - * 入栈顺序: data(String 或 byte[]) - * 出栈顺序: data - * 返回 0 - */ + + /* PRINTLN —— 控制台输出(自动换行)。*/ case "PRINTLN" -> { Object dataObj = stack.pop(); - if (dataObj instanceof byte[] b) { - System.out.println(new String(b)); - } else { - System.out.println(dataObj); - } - stack.push(0); // 表示成功 + output(dataObj, true); + stack.push(0); // success } + /*==================== 其它未实现/扩展命令 ====================*/ /* @@ -389,4 +343,83 @@ public class SyscallCommand implements Command { // 默认:下一条指令 return pc + 1; } + + /*------------------------------------ 工具方法 ------------------------------------*/ + + /** + * POSIX open 标志到 Java NIO OpenOption 的映射 + *

      + * 将 Linux/UNIX 的 open 调用 flags 参数,转换为 Java NIO 的 OpenOption 集合。 + * 目前仅支持 WRITE(0x1)、READ、CREATE(0x40)、TRUNCATE(0x200)、APPEND(0x400)等常用标志。 + *

      + * + * @param flags POSIX 风格 open 标志(如 O_WRONLY=0x1, O_CREAT=0x40 等) + * @return 映射后的 OpenOption 集合 + */ + private static Set flagsToOptions(int flags) { + Set opts = new HashSet<>(); + // 0x1 = 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; + } + + /** + * 统一异常处理: + *

      + * 捕获 syscall 内部所有异常,将 -1 压入操作数栈,表示系统调用失败(暂不区分错误类型)。 + * 常见异常如文件不存在、权限不足、通道类型不符、网络故障等。 + *

      + * + * @param stack 当前操作数栈 + * @param e 捕获的异常对象 + */ + private static void pushErr(OperandStack stack, Exception e) { + stack.push(-1); // 目前统一用 -1,后续可按异常类型/errno 映射 + } + + /** + * 统一的控制台输出辅助方法,支持: + *
        + *
      • 所有基本类型及其包装类(int、double、long、float…)
      • + *
      • String、byte[]、char[] 以及其他原生数组
      • + *
      • 任意 Object(调用 {@code toString})
      • + *
      + * + * @param obj 要输出的对象 + * @param newline 是否追加换行 + */ + 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); + } + + /** + * 将各种原生数组转换成可读字符串。 + */ + 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"; + } } From 64cefebee542f7ae263ca1896363a6ae143f8a36 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 19 Jul 2025 17:04:49 +0800 Subject: [PATCH 09/36] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20BuiltinUtils?= =?UTF-8?q?=20=E6=A0=87=E5=87=86=E5=BA=93=E5=B9=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 BuiltinTypeRegistry 类,扩展内置类型和模块的注册功能 - 新增 BuiltinUtils 标准库模块,提供 print 和 println 函数 - 实现 syscall 内核函数,供 BuiltinUtils 内部使用 - 更新测试用例,添加 Demo14 项目以验证新功能 --- .run/测试.run.xml | 1 - playground/Demo/Demo14/BuiltinUtils.snow | 19 ++++ playground/Demo/Demo14/Main.snow | 11 +++ .../semantic/core/BuiltinTypeRegistry.java | 90 +++++++++++-------- 4 files changed, 85 insertions(+), 36 deletions(-) create mode 100644 playground/Demo/Demo14/BuiltinUtils.snow create mode 100644 playground/Demo/Demo14/Main.snow diff --git a/.run/测试.run.xml b/.run/测试.run.xml index 5000363..c37e7f3 100644 --- a/.run/测试.run.xml +++ b/.run/测试.run.xml @@ -3,7 +3,6 @@ - diff --git a/playground/Demo/Demo14/BuiltinUtils.snow b/playground/Demo/Demo14/BuiltinUtils.snow new file mode 100644 index 0000000..d915ee8 --- /dev/null +++ b/playground/Demo/Demo14/BuiltinUtils.snow @@ -0,0 +1,19 @@ +module: BuiltinUtils + function: print + parameter: + declare msg: int + return_type: void + body: + syscall("PRINT",1) + end body + end function + + function: println + parameter: + declare msg: int + return_type: void + body: + syscall("PRINTLN",1) + end body + end function +end module diff --git a/playground/Demo/Demo14/Main.snow b/playground/Demo/Demo14/Main.snow new file mode 100644 index 0000000..baf23e4 --- /dev/null +++ b/playground/Demo/Demo14/Main.snow @@ -0,0 +1,11 @@ +module: Main + import: BuiltinUtils + + function: main + return_type: void + body: + BuiltinUtils.print(1) + BuiltinUtils.println(2) + end body + end function +end module \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java index d1566ab..3e69f38 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java @@ -1,54 +1,74 @@ package org.jcnc.snow.compiler.semantic.core; -import org.jcnc.snow.compiler.semantic.type.*; +import org.jcnc.snow.compiler.semantic.type.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.FunctionType; +import org.jcnc.snow.compiler.semantic.type.Type; -import java.util.Map; +import java.util.*; /** - * {@code BuiltinTypeRegistry} 是内置类型和内置模块的集中注册中心。 - *

      - * 本类主要负责: + * 语言全部内置类型 / 模块 / 函数的注册中心。 + * + *

      目前同时注册:

      *
        - *
      • 定义语言中所有可识别的基础类型(如 int、float、string 等);
      • - *
      • 在语义分析初始化时,将内置模块(如 {@code BuiltinUtils})注册到上下文中;
      • - *
      • 提供对内置类型的快速查找支持。
      • + *
      • 所有基础类型(byte、short、int …)
      • + *
      • 标准库模块 BuiltinUtils —— 仅声明函数签名,真正实现写在 Snow 源码中
      • + *
      • 运行时内核函数 syscall —— 供标准库内部实现调用
      • *
      - * 该类为纯工具类,所有成员均为静态,不可实例化。 */ public final class BuiltinTypeRegistry { - /** - * 内置类型映射表: 将类型名称字符串映射到对应的 {@link Type} 实例。 - *

      - * 用于类型解析过程(如解析变量声明或函数返回类型)中, - * 将用户源码中的类型字符串转换为语义类型对象。 - */ - public static final Map BUILTIN_TYPES = Map.of( - "int", BuiltinType.INT, - "long", BuiltinType.LONG, - "short", BuiltinType.SHORT, - "byte", BuiltinType.BYTE, - "float", BuiltinType.FLOAT, - "double", BuiltinType.DOUBLE, - "string", BuiltinType.STRING, - "boolean", BuiltinType.BOOLEAN, - "void", BuiltinType.VOID - ); + /** 基础类型表:名称 → Type */ + public static final Map BUILTIN_TYPES; + static { + Map t = new HashMap<>(); + t.put("byte", BuiltinType.BYTE); + t.put("short", BuiltinType.SHORT); + t.put("int", BuiltinType.INT); + t.put("long", BuiltinType.LONG); + t.put("float", BuiltinType.FLOAT); + t.put("double", BuiltinType.DOUBLE); + t.put("string", BuiltinType.STRING); + t.put("void", BuiltinType.VOID); + BUILTIN_TYPES = Collections.unmodifiableMap(t); + } - /** - * 私有构造函数,禁止实例化。 - */ private BuiltinTypeRegistry() { } /** - * 初始化语义上下文中与内置模块相关的内容。 - *

      - * 当前实现将内置模块 {@code BuiltinUtils} 注册至上下文模块表中, - * 使其在用户代码中可被访问(如 {@code BuiltinUtils.to_string(...)})。 - * - * @param ctx 当前语义分析上下文 + * 供语义分析阶段调用:向上下文注入所有内置模块 / 函数声明。 */ public static void init(Context ctx) { + /* ---------- BuiltinUtils ---------- */ + ModuleInfo utils = new ModuleInfo("BuiltinUtils"); + // print(string): void + utils.getFunctions().put( + "print", + new FunctionType( + Collections.singletonList(BuiltinType.STRING), + BuiltinType.VOID + ) + ); + + // println(string): void + utils.getFunctions().put( + "println", + new FunctionType( + Collections.singletonList(BuiltinType.STRING), + BuiltinType.VOID + ) + ); + + // syscall(string, string): void —— 供 BuiltinUtils 内部使用 + utils.getFunctions().put( + "syscall", + new FunctionType( + Arrays.asList(BuiltinType.STRING, BuiltinType.STRING), + BuiltinType.VOID + ) + ); + + ctx.getModules().putIfAbsent("BuiltinUtils", utils); } } From 72982c112751911cb9250257efe31d2d98ec254e Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 20 Jul 2025 18:48:54 +0800 Subject: [PATCH 10/36] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=20Demo14?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .run/Demo14.run.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .run/Demo14.run.xml diff --git a/.run/Demo14.run.xml b/.run/Demo14.run.xml new file mode 100644 index 0000000..6071504 --- /dev/null +++ b/.run/Demo14.run.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file From 983a287c422c9282d4d77fd8c4296759c06e5c7f Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 20 Jul 2025 20:14:12 +0800 Subject: [PATCH 11/36] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=20BuiltinU?= =?UTF-8?q?tils=20=E4=B8=AD=20syscall=20=E5=87=BD=E6=95=B0=E7=9A=84?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除了 BuiltinUtils 中的 print 和 println函数 - 将 syscall 函数的第二个参数类型从 STRING 改为 INT --- .../semantic/core/BuiltinTypeRegistry.java | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java index 3e69f38..e4bba8e 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java @@ -42,29 +42,11 @@ public final class BuiltinTypeRegistry { /* ---------- BuiltinUtils ---------- */ ModuleInfo utils = new ModuleInfo("BuiltinUtils"); - // print(string): void - utils.getFunctions().put( - "print", - new FunctionType( - Collections.singletonList(BuiltinType.STRING), - BuiltinType.VOID - ) - ); - - // println(string): void - utils.getFunctions().put( - "println", - new FunctionType( - Collections.singletonList(BuiltinType.STRING), - BuiltinType.VOID - ) - ); - - // syscall(string, string): void —— 供 BuiltinUtils 内部使用 + // syscall(string, int): void —— 供 BuiltinUtils 内部使用 utils.getFunctions().put( "syscall", new FunctionType( - Arrays.asList(BuiltinType.STRING, BuiltinType.STRING), + Arrays.asList(BuiltinType.STRING, BuiltinType.INT), BuiltinType.VOID ) ); From b332c76ef88d8353569e776cdc5ed6d66d3893da Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 20 Jul 2025 20:14:52 +0800 Subject: [PATCH 12/36] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=AF=BC=E5=85=A5=E6=A8=A1=E5=9D=97=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=94=AF=E6=8C=81=20-=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E4=BA=86=E5=9C=A8=E5=BD=93=E5=89=8D=E6=A8=A1=E5=9D=97=E6=9C=AA?= =?UTF-8?q?=E6=89=BE=E5=88=B0=E7=9B=AE=E6=A0=87=E5=87=BD=E6=95=B0=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E8=87=AA=E5=8A=A8=E9=81=8D=E5=8E=86=E6=89=80=E6=9C=89?= =?UTF-8?q?=E5=B7=B2=E5=AF=BC=E5=85=A5=E6=A8=A1=E5=9D=97=E5=AF=BB=E6=89=BE?= =?UTF-8?q?=E5=94=AF=E4=B8=80=E5=90=8C=E5=90=8D=E5=87=BD=E6=95=B0=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20-=20=E5=A6=82=E6=9E=9C=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=A8=A1=E5=9D=97=E5=90=AB=E6=9C=89=E5=90=8C?= =?UTF-8?q?=E5=90=8D=E5=87=BD=E6=95=B0=EF=BC=8C=E4=BC=9A=E6=8A=A5=E9=94=99?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E5=87=BD=E6=95=B0=E8=B0=83=E7=94=A8=E4=B8=8D?= =?UTF-8?q?=E6=98=8E=E7=A1=AE=20-=20=E8=BF=99=E4=B8=AA=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BA=86=E5=87=BD=E6=95=B0=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E8=8C=83=E5=9B=B4=EF=BC=8C=E6=8F=90=E9=AB=98=E4=BA=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=9A=84=E7=81=B5=E6=B4=BB=E6=80=A7=E5=92=8C?= =?UTF-8?q?=E5=8F=AF=E5=A4=8D=E7=94=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo/Demo14/BuiltinUtils.snow | 19 ----------- .../expression/CallExpressionAnalyzer.java | 34 +++++++++++++++++-- 2 files changed, 32 insertions(+), 21 deletions(-) delete mode 100644 playground/Demo/Demo14/BuiltinUtils.snow diff --git a/playground/Demo/Demo14/BuiltinUtils.snow b/playground/Demo/Demo14/BuiltinUtils.snow deleted file mode 100644 index d915ee8..0000000 --- a/playground/Demo/Demo14/BuiltinUtils.snow +++ /dev/null @@ -1,19 +0,0 @@ -module: BuiltinUtils - function: print - parameter: - declare msg: int - return_type: void - body: - syscall("PRINT",1) - end body - end function - - function: println - parameter: - declare msg: int - return_type: void - body: - syscall("PRINTLN",1) - end body - end function -end module diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java index 8c2eddc..df0993c 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java @@ -20,7 +20,7 @@ import java.util.List; *

      * 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式,执行如下操作: *

        - *
      • 识别调用目标(支持模块成员函数调用和当前模块函数调用);
      • + *
      • 识别调用目标(支持模块成员函数调用和当前模块函数调用,也支持自动在所有已导入模块中查找唯一同名函数);
      • *
      • 根据被调用函数的参数签名检查实参数量和类型的兼容性;
      • *
      • 支持数值参数的宽化转换(如 int → double);
      • *
      • 支持数值到字符串的隐式转换(自动视为调用 {@code to_string});
      • @@ -77,8 +77,38 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer Date: Sun, 20 Jul 2025 20:15:07 +0800 Subject: [PATCH 13/36] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/BugFarm/Bug1/Main.snow | 2 +- playground/Demo/Demo14/Main.snow | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/playground/BugFarm/Bug1/Main.snow b/playground/BugFarm/Bug1/Main.snow index 3c4df88..3468433 100644 --- a/playground/BugFarm/Bug1/Main.snow +++ b/playground/BugFarm/Bug1/Main.snow @@ -2,7 +2,7 @@ module: Main function: main return_type: void body: - declare abc:string ="0" + declare abc:int =1 end body end function end module diff --git a/playground/Demo/Demo14/Main.snow b/playground/Demo/Demo14/Main.snow index baf23e4..42049ea 100644 --- a/playground/Demo/Demo14/Main.snow +++ b/playground/Demo/Demo14/Main.snow @@ -1,11 +1,10 @@ module: Main - import: BuiltinUtils + import: BuiltinUtils - function: main - return_type: void - body: - BuiltinUtils.print(1) - BuiltinUtils.println(2) - end body - end function + function: main + return_type: void + body: + syscall("PRINT",1) + end body + end function end module \ No newline at end of file From 3aa38027c87334c0b2750e4cc9069d8fbab865da Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 20 Jul 2025 20:45:17 +0800 Subject: [PATCH 14/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=AD=97=E9=9D=A2=E9=87=8F=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ExpressionBuilder 类中增加了处理字符串字面量的逻辑 - 新增 buildStringLiteral 方法用于生成字符串常量寄存器 - 更新了 build 方法的 switch 语句,支持 StringLiteralNode 类型 - 优化了代码结构,提高了可读性和可维护性 --- .../ir/builder/ExpressionBuilder.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) 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 d56103a..31e2973 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 @@ -34,13 +34,14 @@ public record ExpressionBuilder(IRContext ctx) { *

        会根据节点的实际类型分别处理: *

          *
        • 数字字面量: 新建常量寄存器
        • + *
        • 字符串字面量: 新建常量寄存器(字符串类型)
        • *
        • 布尔字面量: 生成值为 0 或 1 的常量寄存器
        • *
        • 标识符: 查找当前作用域中的寄存器
        • *
        • 二元表达式: 递归处理子表达式并进行相应运算
        • - *
        • 一元运算符: + *
        • 一元运算符: *
            *
          • -x(取负,生成 NEG_I32 指令)与
          • - *
          • code>!x(逻辑非,转换为 x == 0 比较指令)
          • + *
          • !x(逻辑非,转换为 x == 0 比较指令)
          • *
          *
        • *
        • 函数调用: 生成对应的Call指令
        • @@ -51,11 +52,12 @@ public record ExpressionBuilder(IRContext ctx) { * @return 该表达式的计算结果寄存器 * @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型 */ - public IRVirtualRegister build(ExpressionNode expr) { return switch (expr) { // 数字字面量 case NumberLiteralNode n -> buildNumberLiteral(n.value()); + // 字符串字面量 + case StringLiteralNode s -> buildStringLiteral(s.value()); // 布尔字面量 case BoolLiteralNode b -> buildBoolLiteral(b.getValue()); // 标识符 @@ -69,6 +71,7 @@ public record ExpressionBuilder(IRContext ctx) { case BinaryExpressionNode bin -> buildBinary(bin); // 函数调用 case CallExpressionNode call -> buildCall(call); + // 一元表达式 case UnaryExpressionNode u -> buildUnary(u); default -> throw new IllegalStateException( "不支持的表达式类型: " + expr.getClass().getSimpleName()); @@ -111,6 +114,12 @@ public record ExpressionBuilder(IRContext ctx) { // 数字字面量,直接加载到目标寄存器 case NumberLiteralNode n -> InstructionFactory.loadConstInto(ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value())); + // 字符串字面量,直接加载到目标寄存器 + case StringLiteralNode s -> + InstructionFactory.loadConstInto(ctx, dest, new IRConstant(s.value())); + // 布尔字面量,直接加载到目标寄存器 + case BoolLiteralNode b -> + InstructionFactory.loadConstInto(ctx, dest, new IRConstant(b.getValue() ? 1 : 0)); // 标识符,查找并move到目标寄存器 case IdentifierNode id -> { IRVirtualRegister src = ctx.getScope().lookup(id.name()); @@ -223,6 +232,19 @@ public record ExpressionBuilder(IRContext ctx) { return reg; } + /** + * 处理字符串字面量,生成常量寄存器和加载指令。 + * + * @param value 字符串内容 + * @return 存放该字符串常量的寄存器 + */ + private IRVirtualRegister buildStringLiteral(String value) { + IRConstant constant = new IRConstant(value); + IRVirtualRegister reg = ctx.newRegister(); + ctx.addInstruction(new LoadConstInstruction(reg, constant)); + return reg; + } + /** 布尔字面量 → CONST (true=1,false=0)*/ private IRVirtualRegister buildBoolLiteral(boolean value) { IRConstant constant = new IRConstant(value ? 1 : 0); From c6067a875806f722721c1dac79e4dd3a4276d8d2 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 16:46:34 +0800 Subject: [PATCH 15/36] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E7=B1=BB=E5=9E=8B=E5=B8=B8=E9=87=8F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E5=8F=8D=E7=A0=81=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 OPCODE_MAP 中添加 R_PUSH、R_LOAD 和 R_STORE 指令- 优化反码映射逻辑,支持十六进制表示的指令码 - 在 getConstType 方法中增加对 String 类型的支持 --- .../snow/compiler/backend/utils/OpHelper.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java index d006802..977c85e 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java @@ -167,6 +167,9 @@ public final class OpHelper { map.put("D2I", Integer.toString(VMOpCode.D2I)); map.put("D2L", Integer.toString(VMOpCode.D2L)); map.put("D2F", Integer.toString(VMOpCode.D2F)); + map.put("R_PUSH", Integer.toString(VMOpCode.R_PUSH)); + map.put("R_LOAD", Integer.toString(VMOpCode.R_LOAD)); + map.put("R_STORE", Integer.toString(VMOpCode.R_STORE)); map.put("POP", Integer.toString(VMOpCode.POP)); map.put("DUP", Integer.toString(VMOpCode.DUP)); map.put("SWAP", Integer.toString(VMOpCode.SWAP)); @@ -180,7 +183,15 @@ public final class OpHelper { OPCODE_MAP = Collections.unmodifiableMap(map); Map revmap = new HashMap<>(); // reverse map - OPCODE_MAP.forEach((key, value) -> revmap.put(Integer.parseInt(value), key)); + OPCODE_MAP.forEach((key, value) -> { + int codeInt; + if (value.startsWith("0x") || value.startsWith("0X")) { + codeInt = Integer.parseInt(value.substring(2), 16); + } else { + codeInt = Integer.parseInt(value); + } + revmap.put(codeInt, key); + }); OPCODE_NAME_MAP = Collections.unmodifiableMap(revmap); } @@ -222,11 +233,13 @@ public final class OpHelper { if (v instanceof Byte) return "B"; if (v instanceof Double) return "D"; if (v instanceof Float) return "F"; + if (v instanceof String) return "R"; //引用类型 throw new IllegalStateException("Unknown const type: " + v.getClass()); } /** * 根据 opcode 数值的字符串形式获取指令名 + * * @param code 字符串形式的 opcode 数值 * @return opcode 对应的指令名 */ @@ -236,6 +249,7 @@ public final class OpHelper { /** * 根据 opcode 获取指令名 + * * @param code opcode * @return opcode 对应的指令名 */ From 2cb428ed9ba992150f0761a92787a6bb3be3a007 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 16:48:50 +0800 Subject: [PATCH 16/36] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E6=8E=A7=E5=88=B6=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 RPushCommand、RLoadCommand 和 RStoreCommand 三个引用控制命令类 - 在 CommandFactory 中注册这三个命令 --- .../org/jcnc/snow/vm/factories/CommandFactory.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java index dbdab0f..71479a3 100644 --- a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java +++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java @@ -6,6 +6,9 @@ import org.jcnc.snow.vm.commands.type.control.double64.*; import org.jcnc.snow.vm.commands.type.control.float32.*; import org.jcnc.snow.vm.commands.type.control.int32.*; import org.jcnc.snow.vm.commands.type.control.long64.*; +import org.jcnc.snow.vm.commands.type.control.ref.RLoadCommand; +import org.jcnc.snow.vm.commands.type.control.ref.RPushCommand; +import org.jcnc.snow.vm.commands.type.control.ref.RStoreCommand; import org.jcnc.snow.vm.commands.type.control.short16.*; import org.jcnc.snow.vm.commands.type.control.byte8.BAndCommand; import org.jcnc.snow.vm.commands.type.control.byte8.BOrCommand; @@ -62,6 +65,7 @@ public class CommandFactory { static { + // region Type Control (0x0000-0x00BF) // region Byte8 (0x0000-0x001F) COMMANDS[VMOpCode.B_ADD] = new BAddCommand(); @@ -205,6 +209,7 @@ public class CommandFactory { COMMANDS[VMOpCode.D_CL] = new DCLCommand(); COMMANDS[VMOpCode.D_CLE] = new DCLECommand(); // endregion + // endregion // region Type Conversion (0x00C0-0x00DF) @@ -245,6 +250,12 @@ public class CommandFactory { COMMANDS[VMOpCode.D2F] = new D2FCommand(); // endregion + // region Reference Control (0x00E0-0x00EF) + COMMANDS[VMOpCode.R_PUSH] = new RPushCommand(); + COMMANDS[VMOpCode.R_LOAD] = new RLoadCommand(); + COMMANDS[VMOpCode.R_STORE] = new RStoreCommand(); + // endregion + // region Stack Control (0x0100-0x01FF) COMMANDS[VMOpCode.POP] = new PopCommand(); COMMANDS[VMOpCode.DUP] = new DupCommand(); From fec9fe3527ac8111b82182980a775e0035b4e386 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 16:51:30 +0800 Subject: [PATCH 17/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20BuiltinT?= =?UTF-8?q?ypeRegistry=20=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重新整理基础类型表,添加 boolean 类型 - 优化文档注释,增加类和方法的详细描述 - 改进代码结构,提高可读性和可维护性 --- .../semantic/core/BuiltinTypeRegistry.java | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java index e4bba8e..9f266c9 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java @@ -7,42 +7,62 @@ import org.jcnc.snow.compiler.semantic.type.Type; import java.util.*; /** - * 语言全部内置类型 / 模块 / 函数的注册中心。 + * BuiltinTypeRegistry - 语言全部内置类型/模块/函数注册中心 * - *

          目前同时注册:

          + *

          + * 该类统一注册编译器需要用到的所有基础类型、标准库模块与内核函数,供语义分析及类型检查阶段使用。 *

            - *
          • 所有基础类型(byte、short、int …)
          • - *
          • 标准库模块 BuiltinUtils —— 仅声明函数签名,真正实现写在 Snow 源码中
          • - *
          • 运行时内核函数 syscall —— 供标准库内部实现调用
          • + *
          • 所有基础类型(byte、short、int、long、float、double、string、boolean、void)
          • + *
          • 标准库模块 BuiltinUtils(仅注册函数签名,具体实现由 Snow 语言源码实现)
          • + *
          • 内核函数 syscall(供标准库内部实现调用)
          • *
          + *

          */ public final class BuiltinTypeRegistry { - /** 基础类型表:名称 → Type */ + /** + * 基础类型表:类型名称 → Type 实例 + *

          + * 本 Map 静态初始化,注册所有 Snow 语言基础类型,供类型检查与类型推断使用。 + *

          + */ public static final Map BUILTIN_TYPES; static { Map t = new HashMap<>(); - t.put("byte", BuiltinType.BYTE); - t.put("short", BuiltinType.SHORT); - t.put("int", BuiltinType.INT); - t.put("long", BuiltinType.LONG); - t.put("float", BuiltinType.FLOAT); - t.put("double", BuiltinType.DOUBLE); - t.put("string", BuiltinType.STRING); - t.put("void", BuiltinType.VOID); - BUILTIN_TYPES = Collections.unmodifiableMap(t); + t.put("byte", BuiltinType.BYTE); // 字节型 + t.put("short", BuiltinType.SHORT); // 短整型 + t.put("int", BuiltinType.INT); // 整型 + t.put("long", BuiltinType.LONG); // 长整型 + t.put("float", BuiltinType.FLOAT); // 单精度浮点 + t.put("double", BuiltinType.DOUBLE); // 双精度浮点 + t.put("string", BuiltinType.STRING); // 字符串 + t.put("void", BuiltinType.VOID); // 无返回 + t.put("boolean", BuiltinType.BOOLEAN); // 布尔类型 + + BUILTIN_TYPES = Collections.unmodifiableMap(t); // 不可变映射,防止被意外更改 } + /** + * 私有构造方法,禁止实例化 + */ private BuiltinTypeRegistry() { } /** - * 供语义分析阶段调用:向上下文注入所有内置模块 / 函数声明。 + * 初始化内置模块和函数声明 + * + *

          + * 语义分析阶段调用,将所有基础模块与函数声明注册到语义上下文中。 + * - 目前注册 BuiltinUtils 标准库模块(仅注册签名,不负责具体实现)。 + * - syscall 函数注册到 BuiltinUtils 内,供标准库内部调用。 + *

          + * + * @param ctx 全局语义分析上下文,持有模块表 */ public static void init(Context ctx) { - /* ---------- BuiltinUtils ---------- */ - ModuleInfo utils = new ModuleInfo("BuiltinUtils"); + /* ---------- 注册标准库 os ---------- */ + ModuleInfo utils = new ModuleInfo("os"); - // syscall(string, int): void —— 供 BuiltinUtils 内部使用 + // syscall(string, int): void —— 供标准库内部使用的调用接口 utils.getFunctions().put( "syscall", new FunctionType( @@ -51,6 +71,7 @@ public final class BuiltinTypeRegistry { ) ); - ctx.getModules().putIfAbsent("BuiltinUtils", utils); + // 注册 BuiltinUtils 到上下文的模块表(若已存在则不重复添加) + ctx.getModules().putIfAbsent("os", utils); } } From 7a52c7dd78f766114669d78e132a68d8acae06a9 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 16:56:18 +0800 Subject: [PATCH 18/36] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20syscall=20?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=9A=84=20VM=E6=8C=87=E4=BB=A4=E7=94=9F?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增对 syscall 调用的特殊处理逻辑 - 支持 IRConstant 直接字面量和虚拟寄存器绑定的字符串常量作为 syscall 子命令 - 实现了 syscall 调用中参数压栈和 SYSCALL指令生成的逻辑 -优化了普通函数调用的指令生成流程 --- .../backend/generator/CallGenerator.java | 113 ++++++++++++++++-- 1 file changed, 101 insertions(+), 12 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 84fe8cf..0f7c9c8 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 @@ -4,53 +4,142 @@ import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; import org.jcnc.snow.compiler.backend.utils.OpHelper; import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable; +import org.jcnc.snow.compiler.ir.core.IRValue; import org.jcnc.snow.compiler.ir.instruction.CallInstruction; +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.Map; +import java.util.*; /** - * 将 IR CallInstruction 生成 VM 指令 + * CallGenerator - 将 IR {@code CallInstruction} 生成 VM 指令 + * + *

          + * 本类负责将中间表示(IR)中的函数调用指令 {@link CallInstruction} 转换为虚拟机(VM)指令。 + * 支持普通函数调用和特殊的 syscall 指令转换。 + *

          + * + *

          + * 能力说明: + *

            + *
          • 支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量,直接降级为 {@code SYSCALL <SUBCMD>} 指令。
          • + *
          • 对普通函数,完成参数加载、调用、返回值保存等指令生成。
          • + *
          + *

          */ public class CallGenerator implements InstructionGenerator { + /** + * <虚拟寄存器 id, 对应的字符串常量> + *

          用于记录虚拟寄存器与其绑定字符串常量的映射。由 {@link LoadConstGenerator} 在编译期间填充。

          + */ + private static final Map STRING_CONST_POOL = new HashMap<>(); + + /** + * 注册字符串常量到虚拟寄存器 + *

          供 {@link LoadConstGenerator} 在加载字符串常量时调用。

          + * + * @param regId 虚拟寄存器 id + * @param value 字符串常量值 + */ + public static void registerStringConst(int regId, String value) { + STRING_CONST_POOL.put(regId, value); + } + + /** + * 返回本生成器支持的 IR 指令类型(CallInstruction) + */ @Override public Class supportedClass() { return CallInstruction.class; } + /** + * 生成 VM 指令的主逻辑 + * + * @param ins 当前 IR 指令(函数调用) + * @param out 指令输出构建器 + * @param slotMap IR 虚拟寄存器与物理槽位映射 + * @param currentFn 当前函数名 + */ @Override public void generate(CallInstruction ins, VMProgramBuilder out, Map slotMap, String currentFn) { - /* 1. 推断返回值类型(用于非 void 情况下的 I/F/D/L_STORE) */ - char retType = 'I'; + /* ========== 特殊处理 syscall 调用 ========== */ + if ("syscall".equals(ins.getFunctionName()) || + ins.getFunctionName().endsWith(".syscall")) { + + List args = ins.getArguments(); + if (args.isEmpty()) { + throw new IllegalStateException("syscall 需要子命令参数"); + } + + // ---------- 0. 解析 syscall 子命令 ---------- + // 子命令支持 IRConstant(直接字面量)或虚拟寄存器(需已绑定字符串) + String subcmd; + IRValue first = args.getFirst(); + + if (first instanceof IRConstant(Object value)) { // 直接字面量 + if (!(value instanceof String s)) + throw new IllegalStateException("syscall 第一个参数必须是字符串常量"); + subcmd = s.toUpperCase(Locale.ROOT); + + } else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器 + // 从常量池中查找是否已绑定字符串 + subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id())) + .orElseThrow(() -> + new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr)); + subcmd = subcmd.toUpperCase(Locale.ROOT); + + } else { + throw new IllegalStateException("syscall 第一个参数必须是字符串常量"); + } + + // ---------- 1. 压栈其余 syscall 参数(index 1 开始) ---------- + for (int i = 1; i < args.size(); i++) { + IRVirtualRegister vr = (IRVirtualRegister) args.get(i); + int slotId = slotMap.get(vr); + char t = out.getSlotType(slotId); + if (t == '\0') t = 'I'; // 默认整型 + out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId); + } + + // ---------- 2. 生成 SYSCALL 指令 ---------- + out.emit(VMOpCode.SYSCALL + " " + subcmd); + return; // syscall 无返回值,直接返回 + } + + /* ========== 普通函数调用 ========== */ + + // ---------- 1. 推断返回值类型(非 void 返回时用) ---------- + char retType = 'I'; // 默认为整型 if (!ins.getArguments().isEmpty()) { int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst()); retType = out.getSlotType(firstSlot); if (retType == '\0') retType = 'I'; } - /* 2. 依次加载实参 */ + // ---------- 2. 加载全部实参 ---------- for (var arg : ins.getArguments()) { int slotId = slotMap.get((IRVirtualRegister) arg); char t = out.getSlotType(slotId); - if (t == '\0') t = 'I'; + if (t == '\0') t = 'I'; // 默认整型 out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId); } - /* 3. 发出 CALL 指令 */ + // ---------- 3. 发出 CALL 指令 ---------- out.emitCall(ins.getFunctionName(), ins.getArguments().size()); - /* 3.5 若被调用函数返回 void,则无需保存返回值 */ - String rt = GlobalFunctionTable.getReturnType(ins.getFunctionName()); - if ("void".equals(rt)) { - return; // 直接结束,无 _STORE + // ---------- 3.5 如果为 void 返回直接结束 ---------- + if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) { + return; } - /* 4. 保存返回值到目标槽 */ + // ---------- 4. 保存返回值 ---------- int destSlot = slotMap.get(ins.getDest()); out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot); out.setSlotType(destSlot, retType); From 970976ecc5ad72e031b2167437056c5480182dc7 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 16:57:50 +0800 Subject: [PATCH 19/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20VM=E9=80=80?= =?UTF-8?q?=E5=87=BA=E6=97=B6=E7=9A=84=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 VMLauncher 启动后增加提示信息 "=== Launching VM ===" - 在 VM 退出后增加提示信息 "=== VM exited ===" --- src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java index f570bee..844c2fc 100644 --- a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java +++ b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java @@ -220,6 +220,7 @@ public final class CompileTask implements Task { if (runAfterCompile) { System.out.println("\n=== Launching VM ==="); VMLauncher.main(new String[]{outputFile.toString()}); + System.out.println("\n=== VM exited ==="); } return 0; From 3aef7cd9065fdaf4123a4ff0e9cbe3baca029491 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:06:40 +0800 Subject: [PATCH 20/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20Expressi?= =?UTF-8?q?onBuilder=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化了代码结构,提高了代码的可读性和可维护性 - 添加了详细的注释,解释了各个方法的功能和实现细节 - 改进了对不同表达式类型的处理逻辑,增强了表达式构建的能力 - 优化了寄存器的使用和管理,提高了 IR 指令生成的效率 --- .../ir/builder/ExpressionBuilder.java | 319 ++++++++++-------- 1 file changed, 171 insertions(+), 148 deletions(-) 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 31e2973..58eccfc 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 @@ -5,130 +5,117 @@ import org.jcnc.snow.compiler.ir.instruction.CallInstruction; import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction; import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction; import org.jcnc.snow.compiler.ir.utils.ComparisonUtils; +import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; import org.jcnc.snow.compiler.ir.value.IRConstant; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; -import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import java.util.*; /** - * 表达式构建器 + * ExpressionBuilder - 表达式 → IR 构建器 + * *

          - * 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器, - * 是编译器IR生成阶段的核心工具。 - *
          - * 主要职责包括: + * 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。 + * 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。 + *

          + * + *

          + * 主要功能: *

            - *
          • 将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列
          • - *
          • 管理并分配虚拟寄存器,保证IR操作的数据流正确
          • + *
          • 将表达式节点映射为虚拟寄存器
          • + *
          • 为每种表达式类型生成对应 IR 指令
          • + *
          • 支持表达式嵌套的递归构建
          • + *
          • 支持写入指定目标寄存器,避免冗余的 move 指令
          • *
          - *

          + *

          */ public record ExpressionBuilder(IRContext ctx) { + /* ───────────────── 顶层入口 ───────────────── */ + /** - * 构建并返回某个表达式节点对应的虚拟寄存器。 + * 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。 * - *

          会根据节点的实际类型分别处理: - *

            - *
          • 数字字面量: 新建常量寄存器
          • - *
          • 字符串字面量: 新建常量寄存器(字符串类型)
          • - *
          • 布尔字面量: 生成值为 0 或 1 的常量寄存器
          • - *
          • 标识符: 查找当前作用域中的寄存器
          • - *
          • 二元表达式: 递归处理子表达式并进行相应运算
          • - *
          • 一元运算符: - *
              - *
            • -x(取负,生成 NEG_I32 指令)与
            • - *
            • !x(逻辑非,转换为 x == 0 比较指令)
            • - *
            - *
          • - *
          • 函数调用: 生成对应的Call指令
          • - *
          • 其它类型不支持,抛出异常
          • - *
          + *

          + * 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。 + *

          * - * @param expr 要转换的表达式AST节点 - * @return 该表达式的计算结果寄存器 - * @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型 + * @param expr 任意 AST 表达式节点 + * @return 存储该表达式结果的虚拟寄存器 + * @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符 */ public IRVirtualRegister build(ExpressionNode expr) { return switch (expr) { - // 数字字面量 + // 数字字面量,例如 123、3.14 case NumberLiteralNode n -> buildNumberLiteral(n.value()); - // 字符串字面量 + // 字符串字面量,例如 "abc" case StringLiteralNode s -> buildStringLiteral(s.value()); - // 布尔字面量 + // 布尔字面量,例如 true / false 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()); yield reg; } - // 二元表达式 + // 二元表达式(如 a+b, x==y) case BinaryExpressionNode bin -> buildBinary(bin); - // 函数调用 - case CallExpressionNode call -> buildCall(call); - // 一元表达式 - case UnaryExpressionNode u -> buildUnary(u); + // 函数/方法调用表达式 + case CallExpressionNode call -> buildCall(call); + // 一元表达式(如 -a, !a) + case UnaryExpressionNode un -> buildUnary(un); + + // 默认分支:遇到未知表达式类型则直接抛异常 default -> throw new IllegalStateException( "不支持的表达式类型: " + expr.getClass().getSimpleName()); }; } - /** 处理一元表达式 */ - private IRVirtualRegister buildUnary(UnaryExpressionNode un) { - String op = un.operator(); - IRVirtualRegister val = build(un.operand()); - - // -x → NEG_*(根据类型自动选择位宽) - if (op.equals("-")) { - IRVirtualRegister dest = ctx.newRegister(); - IROpCode code = ExpressionUtils.negOp(un.operand()); - ctx.addInstruction(new UnaryOperationInstruction(code, dest, val)); - return dest; - } - - // !x → (x == 0) - if (op.equals("!")) { - IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0); - return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, val, zero); - } - - throw new IllegalStateException("未知一元运算符: " + op); - } + /* ───────────────── 写入指定寄存器 ───────────────── */ /** - * 直接将表达式计算结果写入指定的目标寄存器(dest)。 - *

          - * 与{@link #build(ExpressionNode)}类似,但支持目标寄存器复用(避免不必要的move)。 + * 生成表达式,并将其结果直接写入目标寄存器,避免冗余的 move 操作。 * - * @param node 表达式AST节点 - * @param dest 目标寄存器 - * @throws IllegalStateException 未定义标识符/不支持的表达式类型时报错 + *

          + * 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。 + *

          + * + * @param node 要生成的表达式节点 + * @param dest 目标虚拟寄存器(用于存储结果) */ public void buildInto(ExpressionNode node, IRVirtualRegister dest) { switch (node) { - // 数字字面量,直接加载到目标寄存器 + // 数字字面量:生成 loadConst 指令写入目标寄存器 case NumberLiteralNode n -> - InstructionFactory.loadConstInto(ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value())); - // 字符串字面量,直接加载到目标寄存器 + InstructionFactory.loadConstInto( + ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value())); + + // 字符串字面量:生成 loadConst 指令写入目标寄存器 case StringLiteralNode s -> - InstructionFactory.loadConstInto(ctx, dest, new IRConstant(s.value())); - // 布尔字面量,直接加载到目标寄存器 + 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)); - // 标识符,查找并move到目标寄存器 + InstructionFactory.loadConstInto( + ctx, dest, new IRConstant(b.getValue() ? 1 : 0)); + + // 标识符:查表获取原寄存器,然后 move 到目标寄存器 case IdentifierNode id -> { IRVirtualRegister src = ctx.getScope().lookup(id.name()); - if (src == null) throw new IllegalStateException("未定义标识符: " + id.name()); + if (src == null) + throw new IllegalStateException("未定义标识符: " + id.name()); InstructionFactory.move(ctx, src, dest); } - // 二元表达式,直接写入目标寄存器 + + // 二元表达式:递归生成并写入目标寄存器 case BinaryExpressionNode bin -> buildBinaryInto(bin, dest); - // 其他表达式,先递归生成寄存器,再move到目标寄存器 + + // 其它复杂情况:先 build 到新寄存器,再 move 到目标寄存器 default -> { IRVirtualRegister tmp = build(node); InstructionFactory.move(ctx, tmp, dest); @@ -136,41 +123,100 @@ public record ExpressionBuilder(IRContext ctx) { } } + /* ───────────────── 具体表达式类型 ───────────────── */ + /** - * 构建二元表达式的IR,生成新寄存器存储结果。 - *

          - * 先递归构建左右操作数,之后根据操作符类别(算术或比较)决定生成的IR操作码, - * 并生成对应的二元运算指令。 + * 一元表达式构建 * - * @param bin 二元表达式节点 - * @return 存放结果的虚拟寄存器 + *

          + * 支持算术取负(-a)、逻辑非(!a)等一元运算符。 + *

          + * + * @param un 一元表达式节点 + * @return 结果存储的新分配虚拟寄存器 */ - private IRVirtualRegister buildBinary(BinaryExpressionNode bin) { - String op = bin.operator(); - IRVirtualRegister left = build(bin.left()); - IRVirtualRegister right = build(bin.right()); + private IRVirtualRegister buildUnary(UnaryExpressionNode un) { + // 递归生成操作数的寄存器 + IRVirtualRegister src = build(un.operand()); + // 分配目标寄存器 + IRVirtualRegister dest = ctx.newRegister(); - // 1. 比较运算 - if (ComparisonUtils.isComparisonOperator(op)) { - return InstructionFactory.binOp( - ctx, - ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()), - left, right); + switch (un.operator()) { + // 算术负号:生成取负指令 + case "-" -> ctx.addInstruction( + new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src)); + // 逻辑非:等价于 a == 0,生成比较指令 + case "!" -> { + IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0); + return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero); + } + // 其它一元运算符不支持,抛异常 + default -> throw new IllegalStateException("未知一元运算符: " + un.operator()); } - - // 2. 其他算术 / 逻辑运算 - IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right()); - if (code == null) throw new IllegalStateException("不支持的运算符: " + op); - return InstructionFactory.binOp(ctx, code, left, right); + return dest; } /** - * 将二元表达式的结果直接写入指定寄存器dest。 - *

          - * 结构与{@link #buildBinary(BinaryExpressionNode)}类似,但不会新分配寄存器。 + * 构建函数或方法调用表达式。 + * + * @param call AST 调用表达式节点 + * @return 存储调用结果的虚拟寄存器 + */ + private IRVirtualRegister buildCall(CallExpressionNode call) { + // 递归生成所有参数(实参)对应的寄存器 + List argv = call.arguments().stream().map(this::build).toList(); + + // 解析被调用目标名,支持普通函数/成员方法 + String callee = switch (call.callee()) { + // 成员方法调用,例如 obj.foo() + case MemberExpressionNode m when m.object() instanceof IdentifierNode id + -> id.name() + "." + m.member(); + // 普通函数调用 + case IdentifierNode id -> id.name(); + // 其它情况暂不支持 + default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName()); + }; + + // 为返回值分配新寄存器,生成 Call 指令 + IRVirtualRegister dest = ctx.newRegister(); + ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv))); + return dest; + } + + /** + * 二元表达式构建,结果存储到新寄存器。 + *
          + * 支持算术、位运算与比较(==, !=, >, <, ...)。 * * @param bin 二元表达式节点 - * @param dest 目标寄存器 + * @return 存储表达式结果的虚拟寄存器 + */ + private IRVirtualRegister buildBinary(BinaryExpressionNode bin) { + // 递归生成左、右子表达式的寄存器 + IRVirtualRegister a = build(bin.left()); + IRVirtualRegister b = build(bin.right()); + String op = bin.operator(); + + // 比较运算符(==、!=、>、< 等),需要生成条件跳转或布尔值寄存器 + if (ComparisonUtils.isComparisonOperator(op)) { + return InstructionFactory.binOp( + ctx, + // 通过比较工具获得合适的 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); + return InstructionFactory.binOp(ctx, code, a, b); + } + + /** + * 二元表达式构建,结果直接写入目标寄存器(用于赋值左值等优化场景)。 + * + * @param bin 二元表达式节点 + * @param dest 目标虚拟寄存器 */ private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) { IRVirtualRegister a = build(bin.left()); @@ -189,67 +235,44 @@ public record ExpressionBuilder(IRContext ctx) { } } - /** - * 处理函数调用表达式,生成对应的Call指令和目标寄存器。 - *

          - * 支持普通标识符调用和成员调用(如 mod.func),会为每个参数依次生成子表达式的寄存器。 - * - * @param call 调用表达式AST节点 - * @return 返回结果存放的寄存器 - */ - private IRVirtualRegister buildCall(CallExpressionNode call) { - // 递归构建所有参数的寄存器 - List argv = call.arguments().stream() - .map(this::build) - .toList(); - // 获取完整调用目标名称(支持成员/模块调用和普通调用) - String fullName = switch (call.callee()) { - case MemberExpressionNode member when member.object() instanceof IdentifierNode _ -> - ((IdentifierNode)member.object()).name() + "." + member.member(); - case IdentifierNode id -> id.name(); - default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName()); - }; - // 申请目标寄存器 - IRVirtualRegister dest = ctx.newRegister(); - // 添加Call指令到IR上下文 - ctx.addInstruction(new CallInstruction(dest, fullName, new ArrayList<>(argv))); - return dest; - } + /* ───────────────── 字面量辅助方法 ───────────────── */ /** - * 处理数字字面量,生成常量寄存器和加载指令。 - *

          - * 会将字符串型字面量(如 "123", "1.0f")解析为具体的IRConstant, - * 并分配一个新的虚拟寄存器来存放该常量。 + * 构建数字字面量表达式(如 123),分配新寄存器并生成 LoadConst 指令。 * - * @param value 字面量字符串 - * @return 存放该常量的寄存器 + * @param value 字面量文本(字符串格式) + * @return 存储该字面量的寄存器 */ private IRVirtualRegister buildNumberLiteral(String value) { - IRConstant constant = ExpressionUtils.buildNumberConstant(ctx, value); - IRVirtualRegister reg = ctx.newRegister(); - ctx.addInstruction(new LoadConstInstruction(reg, constant)); - return reg; + IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value); + IRVirtualRegister r = ctx.newRegister(); + ctx.addInstruction(new LoadConstInstruction(r, c)); + return r; } /** - * 处理字符串字面量,生成常量寄存器和加载指令。 + * 构建字符串字面量表达式,分配新寄存器并生成 LoadConst 指令。 * * @param value 字符串内容 - * @return 存放该字符串常量的寄存器 + * @return 存储该字符串的寄存器 */ private IRVirtualRegister buildStringLiteral(String value) { - IRConstant constant = new IRConstant(value); - IRVirtualRegister reg = ctx.newRegister(); - ctx.addInstruction(new LoadConstInstruction(reg, constant)); - return reg; + IRConstant c = new IRConstant(value); + IRVirtualRegister r = ctx.newRegister(); + ctx.addInstruction(new LoadConstInstruction(r, c)); + return r; } - /** 布尔字面量 → CONST (true=1,false=0)*/ - private IRVirtualRegister buildBoolLiteral(boolean value) { - IRConstant constant = new IRConstant(value ? 1 : 0); - IRVirtualRegister reg = ctx.newRegister(); - ctx.addInstruction(new LoadConstInstruction(reg, constant)); - return reg; + /** + * 构建布尔字面量表达式(true/false),分配新寄存器并生成 LoadConst 指令(1 表示 true,0 表示 false)。 + * + * @param v 布尔值 + * @return 存储 1/0 的寄存器 + */ + private IRVirtualRegister buildBoolLiteral(boolean v) { + IRConstant c = new IRConstant(v ? 1 : 0); + IRVirtualRegister r = ctx.newRegister(); + ctx.addInstruction(new LoadConstInstruction(r, c)); + return r; } } From 6a339149f11f6584959cb7535b868a65c2eff744 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:09:26 +0800 Subject: [PATCH 21/36] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=20LoadConstGen?= =?UTF-8?q?erator=20=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化类注释,明确类的功能和额外支持的特性 - 重构 generate 方法,提高代码可读性和维护性- 增加对字符串常量的处理,支持 syscall 降级场景 - 完善类型前缀处理,增加 'R' 前缀用于字符串常量 --- .../backend/generator/LoadConstGenerator.java | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java index 760422a..a24f6d9 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java @@ -10,16 +10,18 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import java.util.Map; /** - * 常量加载指令生成器 - * 该类用于生成将常量加载到虚拟机寄存器的指令,包括 PUSH 常量值和 STORE 到指定槽位, - * 并为每个槽位设置正确的类型前缀(如 'I', 'L', 'F' 等)。 + * LoadConstGenerator - 将 IR {@code LoadConstInstruction} 生成 VM 指令 + * + *

          + * 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令。 + * 额外支持:如果常量类型为 {@code String},会同步登记到 + * {@link CallGenerator} 的字符串常量池,方便 syscall 降级场景使用。 + *

          */ public class LoadConstGenerator implements InstructionGenerator { /** - * 返回本生成器支持的指令类型,即 LoadConstInstruction。 - * - * @return 支持的指令类型的 Class 对象 + * 指定本生成器支持的 IR 指令类型(LoadConstInstruction) */ @Override public Class supportedClass() { @@ -27,49 +29,49 @@ public class LoadConstGenerator implements InstructionGenerator slotMap, String currentFn) { - // 1. 获取常量值(第一个操作数必为常量) + + /* 1. 获取常量值 */ IRConstant constant = (IRConstant) ins.operands().getFirst(); Object value = constant.value(); - // 2. 生成 PUSH 指令,将常量值推入操作数栈 - // 通过 OpHelper 辅助方法获取合适的数据类型前缀 - String pushOp = OpHelper.pushOpcodeFor(value); - out.emit(pushOp + " " + value); + /* 2. 生成 PUSH 指令,将常量值入栈 */ + out.emit(OpHelper.pushOpcodeFor(value) + " " + value); - // 3. 生成 STORE 指令,将栈顶的值存入对应槽位(寄存器) - // 同样通过 OpHelper 获取对应类型的 STORE 指令 - String storeOp = OpHelper.storeOpcodeFor(value); - // 获取目标虚拟寄存器对应的槽位编号 + /* 3. STORE 到目标槽位 */ int slot = slotMap.get(ins.dest()); - out.emit(storeOp + " " + slot); + out.emit(OpHelper.storeOpcodeFor(value) + " " + slot); - // 4. 根据常量的 Java 类型,为槽位设置正确的前缀字符 - // 这样在后续类型检查/运行时可用,常见前缀如 'I', 'L', 'F', 'D', 'S', 'B' + /* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */ char prefix = switch (value) { - case Integer _ -> 'I'; // 整型 - case Long _ -> 'L'; // 长整型 - case Short _ -> 'S'; // 短整型 - case Byte _ -> 'B'; // 字节型 - case Double _ -> 'D'; // 双精度浮点型 - case Float _ -> 'F'; // 单精度浮点型 - case Boolean _ -> 'I'; // 布尔(作为 0/1 整型存储) - case null, default -> - throw new IllegalStateException("Unknown const type: " + (value != null ? value.getClass() : null)); + case Integer _ -> 'I'; // 整型 + case Long _ -> 'L'; // 长整型 + case Short _ -> 'S'; // 短整型 + case Byte _ -> 'B'; // 字节型 + case Double _ -> 'D'; // 双精度 + case Float _ -> 'F'; // 单精度 + case Boolean _ -> 'I'; // 布尔类型用 I 处理 + case String _ -> 'R'; // 字符串常量 + case null, default -> // 其它类型异常 + throw new IllegalStateException("未知的常量类型: " + + (value != null ? value.getClass() : null)); }; - - // 写入槽位类型映射表 out.setSlotType(slot, prefix); + + /* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */ + if (value instanceof String s) { + CallGenerator.registerStringConst(ins.dest().id(), s); + } } } From 8eeed6f6b999c059c2159eb589ae9b17491e0f2f Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:10:20 +0800 Subject: [PATCH 22/36] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=95=B0=E6=A0=88=E6=89=93=E5=8D=B0=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在操作数栈状态打印时,增加空行以提高可读性 --- src/main/java/org/jcnc/snow/vm/module/OperandStack.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/vm/module/OperandStack.java b/src/main/java/org/jcnc/snow/vm/module/OperandStack.java index 347387f..1e0d158 100644 --- a/src/main/java/org/jcnc/snow/vm/module/OperandStack.java +++ b/src/main/java/org/jcnc/snow/vm/module/OperandStack.java @@ -84,7 +84,7 @@ public class OperandStack { *

          */ public void printOperandStack() { - LoggingUtils.logInfo("Operand Stack state:", stack + "\n"); + LoggingUtils.logInfo("\n\nOperand Stack state:", stack + "\n"); } /** From b4c933e3d4c310969a6ac265531e23d6b9d4c6fd Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:10:42 +0800 Subject: [PATCH 23/36] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=20Demo14=20?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改导入模块,使用 os 模块替代 BuiltinUtils - 更新 syscall 调用,增加表达式计算 --- playground/Demo/Demo14/Main.snow | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/Demo/Demo14/Main.snow b/playground/Demo/Demo14/Main.snow index 42049ea..8e5baff 100644 --- a/playground/Demo/Demo14/Main.snow +++ b/playground/Demo/Demo14/Main.snow @@ -1,10 +1,10 @@ module: Main - import: BuiltinUtils + import: os function: main return_type: void body: - syscall("PRINT",1) + syscall("PRINT",2222+1) end body end function end module \ No newline at end of file From e7c7451004d8b5d99ee68212771332bf02bfe385 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:16:54 +0800 Subject: [PATCH 24/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20CallComm?= =?UTF-8?q?and=20=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=20-=20=E9=87=8D=E6=96=B0=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E7=B1=BB=E6=96=87=E6=A1=A3=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=20CallCommand=20=E5=8A=9F=E8=83=BD=E5=92=8C=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E7=9A=84=E8=AF=A6=E7=BB=86=E6=8F=8F=E8=BF=B0=20-=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20execute=20=E6=96=B9=E6=B3=95=E7=9A=84=E8=AF=A6?= =?UTF-8?q?=E7=BB=86=E6=B3=A8=E9=87=8A=EF=BC=8C=E6=98=8E=E7=A1=AE=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=92=8C=E8=BF=94=E5=9B=9E=E5=80=BC=E7=9A=84=E7=94=A8?= =?UTF-8?q?=E9=80=94=20-=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E6=8F=90=E9=AB=98=E5=8F=AF=E8=AF=BB=E6=80=A7?= =?UTF-8?q?=E5=92=8C=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vm/commands/flow/control/CallCommand.java | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java index befbfea..1171885 100644 --- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java +++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java @@ -5,12 +5,48 @@ import org.jcnc.snow.vm.interfaces.Command; import org.jcnc.snow.vm.module.*; /** - * CALL addr nArgs — pushes a new stack-frame, transfers the specified - * argument count from the operand stack into the callee’s local slots - * 0‥n-1 (left-to-right), then jumps to {@code addr}. + * The CallCommand class implements the {@link Command} interface and represents a subroutine/function call + * instruction in the virtual machine. + *

          + * This command facilitates method invocation by creating a new stack frame, transferring arguments + * from the operand stack to the callee's local variable store, and jumping to the specified target address. + *

          + * + *

          Specific behavior:

          + *
            + *
          • Parses the target address and the number of arguments from the instruction parameters.
          • + *
          • Validates the operands and checks for correct argument count.
          • + *
          • Builds a new local variable store for the callee and transfers arguments from the operand stack + * (left-to-right order, where the top of the stack is the last argument).
          • + *
          • Pushes a new stack frame onto the call stack, saving the return address and local variables.
          • + *
          • Jumps to the specified target address to begin execution of the callee function.
          • + *
          • If any error occurs (e.g., malformed operands, stack underflow), an exception is thrown.
          • + *
          */ public class CallCommand implements Command { + /** + * Executes the CALL instruction, initiating a subroutine/function call within the virtual machine. + *

          + * This method handles the creation of a new stack frame for the callee, argument passing, + * and control transfer to the target function address. + *

          + * + * @param parts The instruction parameters. Must include: + *
            + *
          • {@code parts[0]}: The "CALL" operator.
          • + *
          • {@code parts[1]}: The target address of the callee function.
          • + *
          • {@code parts[2]}: The number of arguments to pass.
          • + *
          + * @param currentPC The current program counter, used to record the return address for after the call. + * @param operandStack The operand stack manager. Arguments are popped from this stack. + * @param callerLVS The local variable store of the caller function (not directly modified here). + * @param callStack The virtual machine's call stack manager, used to push the new stack frame. + * @return The new program counter value, which is the address of the callee function (i.e., jump target). + * The VM should transfer control to this address after setting up the call frame. + * @throws IllegalArgumentException If the instruction parameters are malformed or missing. + * @throws IllegalStateException If the operand stack does not contain enough arguments. + */ @Override public int execute(String[] parts, int currentPC, From 6098a290b1d67686df16098ac4dc6f88ea111378 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:18:30 +0800 Subject: [PATCH 25/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20R=5FLOAD?= =?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 RLoadCommand 类,实现 Command接口 - 该指令用于从局部变量表中加载引用对象,并将其推入操作数栈- 指令格式:R_LOAD - 其中 是局部变量表中的索引 - 执行过程包括解析索引、获取引用、推入栈顶和更新程序计数器 --- .../vm/commands/ref/control/RLoadCommand.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java new file mode 100644 index 0000000..d7e314d --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java @@ -0,0 +1,54 @@ +package org.jcnc.snow.vm.commands.ref.control; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code RLoadCommand} class implements the {@link Command} interface and represents the + * reference load instruction ({@code R_LOAD}) in the virtual machine. + * + *

          + * This instruction loads a reference object from the current stack frame’s local variable store + * at the specified slot and pushes it onto the operand stack. + *

          + * + *

          Instruction format: {@code R_LOAD }

          + *
            + *
          • {@code }: The index in the local variable table to load the reference from.
          • + *
          + * + *

          Behavior:

          + *
            + *
          • Parses the slot index from the instruction parameters.
          • + *
          • Fetches the reference object stored at the specified slot in the current stack frame's local variable store.
          • + *
          • Pushes the fetched reference onto the operand stack.
          • + *
          • Increments the program counter to the next instruction.
          • + *
          + */ +public final class RLoadCommand implements Command { + + /** + * Executes the {@code R_LOAD} instruction, loading a reference from the local variable table and pushing it onto the operand stack. + * + * @param parts The instruction parameters. {@code parts[0]} is the operator ("R_LOAD"), {@code parts[1]} is the slot index. + * @param pc The current program counter value, indicating the instruction address being executed. + * @param stack The operand stack manager. The loaded reference will be pushed onto this stack. + * @param lvs The local variable store. (Not used directly, as this command uses the store from the current stack frame.) + * @param cs The call stack manager. The reference will be loaded from the local variable store of the top stack frame. + * @return The next program counter value ({@code pc + 1}), pointing to the next instruction. + * @throws NumberFormatException if the slot parameter cannot be parsed as an integer. + */ + @Override + public int execute(String[] parts, int pc, + OperandStack stack, + LocalVariableStore lvs, + CallStack cs) { + + int slot = Integer.parseInt(parts[1]); + Object v = cs.peekFrame().getLocalVariableStore().getVariable(slot); + stack.push(v); + return pc + 1; + } +} From a7117717bd76ef791fa565e9e990336c4e10e8fa Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:19:05 +0800 Subject: [PATCH 26/36] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E5=B7=A5=E5=8E=82=E4=B8=AD=E7=9A=84=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 RLoadCommand、RPushCommand 和 RStoreCommand 的导入路径从 type.control.ref 改为 ref.control - 这个改动统一了引用类型命令的包结构,与其他类型命令保持一致 --- .../java/org/jcnc/snow/vm/factories/CommandFactory.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java index 71479a3..fc4a3ed 100644 --- a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java +++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java @@ -6,9 +6,9 @@ import org.jcnc.snow.vm.commands.type.control.double64.*; import org.jcnc.snow.vm.commands.type.control.float32.*; import org.jcnc.snow.vm.commands.type.control.int32.*; import org.jcnc.snow.vm.commands.type.control.long64.*; -import org.jcnc.snow.vm.commands.type.control.ref.RLoadCommand; -import org.jcnc.snow.vm.commands.type.control.ref.RPushCommand; -import org.jcnc.snow.vm.commands.type.control.ref.RStoreCommand; +import org.jcnc.snow.vm.commands.ref.control.RLoadCommand; +import org.jcnc.snow.vm.commands.ref.control.RPushCommand; +import org.jcnc.snow.vm.commands.ref.control.RStoreCommand; import org.jcnc.snow.vm.commands.type.control.short16.*; import org.jcnc.snow.vm.commands.type.control.byte8.BAndCommand; import org.jcnc.snow.vm.commands.type.control.byte8.BOrCommand; From 5ef36d57009702a38722c6af72fa94ae5ff0778b Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:23:43 +0800 Subject: [PATCH 27/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20R=5FPUSH?= =?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 RPushCommand 类实现 Command 接口,用于处理引用类型推入操作 - 支持将字符串字面量等引用类型数据推入操作栈 --- .../vm/commands/ref/control/RPushCommand.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java 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 new file mode 100644 index 0000000..1386018 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java @@ -0,0 +1,63 @@ +package org.jcnc.snow.vm.commands.ref.control; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code RPushCommand} class implements the {@link Command} interface and represents the + * reference push instruction ({@code R_PUSH}) in the virtual machine. + * + *

          + * This instruction pushes a reference object, such as a String literal, onto the operand stack. + *

          + * + *

          Instruction format: {@code R_PUSH }

          + *
            + *
          • {@code }: The reference value (e.g., string) to be pushed onto the stack. + * If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.
          • + *
          + * + *

          Behavior:

          + *
            + *
          • Checks that the instruction has at least one parameter after the operator.
          • + *
          • Concatenates all parameters after {@code R_PUSH} into a single string (separated by spaces).
          • + *
          • Pushes the resulting string as a reference object onto the operand stack.
          • + *
          • Increments the program counter to the next instruction.
          • + *
          • Throws an {@code IllegalStateException} if the instruction is missing required parameters.
          • + *
          + */ +public final class RPushCommand implements Command { + + /** + * Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal) + * onto the operand stack. + * + * @param parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"), + * {@code parts[1..]} are the parts of the literal to be concatenated and pushed. + * @param pc The current program counter value, indicating the instruction address being executed. + * @param stack The operand stack manager. The literal will be pushed onto this stack. + * @param lvs The local variable store. (Not used in this instruction.) + * @param cs The call stack manager. (Not used in this instruction.) + * @return The next program counter value ({@code pc + 1}), pointing to the next instruction. + * @throws IllegalStateException if the instruction is missing required parameters. + */ + @Override + public int execute(String[] parts, int pc, + OperandStack stack, + LocalVariableStore lvs, + CallStack cs) { + + if (parts.length < 2) + throw new IllegalStateException("R_PUSH missing parameter"); + + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < parts.length; i++) { + if (i > 1) sb.append(' '); + sb.append(parts[i]); + } + stack.push(sb.toString()); + return pc + 1; + } +} From 33d89e99083f8244896d2e752c8a1b094caf54cb Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:24:57 +0800 Subject: [PATCH 28/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20R=5FSTORE?= =?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 RStoreCommand 类,实现 Command接口 - 添加 R_STORE指令的执行逻辑 - 更新虚拟机的指令集,支持 R_STORE 指令 --- .../commands/ref/control/RStoreCommand.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java new file mode 100644 index 0000000..0237908 --- /dev/null +++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.vm.commands.ref.control; + +import org.jcnc.snow.vm.interfaces.Command; +import org.jcnc.snow.vm.module.CallStack; +import org.jcnc.snow.vm.module.LocalVariableStore; +import org.jcnc.snow.vm.module.OperandStack; + +/** + * The {@code RStoreCommand} class implements the {@link Command} interface and represents the + * reference store instruction ({@code R_STORE}) in the virtual machine. + * + *

          + * This instruction pops a reference object from the top of the operand stack + * and stores it in the local variable table at the specified slot index of the current stack frame. + *

          + * + *

          Instruction format: {@code R_STORE }

          + *
            + *
          • {@code }: The index in the local variable table where the reference will be stored.
          • + *
          + * + *

          Behavior:

          + *
            + *
          • Parses the slot index from the instruction parameters.
          • + *
          • Pops a reference object from the operand stack.
          • + *
          • Stores the popped reference object into the local variable table of the current stack frame at the specified slot.
          • + *
          • Increments the program counter to the next instruction.
          • + *
          • If the operand stack is empty, a {@code java.util.EmptyStackException} may be thrown.
          • + *
          + */ +public final class RStoreCommand implements Command { + + /** + * Executes the {@code R_STORE} instruction, storing a reference object from the top of the operand stack + * into the local variable table of the current stack frame. + * + * @param parts The instruction parameters. {@code parts[0]} is the operator ("R_STORE"), + * {@code parts[1]} is the slot index. + * @param pc The current program counter value, indicating the instruction address being executed. + * @param stack The operand stack manager. The reference object will be popped from this stack. + * @param lvs The local variable store. (Not used directly, as the store from the current stack frame is used.) + * @param cs The call stack manager. The reference will be stored in the local variable store of the top stack frame. + * @return The next program counter value ({@code pc + 1}), pointing to the next instruction. + * @throws NumberFormatException if the slot parameter cannot be parsed as an integer. + * @throws java.util.EmptyStackException if the operand stack is empty when popping. + */ + @Override + public int execute(String[] parts, int pc, + OperandStack stack, + LocalVariableStore lvs, + CallStack cs) { + + int slot = Integer.parseInt(parts[1]); + Object v = stack.pop(); + cs.peekFrame().getLocalVariableStore().setVariable(slot, v); + return pc + 1; + } +} From b30b6aeaaa166414839063f2b26503c22dd44466 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 17:26:42 +0800 Subject: [PATCH 29/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E7=B1=BB=E5=9E=8B=E6=8E=A7=E5=88=B6=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 R_PUSH、R_LOAD 和 R_STORE 指令,用于处理对象引用类型 - 这些指令分别用于推送、加载和存储对象引用到操作栈或本地变量表 --- .../org/jcnc/snow/vm/engine/VMOpCode.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java index 8e1a880..c333e34 100644 --- a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java +++ b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java @@ -1,5 +1,8 @@ package org.jcnc.snow.vm.engine; +import org.jcnc.snow.vm.commands.ref.control.RLoadCommand; +import org.jcnc.snow.vm.commands.ref.control.RPushCommand; +import org.jcnc.snow.vm.commands.ref.control.RStoreCommand; import org.jcnc.snow.vm.commands.system.control.SyscallCommand; import org.jcnc.snow.vm.commands.type.control.byte8.*; import org.jcnc.snow.vm.commands.type.control.double64.*; @@ -2483,6 +2486,74 @@ public class VMOpCode { // endregion Double64 // endregion Conversion + // region Reference Control (0x00E0-0x00EF) + /** + * R_PUSH Opcode: Represents an operation that pushes an object reference (such as a String or any reference type) + * onto the operand stack. + *

          This opcode is implemented by the {@link RPushCommand} class, which defines its specific execution logic.

          + * + *

          Execution Steps:

          + *
            + *
          1. Parses the object reference literal (e.g., a string) from the instruction parameters.
          2. + *
          3. Creates or interprets the reference as an object instance if necessary.
          4. + *
          5. Pushes the reference object onto the operand stack.
          6. + *
          7. Increments the program counter (PC) to proceed with the next sequential instruction.
          8. + *
          + * + *

          This opcode is commonly used for:

          + *
            + *
          • Pushing string literals, objects, or other references needed for subsequent operations.
          • + *
          • Supplying parameters for method calls that expect reference types.
          • + *
          • Initializing the operand stack with reference values for computation or storage.
          • + *
          + */ + public static final int R_PUSH = 0x00E0; + /** + * R_LOAD Opcode: Represents an operation that loads an object reference from the local variable table + * and pushes it onto the operand stack. + *

          This opcode is implemented by the {@link RLoadCommand} class, which defines its specific execution logic.

          + * + *

          Execution Steps:

          + *
            + *
          1. Parses the target slot index from the instruction parameters.
          2. + *
          3. Retrieves the reference object stored at the specified slot in the local variable table + * of the current stack frame.
          4. + *
          5. Pushes the retrieved reference onto the operand stack.
          6. + *
          7. Increments the program counter (PC) to proceed with the next sequential instruction.
          8. + *
          + * + *

          This opcode is commonly used for:

          + *
            + *
          • Accessing local variables (such as function arguments or local object references) during method execution.
          • + *
          • Preparing reference values for operations or method invocations.
          • + *
          • Reusing objects previously stored in local variables.
          • + *
          + */ + public static final int R_LOAD = 0x00E1; + /** + * R_STORE Opcode: Represents an operation that pops an object reference from the top of the operand stack + * and stores it into the local variable table at the specified slot index. + *

          This opcode is implemented by the {@link RStoreCommand} class, which defines its specific execution logic.

          + * + *

          Execution Steps:

          + *
            + *
          1. Parses the target slot index from the instruction parameters.
          2. + *
          3. Pops the top reference object from the operand stack.
          4. + *
          5. Stores the popped reference object into the specified slot in the local variable table + * of the current stack frame.
          6. + *
          7. Increments the program counter (PC) to proceed with the next sequential instruction.
          8. + *
          + * + *

          This opcode is commonly used for:

          + *
            + *
          • Storing computation results or intermediate reference values for later use.
          • + *
          • Passing object references to local variables in preparation for further operations or method calls.
          • + *
          • Managing object lifetimes and ensuring correct referencing in scoped execution contexts.
          • + *
          + */ + public static final int R_STORE = 0x00E2; + // endregion + // region Stack Control (0x0100-0x01FF) /** * POP Opcode: Represents a stack operation that removes the top element from the operand stack. From a454eed26fd825969862d431b4fa5f900350be5b Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 22:52:40 +0800 Subject: [PATCH 30/36] =?UTF-8?q?test:=20=E9=87=8D=E6=9E=84=20Demo14=20?= =?UTF-8?q?=E6=BC=94=E7=A4=BA=E4=BB=A3=E7=A0=81=20-=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E4=BA=86=20Main.snow=20=E6=96=87=E4=BB=B6=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8=20-=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20OS.snow=20=E6=96=87=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20print=20=E5=87=BD=E6=95=B0=E5=B0=81?= =?UTF-8?q?=E8=A3=85=20-=20=E4=BF=AE=E6=94=B9=20Main.snow=EF=BC=8C?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=96=B0=E7=9A=84=20print=20=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=9B=BF=E4=BB=A3=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo/Demo14/Main.snow | 3 +-- playground/Demo/Demo14/OS.snow | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 playground/Demo/Demo14/OS.snow diff --git a/playground/Demo/Demo14/Main.snow b/playground/Demo/Demo14/Main.snow index 8e5baff..7ef2b05 100644 --- a/playground/Demo/Demo14/Main.snow +++ b/playground/Demo/Demo14/Main.snow @@ -1,10 +1,9 @@ module: Main import: os - function: main return_type: void body: - syscall("PRINT",2222+1) + print(222) end body end function end module \ No newline at end of file diff --git a/playground/Demo/Demo14/OS.snow b/playground/Demo/Demo14/OS.snow new file mode 100644 index 0000000..f985171 --- /dev/null +++ b/playground/Demo/Demo14/OS.snow @@ -0,0 +1,11 @@ +module: Main + import: os + function: print + parameter: + declare i1: int + return_type: void + body: + syscall("PRINT",i1) + end body + end function +end module \ No newline at end of file From 074f0b6809bec76c0547f0c467ead58d0e86ba68 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 22:54:49 +0800 Subject: [PATCH 31/36] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E5=BA=93?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=AE=9E=E7=8E=B0=E6=89=93=E5=8D=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/os/OS.snow | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 lib/os/OS.snow diff --git a/lib/os/OS.snow b/lib/os/OS.snow new file mode 100644 index 0000000..f985171 --- /dev/null +++ b/lib/os/OS.snow @@ -0,0 +1,11 @@ +module: Main + import: os + function: print + parameter: + declare i1: int + return_type: void + body: + syscall("PRINT",i1) + end body + end function +end module \ No newline at end of file From e84aedc100f44de9646439e180ddae138f565470 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 23:13:50 +0800 Subject: [PATCH 32/36] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=90=8D=E7=A7=B0=E4=B8=BA=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/os/OS.snow | 2 +- playground/Demo/Demo14/OS.snow | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/os/OS.snow b/lib/os/OS.snow index f985171..6026d43 100644 --- a/lib/os/OS.snow +++ b/lib/os/OS.snow @@ -1,4 +1,4 @@ -module: Main +module: os import: os function: print parameter: diff --git a/playground/Demo/Demo14/OS.snow b/playground/Demo/Demo14/OS.snow index f985171..6026d43 100644 --- a/playground/Demo/Demo14/OS.snow +++ b/playground/Demo/Demo14/OS.snow @@ -1,4 +1,4 @@ -module: Main +module: os import: os function: print parameter: From 6d79e28c51c002ffe8f12ae64589829f4a26bf26 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Jul 2025 23:42:03 +0800 Subject: [PATCH 33/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20SyscallC?= =?UTF-8?q?ommand=20=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=B3=A8=E9=87=8A-=20=E9=87=8D=E6=96=B0=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E7=B1=BB=E7=BB=93=E6=9E=84=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新文档注释,使其更加清晰和详细 - 优化部分方法实现,提高可读性和可维护性 --- .../system/control/SyscallCommand.java | 261 ++++++------------ 1 file changed, 88 insertions(+), 173 deletions(-) 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 eebcd28..90caf8b 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 @@ -16,53 +16,47 @@ import java.util.*; import static java.nio.file.StandardOpenOption.*; /** - * {@code SyscallCommand} —— I/O syscall 子命令集合指令。 + * SyscallCommand —— 虚拟机系统调用(SYSCALL)指令实现。 * *

          - * 封装类 UNIX 文件描述符(File Descriptor, FD)操作、文件/网络 I/O、管道、fd 复制、多路复用(select)等系统调用能力, - * 基于 Java NIO 统一实现,供虚拟机指令集以 SYSCALL 形式扩展使用。 + * 本类负责将虚拟机指令集中的 SYSCALL 进行分派,模拟现实系统常见的文件、网络、管道、标准输出等操作, + * 通过操作数栈完成参数、返回值传递,并借助文件描述符表(FDTable)进行底层资源管理。 + * 所有 I/O 相关功能均基于 Java NIO 实现,兼容多种 I/O 场景。 *

          * - * 栈操作约定: + *

          参数与栈约定:

          *
            - *
          • 参数按“右值先入栈、左值后入栈”的顺序推入操作数栈,执行 {@code SYSCALL } 后出栈。
          • - *
          • 即:调用 SYSCALL OPEN path flags mode,入栈顺序为 path(先入)→ flags → mode(后入),出栈时 mode→flags→path 弹出。
          • + *
          • 所有调用参数,均按“右值先入、左值后入”顺序压入 {@link OperandStack}。
          • + *
          • SYSCALL 指令自动弹出参数并处理结果;返回值(如描述符、读取长度、是否成功等)压回栈顶。
          • *
          * - * 返回值说明: + *

          异常与失败处理:

          *
            - *
          • 成功:压入正值(如 FD、实际数据、字节数、0 表示成功)。
          • - *
          • 失败:统一压入 -1,后续可扩展为 errno 机制。
          • + *
          • 系统调用失败或遇到异常时,均向操作数栈压入 {@code -1},以便调用者统一检测。
          • *
          * - * 支持的子命令(部分): + *

          支持的子命令示例:

          *
            *
          • PRINT / PRINTLN —— 控制台输出
          • - *
          • OPEN / CLOSE / READ / WRITE / SEEK —— 文件 I/O 操作
          • - *
          • PIPE / DUP —— 管道和 FD 复制
          • - *
          • SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络套接字
          • - *
          • SELECT —— I/O 多路复用
          • + *
          • OPEN / CLOSE / READ / WRITE / SEEK —— 文件相关操作
          • + *
          • PIPE / DUP —— 管道与文件描述符复制
          • + *
          • SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络通信
          • + *
          • SELECT —— 多通道 I/O 就绪检测
          • *
          */ public class SyscallCommand implements Command { - - - /*--------------------------------------------------------------------------------*/ - /** - * 执行 SYSCALL 子命令: - *

          - * 按照 parts[1] 指定的 SYSCALL 子命令,结合虚拟机栈与资源表完成对应的系统调用模拟。 - * 支持基础文件、网络、管道等 I/O 操作,并对异常统一处理。 - *

          + * 分发并执行 SYSCALL 子命令,根据子命令类型从操作数栈取出参数、操作底层资源,并将结果压回栈顶。 * - * @param parts 指令字符串数组,parts[1] 为子命令 + * @param parts 指令及子命令参数分割数组,parts[1]为子命令名 * @param pc 当前指令计数器 * @param stack 操作数栈 * @param locals 局部变量表 * @param callStack 调用栈 - * @return 下一条指令的 pc 值(默认 pc+1) + * @return 下一条指令的 pc 值(通常为 pc+1) + * @throws IllegalArgumentException 缺少子命令参数时抛出 + * @throws UnsupportedOperationException 不支持的 SYSCALL 子命令时抛出 */ @Override public int execute(String[] parts, int pc, @@ -70,46 +64,27 @@ public class SyscallCommand implements Command { LocalVariableStore locals, CallStack callStack) { - if (parts.length < 2) - throw new IllegalArgumentException("SYSCALL needs sub-command"); + if (parts.length < 2) { + throw new IllegalArgumentException("SYSCALL missing subcommand"); + } String cmd = parts[1].toUpperCase(Locale.ROOT); try { switch (cmd) { - - /*==================== 文件 / 目录 ====================*/ - - /* - * OPEN —— 打开文件,返回文件描述符(File Descriptor, FD)。 - * 入栈(push)顺序: path(文件路径, String), flags(int), mode(int,文件权限,暂未用) - * 出栈(pop)顺序: mode → flags → path - * 成功返回 fd,失败返回 -1。 - */ + // 文件相关操作 case "OPEN" -> { - int mode = (Integer) stack.pop(); // 目前未用 + int mode = (Integer) stack.pop(); int flags = (Integer) stack.pop(); String path = String.valueOf(stack.pop()); FileChannel fc = FileChannel.open(Paths.get(path), flagsToOptions(flags)); stack.push(FDTable.register(fc)); } - /* - * CLOSE —— 关闭文件描述符。 - * 入栈顺序: fd(int) - * 出栈顺序: fd - * 返回 0 成功,-1 失败 - */ case "CLOSE" -> { int fd = (Integer) stack.pop(); FDTable.close(fd); stack.push(0); } - /* - * READ —— 从 fd 读取指定字节数。 - * 入栈顺序: fd(int), count(int,字节数) - * 出栈顺序: count → fd - * 返回:byte[](实际读取的数据,EOF 时长度为 0) - */ case "READ" -> { int count = (Integer) stack.pop(); int fd = (Integer) stack.pop(); @@ -126,12 +101,6 @@ public class SyscallCommand implements Command { buf.get(out); stack.push(out); } - /* - * WRITE —— 向 fd 写数据。 - * 入栈顺序: fd(int), data(byte[] 或 String) - * 出栈顺序: data → fd - * 返回写入字节数,失败 -1 - */ case "WRITE" -> { Object dataObj = stack.pop(); int fd = (Integer) stack.pop(); @@ -146,12 +115,6 @@ public class SyscallCommand implements Command { int written = wch.write(ByteBuffer.wrap(data)); stack.push(written); } - /* - * SEEK —— 文件定位,移动文件读写指针。 - * 入栈顺序: fd(int), offset(long/int), whence(int, 0=SET,1=CUR,2=END) - * 出栈顺序: whence → offset → fd - * 返回新位置(long),失败 -1 - */ case "SEEK" -> { int whence = (Integer) stack.pop(); long off = ((Number) stack.pop()).longValue(); @@ -165,58 +128,32 @@ public class SyscallCommand implements Command { case 0 -> sbc.position(off); case 1 -> sbc.position(sbc.position() + off); case 2 -> sbc.position(sbc.size() + off); - default -> throw new IllegalArgumentException("bad whence"); + default -> throw new IllegalArgumentException("Invalid offset type"); }; stack.push(newPos); } - /*==================== 管道 / FD 相关 ====================*/ - - /* - * PIPE —— 创建匿名管道。 - * 入栈顺序: (无) - * 出栈顺序: (无) - * 返回顺序: write fd(先压栈),read fd(后压栈) - */ + // 管道与描述符操作 case "PIPE" -> { Pipe p = Pipe.open(); - stack.push(FDTable.register(p.sink())); // write fd - stack.push(FDTable.register(p.source())); // read fd + stack.push(FDTable.register(p.sink())); + stack.push(FDTable.register(p.source())); } - /* - * DUP —— 复制一个已存在的 fd(dup)。 - * 入栈顺序: oldfd(int) - * 出栈顺序: oldfd - * 返回新的 fd,失败 -1 - */ case "DUP" -> { int oldfd = (Integer) stack.pop(); stack.push(FDTable.dup(oldfd)); } - /*==================== 网络相关 ====================*/ - - /* - * SOCKET —— 创建套接字,支持 stream/dgram。 - * 入栈顺序: domain(int, AF_INET=2等), type(int, 1=STREAM,2=DGRAM), protocol(int, 预留) - * 出栈顺序: protocol → type → domain - * 返回 fd - */ + // 网络相关 case "SOCKET" -> { - int proto = (Integer) stack.pop(); // 预留,暂不用 - int type = (Integer) stack.pop(); // 1=STREAM,2=DGRAM - int domain = (Integer) stack.pop(); // AF_INET=2…… + int proto = (Integer) stack.pop(); + int type = (Integer) stack.pop(); + int domain = (Integer) stack.pop(); Channel ch = (type == 1) ? SocketChannel.open() : DatagramChannel.open(); stack.push(FDTable.register(ch)); } - /* - * CONNECT —— 发起 TCP 连接。 - * 入栈顺序: fd(int), host(String), port(int) - * 出栈顺序: port → host → fd - * 返回 0 成功,-1 失败 - */ case "CONNECT" -> { int port = (Integer) stack.pop(); String host = String.valueOf(stack.pop()); @@ -225,14 +162,10 @@ public class SyscallCommand implements Command { if (ch instanceof SocketChannel sc) { sc.connect(new InetSocketAddress(host, port)); stack.push(0); - } else stack.push(-1); + } else { + stack.push(-1); + } } - /* - * BIND —— 绑定端口。 - * 入栈顺序: fd(int), host(String), port(int) - * 出栈顺序: port → host → fd - * 返回 0 成功,-1 失败 - */ case "BIND" -> { int port = (Integer) stack.pop(); String host = String.valueOf(stack.pop()); @@ -241,45 +174,32 @@ public class SyscallCommand implements Command { if (ch instanceof ServerSocketChannel ssc) { ssc.bind(new InetSocketAddress(host, port)); stack.push(0); - } else stack.push(-1); + } else { + stack.push(-1); + } } - /* - * LISTEN —— 监听 socket,兼容 backlog。 - * 入栈顺序: fd(int), backlog(int) - * 出栈顺序: backlog → fd - * 返回 0 成功,-1 失败 - * 注意:Java NIO 打开 ServerSocketChannel 已自动监听,无 backlog 处理,行为和 UNIX 有区别。 - */ case "LISTEN" -> { int backlog = (Integer) stack.pop(); int fd = (Integer) stack.pop(); Channel ch = FDTable.get(fd); - if (ch instanceof ServerSocketChannel) stack.push(0); - else stack.push(-1); + if (ch instanceof ServerSocketChannel) { + stack.push(0); + } else { + stack.push(-1); + } } - /* - * ACCEPT —— 接收连接。 - * 入栈顺序: fd(int) - * 出栈顺序: fd - * 返回新连接 fd,失败 -1 - */ case "ACCEPT" -> { int fd = (Integer) stack.pop(); Channel ch = FDTable.get(fd); if (ch instanceof ServerSocketChannel ssc) { SocketChannel cli = ssc.accept(); stack.push(FDTable.register(cli)); - } else stack.push(-1); + } else { + stack.push(-1); + } } - /*==================== 多路复用 ====================*/ - - /* - * SELECT —— I/O 多路复用,监视多个 fd 是否可读写。 - * 入栈顺序: fds(List), timeout_ms(long) - * 出栈顺序: timeout_ms → fds - * 返回: 就绪 fd 列表(List) - */ + // 多路复用 case "SELECT" -> { long timeout = ((Number) stack.pop()).longValue(); @SuppressWarnings("unchecked") @@ -298,107 +218,94 @@ public class SyscallCommand implements Command { int ready = sel.select(timeout); List readyFds = new ArrayList<>(); if (ready > 0) { - for (SelectionKey k : sel.selectedKeys()) + for (SelectionKey k : sel.selectedKeys()) { readyFds.add((Integer) k.attachment()); + } } stack.push(readyFds); sel.close(); } - - /*==================== 控制台输出 ====================*/ - - /* - * PRINT —— 控制台输出(无换行)。 - * 入栈顺序: data(任意基本类型或其包装类型、String、byte[] 等) - * 出栈顺序: data - * 返回 0 - */ + // 控制台输出 case "PRINT" -> { Object dataObj = stack.pop(); output(dataObj, false); - stack.push(0); // success + stack.push(0); } - - /* PRINTLN —— 控制台输出(自动换行)。*/ case "PRINTLN" -> { Object dataObj = stack.pop(); output(dataObj, true); - stack.push(0); // success + stack.push(0); } - - /*==================== 其它未实现/扩展命令 ====================*/ - - /* - * 其它自定义 syscall 子命令未实现 - */ - default -> throw new UnsupportedOperationException("Unsupported SYSCALL: " + cmd); + default -> throw new UnsupportedOperationException("Unsupported SYSCALL subcommand: " + cmd); } } catch (Exception e) { - // 统一异常处理,异常时压入 -1 pushErr(stack, e); } - // 默认:下一条指令 return pc + 1; } - /*------------------------------------ 工具方法 ------------------------------------*/ - /** - * POSIX open 标志到 Java NIO OpenOption 的映射 + * 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。 *

          - * 将 Linux/UNIX 的 open 调用 flags 参数,转换为 Java NIO 的 OpenOption 集合。 - * 目前仅支持 WRITE(0x1)、READ、CREATE(0x40)、TRUNCATE(0x200)、APPEND(0x400)等常用标志。 + * 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合, + * 以支持文件读、写、创建、截断、追加等多种访问场景。 + * 常用于 SYSCALL 的 OPEN 子命令。 *

          * - * @param flags POSIX 风格 open 标志(如 O_WRONLY=0x1, O_CREAT=0x40 等) - * @return 映射后的 OpenOption 集合 + * @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。 + * @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法 */ private static Set flagsToOptions(int flags) { Set opts = new HashSet<>(); - // 0x1 = WRITE,否则默认 READ + // 如果有写入标志,则添加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 代表本次系统调用失败。 *

          - * 捕获 syscall 内部所有异常,将 -1 压入操作数栈,表示系统调用失败(暂不区分错误类型)。 - * 常见异常如文件不存在、权限不足、通道类型不符、网络故障等。 + * 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号, + * 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。 *

          * - * @param stack 当前操作数栈 - * @param e 捕获的异常对象 + * @param stack 操作数栈,将失败信号写入此栈 + * @param e 抛出的异常对象,可在调试时输出日志 */ private static void pushErr(OperandStack stack, Exception e) { - stack.push(-1); // 目前统一用 -1,后续可按异常类型/errno 映射 + stack.push(-1); + System.err.println("Syscall exception: " + e); } /** - * 统一的控制台输出辅助方法,支持: - *
            - *
          • 所有基本类型及其包装类(int、double、long、float…)
          • - *
          • String、byte[]、char[] 以及其他原生数组
          • - *
          • 任意 Object(调用 {@code toString})
          • - *
          + * 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。 + *

          + * 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。 + * 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。 + *

          * - * @param obj 要输出的对象 - * @param newline 是否追加换行 + * @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(); @@ -408,7 +315,15 @@ public class SyscallCommand implements Command { } /** - * 将各种原生数组转换成可读字符串。 + * 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。 + *

          + * 本方法针对 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); From 07b7d5c40e6f548ea60fb6afb865d4fb3de2c258 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Jul 2025 16:35:43 +0800 Subject: [PATCH 34/36] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=BB=88=E6=AD=A2=E5=92=8C=E5=87=BD=E6=95=B0=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 CallCommand 中添加换行符,改善函数调用的输出可读性 - 修改 HaltCommand 中的终止消息格式,提高信息的清晰度 --- .../org/jcnc/snow/vm/commands/flow/control/CallCommand.java | 2 +- .../org/jcnc/snow/vm/commands/system/control/HaltCommand.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java index 1171885..26dda90 100644 --- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java +++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java @@ -80,7 +80,7 @@ public class CallCommand implements Command { new MethodContext("subroutine@" + targetAddr, null)); callStack.pushFrame(newFrame); - System.out.println("Calling function at address: " + targetAddr); + System.out.println("\nCalling function at address: " + targetAddr); return targetAddr; // jump } } diff --git a/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java b/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java index 06a5da4..71e208c 100644 --- a/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java +++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java @@ -59,7 +59,7 @@ public class HaltCommand implements Command { @Override public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) { // Output the termination message - LoggingUtils.logInfo("Process has ended", "\n"); + LoggingUtils.logInfo("\nProcess has ended", ""); // Return -1 to indicate the program termination, and the virtual machine will not continue executing subsequent instructions return -1; From 70feb1fe5e94a771d591ad477f6b528562d6fadc Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Jul 2025 16:36:23 +0800 Subject: [PATCH 35/36] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=20RetCommand?= =?UTF-8?q?=20=E7=B1=BB=E7=9A=84=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java index 1e4ebd5..505f378 100644 --- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java +++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java @@ -44,7 +44,7 @@ public class RetCommand implements Command { finished.getLocalVariableStore().clearVariables(); int returnAddr = finished.getReturnAddress(); - System.out.println("Return " + returnAddr); + System.out.println("\nReturn " + returnAddr); return returnAddr; } } From 1605390f0863655d6b72781597bd020b8faf2f62 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Jul 2025 16:37:53 +0800 Subject: [PATCH 36/36] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=9C=AB=E5=B0=BE=20CALL=20=E6=8C=87=E4=BB=A4?= =?UTF-8?q?=E7=9A=84=E6=9C=AA=E8=A7=A3=E6=9E=90=E7=AC=A6=E5=8F=B7=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在函数开始时登记函数入口地址 - 在函数末尾强制添加 RET 或 HALT 指令 - 优化了代码注释和格式 --- .../backend/builder/VMCodeGenerator.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java index 8370e90..2458b9e 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java @@ -1,6 +1,7 @@ package org.jcnc.snow.compiler.backend.builder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.utils.OpHelper; import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.core.IRInstruction; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; @@ -16,7 +17,7 @@ import java.util.stream.Collectors; * 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。 *

          *

          - * 工作流程简述: + * 工作流程简述: *

            *
          1. 接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。
          2. *
          3. 遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。
          4. @@ -74,18 +75,26 @@ public final class VMCodeGenerator { */ public void generate(IRFunction fn) { this.currentFn = fn.name(); - out.beginFunction(currentFn); // 输出函数起始 + + /* 登记函数入口地址 —— 解决 CALL 未解析符号问题 */ + out.beginFunction(currentFn); + + /* 逐条分发 IR 指令给对应的生成器 */ for (IRInstruction ins : fn.body()) { @SuppressWarnings("unchecked") - // 取得与当前 IR 指令类型匹配的生成器(泛型强转消除类型警告) InstructionGenerator gen = (InstructionGenerator) registry.get(ins.getClass()); if (gen == null) { throw new IllegalStateException("Unsupported IR: " + ins); } - // 通过多态分发到实际生成器 gen.generate(ins, out, slotMap, currentFn); } - out.endFunction(); // 输出函数结束 + + /* 强制补上函数结尾的返回/终止指令 */ + String retOpcode = "main".equals(currentFn) ? "HALT" : "RET"; + out.emit(OpHelper.opcode(retOpcode)); + + /* 结束函数 */ + out.endFunction(); } }