refactor: 优化全局变量处理和常量折叠逻辑

- 过滤与函数参数重名的全局声明,避免命名冲突
- 优化常量折叠逻辑,支持跨模块常量优化- 修复字符串字面量和布尔字面量的处理
- 优化代码结构,提高可读性和可维护性
This commit is contained in:
Luke 2025-08-26 16:25:19 +08:00
parent 71cbb3a737
commit b058ee4526

View File

@ -10,7 +10,9 @@ 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.HashSet;
import java.util.List;
import java.util.Set;
/**
* IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}
@ -38,16 +40,16 @@ public final class IRProgramBuilder {
for (Node node : roots) {
switch (node) {
case ModuleNode moduleNode -> {
// 处理模块节点:遍历其中所有函数统一用模块名.函数名作为全限定名
// 处理模块节点遍历其中所有函数统一用模块名.函数名作为全限定名避免命名冲突
for (FunctionNode f : moduleNode.functions()) {
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
}
}
case FunctionNode functionNode ->
// 处理顶层函数节点:直接构建为 IRFunction 并加入
// 处理顶层函数节点直接构建为 IRFunction 并加入
irProgram.add(buildFunction(functionNode));
case StatementNode statementNode ->
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
// 处理脚本式顶层语句封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default ->
// 遇到未知类型节点抛出异常
@ -62,10 +64,9 @@ public final class IRProgramBuilder {
/**
* 扫描所有模块节点将其中声明的 const 全局变量compile-time 常量
* "模块名.常量名" 形式注册到全局常量表
* <p>
* 只处理直接字面量数字字符串布尔暂不支持复杂表达式
* 支持跨模块常量折叠用于后续 IR 生成过程中的常量折叠优化
*
* @param roots 所有顶层 AST 节点
* @param roots AST 顶层节点列表
*/
private void preloadGlobals(List<Node> roots) {
for (Node n : roots) {
@ -73,6 +74,7 @@ public final class IRProgramBuilder {
String moduleName = mod.name();
if (mod.globals() == null) continue;
for (DeclarationNode decl : mod.globals()) {
// 只处理 compile-time const 常量并要求有初始值
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
ExpressionNode init = decl.getInitializer().get();
Object value = evalLiteral(init);
@ -86,53 +88,40 @@ public final class IRProgramBuilder {
/**
* 字面量提取与类型折叠工具
* <p>
* 用于从表达式节点中解析出直接可用于全局常量表注册的 Java
* 支持以下字面量类型
* <ul>
* <li>整数与浮点可带下划线分隔符支持类型后缀 b/s/l/f/d自动转换为对应 Java 基本类型</li>
* <li>字符串字面量直接返回内容</li>
* <li>布尔字面量true 返回 1false 返回 0用于数值语境</li>
* </ul>
* 不支持的表达式或解析失败将返回 null
* 用于将表达式节点还原为 Java 原生类型intlongdoubleString等仅支持直接字面量
*
* @param expr 字面量表达式节点通常为 NumberLiteralNode/StringLiteralNode/BoolLiteralNode
* @return 提取出的 Java 可为 Integer/Byte/Short/Long/Double/String/Integer(布尔) null
* @param expr 要计算的表达式节点
* @return 提取到的原生常量值不支持的情况返回 null
*/
private Object evalLiteral(ExpressionNode expr) {
return switch (expr) {
case NumberLiteralNode num -> {
String raw = num.value();
// 1. 去掉下划线分隔符便于解析 1_000_000
String s = raw.replace("_", "");
// 2. 提取类型后缀b/s/l/f/d若有则去除
char last = Character.toLowerCase(s.charAt(s.length() - 1));
String core = switch (last) {
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
default -> s;
};
try {
// 3. 若为浮点字面量或科学计数法直接转 double
if (core.contains(".") || core.contains("e") || core.contains("E")) {
double dv = Double.parseDouble(core);
yield dv;
// 浮点数处理
yield Double.parseDouble(core);
}
// 4. 整数字面量先转 long再根据后缀决定精度
long lv = Long.parseLong(core);
yield switch (last) {
case 'b' -> (byte) lv; // 末尾 b byte
case 's' -> (short) lv; // 末尾 s short
case 'l' -> lv; // 末尾 l long
default -> (int) lv; // 无后缀 int
case 'b' -> (byte) lv;
case 's' -> (short) lv;
case 'l' -> lv;
default -> (int) lv;
};
} catch (NumberFormatException ignore) {
// 解析失败返回 null
yield null;
}
}
case StringLiteralNode str -> str.value(); // 字符串字面量返回内容
case BoolLiteralNode b -> b.getValue() ? 1 : 0; // 布尔字面量true=1false=0
default -> null; // 其它类型不支持
case StringLiteralNode str -> str.value();
case BoolLiteralNode b -> b.getValue() ? 1 : 0;
default -> null;
};
}
@ -140,23 +129,41 @@ public final class IRProgramBuilder {
/**
* 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一
* <p>
* 如果模块有全局声明则这些声明会被插入到函数体前部
* 如果模块有全局声明则这些声明会被插入到函数体前部**会过滤掉与参数同名的全局声明**
*
* @param moduleNode 当前模块节点
* @param functionNode 模块中的函数节点
* @return 包含全局声明已加前缀函数名 IRFunction
* @param moduleNode 所属模块节点
* @param functionNode 待构建的函数节点
* @return 包含全局声明 IRFunction
*/
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
// 拼接模块名和函数名生成全限定名
String qualifiedName = moduleNode.name() + "." + functionNode.name();
// 若无全局声明仅重命名后直接构建
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
// 无全局声明直接重命名构建
return buildFunction(renameFunction(functionNode, qualifiedName));
}
// 若有全局声明插入到函数体最前面
List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
newBody.addAll(moduleNode.globals());
// ------- 过滤与参数重名的全局声明 -------
Set<String> paramNames = new HashSet<>();
for (ParameterNode p : functionNode.parameters()) {
paramNames.add(p.name());
}
List<StatementNode> filteredGlobals = new ArrayList<>();
for (DeclarationNode g : moduleNode.globals()) {
// 避免全局声明和参数重名优先参数
if (!paramNames.contains(g.getName())) {
filteredGlobals.add(g);
}
}
if (filteredGlobals.isEmpty()) {
// 过滤后已无可插入的全局声明
return buildFunction(renameFunction(functionNode, qualifiedName));
}
// 合并全局声明与函数体前插全局声明
List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
newBody.addAll(filteredGlobals);
newBody.addAll(functionNode.body());
FunctionNode wrapped = new FunctionNode(
qualifiedName,
@ -169,11 +176,11 @@ public final class IRProgramBuilder {
}
/**
* 生成一个重命名的 FunctionNode(只修改函数名其他属性保持不变)
* 生成一个重命名的 FunctionNode只修改函数名其他属性保持不变
*
* @param fn 原始函数节点
* @param newName 新的函数名(通常为全限定名)
* @return 重命名后的 FunctionNode
* @param newName 新的函数名全限定名
* @return 重命名后的函数节点
*/
private FunctionNode renameFunction(FunctionNode fn, String newName) {
return new FunctionNode(
@ -188,8 +195,8 @@ public final class IRProgramBuilder {
/**
* 构建 IRFunction
*
* @param functionNode 要转换的函数节点
* @return 转换结果 IRFunction
* @param functionNode 待构建的 FunctionNode
* @return 构建后的 IRFunction
*/
private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode);
@ -197,11 +204,10 @@ public final class IRProgramBuilder {
/**
* 将顶层语句节点封装成特殊的 "_start" 函数
* <p>
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)
* 主要用于脚本模式支持使得顶层语句也可以被 IR 执行引擎统一处理
*
* @param stmt 要封装的顶层语句
* @return 包装成 FunctionNode "_start" 函数
* @param stmt 顶层语句节点
* @return 封装后的 FunctionNode
*/
private FunctionNode wrapTopLevel(StatementNode stmt) {
return new FunctionNode(