refactor: 重构虚拟机和编译器的调试输出逻辑
This commit is contained in:
parent
da7fc0b046
commit
6f81feab3f
@ -1,5 +1,6 @@
|
|||||||
package org.jcnc.snow.compiler.lexer.core;
|
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.base.TokenScanner;
|
||||||
import org.jcnc.snow.compiler.lexer.scanners.*;
|
import org.jcnc.snow.compiler.lexer.scanners.*;
|
||||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
@ -10,6 +11,8 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jcnc.snow.common.SnowConfig.print;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snow 语言词法分析器核心实现。
|
* Snow 语言词法分析器核心实现。
|
||||||
* <p>采用“<b>先扫描 → 后批量校验 → 统一报告</b>”策略:
|
* <p>采用“<b>先扫描 → 后批量校验 → 统一报告</b>”策略:
|
||||||
@ -53,14 +56,17 @@ public class LexerEngine {
|
|||||||
/* 2. 后置整体校验 */
|
/* 2. 后置整体校验 */
|
||||||
validateTokens();
|
validateTokens();
|
||||||
/* 3. 打印 token */
|
/* 3. 打印 token */
|
||||||
TokenPrinter.print(tokens);
|
if (SnowConfig.isDebug()) {
|
||||||
|
TokenPrinter.print(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
/* 4. 统一报告错误 */
|
/* 4. 统一报告错误 */
|
||||||
report(errors);
|
report(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void report(List<LexicalError> errors) {
|
public static void report(List<LexicalError> errors) {
|
||||||
if (errors == null || errors.isEmpty()) {
|
if (errors == null || errors.isEmpty()) {
|
||||||
System.out.println("\n## 词法分析通过,没有发现错误\n");
|
print("\n## 词法分析通过,没有发现错误\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
System.err.println("\n词法分析发现 " + errors.size() + " 个错误: ");
|
System.err.println("\n词法分析发现 " + errors.size() + " 个错误: ");
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jcnc.snow.common.SnowConfig.print;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code SemanticAnalysisReporter} 用于在语义分析结束后汇总并打印所有收集到的
|
* {@code SemanticAnalysisReporter} 用于在语义分析结束后汇总并打印所有收集到的
|
||||||
* {@link SemanticError}。为了同时满足“完整错误收集”与“按需快速失败”两种使用场景,
|
* {@link SemanticError}。为了同时满足“完整错误收集”与“按需快速失败”两种使用场景,
|
||||||
@ -28,7 +30,7 @@ public final class SemanticAnalysisReporter {
|
|||||||
System.err.println("语义分析发现 " + errors.size() + " 个错误: ");
|
System.err.println("语义分析发现 " + errors.size() + " 个错误: ");
|
||||||
errors.forEach(err -> System.err.println(" " + err));
|
errors.forEach(err -> System.err.println(" " + err));
|
||||||
} else {
|
} else {
|
||||||
System.out.println("\n## 语义分析通过,没有发现错误\n");
|
print("\n## 语义分析通过,没有发现错误\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
package org.jcnc.snow.pkg.tasks;
|
package org.jcnc.snow.pkg.tasks;
|
||||||
|
|
||||||
import org.jcnc.snow.cli.commands.CompileCommand;
|
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.alloc.RegisterAllocator;
|
||||||
import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator;
|
import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator;
|
||||||
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
import org.jcnc.snow.compiler.backend.generator.InstructionGeneratorProvider;
|
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.builder.IRProgramBuilder;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
@ -26,6 +28,8 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jcnc.snow.common.SnowConfig.print;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 任务: 编译 .snow 源文件为 VM 字节码(.water 文件)。
|
* CLI 任务: 编译 .snow 源文件为 VM 字节码(.water 文件)。
|
||||||
* <p>
|
* <p>
|
||||||
@ -39,16 +43,20 @@ import java.util.*;
|
|||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public final class CompileTask implements Task {
|
public final class CompileTask implements Task {
|
||||||
/** 项目信息 */
|
/**
|
||||||
|
* 项目信息
|
||||||
|
*/
|
||||||
private final Project project;
|
private final Project project;
|
||||||
/** 原始命令行参数 */
|
/**
|
||||||
|
* 原始命令行参数
|
||||||
|
*/
|
||||||
private final String[] args;
|
private final String[] args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个编译任务。
|
* 创建一个编译任务。
|
||||||
*
|
*
|
||||||
* @param project 项目信息对象
|
* @param project 项目信息对象
|
||||||
* @param args 命令行参数数组
|
* @param args 命令行参数数组
|
||||||
*/
|
*/
|
||||||
public CompileTask(Project project, String[] args) {
|
public CompileTask(Project project, String[] args) {
|
||||||
this.project = project;
|
this.project = project;
|
||||||
@ -64,6 +72,56 @@ public final class CompileTask implements Task {
|
|||||||
this(project, new String[0]);
|
this(project, new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------- 调试输出工具 ---------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推断 .water 输出文件名。
|
||||||
|
* <ul>
|
||||||
|
* <li>如果指定 -o,直接使用该名称。</li>
|
||||||
|
* <li>目录编译时,取目录名。</li>
|
||||||
|
* <li>单文件编译时,取文件名去掉 .snow 后缀。</li>
|
||||||
|
* <li>否则默认 "program"。</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param sources 源文件路径列表
|
||||||
|
* @param outName 输出文件名(如有指定,否则为 null)
|
||||||
|
* @param dir 源码目录(如有指定,否则为 null)
|
||||||
|
* @return 推断出的输出文件路径(.water 文件)
|
||||||
|
*/
|
||||||
|
private static Path deriveOutputPath(List<Path> 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<IRFunction> 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[])} 进行实际编译流程。
|
* 执行编译任务。该方法会解析参数并调用 {@link #execute(String[])} 进行实际编译流程。
|
||||||
*
|
*
|
||||||
@ -96,6 +154,7 @@ public final class CompileTask implements Task {
|
|||||||
String arg = args[i];
|
String arg = args[i];
|
||||||
switch (arg) {
|
switch (arg) {
|
||||||
case "run" -> runAfterCompile = true;
|
case "run" -> runAfterCompile = true;
|
||||||
|
case "-debug" -> SnowConfig.MODE = Mode.DEBUG;
|
||||||
case "-o" -> {
|
case "-o" -> {
|
||||||
if (i + 1 < args.length) outputName = args[++i];
|
if (i + 1 < args.length) outputName = args[++i];
|
||||||
else {
|
else {
|
||||||
@ -151,8 +210,8 @@ public final class CompileTask implements Task {
|
|||||||
// ---------------- 1. 词法/语法分析,并打印源代码 ----------------
|
// ---------------- 1. 词法/语法分析,并打印源代码 ----------------
|
||||||
List<Node> allAst = new ArrayList<>();
|
List<Node> allAst = new ArrayList<>();
|
||||||
|
|
||||||
System.out.println("## 编译器输出");
|
print("## 编译器输出");
|
||||||
System.out.println("### Snow 源代码");
|
print("### Snow 源代码");
|
||||||
|
|
||||||
for (Path p : sources) {
|
for (Path p : sources) {
|
||||||
if (!Files.exists(p)) {
|
if (!Files.exists(p)) {
|
||||||
@ -163,8 +222,8 @@ public final class CompileTask implements Task {
|
|||||||
String code = Files.readString(p, StandardCharsets.UTF_8);
|
String code = Files.readString(p, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
// 打印源码
|
// 打印源码
|
||||||
System.out.println("#### " + p.getFileName());
|
print("#### " + p.getFileName());
|
||||||
System.out.println(code);
|
print(code);
|
||||||
|
|
||||||
// 词法、语法分析
|
// 词法、语法分析
|
||||||
LexerEngine lexer = new LexerEngine(code, p.toString());
|
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||||
@ -185,11 +244,11 @@ public final class CompileTask implements Task {
|
|||||||
program = reorderForEntry(program);
|
program = reorderForEntry(program);
|
||||||
|
|
||||||
// 打印 AST 和 IR
|
// 打印 AST 和 IR
|
||||||
System.out.println("### AST");
|
print("### AST");
|
||||||
ASTPrinter.printJson(allAst);
|
if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
|
||||||
|
|
||||||
System.out.println("### IR");
|
print("### IR");
|
||||||
System.out.println(program);
|
print(program.toString());
|
||||||
|
|
||||||
// ---------------- 4. IR → VM 指令 ----------------
|
// ---------------- 4. IR → VM 指令 ----------------
|
||||||
VMProgramBuilder builder = new VMProgramBuilder();
|
VMProgramBuilder builder = new VMProgramBuilder();
|
||||||
@ -203,74 +262,28 @@ public final class CompileTask implements Task {
|
|||||||
}
|
}
|
||||||
List<String> finalCode = builder.build();
|
List<String> finalCode = builder.build();
|
||||||
|
|
||||||
System.out.println("### VM code");
|
print("### VM code");
|
||||||
for (int i = 0; i < finalCode.size(); i++) {
|
if (SnowConfig.isDebug()) {
|
||||||
String[] parts = finalCode.get(i).split(" ");
|
for (int i = 0; i < finalCode.size(); i++) {
|
||||||
String name = OpHelper.opcodeName(parts[0]);
|
String[] parts = finalCode.get(i).split(" ");
|
||||||
parts = Arrays.copyOfRange(parts, 1, parts.length);
|
String name = OpHelper.opcodeName(parts[0]);
|
||||||
System.out.printf("%04d: %-10s %s\n", i, name, String.join(" ", parts));
|
parts = Arrays.copyOfRange(parts, 1, parts.length);
|
||||||
|
print("%04d: %-10s %s%n", i, name, String.join(" ", parts));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- 5. 写出 .water 文件 ----------------
|
// ---------------- 5. 写出 .water 文件 ----------------
|
||||||
Path outputFile = deriveOutputPath(sources, outputName, dir);
|
Path outputFile = deriveOutputPath(sources, outputName, dir);
|
||||||
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
|
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) {
|
if (runAfterCompile) {
|
||||||
System.out.println("\n=== Launching VM ===");
|
print("\n=== Launching VM ===");
|
||||||
VMLauncher.main(new String[]{outputFile.toString()});
|
VMLauncher.main(new String[]{outputFile.toString()});
|
||||||
System.out.println("\n=== VM exited ===");
|
print("\n=== VM exited ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 推断 .water 输出文件名。
|
|
||||||
* <ul>
|
|
||||||
* <li>如果指定 -o,直接使用该名称。</li>
|
|
||||||
* <li>目录编译时,取目录名。</li>
|
|
||||||
* <li>单文件编译时,取文件名去掉 .snow 后缀。</li>
|
|
||||||
* <li>否则默认 "program"。</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param sources 源文件路径列表
|
|
||||||
* @param outName 输出文件名(如有指定,否则为 null)
|
|
||||||
* @param dir 源码目录(如有指定,否则为 null)
|
|
||||||
* @return 推断出的输出文件路径(.water 文件)
|
|
||||||
*/
|
|
||||||
private static Path deriveOutputPath(List<Path> 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<IRFunction> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
package org.jcnc.snow.vm;
|
package org.jcnc.snow.vm;
|
||||||
|
|
||||||
|
import org.jcnc.snow.common.Mode;
|
||||||
import org.jcnc.snow.vm.execution.CommandLoader;
|
import org.jcnc.snow.vm.execution.CommandLoader;
|
||||||
import org.jcnc.snow.vm.engine.VMCommandExecutor;
|
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.engine.VirtualMachineEngine;
|
||||||
import org.jcnc.snow.vm.io.FilePathResolver;
|
import org.jcnc.snow.vm.io.FilePathResolver;
|
||||||
import org.jcnc.snow.vm.utils.VMStateLogger;
|
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.
|
* @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).
|
* 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
|
// Retrieve and validate file path
|
||||||
String filePath = FilePathResolver.getFilePath(args);
|
String filePath = FilePathResolver.getFilePath(args);
|
||||||
if (filePath == null) return;
|
if (filePath == null) return;
|
||||||
@ -55,7 +55,7 @@ public class VMInitializer {
|
|||||||
if (commands.isEmpty()) return;
|
if (commands.isEmpty()) return;
|
||||||
|
|
||||||
// Execute the commands using the virtual machine engine
|
// Execute the commands using the virtual machine engine
|
||||||
VirtualMachineEngine virtualMachineEngine = new VirtualMachineEngine(vmMode);
|
VirtualMachineEngine virtualMachineEngine = new VirtualMachineEngine();
|
||||||
VMCommandExecutor.executeInstructions(virtualMachineEngine, commands);
|
VMCommandExecutor.executeInstructions(virtualMachineEngine, commands);
|
||||||
|
|
||||||
// Print the virtual machine's state
|
// Print the virtual machine's state
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package org.jcnc.snow.vm;
|
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;
|
import static org.jcnc.snow.vm.VMInitializer.initializeAndRunVM;
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ import static org.jcnc.snow.vm.VMInitializer.initializeAndRunVM;
|
|||||||
*
|
*
|
||||||
* <p>This class provides the entry point to launch the virtual machine. The main method retrieves the file path
|
* <p>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
|
* 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}).</p>
|
* specified mode (e.g., {@link Mode#DEBUG}).</p>
|
||||||
*/
|
*/
|
||||||
public class VMLauncher {
|
public class VMLauncher {
|
||||||
|
|
||||||
@ -44,6 +45,6 @@ public class VMLauncher {
|
|||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// Call the method that initializes and runs the VM in DEBUG mode
|
// Call the method that initializes and runs the VM in DEBUG mode
|
||||||
initializeAndRunVM(args, VMMode.RUN);
|
initializeAndRunVM(args, Mode.RUN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
package org.jcnc.snow.vm.commands.flow.control;
|
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.interfaces.Command;
|
||||||
import org.jcnc.snow.vm.module.*;
|
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
|
* The CallCommand class implements the {@link Command} interface and represents a subroutine/function call
|
||||||
* instruction in the virtual machine.
|
* instruction in the virtual machine.
|
||||||
@ -32,18 +34,18 @@ public class CallCommand implements Command {
|
|||||||
* and control transfer to the target function address.
|
* and control transfer to the target function address.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param parts The instruction parameters. Must include:
|
* @param parts The instruction parameters. Must include:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code parts[0]}: The "CALL" operator.</li>
|
* <li>{@code parts[0]}: The "CALL" operator.</li>
|
||||||
* <li>{@code parts[1]}: The target address of the callee function.</li>
|
* <li>{@code parts[1]}: The target address of the callee function.</li>
|
||||||
* <li>{@code parts[2]}: The number of arguments to pass.</li>
|
* <li>{@code parts[2]}: The number of arguments to pass.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* @param currentPC The current program counter, used to record the return address for after the call.
|
* @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 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 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 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).
|
* @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 IllegalArgumentException If the instruction parameters are malformed or missing.
|
||||||
* @throws IllegalStateException If the operand stack does not contain enough arguments.
|
* @throws IllegalStateException If the operand stack does not contain enough arguments.
|
||||||
*/
|
*/
|
||||||
@ -61,13 +63,13 @@ public class CallCommand implements Command {
|
|||||||
int nArgs;
|
int nArgs;
|
||||||
try {
|
try {
|
||||||
targetAddr = Integer.parseInt(parts[1]);
|
targetAddr = Integer.parseInt(parts[1]);
|
||||||
nArgs = Integer.parseInt(parts[2]);
|
nArgs = Integer.parseInt(parts[2]);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new IllegalArgumentException("CALL: malformed operands", e);
|
throw new IllegalArgumentException("CALL: malformed operands", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build new frame & local table for callee */
|
/* 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 */
|
/* transfer arguments: operand stack top is last arg */
|
||||||
for (int slot = nArgs - 1; slot >= 0; slot--) {
|
for (int slot = nArgs - 1; slot >= 0; slot--) {
|
||||||
@ -80,7 +82,7 @@ public class CallCommand implements Command {
|
|||||||
new MethodContext("subroutine@" + targetAddr, null));
|
new MethodContext("subroutine@" + targetAddr, null));
|
||||||
callStack.pushFrame(newFrame);
|
callStack.pushFrame(newFrame);
|
||||||
|
|
||||||
System.out.println("\nCalling function at address: " + targetAddr);
|
print("\nCalling function at address: " + targetAddr);
|
||||||
return targetAddr; // jump
|
return targetAddr; // jump
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package org.jcnc.snow.vm.commands.flow.control;
|
|||||||
import org.jcnc.snow.vm.interfaces.Command;
|
import org.jcnc.snow.vm.interfaces.Command;
|
||||||
import org.jcnc.snow.vm.module.*;
|
import org.jcnc.snow.vm.module.*;
|
||||||
|
|
||||||
|
import static org.jcnc.snow.common.SnowConfig.print;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@code RET} instruction (method return).
|
* Implements the {@code RET} instruction (method return).
|
||||||
*
|
*
|
||||||
@ -44,7 +46,7 @@ public class RetCommand implements Command {
|
|||||||
finished.getLocalVariableStore().clearVariables();
|
finished.getLocalVariableStore().clearVariables();
|
||||||
|
|
||||||
int returnAddr = finished.getReturnAddress();
|
int returnAddr = finished.getReturnAddress();
|
||||||
System.out.println("\nReturn " + returnAddr);
|
print("\nReturn " + returnAddr);
|
||||||
return returnAddr;
|
return returnAddr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.jcnc.snow.vm.engine;
|
package org.jcnc.snow.vm.engine;
|
||||||
|
|
||||||
|
import org.jcnc.snow.common.Mode;
|
||||||
import org.jcnc.snow.vm.execution.CommandExecutionHandler;
|
import org.jcnc.snow.vm.execution.CommandExecutionHandler;
|
||||||
import org.jcnc.snow.vm.module.*;
|
import org.jcnc.snow.vm.module.*;
|
||||||
|
|
||||||
@ -53,10 +54,10 @@ public class VirtualMachineEngine {
|
|||||||
*
|
*
|
||||||
* @param vmMode execution mode (DEBUG / RUN)
|
* @param vmMode execution mode (DEBUG / RUN)
|
||||||
*/
|
*/
|
||||||
public VirtualMachineEngine(VMMode vmMode) {
|
public VirtualMachineEngine() {
|
||||||
this.operandStack = new OperandStack();
|
this.operandStack = new OperandStack();
|
||||||
this.callStack = new CallStack();
|
this.callStack = new CallStack();
|
||||||
this.localVariableStore = new LocalVariableStore(vmMode); // shared with root frame
|
this.localVariableStore = new LocalVariableStore(); // shared with root frame
|
||||||
this.commandExecutionHandler =
|
this.commandExecutionHandler =
|
||||||
new CommandExecutionHandler(operandStack, localVariableStore, callStack);
|
new CommandExecutionHandler(operandStack, localVariableStore, callStack);
|
||||||
this.programCounter = 0;
|
this.programCounter = 0;
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
package org.jcnc.snow.vm.module;
|
package org.jcnc.snow.vm.module;
|
||||||
|
|
||||||
|
import org.jcnc.snow.common.SnowConfig;
|
||||||
import org.jcnc.snow.vm.gui.LocalVariableStoreSwing;
|
import org.jcnc.snow.vm.gui.LocalVariableStoreSwing;
|
||||||
import org.jcnc.snow.vm.utils.LoggingUtils;
|
import org.jcnc.snow.vm.utils.LoggingUtils;
|
||||||
import org.jcnc.snow.vm.engine.VMMode;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code LocalVariableStore} represents a simple dynamically-sized
|
* The {@code LocalVariableStore} represents a simple dynamically-sized
|
||||||
@ -18,25 +17,23 @@ import java.util.Objects;
|
|||||||
public class LocalVariableStore {
|
public class LocalVariableStore {
|
||||||
|
|
||||||
private final ArrayList<Object> localVariables;
|
private final ArrayList<Object> localVariables;
|
||||||
private final VMMode vmMode;
|
|
||||||
|
|
||||||
/* ---------- construction ---------- */
|
/* ---------- construction ---------- */
|
||||||
|
|
||||||
public LocalVariableStore(VMMode vmMode, int initialCapacity) {
|
public LocalVariableStore(int initialCapacity) {
|
||||||
this.localVariables = new ArrayList<>(initialCapacity);
|
this.localVariables = new ArrayList<>(initialCapacity);
|
||||||
this.vmMode = vmMode;
|
|
||||||
handleMode();
|
handleMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalVariableStore(VMMode vmMode) {
|
public LocalVariableStore() {
|
||||||
this.localVariables = new ArrayList<>();
|
this.localVariables = new ArrayList<>();
|
||||||
this.vmMode = vmMode;
|
|
||||||
handleMode();
|
handleMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- public API ---------- */
|
/* ---------- 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) {
|
public void setVariable(int index, Object value) {
|
||||||
ensureCapacity(index + 1);
|
ensureCapacity(index + 1);
|
||||||
localVariables.set(index, value);
|
localVariables.set(index, value);
|
||||||
@ -46,10 +43,17 @@ public class LocalVariableStore {
|
|||||||
* 兼容早期实现: VM 指令译码器可直接调用 store / load
|
* 兼容早期实现: VM 指令译码器可直接调用 store / load
|
||||||
* 而无需关心内部命名差异。
|
* 而无需关心内部命名差异。
|
||||||
* ------------------------------------------------------------ */
|
* ------------------------------------------------------------ */
|
||||||
public void store(int index, Object value) { setVariable(index, value); }
|
public void store(int index, Object value) {
|
||||||
public Object load(int index) { return getVariable(index); }
|
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) {
|
public Object getVariable(int index) {
|
||||||
/* 修改点 #1 —— 自动扩容以避免 LOAD 越界异常 */
|
/* 修改点 #1 —— 自动扩容以避免 LOAD 越界异常 */
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@ -58,12 +62,16 @@ public class LocalVariableStore {
|
|||||||
return localVariables.get(index); // 可能为 null,符合 JVM 语义
|
return localVariables.get(index); // 可能为 null,符合 JVM 语义
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Exposes the backing list (read-only preferred). */
|
/**
|
||||||
|
* Exposes the backing list (read-only preferred).
|
||||||
|
*/
|
||||||
public ArrayList<Object> getLocalVariables() {
|
public ArrayList<Object> getLocalVariables() {
|
||||||
return localVariables;
|
return localVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Prints every slot to the logger. */
|
/**
|
||||||
|
* Prints every slot to the logger.
|
||||||
|
*/
|
||||||
public void printLv() {
|
public void printLv() {
|
||||||
if (localVariables.isEmpty()) {
|
if (localVariables.isEmpty()) {
|
||||||
LoggingUtils.logInfo("Local variable table is empty", "");
|
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() {
|
public void clearVariables() {
|
||||||
localVariables.clear();
|
localVariables.clear();
|
||||||
}
|
}
|
||||||
@ -99,7 +109,9 @@ public class LocalVariableStore {
|
|||||||
|
|
||||||
/* ---------- internal helpers ---------- */
|
/* ---------- internal helpers ---------- */
|
||||||
|
|
||||||
/** Ensures backing list can hold {@code minCapacity} slots. */
|
/**
|
||||||
|
* Ensures backing list can hold {@code minCapacity} slots.
|
||||||
|
*/
|
||||||
private void ensureCapacity(int minCapacity) {
|
private void ensureCapacity(int minCapacity) {
|
||||||
/* 修改点 #3 —— 使用 while 循环填充 null,确保 slot 可随机写入 */
|
/* 修改点 #3 —— 使用 while 循环填充 null,确保 slot 可随机写入 */
|
||||||
while (localVariables.size() < minCapacity) {
|
while (localVariables.size() < minCapacity) {
|
||||||
@ -107,10 +119,12 @@ public class LocalVariableStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Mode-specific UI hook (unchanged). */
|
/**
|
||||||
|
* Mode-specific UI hook (unchanged).
|
||||||
|
*/
|
||||||
private void handleMode() {
|
private void handleMode() {
|
||||||
/* no-op */
|
/* no-op */
|
||||||
if (Objects.requireNonNull(vmMode) == VMMode.DEBUG) {
|
if (SnowConfig.isDebug()) {
|
||||||
LocalVariableStoreSwing.display(this, "Local Variable Table");
|
LocalVariableStoreSwing.display(this, "Local Variable Table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package org.jcnc.snow.vm.utils;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
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.
|
* 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.
|
* 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) {
|
public static void logInfo(String title, String message) {
|
||||||
// Output the informational message to the console
|
// Output the informational message to the console
|
||||||
System.out.println(title + message);
|
print(title + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user