diff --git a/.run/Bug1.run.xml b/.run/Bug1.run.xml
index f9c6da7..3c8e9ce 100644
--- a/.run/Bug1.run.xml
+++ b/.run/Bug1.run.xml
@@ -4,12 +4,6 @@
-
-
-
-
-
-
diff --git a/.run/Demo1.run.xml b/.run/Demo1.run.xml
index 055866b..98e3db5 100644
--- a/.run/Demo1.run.xml
+++ b/.run/Demo1.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo10.run.xml b/.run/Demo10.run.xml
index 3f6bf8b..29e014f 100644
--- a/.run/Demo10.run.xml
+++ b/.run/Demo10.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo11.run.xml b/.run/Demo11.run.xml
index ea7c1fc..6888bed 100644
--- a/.run/Demo11.run.xml
+++ b/.run/Demo11.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo12.run.xml b/.run/Demo12.run.xml
index 5d75b19..844a988 100644
--- a/.run/Demo12.run.xml
+++ b/.run/Demo12.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo13.run.xml b/.run/Demo13.run.xml
index 2544ad6..e6dff33 100644
--- a/.run/Demo13.run.xml
+++ b/.run/Demo13.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo14.run.xml b/.run/Demo14.run.xml
index 6071504..8b0f241 100644
--- a/.run/Demo14.run.xml
+++ b/.run/Demo14.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo15.run.xml b/.run/Demo15.run.xml
new file mode 100644
index 0000000..1767560
--- /dev/null
+++ b/.run/Demo15.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo2.run.xml b/.run/Demo2.run.xml
index 3b10a4e..4fb0f17 100644
--- a/.run/Demo2.run.xml
+++ b/.run/Demo2.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo3.run.xml b/.run/Demo3.run.xml
index 9d0224e..0bf6172 100644
--- a/.run/Demo3.run.xml
+++ b/.run/Demo3.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo4.run.xml b/.run/Demo4.run.xml
index a84cbe3..d777224 100644
--- a/.run/Demo4.run.xml
+++ b/.run/Demo4.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo5.run.xml b/.run/Demo5.run.xml
index e392f42..3fa8a56 100644
--- a/.run/Demo5.run.xml
+++ b/.run/Demo5.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo6.run.xml b/.run/Demo6.run.xml
index a55fc8e..a5c3432 100644
--- a/.run/Demo6.run.xml
+++ b/.run/Demo6.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo7.run.xml b/.run/Demo7.run.xml
index e8479ec..bef0dce 100644
--- a/.run/Demo7.run.xml
+++ b/.run/Demo7.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo8.run.xml b/.run/Demo8.run.xml
index d4554e1..c14026b 100644
--- a/.run/Demo8.run.xml
+++ b/.run/Demo8.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Demo9.run.xml b/.run/Demo9.run.xml
index 1002732..c8036a0 100644
--- a/.run/Demo9.run.xml
+++ b/.run/Demo9.run.xml
@@ -3,13 +3,7 @@
-
-
-
-
-
-
-
+
diff --git a/.run/Help.run.xml b/.run/Help.run.xml
index 849a3b4..89a7af9 100644
--- a/.run/Help.run.xml
+++ b/.run/Help.run.xml
@@ -3,12 +3,6 @@
-
-
-
-
-
-
diff --git a/.run/Version.run.xml b/.run/Version.run.xml
index 5816f30..954c579 100644
--- a/.run/Version.run.xml
+++ b/.run/Version.run.xml
@@ -3,12 +3,6 @@
-
-
-
-
-
-
diff --git a/.run/测试.run.xml b/.run/测试.run.xml
index c37e7f3..53dfe5a 100644
--- a/.run/测试.run.xml
+++ b/.run/测试.run.xml
@@ -5,6 +5,8 @@
+
+
diff --git a/playground/Demo/Demo15/Main.snow b/playground/Demo/Demo15/Main.snow
new file mode 100644
index 0000000..df8dc63
--- /dev/null
+++ b/playground/Demo/Demo15/Main.snow
@@ -0,0 +1,13 @@
+module: Main
+ import: os
+ globals:
+ declare num1:int=10
+ declare num2:int=10
+ function: main
+ return_type: void
+ body:
+ declare num1:int=11
+ print(num1+num2)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo15/OS.snow b/playground/Demo/Demo15/OS.snow
new file mode 100644
index 0000000..6026d43
--- /dev/null
+++ b/playground/Demo/Demo15/OS.snow
@@ -0,0 +1,11 @@
+module: os
+ import: os
+ function: print
+ parameter:
+ declare i1: int
+ return_type: void
+ body:
+ syscall("PRINT",i1)
+ end body
+ end function
+end module
\ No newline at end of file
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 361413b..6955a16 100644
--- a/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
+++ b/src/main/java/org/jcnc/snow/cli/commands/CompileCommand.java
@@ -60,9 +60,9 @@ public final class CompileCommand implements CLICommand {
List argList = new ArrayList<>();
- // 保留用户在 cloud 模式下传入的 “run” / “-debug” 标志
+ // 保留用户在 cloud 模式下传入的 “run” / “--debug” 标志
for (String a : args) {
- if ("run".equals(a) || "-debug".equals(a)) {
+ if ("run".equals(a) || "--debug".equals(a)) {
argList.add(a);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java
index d32a544..dbde250 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java
@@ -8,6 +8,7 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -36,8 +37,8 @@ public final class IRProgramBuilder {
for (Node node : roots) {
switch (node) {
case ModuleNode moduleNode ->
- // 模块节点: 批量构建并添加模块内所有函数
- moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f)));
+ // 对每个模块,所有函数均自动注入 globals
+ moduleNode.functions().forEach(f -> irProgram.add(buildFunctionWithGlobals(moduleNode, f)));
case FunctionNode functionNode ->
// 顶层函数节点: 直接构建并添加
irProgram.add(buildFunction(functionNode));
@@ -52,6 +53,30 @@ public final class IRProgramBuilder {
return irProgram;
}
+ /**
+ * 构建带有模块全局声明“注入”的函数。
+ * 为了在当前 IR 设计下能访问全局变量,将模块的 globals 作为前置声明
+ * 追加到函数体最前面,使其在函数内被注册到 IR 作用域中。
+ */
+ private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
+ if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
+ return buildFunction(functionNode);
+ }
+ // 重建函数体:先放 globals 的声明,再放原来的语句
+ List newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
+ newBody.addAll(moduleNode.globals());
+ newBody.addAll(functionNode.body());
+
+ FunctionNode wrapped = new FunctionNode(
+ functionNode.name(),
+ functionNode.parameters(),
+ functionNode.returnType(),
+ newBody,
+ functionNode.context()
+ );
+ return buildFunction(wrapped);
+ }
+
/**
* 利用 FunctionBuilder 将 FunctionNode 转换为 IRFunction。
*
@@ -79,8 +104,8 @@ public final class IRProgramBuilder {
private FunctionNode wrapTopLevel(StatementNode stmt) {
return new FunctionNode(
"_start",
- null,
- String.valueOf(List.of()),
+ List.of(),
+ "void",
List.of(stmt),
new NodeContext(-1, -1, "")
);
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
index 27ef96b..c22acf0 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
@@ -12,7 +12,7 @@ import java.util.Set;
*
*
*
- * 主要功能与特性:
+ * 主要功能与特性:
*
* - 统一管理语言关键字和类型名集合,便于扩展与维护。
* - 自动推断 Token 类型,无需外部干预。
@@ -25,7 +25,7 @@ public class TokenFactory {
/**
* 语言的保留关键字集合。
*/
- private static final Set KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step");
+ private static final Set KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step","globals");
/**
* 内置类型名称集合,如 int、string 等。
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
index 84262dd..2a1b371 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
@@ -18,6 +18,7 @@ import java.util.StringJoiner;
public record ModuleNode(
String name,
List imports,
+ List globals,
List functions,
NodeContext context
) implements Node {
@@ -33,12 +34,17 @@ public record ModuleNode(
for (ImportNode imp : imports) {
importJoiner.add(imp.moduleName());
}
+ StringJoiner globalJoiner = new StringJoiner(", ");
+ for (DeclarationNode g : globals) {
+ globalJoiner.add(g.getType() + " " + g.getName());
+ }
StringJoiner funcJoiner = new StringJoiner(", ");
for (FunctionNode fn : functions) {
funcJoiner.add(fn.name());
}
return "Module(name=" + name
+ ", imports=[" + importJoiner + "]"
+ + ", globals=[" + globalJoiner + "]"
+ ", functions=[" + funcJoiner + "])";
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
index 5cc7335..f438719 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
@@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ImportNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
@@ -10,6 +11,7 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.function.FunctionParser;
+import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
import java.util.ArrayList;
import java.util.List;
@@ -17,7 +19,7 @@ import java.util.List;
/**
* {@code ModuleParser} 负责解析源码中的模块结构,是顶层结构解析器实现之一。
*
- * 模块定义可包含多个导入(import)语句和函数定义(function),
+ * 模块定义可包含多个导入(import)语句、globals 全局声明和函数定义(function),
* 导入语句可在模块中任意位置出现,且允许模块体中穿插任意数量的空行(空行会被自动忽略,不影响语法结构)。
*
*
@@ -26,6 +28,8 @@ import java.util.List;
*
* module: mymod
* import ...
+ * globals:
+ * declare ...
* function ...
* ...
* end module
@@ -40,7 +44,7 @@ public class ModuleParser implements TopLevelParser {
* 解析过程包括:
*
* - 匹配模块声明起始 {@code module: IDENTIFIER}。
- * - 收集模块体内所有 import 和 function 语句,允许穿插空行。
+ * - 收集模块体内所有 import、globals 和 function 语句,允许穿插空行。
* - 匹配模块结束 {@code end module}。
*
* 若遇到未识别的语句,将抛出 {@link UnexpectedToken} 异常,定位错误位置和原因。
@@ -64,12 +68,15 @@ public class ModuleParser implements TopLevelParser {
ts.expectType(TokenType.NEWLINE);
List imports = new ArrayList<>();
+ List globals = new ArrayList<>();
List functions = new ArrayList<>();
ImportParser importParser = new ImportParser();
FunctionParser funcParser = new FunctionParser();
+ DeclarationStatementParser globalsParser = new DeclarationStatementParser();
while (true) {
+ // 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
@@ -78,12 +85,33 @@ public class ModuleParser implements TopLevelParser {
break;
}
String lex = ts.peek().getLexeme();
- if ("import".equals(lex)) {
- imports.addAll(importParser.parse(ctx));
- } else if ("function".equals(lex)) {
- functions.add(funcParser.parse(ctx));
- } else {
- throw new UnexpectedToken(
+ switch (lex) {
+ case "import" -> imports.addAll(importParser.parse(ctx));
+ case "function" -> functions.add(funcParser.parse(ctx));
+ case "globals" -> {
+ ts.expect("globals");
+ ts.expect(":");
+ ts.expectType(TokenType.NEWLINE);
+ while (true) {
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+ String innerLex = ts.peek().getLexeme();
+ if ("declare".equals(innerLex)) {
+ globals.add(globalsParser.parse(ctx));
+ } else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
+ break;
+ } else {
+ throw new UnexpectedToken(
+ "globals 区块中不支持的内容: " + innerLex,
+ ts.peek().getLine(),
+ ts.peek().getCol()
+ );
+ }
+ }
+ }
+ case null, default -> throw new UnexpectedToken(
"Unexpected token in module: " + lex,
ts.peek().getLine(),
ts.peek().getCol()
@@ -94,6 +122,6 @@ public class ModuleParser implements TopLevelParser {
ts.expect("end");
ts.expect("module");
- return new ModuleNode(name, imports, functions, new NodeContext(line, column, file));
+ return new ModuleNode(name, imports, globals, functions, new NodeContext(line, column, file));
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
index 9091af8..b3094c1 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
@@ -14,7 +14,7 @@ import java.util.*;
* 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串,用于调试、
* 可视化或跨语言数据传输。
*
- * 支持的节点类型包括(新增对 {@code BoolLiteralNode}、{@code UnaryExpressionNode} 的完整支持):
+ * 支持的节点类型包括:
*
* - {@link ModuleNode}
* - {@link FunctionNode}
@@ -83,7 +83,7 @@ public class ASTJsonSerializer {
return switch (n) {
// 模块节点
case ModuleNode(
- String name, List imports, List functions, NodeContext _
+ String name, List imports, List globals, List functions, NodeContext _
) -> {
Map map = newNodeMap("Module");
map.put("name", name);
@@ -92,6 +92,15 @@ public class ASTJsonSerializer {
imps.add(Map.of("type", "Import", "module", imp.moduleName()));
}
map.put("imports", imps);
+ List