feat: 将真正的编译实现下沉到 pkg 层,避免 CLI 与 pkg 的重复实现
This commit is contained in:
parent
7aa4d7dcd0
commit
004bc076b1
@ -1,48 +1,20 @@
|
|||||||
package org.jcnc.snow.cli.commands;
|
package org.jcnc.snow.cli.commands;
|
||||||
|
|
||||||
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.cli.CLICommand;
|
import org.jcnc.snow.cli.CLICommand;
|
||||||
import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder;
|
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
import org.jcnc.snow.pkg.model.Project;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
import org.jcnc.snow.pkg.tasks.CompileTask;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
|
||||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|
||||||
import org.jcnc.snow.compiler.lexer.core.LexerEngine;
|
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
|
||||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
|
||||||
import org.jcnc.snow.compiler.parser.core.ParserEngine;
|
|
||||||
import org.jcnc.snow.compiler.parser.function.ASTPrinter;
|
|
||||||
import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner;
|
|
||||||
import org.jcnc.snow.vm.VMLauncher;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:将 .snow 源文件编译为 VM 字节码(.water 文件)。
|
* CLI 命令:编译当前项目。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持递归目录、多文件编译,可选编译后立即运行。<br>
|
* 负责读取项目描述文件并委托给 {@link CompileTask},
|
||||||
* 命令参数支持 run、-o、-d 等。
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* 用法示例:
|
|
||||||
* $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow …]
|
|
||||||
* </pre>
|
|
||||||
*/
|
*/
|
||||||
public final class CompileCommand implements CLICommand {
|
public final class CompileCommand implements CLICommand {
|
||||||
|
|
||||||
/* --------------------------------------------------------------------- */
|
|
||||||
/* CLICommand 接口实现 */
|
|
||||||
/* --------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
return "compile";
|
return "compile";
|
||||||
@ -60,198 +32,14 @@ public final class CompileCommand implements CLICommand {
|
|||||||
System.out.println("Options:");
|
System.out.println("Options:");
|
||||||
System.out.println(" run compile then run");
|
System.out.println(" run compile then run");
|
||||||
System.out.println(" -o <name> specify output base name (without .water suffix)");
|
System.out.println(" -o <name> specify output base name (without .water suffix)");
|
||||||
System.out.println(" -d <srcDir> recursively compile all .snow files in directory");
|
System.out.println(" -d <srcDir> recursively compile all .snow files in directory"); }
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------- */
|
|
||||||
/* 核心:执行 compile 子命令 */
|
|
||||||
/* --------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int execute(String[] args) throws Exception {
|
public int execute(String[] args) throws Exception {
|
||||||
/* ---------------- 解析命令行参数 ---------------- */
|
// 解析云项目描述文件(默认为工作目录下的 cloud.snow)
|
||||||
boolean runAfterCompile = false;
|
Project project = CloudDSLParser.parse(Paths.get("project.cloud"));
|
||||||
String outputName = null;
|
// 委托给 CompileTask 处理核心编译逻辑
|
||||||
Path dir = null;
|
new CompileTask(project, args).run();
|
||||||
List<Path> sources = new ArrayList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
String arg = args[i];
|
|
||||||
switch (arg) {
|
|
||||||
case "run" -> runAfterCompile = true;
|
|
||||||
case "-o" -> {
|
|
||||||
if (i + 1 < args.length) outputName = args[++i];
|
|
||||||
else {
|
|
||||||
System.err.println("Missing argument for -o");
|
|
||||||
printUsage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "-d" -> {
|
|
||||||
if (i + 1 < args.length) dir = Path.of(args[++i]);
|
|
||||||
else {
|
|
||||||
System.err.println("Missing argument for -d");
|
|
||||||
printUsage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
if (arg.endsWith(".snow")) {
|
|
||||||
sources.add(Path.of(arg));
|
|
||||||
} else {
|
|
||||||
System.err.println("Unknown option or file: " + arg);
|
|
||||||
printUsage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------- 如果指定了目录则递归收集所有 *.snow --------- */
|
|
||||||
if (dir != null) {
|
|
||||||
if (!Files.isDirectory(dir)) {
|
|
||||||
System.err.println("Not a directory: " + dir);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try (var stream = Files.walk(dir)) {
|
|
||||||
stream.filter(p -> p.toString().endsWith(".snow"))
|
|
||||||
.sorted() // 确保稳定顺序
|
|
||||||
.forEach(sources::add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sources.isEmpty()) {
|
|
||||||
System.err.println("No .snow source files found.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 多文件但未指定 -o 且非目录编译 —— 提示必须指定输出名 */
|
|
||||||
if (sources.size() > 1 && outputName == null && dir == null) {
|
|
||||||
System.err.println("Please specify output name using -o <name>");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
/* 1. 词法 + 语法分析;同时打印源代码 */
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
List<Node> allAst = new ArrayList<>();
|
|
||||||
|
|
||||||
System.out.println("## 编译器输出");
|
|
||||||
System.out.println("### Snow 源代码"); // ========== 新增:二级标题 ==========
|
|
||||||
|
|
||||||
for (Path p : sources) {
|
|
||||||
if (!Files.exists(p)) {
|
|
||||||
System.err.println("File not found: " + p);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String code = Files.readString(p, StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
// ------- 打印每个文件的源码 -------
|
|
||||||
System.out.println("#### " + p.getFileName());
|
|
||||||
System.out.println(code);
|
|
||||||
// --------------------------------------------------------
|
|
||||||
|
|
||||||
/* 词法 + 语法 */
|
|
||||||
LexerEngine lexer = new LexerEngine(code, p.toString());
|
|
||||||
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
|
|
||||||
allAst.addAll(new ParserEngine(ctx).parse());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
/* 2. 语义分析 */
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
/* 3. AST → IR,并把 main 函数调到首位 */
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
|
|
||||||
program = reorderForEntry(program);
|
|
||||||
|
|
||||||
/* ---------------- 打印 AST / IR ---------------- */
|
|
||||||
System.out.println("### AST");
|
|
||||||
ASTPrinter.printJson(allAst);
|
|
||||||
|
|
||||||
System.out.println("### IR");
|
|
||||||
System.out.println(program);
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
/* 4. IR → VM 指令 */
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
VMProgramBuilder builder = new VMProgramBuilder();
|
|
||||||
List<InstructionGenerator<? extends IRInstruction>> generators =
|
|
||||||
InstructionGeneratorProvider.defaultGenerators();
|
|
||||||
|
|
||||||
for (IRFunction fn : program.functions()) {
|
|
||||||
Map<IRVirtualRegister, Integer> slotMap =
|
|
||||||
new RegisterAllocator().allocate(fn);
|
|
||||||
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
|
|
||||||
}
|
|
||||||
List<String> finalCode = builder.build();
|
|
||||||
|
|
||||||
System.out.println("### VM code");
|
|
||||||
finalCode.forEach(System.out::println);
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
/* 5. 写出 .water 文件 */
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
Path outputFile = deriveOutputPath(sources, outputName, dir);
|
|
||||||
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
|
|
||||||
System.out.println("Written to " + outputFile.toAbsolutePath());
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
/* 6. 可选:立即运行 VM */
|
|
||||||
/* ----------------------------------------------------------------- */
|
|
||||||
if (runAfterCompile) {
|
|
||||||
System.out.println("\n=== Launching VM ===");
|
|
||||||
VMLauncher.main(new String[]{outputFile.toString()});
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* --------------------------------------------------------------------- */
|
|
||||||
/* 辅助方法 */
|
|
||||||
/* --------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据输入情况推断 .water 输出文件名:
|
|
||||||
* <ul>
|
|
||||||
* <li>若指定 -o,则直接使用</li>
|
|
||||||
* <li>目录编译:取目录名</li>
|
|
||||||
* <li>单文件编译:取文件名去掉 .snow</li>
|
|
||||||
* <li>其他情况兜底为 "program"</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
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 即入口。
|
|
||||||
*/
|
|
||||||
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,65 +1,41 @@
|
|||||||
|
|
||||||
package org.jcnc.snow.cli.commands;
|
package org.jcnc.snow.cli.commands;
|
||||||
|
|
||||||
import org.jcnc.snow.cli.CLICommand;
|
import org.jcnc.snow.cli.CLICommand;
|
||||||
import org.jcnc.snow.vm.VMLauncher;
|
import org.jcnc.snow.pkg.tasks.RunTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:运行已编译的 VM 字节码文件(.water)。
|
* CLI 命令:运行已编译的 VM 字节码文件(.water)。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于执行 VM 程序文件。支持传递额外 VM 参数,实际运行由 {@link VMLauncher#main(String[])} 完成。
|
* 仅解析参数并委托给 {@link RunTask},
|
||||||
|
* 将 VM 运行逻辑下沉至 pkg 层,保持 CLI 无状态、薄封装。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* 用法示例:
|
|
||||||
* $ snow run program.water [additional VM options]
|
|
||||||
* </pre>
|
|
||||||
*/
|
*/
|
||||||
public final class RunCommand implements CLICommand {
|
public final class RunCommand implements CLICommand {
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回命令名,用于 CLI 调用。
|
|
||||||
*
|
|
||||||
* @return 命令名称字符串("run")
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
return "run";
|
return "run";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回命令简介,用于 CLI 帮助或命令列表展示。
|
|
||||||
*
|
|
||||||
* @return 命令描述字符串
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String description() {
|
public String description() {
|
||||||
return "Execute compiled VM instructions.";
|
return "Run the compiled VM bytecode file (.water)";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 打印该命令的用法说明。
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void printUsage() {
|
|
||||||
System.out.println("""
|
|
||||||
Usage: snow run <program.water> [additional VM options]
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行 run 命令,运行指定的 VM 字节码文件。
|
|
||||||
*
|
|
||||||
* @param args 剩余参数(不含命令名),第一个应为 .water 文件路径,其后为可选 VM 参数
|
|
||||||
* @return 0 表示执行成功,1 表示参数错误
|
|
||||||
* @throws Exception VM 启动或执行过程中可能抛出的异常
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int execute(String[] args) throws Exception {
|
public int execute(String[] args) throws Exception {
|
||||||
if (args.length == 0) {
|
if (args.length == 0) {
|
||||||
printUsage();
|
printUsage();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
VMLauncher.main(args);
|
new RunTask(args).run();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void printUsage() {
|
||||||
|
System.out.println("Usage:");
|
||||||
|
System.out.println(" snow run <program.water> [vm-options]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +1,251 @@
|
|||||||
package org.jcnc.snow.pkg.tasks;
|
package org.jcnc.snow.pkg.tasks;
|
||||||
|
|
||||||
|
import org.jcnc.snow.cli.commands.CompileCommand;
|
||||||
import org.jcnc.snow.pkg.model.Project;
|
import org.jcnc.snow.pkg.model.Project;
|
||||||
|
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.ir.builder.IRProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerEngine;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.core.ParserEngine;
|
||||||
|
import org.jcnc.snow.compiler.parser.function.ASTPrinter;
|
||||||
|
import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner;
|
||||||
|
import org.jcnc.snow.vm.VMLauncher;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译项目源代码的任务实现。
|
* CLI 命令:将 .snow 源文件编译为 VM 字节码(.water 文件)。
|
||||||
* <p>
|
* <p>
|
||||||
* 实现 {@link Task} 接口,用于构建流程中的编译阶段。当前仅为示例,未集成实际编译器。
|
* 支持递归目录、多文件编译,可选编译后立即运行。<br>
|
||||||
|
* 命令参数支持 run、-o、-d 等。
|
||||||
* </p>
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 用法示例:
|
||||||
|
* $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow …]
|
||||||
|
* </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;
|
||||||
|
|
||||||
/**
|
public CompileTask(Project project, String[] args) {
|
||||||
* 创建 CompileTask 实例。
|
|
||||||
*
|
|
||||||
* @param project 目标项目
|
|
||||||
*/
|
|
||||||
public CompileTask(Project project) {
|
|
||||||
this.project = project;
|
this.project = project;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileTask(Project project) {
|
||||||
|
this(project, new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行编译任务,打印源代码目录和输出目录。
|
|
||||||
* <p>
|
|
||||||
* 实际编译尚未实现(TODO)。
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @throws Exception 预留,未来集成编译器可能抛出异常
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
// 获取源码目录和输出目录,默认分别为 "src" 和 "build/classes"
|
// 将任务委托给原始 execute 实现
|
||||||
Path srcDir = Path.of(project.getProperties().getOrDefault("src_dir", "src"));
|
execute(this.args);
|
||||||
Path outDir = Path.of(project.getProperties().getOrDefault("output_dir", "build/classes"));
|
}
|
||||||
System.out.println("[compile] sources=" + srcDir + " output=" + outDir);
|
|
||||||
// TODO: 集成实际的编译器
|
/* --------------------------------------------------------------------- */
|
||||||
|
/* 执行 compile 子命令 */
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
public int execute(String[] args) throws Exception {
|
||||||
|
/* ---------------- 解析命令行参数 ---------------- */
|
||||||
|
boolean runAfterCompile = false;
|
||||||
|
String outputName = null;
|
||||||
|
Path dir = null;
|
||||||
|
List<Path> sources = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
String arg = args[i];
|
||||||
|
switch (arg) {
|
||||||
|
case "run" -> runAfterCompile = true;
|
||||||
|
case "-o" -> {
|
||||||
|
if (i + 1 < args.length) outputName = args[++i];
|
||||||
|
else {
|
||||||
|
System.err.println("Missing argument for -o");
|
||||||
|
new CompileCommand().printUsage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "-d" -> {
|
||||||
|
if (i + 1 < args.length) dir = Path.of(args[++i]);
|
||||||
|
else {
|
||||||
|
System.err.println("Missing argument for -d");
|
||||||
|
new CompileCommand().printUsage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
if (arg.endsWith(".snow")) {
|
||||||
|
sources.add(Path.of(arg));
|
||||||
|
} else {
|
||||||
|
System.err.println("Unknown option or file: " + arg);
|
||||||
|
new CompileCommand().printUsage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------- 如果指定了目录则递归收集所有 *.snow --------- */
|
||||||
|
if (dir != null) {
|
||||||
|
if (!Files.isDirectory(dir)) {
|
||||||
|
System.err.println("Not a directory: " + dir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
try (var stream = Files.walk(dir)) {
|
||||||
|
stream.filter(p -> p.toString().endsWith(".snow"))
|
||||||
|
.sorted() // 确保稳定顺序
|
||||||
|
.forEach(sources::add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sources.isEmpty()) {
|
||||||
|
System.err.println("No .snow source files found.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 多文件但未指定 -o 且非目录编译 —— 提示必须指定输出名 */
|
||||||
|
if (sources.size() > 1 && outputName == null && dir == null) {
|
||||||
|
System.err.println("Please specify output name using -o <name>");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/* 1. 词法 + 语法分析;同时打印源代码 */
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
List<Node> allAst = new ArrayList<>();
|
||||||
|
|
||||||
|
System.out.println("## 编译器输出");
|
||||||
|
System.out.println("### Snow 源代码"); // ========== 新增:二级标题 ==========
|
||||||
|
|
||||||
|
for (Path p : sources) {
|
||||||
|
if (!Files.exists(p)) {
|
||||||
|
System.err.println("File not found: " + p);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String code = Files.readString(p, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// ------- 打印每个文件的源码 -------
|
||||||
|
System.out.println("#### " + p.getFileName());
|
||||||
|
System.out.println(code);
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
/* 词法 + 语法 */
|
||||||
|
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||||
|
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
|
||||||
|
allAst.addAll(new ParserEngine(ctx).parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/* 2. 语义分析 */
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/* 3. AST → IR,并把 main 函数调到首位 */
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
|
||||||
|
program = reorderForEntry(program);
|
||||||
|
|
||||||
|
/* ---------------- 打印 AST / IR ---------------- */
|
||||||
|
System.out.println("### AST");
|
||||||
|
ASTPrinter.printJson(allAst);
|
||||||
|
|
||||||
|
System.out.println("### IR");
|
||||||
|
System.out.println(program);
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/* 4. IR → VM 指令 */
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
VMProgramBuilder builder = new VMProgramBuilder();
|
||||||
|
List<InstructionGenerator<? extends IRInstruction>> generators =
|
||||||
|
InstructionGeneratorProvider.defaultGenerators();
|
||||||
|
|
||||||
|
for (IRFunction fn : program.functions()) {
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap =
|
||||||
|
new RegisterAllocator().allocate(fn);
|
||||||
|
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
|
||||||
|
}
|
||||||
|
List<String> finalCode = builder.build();
|
||||||
|
|
||||||
|
System.out.println("### VM code");
|
||||||
|
finalCode.forEach(System.out::println);
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/* 5. 写出 .water 文件 */
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
Path outputFile = deriveOutputPath(sources, outputName, dir);
|
||||||
|
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
|
||||||
|
System.out.println("Written to " + outputFile.toAbsolutePath());
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
/* 6. 可选:立即运行 VM */
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
if (runAfterCompile) {
|
||||||
|
System.out.println("\n=== Launching VM ===");
|
||||||
|
VMLauncher.main(new String[]{outputFile.toString()});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
/* 辅助方法 */
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据输入情况推断 .water 输出文件名:
|
||||||
|
* <ul>
|
||||||
|
* <li>若指定 -o,则直接使用</li>
|
||||||
|
* <li>目录编译:取目录名</li>
|
||||||
|
* <li>单文件编译:取文件名去掉 .snow</li>
|
||||||
|
* <li>其他情况兜底为 "program"</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
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 即入口。
|
||||||
|
*/
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/main/java/org/jcnc/snow/pkg/tasks/RunTask.java
Normal file
39
src/main/java/org/jcnc/snow/pkg/tasks/RunTask.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
package org.jcnc.snow.pkg.tasks;
|
||||||
|
|
||||||
|
import org.jcnc.snow.vm.VMLauncher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务:执行已编译的 VM 字节码文件(.water)。
|
||||||
|
* <p>
|
||||||
|
* 作为 CLI、IDE 插件或其他宿主环境启动虚拟机的统一入口,
|
||||||
|
* 将调用 {@link VMLauncher#main(String[])} 执行。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class RunTask implements Task {
|
||||||
|
|
||||||
|
/** 传递给 VM 的完整参数列表(第一个应为 .water 文件路径) */
|
||||||
|
private final String[] args;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建运行任务。
|
||||||
|
*
|
||||||
|
* @param args VM 参数数组(第一个为程序路径,其后为可选参数)
|
||||||
|
*/
|
||||||
|
public RunTask(String... args) {
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行运行任务,内部委托 {@link VMLauncher#main(String[])}。
|
||||||
|
*
|
||||||
|
* @throws Exception 虚拟机启动或运行期间抛出的异常
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
if (args == null || args.length == 0) {
|
||||||
|
throw new IllegalArgumentException("VM run requires at least the program file path.");
|
||||||
|
}
|
||||||
|
VMLauncher.main(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user