refactor: 重构函数语义检查器并增强错误提示
- 重构 FunctionChecker 类,优化函数体语义检查流程- 增加全局符号表构建和局部变量声明的详细注释 - 改进错误提示信息,提高代码可读性和可维护性
This commit is contained in:
parent
b56824d935
commit
cdfcbf0d6f
@ -17,35 +17,55 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对所有模块的函数体进行两遍扫描的语义检查:
|
||||
* 1) 先为每个模块建立全局符号表(globals),注册模块级变量/常量;
|
||||
* 2) 再在全局表就绪后,依次分析各函数体。
|
||||
* {@code FunctionChecker} 负责对所有模块的函数体进行两遍扫描式的语义检查。
|
||||
* <p>
|
||||
* 检查流程为:
|
||||
* <ol>
|
||||
* <li>第一遍:为每个模块构建全局符号表,注册所有模块级变量与常量声明(并检查重复/未知类型)。</li>
|
||||
* <li>第二遍:在所有全局符号表准备好后,依次分析各模块下所有函数的参数和函数体语句。</li>
|
||||
* </ol>
|
||||
* 检查要点:
|
||||
* <ul>
|
||||
* <li>类型未知或变量/常量重复声明时,均收集为语义错误。</li>
|
||||
* <li>所有函数参数注册为局部变量。</li>
|
||||
* <li>所有函数体语句分派到对应 StatementAnalyzer 实例做分析。</li>
|
||||
* <li>非 void 返回类型的函数,必须有至少一条 return。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public record FunctionChecker(Context ctx) {
|
||||
|
||||
/**
|
||||
* 对传入的所有模块做函数体的两遍扫描式语义检查。
|
||||
*
|
||||
* @param mods 所有待分析的模块 AST 节点集合
|
||||
*/
|
||||
public void check(Iterable<ModuleNode> mods) {
|
||||
List<ModuleNode> moduleList = new ArrayList<>();
|
||||
|
||||
// ---------- 第一遍:构建并注册各模块全局符号表 ----------
|
||||
for (ModuleNode mod : mods) {
|
||||
ctx.setCurrentModule(mod.name());
|
||||
ctx.setCurrentModule(mod.name()); // 标记当前模块
|
||||
moduleList.add(mod);
|
||||
|
||||
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||
SymbolTable globalScope = new SymbolTable(null);
|
||||
SymbolTable globalScope = new SymbolTable(null); // 模块级全局作用域
|
||||
|
||||
// 处理所有全局变量/常量声明
|
||||
for (DeclarationNode g : mod.globals()) {
|
||||
Type t = ctx.parseType(g.getType());
|
||||
Type t = ctx.parseType(g.getType()); // 解析声明类型
|
||||
if (t == null) {
|
||||
// 类型未知,记录错误,兜底为 int 类型,避免后续 NullPointer
|
||||
ctx.errors().add(new SemanticError(g, "未知类型: " + g.getType()));
|
||||
t = BuiltinType.INT; // 兜底,避免后续 NPE
|
||||
t = BuiltinType.INT;
|
||||
}
|
||||
SymbolKind kind = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
|
||||
String dupType = g.isConst() ? "常量" : "变量";
|
||||
// 注册符号表(防止重名)
|
||||
if (!globalScope.define(new Symbol(g.getName(), t, kind))) {
|
||||
ctx.errors().add(new SemanticError(g, dupType + "重复声明: " + g.getName()));
|
||||
}
|
||||
}
|
||||
// 将全局符号表挂载到模块信息对象
|
||||
mi.setGlobals(globalScope);
|
||||
}
|
||||
|
||||
@ -53,13 +73,13 @@ public record FunctionChecker(Context ctx) {
|
||||
for (ModuleNode mod : moduleList) {
|
||||
ctx.setCurrentModule(mod.name());
|
||||
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||
SymbolTable globalScope = mi.getGlobals();
|
||||
SymbolTable globalScope = mi.getGlobals(); // 全局作用域
|
||||
|
||||
for (FunctionNode fn : mod.functions()) {
|
||||
// 构建函数局部作用域:父作用域为全局
|
||||
// 构建函数的局部作用域(父作用域为模块全局)
|
||||
SymbolTable locals = new SymbolTable(globalScope);
|
||||
|
||||
// 注册函数参数为局部变量
|
||||
// 注册所有函数参数到局部作用域,类型未知时兜底为 int
|
||||
fn.parameters().forEach(p -> {
|
||||
Type t = ctx.parseType(p.type());
|
||||
if (t == null) {
|
||||
@ -69,19 +89,20 @@ public record FunctionChecker(Context ctx) {
|
||||
locals.define(new Symbol(p.name(), t, SymbolKind.VARIABLE));
|
||||
});
|
||||
|
||||
// 分析函数体语句 —— 关键修复:传“实例”而不是 Class
|
||||
// 分析函数体所有语句
|
||||
for (StatementNode stmt : fn.body()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
StatementAnalyzer<StatementNode> analyzer =
|
||||
(StatementAnalyzer<StatementNode>) ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||
ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||
if (analyzer != null) {
|
||||
// 传递语义分析器“实例”,避免类型擦除/反射调用
|
||||
analyzer.analyze(ctx, mi, fn, locals, stmt);
|
||||
} else {
|
||||
// 语句类型未支持,收集错误
|
||||
ctx.errors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
|
||||
}
|
||||
}
|
||||
|
||||
// 非 void 的函数必须至少包含一条 return
|
||||
// 非 void 函数,要求必须含至少一条 return 语句
|
||||
Type ret = ctx.parseType(fn.returnType());
|
||||
if (ret != null && ret != BuiltinType.VOID) {
|
||||
boolean hasReturn = fn.body().stream().anyMatch(s -> s instanceof ReturnNode);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user