From 49f91e3b4edb58e8ab8b1fe846126301704662bf Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 17 Jun 2025 18:34:30 +0800 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E7=94=9F=E6=88=90=E5=99=A8=E9=9B=86=E4=B8=AD=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InstructionGeneratorProvider.java | 42 +++++++++++++++++++ .../jcnc/snow/compiler/cli/SnowCompiler.java | 26 +++++------- 2 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/backend/generator/InstructionGeneratorProvider.java diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/InstructionGeneratorProvider.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/InstructionGeneratorProvider.java new file mode 100644 index 0000000..2b2517d --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/InstructionGeneratorProvider.java @@ -0,0 +1,42 @@ +package org.jcnc.snow.compiler.backend.generator; + +import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.ir.core.IRInstruction; + +import java.util.List; + +/** + * 指令生成器集中注册表。 + * + *

本类在类加载阶段即完成所有后端 {@link InstructionGenerator} 实例 + * 的创建,并以不可变列表形式对外暴露。 + */ +public final class InstructionGeneratorProvider { + + /** 工具类禁止实例化。 */ + private InstructionGeneratorProvider() { /* no-instance */ } + + /** 缺省指令生成器列表(不可修改,顺序即执行顺序)。 */ + private static final List> DEFAULT = + List.of( + new LoadConstGenerator(), // 常量加载 + new BinaryOpGenerator(), // 二元运算 + new UnaryOpGenerator(), // 一元运算 + new CallGenerator(), // 函数调用 + new ReturnGenerator(), // 函数返回 + new LabelGenerator(), // 标签定义 + new JumpGenerator(), // 无条件跳转 + new CmpJumpGenerator() // 条件跳转 + ); + + /** + * 返回生产环境使用的缺省指令生成器列表。 + * 该列表为不可变集合,如尝试修改将抛出 + * {@link UnsupportedOperationException}。 + * + * @return 不可变的 {@code List} 实例 + */ + public static List> defaultGenerators() { + return DEFAULT; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java b/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java index fa0831a..512da03 100644 --- a/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java +++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java @@ -4,11 +4,12 @@ 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.*; +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; @@ -20,7 +21,8 @@ import org.jcnc.snow.vm.engine.VirtualMachineEngine; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; /** @@ -78,19 +80,10 @@ public class SnowCompiler { /* ---------- 5. IR → VM 指令 ---------- */ VMProgramBuilder builder = new VMProgramBuilder(); - List> generators = Arrays.asList( - new LoadConstGenerator(), - new BinaryOpGenerator(), - new UnaryOpGenerator(), - new CallGenerator(), - new ReturnGenerator(), - new LabelGenerator(), - new JumpGenerator(), - new CmpJumpGenerator() - ); + List> generators = InstructionGeneratorProvider.defaultGenerators(); for (IRFunction fn : program.functions()) { - Map slotMap = + Map slotMap = new RegisterAllocator().allocate(fn); new VMCodeGenerator(slotMap, builder, generators).generate(fn); } @@ -108,8 +101,8 @@ public class SnowCompiler { /** * 根据参数收集待编译文件: - * - snow file1 file2 … ← 多文件 / 单文件 - * - snow -d srcDir ← 目录递归所有 *.snow + * - snow file1 file2 … ← 多文件 / 单文件 + * - snow -d srcDir ← 目录递归所有 *.snow */ private static List collectSources(String[] args) throws IOException { if (args.length == 2 && "-d".equals(args[0])) { @@ -137,7 +130,8 @@ public class SnowCompiler { int idx = -1; for (int i = 0; i < ordered.size(); i++) { if ("main".equals(ordered.get(i).name())) { - idx = i; break; + idx = i; + break; } } if (idx > 0) Collections.swap(ordered, 0, idx); From a8d9bbe81d8dbe32827c85227d5470a3d07746c8 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 17 Jun 2025 20:16:55 +0800 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0Snow=20?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E7=BB=9F=E4=B8=80=E5=91=BD=E4=BB=A4=E8=A1=8C?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=EF=BC=88CLI=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- src/main/java/module-info.java | 1 + .../jcnc/snow/compiler/cli/CLICommand.java | 44 ++++ .../org/jcnc/snow/compiler/cli/SnowCLI.java | 120 +++++++++ .../jcnc/snow/compiler/cli/SnowCompiler.java | 143 ----------- .../compiler/cli/commands/CompileCommand.java | 233 ++++++++++++++++++ .../compiler/cli/commands/RunCommand.java | 68 +++++ 7 files changed, 467 insertions(+), 144 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java create mode 100644 src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java delete mode 100644 src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java create mode 100644 src/main/java/org/jcnc/snow/compiler/cli/commands/CompileCommand.java create mode 100644 src/main/java/org/jcnc/snow/compiler/cli/commands/RunCommand.java diff --git a/pom.xml b/pom.xml index ba9e035..edc8a5d 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ - org.jcnc.snow.compiler.cli.SnowCompiler + org.jcnc.snow.compiler.cli.SnowCLI true diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4759b93..3f1c505 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,5 @@ module org.jcnc.snow.compiler { + uses org.jcnc.snow.compiler.cli.CLICommand; requires java.desktop; requires java.logging; exports org.jcnc.snow.compiler.ir.core; diff --git a/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java b/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java new file mode 100644 index 0000000..4e342af --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/cli/CLICommand.java @@ -0,0 +1,44 @@ +package org.jcnc.snow.compiler.cli; + +/** + *

+ * 所有子命令(如 compile、run 等)都必须实现的命令接口。 + *

+ * + *

+ + */ +public interface CLICommand { + + /** + * 获取命令的名称(如 "compile"、"run")。 + * + * @return 命令名字符串 + */ + String name(); + + /** + * 获取命令的一行简介(用于 help 列表)。 + * + * @return 命令描述字符串 + */ + String description(); + + /** + * 打印命令的专用 usage 信息(可选实现)。 + * 可覆盖此方法自定义帮助信息,默认无操作。 + */ + default void printUsage() {} + + /** + * 执行命令逻辑。 + * + * @param args 传递给子命令的参数(不含命令名本身) + * @return 进程退出码(0 为成功,非0为错误) + * @throws Exception 可抛出任意异常,框架会统一捕获和输出 + */ + int execute(String[] args) throws Exception; +} diff --git a/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java new file mode 100644 index 0000000..1d1dbf4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java @@ -0,0 +1,120 @@ +package org.jcnc.snow.compiler.cli; + +import org.jcnc.snow.compiler.cli.commands.CompileCommand; +import org.jcnc.snow.compiler.cli.commands.RunCommand; + +import java.util.*; + +/** + *

+ * Snow 语言统一命令行入口(CLI)。 + *
+ * 负责命令注册、解析与调度。 + *

+ *
+ * 示例用法:
+ *   $ snow help
+ *   $ snow compile main.snow
+ *   $ snow run     main.vm
+ * 
+ * + */ +public final class SnowCLI { + + /** 命令注册表,按插入顺序保存命令名到实现的映射。 */ + private final Map registry = new LinkedHashMap<>(); + + /** + * 构造 CLI,自动注册所有可用命令。 + * + */ + public SnowCLI() { + // 1. 自动发现 ServiceLoader 扩展命令 + ServiceLoader.load(CLICommand.class).forEach(this::register); + // 2. 注册核心命令,保证 CLI 可用 + register(new CompileCommand()); + register(new RunCommand()); + } + + /** + * 注册一个命令到 CLI(命令名唯一,若已注册则跳过)。 + * + * @param cmd 待注册命令 + */ + private void register(CLICommand cmd) { + registry.putIfAbsent(cmd.name(), cmd); + } + + /** + * 解析命令行参数并执行相应命令。 + * + * @param args 命令行参数 + * @return 进程退出码(0=成功, 1=未知命令, -1=命令异常) + */ + public int run(String[] args) { + // 无参数或 help,打印全局用法 + if (args.length == 0 + || Set.of("help", "-h", "--help").contains(args[0])) { + printGlobalUsage(); + return 0; + } + + // 根据命令名查找注册表 + CLICommand cmd = registry.get(args[0]); + if (cmd == null) { + System.err.printf("Unknown command: %s%n%n", args[0]); + printGlobalUsage(); + return 1; + } + + // 提取命令余下参数(不包含命令名) + String[] sub = Arrays.copyOfRange(args, 1, args.length); + try { + return cmd.execute(sub); + } catch (Exception e) { + System.err.printf("Error executing command '%s': %s%n", + cmd.name(), e.getMessage()); + e.printStackTrace(System.err); + return -1; + } + } + + /** + * 打印全局帮助信息(所有已注册命令的 usage)。 + */ + private void printGlobalUsage() { + System.out.println(""" + Snow Programming Language CLI + Usage: snow [options] + + Available commands:"""); + int pad = registry.keySet().stream() + .mapToInt(String::length).max().orElse(10) + 2; + registry.values().stream() + .sorted(Comparator.comparing(CLICommand::name)) + .forEach(c -> System.out.printf(" %-" + pad + "s%s%n", + c.name(), c.description())); + + System.out.println(""" + + Use 'snow --help' for command-specific details. + """); + } + + /** + * CLI 程序主入口。 + * + * @param args 命令行参数 + */ + public static void main(String[] args) { + int code = new SnowCLI().run(args); + System.exit(code); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java b/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java deleted file mode 100644 index 512da03..0000000 --- a/src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.jcnc.snow.compiler.cli; - -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.engine.VMMode; -import org.jcnc.snow.vm.engine.VirtualMachineEngine; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; - -/** - * SnowCompiler CLI —— 多文件 / 单文件 / 目录 模式。 - */ -public class SnowCompiler { - - public static void main(String[] args) throws IOException { - if (args.length == 0) { - System.err.println(""" - Usage: - snow [file2.snow …] - snow -d (compile all *.snow recursively) - """); - return; - } - - /* ---------- 1. 收集所有待编译源码 ---------- */ - List sources = collectSources(args); - if (sources.isEmpty()) { - System.err.println("No .snow source files found."); - return; - } - - /* ---------- 2. 逐个词法+语法分析,合并 AST ---------- */ - List allAst = new ArrayList<>(); - for (Path p : sources) { - if (!Files.exists(p)) { - System.err.println("File not found: " + p); - return; - } - 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()); - } - - /* ---------- 3. 语义分析 ---------- */ - SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false); - - /* ---------- 4. AST → IR ---------- */ - IRProgram program = new IRProgramBuilder().buildProgram(allAst); - program = reorderForEntry(program); // 保证入口 main 在首位 - - System.out.println("## 编译器输出"); - System.out.println("### AST"); - ASTPrinter.printJson(allAst); - System.out.println("### IR"); - System.out.println(program); - - /* ---------- 5. IR → VM 指令 ---------- */ - VMProgramBuilder builder = new VMProgramBuilder(); - List> generators = InstructionGeneratorProvider.defaultGenerators(); - - for (IRFunction fn : program.functions()) { - Map slotMap = - new RegisterAllocator().allocate(fn); - new VMCodeGenerator(slotMap, builder, generators).generate(fn); - } - List finalCode = builder.build(); - - System.out.println("### VM code"); - finalCode.forEach(System.out::println); - - /* ---------- 6. 运行虚拟机 ---------- */ - VirtualMachineEngine vm = new VirtualMachineEngine(VMMode.RUN); - vm.execute(finalCode); - vm.printLocalVariables(); - } - - - /** - * 根据参数收集待编译文件: - * - snow file1 file2 … ← 多文件 / 单文件 - * - snow -d srcDir ← 目录递归所有 *.snow - */ - 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,则保持原顺序(语义分析会报错)。 - */ - 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; - break; - } - } - if (idx > 0) Collections.swap(ordered, 0, idx); - - IRProgram out = new IRProgram(); - ordered.forEach(out::add); - return out; - } -} 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 new file mode 100644 index 0000000..cfa7e36 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/cli/commands/CompileCommand.java @@ -0,0 +1,233 @@ +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.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.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 + *

+ */ +public final class CompileCommand implements CLICommand { + + /** + * 获取命令名称。 + * + * @return 命令名 "compile" + */ + @Override + public String name() { return "compile"; } + + /** + * 获取命令的简要描述。 + * + * @return 命令描述文本 + */ + @Override + public String description() { + return "Compile .snow source files into VM byte-code."; + } + + /** + * 打印用法说明。 + */ + @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) + """); + } + + /** + * 执行 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); + + List sources = collectSources(srcArgs); + if (sources.isEmpty()) { + System.err.println("No .snow source files found."); + return 1; + } + + // 1. 词法+语法分析,合并AST + List allAst = new ArrayList<>(); + 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); + 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 + IRProgram program = new IRProgramBuilder().buildProgram(allAst); + program = reorderForEntry(program); // 保证入口 main 在首位 + + System.out.println("## 编译器输出"); + System.out.println("### AST"); + ASTPrinter.printJson(allAst); + System.out.println("### IR"); + System.out.println(program); + + // 4. IR -> VM指令 + VMProgramBuilder builder = new VMProgramBuilder(); + List> generators = + InstructionGeneratorProvider.defaultGenerators(); + + for (IRFunction fn : program.functions()) { + Map slotMap = + new RegisterAllocator().allocate(fn); + new VMCodeGenerator(slotMap, builder, generators).generate(fn); + } + List finalCode = builder.build(); + + System.out.println("### VM code"); + finalCode.forEach(System.out::println); + + // 5. 将VM指令写入文件 + Path outputFile = deriveOutputPath(sources); + Files.write(outputFile, finalCode, StandardCharsets.UTF_8); + System.out.println("Written to " + outputFile.toAbsolutePath()); + + // 6. 若指定run,立即执行 + if (runAfterCompile) { + System.out.println("\n=== Launching VM ==="); + VMLauncher.main(new String[]{outputFile.toString()}); + } + return 0; + } + + /** + * 收集所有待编译的源文件。 + *
    + *
  • 参数 "-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程序对象 + */ + 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; + 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 new file mode 100644 index 0000000..fb737d4 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/cli/commands/RunCommand.java @@ -0,0 +1,68 @@ +package org.jcnc.snow.compiler.cli.commands; + +import org.jcnc.snow.compiler.cli.CLICommand; +import org.jcnc.snow.vm.VMLauncher; + +/** + *

+ * 命令实现:`snow run` + *
+ * 用于运行已编译的 VM 字节码文件(.vm)。 + *

+ *
    + *
  • 支持传递额外 VM 参数。
  • + *
  • 实际执行通过 {@link VMLauncher#main(String[])} 入口完成。
  • + *
+ *

+ * 用法:
+ * snow run program.vm + *

+ */ +public final class RunCommand implements CLICommand { + + /** + * 获取命令名。 + * + * @return "run" + */ + @Override + public String name() { return "run"; } + + /** + * 获取命令描述。 + * + * @return 命令简介 + */ + @Override + public String description() { + return "Execute compiled VM instructions."; + } + + /** + * 打印该命令的用法说明。 + */ + @Override + public void printUsage() { + System.out.println(""" + Usage: snow run [additional VM options] + """); + } + + /** + * 执行 run 命令,运行 VM 指令文件。 + * + * @param args 剩余参数(不含命令名),第一个应为 .vm 文件路径 + * @return 0 表示成功,1 表示参数错误 + * @throws Exception 运行 VM 时可能抛出的异常 + */ + @Override + public int execute(String[] args) throws Exception { + if (args.length == 0) { + printUsage(); + return 1; + } + // 直接复用 VM 启动逻辑 + VMLauncher.main(args); + return 0; + } +} From 9bb624f024e0dcc583681396cee0d717f8dbcefd Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 17 Jun 2025 23:25:26 +0800 Subject: [PATCH 03/19] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E4=B8=BAwater=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcnc/snow/compiler/cli/CLICommand.java | 4 +- .../org/jcnc/snow/compiler/cli/SnowCLI.java | 1 - .../compiler/cli/commands/CompileCommand.java | 217 ++++++++---------- .../compiler/cli/commands/RunCommand.java | 11 +- 4 files changed, 101 insertions(+), 132 deletions(-) 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; } From 2f473d1e13e7ed6a68e7f006124c799dfc12e12d Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 17 Jun 2025 23:29:19 +0800 Subject: [PATCH 04/19] =?UTF-8?q?docs:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=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/compiler/cli/SnowCLI.java | 6 ------ 1 file changed, 6 deletions(-) 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 d45675c..87b50a2 100644 --- a/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java +++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java @@ -11,12 +11,6 @@ import java.util.*; *
    * 负责命令注册、解析与调度。 *

    - *
    - * 示例用法:
    - *   $ snow help
    - *   $ snow compile main.snow
    - *   $ snow run     main.vm
    - * 
    *
      *
    • 支持核心命令自动注册(compile/run)。
    • *
    • 支持通过 ServiceLoader 自动发现并注册第三方命令。
    • From ee09f14bfa950e9fb528321a1a099cbd0a2561d0 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 17 Jun 2025 23:46:02 +0800 Subject: [PATCH 05/19] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0cli=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jcnc/snow/compiler/cli/SnowCLI.java | 166 ++++++++---------- 1 file changed, 71 insertions(+), 95 deletions(-) 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 87b50a2..5270dc3 100644 --- a/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java +++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java @@ -3,111 +3,87 @@ package org.jcnc.snow.compiler.cli; import org.jcnc.snow.compiler.cli.commands.CompileCommand; import org.jcnc.snow.compiler.cli.commands.RunCommand; -import java.util.*; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; /** - *

      - * Snow 语言统一命令行入口(CLI)。 - *
      - * 负责命令注册、解析与调度。 - *

      - *
        - *
      • 支持核心命令自动注册(compile/run)。
      • - *
      • 支持通过 ServiceLoader 自动发现并注册第三方命令。
      • - *
      • 统一异常处理和 usage 帮助输出。
      • - *
      + * SnowCLI 是项目的命令行入口类,负责解析用户输入、分发子命令并统一处理帮助和错误。 */ -public final class SnowCLI { - - /** 命令注册表,按插入顺序保存命令名到实现的映射。 */ - private final Map registry = new LinkedHashMap<>(); +public class SnowCLI { /** - * 构造 CLI,自动注册所有可用命令。 - *
        - *
      • 通过 ServiceLoader 加载所有外部扩展命令。
      • - *
      • 始终内置注册 compile、run 等核心命令。
      • - *
      + * 保存所有可用子命令的映射:命令名称 -> 命令实例的提供者 + * 通过 Map.of 初始化,添加新命令时只需在此注册。 */ - public SnowCLI() { - // 1. 自动发现 ServiceLoader 扩展命令 - ServiceLoader.load(CLICommand.class).forEach(this::register); - // 2. 注册核心命令,保证 CLI 可用 - register(new CompileCommand()); - register(new RunCommand()); - } + private static final Map> COMMANDS = Map.of( + "compile", CompileCommand::new, + "run", RunCommand::new + ); /** - * 注册一个命令到 CLI(命令名唯一,若已注册则跳过)。 + * 程序主入口,解析和分发命令。 * - * @param cmd 待注册命令 - */ - private void register(CLICommand cmd) { - registry.putIfAbsent(cmd.name(), cmd); - } - - /** - * 解析命令行参数并执行相应命令。 - * - * @param args 命令行参数 - * @return 进程退出码(0=成功, 1=未知命令, -1=命令异常) - */ - public int run(String[] args) { - // 无参数或 help,打印全局用法 - if (args.length == 0 - || Set.of("help", "-h", "--help").contains(args[0])) { - printGlobalUsage(); - return 0; - } - - // 根据命令名查找注册表 - CLICommand cmd = registry.get(args[0]); - if (cmd == null) { - System.err.printf("Unknown command: %s%n%n", args[0]); - printGlobalUsage(); - return 1; - } - - // 提取命令余下参数(不包含命令名) - String[] sub = Arrays.copyOfRange(args, 1, args.length); - try { - return cmd.execute(sub); - } catch (Exception e) { - System.err.printf("Error executing command '%s': %s%n", - cmd.name(), e.getMessage()); - e.printStackTrace(System.err); - return -1; - } - } - - /** - * 打印全局帮助信息(所有已注册命令的 usage)。 - */ - private void printGlobalUsage() { - System.out.println(""" - Usage: snow [options] - - Available commands:"""); - int pad = registry.keySet().stream() - .mapToInt(String::length).max().orElse(10) + 2; - registry.values().stream() - .sorted(Comparator.comparing(CLICommand::name)) - .forEach(c -> System.out.printf(" %-" + pad + "s%s%n", - c.name(), c.description())); - - System.out.println(""" - - Use 'snow --help' for command-specific details. - """); - } - - /** - * CLI 程序主入口。 - * - * @param args 命令行参数 + * @param args 用户在命令行中输入的参数数组 */ public static void main(String[] args) { - int code = new SnowCLI().run(args); - System.exit(code); + // 如果未给出任何参数,则打印通用帮助并退出 + if (args.length == 0) { + printGeneralUsage(); + System.exit(1); + } + + // 第一个参数为子命令名称 + String cmdName = args[0]; + Supplier cmdSupplier = COMMANDS.get(cmdName); + // 如果命令不存在,则打印错误和帮助 + if (cmdSupplier == null) { + System.err.println("Unknown command: " + cmdName); + printGeneralUsage(); + System.exit(1); + } + + // 创建子命令实例 + CLICommand cmd = cmdSupplier.get(); + + // 提取子命令的剩余参数 + String[] sub = Arrays.copyOfRange(args, 1, args.length); + + // 支持统一的帮助标志:help, -h, --help + if (sub.length > 0 && Set.of("help", "-h", "--help").contains(sub[0])) { + // 调用子命令自己的 printUsage + cmd.printUsage(); + System.exit(0); + } + + // 执行子命令并捕获异常 + try { + int exitCode = cmd.execute(sub); + System.exit(exitCode); + } catch (Exception e) { + // 打印错误信息和堆栈 + System.err.println("Error: " + e.getMessage()); + System.exit(1); + } + } + + /** + * 打印动态生成的通用帮助信息。 + * 会遍历 COMMANDS,输出所有子命令的名称及描述信息。 + */ + private static void printGeneralUsage() { + // 使用方式说明 + System.out.println("Usage: snow [options]"); + System.out.println("Commands:"); + + // 对每个注册的子命令,获取 description 并格式化输出 + COMMANDS.forEach((name, supplier) -> { + CLICommand c = supplier.get(); + // %-10s 保证命令名称列宽度为 10 + System.out.printf(" %-10s %s%n", name, c.description()); + }); + // 提示如何查看子命令帮助 + System.out.println("Use 'snow --help' to see command-specific options."); } } From 8ca75787b74ec7c4cb9b77537afb7497515cf685 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 18 Jun 2025 00:05:42 +0800 Subject: [PATCH 06/19] =?UTF-8?q?feat:=20=E6=89=93=E5=8D=B0=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E7=94=9F=E6=88=90=E7=9A=84=E9=80=9A=E7=94=A8=E5=B8=AE?= =?UTF-8?q?=E5=8A=A9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jcnc/snow/compiler/cli/SnowCLI.java | 101 +++++++++++++----- .../compiler/cli/commands/VersionCommand.java | 37 +++++++ 2 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/cli/commands/VersionCommand.java 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 5270dc3..b61d2ad 100644 --- a/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java +++ b/src/main/java/org/jcnc/snow/compiler/cli/SnowCLI.java @@ -2,67 +2,98 @@ package org.jcnc.snow.compiler.cli; import org.jcnc.snow.compiler.cli.commands.CompileCommand; import org.jcnc.snow.compiler.cli.commands.RunCommand; +import org.jcnc.snow.compiler.cli.commands.VersionCommand; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Supplier; /** - * SnowCLI 是项目的命令行入口类,负责解析用户输入、分发子命令并统一处理帮助和错误。 + * SnowCLI 是项目的命令行入口类,负责解析用户输入、 + * 分发子命令,并统一处理帮助、版本和错误输出。 */ public class SnowCLI { + /** Snow 编程语言的版本号。 */ + public static final String VERSION = "1.0.0"; + + /** 全局帮助标志,当输入匹配时显示帮助信息。 */ + private static final Set GLOBAL_HELP_FLAGS = Set.of("help", "-h", "--help"); + + /** 全局版本标志,当输入匹配时显示版本信息。 */ + private static final Set GLOBAL_VERSION_FLAGS = Set.of("-v", "--version"); /** - * 保存所有可用子命令的映射:命令名称 -> 命令实例的提供者 + * 全局选项定义列表。 + * 每个 Option 包含可用标志列表及对应的描述信息。 + */ + private static final List