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.
+ *
+ * @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