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 0552426..fc96b6e 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,28 +11,10 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * {@code IfAnalyzer} 是用于分析 {@link IfNode} 条件语句的语义分析器。 - *

- * 它负责验证 if 条件表达式的类型合法性,并递归分析 then 分支和 else 分支中的所有子语句。 - * 分析规则如下: - *

+ * 改进版 {@code IfAnalyzer},为 then/else 分支提供独立块级作用域。 */ public class IfAnalyzer implements StatementAnalyzer { - /** - * 执行 if 语句的语义分析,包括条件类型检查和分支分析。 - * - * @param ctx 当前语义分析上下文,提供注册表、错误收集、日志等服务。 - * @param mi 当前模块信息(本实现未直接使用)。 - * @param fn 当前所在函数节点(本实现未直接使用)。 - * @param locals 当前作用域的符号表,提供变量查找支持。 - * @param ifn 要分析的 {@link IfNode} 语法节点,包含条件表达式及两个分支语句块。 - */ @Override public void analyze(Context ctx, ModuleInfo mi, @@ -40,32 +22,35 @@ public class IfAnalyzer implements StatementAnalyzer { SymbolTable locals, IfNode ifn) { - // 1. 分析并校验条件表达式类型 - ctx.log("检查 if 条件"); + // 1. 条件类型检查 var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ifn.condition()); - Type cond = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition()); - if (cond != BuiltinType.INT) { - ctx.getErrors().add(new SemanticError( - ifn, - "if 条件必须为 int 类型(表示真假)" - )); - ctx.log("错误: if 条件类型不为 int"); + Type condType = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition()); + if (condType != BuiltinType.INT) { + ctx.getErrors().add(new SemanticError(ifn, "if 条件必须为 int 类型(表示真假)")); } - // 2. 递归分析 then 分支语句 + // 2. then 分支 + SymbolTable thenScope = new SymbolTable(locals); for (var stmt : ifn.thenBranch()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { - stAnalyzer.analyze(ctx, mi, fn, locals, stmt); + stAnalyzer.analyze(ctx, mi, fn, thenScope, stmt); + } else { + ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); } } - // 3. 递归分析 else 分支语句(若存在) - for (var stmt : ifn.elseBranch()) { - var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); - if (stAnalyzer != null) { - stAnalyzer.analyze(ctx, mi, fn, locals, stmt); + // 3. else 分支(可选) + if (!ifn.elseBranch().isEmpty()) { + SymbolTable elseScope = new SymbolTable(locals); + for (var stmt : ifn.elseBranch()) { + var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); + if (stAnalyzer != null) { + stAnalyzer.analyze(ctx, mi, fn, elseScope, stmt); + } else { + ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); + } } } } -} +} \ 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 6599986..ad5b71a 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,30 +11,10 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * {@code LoopAnalyzer} 是用于分析 {@link LoopNode} 循环结构的语义分析器。 - *

- * 支持的循环结构包括典型的 C 风格 for-loop:由初始化语句、条件表达式、更新语句和循环体组成。 - *

- * 分析流程如下: - *

    - *
  1. 分析初始化语句 {@code initializer};
  2. - *
  3. 分析并验证条件表达式 {@code condition} 类型是否为 {@link BuiltinType#INT};
  4. - *
  5. 分析更新语句 {@code update};
  6. - *
  7. 递归分析循环体 {@code body} 中的每个子语句;
  8. - *
  9. 所有阶段若发现语义错误均记录至 {@link Context#getErrors()},并写入日志。
  10. - *
+ * 改进版 {@code LoopAnalyzer},为循环结构提供块级作用域支持。 */ public class LoopAnalyzer implements StatementAnalyzer { - /** - * 执行循环结构的语义分析。 - * - * @param ctx 当前语义分析上下文,提供模块信息、错误记录、日志输出和分析器注册表等服务。 - * @param mi 当前模块信息(本方法中未直接使用,保留用于接口一致性)。 - * @param fn 当前函数节点(本方法中未直接使用,保留用于可能的上下文分析扩展)。 - * @param locals 当前作用域的符号表,记录变量类型及其可见性。 - * @param ln 待分析的 {@link LoopNode} 节点,包含初始化语句、条件表达式、更新语句和循环体。 - */ @Override public void analyze(Context ctx, ModuleInfo mi, @@ -42,35 +22,35 @@ public class LoopAnalyzer implements StatementAnalyzer { SymbolTable locals, LoopNode ln) { - // 1. 分析初始化语句 - ctx.log("检查 loop 循环"); + // 1. 整个循环的块级作用域 + SymbolTable loopScope = new SymbolTable(locals); + + // 2. 初始化语句 var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer()); if (initAnalyzer != null) { - initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer()); + initAnalyzer.analyze(ctx, mi, fn, loopScope, ln.initializer()); } - // 2. 分析条件表达式并检查类型 + // 3. 条件表达式 var condAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ln.condition()); - Type cond = condAnalyzer.analyze(ctx, mi, fn, locals, ln.condition()); - if (cond != BuiltinType.INT) { - ctx.getErrors().add(new SemanticError( - ln, - "loop 条件必须为 int 类型(表示真假)" - )); - ctx.log("错误: loop 条件类型不为 int"); + Type condType = condAnalyzer.analyze(ctx, mi, fn, loopScope, ln.condition()); + if (condType != BuiltinType.INT) { + ctx.getErrors().add(new SemanticError(ln, "loop 条件必须为 int 类型(表示真假)")); } - // 3. 分析更新语句 + // 4. 更新语句 var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update()); if (updateAnalyzer != null) { - updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update()); + updateAnalyzer.analyze(ctx, mi, fn, loopScope, ln.update()); } - // 4. 递归分析循环体中的每个语句 + // 5. 循环体 for (var stmt : ln.body()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { - stAnalyzer.analyze(ctx, mi, fn, locals, stmt); + 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/utils/SemanticAnalysisReporter.java b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java index be862fe..af44dc1 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java @@ -5,51 +5,47 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError; import java.util.List; /** - * {@code SemanticAnalysisReporter} 是语义分析阶段的结果报告工具类。 - *

- * 用于统一处理语义分析阶段产生的错误信息输出与流程终止逻辑。 - * 通常作为语义分析器的收尾阶段调用。 - * - *

主要职责包括: + * {@code SemanticAnalysisReporter} 用于在语义分析结束后汇总并打印所有收集到的 + * {@link SemanticError}。为了同时满足“完整错误收集”与“按需快速失败”两种使用场景, + * 现在提供两个公共 API: *

    - *
  • 打印所有收集到的 {@link SemanticError};
  • - *
  • 若存在错误,使用 {@code System.exit(1)} 终止编译流程;
  • - *
  • 若无错误,输出分析通过提示。
  • + *
  • {@link #report(List)} ‑ 仅打印,不终止;
  • + *
  • {@link #reportAndExitIfNecessary(List)} ‑ 若存在错误则 打印并退出
  • *
- * - *

该类为工具类,禁止实例化,方法均为静态调用。 + * 调用方可根据需求选择合适方法。 */ public final class SemanticAnalysisReporter { - // 禁止实例化 - private SemanticAnalysisReporter() { - - } + private SemanticAnalysisReporter() { } /** - * 打印语义分析结果并在必要时终止程序。 - *

- * 如果错误列表非空: - *

    - *
  • 逐条打印错误信息(含位置与描述);
  • - *
  • 使用 {@code System.exit(1)} 退出,表示语义分析失败。
  • - *
- * 如果错误列表为空: - *
    - *
  • 打印“语义分析通过”提示。
  • - *
+ * 打印语义分析结果;不会退出进程。 * - * @param errors 语义分析阶段收集到的错误列表(允许为 null) + * @param errors 语义分析阶段收集到的错误列表(允许为 {@code null}) */ - public static void reportAndExitIfNecessary(List errors) { - if (errors != null && !errors.isEmpty()) { - System.err.println("语义分析发现错误:"); - for (SemanticError error : errors) { - System.err.println(" " + error); - } - System.exit(1); // 非正常退出,阻止后续编译流程 + public static void report(List errors) { + if (hasErrors(errors)) { + System.err.println("语义分析发现 " + errors.size() + " 个错误:"); + errors.forEach(err -> System.err.println(" " + err)); } else { System.out.println("语义分析通过,没有发现错误。"); } } -} + + /** + * 打印语义分析结果;如有错误立即以状态码 1 结束进程。 + * 适用于 CLI 工具需要立即中止后续编译阶段的场景。 + * + * @param errors 语义分析阶段收集到的错误列表(允许为 {@code null}) + */ + public static void reportAndExitIfNecessary(List errors) { + report(errors); + if (hasErrors(errors)) { + System.exit(1); + } + } + + private static boolean hasErrors(List errors) { + return errors != null && !errors.isEmpty(); + } +} \ No newline at end of file diff --git a/test b/test index 1f3471f..7d0eae5 100644 --- a/test +++ b/test @@ -5,20 +5,31 @@ module: CommonTasks return_type:int body: + declare x : int + declare x : string + y = 5 + x = "hello" - return CommonTasks.test(3,1) + if x then + declare z : float + declare z : int + else + z = 3.14 + end if + + return CommonTasks.test(3, "a") end body end function - function:test + function: test parameter: - declare num1:int - declare num2:int + declare num1 : int + declare num2 : int return_type:int body: declare result : int - result = num1 + num2 + result = num1 + "hello" return result end body end function