diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
index 25132b9..711fceb 100644
--- a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
+++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
@@ -20,7 +20,7 @@ https://gitee.com/jcnc-org/snow/blob/main/doc/Git-Management/Git-Management.md
感谢你的配合!🙏
-->
-# 描述 (Description)
+## 描述 (Description)
请简要描述本次变更的目的和内容。
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index aa5369c..3f8a7b3 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -1,5 +1,7 @@
+import org.jcnc.snow.cli.api.CLICommand;
+
module org.jcnc.snow.compiler {
- uses org.jcnc.snow.cli.CLICommand;
+ uses CLICommand;
requires java.desktop;
requires java.logging;
exports org.jcnc.snow.compiler.ir.core;
diff --git a/src/main/java/org/jcnc/snow/cli/SnowCLI.java b/src/main/java/org/jcnc/snow/cli/SnowCLI.java
index 5fae2a7..03a4391 100644
--- a/src/main/java/org/jcnc/snow/cli/SnowCLI.java
+++ b/src/main/java/org/jcnc/snow/cli/SnowCLI.java
@@ -1,8 +1,7 @@
package org.jcnc.snow.cli;
-import org.jcnc.snow.cli.commands.CompileCommand;
-import org.jcnc.snow.cli.commands.RunCommand;
-import org.jcnc.snow.cli.commands.VersionCommand;
+import org.jcnc.snow.cli.api.CLICommand;
+import org.jcnc.snow.cli.commands.*;
import org.jcnc.snow.cli.utils.CLIUtils;
import org.jcnc.snow.cli.utils.VersionUtils;
@@ -30,14 +29,16 @@ public class SnowCLI {
* 值为返回相应 {@link CLICommand} 实例的 Supplier。
*/
private static final Map> COMMANDS = Map.of(
+ "generate", GenerateCommand::new,
"compile", CompileCommand::new,
"run", RunCommand::new,
"version", VersionCommand::new,
- "init", org.jcnc.snow.cli.commands.InitCommand::new,
- "build", org.jcnc.snow.cli.commands.BuildCommand::new,
- "install", org.jcnc.snow.cli.commands.InstallCommand::new,
- "publish", org.jcnc.snow.cli.commands.PublishCommand::new,
- "clean", org.jcnc.snow.cli.commands.CleanCommand::new
+ "init", InitCommand::new,
+ "build", BuildCommand::new,
+ "install", InstallCommand::new,
+ "publish", PublishCommand::new,
+ "clean", CleanCommand::new
+
);
/**
diff --git a/src/main/java/org/jcnc/snow/cli/CLICommand.java b/src/main/java/org/jcnc/snow/cli/api/CLICommand.java
similarity index 94%
rename from src/main/java/org/jcnc/snow/cli/CLICommand.java
rename to src/main/java/org/jcnc/snow/cli/api/CLICommand.java
index 0874cf1..71e227e 100644
--- a/src/main/java/org/jcnc/snow/cli/CLICommand.java
+++ b/src/main/java/org/jcnc/snow/cli/api/CLICommand.java
@@ -1,4 +1,6 @@
-package org.jcnc.snow.cli;
+package org.jcnc.snow.cli.api;
+
+import org.jcnc.snow.cli.SnowCLI;
/**
* 所有 CLI 子命令(如 compile、run 等)都必须实现的命令接口。
diff --git a/src/main/java/org/jcnc/snow/cli/commands/BuildCommand.java b/src/main/java/org/jcnc/snow/cli/commands/BuildCommand.java
index f8b9c45..17e1c0f 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/BuildCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/BuildCommand.java
@@ -1,6 +1,6 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
+import org.jcnc.snow.cli.api.CLICommand;
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
diff --git a/src/main/java/org/jcnc/snow/cli/commands/CleanCommand.java b/src/main/java/org/jcnc/snow/cli/commands/CleanCommand.java
index 8f42b65..0c65244 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/CleanCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/CleanCommand.java
@@ -1,6 +1,6 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
+import org.jcnc.snow.cli.api.CLICommand;
import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
import org.jcnc.snow.pkg.tasks.CleanTask;
diff --git a/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java b/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
index 8d77e8d..993e1cb 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
@@ -1,48 +1,35 @@
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.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 org.jcnc.snow.cli.api.CLICommand;
+import org.jcnc.snow.pkg.dsl.CloudDSLParser;
+import org.jcnc.snow.pkg.model.Project;
+import org.jcnc.snow.pkg.tasks.CompileTask;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.*;
-
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
- * CLI 命令:将 .snow 源文件编译为 VM 字节码(.water 文件)。
- *
- * 支持递归目录、多文件编译,可选编译后立即运行。
- * 命令参数支持 run、-o、-d 等。
- *
+ * CLI 命令:编译当前项目。
*
- *
- * 用法示例:
- * $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow …]
- *
+ * 工作模式说明:
+ *
+ * - Cloud 模式
+ * - 项目根目录存在 {@code project.cloud} 时触发;
+ * - 解析 build 区块,自动推导源码目录与输出文件名;
+ * - 用法:{@code snow compile [run]}
+ * - Local 模式
+ * - 未检测到 {@code project.cloud} 时回退;
+ * - 保持向后兼容:{@code snow compile [run] [-o ] [-d ] [file.snow …]}
+ *
+ *
+ * 两种模式均将最终参数交由 {@link CompileTask} 处理。
*/
public final class CompileCommand implements CLICommand {
- /* --------------------------------------------------------------------- */
- /* CLICommand 接口实现 */
- /* --------------------------------------------------------------------- */
-
@Override
public String name() {
return "compile";
@@ -56,202 +43,50 @@ public final class CompileCommand implements CLICommand {
@Override
public void printUsage() {
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");
+ System.out.println(" snow compile [run] (cloud mode, use project.cloud)");
+ System.out.println(" snow compile [run] [-o ] [-d ] [file1.snow …] (GOPATH mode)");
}
- /* --------------------------------------------------------------------- */
- /* 核心:执行 compile 子命令 */
- /* --------------------------------------------------------------------- */
-
@Override
public int execute(String[] args) throws Exception {
- /* ---------------- 解析命令行参数 ---------------- */
- boolean runAfterCompile = false;
- 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;
- 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;
- }
+ Path dslFile = Paths.get("project.cloud");
+ Project project;
+ String[] compileArgs;
+
+ /* ---------- 1. Cloud 模式 ---------- */
+ if (Files.exists(dslFile)) {
+ project = CloudDSLParser.parse(dslFile);
+
+ List argList = new ArrayList<>();
+
+ // 保留用户在 cloud 模式下传入的 “run” 标志
+ for (String a : args) {
+ if ("run".equals(a)) {
+ argList.add("run");
}
}
+
+ /* 源码目录:build.srcDir -> 默认 src */
+ String srcDir = project.getBuild().get("srcDir", "src");
+ argList.add("-d");
+ argList.add(srcDir);
+
+ /* 输出名称:build.output -> fallback to artifact */
+ String output = project.getBuild().get("output", project.getArtifact());
+ argList.add("-o");
+ argList.add(output);
+
+ compileArgs = argList.toArray(new String[0]);
+ }
+ /* ---------- 2. Local 模式 ---------- */
+ else {
+ project = Project.fromFlatMap(Collections.emptyMap()); // 占位项目,保持接口统一
+ compileArgs = args; // 透传原始 CLI 参数
}
- /* --------- 如果指定了目录则递归收集所有 *.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 ");
- return 1;
- }
-
- /* ----------------------------------------------------------------- */
- /* 1. 词法 + 语法分析;同时打印源代码 */
- /* ----------------------------------------------------------------- */
- List 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> 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. 写出 .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()});
- }
-
+ // 委托给 CompileTask 完成实际编译/运行
+ new CompileTask(project, compileArgs).run();
return 0;
}
-
- /* --------------------------------------------------------------------- */
- /* 辅助方法 */
- /* --------------------------------------------------------------------- */
-
- /**
- * 根据输入情况推断 .water 输出文件名:
- *
- * - 若指定 -o,则直接使用
- * - 目录编译:取目录名
- * - 单文件编译:取文件名去掉 .snow
- * - 其他情况兜底为 "program"
- *
- */
- private static Path deriveOutputPath(List sources, String outName, Path dir) {
- String base;
- if (outName != null) {
- base = outName;
- } else if (dir != null) {
- base = dir.getFileName().toString();
- } else if (sources.size() == 1) {
- base = sources.getFirst().getFileName().toString()
- .replaceFirst("\\.snow$", "");
- } else {
- base = "program";
- }
- return Path.of(base + ".water");
- }
-
- /**
- * 把 main 函数交换到程序函数列表首位,确保 PC=0 即入口。
- */
- private static IRProgram reorderForEntry(IRProgram in) {
- List ordered = new ArrayList<>(in.functions());
- for (int i = 0; i < ordered.size(); i++) {
- if ("main".equals(ordered.get(i).name())) {
- Collections.swap(ordered, 0, i);
- break;
- }
- }
- IRProgram out = new IRProgram();
- ordered.forEach(out::add);
- return out;
- }
}
diff --git a/src/main/java/org/jcnc/snow/cli/commands/GenerateCommand.java b/src/main/java/org/jcnc/snow/cli/commands/GenerateCommand.java
new file mode 100644
index 0000000..b83a28d
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/cli/commands/GenerateCommand.java
@@ -0,0 +1,87 @@
+package org.jcnc.snow.cli.commands;
+
+import org.jcnc.snow.cli.api.CLICommand;
+import org.jcnc.snow.pkg.dsl.CloudDSLParser;
+import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
+import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
+import org.jcnc.snow.pkg.model.Project;
+import org.jcnc.snow.pkg.tasks.GenerateTask;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * CLI 命令:根据 project.cloud 生成项目目录结构。
+ *
+ * 负责解析云项目描述文件,并通过 {@link GenerateTask}
+ * 在 INIT 生命周期阶段内生成基础目录结构。
+ *
+ *
+ *
+ * 用法示例:
+ * $ snow generate
+ *
+ *
+ *
+ * 注意事项:
+ * - 若当前目录不存在 project.cloud,则提示用户先执行 `snow init`。
+ * - 执行成功后,会输出已创建的目录/文件。
+ *
+ */
+public final class GenerateCommand implements CLICommand {
+
+ /**
+ * 返回命令名称,用于 CLI 调用。
+ *
+ * @return 命令名称,如 "generate"
+ */
+ @Override
+ public String name() {
+ return "generate";
+ }
+
+ /**
+ * 返回命令简介,用于 CLI 帮助或命令列表展示。
+ *
+ * @return 命令描述字符串
+ */
+ @Override
+ public String description() {
+ return "Generate project directory structure based on project.cloud.";
+ }
+
+ /**
+ * 打印命令用法信息。
+ */
+ @Override
+ public void printUsage() {
+ System.out.println("Usage: snow generate");
+ }
+
+ /**
+ * 执行生成任务。
+ *
+ * @param args CLI 传入的参数数组(此命令不接受参数)
+ * @return 执行结果码(0 表示成功,1 表示 project.cloud 缺失)
+ * @throws Exception 执行过程中出现错误时抛出
+ */
+ @Override
+ public int execute(String[] args) throws Exception {
+ Path dsl = Paths.get("project.cloud");
+ if (Files.notExists(dsl)) {
+ System.err.println("project.cloud not found. Please run `snow init` first.");
+ return 1;
+ }
+
+ /* 1. 解析 DSL */
+ Project project = CloudDSLParser.parse(dsl);
+
+ /* 2. 执行生成任务 —— 复用 Lifecycle INIT 阶段 */
+ LifecycleManager lm = new LifecycleManager();
+ lm.register(LifecyclePhase.INIT, new GenerateTask(project));
+ lm.executeAll();
+
+ return 0;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/cli/commands/InitCommand.java b/src/main/java/org/jcnc/snow/cli/commands/InitCommand.java
index 62a80b3..d9cbf92 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/InitCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/InitCommand.java
@@ -1,15 +1,16 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
+import org.jcnc.snow.cli.api.CLICommand;
+import org.jcnc.snow.cli.utils.ProjectCloudExample;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
- * CLI 命令:创建新的项目骨架。
+ * CLI 命令:初始化项目配置文件。
*
- * 用于快速初始化标准目录结构和 DSL 配置文件(project.cloud)。
+ * 用于快速生成 DSL 配置文件(project.cloud)。
*
*
*
@@ -36,7 +37,7 @@ public final class InitCommand implements CLICommand {
*/
@Override
public String description() {
- return "Initialize a new project skeleton with directory structure and project.cloud file.";
+ return "Initialize a new project with project.cloud file.";
}
/**
@@ -44,11 +45,11 @@ public final class InitCommand implements CLICommand {
*/
@Override
public void printUsage() {
- System.out.println("Usage: snow init [--lang ]");
+ System.out.println("Usage: snow init");
}
/**
- * 执行项目初始化流程,创建 src 目录和 DSL 配置文件。
+ * 执行项目初始化流程,仅创建 DSL 配置文件。
*
* @param args CLI 传入的参数数组
* @return 执行结果码(0 表示成功)
@@ -56,19 +57,11 @@ public final class InitCommand implements CLICommand {
*/
@Override
public int execute(String[] args) throws Exception {
- // 生成 skeleton `.cloud` 文件和 src 目录
+ // 仅生成 `.cloud` 文件
Path dir = Paths.get(".").toAbsolutePath();
- Files.createDirectories(dir.resolve("src"));
Path dsl = dir.resolve("project.cloud");
if (Files.notExists(dsl)) {
- Files.writeString(dsl, """
- # Generated by snow init
- project {
- group = "com.example"
- artifact = "demo-app"
- version = "0.0.1-SNAPSHOT"
- }
- """);
+ Files.writeString(dsl, ProjectCloudExample.getProjectCloud());
System.out.println("[init] created " + dsl);
} else {
System.out.println("[init] project.cloud already exists");
diff --git a/src/main/java/org/jcnc/snow/cli/commands/InstallCommand.java b/src/main/java/org/jcnc/snow/cli/commands/InstallCommand.java
index 7cd0a2b..6199602 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/InstallCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/InstallCommand.java
@@ -1,6 +1,6 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
+import org.jcnc.snow.cli.api.CLICommand;
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
import org.jcnc.snow.pkg.model.Project;
import org.jcnc.snow.pkg.resolver.DependencyResolver;
diff --git a/src/main/java/org/jcnc/snow/cli/commands/PublishCommand.java b/src/main/java/org/jcnc/snow/cli/commands/PublishCommand.java
index 2073f91..658e799 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/PublishCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/PublishCommand.java
@@ -1,6 +1,6 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
+import org.jcnc.snow.cli.api.CLICommand;
import org.jcnc.snow.pkg.dsl.CloudDSLParser;
import org.jcnc.snow.pkg.lifecycle.LifecycleManager;
import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
diff --git a/src/main/java/org/jcnc/snow/cli/commands/RunCommand.java b/src/main/java/org/jcnc/snow/cli/commands/RunCommand.java
index 09a10d4..0aeffe6 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/RunCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/RunCommand.java
@@ -1,25 +1,26 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
-import org.jcnc.snow.vm.VMLauncher;
+import org.jcnc.snow.cli.api.CLICommand;
+import org.jcnc.snow.pkg.tasks.RunTask;
/**
* CLI 命令:运行已编译的 VM 字节码文件(.water)。
*
- * 用于执行 VM 程序文件。支持传递额外 VM 参数,实际运行由 {@link VMLauncher#main(String[])} 完成。
+ * 仅解析参数并委托给 {@link RunTask},
+ * 将 VM 运行逻辑下沉至 pkg 层,保持 CLI 无状态、薄封装。
*
*
*
* 用法示例:
- * $ snow run program.water [additional VM options]
+ * $ snow run main.water
*
*/
public final class RunCommand implements CLICommand {
/**
- * 返回命令名,用于 CLI 调用。
+ * 返回命令名称,用于 CLI 调用。
*
- * @return 命令名称字符串("run")
+ * @return 命令名称,如 "run"
*/
@Override
public String name() {
@@ -33,25 +34,15 @@ public final class RunCommand implements CLICommand {
*/
@Override
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 [additional VM options]
- """);
- }
-
- /**
- * 执行 run 命令,运行指定的 VM 字节码文件。
+ * 执行运行任务。
*
- * @param args 剩余参数(不含命令名),第一个应为 .water 文件路径,其后为可选 VM 参数
- * @return 0 表示执行成功,1 表示参数错误
- * @throws Exception VM 启动或执行过程中可能抛出的异常
+ * @param args CLI 传入的参数数组
+ * @return 执行结果码(0 表示成功,非 0 表示失败)
+ * @throws Exception 执行过程中出现错误时抛出
*/
@Override
public int execute(String[] args) throws Exception {
@@ -59,7 +50,17 @@ public final class RunCommand implements CLICommand {
printUsage();
return 1;
}
- VMLauncher.main(args);
+ // 委托给 RunTask 执行字节码运行逻辑
+ new RunTask(args).run();
return 0;
}
+
+ /**
+ * 打印命令用法信息。
+ */
+ @Override
+ public void printUsage() {
+ System.out.println("Usage:");
+ System.out.println(" snow run ");
+ }
}
diff --git a/src/main/java/org/jcnc/snow/cli/commands/VersionCommand.java b/src/main/java/org/jcnc/snow/cli/commands/VersionCommand.java
index c8f3af8..d2989b5 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/VersionCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/VersionCommand.java
@@ -1,7 +1,7 @@
package org.jcnc.snow.cli.commands;
-import org.jcnc.snow.cli.CLICommand;
import org.jcnc.snow.cli.SnowCLI;
+import org.jcnc.snow.cli.api.CLICommand;
/**
* CLI 子命令:输出当前 Snow 工具的版本号。
diff --git a/src/main/java/org/jcnc/snow/cli/utils/CLIUtils.java b/src/main/java/org/jcnc/snow/cli/utils/CLIUtils.java
index 462ead8..ca36951 100644
--- a/src/main/java/org/jcnc/snow/cli/utils/CLIUtils.java
+++ b/src/main/java/org/jcnc/snow/cli/utils/CLIUtils.java
@@ -1,6 +1,6 @@
package org.jcnc.snow.cli.utils;
-import org.jcnc.snow.cli.CLICommand;
+import org.jcnc.snow.cli.api.CLICommand;
import java.util.List;
import java.util.Map;
diff --git a/src/main/java/org/jcnc/snow/cli/utils/ProjectCloudExample.java b/src/main/java/org/jcnc/snow/cli/utils/ProjectCloudExample.java
new file mode 100644
index 0000000..b51e126
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/cli/utils/ProjectCloudExample.java
@@ -0,0 +1,31 @@
+package org.jcnc.snow.cli.utils;
+
+public class ProjectCloudExample {
+ /**
+ * 工具类构造方法,禁止实例化。
+ */
+ private ProjectCloudExample() {
+ // 工具类不允许实例化
+ }
+
+ /**
+ * 获取 main.snow 示例模块的内容字符串。
+ *
+ * @return main.snow 示例模块的完整代码
+ */
+ public static String getProjectCloud() {
+ return """
+ # Generated by snow init
+ project {
+ group = "com.example"
+ artifact = "demo-app"
+ version = "0.0.1-SNAPSHOT"
+ }
+
+ build {
+ srcDir = "src"
+ output = "build/demo-app"
+ }
+ """;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/pkg/doc/README.md b/src/main/java/org/jcnc/snow/pkg/doc/README.md
new file mode 100644
index 0000000..ec50ed5
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/pkg/doc/README.md
@@ -0,0 +1,64 @@
+# Snow Build - 包管理模块
+
+> Snow 构建工具中的 **pkg** 子系统 —— 负责解析 `.cloud` 配置、解析依赖、 orchestrate 构建生命周期并执行各阶段任务
+
+## 项目简介
+
+**包管理模块(pkg)** 是 Snow 构建工具的关键组成部分,承担“从配置到产物” 的整条流水线:
+
+1. **DSL 解析**:读取并解析 `.cloud` 配置文件,生成统一的项目/依赖/构建配置模型;
+2. **生命周期编排**:按 *INIT → RESOLVE\_DEPENDENCIES → COMPILE → PACKAGE → PUBLISH → CLEAN* 的顺序驱动构建流程;
+3. **依赖解析与缓存**:按需下载缺失依赖并存入本地缓存,离线优先;
+4. **任务执行**:在各生命周期阶段调用对应 `Task` 实现(清理、编译、打包、发布等);
+5. **配置模板展开**:支持在配置中使用 `@{key}` 形式的占位符,并在构建前统一替换。
+
+整个模块强调 **可扩展性** 与 **内聚职责**:DSL → Model、Lifecycle → Task、Resolver → Repository 各自解耦,可独立演进。
+
+## 核心功能
+
+| 功能 | 关键类 | 说明 |
+|-----------------|----------------------------------------------------------|-------------------------------------------------|
+| **CloudDSL 解析** | `CloudDSLParser` | 支持区块语法、嵌套 build 展平、注释过滤,解析为扁平 `Map` |
+| **模型对象** | `Project / Dependency / Repository / BuildConfiguration` | 只读数据类,提供静态工厂方法 *fromFlatMap()* |
+| **生命周期管理** | `LifecycleManager`, `LifecyclePhase` | 注册并顺序执行阶段任务,阶段可扩展 |
+| **任务体系** | `Task` + `*Task` 实现 | Clean / Compile / Package / Publish 四大内置任务,易于新增 |
+| **依赖解析器** | `DependencyResolver` | 支持本地缓存、HTTP 下载、URI 解析、断点续传(基于 NIO) |
+| **模板变量替换** | `BuildConfiguration` | 在加载阶段把 `@{key}` 替换为外部属性值 |
+
+## 模块结构
+
+```
+pkg/
+ ├── dsl/
+ │ └── CloudDSLParser.java // .cloud DSL 解析
+ ├── lifecycle/
+ │ ├── LifecycleManager.java // 阶段编排
+ │ └── LifecyclePhase.java // 阶段枚举
+ ├── model/
+ │ ├── Project.java // 项目信息
+ │ ├── BuildConfiguration.java // 构建配置(支持变量)
+ │ ├── Dependency.java // 依赖描述
+ │ └── Repository.java // 仓库描述
+ ├── resolver/
+ │ └── DependencyResolver.java // 依赖解析与缓存
+ └── tasks/
+ ├── Task.java // 任务接口
+ ├── CleanTask.java // 清理
+ ├── CompileTask.java // 编译
+ ├── PackageTask.java // 打包
+ └── PublishTask.java // 发布
+```
+
+## 典型流程概览
+
+1. 解析配置
+
+2. 注册任务并执行
+
+3. 解析依赖(在 RESOLVE\_DEPENDENCIES 阶段内部)
+
+## 开发环境
+
+* JDK 24 或更高版本
+* Maven 构建管理
+* 推荐 IDE:IntelliJ IDEA
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/pkg/dsl/CloudDSLParser.java b/src/main/java/org/jcnc/snow/pkg/dsl/CloudDSLParser.java
index c707a65..301e11d 100644
--- a/src/main/java/org/jcnc/snow/pkg/dsl/CloudDSLParser.java
+++ b/src/main/java/org/jcnc/snow/pkg/dsl/CloudDSLParser.java
@@ -10,61 +10,61 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * CloudDSLParser — .cloud 配置文件解析器
- *
- * 作用:
- * - 读取 Snow 构建工具自定义的 .cloud 文件
- * - 将内容转换为内存中的 {@link Project} 模型
- *
- * 解析规则(:
- *
- * 1. 顶级区块(project、properties、repositories、dependencies、build)
- * 以 “sectionName {” 开始,以 “}” 结束。
- *
- * 2. 区块内部识别 “key = value” 形式的赋值。
- *
- * 3. build 区块允许嵌套;内部键通过 “.” 展平,
- * 例如 compile.enabled = true。
+ * CloudDSL 配置文件解析器。
*
+ * 负责将自定义的 .cloud 构建配置文件解析为 {@link Project} 模型。
+ *
+ *
+ *
+ * - 顶级区块(如 project、properties、repositories、dependencies、build)以
sectionName { 开始,以 } 结束
+ * - 区块内部只识别
key = value 赋值,行尾可有 # 注释
+ * - build 区块支持嵌套,内部键通过
. 展平,例如 compile.enabled = true
+ * - 新增:对
"value" 或 'value' 形式的字面量自动去引号,调用方得到的均是不含引号的裸字符串
+ *
+ *
+ *
+ * 示例 .cloud 文件片段:
+ * project {
+ * group = com.example
+ * artifact = "demo-app"
+ * version = 1.0.0
+ * }
+ *
*/
public final class CloudDSLParser {
- /* ---------- 正则表达式 ---------- */
-
- /**
- * 匹配 “sectionName {” 形式的行
- */
+ /** 匹配 sectionName { 的行 */
private static final Pattern SECTION_HEADER = Pattern.compile("^(\\w+)\\s*\\{\\s*$");
/**
- * 匹配 “key = value” 行。
- * value 允许空格,并忽略行尾 “# …” 注释。
+ * 匹配 key = value 行,忽略行尾注释。
+ * 使用非贪婪匹配 .*?,确保 value 内部允许出现空格或 =。
*/
- private static final Pattern KEY_VALUE = Pattern.compile("^(\\w+)\\s*=\\s*([^#]+?)\\s*(?:#.*)?$");
+ private static final Pattern KEY_VALUE = Pattern.compile("^(\\w+)\\s*=\\s*(.*?)\\s*(?:#.*)?$");
- /**
- * 匹配仅包含 “}” 的行(可有前后空白)
- */
+ /** 匹配仅为 } 的行 */
private static final Pattern BLOCK_END = Pattern.compile("^}\\s*$");
- /**
- * 私有构造,禁止实例化
- */
- private CloudDSLParser() {
- }
+ /** 工具类禁止实例化 */
+ private CloudDSLParser() {}
/**
- * 解析指定 .cloud 文件并生成 {@link Project} 对象。
+ * 解析指定 .cloud 文件为 {@link Project} 对象。
+ *
+ * - 遇到语法错误(括号不配对、无法识别的行)时抛出异常
+ * - 支持嵌套区块和注释
+ * - 对字面量自动去除成对单/双引号
+ *
*
- * @param path 文件路径
- * @return 解析后的 Project
+ * @param path .cloud 文件路径
+ * @return 解析得到的 Project 实例
* @throws IOException 文件读取失败
- * @throws IllegalStateException 语法错误(如括号不匹配、未知语句)
+ * @throws IllegalStateException 文件内容格式非法或语法错误
*/
public static Project parse(Path path) throws IOException {
- Deque sectionStack = new ArrayDeque<>(); // 记录当前区块层级
- Map flatMap = new LinkedHashMap<>(); // 扁平化 key → value
+ Deque sectionStack = new ArrayDeque<>(); // 当前区块栈
+ Map flatMap = new LinkedHashMap<>(); // 扁平化后的 key → value
List lines = Files.readAllLines(path);
@@ -73,19 +73,19 @@ public final class CloudDSLParser {
lineNo++;
String line = raw.trim();
- /* 1. 跳过空行和注释 */
+ // 跳过空行和注释
if (line.isEmpty() || line.startsWith("#")) {
continue;
}
- /* 2. 区块起始:sectionName { */
+ // 区块起始
Matcher sec = SECTION_HEADER.matcher(line);
if (sec.matches()) {
sectionStack.push(sec.group(1));
continue;
}
- /* 3. 区块结束:} */
+ // 区块结束
if (BLOCK_END.matcher(line).matches()) {
if (sectionStack.isEmpty()) {
throw new IllegalStateException("第 " + lineNo + " 行出现未配对的 '}'");
@@ -94,13 +94,14 @@ public final class CloudDSLParser {
continue;
}
- /* 4. 键值对:key = value */
+ // 键值对
Matcher kv = KEY_VALUE.matcher(line);
if (kv.matches()) {
String key = kv.group(1).trim();
String value = kv.group(2).trim();
+ value = unquote(value); // 去除首尾成对引号
- /* 4.1 计算前缀(栈倒序,即从外到内) */
+ // 计算区块前缀
String prefix = String.join(".", (Iterable) sectionStack::descendingIterator);
if (!prefix.isEmpty()) {
key = prefix + "." + key;
@@ -110,16 +111,29 @@ public final class CloudDSLParser {
continue;
}
- /* 5. 无法识别的行 */
+ // 无法识别的行
throw new IllegalStateException("无法解析第 " + lineNo + " 行: " + raw);
}
- /* 6. 检查括号是否全部闭合 */
+ // 检查区块是否全部闭合
if (!sectionStack.isEmpty()) {
throw new IllegalStateException("文件结束但区块未闭合:" + sectionStack);
}
- /* 7. 构造 Project 模型 */
+ // 构建 Project 模型
return Project.fromFlatMap(flatMap);
}
+
+ /**
+ * 如果字符串首尾包裹成对单引号或双引号,则去掉引号后返回;否则直接返回原字符串。
+ */
+ private static String unquote(String s) {
+ if (s == null || s.length() < 2) return s;
+ char first = s.charAt(0);
+ char last = s.charAt(s.length() - 1);
+ if ((first == '"' && last == '"') || (first == '\'' && last == '\'')) {
+ return s.substring(1, s.length() - 1);
+ }
+ return s;
+ }
}
diff --git a/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecycleManager.java b/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecycleManager.java
index 60f5ebc..6c1b5db 100644
--- a/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecycleManager.java
+++ b/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecycleManager.java
@@ -6,9 +6,10 @@ import java.util.EnumMap;
import java.util.Map;
/**
- * 管理不同生命周期阶段与其对应任务的工具类。
+ * 生命周期任务管理器。
+ * 用于管理不同生命周期阶段与其对应 {@link Task},并支持顺序执行所有已注册任务。
*
- * 可为每个 {@link LifecyclePhase} 注册对应的 {@link Task},并按阶段顺序执行所有任务。
+ * 可为每个 {@link LifecyclePhase} 注册对应的 {@link Task},并在构建/部署流程中自动执行。
*
*
*
@@ -20,9 +21,7 @@ import java.util.Map;
*/
public final class LifecycleManager {
- /**
- * 存储生命周期阶段与对应任务的映射关系。
- */
+ /** 生命周期阶段与对应任务的映射关系 */
private final Map tasks = new EnumMap<>(LifecyclePhase.class);
/**
@@ -45,11 +44,13 @@ public final class LifecycleManager {
/**
* 按 {@link LifecyclePhase} 声明顺序依次执行所有已注册任务。
- *
- * 未注册任务的阶段将被跳过。任务执行前会打印阶段名。
- *
+ *
+ * - 未注册任务的阶段会被自动跳过
+ * - 每个任务执行前会输出当前阶段名
+ * - 执行中遇到异常将立即抛出并终止后续执行
+ *
*
- * @throws Exception 若某个任务执行时抛出异常,将直接抛出并终止后续任务执行
+ * @throws Exception 若某个任务执行时抛出异常,将直接抛出
*/
public void executeAll() throws Exception {
for (LifecyclePhase phase : LifecyclePhase.values()) {
diff --git a/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecyclePhase.java b/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecyclePhase.java
index aaa464c..39d9759 100644
--- a/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecyclePhase.java
+++ b/src/main/java/org/jcnc/snow/pkg/lifecycle/LifecyclePhase.java
@@ -1,7 +1,10 @@
package org.jcnc.snow.pkg.lifecycle;
/**
- * 定义了典型软件包生命周期的各个阶段。
+ * 定义典型软件包生命周期的各个阶段枚举。
+ *
+ * 用于区分构建、依赖、发布等不同阶段的任务调度与管理。
+ *
*/
public enum LifecyclePhase {
/** 初始化阶段 */
diff --git a/src/main/java/org/jcnc/snow/pkg/model/BuildConfiguration.java b/src/main/java/org/jcnc/snow/pkg/model/BuildConfiguration.java
index c0f0075..de4b64c 100644
--- a/src/main/java/org/jcnc/snow/pkg/model/BuildConfiguration.java
+++ b/src/main/java/org/jcnc/snow/pkg/model/BuildConfiguration.java
@@ -6,16 +6,16 @@ import java.util.HashMap;
/**
* 构建配置对象,封装构建过程中的所有选项。
*
- * 支持基于模板变量(形如{@code @{key}})的选项值替换。
+ * 支持模板变量(形如 @{key})的值自动替换。
*
*/
public final class BuildConfiguration {
- /** 存储配置项的键值对 */
+ /** 存储所有配置项 */
private final Map options;
/**
- * 私有构造函数,用于初始化配置项。
+ * 私有构造函数,仅供工厂方法调用。
*
* @param options 配置项键值对
*/
@@ -24,14 +24,15 @@ public final class BuildConfiguration {
}
/**
- * 基于原始配置项和变量属性创建配置对象。
- *
- * 会将原始配置中的所有值中的{@code @{key}}模板,替换为属性props中对应的值。
- *
+ * 基于原始配置项和属性集创建配置对象。
+ *
+ * - 会将所有值中的
@{key} 模板变量,替换为 props 中对应的值
+ * - 属性未匹配到时保留原模板
+ *
*
- * @param flat 原始的配置项,值中可包含模板变量(如@{name})
- * @param props 用于替换模板变量的属性集
- * @return 构建完成的配置对象
+ * @param flat 原始配置项,值中可包含模板变量
+ * @param props 变量替换用的属性集
+ * @return 处理后生成的配置对象
*/
public static BuildConfiguration fromFlatMap(Map flat, Map props) {
Map resolved = new HashMap<>();
@@ -46,11 +47,11 @@ public final class BuildConfiguration {
}
/**
- * 获取指定key对应的配置值。
+ * 获取指定配置项的值。
*
* @param key 配置项名称
- * @param def 默认值(若未找到key则返回此值)
- * @return 配置项对应值,若不存在则返回默认值
+ * @param def 默认值(未找到时返回)
+ * @return 配置项值,若不存在则返回默认值
*/
public String get(String key, String def) {
return options.getOrDefault(key, def);
diff --git a/src/main/java/org/jcnc/snow/pkg/model/Dependency.java b/src/main/java/org/jcnc/snow/pkg/model/Dependency.java
index 50f4bea..481682e 100644
--- a/src/main/java/org/jcnc/snow/pkg/model/Dependency.java
+++ b/src/main/java/org/jcnc/snow/pkg/model/Dependency.java
@@ -5,18 +5,23 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * 表示一个 Maven 风格的 group:artifact:version 依赖坐标。
+ * group:artifact:version 依赖坐标对象。
*
- * 支持通过占位符和属性映射进行动态变量替换,可用于构建工具或依赖管理场景。
+ * 支持占位符和属性映射进行动态变量替换,适用于 Snow 语言包管理和源码依赖场景。
*
*
*
- * 示例:
+ * 示例用法:
* Dependency dep = Dependency.fromString(
* "core", "com.example:core:@{version}",
* Map.of("version", "1.2.3")
* );
*
+ *
+ * @param id 依赖唯一标识
+ * @param group 组织/分组名
+ * @param artifact 构件名
+ * @param version 版本号
*/
public record Dependency(
String id,
@@ -25,25 +30,23 @@ public record Dependency(
String version
) {
- /**
- * 用于匹配 group:artifact:version 格式的正则表达式。
- */
+ /** 匹配 group:artifact:version 格式的正则表达式。 */
private static final Pattern GAV = Pattern.compile("([^:]+):([^:]+):(.+)");
/**
* 根据字符串坐标和属性映射创建依赖对象。
*
- * 坐标中的占位符如 {@code @{key}} 会用 props 中对应的值替换。
+ * 坐标中的占位符(如 @{key})会用 props 中对应的值替换。
*
*
* @param id 依赖唯一标识
- * @param coordinate 依赖坐标字符串,格式为 group:artifact:version,支持变量占位符
- * @param props 占位符替换的属性映射
+ * @param coordinate 坐标字符串,格式为 group:artifact:version,支持占位符
+ * @param props 占位符替换属性映射
* @return 解析后的 Dependency 实例
- * @throws IllegalArgumentException 如果坐标格式非法
+ * @throws IllegalArgumentException 坐标格式非法时抛出
*/
public static Dependency fromString(String id, String coordinate, Map props) {
- // 替换 @{prop} 占位符
+ // 替换占位符
String resolved = coordinate;
for (Map.Entry p : props.entrySet()) {
resolved = resolved.replace("@{" + p.getKey() + "}", p.getValue());
@@ -57,23 +60,23 @@ public record Dependency(
}
/**
- * 生成依赖对应的标准仓库 jar 路径。
+ * 生成依赖对应的源码文件路径。
*
- * 路径格式通常为:groupId/artifactId/version/artifactId-version.jar
- * 例如:com/example/core/1.2.3/core-1.2.3.jar
+ * 路径格式:groupId/artifactId/version/artifactId.snow
+ * 例如:com/example/core/1.2.3/core.snow
*
*
- * @return 仓库 jar 文件的相对路径
+ * @return 仓库源码文件的相对路径
*/
public String toPath() {
String groupPath = group.replace('.', '/');
- return groupPath + "/" + artifact + "/" + version + "/" + artifact + "-" + version + ".jar";
+ return groupPath + "/" + artifact + "/" + version + "/" + artifact + ".snow";
}
/**
* 返回该依赖的 group:artifact:version 字符串表示。
*
- * @return Maven 坐标字符串
+ * @return 坐标字符串
*/
@Override
public String toString() {
diff --git a/src/main/java/org/jcnc/snow/pkg/model/Project.java b/src/main/java/org/jcnc/snow/pkg/model/Project.java
index 2b09cdb..efedc97 100644
--- a/src/main/java/org/jcnc/snow/pkg/model/Project.java
+++ b/src/main/java/org/jcnc/snow/pkg/model/Project.java
@@ -8,42 +8,41 @@ import java.util.Map;
/**
* 表示一个软件包/模块的项目信息,包括元数据、属性、仓库、依赖和构建配置等。
*
- * 本类为不可变对象,仅提供getter,无setter。
- * 支持通过 {@link #fromFlatMap(Map)} 静态工厂方法从扁平Map快速创建。
+ * 本类为不可变对象,仅提供 getter 方法,无 setter。
+ * 支持通过 {@link #fromFlatMap(Map)} 静态工厂方法,从扁平 Map 快速创建实例。
*
*
*
- * Map<String,String> map = ...;
+ * Map<String, String> map = ...;
* Project project = Project.fromFlatMap(map);
*
*/
public final class Project {
- /** 组织/分组名(如com.example) */
+ /** 组织/分组名(如 com.example) */
private final String group;
- /** 构件/模块名(如app-core) */
+ /** 构件/模块名(如 app-core) */
private final String artifact;
/** 项目展示名称 */
private final String name;
- /** 版本号(如1.0.0) */
+ /** 版本号(如 1.0.0) */
private final String version;
/** 项目描述 */
private final String description;
/** 许可证标识 */
private final String license;
- /** 项目主页URL */
+ /** 项目主页 URL */
private final String homepage;
/** 额外属性(不影响主字段,可用于模板/占位符) */
private final Map properties;
- /** 仓库列表(仓库ID -> 仓库对象) */
+ /** 仓库列表(仓库 ID -> 仓库对象) */
private final Map repositories;
/** 依赖列表 */
private final List dependencies;
/** 构建配置 */
private final BuildConfiguration build;
-
/**
* 构造函数(私有),请使用 {@link #fromFlatMap(Map)} 创建实例。
*/
@@ -74,7 +73,7 @@ public final class Project {
}
/**
- * 通过扁平Map创建 Project 实例。约定key格式如下:
+ * 通过扁平 Map 创建 Project 实例。key 格式约定如下:
*
* - project.* —— 项目元数据
* - properties.* —— 额外属性
@@ -83,12 +82,11 @@ public final class Project {
* - build.* —— 构建配置
*
*
- * @param map 扁平的配置map
+ * @param map 扁平的配置 map
* @return Project 实例
*/
public static Project fromFlatMap(Map map) {
-
- // 1. simple project metadata
+ // 1. 基本元数据
String group = map.getOrDefault("project.group", "unknown");
String artifact = map.getOrDefault("project.artifact", "unknown");
String name = map.getOrDefault("project.name", artifact);
@@ -123,14 +121,13 @@ public final class Project {
}
});
- // 5. build.* simply hand the subtree map
+ // 5. build.*
Map buildMap = new LinkedHashMap<>();
map.forEach((k, v) -> {
if (k.startsWith("build.")) {
buildMap.put(k.substring("build.".length()), v);
}
});
-
BuildConfiguration buildCfg = BuildConfiguration.fromFlatMap(buildMap, props);
return new Project(group, artifact, name, version, description, license, homepage, props, repos, deps, buildCfg);
@@ -161,12 +158,12 @@ public final class Project {
return description;
}
- /** @return 许可证 */
+ /** @return 许可证标识 */
public String getLicense() {
return license;
}
- /** @return 项目主页URL */
+ /** @return 项目主页 URL */
public String getHomepage() {
return homepage;
}
diff --git a/src/main/java/org/jcnc/snow/pkg/model/Repository.java b/src/main/java/org/jcnc/snow/pkg/model/Repository.java
index 3e839f9..73d97b2 100644
--- a/src/main/java/org/jcnc/snow/pkg/model/Repository.java
+++ b/src/main/java/org/jcnc/snow/pkg/model/Repository.java
@@ -3,16 +3,16 @@ package org.jcnc.snow.pkg.model;
/**
* 表示一个远程仓库的基本信息,通常用于依赖解析和发布。
*
- * 每个仓库由唯一的ID和对应的URL确定。
+ * 每个仓库由唯一的 ID 和对应的 URL 标识。
*
*
*
- * 示例:
+ * 示例用法:
* Repository repo = new Repository("central", "https://");
*
*
* @param id 仓库唯一标识
- * @param url 仓库地址(一般为HTTP(S)链接)
+ * @param url 仓库地址(通常为 HTTP(S) 链接)
*/
public record Repository(String id, String url) {
}
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
index 0b5c1c5..80700d6 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
@@ -6,17 +6,27 @@ import java.nio.file.Path;
import java.util.Comparator;
/**
- * 清理构建输出目录(如 build 和 dist)的任务实现。
+ * 用于清理构建输出目录(如 {@code build} 和 {@code dist})的任务实现类。
*
- * 实现 {@link Task} 接口,通常用于构建流程中的清理阶段。
+ * 实现 {@link Task} 接口,常用于自动化构建流程的清理阶段,负责递归删除指定的构建产物目录。
*
+ *
+ * 本类为无状态实现,线程安全。
+ *
+ *
+ * 示例用法:
+ * {@code
+ * Task clean = new CleanTask();
+ * clean.run();
+ * }
*/
public final class CleanTask implements Task {
/**
- * 执行清理任务,删除 "build" 和 "dist" 目录及其所有内容。
+ * 执行清理任务,递归删除当前目录下的 {@code build} 和 {@code dist} 目录及其所有内容。
+ * 如果目标目录不存在,则跳过不处理。
*
- * @throws IOException 删除目录过程中出现 IO 错误时抛出
+ * @throws IOException 删除目录或文件过程中发生 IO 错误时抛出
*/
@Override
public void run() throws IOException {
@@ -27,10 +37,15 @@ public final class CleanTask implements Task {
/**
* 递归删除指定目录及其所有子文件和子目录。
- * 使用 try-with-resources 自动关闭文件流,避免资源泄漏。
+ *
+ * 若目录不存在,则直接返回。
+ *
+ *
+ * 内部使用 try-with-resources 保证文件流自动关闭,避免资源泄漏。
+ *
*
* @param dir 需要删除的目录路径
- * @throws IOException 删除过程中出现 IO 错误时抛出
+ * @throws IOException 删除目录或文件过程中发生 IO 错误时抛出
*/
private void deleteDir(Path dir) throws IOException {
if (Files.notExists(dir)) return;
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
index fb3fb99..04519d7 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
@@ -1,43 +1,264 @@
package org.jcnc.snow.pkg.tasks;
+import org.jcnc.snow.cli.commands.CompileCommand;
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.util.*;
/**
- * 编译项目源代码的任务实现。
+ * CLI 任务:编译 .snow 源文件为 VM 字节码(.water 文件)。
*
- * 实现 {@link Task} 接口,用于构建流程中的编译阶段。当前仅为示例,未集成实际编译器。
+ * 支持单文件、多文件和目录递归编译,并可在编译后立即运行虚拟机。
+ * 命令行参数支持 run、-o、-d 及直接指定源文件。
*
+ *
+ *
+ * 用法示例:
+ * $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow ...]
+ *
*/
public final class CompileTask implements Task {
-
- /** 待编译的项目 */
+ /** 项目信息 */
private final Project project;
+ /** 原始命令行参数 */
+ private final String[] args;
/**
- * 创建 CompileTask 实例。
+ * 创建一个编译任务。
*
- * @param project 目标项目
+ * @param project 项目信息对象
+ * @param args 命令行参数数组
*/
- public CompileTask(Project project) {
+ public CompileTask(Project project, String[] args) {
this.project = project;
+ this.args = args;
}
/**
- * 执行编译任务,打印源代码目录和输出目录。
- *
- * 实际编译尚未实现(TODO)。
- *
+ * 创建一个不带参数的编译任务。
*
- * @throws Exception 预留,未来集成编译器可能抛出异常
+ * @param project 项目信息对象
+ */
+ public CompileTask(Project project) {
+ this(project, new String[0]);
+ }
+
+ /**
+ * 执行编译任务。该方法会解析参数并调用 {@link #execute(String[])} 进行实际编译流程。
+ *
+ * @throws Exception 执行过程中出现任意异常时抛出
*/
@Override
public void run() throws Exception {
- // 获取源码目录和输出目录,默认分别为 "src" 和 "build/classes"
- Path srcDir = Path.of(project.getProperties().getOrDefault("src_dir", "src"));
- Path outDir = Path.of(project.getProperties().getOrDefault("output_dir", "build/classes"));
- System.out.println("[compile] sources=" + srcDir + " output=" + outDir);
- // TODO: 集成实际的编译器
+ execute(this.args);
+ }
+
+ /**
+ * 编译 .snow 源文件为 VM 字节码,并可选择立即运行。
+ *
+ * - 支持参数 run(编译后运行)、-o(输出文件名)、-d(递归目录)、直接指定多个源文件。
+ * - 输出源代码、AST、IR、最终 VM code,并写出 .water 文件。
+ *
+ *
+ * @param args 命令行参数数组
+ * @return 0 表示成功,非 0 表示失败
+ * @throws Exception 编译或写入过程中出现异常时抛出
+ */
+ public int execute(String[] args) throws Exception {
+ // ---------------- 解析命令行参数 ----------------
+ boolean runAfterCompile = false;
+ 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;
+ 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 ");
+ return 1;
+ }
+
+ // ---------------- 1. 词法/语法分析,并打印源代码 ----------------
+ List 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> 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. 写出 .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 输出文件名。
+ *
+ * - 如果指定 -o,直接使用该名称。
+ * - 目录编译时,取目录名。
+ * - 单文件编译时,取文件名去掉 .snow 后缀。
+ * - 否则默认 "program"。
+ *
+ *
+ * @param sources 源文件路径列表
+ * @param outName 输出文件名(如有指定,否则为 null)
+ * @param dir 源码目录(如有指定,否则为 null)
+ * @return 推断出的输出文件路径(.water 文件)
+ */
+ private static Path deriveOutputPath(List sources, String outName, Path dir) {
+ String base;
+ if (outName != null) {
+ base = outName;
+ } else if (dir != null) {
+ base = dir.getFileName().toString();
+ } else if (sources.size() == 1) {
+ base = sources.getFirst().getFileName().toString()
+ .replaceFirst("\\.snow$", "");
+ } else {
+ base = "program";
+ }
+ return Path.of(base + ".water");
+ }
+
+ /**
+ * 将 main 函数调整至函数列表首位,确保程序入口为 PC=0。
+ *
+ * @param in 原始 IRProgram
+ * @return 调整入口后的 IRProgram
+ */
+ private static IRProgram reorderForEntry(IRProgram in) {
+ List ordered = new ArrayList<>(in.functions());
+ for (int i = 0; i < ordered.size(); i++) {
+ if ("main".equals(ordered.get(i).name())) {
+ Collections.swap(ordered, 0, i);
+ break;
+ }
+ }
+ IRProgram out = new IRProgram();
+ ordered.forEach(out::add);
+ return out;
}
}
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java
new file mode 100644
index 0000000..83b9253
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java
@@ -0,0 +1,95 @@
+package org.jcnc.snow.pkg.tasks;
+
+import org.jcnc.snow.pkg.model.Project;
+import org.jcnc.snow.pkg.utils.SnowExample;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 项目脚手架生成任务。
+ * 根据 {@link Project} 元数据自动创建标准项目目录结构,并生成示例入口文件
+ * main.snow。
+ *
+ *
+ * 生成内容包括:
+ *
+ * src/ —— 源码根目录
+ * src/{group package}/ —— 按 project.group 创建的包路径
+ * (如 com.example → src/com/example/)
+ * test/ —— 测试源码目录
+ * build/ —— 编译输出目录
+ * dist/ —— 打包输出目录
+ * src/{group package}/main.snow —— 示例入口文件
+ *
+ * 如目录或入口文件已存在,则自动跳过,不会覆盖。
+ *
+ */
+public final class GenerateTask implements Task {
+
+ /** 项目信息元数据 */
+ private final Project project;
+
+ /**
+ * 创建项目生成任务。
+ *
+ * @param project 项目信息元数据对象
+ */
+ public GenerateTask(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * 执行脚手架生成流程,创建标准目录和入口示例文件。
+ *
+ * - 若相关目录不存在则创建
+ * - 若设置了
project.group,则在 src/ 下新建对应包路径
+ * - 示例入口文件
main.snow 写入包路径目录
+ * - 生成过程输出进度信息
+ *
+ *
+ * @throws IOException 创建目录或写入文件时发生 IO 错误时抛出
+ */
+ @Override
+ public void run() throws IOException {
+ Path root = Paths.get(".").toAbsolutePath();
+
+ /* ---------- 1. 构造待创建目录列表 ---------- */
+ List dirs = new ArrayList<>(List.of(
+ root.resolve("src"),
+ root.resolve("test"),
+ root.resolve("build"),
+ root.resolve("dist")
+ ));
+
+ /* ---------- 2. 处理 group:追加包目录 ---------- */
+ String group = project != null ? project.getGroup() : null;
+ Path srcDir = root.resolve("src");
+ Path packageDir = srcDir; // 默认直接在 src 下
+ if (group != null && !group.isBlank()) {
+ packageDir = srcDir.resolve(group.replace('.', '/'));
+ dirs.add(packageDir);
+ }
+
+ /* ---------- 3. 创建目录 ---------- */
+ for (Path dir : dirs) {
+ if (Files.notExists(dir)) {
+ Files.createDirectories(dir);
+ System.out.println("[generate] created directory " + root.relativize(dir));
+ }
+ }
+
+ /* ---------- 4. 写入示例入口文件 main.snow ---------- */
+ Path mainSnow = packageDir.resolve("main.snow");
+ if (Files.notExists(mainSnow)) {
+ Files.writeString(mainSnow, SnowExample.getMainModule());
+ System.out.println("[generate] created " + root.relativize(mainSnow));
+ }
+
+ System.out.println("[generate] project scaffold is ready.");
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/PackageTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/PackageTask.java
index 7524f73..a04dcf0 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/PackageTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/PackageTask.java
@@ -9,29 +9,38 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
- * 项目打包任务,将编译输出目录(如 build/classes)打包为 .ice 文件。
+ * 项目打包任务,将编译输出目录(如 build/classes)打包为 .ice 文件。
*
- * 实现 {@link Task} 接口,通常用于构建流程中的打包阶段。
+ * 实现 {@link Task} 接口,通常用于构建流程的打包阶段。
+ * 只会打包 build/classes 目录下所有文件,不含其他目录。
+ *
+ *
+ * 输出文件位于 dist 目录,命名为 artifact-version.ice。
*
*/
public final class PackageTask implements Task {
- /** 目标项目 */
+ /** 目标项目元数据 */
private final Project project;
/**
- * 创建 PackageTask 实例。
+ * 创建打包任务。
*
- * @param project 目标项目
+ * @param project 目标项目元数据
*/
public PackageTask(Project project) {
this.project = project;
}
/**
- * 执行打包任务,将编译输出目录压缩为 artifact-version.ice 文件。
+ * 执行打包任务,将 build/classes 目录下所有文件压缩为 dist/artifact-version.ice。
+ *
+ * - 若输出目录 dist 不存在会自动创建
+ * - 只打包 build/classes 下所有普通文件(保持相对目录结构)
+ * - 如无 build/classes 则不会生成包
+ *
*
- * @throws Exception 打包过程中出现 IO 或其他异常时抛出
+ * @throws Exception 打包过程中发生 IO 或其他异常时抛出
*/
@Override
public void run() throws Exception {
@@ -43,15 +52,15 @@ public final class PackageTask implements Task {
Path packageFile = distDir.resolve(fileName);
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(packageFile))) {
- // 仅将编译输出目录打包
+ // 仅将 build/classes 目录打包
Path classesDir = Path.of("build/classes");
if (Files.exists(classesDir)) {
- // 使用 try-with-resources 正确关闭 Stream
+ // 遍历所有文件并写入 zip
try (Stream stream = Files.walk(classesDir)) {
stream.filter(Files::isRegularFile)
.forEach(p -> {
try {
- // 将文件以相对路径加入压缩包
+ // 以 classesDir 为根的相对路径存入 zip
zos.putNextEntry(new ZipEntry(classesDir.relativize(p).toString()));
Files.copy(p, zos);
zos.closeEntry();
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/PublishTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/PublishTask.java
index d67b0e1..310b8fa 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/PublishTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/PublishTask.java
@@ -5,25 +5,26 @@ import org.jcnc.snow.pkg.model.Project;
/**
* 发布项目构件到远程仓库的任务实现。
*
- * 实现 {@link Task} 接口,通常用于构建流程中的发布阶段。目前仅为演示,尚未实现实际上传。
+ * 实现 {@link Task} 接口,通常用于构建流程中的发布阶段。
+ * 当前仅输出发布提示,尚未实现实际上传功能。
*
*/
public final class PublishTask implements Task {
- /** 目标项目 */
+ /** 目标项目元数据 */
private final Project project;
/**
- * 创建 PublishTask 实例。
+ * 创建发布任务。
*
- * @param project 目标项目
+ * @param project 目标项目元数据
*/
public PublishTask(Project project) {
this.project = project;
}
/**
- * 执行发布任务。当前仅打印发布提示,未实现实际上传逻辑。
+ * 执行发布任务。目前仅打印发布提示信息,未实现实际上传逻辑。
*
* @throws Exception 预留,未来实现上传逻辑时可能抛出异常
*/
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/RunTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/RunTask.java
new file mode 100644
index 0000000..ce83966
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/RunTask.java
@@ -0,0 +1,44 @@
+package org.jcnc.snow.pkg.tasks;
+
+import org.jcnc.snow.vm.VMLauncher;
+
+/**
+ * 任务:执行已编译的 VM 字节码文件(.water)。
+ *
+ * 作为 CLI、IDE 插件或其他宿主环境启动虚拟机的统一入口,
+ * 通过调用 {@link VMLauncher#main(String[])} 启动 VM 并执行指定程序。
+ *
+ */
+public final class RunTask implements Task {
+
+ /**
+ * 传递给虚拟机的完整参数列表(第一个应为 .water 文件路径)
+ */
+ private final String[] args;
+
+ /**
+ * 创建运行任务。
+ *
+ * @param args VM 参数数组(第一个为 .water 程序路径,其后为可选参数)
+ */
+ public RunTask(String... args) {
+ this.args = args;
+ }
+
+ /**
+ * 执行运行任务。内部委托 {@link VMLauncher#main(String[])} 启动 VM。
+ *
+ * - 如果参数为空则抛出 {@link IllegalArgumentException}
+ * - 异常由虚拟机本身抛出,直接透出
+ *
+ *
+ * @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);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/Task.java b/src/main/java/org/jcnc/snow/pkg/tasks/Task.java
index 755adc2..071fdbc 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/Task.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/Task.java
@@ -1,9 +1,9 @@
package org.jcnc.snow.pkg.tasks;
/**
- * 构建任务的通用接口,所有具体任务都应实现该接口。
+ * 构建任务的通用接口,所有具体任务(如编译、打包、清理等)都应实现该接口。
*
- * 用于统一生命周期内的任务行为,例如编译、打包、清理等。
+ * 用于统一不同阶段任务的生命周期与执行行为。
*
*/
public interface Task {
@@ -11,7 +11,7 @@ public interface Task {
/**
* 执行具体任务的入口方法。
*
- * @throws Exception 任务执行过程中出现的异常
+ * @throws Exception 任务执行过程中出现的任意异常
*/
void run() throws Exception;
}
diff --git a/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java b/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
new file mode 100644
index 0000000..6aea927
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
@@ -0,0 +1,58 @@
+package org.jcnc.snow.pkg.utils;
+
+/**
+ * 示例模块模板工具类,提供 main.snow 的标准示例代码字符串。
+ *
+ * 用于项目脚手架生成或帮助用户快速上手 .snow 语言。
+ *
+ */
+public final class SnowExample {
+
+ /**
+ * 工具类构造方法,禁止实例化。
+ */
+ private SnowExample() {
+ // 工具类不允许实例化
+ }
+
+ /**
+ * 获取 main.snow 示例模块的内容字符串。
+ *
+ * @return main.snow 示例模块的完整代码
+ */
+ public static String getMainModule() {
+ return """
+ module: Math
+ function: main
+ parameter:
+ return_type: int
+ body:
+ Math.factorial(6)
+ return 0
+ end body
+ end function
+
+ function: factorial
+ parameter:
+ declare n:int
+ return_type: int
+ body:
+ declare num1:int = 1
+ loop:
+ initializer:
+ declare counter:int = 1
+ condition:
+ counter <= n
+ update:
+ counter = counter + 1
+ body:
+ num1 = num1 * counter
+ end body
+ end loop
+ return num1
+ end body
+ end function
+ end module
+ """;
+ }
+}