diff --git a/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java b/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java
index 4e342af..80b5169 100644
--- a/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java
+++ b/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java
@@ -9,7 +9,6 @@ package org.jcnc.snow.compiler.cli;
*
可通过 {@link java.util.ServiceLoader ServiceLoader} 自动发现,或直接在 {@link SnowCLI} 注册。
*
*
-
*/
public interface CLICommand {
@@ -31,7 +30,8 @@ public interface CLICommand {
* 打印命令的专用 usage 信息(可选实现)。
* 可覆盖此方法自定义帮助信息,默认无操作。
*/
- default void printUsage() {}
+ default void printUsage() {
+ }
/**
* 执行命令逻辑。
diff --git a/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java
index 1d1dbf4..d45675c 100644
--- a/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java
+++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java
@@ -91,7 +91,6 @@ public final class SnowCLI {
*/
private void printGlobalUsage() {
System.out.println("""
- Snow Programming Language CLI
Usage: snow [options]
Available commands:""");
diff --git a/src/main/java/org/jcnc/snow/compiler/cli/commands/CompileCommand.java b/src/main/java/org/jcnc/snow/compiler/cli/commands/CompileCommand.java
index cfa7e36..d26fa20 100644
--- a/src/main/java/org/jcnc/snow/compiler/cli/commands/CompileCommand.java
+++ b/src/main/java/org/jcnc/snow/compiler/cli/commands/CompileCommand.java
@@ -1,11 +1,11 @@
package org.jcnc.snow.compiler.cli.commands;
-import org.jcnc.snow.compiler.cli.CLICommand;
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.cli.CLICommand;
import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder;
import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.core.IRInstruction;
@@ -19,91 +19,119 @@ import org.jcnc.snow.compiler.parser.function.ASTPrinter;
import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner;
import org.jcnc.snow.vm.VMLauncher;
-import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
/**
- *
* 编译器命令行入口:实现 `snow compile` 命令。
- * 支持编译一个或多个 .snow 源文件为 VM 字节码文件,或编译后直接运行。
- *
- *
- * - 支持编译单个文件、多个文件、或目录递归批量编译。
- * - 支持 compile-only 或 compile-then-run 模式。
- * - 完成词法、语法、语义分析,IR 构建,寄存器分配,VM 指令生成。
- *
- *
- * 用法:
- * snow compile foo.snow
- * snow compile -d src
- * snow compile run foo.snow
- *
+ * 支持编译一个或多个 .snow 源文件为 VM 字节码文件,选项 -o 指定输出名称(不含后缀),
+ * -d 递归目录编译(输出名自动取目录名),输出后缀统一为 .water。
*/
public final class CompileCommand implements CLICommand {
- /**
- * 获取命令名称。
- *
- * @return 命令名 "compile"
- */
@Override
- public String name() { return "compile"; }
+ public String name() {
+ return "compile";
+ }
- /**
- * 获取命令的简要描述。
- *
- * @return 命令描述文本
- */
@Override
public String description() {
- return "Compile .snow source files into VM byte-code.";
+ return "Compile .snow source files into VM byte-code (.water).";
}
- /**
- * 打印用法说明。
- */
@Override
public void printUsage() {
- System.out.println("""
- Usage:
- snow compile [file2.snow …] (compile only)
- snow compile -d (recursively compile)
- snow compile run […] (compile then run)
- """);
+ System.out.println("Usage:");
+ System.out.println(" snow compile [run] [-o ] [-d ] [file1.snow file2.snow …]");
+ System.out.println("Options:");
+ System.out.println(" run compile then run");
+ System.out.println(" -o specify output base name (without .water suffix)");
+ System.out.println(" -d recursively compile all .snow files in directory");
+ }
+
+ 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");
}
- /**
- * 执行 compile 命令。
- *
- * @param args 命令行参数
- * @return 0 表示成功,非0表示错误
- * @throws Exception 发生任何编译、文件、运行错误
- */
@Override
public int execute(String[] args) throws Exception {
- // 0. 检查是否包含 run
boolean runAfterCompile = false;
- int offset = 0;
- if (args.length > 0 && "run".equals(args[0])) {
- runAfterCompile = true;
- offset = 1;
- }
- if (args.length - offset == 0) {
- printUsage();
- return 1;
- }
- String[] srcArgs = Arrays.copyOfRange(args, offset, args.length);
+ String outputName = null;
+ Path dir = null;
+ List sources = new ArrayList<>();
+
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ switch (arg) {
+ case "run":
+ runAfterCompile = true;
+ break;
+ case "-o":
+ if (i + 1 < args.length) {
+ outputName = args[++i];
+ } else {
+ System.err.println("Missing argument for -o");
+ printUsage();
+ return 1;
+ }
+ break;
+ case "-d":
+ if (i + 1 < args.length) {
+ dir = Path.of(args[++i]);
+ } else {
+ System.err.println("Missing argument for -d");
+ printUsage();
+ return 1;
+ }
+ break;
+ 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);
+ }
+ }
- List sources = collectSources(srcArgs);
if (sources.isEmpty()) {
System.err.println("No .snow source files found.");
return 1;
}
- // 1. 词法+语法分析,合并AST
+ // 多文件非目录编译时必须指定 -o
+ if (sources.size() > 1 && outputName == null && dir == null) {
+ System.err.println("Please specify output name using -o ");
+ return 1;
+ }
+
+ // 1. 词法+语法分析
List allAst = new ArrayList<>();
for (Path p : sources) {
if (!Files.exists(p)) {
@@ -119,9 +147,9 @@ public final class CompileCommand implements CLICommand {
// 2. 语义分析
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
- // 3. AST -> IR
+ // 3. AST -> IR 并重排序入口
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
- program = reorderForEntry(program); // 保证入口 main 在首位
+ program = reorderForEntry(program);
System.out.println("## 编译器输出");
System.out.println("### AST");
@@ -129,11 +157,10 @@ public final class CompileCommand implements CLICommand {
System.out.println("### IR");
System.out.println(program);
- // 4. IR -> VM指令
+ // 4. IR -> VM 指令
VMProgramBuilder builder = new VMProgramBuilder();
List> generators =
InstructionGeneratorProvider.defaultGenerators();
-
for (IRFunction fn : program.functions()) {
Map slotMap =
new RegisterAllocator().allocate(fn);
@@ -144,12 +171,12 @@ public final class CompileCommand implements CLICommand {
System.out.println("### VM code");
finalCode.forEach(System.out::println);
- // 5. 将VM指令写入文件
- Path outputFile = deriveOutputPath(sources);
+ // 5. 写出 .water 文件
+ Path outputFile = deriveOutputPath(sources, outputName, dir);
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
System.out.println("Written to " + outputFile.toAbsolutePath());
- // 6. 若指定run,立即执行
+ // 6. 执行
if (runAfterCompile) {
System.out.println("\n=== Launching VM ===");
VMLauncher.main(new String[]{outputFile.toString()});
@@ -158,76 +185,18 @@ public final class CompileCommand implements CLICommand {
}
/**
- * 收集所有待编译的源文件。
- *
- * - 参数 "-d ":递归收集目录下所有 .snow 文件
- * - 否则认为是文件列表参数
- *
- *
- * @param args 命令行参数
- * @return 源文件路径列表,若未找到返回空列表
- * @throws IOException 文件IO错误
- */
- private static List collectSources(String[] args) throws IOException {
- if (args.length == 2 && "-d".equals(args[0])) {
- Path dir = Path.of(args[1]);
- if (!Files.isDirectory(dir)) {
- System.err.println("Not a directory: " + dir);
- return List.of();
- }
- try (var stream = Files.walk(dir)) {
- return stream.filter(p -> p.toString().endsWith(".snow"))
- .sorted() // 稳定顺序,方便比对输出
- .toList();
- }
- }
- // 普通文件参数
- return Arrays.stream(args).map(Path::of).toList();
- }
-
- /**
- * 保证 main 函数排在 Program.functions()[0],使 PC=0 即为程序入口。
- *
- * - 若存在 main,则与第0个函数互换
- * - 若不存在 main,则顺序不变(后续语义分析会报错)
- *
- *
- * @param in 原始IR程序对象
- * @return 处理后的IR程序对象
+ * 保证 main 函数为程序入口
*/
private static IRProgram reorderForEntry(IRProgram in) {
List ordered = new ArrayList<>(in.functions());
- int idx = -1;
for (int i = 0; i < ordered.size(); i++) {
if ("main".equals(ordered.get(i).name())) {
- idx = i;
+ Collections.swap(ordered, 0, i);
break;
}
}
- if (idx > 0) Collections.swap(ordered, 0, idx);
-
IRProgram out = new IRProgram();
ordered.forEach(out::add);
return out;
}
-
- /**
- * 推断输出文件名。
- *
- * - 单文件编译:输出同名 .vm 文件
- * - 多文件/目录编译:输出 "program.vm" 到当前目录
- *
- *
- * @param sources 输入源文件路径列表
- * @return 输出的 VM 文件路径
- */
- private static Path deriveOutputPath(List sources) {
- if (sources.size() == 1) {
- Path src = sources.getFirst();
- String name = src.getFileName().toString()
- .replaceFirst("\\.snow$", "");
- return src.resolveSibling(name + ".vm");
- }
- return Path.of("program.vm");
- }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/cli/commands/RunCommand.java b/src/main/java/org/jcnc/snow/compiler/cli/commands/RunCommand.java
index fb737d4..92373a5 100644
--- a/src/main/java/org/jcnc/snow/compiler/cli/commands/RunCommand.java
+++ b/src/main/java/org/jcnc/snow/compiler/cli/commands/RunCommand.java
@@ -7,7 +7,7 @@ import org.jcnc.snow.vm.VMLauncher;
*
* 命令实现:`snow run`
*
- * 用于运行已编译的 VM 字节码文件(.vm)。
+ * 用于运行已编译的 VM 字节码文件(.water)。
*
*
* - 支持传递额外 VM 参数。
@@ -15,7 +15,7 @@ import org.jcnc.snow.vm.VMLauncher;
*
*
* 用法:
- * snow run program.vm
+ * snow run program.water
*
*/
public final class RunCommand implements CLICommand {
@@ -26,7 +26,9 @@ public final class RunCommand implements CLICommand {
* @return "run"
*/
@Override
- public String name() { return "run"; }
+ public String name() {
+ return "run";
+ }
/**
* 获取命令描述。
@@ -44,7 +46,7 @@ public final class RunCommand implements CLICommand {
@Override
public void printUsage() {
System.out.println("""
- Usage: snow run [additional VM options]
+ Usage: snow run [additional VM options]
""");
}
@@ -61,7 +63,6 @@ public final class RunCommand implements CLICommand {
printUsage();
return 1;
}
- // 直接复用 VM 启动逻辑
VMLauncher.main(args);
return 0;
}