feat: 支持模块全局变量声明

- 在 ModuleNode 中添加 globals 字段,用于存储全局变量声明
- 实现 globals 区块的解析逻辑,支持全局变量的声明
- 更新 ASTJsonSerializer 以支持全局变量的序列化
- 修改 FunctionChecker 和 IRProgramBuilder 以支持全局变量
This commit is contained in:
Luke 2025-07-28 13:39:33 +08:00
parent 421eb0a0d9
commit 21ca6e6cab
6 changed files with 97 additions and 21 deletions

View File

@ -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.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -36,8 +37,8 @@ public final class IRProgramBuilder {
for (Node node : roots) { for (Node node : roots) {
switch (node) { switch (node) {
case ModuleNode moduleNode -> case ModuleNode moduleNode ->
// 模块节点: 批量构建并添加模块内所有函数 // 对每个模块所有函数均自动注入 globals
moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f))); moduleNode.functions().forEach(f -> irProgram.add(buildFunctionWithGlobals(moduleNode, f)));
case FunctionNode functionNode -> case FunctionNode functionNode ->
// 顶层函数节点: 直接构建并添加 // 顶层函数节点: 直接构建并添加
irProgram.add(buildFunction(functionNode)); irProgram.add(buildFunction(functionNode));
@ -52,6 +53,30 @@ public final class IRProgramBuilder {
return irProgram; return irProgram;
} }
/**
* 构建带有模块全局声明注入的函数
* 为了在当前 IR 设计下能访问全局变量将模块的 globals 作为前置声明
* 追加到函数体最前面使其在函数内被注册到 IR 作用域中
*/
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
return buildFunction(functionNode);
}
// 重建函数体先放 globals 的声明再放原来的语句
List<StatementNode> 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 * 利用 FunctionBuilder FunctionNode 转换为 IRFunction
* *
@ -79,8 +104,8 @@ public final class IRProgramBuilder {
private FunctionNode wrapTopLevel(StatementNode stmt) { private FunctionNode wrapTopLevel(StatementNode stmt) {
return new FunctionNode( return new FunctionNode(
"_start", "_start",
null, List.of(),
String.valueOf(List.of()), "void",
List.of(stmt), List.of(stmt),
new NodeContext(-1, -1, "") new NodeContext(-1, -1, "")
); );

View File

@ -12,7 +12,7 @@ import java.util.Set;
* </p> * </p>
* *
* <p> * <p>
* 主要功能与特性: * 主要功能与特性:
* <ul> * <ul>
* <li>统一管理语言关键字和类型名集合便于扩展与维护</li> * <li>统一管理语言关键字和类型名集合便于扩展与维护</li>
* <li>自动推断 Token 类型无需外部干预</li> * <li>自动推断 Token 类型无需外部干预</li>
@ -25,7 +25,7 @@ public class TokenFactory {
/** /**
* 语言的保留关键字集合 * 语言的保留关键字集合
*/ */
private static final Set<String> KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step"); private static final Set<String> KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step","globals");
/** /**
* 内置类型名称集合 intstring * 内置类型名称集合 intstring

View File

@ -18,6 +18,7 @@ import java.util.StringJoiner;
public record ModuleNode( public record ModuleNode(
String name, String name,
List<ImportNode> imports, List<ImportNode> imports,
List<DeclarationNode> globals,
List<FunctionNode> functions, List<FunctionNode> functions,
NodeContext context NodeContext context
) implements Node { ) implements Node {
@ -33,12 +34,17 @@ public record ModuleNode(
for (ImportNode imp : imports) { for (ImportNode imp : imports) {
importJoiner.add(imp.moduleName()); importJoiner.add(imp.moduleName());
} }
StringJoiner globalJoiner = new StringJoiner(", ");
for (DeclarationNode g : globals) {
globalJoiner.add(g.getType() + " " + g.getName());
}
StringJoiner funcJoiner = new StringJoiner(", "); StringJoiner funcJoiner = new StringJoiner(", ");
for (FunctionNode fn : functions) { for (FunctionNode fn : functions) {
funcJoiner.add(fn.name()); funcJoiner.add(fn.name());
} }
return "Module(name=" + name return "Module(name=" + name
+ ", imports=[" + importJoiner + "]" + ", imports=[" + importJoiner + "]"
+ ", globals=[" + globalJoiner + "]"
+ ", functions=[" + funcJoiner + "])"; + ", functions=[" + funcJoiner + "])";
} }
} }

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.module; package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType; 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.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ImportNode; import org.jcnc.snow.compiler.parser.ast.ImportNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode; 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.TokenStream;
import org.jcnc.snow.compiler.parser.context.UnexpectedToken; import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.function.FunctionParser; import org.jcnc.snow.compiler.parser.function.FunctionParser;
import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -17,7 +19,7 @@ import java.util.List;
/** /**
* {@code ModuleParser} 负责解析源码中的模块结构是顶层结构解析器实现之一 * {@code ModuleParser} 负责解析源码中的模块结构是顶层结构解析器实现之一
* <p> * <p>
* 模块定义可包含多个导入import语句和函数定义function * 模块定义可包含多个导入import语句globals 全局声明和函数定义function
* 导入语句可在模块中任意位置出现且允许模块体中穿插任意数量的空行空行会被自动忽略不影响语法结构 * 导入语句可在模块中任意位置出现且允许模块体中穿插任意数量的空行空行会被自动忽略不影响语法结构
* </p> * </p>
* *
@ -26,6 +28,8 @@ import java.util.List;
* <pre> * <pre>
* module: mymod * module: mymod
* import ... * import ...
* globals:
* declare ...
* function ... * function ...
* ... * ...
* end module * end module
@ -40,7 +44,7 @@ public class ModuleParser implements TopLevelParser {
* 解析过程包括: * 解析过程包括:
* <ol> * <ol>
* <li>匹配模块声明起始 {@code module: IDENTIFIER}</li> * <li>匹配模块声明起始 {@code module: IDENTIFIER}</li>
* <li>收集模块体内所有 import function 语句允许穿插空行</li> * <li>收集模块体内所有 importglobals function 语句允许穿插空行</li>
* <li>匹配模块结束 {@code end module}</li> * <li>匹配模块结束 {@code end module}</li>
* </ol> * </ol>
* 若遇到未识别的语句将抛出 {@link UnexpectedToken} 异常定位错误位置和原因 * 若遇到未识别的语句将抛出 {@link UnexpectedToken} 异常定位错误位置和原因
@ -64,12 +68,15 @@ public class ModuleParser implements TopLevelParser {
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
List<ImportNode> imports = new ArrayList<>(); List<ImportNode> imports = new ArrayList<>();
List<DeclarationNode> globals = new ArrayList<>();
List<FunctionNode> functions = new ArrayList<>(); List<FunctionNode> functions = new ArrayList<>();
ImportParser importParser = new ImportParser(); ImportParser importParser = new ImportParser();
FunctionParser funcParser = new FunctionParser(); FunctionParser funcParser = new FunctionParser();
DeclarationStatementParser globalsParser = new DeclarationStatementParser();
while (true) { while (true) {
// 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) { if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next(); ts.next();
continue; continue;
@ -78,12 +85,33 @@ public class ModuleParser implements TopLevelParser {
break; break;
} }
String lex = ts.peek().getLexeme(); String lex = ts.peek().getLexeme();
if ("import".equals(lex)) { switch (lex) {
imports.addAll(importParser.parse(ctx)); case "import" -> imports.addAll(importParser.parse(ctx));
} else if ("function".equals(lex)) { case "function" -> functions.add(funcParser.parse(ctx));
functions.add(funcParser.parse(ctx)); case "globals" -> {
} else { ts.expect("globals");
throw new UnexpectedToken( 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, "Unexpected token in module: " + lex,
ts.peek().getLine(), ts.peek().getLine(),
ts.peek().getCol() ts.peek().getCol()
@ -94,6 +122,6 @@ public class ModuleParser implements TopLevelParser {
ts.expect("end"); ts.expect("end");
ts.expect("module"); 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));
} }
} }

View File

@ -14,7 +14,7 @@ import java.util.*;
* 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串用于调试 * 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串用于调试
* 可视化或跨语言数据传输 * 可视化或跨语言数据传输
* <p> * <p>
* 支持的节点类型包括新增对 {@code BoolLiteralNode}{@code UnaryExpressionNode} 的完整支持: * 支持的节点类型包括:
* <ul> * <ul>
* <li>{@link ModuleNode}</li> * <li>{@link ModuleNode}</li>
* <li>{@link FunctionNode}</li> * <li>{@link FunctionNode}</li>
@ -83,7 +83,7 @@ public class ASTJsonSerializer {
return switch (n) { return switch (n) {
// 模块节点 // 模块节点
case ModuleNode( case ModuleNode(
String name, List<ImportNode> imports, List<FunctionNode> functions, NodeContext _ String name, List<ImportNode> imports, List<DeclarationNode> globals, List<FunctionNode> functions, NodeContext _
) -> { ) -> {
Map<String, Object> map = newNodeMap("Module"); Map<String, Object> map = newNodeMap("Module");
map.put("name", name); map.put("name", name);
@ -92,6 +92,15 @@ public class ASTJsonSerializer {
imps.add(Map.of("type", "Import", "module", imp.moduleName())); imps.add(Map.of("type", "Import", "module", imp.moduleName()));
} }
map.put("imports", imps); map.put("imports", imps);
List<Object> globs = new ArrayList<>(globals.size());
for (DeclarationNode d : globals) {
Map<String, Object> decl = newNodeMap("Declaration");
decl.put("name", d.getName());
decl.put("varType", d.getType());
decl.put("init", d.getInitializer().map(ASTJsonSerializer::exprToMap).orElse(null));
globs.add(decl);
}
map.put("globals", globs);
List<Object> funcs = new ArrayList<>(functions.size()); List<Object> funcs = new ArrayList<>(functions.size());
for (FunctionNode f : functions) { for (FunctionNode f : functions) {
funcs.add(nodeToMap(f)); funcs.add(nodeToMap(f));

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode; import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.ReturnNode; import org.jcnc.snow.compiler.parser.ast.ReturnNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -16,7 +17,7 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
* 它逐个遍历所有模块中的函数定义并对函数体中的每一条语句调用对应的语义分析器 * 它逐个遍历所有模块中的函数定义并对函数体中的每一条语句调用对应的语义分析器
* 执行类型检查作用域验证错误记录等任务 * 执行类型检查作用域验证错误记录等任务
* <p> * <p>
* 核心职责包括: * 核心职责包括:
* <ul> * <ul>
* <li>为每个函数构建局部符号表并注册函数参数为变量</li> * <li>为每个函数构建局部符号表并注册函数参数为变量</li>
* <li>分发函数体语句至相应的 {@link StatementAnalyzer}</li> * <li>分发函数体语句至相应的 {@link StatementAnalyzer}</li>
@ -39,7 +40,7 @@ public record FunctionChecker(Context ctx) {
/** /**
* 执行函数体检查流程 * 执行函数体检查流程
* <p> * <p>
* 对所有模块中的所有函数依次进行处理: * 对所有模块中的所有函数依次进行处理:
* <ol> * <ol>
* <li>查找模块对应的 {@link ModuleInfo}</li> * <li>查找模块对应的 {@link ModuleInfo}</li>
* <li>创建函数局部符号表 {@link SymbolTable}并注册所有参数变量</li> * <li>创建函数局部符号表 {@link SymbolTable}并注册所有参数变量</li>
@ -54,11 +55,18 @@ public record FunctionChecker(Context ctx) {
// 获取当前模块对应的语义信息 // 获取当前模块对应的语义信息
ModuleInfo mi = ctx.modules().get(mod.name()); ModuleInfo mi = ctx.modules().get(mod.name());
// 先构建全局符号表
SymbolTable globalScope = new SymbolTable(null);
for (DeclarationNode g : mod.globals()) {
var t = ctx.parseType(g.getType());
globalScope.define(new Symbol(g.getName(), t, SymbolKind.VARIABLE));
}
// 遍历模块中所有函数定义 // 遍历模块中所有函数定义
for (FunctionNode fn : mod.functions()) { for (FunctionNode fn : mod.functions()) {
// 构建函数局部作用域符号表设置父作用域为 null // 构建函数局部作用域符号表父作用域为 globalScope
SymbolTable locals = new SymbolTable(null); SymbolTable locals = new SymbolTable(globalScope);
// 将函数参数注册为局部变量 // 将函数参数注册为局部变量
fn.parameters().forEach(p -> fn.parameters().forEach(p ->