diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java index 898ee40..46aad81 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java @@ -10,32 +10,38 @@ import java.util.HashMap; import java.util.Map; /** - * Analyzer 注册表:负责维护并按 AST 节点类型分发对应的 StatementAnalyzer 或 ExpressionAnalyzer。 + * {@code AnalyzerRegistry} 是语义分析器的注册与分发中心。 *

- * 支持: + * 它负责根据 AST 节点的类型,查找并返回相应的 {@link StatementAnalyzer} 或 {@link ExpressionAnalyzer} 实例。 + * 同时支持注册自定义分析器,并在未找到对应表达式分析器时提供默认兜底处理器。 + *

+ * 主要职责: *

*/ public class AnalyzerRegistry { - /** 语句节点类型 -> 分析器 映射 */ + /** Statement 节点类型 → 对应语义分析器映射表 */ private final Map, StatementAnalyzer> stmtAnalyzers = new HashMap<>(); - /** 表达式节点类型 -> 分析器 映射 */ + + /** Expression 节点类型 → 对应语义分析器映射表 */ private final Map, ExpressionAnalyzer> exprAnalyzers = new HashMap<>(); - /** 默认兜底表达式分析器,处理所有未显式注册的 ExpressionNode 子类型 */ + + /** 默认兜底表达式分析器,用于处理未注册的表达式类型 */ private final ExpressionAnalyzer defaultUnsupported = new UnsupportedExpressionAnalyzer<>(); - // --- 注册方法 ----------------------------------------------------------- + // ========================= 注册方法 ========================= /** - * 注册一个 StatementAnalyzer,用于处理指定类型的语句节点。 + * 注册一个 {@link StatementAnalyzer} 实例,用于处理指定类型的语句节点。 * - * @param cls 语句节点的 Class 对象 - * @param analyzer 针对该类型节点的分析器实例 - * @param 具体的 StatementNode 子类型 + * @param cls 要注册的语句节点类型(Class 对象) + * @param analyzer 与该类型匹配的分析器实例 + * @param {@link StatementNode} 的具体子类 */ public void registerStatementAnalyzer( Class cls, @@ -45,11 +51,11 @@ public class AnalyzerRegistry { } /** - * 注册一个 ExpressionAnalyzer,用于处理指定类型的表达式节点。 + * 注册一个 {@link ExpressionAnalyzer} 实例,用于处理指定类型的表达式节点。 * - * @param cls 表达式节点的 Class 对象 - * @param analyzer 针对该类型节点的分析器实例 - * @param 具体的 ExpressionNode 子类型 + * @param cls 要注册的表达式节点类型(Class 对象) + * @param analyzer 与该类型匹配的分析器实例 + * @param {@link ExpressionNode} 的具体子类 */ public void registerExpressionAnalyzer( Class cls, @@ -58,16 +64,16 @@ public class AnalyzerRegistry { exprAnalyzers.put(cls, analyzer); } - // --- 获取方法 ----------------------------------------------------------- + // ========================= 获取方法 ========================= /** - * 根据给定的语句节点实例,返回对应的 StatementAnalyzer。 + * 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}。 *

- * 若未注册任何分析器,则返回 null。 + * 若节点类型未注册,返回 {@code null}。 * - * @param stmt 要分析的 StatementNode 实例 - * @param 具体的 StatementNode 子类型 - * @return 对应的 StatementAnalyzer,或 null + * @param stmt 要分析的语句节点实例 + * @param 语句类型(推断自参数) + * @return 与该节点类型对应的分析器,若未注册则为 {@code null} */ @SuppressWarnings("unchecked") public StatementAnalyzer getStatementAnalyzer(S stmt) { @@ -75,13 +81,13 @@ public class AnalyzerRegistry { } /** - * 根据给定的表达式节点实例,返回对应的 ExpressionAnalyzer。 + * 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}。 *

- * 若未注册任何分析器,则返回 {@link #defaultUnsupported} 兜底。 + * 若节点类型未注册,返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}。 * - * @param expr 要分析的 ExpressionNode 实例 - * @param 具体的 ExpressionNode 子类型 - * @return 对应的 ExpressionAnalyzer(可能是默认兜底分析器) + * @param expr 要分析的表达式节点实例 + * @param 表达式类型(推断自参数) + * @return 与该节点类型对应的分析器,或默认兜底分析器 */ @SuppressWarnings("unchecked") public ExpressionAnalyzer getExpressionAnalyzer(E expr) { diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java index 138dcfe..d448b03 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java @@ -11,9 +11,31 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 二元表达式分析器:支持数值宽化转换及字符串拼接。 + * {@code BinaryExpressionAnalyzer} 是一个用于分析二元表达式的语义分析器。 + *

+ * 支持的特性包括: + *

    + *
  • 字符串拼接(当运算符为加号 "+" 且任一操作数为字符串类型时)
  • + *
  • 数值类型的自动宽化转换(如 int 与 float 运算将转换为 float)
  • + *
  • 基本的数值运算符(如 +, -, *, /, %)
  • + *
  • 关系运算符和比较运算符(如 <, <=, >, >=, ==, !=)
  • + *
+ * 对于不支持的运算符或不兼容的类型组合,将记录语义错误,并默认返回 {@code BuiltinType.INT} 以保持分析过程的连续性。 + * + * 实现类遵循 {@code ExpressionAnalyzer} 接口规范。 */ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer { + + /** + * 分析给定的二元表达式节点,返回其表达式类型。 + * + * @param ctx 当前语义分析上下文,用于访问日志记录、错误收集、注册表等服务。 + * @param mi 当前模块信息,包含模块级别的符号与类型定义。 + * @param fn 当前正在分析的函数节点。 + * @param locals 当前函数作用域内的符号表,用于变量查找。 + * @param bin 要分析的二元表达式节点。 + * @return 分析后推断出的表达式类型。 + */ @Override public Type analyze(Context ctx, ModuleInfo mi, @@ -22,40 +44,46 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer>===!=").contains(op)) { + // 情况 2:数值类型运算或比较 + if ("+-*/%".contains(op) || ("<<=>>===!=").contains(op)) { if (left.isNumeric() && right.isNumeric()) { - // 自动宽化到更宽的数值类型 + // 自动宽化到更宽的数值类型(如 int + float => float) Type wide = Type.widen(left, right); - if (wide == null) wide = BuiltinType.INT; // 容错降级 - // 比较运算返回 int + if (wide == null) wide = BuiltinType.INT; // 容错降级为 int + + // 若为比较运算符,统一返回 int 类型作为布尔值表示 if ("< <= > >= == !=".contains(op)) { return BuiltinType.INT; } + return wide; } } - // 未知或不支持的运算符/类型 + // 情况 3:不支持的类型组合,记录语义错误 ctx.getErrors().add(new SemanticError( - bin, - String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right) + bin, + String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right) )); ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right); + + // 错误情况下默认返回 int 类型,以保证语义分析不中断 return BuiltinType.INT; } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java index 603eb12..b82d78f 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java @@ -15,14 +15,29 @@ import java.util.ArrayList; import java.util.List; /** - * 函数调用表达式分析器:负责对 callee(arg1, arg2, ...) 形式的调用进行 - * 语义检查和类型推导,并支持: + * {@code CallExpressionAnalyzer} 是函数调用表达式的语义分析器。 + *

+ * 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式,执行如下操作: *

    - *
  • 数值到字符串的隐式转换(自动插入 to_string);
  • - *
  • 数值参数的宽化转换(如 int → double)。
  • + *
  • 识别调用目标(支持模块成员函数调用和当前模块函数调用);
  • + *
  • 根据被调用函数的参数签名检查实参数量和类型的兼容性;
  • + *
  • 支持数值参数的宽化转换(如 int → double);
  • + *
  • 支持数值到字符串的隐式转换(自动视为调用 {@code to_string});
  • + *
  • 在发生类型不匹配、未导入模块或函数未定义等情况下记录语义错误。
  • *
*/ public class CallExpressionAnalyzer implements ExpressionAnalyzer { + + /** + * 分析函数调用表达式并推断其类型。 + * + * @param ctx 当前语义分析上下文,提供日志、错误记录、模块访问等功能。 + * @param mi 当前模块信息,用于函数查找及模块依赖判断。 + * @param fn 当前分析的函数节点。 + * @param locals 局部符号表,用于变量查找。 + * @param call 待分析的函数调用表达式节点。 + * @return 表达式的返回类型。如果存在语义错误,默认返回 {@code BuiltinType.INT}。 + */ @Override public Type analyze(Context ctx, ModuleInfo mi, @@ -30,13 +45,15 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer args = new ArrayList<>(); for (ExpressionNode arg : call.arguments()) { args.add(ctx.getRegistry().getExpressionAnalyzer(arg) .analyze(ctx, mi, fn, locals, arg)); } - // 参数检查(数量 + 类型兼容 / 宽化 / 数值->字符串隐式转换) + // 参数数量检查 if (args.size() != ft.paramTypes().size()) { ctx.getErrors().add(new SemanticError(call, "参数数量不匹配: 期望 " + ft.paramTypes().size() @@ -82,19 +101,18 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer - * 如果未在符号表中找到对应的符号,将向错误列表中添加一条 - * {@link SemanticError},并返回 {@link BuiltinType#INT} 作为降级类型, - * 以避免后续分析因缺失类型而连锁报错。 + * 它的主要职责是在给定的局部作用域中查找标识符对应的符号定义,并返回其类型信息。 + * 如果标识符未在当前作用域内声明,则: + *
    + *
  • 向语义错误列表中添加一条 {@link SemanticError} 记录;
  • + *
  • 为保证分析过程的连续性,默认返回 {@link BuiltinType#INT} 类型作为降级处理。
  • + *
+ *

+ * 该分析器通常用于处理表达式中的变量引用或常量引用场景。 */ public class IdentifierAnalyzer implements ExpressionAnalyzer { /** - * 对给定的标识符节点进行语义分析。 + * 对标识符表达式进行语义分析,判断其是否在当前作用域中已声明,并返回其类型。 * - * @param ctx 全局上下文,包含模块信息、错误收集和分析器注册表等 - * @param mi 当前模块信息,用于跨模块引用时的额外检查(此处暂未使用) - * @param fn 当前函数节点,可用于更复杂的上下文校验(此处暂未使用) - * @param locals 当前作用域的符号表,包含已声明的变量及其类型 - * @param id 待分析的 {@link IdentifierNode},包含标识符名称 - * @return 如果符号已声明则返回其 {@link Symbol#type()};否则返回降级类型 {@link BuiltinType#INT} + * @param ctx 当前语义分析上下文对象,提供模块信息、错误收集、日志记录等服务。 + * @param mi 当前模块信息(此实现中未使用,但保留用于扩展)。 + * @param fn 当前分析的函数节点(此实现中未使用,但保留用于扩展)。 + * @param locals 当前函数或代码块的符号表,记录已声明的变量及其类型信息。 + * @param id 表达式中出现的 {@link IdentifierNode} 实例,表示待解析的标识符名称。 + * @return 若标识符已在符号表中声明,则返回对应的 {@link Type}; + * 否则返回 {@link BuiltinType#INT} 作为错误降级类型。 */ @Override public Type analyze(Context ctx, @@ -38,17 +43,17 @@ public class IdentifierAnalyzer implements ExpressionAnalyzer { SymbolTable locals, IdentifierNode id) { - // 查找当前作用域下的符号 + // 在当前作用域中查找符号(变量或常量) Symbol sym = locals.resolve(id.name()); if (sym == null) { - // 未声明:记录错误并降级 + // 未声明标识符:记录语义错误,返回降级类型 ctx.getErrors().add(new SemanticError(id, "未声明的标识符: " + id.name())); ctx.log("错误: 未声明的标识符 " + id.name()); return BuiltinType.INT; } - // 已声明:返回符号所持有的类型 + // 返回符号的类型信息 return sym.type(); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java index 148d6fa..9e01c89 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/NumberLiteralAnalyzer.java @@ -10,22 +10,28 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 数字字面量分析器:对 {@link NumberLiteralNode} 节点进行类型推导。 + * {@code NumberLiteralAnalyzer} 是用于处理数字字面量表达式(如整数常量)的语义分析器。 *

- * 在本语言中,所有数字字面量均被视为整型,因此直接返回 {@link BuiltinType#INT}。 - * 本分析器不会产生任何语义错误。 + * 在该语言的设计中,所有数字字面量(如 {@code 42}, {@code 0}, {@code -7})统一视为整型, + * 因此该分析器总是返回 {@link BuiltinType#INT} 类型。 + *

+ * 特点: + *

    + *
  • 不依赖符号表、函数上下文或模块信息,属于上下文无关表达式分析器。
  • + *
  • 不会引发任何语义错误或日志输出,语义稳定、处理简单。
  • + *
*/ public class NumberLiteralAnalyzer implements ExpressionAnalyzer { /** - * 对数字字面量节点进行语义分析。 + * 分析数字字面量表达式并返回其固定类型。 * - * @param ctx 全局上下文,包含模块信息、错误收集、日志输出和分析器注册表等 - * @param mi 当前模块信息(此分析器不涉及模块间调用,参数未使用) - * @param fn 当前函数节点(此分析器不依赖函数上下文,参数未使用) - * @param locals 当前作用域的符号表(数字字面量不依赖符号表,参数未使用) - * @param expr 待分析的 {@link NumberLiteralNode},表示一个数字字面量 - * @return 固定返回 {@link BuiltinType#INT} + * @param ctx 当前语义分析上下文(本分析器不使用该参数,但为接口统一性保留) + * @param mi 当前模块信息(未使用,因字面量无模块依赖) + * @param fn 当前所在的函数节点(未使用,因字面量无函数依赖) + * @param locals 当前作用域的符号表(未使用,因字面量不引用符号) + * @param expr 要分析的 {@link NumberLiteralNode} 节点 + * @return 始终返回 {@link BuiltinType#INT} 类型,表示整型字面量 */ @Override public Type analyze(Context ctx, diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java index 34d2793..518fa93 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/StringLiteralAnalyzer.java @@ -10,22 +10,29 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 字符串字面量分析器:对 {@link StringLiteralNode} 节点进行类型推导。 + * {@code StringLiteralAnalyzer} 是字符串字面量表达式的语义分析器。 *

- * 在本语言中,所有字符串字面量均被视为字符串类型,因此直接返回 {@link BuiltinType#STRING}。 - * 本分析器不会产生任何语义错误。 + * 负责分析源代码中的字符串字面量(例如 {@code "hello"}、{@code ""} 等), + * 并确定其类型。根据语言规范,所有字符串字面量默认视为 {@link BuiltinType#STRING} 类型。 + *

+ * 特点如下: + *

    + *
  • 不依赖符号表、函数上下文或模块信息,属于上下文无关表达式分析器;
  • + *
  • 恒定返回 {@code STRING} 类型;
  • + *
  • 不产生任何语义错误,具备语义稳定性。
  • + *
*/ public class StringLiteralAnalyzer implements ExpressionAnalyzer { /** - * 对字符串字面量节点进行语义分析。 + * 分析字符串字面量表达式并返回其类型。 * - * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等(此分析器不使用上下文) - * @param mi 当前模块信息(此分析器不涉及模块调用,参数未使用) - * @param fn 当前函数节点(此分析器不依赖函数上下文,参数未使用) - * @param locals 当前作用域的符号表(字符串字面量不依赖符号表,参数未使用) - * @param expr 待分析的 {@link StringLiteralNode},表示一个字符串字面量 - * @return 固定返回 {@link BuiltinType#STRING} + * @param ctx 当前语义分析上下文对象(本分析器不使用该参数,保留用于接口一致性) + * @param mi 当前模块信息(未使用,因字面量无模块依赖) + * @param fn 当前所在的函数节点(未使用,因字面量无函数依赖) + * @param locals 当前作用域的符号表(未使用,因字面量不引用符号) + * @param expr 要分析的 {@link StringLiteralNode} 节点 + * @return 始终返回 {@link BuiltinType#STRING} 类型,表示字符串字面量 */ @Override public Type analyze(Context ctx, diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java index 4dd427a..01e3332 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnsupportedExpressionAnalyzer.java @@ -11,29 +11,33 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 默认兜底表达式分析器:用于处理所有未显式注册的 {@link ExpressionNode} 子类型。 + * {@code UnsupportedExpressionAnalyzer} 是一个通用兜底表达式分析器, + * 用于处理所有未显式注册的 {@link ExpressionNode} 子类型。 *

- * 当遇到不支持或未实现的表达式节点时,本分析器会: - *

    - *
  1. 向 {@link Context#getErrors() 错误列表} 中添加一条 {@link SemanticError},
  2. - *
  3. 在日志中输出对应的错误信息,
  4. - *
  5. 并返回降级类型 {@link BuiltinType#INT} 以避免后续连锁报错。
  6. - *
+ * 在语义分析阶段,当分析器注册表中找不到对应节点类型的处理器时, + * 将回退使用本类进行统一处理,以确保编译流程不中断。 + *

+ * 特性说明: + *

    + *
  • 适用于所有未知或暂未实现的表达式类型;
  • + *
  • 自动记录语义错误并打印日志,方便定位与扩展;
  • + *
  • 返回统一的降级类型 {@link BuiltinType#INT},以避免类型缺失造成后续分析失败。
  • + *
* - * @param 任何 {@link ExpressionNode} 子类型 + * @param 任意 {@link ExpressionNode} 的子类,支持泛型兜底匹配 */ public class UnsupportedExpressionAnalyzer implements ExpressionAnalyzer { /** - * 对未知或不支持的表达式节点进行处理。 + * 对不支持或未实现的表达式节点执行兜底处理。 * - * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等 - * @param mi 当前模块信息(此分析器不使用模块上下文,参数可忽略) - * @param fn 当前函数节点(此分析器不使用函数上下文,参数可忽略) - * @param locals 当前作用域的符号表(此分析器不依赖局部符号,参数可忽略) - * @param expr 待分析的表达式节点,类型为任意 {@link ExpressionNode} 子类型 - * @return 固定返回 {@link BuiltinType#INT},作为错误降级类型 + * @param ctx 当前语义分析上下文对象,用于错误记录与日志输出 + * @param mi 当前模块信息(此方法中未使用,保留用于接口一致性) + * @param fn 当前函数节点(此方法中未使用,保留用于接口一致性) + * @param locals 当前局部作用域符号表(此方法中未使用,因不解析具体含义) + * @param expr 不支持的表达式节点 + * @return 固定返回 {@link BuiltinType#INT} 类型,作为占位降级类型 */ @Override public Type analyze(Context ctx, @@ -41,11 +45,16 @@ public class UnsupportedExpressionAnalyzer FunctionNode fn, SymbolTable locals, E expr) { + // 记录语义错误 ctx.getErrors().add(new SemanticError( expr, "不支持的表达式类型: " + expr )); + + // 输出错误日志以便调试或后续支持扩展 ctx.log("错误: 不支持的表达式类型 " + expr); + + // 返回默认类型以避免连锁报错 return BuiltinType.INT; } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java index 8902ce6..91056b5 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/AssignmentAnalyzer.java @@ -10,34 +10,58 @@ import org.jcnc.snow.compiler.semantic.symbol.*; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 赋值语句分析器:支持数值宽化自动转换。 + * {@code AssignmentAnalyzer} 是赋值语句的语义分析器。 + *

+ * 负责分析和验证赋值语句的合法性,包括: + *

    + *
  • 变量是否已声明且可赋值(必须为 {@link SymbolKind#VARIABLE} 类型);
  • + *
  • 赋值右值的类型是否与变量类型兼容;
  • + *
  • 是否允许进行数值类型的自动宽化转换(如 {@code int → float})。
  • + *
+ * 若类型不兼容且无法自动宽化,则将记录语义错误并输出日志信息。 */ public class AssignmentAnalyzer implements StatementAnalyzer { + + /** + * 分析赋值语句的语义有效性,包括左值合法性与类型匹配性。 + * + * @param ctx 当前语义分析上下文,包含模块表、错误收集、日志输出等服务。 + * @param mi 当前模块信息,用于支持跨模块语义校验(当前未涉及)。 + * @param fn 当前分析的函数节点,提供局部作用域上下文。 + * @param locals 当前函数或代码块的符号表,用于变量解析与类型信息获取。 + * @param asg 要分析的赋值语句节点 {@link AssignmentNode}。 + */ @Override public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, AssignmentNode asg) { + + // 获取赋值左值变量名并进行符号解析 ctx.log("赋值检查: " + asg.variable()); Symbol sym = locals.resolve(asg.variable()); + + // 检查变量是否已声明且为可赋值的变量类型 if (sym == null || sym.kind() != SymbolKind.VARIABLE) { ctx.getErrors().add(new SemanticError(asg, - "未声明的变量: " + asg.variable())); + "未声明的变量: " + asg.variable())); ctx.log("错误: 未声明的变量 " + asg.variable()); return; } + // 分析右值表达式类型 Type valType = ctx.getRegistry().getExpressionAnalyzer(asg.value()) - .analyze(ctx, mi, fn, locals, asg.value()); + .analyze(ctx, mi, fn, locals, asg.value()); + // 类型检查:若类型不兼容,则尝试判断是否允许宽化转换 if (!sym.type().isCompatible(valType)) { - // 数值宽化允许 + // 数值类型允许自动宽化转换(如 int → double) if (!(sym.type().isNumeric() && valType.isNumeric() - && Type.widen(valType, sym.type()) == sym.type())) { + && Type.widen(valType, sym.type()) == sym.type())) { ctx.getErrors().add(new SemanticError(asg, - "赋值类型不匹配: 期望 " + sym.type() - + ", 实际 " + valType)); + "赋值类型不匹配: 期望 " + sym.type() + + ", 实际 " + valType)); ctx.log("错误: 赋值类型不匹配 " + asg.variable()); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java index 5cc3151..f20b55f 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/DeclarationAnalyzer.java @@ -11,48 +11,73 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 变量声明语句分析器:支持数值宽化自动转换和初始化检查。 + * {@code DeclarationAnalyzer} 是变量声明语句的语义分析器。 + *

+ * 它负责处理类似 {@code int x = 10;} 的声明语句,具体分析内容包括: + *

    + *
  • 类型解析:将声明中的类型字符串转换为语义层的 {@link Type} 对象;
  • + *
  • 符号定义:将变量注册到当前作用域的 {@link SymbolTable} 中;
  • + *
  • 重复定义检查:防止同一作用域下的变量名冲突;
  • + *
  • 初始化表达式类型校验:检查类型兼容性,支持数值类型宽化(如 int → float)。
  • + *
+ * 若出现类型未识别、重复声明或类型不兼容等问题,将向语义错误列表添加对应错误信息。 */ public class DeclarationAnalyzer implements StatementAnalyzer { + + /** + * 对变量声明语句执行语义分析。 + * + * @param ctx 当前语义分析上下文对象,提供类型解析、错误记录、日志输出等功能。 + * @param mi 当前模块信息,支持跨模块引用检查(本分析器未直接使用)。 + * @param fn 当前函数节点,表示当前所在作用域的函数(用于初始化分析上下文)。 + * @param locals 当前作用域的符号表,用于注册和查找变量。 + * @param decl 要分析的变量声明节点 {@link DeclarationNode}。 + */ @Override public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, DeclarationNode decl) { + + // 1. 解析声明类型 Type varType = ctx.parseType(decl.getType()); if (varType == null) { ctx.getErrors().add(new SemanticError(decl, - "未知类型: " + decl.getType())); + "未知类型: " + decl.getType())); ctx.log("错误: 未知类型 " + decl.getType() - + " 在声明 " + decl.getName()); - varType = BuiltinType.INT; + + " 在声明 " + decl.getName()); + varType = BuiltinType.INT; // 容错处理:默认降级为 int } ctx.log("声明变量: " + decl.getName() - + " 类型: " + varType); + + " 类型: " + varType); + // 2. 将变量注册到当前作用域符号表中,检查重复定义 if (!locals.define(new Symbol( - decl.getName(), varType, SymbolKind.VARIABLE + decl.getName(), varType, SymbolKind.VARIABLE ))) { ctx.getErrors().add(new SemanticError(decl, - "变量重复声明: " + decl.getName())); + "变量重复声明: " + decl.getName())); ctx.log("错误: 变量重复声明 " + decl.getName()); } - // 初始化表达式类型检查 - Type finalVarType = varType; + // 3. 检查初始化表达式(如果存在) + Type finalVarType = varType; // 用于 lambda 捕获 decl.getInitializer().ifPresent(init -> { Type initType = ctx.getRegistry().getExpressionAnalyzer(init) - .analyze(ctx, mi, fn, locals, init); + .analyze(ctx, mi, fn, locals, init); + + // 检查类型是否兼容,或是否允许数值宽化转换 if (!finalVarType.isCompatible(initType)) { - // 数值宽化允许 - if (!(finalVarType.isNumeric() && initType.isNumeric() - && Type.widen(initType, finalVarType) == finalVarType)) { + boolean canWiden = finalVarType.isNumeric() + && initType.isNumeric() + && Type.widen(initType, finalVarType) == finalVarType; + if (!canWiden) { ctx.getErrors().add(new SemanticError(decl, - "初始化类型不匹配: 期望 " + finalVarType - + ", 实际 " + initType)); + "初始化类型不匹配: 期望 " + finalVarType + + ", 实际 " + initType)); ctx.log("错误: 初始化类型不匹配 " - + decl.getName()); + + decl.getName()); } } }); 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 2d1ad76..0552426 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,26 +11,27 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * If 语句分析器:负责对 {@link IfNode} 节点进行语义检查。 + * {@code IfAnalyzer} 是用于分析 {@link IfNode} 条件语句的语义分析器。 *

- * 分析流程: - *

    - *
  1. 对条件表达式进行类型推导,要求类型为 {@link BuiltinType#INT};
  2. - *
  3. 若条件类型不符,记录错误并降级继续分析;
  4. - *
  5. 分别递归分析 then 分支和 else 分支中的每个子语句;
  6. - *
  7. 不支持的子语句类型将由注册表中未匹配到的分析器处理(或被记录为错误)。
  8. - *
+ * 它负责验证 if 条件表达式的类型合法性,并递归分析 then 分支和 else 分支中的所有子语句。 + * 分析规则如下: + *
    + *
  • if 条件必须为 {@link BuiltinType#INT} 类型,用于表示布尔真值;
  • + *
  • 若条件类型不为 int,将报告语义错误并继续分析;
  • + *
  • then 和 else 分支中的语句将分别递归进行语义分析;
  • + *
  • 若某个子语句无匹配分析器,将回退到默认处理器或报告不支持。
  • + *
*/ public class IfAnalyzer implements StatementAnalyzer { /** - * 对给定的 if 节点进行语义分析。 + * 执行 if 语句的语义分析,包括条件类型检查和分支分析。 * - * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等 - * @param mi 当前模块信息,用于跨模块调用检查(此分析器未直接使用) - * @param fn 当前函数节点,可用于更复杂的上下文校验(此分析器未直接使用) - * @param locals 当前作用域的符号表,包含已定义变量及其类型 - * @param ifn 待分析的 {@link IfNode},包含条件表达式、then 分支和 else 分支 + * @param ctx 当前语义分析上下文,提供注册表、错误收集、日志等服务。 + * @param mi 当前模块信息(本实现未直接使用)。 + * @param fn 当前所在函数节点(本实现未直接使用)。 + * @param locals 当前作用域的符号表,提供变量查找支持。 + * @param ifn 要分析的 {@link IfNode} 语法节点,包含条件表达式及两个分支语句块。 */ @Override public void analyze(Context ctx, @@ -38,7 +39,8 @@ public class IfAnalyzer implements StatementAnalyzer { FunctionNode fn, SymbolTable locals, IfNode ifn) { - // 1. 条件表达式类型检查 + + // 1. 分析并校验条件表达式类型 ctx.log("检查 if 条件"); var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ifn.condition()); Type cond = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition()); @@ -50,7 +52,7 @@ public class IfAnalyzer implements StatementAnalyzer { ctx.log("错误: if 条件类型不为 int"); } - // 2. 递归分析 then 分支 + // 2. 递归分析 then 分支语句 for (var stmt : ifn.thenBranch()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { @@ -58,7 +60,7 @@ public class IfAnalyzer implements StatementAnalyzer { } } - // 3. 递归分析 else 分支 + // 3. 递归分析 else 分支语句(若存在) for (var stmt : ifn.elseBranch()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { 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 de67823..6599986 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,28 +11,29 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 循环语句分析器:负责对 {@link LoopNode} 节点进行语义检查。 + * {@code LoopAnalyzer} 是用于分析 {@link LoopNode} 循环结构的语义分析器。 *

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

+ * 分析流程如下: *

    - *
  1. 分析并执行初始化语句(initializer);
  2. - *
  3. 对循环条件表达式进行类型推导,要求为 {@link BuiltinType#INT};
  4. - *
  5. 分析并执行更新语句(update);
  6. - *
  7. 递归分析循环体(body)中的每个子语句;
  8. - *
  9. 在任何步骤发现类型或语义错误时,都会向 {@link Context#getErrors() 错误列表} 中添加对应的 - * {@link SemanticError} 并在日志中记录。
  10. + *
  11. 分析初始化语句 {@code initializer};
  12. + *
  13. 分析并验证条件表达式 {@code condition} 类型是否为 {@link BuiltinType#INT};
  14. + *
  15. 分析更新语句 {@code update};
  16. + *
  17. 递归分析循环体 {@code body} 中的每个子语句;
  18. + *
  19. 所有阶段若发现语义错误均记录至 {@link Context#getErrors()},并写入日志。
  20. *
*/ public class LoopAnalyzer implements StatementAnalyzer { /** - * 对给定的循环节点进行语义分析。 + * 执行循环结构的语义分析。 * - * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等 - * @param mi 当前模块信息,用于跨模块调用检查(此分析器未直接使用) - * @param fn 当前函数节点,可用于更复杂的上下文校验(此分析器未直接使用) - * @param locals 当前作用域的符号表,包含已定义变量及其类型 - * @param ln 待分析的 {@link LoopNode},包含 initializer、condition、update 和 body + * @param ctx 当前语义分析上下文,提供模块信息、错误记录、日志输出和分析器注册表等服务。 + * @param mi 当前模块信息(本方法中未直接使用,保留用于接口一致性)。 + * @param fn 当前函数节点(本方法中未直接使用,保留用于可能的上下文分析扩展)。 + * @param locals 当前作用域的符号表,记录变量类型及其可见性。 + * @param ln 待分析的 {@link LoopNode} 节点,包含初始化语句、条件表达式、更新语句和循环体。 */ @Override public void analyze(Context ctx, @@ -40,14 +41,15 @@ public class LoopAnalyzer implements StatementAnalyzer { FunctionNode fn, SymbolTable locals, LoopNode ln) { - // 1. 初始化语句 + + // 1. 分析初始化语句 ctx.log("检查 loop 循环"); var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer()); if (initAnalyzer != null) { initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer()); } - // 2. 条件表达式类型检查 + // 2. 分析条件表达式并检查类型 var condAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ln.condition()); Type cond = condAnalyzer.analyze(ctx, mi, fn, locals, ln.condition()); if (cond != BuiltinType.INT) { @@ -58,13 +60,13 @@ public class LoopAnalyzer implements StatementAnalyzer { ctx.log("错误: loop 条件类型不为 int"); } - // 3. 更新语句 + // 3. 分析更新语句 var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update()); if (updateAnalyzer != null) { updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update()); } - // 4. 循环体语句 + // 4. 递归分析循环体中的每个语句 for (var stmt : ln.body()) { var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (stAnalyzer != null) { diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java index 570842c..3908909 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ReturnAnalyzer.java @@ -12,27 +12,26 @@ import org.jcnc.snow.compiler.semantic.type.FunctionType; import org.jcnc.snow.compiler.semantic.type.Type; /** - * Return 语句分析器:负责对 {@link ReturnNode} 节点进行语义检查。 + * {@code ReturnAnalyzer} 是用于分析 {@link ReturnNode} 返回语句的语义分析器。 *

- * 分析过程包括: - *

    - *
  1. 获取当前函数的预期返回类型;
  2. - *
  3. 如果存在返回表达式,则递归分析其类型并与预期类型进行兼容性检查;
  4. - *
  5. 如果没有返回表达式且预期类型不是 {@link BuiltinType#VOID},则记录缺少返回值的错误;
  6. - *
  7. 遇到类型不兼容时,会向 {@link Context#getErrors() 错误列表} 中添加 {@link SemanticError},
  8. - *
  9. 所有错误同时会记录到日志以便调试。
  10. - *
+ * 它负责检查函数中的 return 语句是否与函数定义的返回类型匹配。分析流程包括: + *
    + *
  • 获取当前函数的返回类型 {@link FunctionType#returnType()};
  • + *
  • 若 return 语句包含返回值表达式,检查其类型与函数定义是否兼容;
  • + *
  • 若 return 语句未指定返回值,而函数返回类型非 {@link BuiltinType#VOID},则视为错误;
  • + *
  • 所有不兼容情况将记录为 {@link SemanticError} 并写入分析日志。
  • + *
*/ public class ReturnAnalyzer implements StatementAnalyzer { /** - * 对给定的 return 节点进行语义分析。 + * 分析 return 语句的语义合法性。 * - * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等 - * @param mi 当前模块信息,用于查找函数签名和返回类型 - * @param fn 当前正在分析的函数节点,包含函数名和参数列表 - * @param locals 当前作用域的符号表(此分析器不使用局部符号表) - * @param ret 待分析的 {@link ReturnNode},可能包含一个返回表达式 + * @param ctx 当前语义分析上下文对象,提供模块访问、错误记录与分析器调度功能。 + * @param mi 当前模块信息,用于定位当前函数定义。 + * @param fn 当前所在的函数节点,包含函数名及参数定义。 + * @param locals 当前作用域的符号表(return 不依赖变量声明,此参数未使用)。 + * @param ret {@link ReturnNode} 语法节点,表示函数中的 return 语句,可能包含返回值表达式。 */ @Override public void analyze(Context ctx, @@ -40,38 +39,40 @@ public class ReturnAnalyzer implements StatementAnalyzer { FunctionNode fn, SymbolTable locals, ReturnNode ret) { + ctx.log("检查 return"); - // 查找函数的预期返回类型 + // 获取当前函数的定义信息 FunctionType expected = ctx.getModules() .get(mi.getName()) .getFunctions() .get(fn.name()); - // 如果有返回表达式,则检查其类型 + // 情况 1:存在返回表达式,需进行类型检查 ret.getExpression().ifPresentOrElse(exp -> { - var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(exp); - Type actual = exprAnalyzer.analyze(ctx, mi, fn, locals, exp); - if (!expected.returnType().isCompatible(actual)) { - ctx.getErrors().add(new SemanticError( - ret, - "return 类型不匹配: 期望 " - + expected.returnType() - + ", 实际 " - + actual - )); - ctx.log("错误: return 类型不匹配"); - } - }, - // 如果没有返回表达式,但函数应有返回值 - () -> { - if (expected.returnType() != BuiltinType.VOID) { - ctx.getErrors().add(new SemanticError( - ret, - "非 void 函数必须返回值" - )); - ctx.log("错误: 非 void 函数缺少返回值"); - } - }); + var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(exp); + Type actual = exprAnalyzer.analyze(ctx, mi, fn, locals, exp); + + if (!expected.returnType().isCompatible(actual)) { + ctx.getErrors().add(new SemanticError( + ret, + "return 类型不匹配: 期望 " + + expected.returnType() + + ", 实际 " + + actual + )); + ctx.log("错误: return 类型不匹配"); + } + + // 情况 2:无返回表达式,但函数定义了非 void 返回类型 + }, () -> { + if (expected.returnType() != BuiltinType.VOID) { + ctx.getErrors().add(new SemanticError( + ret, + "非 void 函数必须返回值" + )); + ctx.log("错误: 非 void 函数缺少返回值"); + } + }); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java index ebff75b..ded39fa 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java @@ -6,30 +6,55 @@ import org.jcnc.snow.compiler.semantic.analyzers.expression.*; import org.jcnc.snow.compiler.semantic.analyzers.statement.*; /** - * 负责一次性把所有语句/表达式分析器注册进 `AnalyzerRegistry`。 + * {@code AnalyzerRegistrar} 负责将所有语句与表达式的语义分析器 + * 统一注册到 {@link AnalyzerRegistry} 中。 + *

+ * 本类为静态工具类,不可实例化,其唯一公开方法 {@link #registerAll(AnalyzerRegistry)} + * 应在语义分析初始化阶段调用一次,确保所有节点类型都能正确分发到对应分析器。 + *

+ * 注册内容包括: + *

    + *
  • 所有标准语句节点(如变量声明、赋值、条件、循环、返回等)的分析器;
  • + *
  • 所有标准表达式节点(如字面量、标识符、函数调用、二元表达式等)的分析器;
  • + *
  • 对不支持或未实现的表达式节点提供兜底分析器 {@link UnsupportedExpressionAnalyzer}。
  • + *
*/ public final class AnalyzerRegistrar { + + /** + * 私有构造函数禁止实例化。 + */ private AnalyzerRegistrar() { } + /** + * 向指定 {@link AnalyzerRegistry} 注册所有语法分析器实例。 + * + * @param registry 待注册的分析器注册表实例 + */ public static void registerAll(AnalyzerRegistry registry) { - // ---------- 语句分析器 ---------- + // ---------- 注册语句分析器 ---------- registry.registerStatementAnalyzer(DeclarationNode.class, new DeclarationAnalyzer()); registry.registerStatementAnalyzer(AssignmentNode.class, new AssignmentAnalyzer()); - registry.registerStatementAnalyzer(IfNode.class, new IfAnalyzer()); - registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer()); - registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); + registry.registerStatementAnalyzer(IfNode.class, new IfAnalyzer()); + registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer()); + registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); + + // 特殊处理:表达式语句(如 "foo();")作为语句包装表达式 registry.registerStatementAnalyzer(ExpressionStatementNode.class, - (ctx, mi, fn, locals, stmt) -> - registry.getExpressionAnalyzer(stmt.expression()) - .analyze(ctx, mi, fn, locals, stmt.expression()) + (ctx, mi, fn, locals, stmt) -> + registry.getExpressionAnalyzer(stmt.expression()) + .analyze(ctx, mi, fn, locals, stmt.expression()) ); - // ---------- 表达式分析器 ---------- - registry.registerExpressionAnalyzer(NumberLiteralNode.class, new NumberLiteralAnalyzer()); - registry.registerExpressionAnalyzer(StringLiteralNode.class, new StringLiteralAnalyzer()); - registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer()); - registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer()); - registry.registerExpressionAnalyzer(BinaryExpressionNode.class,new BinaryExpressionAnalyzer()); - registry.registerExpressionAnalyzer(MemberExpressionNode.class,new UnsupportedExpressionAnalyzer<>()); + // ---------- 注册表达式分析器 ---------- + registry.registerExpressionAnalyzer(NumberLiteralNode.class, new NumberLiteralAnalyzer()); + registry.registerExpressionAnalyzer(StringLiteralNode.class, new StringLiteralAnalyzer()); + registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer()); + registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer()); + registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); + + // 对尚未实现的表达式类型使用兜底处理器(如 MemberExpression) + registry.registerExpressionAnalyzer(MemberExpressionNode.class, + new UnsupportedExpressionAnalyzer<>()); } -} \ No newline at end of file +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java index d856a83..a1dc661 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/BuiltinTypeRegistry.java @@ -5,9 +5,24 @@ import org.jcnc.snow.compiler.semantic.type.*; import java.util.Map; /** - * 统一维护内置类型与内置模块(如 BuiltinUtils)。 + * {@code BuiltinTypeRegistry} 是内置类型和内置模块的集中注册中心。 + *

+ * 本类主要负责: + *

    + *
  • 定义语言中所有可识别的基础类型(如 int、float、string 等);
  • + *
  • 在语义分析初始化时,将内置模块(如 {@code BuiltinUtils})注册到上下文中;
  • + *
  • 提供对内置类型的快速查找支持。
  • + *
+ * 该类为纯工具类,所有成员均为静态,不可实例化。 */ public final class BuiltinTypeRegistry { + + /** + * 内置类型映射表:将类型名称字符串映射到对应的 {@link Type} 实例。 + *

+ * 用于类型解析过程(如解析变量声明或函数返回类型)中, + * 将用户源码中的类型字符串转换为语义类型对象。 + */ public static final Map BUILTIN_TYPES = Map.of( "int", BuiltinType.INT, "long", BuiltinType.LONG, @@ -19,9 +34,20 @@ public final class BuiltinTypeRegistry { "void", BuiltinType.VOID ); + /** + * 私有构造函数,禁止实例化。 + */ private BuiltinTypeRegistry() { } + /** + * 初始化语义上下文中与内置模块相关的内容。 + *

+ * 当前实现将内置模块 {@code BuiltinUtils} 注册至上下文模块表中, + * 使其在用户代码中可被访问(如 {@code BuiltinUtils.to_string(...)})。 + * + * @param ctx 当前语义分析上下文 + */ public static void init(Context ctx) { ctx.modules().put("BuiltinUtils", ModuleInfo.builtin()); } -} \ No newline at end of file +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java index 822c254..2d051cb 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java @@ -8,25 +8,36 @@ import java.util.List; import java.util.Map; /** - * 语义分析公共上下文:在整个分析流程中共享,用于存储模块信息、收集错误、控制日志,以及管理分析器注册表。 + * {@code Context} 表示语义分析阶段的共享上下文环境。 + *

+ * 它贯穿整个语义分析流程,用于维护并提供以下核心服务: + *

    + *
  • 模块信息管理:包含所有已加载模块(源模块与内置模块);
  • + *
  • 错误收集:集中存储语义分析期间产生的 {@link SemanticError};
  • + *
  • 日志控制:支持按需输出详细调试日志;
  • + *
  • 分析器调度:通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发。
  • + *
*/ public class Context { - /** 所有已注册模块名 -> 模块信息 的映射 */ + /** 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */ private final Map modules; - /** 语义分析过程中收集的所有错误列表 */ + + /** 错误列表:语义分析过程中收集的所有 {@link SemanticError} */ private final List errors; - /** 是否启用详细日志输出 */ + + /** 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息 */ private final boolean verbose; - /** 存放并分发各类语句/表达式分析器的注册表 */ + + /** 语义分析器注册表:用于按节点类型动态调度分析器 */ private final AnalyzerRegistry registry; /** - * 构造一个新的 Context 实例。 + * 构造语义分析上下文对象。 * - * @param modules 已初始化的模块信息映射,用于查找模块签名、导入关系等 - * @param errors 用于收集语义分析过程中发现的所有 {@link SemanticError} - * @param verbose 是否启用详细日志;当为 true 时,调用 {@link #log(String)} 会打印日志 - * @param registry 已设置好所有分析器的 {@link AnalyzerRegistry} + * @param modules 已注册的模块信息集合 + * @param errors 错误收集器,分析器将所有语义错误写入此列表 + * @param verbose 是否启用调试日志输出 + * @param registry 分析器注册表,提供类型到分析器的映射与调度能力 */ public Context(Map modules, List errors, @@ -38,31 +49,42 @@ public class Context { this.registry = registry; } + // ------------------ 模块信息 ------------------ + /** - * 获取所有模块信息的映射。 + * 获取所有模块信息映射表。 * - * @return 模块名到 {@link ModuleInfo} 的 Map + * @return 模块名 → 模块信息 {@link ModuleInfo} 的映射 */ public Map getModules() { return modules; } - public Map modules() { return modules; } - public List errors() { return errors; } - public AnalyzerRegistry registry() { return registry; } + /** @return 模块信息(快捷方式) */ + public Map modules() { + return modules; + } + + // ------------------ 错误收集 ------------------ + /** - * 获取语义错误列表。 - * 分析过程中产生的 {@link SemanticError} 会被收集到此列表。 + * 获取语义分析过程中记录的所有错误。 * - * @return 语义错误列表 + * @return 错误列表 */ public List getErrors() { return errors; } + /** @return 错误列表(快捷方式) */ + public List errors() { + return errors; + } + + // ------------------ 分析器注册表 ------------------ + /** - * 获取分析器注册表。 - * 可通过此注册表按 AST 节点类型分发对应的语句或表达式分析器。 + * 获取分析器注册表,用于分发语句与表达式分析器。 * * @return {@link AnalyzerRegistry} 实例 */ @@ -70,10 +92,17 @@ public class Context { return registry; } + /** @return 注册表(快捷方式) */ + public AnalyzerRegistry registry() { + return registry; + } + + // ------------------ 日志输出 ------------------ + /** - * 打印日志信息,仅在 {@code verbose} 为 true 时输出到标准输出。 + * 打印日志信息,仅当 {@code verbose} 为 true 时生效。 * - * @param msg 要打印的日志内容 + * @param msg 日志内容 */ public void log(String msg) { if (verbose) { @@ -81,14 +110,16 @@ public class Context { } } + // ------------------ 工具函数 ------------------ + /** - * 将类型名解析为内置类型 {@link Type} 实例。 + * 将类型名称字符串解析为对应的内置 {@link Type} 实例。 *

- * 若名称在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在,则返回对应类型; - * 否则返回 {@code null},调用方可据此决定降级为默认类型并记录错误。 + * 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在,则返回对应类型; + * 否则返回 {@code null},调用方可据此决定是否降级处理。 * - * @param name 类型名称(如 "int", "string", "void") - * @return 对应的 {@link Type},或 {@code null} 表示未知类型 + * @param name 类型名称(如 "int", "float", "void", "string" 等) + * @return 匹配的 {@link Type},若无匹配项则返回 {@code null} */ public Type parseType(String name) { return BuiltinTypeRegistry.BUILTIN_TYPES.get(name); diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java index 384a788..22edea7 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java @@ -3,31 +3,81 @@ package org.jcnc.snow.compiler.semantic.core; import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.symbol.*; -import java.util.*; /** - * 遍历每个函数体并通过分发器执行语句/表达式分析。 + * {@code FunctionChecker} 是语义分析阶段中用于检查函数体语句合法性的调度器。 + *

+ * 它逐个遍历所有模块中的函数定义,并对函数体中的每一条语句调用对应的语义分析器, + * 执行类型检查、作用域验证、错误记录等任务。 + *

+ * 核心职责包括: + *

    + *
  • 为每个函数构建局部符号表并注册函数参数为变量;
  • + *
  • 分发函数体语句至相应的 {@link org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer};
  • + *
  • 记录未支持语句类型为语义错误;
  • + *
  • 依赖上下文 {@link Context} 提供模块信息、类型解析、错误收集等服务。
  • + *
*/ public class FunctionChecker { - private final Context ctx; - public FunctionChecker(Context ctx) { this.ctx = ctx; } + /** 全局语义分析上下文,提供模块信息、注册表、错误记录等支持 */ + private final Context ctx; + + /** + * 构造函数体检查器。 + * + * @param ctx 当前语义分析上下文 + */ + public FunctionChecker(Context ctx) { + this.ctx = ctx; + } + + /** + * 执行函数体检查流程。 + *

+ * 对所有模块中的所有函数依次进行处理: + *

    + *
  1. 查找模块对应的 {@link ModuleInfo};
  2. + *
  3. 创建函数局部符号表 {@link SymbolTable},并注册所有参数变量;
  4. + *
  5. 对函数体中的每一条语句分发到已注册的分析器进行语义分析;
  6. + *
  7. 若某条语句无可用分析器,则记录为 {@link SemanticError}。
  8. + *
+ * + * @param mods 所有模块的 AST 根节点集合 + */ public void check(Iterable mods) { for (ModuleNode mod : mods) { + // 获取当前模块对应的语义信息 ModuleInfo mi = ctx.modules().get(mod.name()); - for (FunctionNode fn : mod.functions()) { - SymbolTable locals = new SymbolTable(null); - fn.parameters().forEach(p -> locals.define(new Symbol(p.name(), ctx.parseType(p.type()), SymbolKind.VARIABLE))); + // 遍历模块中所有函数定义 + for (FunctionNode fn : mod.functions()) { + + // 构建函数局部作用域符号表,设置父作用域为 null + SymbolTable locals = new SymbolTable(null); + + // 将函数参数注册为局部变量 + fn.parameters().forEach(p -> + locals.define(new Symbol( + p.name(), + ctx.parseType(p.type()), + SymbolKind.VARIABLE + )) + ); + + // 遍历并分析函数体内的每条语句 for (var stmt : fn.body()) { var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt); if (analyzer != null) { analyzer.analyze(ctx, mi, fn, locals, stmt); } else { - ctx.errors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); + ctx.errors().add(new SemanticError( + stmt, + "不支持的语句类型: " + stmt + )); } } } } } -} \ No newline at end of file +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java index ccc0661..08e5a4e 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java @@ -6,28 +6,33 @@ import org.jcnc.snow.compiler.semantic.type.FunctionType; import java.util.*; /** - * 模块信息:保存单个模块在语义分析阶段的元数据。 + * {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装。 *

- * 包含以下内容: + * 用于在分析期间管理模块间依赖、函数签名查找等关键任务。 + * 每个模块对应一个唯一的 {@code ModuleInfo} 实例。 + *

+ * 包含信息包括: *

    *
  • 模块名称(唯一标识);
  • - *
  • 该模块导入的其他模块名称集合;
  • - *
  • 该模块中定义的函数签名映射,将函数名映射到对应的 {@link FunctionType}。
  • + *
  • 该模块导入的其他模块名集合;
  • + *
  • 该模块中定义的所有函数签名 {@code Map}。
  • *
- * 在语义分析过程中,用于管理模块依赖关系和查找函数签名。 */ public class ModuleInfo { - /** 模块名称 */ + + /** 模块名称,作为全局唯一标识 */ private final String name; - /** 导入的其他模块名称集合 */ + + /** 该模块显式导入的模块名集合(用于跨模块访问符号) */ private final Set imports = new HashSet<>(); - /** 模块中定义的函数签名映射:函数名 -> 函数类型 */ + + /** 该模块中定义的函数名 → 函数类型映射 */ private final Map functions = new HashMap<>(); /** - * 构造一个新的模块信息对象。 + * 构造模块信息对象。 * - * @param name 模块名称,必须唯一 + * @param name 模块名称,必须唯一且不可为空 */ public ModuleInfo(String name) { this.name = name; @@ -36,40 +41,59 @@ public class ModuleInfo { /** * 获取模块名称。 * - * @return 模块的唯一名称 + * @return 当前模块的唯一名称 */ public String getName() { return name; } /** - * 获取该模块导入的其他模块名称集合。 + * 获取该模块导入的模块名称集合。 *

- * 返回的 Set 为内部对象的直接引用,可对其进行添加或移除操作,以维护导入列表。 + * 返回集合为内部数据的直接引用,调用方可通过 {@code add/remove} 方法动态维护导入信息。 * - * @return 导入模块名称的可变集合 + * @return 可变集合,包含所有导入模块名 */ public Set getImports() { return imports; } /** - * 获取模块中定义的函数签名映射表。 + * 获取模块中已声明的函数签名表。 *

- * 键为函数名,值为对应的 {@link FunctionType}。 - * 返回的 Map 为内部对象的直接引用,可对其进行添加或移除操作,以维护函数签名列表。 + * 映射键为函数名,值为对应的 {@link FunctionType}。 + * 返回对象为内部引用,可用于添加、修改或删除函数定义。 * - * @return 函数签名映射表 + * @return 模块内函数定义映射表 */ public Map getFunctions() { return functions; } + /** + * 创建并返回一个用于注册内置函数的内置模块 {@code BuiltinUtils}。 + *

+ * 该模块提供如下内置函数签名: + *

    + *
  • {@code to_int(string): int}
  • + *
  • {@code to_string(int): string}
  • + *
  • {@code print(string): void}
  • + *
+ * + * @return 一个预构建的 {@code BuiltinUtils} 模块信息对象 + */ public static ModuleInfo builtin() { ModuleInfo mi = new ModuleInfo("BuiltinUtils"); - mi.getFunctions().put("to_int", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.INT)); - mi.getFunctions().put("to_string", new FunctionType(List.of(BuiltinType.INT), BuiltinType.STRING)); - mi.getFunctions().put("print", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.VOID)); + + mi.getFunctions().put("to_int", new FunctionType( + List.of(BuiltinType.STRING), BuiltinType.INT)); + + mi.getFunctions().put("to_string", new FunctionType( + List.of(BuiltinType.INT), BuiltinType.STRING)); + + mi.getFunctions().put("print", new FunctionType( + List.of(BuiltinType.STRING), BuiltinType.VOID)); + return mi; } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java index fac0538..85546b1 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleRegistry.java @@ -3,15 +3,40 @@ package org.jcnc.snow.compiler.semantic.core; import org.jcnc.snow.compiler.parser.ast.ModuleNode; /** - * 负责将用户模块名称填入 `modules` 映射。 + * {@code ModuleRegistry} 负责将用户源码中声明的模块名称注册至全局语义上下文。 + *

+ * 它会遍历语法树中的所有模块节点 {@link ModuleNode},并将其模块名填入 + * {@link Context#modules()} 映射中,为后续语义分析阶段中的模块查找、 + * 导入验证和跨模块调用提供支持。 + *

+ * 注册结果为 {@code Map},键为模块名,值为新建的 {@link ModuleInfo}。 + * 若某模块名已存在(如内置模块),则不会重复注册。 */ public class ModuleRegistry { - private final Context ctx; - public ModuleRegistry(Context ctx) { this.ctx = ctx; } + /** 当前语义分析上下文,用于访问模块表 */ + private final Context ctx; + + /** + * 构造模块注册器。 + * + * @param ctx 当前语义分析上下文 + */ + public ModuleRegistry(Context ctx) { + this.ctx = ctx; + } + + /** + * 遍历并注册所有用户定义模块。 + *

+ * 对于每个模块节点,将其名称注册到 {@code ctx.modules()} 中作为键, + * 若模块已存在(例如内置模块),则不会覆盖。 + * + * @param mods 所有模块节点的集合 + */ public void registerUserModules(Iterable mods) { for (ModuleNode mod : mods) { ctx.modules().putIfAbsent(mod.name(), new ModuleInfo(mod.name())); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java index b7d61c2..c3a1493 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SemanticAnalyzer.java @@ -7,25 +7,53 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError; import java.util.*; /** - * 语义分析总控,仅负责调用各独立组件完成完整流程。 + * {@code SemanticAnalyzer} 是编译器语义分析阶段的顶层调度器。 + *

+ * 它负责统一协调模块注册、函数签名登记和函数体语义检查等子任务,构建并维护语义上下文 {@link Context}, + * 并最终输出所有收集到的语义错误列表 {@link SemanticError}。 + *

+ * 语义分析流程分为三个阶段: + *

    + *
  1. 模块注册:将所有用户模块的名称添加至全局模块表中,供后续导入检查与引用;
  2. + *
  3. 函数签名注册:提取函数定义的签名(名称与类型),填入每个模块对应的 {@link ModuleInfo};
  4. + *
  5. 函数体检查:遍历每个函数体,对所有语句与表达式执行类型检查和语义验证。
  6. + *
+ *

+ * 内部使用组件: + *

    + *
  • {@link ModuleRegistry}:注册用户模块;
  • + *
  • {@link SignatureRegistrar}:提取函数签名;
  • + *
  • {@link FunctionChecker}:分析函数体内语句;
  • + *
  • {@link BuiltinTypeRegistry}:初始化内置模块和类型;
  • + *
  • {@link AnalyzerRegistrar}:注册语句和表达式分析器。
  • + *
*/ public class SemanticAnalyzer { + + /** 全局语义分析上下文,包含模块信息、错误记录、分析器注册表等 */ private final Context ctx; + + /** 分析器注册表,管理语法节点与分析器之间的映射关系 */ private final AnalyzerRegistry registry = new AnalyzerRegistry(); - // 组件 + // 分析流程中用到的核心子组件 private final ModuleRegistry moduleRegistry; private final SignatureRegistrar signatureRegistrar; private final FunctionChecker functionChecker; + /** + * 构造语义分析器并完成初始配置。 + * + * @param verbose 是否启用日志输出 + */ public SemanticAnalyzer(boolean verbose) { this.ctx = new Context(new HashMap<>(), new ArrayList<>(), verbose, registry); - // 初始化内置模块与分析器注册表 + // 初始化内置模块及分析器注册表 BuiltinTypeRegistry.init(ctx); AnalyzerRegistrar.registerAll(registry); - // 其余组件 + // 构造核心组件 this.moduleRegistry = new ModuleRegistry(ctx); this.signatureRegistrar = new SignatureRegistrar(ctx); this.functionChecker = new FunctionChecker(ctx); @@ -33,15 +61,20 @@ public class SemanticAnalyzer { /** * 执行完整语义分析流程。 + *

+ * 输入为用户的模块语法树集合,输出为分析阶段产生的语义错误列表。 + * + * @param modules 所有用户模块(语法树) + * @return 所有语义错误的列表,若分析无误则为空 */ public List analyze(List modules) { ctx.log("开始语义分析"); - moduleRegistry.registerUserModules(modules); - signatureRegistrar.register(modules); - functionChecker.check(modules); + moduleRegistry.registerUserModules(modules); // 注册模块名 + signatureRegistrar.register(modules); // 提取函数签名 + functionChecker.check(modules); // 分析函数体 ctx.log("分析完成,错误总数: " + ctx.errors().size()); return ctx.errors(); } -} \ No newline at end of file +} 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 904838b..db92c2e 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,23 +9,46 @@ import java.util.List; import java.util.stream.Collectors; /** - * 语义分析统一入口,封装模块筛选、分析器调用、错误报告。 + * {@code SemanticAnalyzerRunner} 是语义分析的统一入口。 + *

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

+ * 使用方式: + *

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

+ * 功能概述: + *

    + *
  • 筛选出所有 {@link ModuleNode} 节点(模块级入口);
  • + *
  • 调用 {@link SemanticAnalyzer} 执行完整语义分析流程;
  • + *
  • 将收集到的 {@link SemanticError} 列表交由报告器处理;
  • + *
  • 若存在语义错误,调用 {@link SemanticAnalysisReporter#reportAndExitIfNecessary(List)} 自动中止流程。
  • + *
*/ public class SemanticAnalyzerRunner { + /** - * 对给定 AST 执行语义分析,并在有错误时终止程序。 + * 对输入的语法树执行语义分析。 + *

+ * 语法树应为编译前阶段(如解析器)产出的 AST 列表, + * 本方法会自动筛选其中的 {@link ModuleNode} 节点,并调用语义分析器执行完整分析。 * - * @param ast AST 根节点列表 - * @param verbose 是否启用详细日志输出 + * @param ast 根节点列表(应包含一个或多个 {@link ModuleNode}) + * @param verbose 是否启用详细日志(将控制内部 {@link Context#log(String)} 的行为) */ public static void runSemanticAnalysis(List ast, boolean verbose) { + // 1. 提取模块节点 List modules = ast.stream() .filter(ModuleNode.class::isInstance) .map(ModuleNode.class::cast) .collect(Collectors.toList()); + // 2. 执行语义分析 List errors = new SemanticAnalyzer(verbose).analyze(modules); + // 3. 报告并在必要时终止 SemanticAnalysisReporter.reportAndExitIfNecessary(errors); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java index f36abca..e4bfce5 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java @@ -3,43 +3,79 @@ package org.jcnc.snow.compiler.semantic.core; import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.type.*; + import java.util.*; /** - * 负责函数签名登记与导入合法性检查。 + * {@code SignatureRegistrar} 负责函数签名登记与导入语义检查。 + *

+ * 在语义分析初期阶段,它遍历每个模块,完成以下任务: + *

    + *
  • 验证所有 {@link ImportNode} 导入的模块是否存在于全局模块表 {@link Context#modules()} 中;
  • + *
  • 将每个 {@link FunctionNode} 的函数签名(参数类型和返回类型)注册到对应 {@link ModuleInfo} 中;
  • + *
  • 在参数或返回类型无法识别时,记录 {@link SemanticError},并进行容错降级。
  • + *
+ * 本组件作为语义分析的准备阶段,为后续函数体检查提供函数类型上下文。 */ public class SignatureRegistrar { - private final Context ctx; - public SignatureRegistrar(Context ctx) { this.ctx = ctx; } + /** 全局语义分析上下文,提供模块、类型、错误管理等功能 */ + private final Context ctx; + + /** + * 构造函数签名注册器。 + * + * @param ctx 当前语义分析上下文 + */ + public SignatureRegistrar(Context ctx) { + this.ctx = ctx; + } + + /** + * 遍历模块并注册函数签名,同时校验导入模块的合法性。 + * + * @param mods 所有模块的语法树节点集合 + */ public void register(Iterable mods) { for (ModuleNode mod : mods) { ModuleInfo mi = ctx.modules().get(mod.name()); - // 导入检查 + // ---------- 1. 模块导入检查 ---------- for (ImportNode imp : mod.imports()) { if (!ctx.modules().containsKey(imp.moduleName())) { - ctx.errors().add(new SemanticError(imp, "未知模块: " + imp.moduleName())); + ctx.errors().add(new SemanticError( + imp, + "未知模块: " + imp.moduleName() + )); } else { mi.getImports().add(imp.moduleName()); } } - // 函数签名 + // ---------- 2. 函数签名注册 ---------- for (FunctionNode fn : mod.functions()) { List params = new ArrayList<>(); + + // 参数类型解析 for (ParameterNode p : fn.parameters()) { Type t = Optional.ofNullable(ctx.parseType(p.type())) - .orElseGet(() -> { - ctx.errors().add(new SemanticError(p, "未知类型: " + p.type())); - return BuiltinType.INT; - }); + .orElseGet(() -> { + ctx.errors().add(new SemanticError( + p, + "未知类型: " + p.type() + )); + return BuiltinType.INT; // 容错降级 + }); params.add(t); } + + // 返回类型解析(默认降级为 void) Type ret = Optional.ofNullable(ctx.parseType(fn.returnType())) - .orElse(BuiltinType.VOID); + .orElse(BuiltinType.VOID); + + // 注册函数签名 mi.getFunctions().put(fn.name(), new FunctionType(params, ret)); } } } -} \ No newline at end of file +} 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 0931c27..650f762 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 @@ -3,29 +3,39 @@ package org.jcnc.snow.compiler.semantic.error; import org.jcnc.snow.compiler.parser.ast.base.Node; /** - * 表示语义分析过程中发现的错误。 + * {@code SemanticError} 表示语义分析过程中发现的错误信息。 *

- * 每个语义错误包含以下内容: + * 语义错误是编译器无法接受的程序逻辑问题,例如: *

    - *
  • {@link Node}:发生错误的 AST 节点,便于定位;
  • - *
  • {@link String message}:错误的详细描述信息。
  • + *
  • 使用了未声明的变量;
  • + *
  • 类型不兼容的赋值;
  • + *
  • 函数返回类型与实际返回值不一致;
  • + *
  • 调用了不存在的函数或模块。
  • *
*

- * 使用 Java 16+ record 定义,自动生成构造方法、访问器(getters)、equals、hashCode 和 toString 方法。 + * 访问器方法({@code node()} / {@code message()})、相等性判断等功能。 * - * @param node 触发语义错误的 AST 节点 - * @param message 错误的详细描述 + *

主要字段说明: + *

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

+ * 输出格式: *

-     * Semantic error at [节点信息]: [错误信息]
+     * Semantic error at [节点]: [错误信息]
      * 
- * 便于在日志输出或调试时快速查看定位。 + * 适用于命令行编译器输出、调试日志或错误收集器展示。 * - * @return 格式化后的错误信息字符串 + * @return 格式化的错误信息字符串 */ @Override public String toString() { diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java index f948938..f6cc9dc 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/Symbol.java @@ -3,25 +3,22 @@ package org.jcnc.snow.compiler.semantic.symbol; import org.jcnc.snow.compiler.semantic.type.Type; /** - * 符号表中的一条符号记录,表示语言中的命名实体(如变量、函数等)。 + * {@code Symbol} 表示符号表中的一条符号记录,描述语言中命名实体的语义信息。 *

- * 每条符号记录包括三个核心属性: + * 符号是语义分析中的基础单元,通常用于表示变量、函数、参数等具名元素。 + * 每个符号具备以下三个核心属性: *

    - *
  • name:符号名称,例如变量名或函数名;
  • - *
  • type:符号的类型信息,对于变量是变量类型,对于函数是返回类型;
  • - *
  • kind:符号的种类,由 {@link SymbolKind} 枚举区分(变量、函数等)。
  • - *
- *

- * 使用 Java 16+ record 定义,自动生成: + *

  • name:符号的名称,例如变量名或函数名;
  • + *
  • type:符号的类型信息,通常为 {@link Type} 的子类实例;
  • + *
  • kind:符号的种类,由 {@link SymbolKind} 枚举表示(例如 VARIABLE、FUNCTION 等)。
  • *
      - *
    • 构造方法;
    • - *
    • 访问器方法(getters);
    • - *
    • equals、hashCode;
    • - *
    • toString 方法。
    • + *
    • 构造器 {@code Symbol(String, Type, SymbolKind)};
    • + *
    • 访问器方法 {@code name()}, {@code type()}, {@code kind()};
    • + *
    • 标准的 {@code equals()}、{@code hashCode()} 和 {@code toString()} 实现。
    • *
    * - * @param name 符号名称 - * @param type 符号类型 - * @param kind 符号种类 + * @param name 符号名称,必须唯一且非空 + * @param type 符号所代表的类型,可为基础类型、函数类型等 + * @param kind 符号种类,决定其语义用途(例如变量、函数、参数等) */ public record Symbol(String name, Type type, SymbolKind kind) { } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java index 6cff3e7..625b2f4 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolKind.java @@ -1,33 +1,40 @@ package org.jcnc.snow.compiler.semantic.symbol; /** - * 符号种类枚举:用于在符号表中区分不同类型的命名实体。 + * {@code SymbolKind} 枚举用于标识符号表中不同类型的命名实体。 *

    - * 在语义分析过程中,不同种类的符号需要不同的处理策略, - * 例如变量声明、函数调用、模块导入等。 - *

    - * + * 在语义分析过程中,编译器需要根据符号的种类(Kind)采用不同的处理策略: + * 例如变量参与类型推导、函数用于调用匹配、模块用于跨作用域引用等。 + *

    + * 当前支持的符号种类包括: *

      - *
    • {@link #VARIABLE} – 变量符号(局部变量、全局变量、成员变量等);
    • - *
    • {@link #FUNCTION} – 函数符号(自由函数、方法、构造函数等);
    • - *
    • {@link #MODULE} – 模块符号,表示一个命名空间或模块;
    • + *
    • {@link #VARIABLE}:变量符号(局部变量、全局变量、成员变量等);
    • + *
    • {@link #FUNCTION}:函数符号(自由函数、方法、构造函数等);
    • + *
    • {@link #MODULE}:模块符号(代表命名空间、库或逻辑模块);
    • *
    */ public enum SymbolKind { + /** - * 变量符号,例如在函数或全局作用域中声明的变量。 + * 变量符号,表示在作用域中声明的可赋值实体。 + *

    + * 包括函数参数、局部变量、全局变量、常量等, + * 分析器会基于其类型参与表达式类型校验和赋值检查。 */ VARIABLE, /** - * 函数符号,表示可调用的函数或方法, - * 用于函数签名注册和调用时的类型匹配。 + * 函数符号,表示可调用的过程实体。 + *

    + * 包括普通函数、方法、构造器等。 + * 用于函数签名注册、函数调用检查及返回值推导。 */ FUNCTION, /** - * 模块符号,表示一个模块或命名空间, - * 用于管理模块导入和跨模块引用。 + * 模块符号,表示一个命名空间或模块单元。 + *

    + * 在跨模块调用、导入语句校验、作用域隔离中发挥作用。 */ MODULE } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java index 2162216..29f81ab 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/symbol/SymbolTable.java @@ -4,26 +4,31 @@ import java.util.HashMap; import java.util.Map; /** - * 符号表(Symbol Table):用于管理命名实体(如变量、函数、模块等)的作用域及其类型信息。 + * {@code SymbolTable} 表示一个语义作用域,用于管理命名实体(如变量、函数、模块等)的符号信息。 *

    - * 本实现支持链式作用域(Nested Scope): + * 本类支持嵌套作用域结构(Nested Scope),适用于块级作用域、函数作用域、模块作用域等语义环境建模。 + * 每个符号表可挂接一个“父作用域”,以支持多层级作用域链上的符号解析。 + * + *

    核心特性包括: *

      - *
    • 每个 {@code SymbolTable} 可拥有一个父作用域,若查找失败可递归向上查找;
    • - *
    • 当前作用域使用内部 {@link Map} 保存定义的符号;
    • - *
    • 提供符号定义与符号解析两种基本操作。
    • + *
    • 符号定义(局部作用域内唯一);
    • + *
    • 符号查找(向上查找父作用域);
    • + *
    • 嵌套作用域支持(通过 parent 引用);
    • + *
    • 可用于语义分析、类型检查、作用域验证等场景。
    • *
    */ public class SymbolTable { - /** 父作用域符号表;若为 null 则表示当前为最外层作用域。 */ + + /** 父作用域引用,若为 {@code null} 则表示为最外层作用域(全局或根作用域) */ private final SymbolTable parent; - /** 当前作用域内定义的符号映射:符号名 -> {@link Symbol}。 */ + /** 当前作用域内定义的符号映射表(符号名 → {@link Symbol}) */ private final Map symbols = new HashMap<>(); /** - * 构造一个新的符号表,并可指定其父作用域以支持嵌套查找。 + * 创建一个新的符号表实例。 * - * @param parent 父作用域的 {@link SymbolTable};若无父作用域,则传入 {@code null} + * @param parent 父作用域符号表,若无父作用域则传入 {@code null} */ public SymbolTable(SymbolTable parent) { this.parent = parent; @@ -32,11 +37,11 @@ public class SymbolTable { /** * 在当前作用域中定义一个新的符号。 *

    - * 如果当前作用域已有同名符号,则定义失败并返回 {@code false}; - * 否则将符号添加到当前作用域并返回 {@code true}。 + * 若符号名称在当前作用域中已存在,则定义失败并返回 {@code false}; + * 否则将该符号添加至当前符号映射中并返回 {@code true}。 * - * @param symbol 要定义的 {@link Symbol} 对象 - * @return {@code true} 如果添加成功;{@code false} 表示名称冲突 + * @param symbol 待定义的符号实体 + * @return 若定义成功则返回 {@code true};否则返回 {@code false} */ public boolean define(Symbol symbol) { if (symbols.containsKey(symbol.name())) { @@ -47,15 +52,16 @@ public class SymbolTable { } /** - * 根据名称解析符号,支持嵌套作用域查找: + * 根据名称解析符号,支持作用域链向上递归查找。 + *

    查找策略: *

      - *
    1. 优先在当前作用域查找;
    2. - *
    3. 若未找到且存在父作用域,则递归在父作用域查找;
    4. - *
    5. 最终未找到则返回 {@code null}。
    6. + *
    7. 优先在当前作用域中查找该符号名称;
    8. + *
    9. 若未找到且存在父作用域,则递归向上查找;
    10. + *
    11. 若所有作用域中均未找到,则返回 {@code null}。
    12. *
    * * @param name 要解析的符号名称 - * @return 对应的 {@link Symbol},或 {@code null}(表示未声明) + * @return 对应的 {@link Symbol} 实例,若未找到则返回 {@code null} */ public Symbol resolve(String name) { Symbol sym = symbols.get(name); diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java index 5c1e07f..0602510 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/BuiltinType.java @@ -1,48 +1,58 @@ package org.jcnc.snow.compiler.semantic.type; /** - * 内置基础类型枚举:支持多种数值类型以及字符串和 void。 + * {@code BuiltinType} 枚举定义了本语言支持的所有内置基础类型。 *

    - * 枚举值: + * 类型涵盖整数、浮点、字符串及 void 类型,广泛应用于变量声明、 + * 表达式类型推导、函数签名、类型检查等语义分析环节。 + * + *

    支持的类型包括: *

      - *
    • {@link #BYTE} – 8 位整数
    • - *
    • {@link #SHORT} – 16 位整数
    • - *
    • {@link #INT} – 32 位整数
    • - *
    • {@link #LONG} – 64 位整数
    • - *
    • {@link #FLOAT} – 单精度浮点数
    • - *
    • {@link #DOUBLE} – 双精度浮点数
    • - *
    • {@link #STRING} – 字符串类型
    • - *
    • {@link #VOID} – 空类型,用于表示无返回值的函数
    • + *
    • {@link #BYTE} - 8 位整数
    • + *
    • {@link #SHORT} - 16 位整数
    • + *
    • {@link #INT} - 32 位整数
    • + *
    • {@link #LONG} - 64 位整数
    • + *
    • {@link #FLOAT} - 单精度浮点数
    • + *
    • {@link #DOUBLE} - 双精度浮点数
    • + *
    • {@link #STRING} - 字符串类型
    • + *
    • {@link #VOID} - 空类型,用于表示无返回值的函数
    • + *
    + * + *

    每个枚举实例实现了 {@link Type} 接口,提供以下语义特性: + *

      + *
    • 数值类型判断 {@link #isNumeric()};
    • + *
    • 类型兼容性判断 {@link #isCompatible(Type)};
    • + *
    • 自动数值宽化支持(通过 {@link Type#widen(Type, Type)} 实现)。
    • *
    - *

    - * 本枚举实现了 {@link Type} 接口,提供了数值宽化和兼容性判断。 */ public enum BuiltinType implements Type { - BYTE, - SHORT, - INT, - LONG, - FLOAT, - DOUBLE, - STRING, - VOID; + + BYTE, // 8 位有符号整数 + SHORT, // 16 位有符号整数 + INT, // 32 位有符号整数(默认整数类型) + LONG, // 64 位有符号整数 + FLOAT, // 单精度浮点数 + DOUBLE, // 双精度浮点数 + STRING, // 字符串类型 + VOID; // 空类型,用于表示函数无返回值 /** - * 判断当前类型是否与另一个类型兼容。 + * 判断当前类型是否与指定类型兼容。 *

    - * 兼容条件: + * 兼容判断规则: *

      - *
    • 完全相同;
    • - *
    • 对于数值类型,允许自动宽化转换(narrow → wide)。
    • + *
    • 类型完全相同,视为兼容;
    • + *
    • 对于数值类型,若目标类型为宽类型(如 int → double),视为兼容;
    • + *
    • 其他情况视为不兼容。
    • *
    * - * @param other 另一个要检查的类型 - * @return 如果兼容返回 true,否则 false + * @param other 要比较的类型 + * @return 若兼容返回 {@code true},否则返回 {@code false} */ @Override public boolean isCompatible(Type other) { if (this == other) return true; - // 如果两者都是数值类型,允许宽化 + // 数值类型间允许自动宽化 if (this.isNumeric() && other.isNumeric()) { return Type.widen(other, this) == this; } @@ -50,9 +60,13 @@ public enum BuiltinType implements Type { } /** - * 判断是否为数值类型(byte、short、int、long、float、double)。 + * 判断当前类型是否为数值类型。 + *

    + * 数值类型包括: + * {@link #BYTE}、{@link #SHORT}、{@link #INT}、{@link #LONG}、 + * {@link #FLOAT}、{@link #DOUBLE}。 * - * @return 如果是数值类型返回 true,否则 false + * @return 若为数值类型返回 {@code true},否则返回 {@code false} */ @Override public boolean isNumeric() { @@ -63,9 +77,11 @@ public enum BuiltinType implements Type { } /** - * 小写形式的类型名称,便于日志和错误输出。 + * 获取当前类型的名称(小写形式)。 + *

    + * 用于日志输出、错误提示等语义描述场景。 * - * @return 小写的类型名称 + * @return 当前类型的名称字符串(如 "int", "string", "void" 等) */ @Override public String toString() { diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java index 0a54057..3eecaa4 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/FunctionType.java @@ -4,26 +4,39 @@ import java.util.List; import java.util.Objects; /** - * 表示函数类型。由参数类型列表和返回类型共同确定。 + * {@code FunctionType} 表示函数的类型信息,由参数类型列表返回类型组成。 *

    - * 例如:一个接受两个 int 参数并返回 string 的函数,其类型描述为 (int, int) -> string。 + * 适用于函数声明、函数调用、类型检查等语义分析场景。 *

    - * 该 record 自动生成了构造方法、访问器、equals、hashCode。 - * 并实现了 {@link Type} 接口,可用于类型兼容性检查和字符串表示。 + * 例如,一个函数接受两个 {@code int} 参数并返回 {@code string},其函数类型为: + *

    + *   (int, int) -> string
    + * 
    * - * @param paramTypes 参数类型列表 - * @param returnType 返回类型 + *

    该类使用 Java 16+ {@code record} 语法定义,自动提供: + *

      + *
    • 构造方法;
    • + *
    • 访问器 {@code paramTypes()} 和 {@code returnType()};
    • + *
    • {@code equals()}, {@code hashCode()}, {@code toString()} 方法;
    • + *
    + * + *

    实现接口:{@link Type} + * + * @param paramTypes 参数类型列表(顺序敏感,不可为 null) + * @param returnType 返回类型(不可为 null) */ public record FunctionType(List paramTypes, Type returnType) implements Type { /** - * 构造函数类型,将参数类型列表包装成不可变列表以保证安全性。 + * 构造函数类型对象。 + *

    + * 参数类型列表将被包装为不可变,以确保函数类型不可修改。 * * @param paramTypes 参数类型列表 * @param returnType 返回类型 */ public FunctionType(List paramTypes, Type returnType) { - this.paramTypes = List.copyOf(paramTypes); + this.paramTypes = List.copyOf(paramTypes); // 确保不可变 this.returnType = returnType; } @@ -32,28 +45,30 @@ public record FunctionType(List paramTypes, Type returnType) implements Ty *

    * 兼容条件: *

      - *
    • 另一对象也为 {@code FunctionType};
    • - *
    • 返回类型兼容;
    • - *
    • 参数类型列表完全相等(顺序和值一致)。
    • + *
    • 对方也是 {@code FunctionType};
    • + *
    • 返回类型兼容(可宽化或完全匹配);
    • + *
    • 参数列表完全相等(类型与顺序严格一致)。
    • *
    * - * @param other 另一个类型 - * @return 如果兼容则返回 {@code true},否则 {@code false} + * @param other 要检查的另一个类型 + * @return 若兼容返回 {@code true},否则 {@code false} */ @Override public boolean isCompatible(Type other) { if (!(other instanceof FunctionType(List types, Type type))) return false; - return returnType.isCompatible(type) - && paramTypes.equals(types); + return returnType.isCompatible(type) && paramTypes.equals(types); } /** - * 返回函数类型的字符串表示。 + * 返回函数类型的标准字符串表示形式。 *

    - * 格式为:(param1, param2, ...) -> returnType, - * 例如 (int, string) -> void。 + * 格式为: + *

    +     *   (param1, param2, ...) -> returnType
    +     * 
    + * 例如 {@code (int, string) -> void} * - * @return 函数类型的描述字符串 + * @return 函数类型的可读性描述 */ @Override public String toString() { @@ -63,28 +78,26 @@ public record FunctionType(List paramTypes, Type returnType) implements Ty /** * 判断两个函数类型是否相等。 *

    - * 相等条件: + * 相等条件为: *

      *
    • 引用相同;
    • - *
    • 或都是 {@code FunctionType},且参数列表和返回类型都相同。
    • + *
    • 或参数类型列表完全相等,且返回类型也相等。
    • *
    * - * @param obj 另一个对象 - * @return 相等返回 {@code true},否则 {@code false} + * @param obj 待比较的对象 + * @return 若相等返回 {@code true},否则 {@code false} */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof FunctionType(List types, Type type))) return false; - return returnType.equals(type) - && paramTypes.equals(types); + return returnType.equals(type) && paramTypes.equals(types); } /** - * 计算函数类型的哈希码。基于参数类型列表和返回类型, - * 与 {@link #equals(Object)} 保持一致。 + * 计算哈希码,确保与 {@link #equals(Object)} 保持一致性。 * - * @return 哈希码 + * @return 哈希值 */ @Override public int hashCode() { diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java index 7b37cad..deecc05 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java @@ -1,4 +1,3 @@ -// File: src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java package org.jcnc.snow.compiler.semantic.type; /** 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 697da7a..24de8ce 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,15 +5,39 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError; import java.util.List; /** - * 用于统一处理语义分析结果的输出与终止逻辑。 + * {@code SemanticAnalysisReporter} 是语义分析阶段的结果报告工具类。 + *

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

    主要职责包括: + *

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

    该类为工具类,禁止实例化,方法均为静态调用。 */ public final class SemanticAnalysisReporter { + + // 禁止实例化 + private SemanticAnalysisReporter() { } + /** - * 根据语义错误列表打印分析结果。 - * 如果存在错误,则打印错误详情并退出程序; - * 如果无错误,则打印成功信息。 + * 打印语义分析结果并在必要时终止程序。 + *

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

      + *
    • 逐条打印错误信息(含位置与描述);
    • + *
    • 使用 {@code System.exit(1)} 退出,表示语义分析失败。
    • + *
    + * 如果错误列表为空: + *
      + *
    • 打印“语义分析通过”提示。
    • + *
    * - * @param errors 语义分析过程中收集到的错误列表 + * @param errors 语义分析阶段收集到的错误列表(允许为 null) */ public static void reportAndExitIfNecessary(List errors) { if (errors != null && !errors.isEmpty()) { @@ -21,7 +45,7 @@ public final class SemanticAnalysisReporter { for (SemanticError error : errors) { System.err.println(" " + error); } - System.exit(1); + System.exit(1); // 非正常退出,阻止后续编译流程 } else { System.out.println("语义分析通过,没有发现错误。"); }