From 56301944fa25122e788ea1273b31aaee2f91ddc8 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 16 May 2025 16:58:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analyzers/statement/IfAnalyzer.java | 42 ++++++++++++++-- .../analyzers/statement/LoopAnalyzer.java | 34 ++++++++++--- .../semantic/core/SemanticAnalyzerRunner.java | 43 ++++++++--------- .../semantic/error/SemanticError.java | 48 +++++++------------ 4 files changed, 102 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java index fc96b6e..3483702 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IfAnalyzer.java @@ -11,10 +11,31 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 改进版 {@code IfAnalyzer},为 then/else 分支提供独立块级作用域。 + * {@code IfAnalyzer} 用于分析 if 语句的语义正确性。 + *

+ * 主要职责如下: + *

+ *

+ * 该分析器提升了语言的健壮性与可维护性,是支持 SCompiler 块级作用域及全局错误收集能力的关键一环。 */ public class IfAnalyzer implements StatementAnalyzer { + /** + * 分析 if 语句的语义合法性,包括条件表达式类型、分支作用域及分支语句检查。 + * + * @param ctx 语义分析上下文(记录全局符号表和错误) + * @param mi 当前模块信息 + * @param fn 当前所在函数 + * @param locals 当前作用域符号表 + * @param ifn if 语句 AST 节点 + */ @Override public void analyze(Context ctx, ModuleInfo mi, @@ -22,30 +43,41 @@ public class IfAnalyzer implements StatementAnalyzer { SymbolTable locals, IfNode ifn) { - // 1. 条件类型检查 + // 1. 检查 if 条件表达式类型 + // 获取对应条件表达式的表达式分析器 var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ifn.condition()); + // 对条件表达式执行类型分析 Type condType = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition()); + // 判断条件类型是否为 int(SCompiler 约定 int 表示真假),否则报错 if (condType != BuiltinType.INT) { ctx.getErrors().add(new SemanticError(ifn, "if 条件必须为 int 类型(表示真假)")); } - // 2. then 分支 + // 2. 分析 then 分支 + // 创建 then 分支的块级作用域(以当前 locals 为父作用域) SymbolTable thenScope = new SymbolTable(locals); + // 遍历 then 分支下的每一条语句 for (var stmt : ifn.thenBranch()) { + // 获取对应语句类型的分析器 var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { + // 对当前语句执行语义分析(作用域为 thenScope) stAnalyzer.analyze(ctx, mi, fn, thenScope, stmt); } else { + // 若找不到对应的分析器,记录错误 ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); } } - // 3. else 分支(可选) + // 3. 分析 else 分支(可选) if (!ifn.elseBranch().isEmpty()) { + // 创建 else 分支的块级作用域(同样以 locals 为父作用域) SymbolTable elseScope = new SymbolTable(locals); + // 遍历 else 分支下的每一条语句 for (var stmt : ifn.elseBranch()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { + // 对当前语句执行语义分析(作用域为 elseScope) stAnalyzer.analyze(ctx, mi, fn, elseScope, stmt); } else { ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); @@ -53,4 +85,4 @@ public class IfAnalyzer implements StatementAnalyzer { } } } -} \ No newline at end of file +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java index ad5b71a..7b67005 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/LoopAnalyzer.java @@ -11,10 +11,28 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 改进版 {@code LoopAnalyzer},为循环结构提供块级作用域支持。 + * {@code LoopAnalyzer} 用于分析 for/while 等循环结构的语义正确性。 + *

+ * 主要职责如下: + *

+ * 该分析器实现了 SCompiler 语言的块级作用域循环与类型健壮性,是健全语义分析的基础部分。 */ public class LoopAnalyzer implements StatementAnalyzer { + /** + * 分析循环结构(如 for、while)的语义合法性。 + * + * @param ctx 语义分析上下文(错误收集等) + * @param mi 当前模块信息 + * @param fn 当前所在函数 + * @param locals 外部传入的符号表(本地作用域) + * @param ln 当前循环节点 + */ @Override public void analyze(Context ctx, ModuleInfo mi, @@ -22,34 +40,38 @@ public class LoopAnalyzer implements StatementAnalyzer { SymbolTable locals, LoopNode ln) { - // 1. 整个循环的块级作用域 + // 1. 创建整个循环结构的块级作用域 + // 新建 loopScope,以支持循环内部变量声明与外部隔离 SymbolTable loopScope = new SymbolTable(locals); - // 2. 初始化语句 + // 2. 分析初始化语句(如 for(i=0)),使用 loopScope 作为作用域 var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer()); if (initAnalyzer != null) { initAnalyzer.analyze(ctx, mi, fn, loopScope, ln.initializer()); } - // 3. 条件表达式 + // 3. 分析条件表达式(如 for(...; cond; ...) 或 while(cond)) var condAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ln.condition()); Type condType = condAnalyzer.analyze(ctx, mi, fn, loopScope, ln.condition()); + // 条件类型必须为 int(即 bool),否则记录错误 if (condType != BuiltinType.INT) { ctx.getErrors().add(new SemanticError(ln, "loop 条件必须为 int 类型(表示真假)")); } - // 4. 更新语句 + // 4. 分析更新语句(如 for(...; ...; update)) var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update()); if (updateAnalyzer != null) { updateAnalyzer.analyze(ctx, mi, fn, loopScope, ln.update()); } - // 5. 循环体 + // 5. 分析循环体内的每一条语句 for (var stmt : ln.body()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { + // 递归分析循环体语句,作用域同样为 loopScope stAnalyzer.analyze(ctx, mi, fn, loopScope, stmt); } else { + // 不支持的语句类型,记录错误 ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java index db92c2e..0998114 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzerRunner.java @@ -9,46 +9,41 @@ import java.util.List; import java.util.stream.Collectors; /** - * {@code SemanticAnalyzerRunner} 是语义分析的统一入口。 + * {@code SemanticAnalyzerRunner} 是语义分析阶段的统一入口与调度器。 *

- * 负责从原始 AST 中提取模块节点、调用语义分析主流程、 - * 并在出现语义错误时统一报告并终止编译流程。 - *

- * 使用方式: - *

{@code
- *   SemanticAnalyzerRunner.runSemanticAnalysis(ast, true);
- * }
- *

- * 功能概述: + * 功能职责: *

    - *
  • 筛选出所有 {@link ModuleNode} 节点(模块级入口);
  • - *
  • 调用 {@link SemanticAnalyzer} 执行完整语义分析流程;
  • - *
  • 将收集到的 {@link SemanticError} 列表交由报告器处理;
  • - *
  • 若存在语义错误,调用 {@link SemanticAnalysisReporter#reportAndExitIfNecessary(List)} 自动中止流程。
  • + *
  • 从原始 AST 列表中过滤并收集所有 {@link ModuleNode} 节点,作为模块分析的起点;
  • + *
  • 调用 {@link SemanticAnalyzer} 对所有模块节点执行完整语义分析流程;
  • + *
  • 汇总并报告所有 {@link SemanticError};如有语义错误,自动中止编译流程,防止后续崩溃。
  • *
+ *

+ * 推荐使用方式: + *

+ *     SemanticAnalyzerRunner.runSemanticAnalysis(ast, true);
+ * 
+ *

+ * 该类是实现 SCompiler “所有错误一次性输出,且错误即终止” 语义分析约束的关键。 */ public class SemanticAnalyzerRunner { /** - * 对输入的语法树执行语义分析。 - *

- * 语法树应为编译前阶段(如解析器)产出的 AST 列表, - * 本方法会自动筛选其中的 {@link ModuleNode} 节点,并调用语义分析器执行完整分析。 + * 对输入的语法树执行语义分析并自动报告。 * * @param ast 根节点列表(应包含一个或多个 {@link ModuleNode}) * @param verbose 是否启用详细日志(将控制内部 {@link Context#log(String)} 的行为) */ public static void runSemanticAnalysis(List ast, boolean verbose) { - // 1. 提取模块节点 + // 1. 从 AST 列表中过滤所有模块节点 ModuleNode List modules = ast.stream() - .filter(ModuleNode.class::isInstance) - .map(ModuleNode.class::cast) - .collect(Collectors.toList()); + .filter(ModuleNode.class::isInstance) // 保留类型为 ModuleNode 的节点 + .map(ModuleNode.class::cast) // 转换为 ModuleNode + .collect(Collectors.toList()); // 收集为 List - // 2. 执行语义分析 + // 2. 调用语义分析器,对所有模块进行全流程语义分析,返回错误列表 List errors = new SemanticAnalyzer(verbose).analyze(modules); - // 3. 报告并在必要时终止 + // 3. 统一报告全部语义错误;如有错误则自动终止编译(System.exit) SemanticAnalysisReporter.reportAndExitIfNecessary(errors); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java b/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java index 650f762..121d0eb 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java @@ -1,44 +1,32 @@ package org.jcnc.snow.compiler.semantic.error; + import org.jcnc.snow.compiler.parser.ast.base.Node; /** - * {@code SemanticError} 表示语义分析过程中发现的错误信息。 - *

- * 语义错误是编译器无法接受的程序逻辑问题,例如: + * 表示一次语义错误。
*

    - *
  • 使用了未声明的变量;
  • - *
  • 类型不兼容的赋值;
  • - *
  • 函数返回类型与实际返回值不一致;
  • - *
  • 调用了不存在的函数或模块。
  • + *
  • 记录对应 {@link Node} 及出错信息;
  • + *
  • 重写 {@link #toString()},以 行 X, 列 Y: message 格式输出,
  • + *
  • 避免默认的 Node@hash 形式。
  • *
- *

- * 访问器方法({@code node()} / {@code message()})、相等性判断等功能。 - * - *

主要字段说明: - *

    - *
  • {@code node}:发生语义错误的 AST 节点,可用于定位源代码位置;
  • - *
  • {@code message}:具体的错误描述,适合用于报错提示、日志输出、IDE 集成等。
  • - *
- * - * @param node 发生错误的抽象语法树节点 {@link Node} - * @param message 错误描述信息 */ public record SemanticError(Node node, String message) { - /** - * 返回格式化后的语义错误信息字符串。 - *

- * 输出格式: - *

-     * Semantic error at [节点]: [错误信息]
-     * 
- * 适用于命令行编译器输出、调试日志或错误收集器展示。 - * - * @return 格式化的错误信息字符串 - */ @Override public String toString() { - return "Semantic error at " + node + ": " + message; + // Node 假定提供 line() / column() 方法;如无则返回 -1 + int line = -1; + int col = -1; + if (node != null) { + try { + line = (int) node.getClass().getMethod("line").invoke(node); + col = (int) node.getClass().getMethod("column").invoke(node); + } catch (ReflectiveOperationException ignored) { + // 若 Node 未提供 line/column 方法则保持 -1 + } + } + String pos = (line >= 0 && col >= 0) ? ("行 " + line + ", 列 " + col) : "未知位置"; + return pos + ": " + message; } }