feat: 支持结构体并优化全局常量处理

- 新增结构体字段布局预扫描和注册功能
- 实现结构体方法和构造函数的降级处理
- 优化全局常量收集逻辑,支持跨模块常量折叠
- 重构代码,明确方法职责,提高可读性和可维护性
This commit is contained in:
Luke 2025-08-29 17:49:12 +08:00
parent 093e5a26b9
commit 1f7458be28

View File

@ -9,19 +9,25 @@ 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.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** /**
* IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram} * IRProgramBuilder 负责将 AST 顶层节点如模块函数语句等转换为可执行的 {@link IRProgram}
* *
* <p>
* 主要工作内容包括
* </p>
* <ul> * <ul>
* <li>预扫描所有模块 <code>declare const</code> 常量登记到全局常量表支持跨模块常量折叠</li> * <li>预扫描所有模块 <code>declare const</code> 常量登记到全局常量表支持跨模块常量折叠</li>
* <li>对模块内的函数加上模块前缀保证命名唯一并将本模块全局声明注入到函数体前部</li> * <li>预扫描并注册所有 struct 的字段布局字段下标 IR 阶段对象字段读写使用</li>
* <li>对模块内的普通函数加上模块前缀ModuleName.func保证命名唯一并将本模块全局声明注入到函数体前部</li>
* <li> struct init methods 降级为普通 IR 函数并注册为 <code>StructName.__init__</code><code>StructName.method</code>同时在其参数列表首位插入隐式 <code>this: StructName</code></li>
* <li>将独立顶层语句自动包装为特殊的 "_start" 函数脚本模式支持</li> * <li>将独立顶层语句自动包装为特殊的 "_start" 函数脚本模式支持</li>
* </ul> * </ul>
*
* <p>
* 该类为不可变工具类仅包含静态行为不持有状态
* </p>
*/ */
public final class IRProgramBuilder { public final class IRProgramBuilder {
@ -31,40 +37,166 @@ public final class IRProgramBuilder {
* @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表 * @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表
* @return 包含所有转换后 IRFunction IRProgram 对象 * @return 包含所有转换后 IRFunction IRProgram 对象
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出 * @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
*
* <p>
* 主流程为
* <ol>
* <li>首先登记全局常量便于后续常量折叠优化</li>
* <li>再注册所有 struct 的字段布局为成员读写和 this.xx 做准备</li>
* <li>随后依次遍历各顶层节点根据类型进行分发处理</li>
* <ul>
* <li>ModuleNode: 先降级并注册 struct 方法/构造再处理普通函数</li>
* <li>FunctionNode: 直接构建为 IRFunction</li>
* <li>StatementNode: 作为脚本模式顶层语句包装进 "_start" 函数</li>
* <li>其他类型: 抛出异常</li>
* </ul>
* </ol>
* </p>
*/ */
public IRProgram buildProgram(List<Node> roots) { public IRProgram buildProgram(List<Node> roots) {
// 预先收集并登记全部模块常量到全局常量表 // 1. 先登记全局常量便于后续常量折叠
preloadGlobals(roots); preloadGlobals(roots);
// 2. 注册所有结构体的字段布局为成员访问做准备
preloadStructLayouts(roots);
// 创建 IR 程序对象
IRProgram irProgram = new IRProgram(); IRProgram irProgram = new IRProgram();
// 3. 遍历并处理所有顶层节点
for (Node node : roots) { for (Node node : roots) {
switch (node) { switch (node) {
case ModuleNode moduleNode -> { case ModuleNode moduleNode -> {
// 处理模块节点遍历其中所有函数统一用模块名.函数名作为全限定名避免命名冲突 // 3.1 先降级并注册本模块所有 struct 的构造/方法struct 方法降级
if (moduleNode.structs() != null) {
for (StructNode structNode : moduleNode.structs()) {
lowerAndRegisterStruct(structNode, irProgram);
}
}
// 3.2 再处理模块里的普通函数模块内函数名全限定注入全局声明
if (moduleNode.functions() != null) {
for (FunctionNode f : moduleNode.functions()) { for (FunctionNode f : moduleNode.functions()) {
irProgram.add(buildFunctionWithGlobals(moduleNode, f)); irProgram.add(buildFunctionWithGlobals(moduleNode, f));
} }
} }
}
case FunctionNode functionNode -> case FunctionNode functionNode ->
// 处理顶层函数节点直接构建为 IRFunction 并加入 // 3.3 处理顶层函数节点直接构建为 IRFunction 并加入
irProgram.add(buildFunction(functionNode)); irProgram.add(buildFunction(functionNode));
case StatementNode statementNode -> case StatementNode statementNode ->
// 处理脚本式顶层语句封装成 "_start" 函数后构建并添加 // 3.4 处理脚本式顶层语句封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode))); irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default -> default ->
// 遇到未知类型节点抛出异常 // 3.5 遇到未知类型节点抛出异常
throw new IllegalStateException("Unsupported top-level node: " + node); throw new IllegalStateException("Unsupported top-level node: " + node);
} }
} }
return irProgram; return irProgram;
} }
// ===================== 预扫描注册结构体字段布局 =====================
/**
* 遍历所有模块与结构体按字段声明顺序构建字段名 -> 槽位索引的映射
* 并注册到 {@link IRBuilderScope#registerStructLayout(String, Map)}
* 这样在构建任意函数/方法时成员访问 this.name就能查询到正确的字段索引
*
* @param roots AST 顶层节点列表包含模块/结构体信息
*/
private void preloadStructLayouts(List<Node> roots) {
for (Node n : roots) {
// 只处理模块节点
if (!(n instanceof ModuleNode mod)) continue;
if (mod.structs() == null) continue;
// 遍历每个 struct收集字段布局
for (StructNode s : mod.structs()) {
List<DeclarationNode> fields = s.fields();
if (fields == null) continue;
Map<String, Integer> layout = new LinkedHashMap<>();
int idx = 0;
for (DeclarationNode d : fields) {
// 字段声明顺序决定槽位顺序如0,1,2,...
layout.put(d.getName(), idx++);
}
// 注册字段布局结构体名通常为简单名 Person类型系统如支持 Module.Struct IRBuilderScope 里兼容处理
IRBuilderScope.registerStructLayout(s.name(), layout);
}
}
}
// ===================== Struct 降级方法/构造 普通函数 =====================
/**
* 将一个 Struct 的构造函数init和方法methods降级为普通 Function并注册进 IRProgram
* <ul>
* <li>构造函数StructName.__init__(this:StructName, ...)</li>
* <li>方法StructName.method(this:StructName, ...)</li>
* </ul>
* <p>降级规则</p>
* <ol>
* <li>构造/方法函数名前缀加上结构体名便于唯一定位</li>
* <li>参数列表最前添加隐式 this:StructName 参数</li>
* </ol>
*
* @param structNode 当前结构体节点
* @param out 注册到的 IRProgram
*/
private void lowerAndRegisterStruct(StructNode structNode, IRProgram out) {
String structName = structNode.name();
// 1) 降级处理构造函数如有
if (structNode.init() != null) {
FunctionNode loweredInit = lowerStructCallable(
structNode.init(),
structName + ".__init__",
structName
);
out.add(buildFunction(loweredInit));
}
// 2) 降级处理所有普通方法
if (structNode.methods() != null) {
for (FunctionNode m : structNode.methods()) {
FunctionNode loweredMethod = lowerStructCallable(
m,
structName + "." + m.name(),
structName
);
out.add(buildFunction(loweredMethod));
}
}
}
/**
* 生成一个带隐式 this:StructName 参数的函数节点副本并重命名为 loweredName
*
* @param original 原始 FunctionNode
* @param loweredName 降级后的新函数名 StructName.method
* @param structName 结构体名用于 this 参数类型
* @return 重新包装后的函数节点
*/
private FunctionNode lowerStructCallable(FunctionNode original, String loweredName, String structName) {
// 在参数列表首位插入隐式 this 参数类型为结构体名
List<ParameterNode> newParams = new ArrayList<>(original.parameters().size() + 1);
newParams.add(new ParameterNode("this", structName, original.context()));
newParams.addAll(original.parameters());
return new FunctionNode(
loweredName,
newParams,
original.returnType(),
original.body(),
original.context()
);
}
// ===================== 全局常量收集 ===================== // ===================== 全局常量收集 =====================
/** /**
* 扫描所有模块节点将其中声明的 const 全局变量compile-time 常量 * 扫描所有模块节点将其中声明的 const 全局变量即编译期常量
* "模块名.常量名" 形式注册到全局常量表 * "模块名.常量名" 形式注册到全局常量表
* 支持跨模块常量折叠用于后续 IR 生成过程中的常量折叠优化 * <p>
* 支持跨模块常量折叠便于 IR 生成时做常量传播与优化
* </p>
* *
* @param roots AST 顶层节点列表 * @param roots AST 顶层节点列表
*/ */
@ -74,7 +206,7 @@ public final class IRProgramBuilder {
String moduleName = mod.name(); String moduleName = mod.name();
if (mod.globals() == null) continue; if (mod.globals() == null) continue;
for (DeclarationNode decl : mod.globals()) { for (DeclarationNode decl : mod.globals()) {
// 只处理 compile-time const 常量并要求有初始值 // 只处理带初始值的 const 常量编译期常量忽略 run-time/初始值
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue; if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
ExpressionNode init = decl.getInitializer().get(); ExpressionNode init = decl.getInitializer().get();
Object value = evalLiteral(init); Object value = evalLiteral(init);
@ -88,24 +220,28 @@ public final class IRProgramBuilder {
/** /**
* 字面量提取与类型折叠工具 * 字面量提取与类型折叠工具
* <p>
* 用于将表达式节点还原为 Java 原生类型intlongdoubleString等仅支持直接字面量 * 用于将表达式节点还原为 Java 原生类型intlongdoubleString等仅支持直接字面量
* 不支持复杂表达式非常量等情况无法静态折叠则返回 null
* *
* @param expr 要计算的表达式节点 * @param expr 要计算的表达式节点要求是字面量
* @return 提取到的原生常量值不支持的情况返回 null * @return 提取到的原生常量值若不支持则返回 null
*/ */
private Object evalLiteral(ExpressionNode expr) { private Object evalLiteral(ExpressionNode expr) {
return switch (expr) { return switch (expr) {
case NumberLiteralNode num -> { case NumberLiteralNode num -> {
// 数字字面量支持下划线类型后缀 123_456L
String raw = num.value(); String raw = num.value();
String s = raw.replace("_", ""); String s = raw.replace("_", "");
char last = Character.toLowerCase(s.charAt(s.length() - 1)); char last = Character.toLowerCase(s.charAt(s.length() - 1));
// 处理类型后缀 123l/123d/123f
String core = switch (last) { String core = switch (last) {
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1); case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
default -> s; default -> s;
}; };
try { try {
// 支持浮点数科学计数法
if (core.contains(".") || core.contains("e") || core.contains("E")) { if (core.contains(".") || core.contains("e") || core.contains("E")) {
// 浮点数处理
yield Double.parseDouble(core); yield Double.parseDouble(core);
} }
long lv = Long.parseLong(core); long lv = Long.parseLong(core);
@ -119,9 +255,9 @@ public final class IRProgramBuilder {
yield null; yield null;
} }
} }
case StringLiteralNode str -> str.value(); case StringLiteralNode str -> str.value(); // 字符串字面量直接返回
case BoolLiteralNode b -> b.getValue() ? 1 : 0; case BoolLiteralNode b -> b.getValue() ? 1 : 0; // 布尔常量转为 1/0
default -> null; default -> null; // 其他情况不支持常量折叠
}; };
} }
@ -129,21 +265,23 @@ public final class IRProgramBuilder {
/** /**
* 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一 * 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一
* 如果模块有全局声明则这些声明会被插入到函数体前部**会过滤掉与参数同名的全局声明** * <p>
* 如果模块有全局声明则这些声明会被插入到函数体前部<b>会过滤掉与参数同名的全局声明</b>防止变量遮蔽
* </p>
* *
* @param moduleNode 所属模块节点 * @param moduleNode 所属模块节点
* @param functionNode 待构建的函数节点 * @param functionNode 待构建的函数节点
* @return 包含全局声明的 IRFunction * @return 包含全局声明的 IRFunction
*/ */
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) { private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
// 拼接模块名和函数名生成全限定名 // 1. 拼接模块名和函数名生成全限定名
String qualifiedName = moduleNode.name() + "." + functionNode.name(); String qualifiedName = moduleNode.name() + "." + functionNode.name();
// 2. 若无全局声明直接重命名构建
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) { if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
// 无全局声明直接重命名构建
return buildFunction(renameFunction(functionNode, qualifiedName)); return buildFunction(renameFunction(functionNode, qualifiedName));
} }
// ------- 过滤与参数重名的全局声明 ------- // 3. 过滤掉与参数重名的全局声明优先参数作用域避免变量遮蔽
Set<String> paramNames = new HashSet<>(); Set<String> paramNames = new HashSet<>();
for (ParameterNode p : functionNode.parameters()) { for (ParameterNode p : functionNode.parameters()) {
paramNames.add(p.name()); paramNames.add(p.name());
@ -156,12 +294,12 @@ public final class IRProgramBuilder {
} }
} }
// 4. 若无可插入的全局声明直接重命名构建
if (filteredGlobals.isEmpty()) { if (filteredGlobals.isEmpty()) {
// 过滤后已无可插入的全局声明
return buildFunction(renameFunction(functionNode, qualifiedName)); return buildFunction(renameFunction(functionNode, qualifiedName));
} }
// 合并全局声明与函数体前插全局声明 // 5. 合并全局声明与函数体前插全局声明
List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size()); List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
newBody.addAll(filteredGlobals); newBody.addAll(filteredGlobals);
newBody.addAll(functionNode.body()); newBody.addAll(functionNode.body());
@ -197,6 +335,8 @@ public final class IRProgramBuilder {
* *
* @param functionNode 待构建的 FunctionNode * @param functionNode 待构建的 FunctionNode
* @return 构建后的 IRFunction * @return 构建后的 IRFunction
*
* <p>本方法仅作中转直接委托给 FunctionBuilder</p>
*/ */
private IRFunction buildFunction(FunctionNode functionNode) { private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode); return new FunctionBuilder().build(functionNode);