From 0dbe39eff3e4f21a9129802dedc54e04acd10532 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Jul 2025 22:33:41 +0800 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE=E7=B1=BB=E5=B9=B6=E7=BB=9F=E4=B8=80=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Mode 枚举类,用于统一定义程序的运行和调试模式- 删除 VMMode 枚举类,使用新的 Mode 枚举替代 - 通过重构简化代码结构,提高代码可维护性 --- src/main/java/org/jcnc/snow/common/Mode.java | 20 +++++++++++++++++++ .../java/org/jcnc/snow/vm/engine/VMMode.java | 19 ------------------ 2 files changed, 20 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/common/Mode.java delete mode 100644 src/main/java/org/jcnc/snow/vm/engine/VMMode.java diff --git a/src/main/java/org/jcnc/snow/common/Mode.java b/src/main/java/org/jcnc/snow/common/Mode.java new file mode 100644 index 0000000..f8ee663 --- /dev/null +++ b/src/main/java/org/jcnc/snow/common/Mode.java @@ -0,0 +1,20 @@ +package org.jcnc.snow.common; + +/** + * 程序的运行/调试模式枚举。 + * + */ +public enum Mode { + /** + * 运行模式。 + */ + RUN, + + /** + * 调试模式。 + */ + DEBUG +} diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMMode.java b/src/main/java/org/jcnc/snow/vm/engine/VMMode.java deleted file mode 100644 index a378ef7..0000000 --- a/src/main/java/org/jcnc/snow/vm/engine/VMMode.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.jcnc.snow.vm.engine; - -/** - * The VMMode enum defines the different operational modes of the virtual machine. - *

This class is used to distinguish the behavior of the virtual machine in different states, with each mode corresponding to a different operational logic.

- */ -public enum VMMode { - /** - * Run Mode: The virtual machine executes instructions in the normal execution flow. - *

In this mode, the virtual machine processes instructions and performs related calculations.

- */ - RUN, - - /** - * Debug Mode: The virtual machine outputs debug information during execution. - *

This mode is used for debugging the virtual machine, allowing developers to view detailed information such as the execution state, local variables, and more.

- */ - DEBUG, -} From da7fc0b046b218a04f14fb5946babefd865e5424 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Jul 2025 22:33:50 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E9=85=8D=E7=BD=AE=E5=92=8C=E8=B0=83=E8=AF=95=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SnowConfig 类,用于统一设置程序运行模式 - 提供调试模式下的日志输出方法 - 支持判断当前运行模式 --- .../java/org/jcnc/snow/common/SnowConfig.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/java/org/jcnc/snow/common/SnowConfig.java diff --git a/src/main/java/org/jcnc/snow/common/SnowConfig.java b/src/main/java/org/jcnc/snow/common/SnowConfig.java new file mode 100644 index 0000000..e48108e --- /dev/null +++ b/src/main/java/org/jcnc/snow/common/SnowConfig.java @@ -0,0 +1,58 @@ +package org.jcnc.snow.common; + +/** + * 全局编译/运行配置和调试输出工具类。 + *

+ * 用于统一设置程序的运行模式,并在调试模式下输出日志信息。 + *

+ */ +public final class SnowConfig { + + /** + * 当前运行模式,默认为 {@link Mode#RUN}。 + */ + public static Mode MODE = Mode.RUN; + + /** + * 私有构造方法,防止实例化。 + */ + private SnowConfig() { + } + + /** + * 仅在 {@link Mode#DEBUG} 模式下打印一行信息。 + * + * @param msg 要输出的信息 + */ + public static void print(String msg) { + if (MODE == Mode.DEBUG) System.out.println(msg); + } + + /** + * 仅在 {@link Mode#DEBUG} 模式下按格式输出信息。 + * + * @param fmt 格式化字符串 + * @param args 格式化参数 + */ + public static void print(String fmt, Object... args) { + if (MODE == Mode.DEBUG) System.out.printf(fmt, args); + } + + /** + * 判断当前是否为 {@link Mode#DEBUG} 模式。 + * + * @return 当且仅当当前为调试模式时返回 true + */ + public static boolean isDebug() { + return MODE == Mode.DEBUG; + } + + /** + * 判断当前是否为 {@link Mode#RUN} 模式。 + * + * @return 当且仅当当前为运行模式时返回 true + */ + public static boolean isRun() { + return MODE == Mode.RUN; + } +} From 6f81feab3fc35df34c778c82afeffe73dedba6b7 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Jul 2025 22:34:15 +0800 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E6=9C=BA=E5=92=8C=E7=BC=96=E8=AF=91=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E8=BE=93=E5=87=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snow/compiler/lexer/core/LexerEngine.java | 10 +- .../utils/SemanticAnalysisReporter.java | 4 +- .../org/jcnc/snow/pkg/tasks/CompileTask.java | 155 ++++++++++-------- .../java/org/jcnc/snow/vm/VMInitializer.java | 6 +- .../java/org/jcnc/snow/vm/VMLauncher.java | 7 +- .../vm/commands/flow/control/CallCommand.java | 32 ++-- .../vm/commands/flow/control/RetCommand.java | 4 +- .../snow/vm/engine/VirtualMachineEngine.java | 5 +- .../snow/vm/module/LocalVariableStore.java | 50 ++++-- .../org/jcnc/snow/vm/utils/LoggingUtils.java | 4 +- 10 files changed, 160 insertions(+), 117 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java index 1ac0792..672116f 100644 --- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java +++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java @@ -1,5 +1,6 @@ package org.jcnc.snow.compiler.lexer.core; +import org.jcnc.snow.common.SnowConfig; import org.jcnc.snow.compiler.lexer.base.TokenScanner; import org.jcnc.snow.compiler.lexer.scanners.*; import org.jcnc.snow.compiler.lexer.token.Token; @@ -10,6 +11,8 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import static org.jcnc.snow.common.SnowConfig.print; + /** * Snow 语言词法分析器核心实现。 *

采用“先扫描 → 后批量校验 → 统一报告”策略: @@ -53,14 +56,17 @@ public class LexerEngine { /* 2. 后置整体校验 */ validateTokens(); /* 3. 打印 token */ - TokenPrinter.print(tokens); + if (SnowConfig.isDebug()) { + TokenPrinter.print(tokens); + } + /* 4. 统一报告错误 */ report(errors); } public static void report(List errors) { if (errors == null || errors.isEmpty()) { - System.out.println("\n## 词法分析通过,没有发现错误\n"); + print("\n## 词法分析通过,没有发现错误\n"); return; } System.err.println("\n词法分析发现 " + errors.size() + " 个错误: "); diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java index edeb9a5..eb332b8 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java @@ -4,6 +4,8 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError; import java.util.List; +import static org.jcnc.snow.common.SnowConfig.print; + /** * {@code SemanticAnalysisReporter} 用于在语义分析结束后汇总并打印所有收集到的 * {@link SemanticError}。为了同时满足“完整错误收集”与“按需快速失败”两种使用场景, @@ -28,7 +30,7 @@ public final class SemanticAnalysisReporter { System.err.println("语义分析发现 " + errors.size() + " 个错误: "); errors.forEach(err -> System.err.println(" " + err)); } else { - System.out.println("\n## 语义分析通过,没有发现错误\n"); + print("\n## 语义分析通过,没有发现错误\n"); } } 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 844c2fc..c908125 100644 --- a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java +++ b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java @@ -1,12 +1,14 @@ package org.jcnc.snow.pkg.tasks; import org.jcnc.snow.cli.commands.CompileCommand; -import org.jcnc.snow.compiler.backend.utils.OpHelper; +import org.jcnc.snow.common.Mode; +import org.jcnc.snow.common.SnowConfig; import org.jcnc.snow.compiler.backend.alloc.RegisterAllocator; import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator; import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; import org.jcnc.snow.compiler.backend.generator.InstructionGeneratorProvider; +import org.jcnc.snow.compiler.backend.utils.OpHelper; import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder; import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.core.IRInstruction; @@ -26,6 +28,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import static org.jcnc.snow.common.SnowConfig.print; + /** * CLI 任务: 编译 .snow 源文件为 VM 字节码(.water 文件)。 *

@@ -39,16 +43,20 @@ import java.util.*; * */ public final class CompileTask implements Task { - /** 项目信息 */ + /** + * 项目信息 + */ private final Project project; - /** 原始命令行参数 */ + /** + * 原始命令行参数 + */ private final String[] args; /** * 创建一个编译任务。 * - * @param project 项目信息对象 - * @param args 命令行参数数组 + * @param project 项目信息对象 + * @param args 命令行参数数组 */ public CompileTask(Project project, String[] args) { this.project = project; @@ -64,6 +72,56 @@ public final class CompileTask implements Task { this(project, new String[0]); } + /* ---------------------- 调试输出工具 ---------------------- */ + + /** + * 推断 .water 输出文件名。 + *

    + *
  • 如果指定 -o,直接使用该名称。
  • + *
  • 目录编译时,取目录名。
  • + *
  • 单文件编译时,取文件名去掉 .snow 后缀。
  • + *
  • 否则默认 "program"。
  • + *
+ * + * @param sources 源文件路径列表 + * @param outName 输出文件名(如有指定,否则为 null) + * @param dir 源码目录(如有指定,否则为 null) + * @return 推断出的输出文件路径(.water 文件) + */ + private static Path deriveOutputPath(List sources, String outName, Path dir) { + String base; + if (outName != null) { + base = outName; + } else if (dir != null) { + base = dir.getFileName().toString(); + } else if (sources.size() == 1) { + base = sources.getFirst().getFileName().toString() + .replaceFirst("\\.snow$", ""); + } else { + base = "program"; + } + return Path.of(base + ".water"); + } + + /** + * 将 main 函数调整至函数列表首位,确保程序入口为 PC=0。 + * + * @param in 原始 IRProgram + * @return 调整入口后的 IRProgram + */ + private static IRProgram reorderForEntry(IRProgram in) { + List ordered = new ArrayList<>(in.functions()); + for (int i = 0; i < ordered.size(); i++) { + if ("main".equals(ordered.get(i).name())) { + Collections.swap(ordered, 0, i); + break; + } + } + IRProgram out = new IRProgram(); + ordered.forEach(out::add); + return out; + } + /** * 执行编译任务。该方法会解析参数并调用 {@link #execute(String[])} 进行实际编译流程。 * @@ -96,6 +154,7 @@ public final class CompileTask implements Task { String arg = args[i]; switch (arg) { case "run" -> runAfterCompile = true; + case "-debug" -> SnowConfig.MODE = Mode.DEBUG; case "-o" -> { if (i + 1 < args.length) outputName = args[++i]; else { @@ -151,8 +210,8 @@ public final class CompileTask implements Task { // ---------------- 1. 词法/语法分析,并打印源代码 ---------------- List allAst = new ArrayList<>(); - System.out.println("## 编译器输出"); - System.out.println("### Snow 源代码"); + print("## 编译器输出"); + print("### Snow 源代码"); for (Path p : sources) { if (!Files.exists(p)) { @@ -163,8 +222,8 @@ public final class CompileTask implements Task { String code = Files.readString(p, StandardCharsets.UTF_8); // 打印源码 - System.out.println("#### " + p.getFileName()); - System.out.println(code); + print("#### " + p.getFileName()); + print(code); // 词法、语法分析 LexerEngine lexer = new LexerEngine(code, p.toString()); @@ -185,11 +244,11 @@ public final class CompileTask implements Task { program = reorderForEntry(program); // 打印 AST 和 IR - System.out.println("### AST"); - ASTPrinter.printJson(allAst); + print("### AST"); + if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst); - System.out.println("### IR"); - System.out.println(program); + print("### IR"); + print(program.toString()); // ---------------- 4. IR → VM 指令 ---------------- VMProgramBuilder builder = new VMProgramBuilder(); @@ -203,74 +262,28 @@ public final class CompileTask implements Task { } List finalCode = builder.build(); - System.out.println("### VM code"); - for (int i = 0; i < finalCode.size(); i++) { - String[] parts = finalCode.get(i).split(" "); - String name = OpHelper.opcodeName(parts[0]); - parts = Arrays.copyOfRange(parts, 1, parts.length); - System.out.printf("%04d: %-10s %s\n", i, name, String.join(" ", parts)); + print("### VM code"); + if (SnowConfig.isDebug()) { + for (int i = 0; i < finalCode.size(); i++) { + String[] parts = finalCode.get(i).split(" "); + String name = OpHelper.opcodeName(parts[0]); + parts = Arrays.copyOfRange(parts, 1, parts.length); + print("%04d: %-10s %s%n", i, name, String.join(" ", parts)); + } } // ---------------- 5. 写出 .water 文件 ---------------- Path outputFile = deriveOutputPath(sources, outputName, dir); Files.write(outputFile, finalCode, StandardCharsets.UTF_8); - System.out.println("Written to " + outputFile.toAbsolutePath()); + print("Written to " + outputFile.toAbsolutePath()); - // ---------------- 6. 可选: 立即运行 VM ---------------- + // ---------------- 6. 立即运行 VM ---------------- if (runAfterCompile) { - System.out.println("\n=== Launching VM ==="); + print("\n=== Launching VM ==="); VMLauncher.main(new String[]{outputFile.toString()}); - System.out.println("\n=== VM exited ==="); + print("\n=== VM exited ==="); } return 0; } - - /** - * 推断 .water 输出文件名。 - *
    - *
  • 如果指定 -o,直接使用该名称。
  • - *
  • 目录编译时,取目录名。
  • - *
  • 单文件编译时,取文件名去掉 .snow 后缀。
  • - *
  • 否则默认 "program"。
  • - *
- * - * @param sources 源文件路径列表 - * @param outName 输出文件名(如有指定,否则为 null) - * @param dir 源码目录(如有指定,否则为 null) - * @return 推断出的输出文件路径(.water 文件) - */ - private static Path deriveOutputPath(List sources, String outName, Path dir) { - String base; - if (outName != null) { - base = outName; - } else if (dir != null) { - base = dir.getFileName().toString(); - } else if (sources.size() == 1) { - base = sources.getFirst().getFileName().toString() - .replaceFirst("\\.snow$", ""); - } else { - base = "program"; - } - return Path.of(base + ".water"); - } - - /** - * 将 main 函数调整至函数列表首位,确保程序入口为 PC=0。 - * - * @param in 原始 IRProgram - * @return 调整入口后的 IRProgram - */ - private static IRProgram reorderForEntry(IRProgram in) { - List ordered = new ArrayList<>(in.functions()); - for (int i = 0; i < ordered.size(); i++) { - if ("main".equals(ordered.get(i).name())) { - Collections.swap(ordered, 0, i); - break; - } - } - IRProgram out = new IRProgram(); - ordered.forEach(out::add); - return out; - } } diff --git a/src/main/java/org/jcnc/snow/vm/VMInitializer.java b/src/main/java/org/jcnc/snow/vm/VMInitializer.java index fc1ce08..ca11e3b 100644 --- a/src/main/java/org/jcnc/snow/vm/VMInitializer.java +++ b/src/main/java/org/jcnc/snow/vm/VMInitializer.java @@ -1,8 +1,8 @@ package org.jcnc.snow.vm; +import org.jcnc.snow.common.Mode; import org.jcnc.snow.vm.execution.CommandLoader; import org.jcnc.snow.vm.engine.VMCommandExecutor; -import org.jcnc.snow.vm.engine.VMMode; import org.jcnc.snow.vm.engine.VirtualMachineEngine; import org.jcnc.snow.vm.io.FilePathResolver; import org.jcnc.snow.vm.utils.VMStateLogger; @@ -45,7 +45,7 @@ public class VMInitializer { * @param vmMode The mode in which the virtual machine should operate. * This can be used to specify different operational modes (e.g., debug mode, normal mode). */ - public static void initializeAndRunVM(String[] args, VMMode vmMode) { + public static void initializeAndRunVM(String[] args, Mode vmMode) { // Retrieve and validate file path String filePath = FilePathResolver.getFilePath(args); if (filePath == null) return; @@ -55,7 +55,7 @@ public class VMInitializer { if (commands.isEmpty()) return; // Execute the commands using the virtual machine engine - VirtualMachineEngine virtualMachineEngine = new VirtualMachineEngine(vmMode); + VirtualMachineEngine virtualMachineEngine = new VirtualMachineEngine(); VMCommandExecutor.executeInstructions(virtualMachineEngine, commands); // Print the virtual machine's state diff --git a/src/main/java/org/jcnc/snow/vm/VMLauncher.java b/src/main/java/org/jcnc/snow/vm/VMLauncher.java index 345d9d1..ecda6cb 100644 --- a/src/main/java/org/jcnc/snow/vm/VMLauncher.java +++ b/src/main/java/org/jcnc/snow/vm/VMLauncher.java @@ -1,6 +1,7 @@ package org.jcnc.snow.vm; -import org.jcnc.snow.vm.engine.VMMode; + +import org.jcnc.snow.common.Mode; import static org.jcnc.snow.vm.VMInitializer.initializeAndRunVM; @@ -12,7 +13,7 @@ import static org.jcnc.snow.vm.VMInitializer.initializeAndRunVM; * *

This class provides the entry point to launch the virtual machine. The main method retrieves the file path * of the VM instructions from the command-line arguments, initializes the VM engine, and runs the VM in the - * specified mode (e.g., {@link VMMode#DEBUG}).

+ * specified mode (e.g., {@link Mode#DEBUG}).

*/ public class VMLauncher { @@ -44,6 +45,6 @@ public class VMLauncher { */ public static void main(String[] args) { // Call the method that initializes and runs the VM in DEBUG mode - initializeAndRunVM(args, VMMode.RUN); + initializeAndRunVM(args, Mode.RUN); } } 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 26dda90..90741be 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 @@ -1,9 +1,11 @@ package org.jcnc.snow.vm.commands.flow.control; -import org.jcnc.snow.vm.engine.VMMode; +import org.jcnc.snow.common.Mode; import org.jcnc.snow.vm.interfaces.Command; import org.jcnc.snow.vm.module.*; +import static org.jcnc.snow.common.SnowConfig.print; + /** * The CallCommand class implements the {@link Command} interface and represents a subroutine/function call * instruction in the virtual machine. @@ -32,18 +34,18 @@ public class CallCommand implements Command { * 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. + * @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. + * 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. */ @@ -61,13 +63,13 @@ public class CallCommand implements Command { int nArgs; try { targetAddr = Integer.parseInt(parts[1]); - nArgs = Integer.parseInt(parts[2]); + nArgs = Integer.parseInt(parts[2]); } catch (NumberFormatException e) { throw new IllegalArgumentException("CALL: malformed operands", e); } /* build new frame & local table for callee */ - LocalVariableStore calleeLVS = new LocalVariableStore(VMMode.RUN); + LocalVariableStore calleeLVS = new LocalVariableStore(); /* transfer arguments: operand stack top is last arg */ for (int slot = nArgs - 1; slot >= 0; slot--) { @@ -80,7 +82,7 @@ public class CallCommand implements Command { new MethodContext("subroutine@" + targetAddr, null)); callStack.pushFrame(newFrame); - System.out.println("\nCalling function at address: " + targetAddr); + print("\nCalling function at address: " + targetAddr); return targetAddr; // jump } } 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 505f378..c31ec96 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 @@ -3,6 +3,8 @@ package org.jcnc.snow.vm.commands.flow.control; import org.jcnc.snow.vm.interfaces.Command; import org.jcnc.snow.vm.module.*; +import static org.jcnc.snow.common.SnowConfig.print; + /** * Implements the {@code RET} instruction (method return). * @@ -44,7 +46,7 @@ public class RetCommand implements Command { finished.getLocalVariableStore().clearVariables(); int returnAddr = finished.getReturnAddress(); - System.out.println("\nReturn " + returnAddr); + print("\nReturn " + returnAddr); return returnAddr; } } diff --git a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java index 0f188ab..704822b 100644 --- a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java +++ b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java @@ -1,5 +1,6 @@ package org.jcnc.snow.vm.engine; +import org.jcnc.snow.common.Mode; import org.jcnc.snow.vm.execution.CommandExecutionHandler; import org.jcnc.snow.vm.module.*; @@ -53,10 +54,10 @@ public class VirtualMachineEngine { * * @param vmMode execution mode (DEBUG / RUN) */ - public VirtualMachineEngine(VMMode vmMode) { + public VirtualMachineEngine() { this.operandStack = new OperandStack(); this.callStack = new CallStack(); - this.localVariableStore = new LocalVariableStore(vmMode); // shared with root frame + this.localVariableStore = new LocalVariableStore(); // shared with root frame this.commandExecutionHandler = new CommandExecutionHandler(operandStack, localVariableStore, callStack); this.programCounter = 0; diff --git a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java index 8c64ff9..e1b3bd1 100644 --- a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java +++ b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java @@ -1,11 +1,10 @@ package org.jcnc.snow.vm.module; +import org.jcnc.snow.common.SnowConfig; import org.jcnc.snow.vm.gui.LocalVariableStoreSwing; import org.jcnc.snow.vm.utils.LoggingUtils; -import org.jcnc.snow.vm.engine.VMMode; import java.util.ArrayList; -import java.util.Objects; /** * The {@code LocalVariableStore} represents a simple dynamically-sized @@ -18,25 +17,23 @@ import java.util.Objects; public class LocalVariableStore { private final ArrayList localVariables; - private final VMMode vmMode; - /* ---------- construction ---------- */ - public LocalVariableStore(VMMode vmMode, int initialCapacity) { + public LocalVariableStore(int initialCapacity) { this.localVariables = new ArrayList<>(initialCapacity); - this.vmMode = vmMode; handleMode(); } - public LocalVariableStore(VMMode vmMode) { + public LocalVariableStore() { this.localVariables = new ArrayList<>(); - this.vmMode = vmMode; handleMode(); } /* ---------- public API ---------- */ - /** Sets the value at {@code index}, expanding the list if necessary. */ + /** + * Sets the value at {@code index}, expanding the list if necessary. + */ public void setVariable(int index, Object value) { ensureCapacity(index + 1); localVariables.set(index, value); @@ -46,10 +43,17 @@ public class LocalVariableStore { * 兼容早期实现: VM 指令译码器可直接调用 store / load * 而无需关心内部命名差异。 * ------------------------------------------------------------ */ - public void store(int index, Object value) { setVariable(index, value); } - public Object load(int index) { return getVariable(index); } + public void store(int index, Object value) { + setVariable(index, value); + } - /** Returns the value at {@code index}. */ + public Object load(int index) { + return getVariable(index); + } + + /** + * Returns the value at {@code index}. + */ public Object getVariable(int index) { /* 修改点 #1 —— 自动扩容以避免 LOAD 越界异常 */ if (index < 0) @@ -58,12 +62,16 @@ public class LocalVariableStore { return localVariables.get(index); // 可能为 null,符合 JVM 语义 } - /** Exposes the backing list (read-only preferred). */ + /** + * Exposes the backing list (read-only preferred). + */ public ArrayList getLocalVariables() { return localVariables; } - /** Prints every slot to the logger. */ + /** + * Prints every slot to the logger. + */ public void printLv() { if (localVariables.isEmpty()) { LoggingUtils.logInfo("Local variable table is empty", ""); @@ -76,7 +84,9 @@ public class LocalVariableStore { } } - /** Clears all variables (used when a stack frame is popped). */ + /** + * Clears all variables (used when a stack frame is popped). + */ public void clearVariables() { localVariables.clear(); } @@ -99,7 +109,9 @@ public class LocalVariableStore { /* ---------- internal helpers ---------- */ - /** Ensures backing list can hold {@code minCapacity} slots. */ + /** + * Ensures backing list can hold {@code minCapacity} slots. + */ private void ensureCapacity(int minCapacity) { /* 修改点 #3 —— 使用 while 循环填充 null,确保 slot 可随机写入 */ while (localVariables.size() < minCapacity) { @@ -107,10 +119,12 @@ public class LocalVariableStore { } } - /** Mode-specific UI hook (unchanged). */ + /** + * Mode-specific UI hook (unchanged). + */ private void handleMode() { /* no-op */ - if (Objects.requireNonNull(vmMode) == VMMode.DEBUG) { + if (SnowConfig.isDebug()) { LocalVariableStoreSwing.display(this, "Local Variable Table"); } } diff --git a/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java b/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java index 2367cb7..3cc4eeb 100644 --- a/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java +++ b/src/main/java/org/jcnc/snow/vm/utils/LoggingUtils.java @@ -3,6 +3,8 @@ package org.jcnc.snow.vm.utils; import java.util.logging.Level; import java.util.logging.Logger; +import static org.jcnc.snow.common.SnowConfig.print; + /** * The LoggingUtils class provides logging functionality, supporting different log levels for output. * This class uses Java's built-in logging system for logging, supporting both console output and log file recording. @@ -51,6 +53,6 @@ public class LoggingUtils { */ public static void logInfo(String title, String message) { // Output the informational message to the console - System.out.println(title + message); + print(title + message); } } From ebb9524db0f33ed8d21807c62d3f0b0aa687bb4f Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 23 Jul 2025 10:24:49 +0800 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20=E4=BF=9D=E7=95=99=20cloud?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E7=9A=84=20"run"=20=E5=92=8C=20"-de?= =?UTF-8?q?bug"=20=E6=A0=87=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/jcnc/snow/cli/commands/CompileCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java b/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java index 277c8b5..361413b 100644 --- a/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java +++ b/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java @@ -60,10 +60,10 @@ public final class CompileCommand implements CLICommand { List argList = new ArrayList<>(); - // 保留用户在 cloud 模式下传入的 “run” 标志 + // 保留用户在 cloud 模式下传入的 “run” / “-debug” 标志 for (String a : args) { - if ("run".equals(a)) { - argList.add("run"); + if ("run".equals(a) || "-debug".equals(a)) { + argList.add(a); } } From 80deaa9c4f60e6e9a0f3e8f4f5836df14f5f8e57 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 23 Jul 2025 10:42:38 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AF=B9=20Graal?= =?UTF-8?q?VM=20native-image=20=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 LocalVariableStore 中添加对 native-image环境的检测 - 在 VMUtils 中实现 isNativeImage 方法 - 更新 module-info.java,添加对 org.graalvm.nativeimage 的依赖 - 优化调试模式下的 UI 显示逻辑,避免在 native-image 环境中显示 Swing 窗口 --- src/main/java/module-info.java | 1 + .../snow/vm/module/LocalVariableStore.java | 18 +++++++++++++----- .../java/org/jcnc/snow/vm/utils/VMUtils.java | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3f8a7b3..3597cae 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -4,6 +4,7 @@ module org.jcnc.snow.compiler { uses CLICommand; requires java.desktop; requires java.logging; + requires org.graalvm.nativeimage; exports org.jcnc.snow.compiler.ir.core; exports org.jcnc.snow.compiler.ir.instruction; } diff --git a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java index e1b3bd1..7207882 100644 --- a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java +++ b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java @@ -6,6 +6,8 @@ import org.jcnc.snow.vm.utils.LoggingUtils; import java.util.ArrayList; +import static org.jcnc.snow.vm.utils.VMUtils.isNativeImage; + /** * The {@code LocalVariableStore} represents a simple dynamically-sized * local-variable table (frame locals) of the VM. @@ -84,6 +86,9 @@ public class LocalVariableStore { } } + + /* ---------- internal helpers ---------- */ + /** * Clears all variables (used when a stack frame is popped). */ @@ -106,9 +111,6 @@ public class LocalVariableStore { } } - - /* ---------- internal helpers ---------- */ - /** * Ensures backing list can hold {@code minCapacity} slots. */ @@ -120,12 +122,18 @@ public class LocalVariableStore { } /** - * Mode-specific UI hook (unchanged). + * Mode-specific UI hook for debugging. + *

+ * If debug mode is enabled and not running inside a GraalVM native-image, + * this method will open the Swing-based variable inspector window. + * In native-image environments (where AWT/Swing is unavailable), + * the window will not be displayed. */ private void handleMode() { - /* no-op */ if (SnowConfig.isDebug()) { + if (isNativeImage()) return; LocalVariableStoreSwing.display(this, "Local Variable Table"); } } + } diff --git a/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java b/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java index 21b526d..ca8d25a 100644 --- a/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java +++ b/src/main/java/org/jcnc/snow/vm/utils/VMUtils.java @@ -1,5 +1,6 @@ package org.jcnc.snow.vm.utils; +import org.graalvm.nativeimage.ImageInfo; import org.jcnc.snow.vm.engine.VirtualMachineEngine; /** @@ -39,4 +40,21 @@ public class VMUtils { vm.printStack(); vm.printLocalVariables(); } + + /** + * Detects if the current runtime environment is a GraalVM native-image. + *

+ * Uses GraalVM's {@code org.graalvm.nativeimage.ImageInfo.inImageCode()} API to determine + * if the application is running as a native executable. If the class is not present + * (for example, in a standard JVM), returns {@code false}. + * + * @return {@code true} if running inside a GraalVM native-image, otherwise {@code false} + */ + public static boolean isNativeImage() { + try { + return ImageInfo.inImageCode(); + } catch (Throwable t) { + return false; + } + } } From 1486a1e3a553e51dc0c19846b0aaae4981a2392b Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 23 Jul 2025 10:49:34 +0800 Subject: [PATCH 6/6] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20LocalVariableS?= =?UTF-8?q?tore=20=E7=B1=BB=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java index 7207882..2904d04 100644 --- a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java +++ b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java @@ -42,8 +42,8 @@ public class LocalVariableStore { } /* ------------------------------------------------------------ - * 兼容早期实现: VM 指令译码器可直接调用 store / load - * 而无需关心内部命名差异。 + * Backward compatibility: VM instruction decoder can directly call + * store / load methods without caring about internal naming differences. * ------------------------------------------------------------ */ public void store(int index, Object value) { setVariable(index, value);