analyzer
+ ) {
exprAnalyzers.put(cls, analyzer);
}
// --- 获取方法 -----------------------------------------------------------
+
+ /**
+ * 根据给定的语句节点实例,返回对应的 StatementAnalyzer。
+ *
+ * 若未注册任何分析器,则返回 null。
+ *
+ * @param stmt 要分析的 StatementNode 实例
+ * @param 具体的 StatementNode 子类型
+ * @return 对应的 StatementAnalyzer,或 null
+ */
@SuppressWarnings("unchecked")
public StatementAnalyzer getStatementAnalyzer(S stmt) {
return (StatementAnalyzer) stmtAnalyzers.get(stmt.getClass());
}
+ /**
+ * 根据给定的表达式节点实例,返回对应的 ExpressionAnalyzer。
+ *
+ * 若未注册任何分析器,则返回 {@link #defaultUnsupported} 兜底。
+ *
+ * @param expr 要分析的 ExpressionNode 实例
+ * @param 具体的 ExpressionNode 子类型
+ * @return 对应的 ExpressionAnalyzer(可能是默认兜底分析器)
+ */
@SuppressWarnings("unchecked")
public ExpressionAnalyzer getExpressionAnalyzer(E expr) {
- return (ExpressionAnalyzer) exprAnalyzers.get(expr.getClass());
+ return (ExpressionAnalyzer)
+ exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/ExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/ExpressionAnalyzer.java
deleted file mode 100644
index 9b0c7c6..0000000
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/ExpressionAnalyzer.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.jcnc.snow.compiler.semantic.analyzers;
-
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
-import org.jcnc.snow.compiler.semantic.core.Context;
-import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
-import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
-import org.jcnc.snow.compiler.semantic.type.Type;
-
-/**
- * 表达式分析器接口。
- */
-public interface ExpressionAnalyzer {
- Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, E expr);
-}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/StatementAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/StatementAnalyzer.java
deleted file mode 100644
index 624d568..0000000
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/StatementAnalyzer.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.jcnc.snow.compiler.semantic.analyzers;
-
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
-import org.jcnc.snow.compiler.semantic.core.Context;
-import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
-import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
-
-/**
- * 语句分析器接口。
- */
-public interface StatementAnalyzer {
- void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, S stmt);
-}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/ExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/ExpressionAnalyzer.java
new file mode 100644
index 0000000..df49049
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/ExpressionAnalyzer.java
@@ -0,0 +1,44 @@
+package org.jcnc.snow.compiler.semantic.analyzers.base;
+
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
+import org.jcnc.snow.compiler.semantic.core.Context;
+import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
+import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
+import org.jcnc.snow.compiler.semantic.type.Type;
+
+/**
+ * 表达式分析器接口:定义了对 AST 中表达式节点进行语义分析的通用契约。
+ *
+ * 各种具体的表达式分析器(如调用、二元运算、标识符、字面量等)需实现此接口,
+ * 在 {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, ExpressionNode)}
+ * 方法中完成类型推导、语义检查,并将发现的错误记录到上下文中。
+ *
+ * @param 要分析的具体表达式节点类型,必须是 {@link ExpressionNode} 的子类型
+ */
+public interface ExpressionAnalyzer {
+
+ /**
+ * 对给定的表达式节点进行语义分析,并返回推导出的类型。
+ *
+ * 实现者应在分析过程中根据节点语义:
+ *
+ * - 校验子表达式类型并递归调用对应的分析器;
+ * - 检查函数调用、运算符合法性;
+ * - 必要时向 {@link Context#getErrors()} 添加 {@link org.jcnc.snow.compiler.semantic.error.SemanticError};
+ * - 返回最终推导出的 {@link Type},以供上层表达式或语句分析使用。
+ *
+ *
+ * @param ctx 全局上下文,提供模块注册表、错误收集、日志输出及分析器注册表等
+ * @param mi 当前模块信息,用于跨模块调用和函数签名查找
+ * @param fn 当前函数节点,可用于返回类型校验或其他函数级上下文
+ * @param locals 当前作用域的符号表,包含已声明的变量及其类型
+ * @param expr 待分析的表达式节点
+ * @return 表达式的推导类型,用于后续类型兼容性检查和类型传播
+ */
+ Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ E expr);
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/StatementAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/StatementAnalyzer.java
new file mode 100644
index 0000000..7e9d1ca
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/base/StatementAnalyzer.java
@@ -0,0 +1,40 @@
+package org.jcnc.snow.compiler.semantic.analyzers.base;
+
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
+import org.jcnc.snow.compiler.semantic.core.Context;
+import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
+import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
+
+/**
+ * 语句分析器接口:定义如何对 AST 中的语句节点执行语义检查。
+ *
+ * 各具体的语句分析器(如声明、赋值、分支、循环、返回等)需实现此接口,
+ * 在 {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, StatementNode)}
+ * 方法中完成:
+ *
+ * - 对自身语义结构进行校验(变量声明、类型匹配、作用域检查等);
+ * - 递归调用其他已注册的语句或表达式分析器;
+ * - 在发现错误时,通过 {@link Context#getErrors()} 记录 {@link org.jcnc.snow.compiler.semantic.error.SemanticError};
+ * - 在上下文需要时记录日志以辅助调试。
+ *
+ *
+ * @param 要分析的具体语句节点类型,必须是 {@link StatementNode} 的子类型
+ */
+public interface StatementAnalyzer {
+
+ /**
+ * 对给定的语句节点执行语义分析。
+ *
+ * @param ctx 全局上下文,提供模块注册表、错误收集、日志输出及分析器注册表等
+ * @param mi 当前模块信息,用于检查模块导入和函数签名等上下文
+ * @param fn 当前函数节点,可用于检查返回类型或函数级别作用域
+ * @param locals 当前作用域的符号表,包含已声明的变量及其类型
+ * @param stmt 待分析的语句节点实例
+ */
+ void analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ S stmt);
+}
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 f8f3bad..6513427 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -10,19 +10,57 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 二元表达式分析器:负责对形如 left op right 的表达式进行类型推导和类型检查。
+ *
+ * 支持以下运算符:
+ *
+ * +:如果任一操作数为 {@link BuiltinType#STRING},则结果为字符串拼接,否则如果都是整数则结果为整数
+ * -, *, /, %:要求两个操作数都是整数,结果为整数
+ * <, <=, >, >=, ==, !=:
+ * 要求两个操作数都是整数,结果为整数(表示真假)
+ *
+ * 对于未知运算符或类型不匹配的情况,会在 {@link Context#getErrors() 错误列表} 中加入对应的
+ * {@link SemanticError},并将结果类型降级为 {@link BuiltinType#INT} 以避免后续连锁错误。
+ */
public class BinaryExpressionAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 对给定的二元表达式节点进行语义分析,返回推导出的类型。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志开关、注册表等
+ * @param mi 当前正在分析的模块信息,用于查找导入、函数签名等
+ * @param fn 当前正在分析的函数节点,用于处理返回类型检查等(可用于更复杂场景)
+ * @param locals 当前作用域的符号表,包含已定义的局部变量及其类型
+ * @param bin 待分析的 {@link BinaryExpressionNode} 节点,包含左子表达式、运算符、右子表达式
+ * @return 推导出的 {@link Type}:
+ *
+ * - 符合运算规则时返回 {@link BuiltinType#INT} 或 {@link BuiltinType#STRING}
+ * - 类型不匹配或未知运算符时降级返回 {@link BuiltinType#INT}
+ *
+ */
@Override
- public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, BinaryExpressionNode bin) {
+ public Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ BinaryExpressionNode bin) {
ctx.log("检查二元表达式: " + bin.operator());
- var leftA = ctx.getRegistry().getExpressionAnalyzer(bin.left());
- Type left = leftA.analyze(ctx, mi, fn, locals, bin.left());
- var rightA = ctx.getRegistry().getExpressionAnalyzer(bin.right());
- Type right = rightA.analyze(ctx, mi, fn, locals, bin.right());
+
+ // 递归分析左右两边的子表达式
+ var leftAnalyzer = ctx.getRegistry().getExpressionAnalyzer(bin.left());
+ Type left = leftAnalyzer.analyze(ctx, mi, fn, locals, bin.left());
+
+ var rightAnalyzer = ctx.getRegistry().getExpressionAnalyzer(bin.right());
+ Type right = rightAnalyzer.analyze(ctx, mi, fn, locals, bin.right());
String op = bin.operator();
Type result;
+
+ // 根据运算符做类型推导
switch (op) {
case "+" -> {
+ // 字符串拼接 或 整数相加
if (left == BuiltinType.STRING || right == BuiltinType.STRING) {
result = BuiltinType.STRING;
} else if (left == BuiltinType.INT && right == BuiltinType.INT) {
@@ -32,6 +70,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer {
+ // 数学运算,要求整数操作数
if (left == BuiltinType.INT && right == BuiltinType.INT) {
result = BuiltinType.INT;
} else {
@@ -39,6 +78,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer", ">=", "==", "!=" -> {
+ // 比较运算,要求整数操作数,返回整数表示真假
if (left == BuiltinType.INT && right == BuiltinType.INT) {
result = BuiltinType.INT;
} else {
@@ -46,19 +86,25 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer {
+ // 未知运算符
ctx.getErrors().add(new SemanticError(bin, "未知运算符: " + op));
ctx.log("错误: 未知运算符 " + op);
return BuiltinType.INT;
}
}
+ // 如果推导失败(类型不匹配),记录错误并降级
if (result == null) {
- ctx.getErrors().add(new SemanticError(bin, String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right)));
+ ctx.getErrors().add(new SemanticError(
+ bin,
+ String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right))
+ );
ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right);
result = BuiltinType.INT;
} else {
ctx.log("二元表达式推导类型: " + result);
}
+
return result;
}
}
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 0cefbf9..cf4c6bd 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
@@ -5,7 +5,7 @@ import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
-import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -17,62 +17,126 @@ import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.ArrayList;
import java.util.List;
+/**
+ * 函数调用表达式分析器:负责对 callee(arg1, arg2, ...) 形式的调用进行
+ * 语义检查和类型推导。
+ *
+ * 支持两种调用方式:
+ *
+ * - 模块函数调用:
ModuleName.func(...),要求模块已注册且已导入,或与当前模块同名
+ * - 本模块函数调用:
func(...)
+ *
+ *
+ * 分析内容包括:
+ *
+ * - 解析目标模块与函数名;
+ * - 检查函数是否已定义;
+ * - 递归分析并收集所有实参的类型;
+ * - 检查参数个数与参数类型是否与函数签名匹配;
+ * - 返回函数签名中的返回类型;
+ * - 在任何错误(未知模块、未导入模块、调用方式不支持、函数未定义、
+ * 参数数量或类型不匹配)时,记录对应的 {@link SemanticError},
+ * 并降级返回 {@link BuiltinType#INT} 以避免后续连锁错误。
+ *
+ */
public class CallExpressionAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 对给定的调用表达式节点进行语义分析,返回推导出的类型。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志输出、分析器注册表等
+ * @param mi 当前正在分析的模块信息,用于查找导入列表和函数签名
+ * @param fn 当前正在分析的函数节点,可用于更复杂的上下文校验
+ * @param locals 当前作用域的符号表,包含已定义的变量及其类型
+ * @param call 待分析的 {@link CallExpressionNode},包含被调用的表达式和实参列表
+ * @return 推导出的函数返回类型;若发生任何错误,则降级返回 {@link BuiltinType#INT}
+ */
@Override
- public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, CallExpressionNode call) {
+ public Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ CallExpressionNode call) {
ctx.log("检查函数调用: " + call.callee());
ModuleInfo target = mi;
String functionName;
ExpressionNode callee = call.callee();
+ // 处理模块限定调用:ModuleName.func(...)
if (callee instanceof MemberExpressionNode(var object, String member)) {
- if (object instanceof IdentifierNode id) {
- String moduleName = id.name();
- if (!ctx.getModules().containsKey(moduleName) ||
- (!mi.getImports().contains(moduleName) && !mi.getName().equals(moduleName))) {
- ctx.getErrors().add(new SemanticError(callee, "未知或未导入模块: " + moduleName));
- ctx.log("错误: 未导入模块 " + moduleName);
+ if (object instanceof IdentifierNode(String name)) {
+ if (!ctx.getModules().containsKey(name)
+ || (!mi.getImports().contains(name) && !mi.getName().equals(name))) {
+ ctx.getErrors().add(new SemanticError(callee,
+ "未知或未导入模块: " + name));
+ ctx.log("错误: 未导入模块 " + name);
return BuiltinType.INT;
}
- target = ctx.getModules().get(moduleName);
+ target = ctx.getModules().get(name);
functionName = member;
- ctx.log("调用模块函数: " + moduleName + "." + member);
+ ctx.log("调用模块函数: " + name + "." + member);
} else {
- ctx.getErrors().add(new SemanticError(callee, "不支持的调用方式: " + callee));
+ ctx.getErrors().add(new SemanticError(callee,
+ "不支持的调用方式: " + callee));
ctx.log("错误: 不支持的调用方式 " + callee);
return BuiltinType.INT;
}
- } else if (callee instanceof IdentifierNode id) {
- functionName = id.name();
+
+ // 处理本模块函数调用:func(...)
+ } else if (callee instanceof IdentifierNode(String name)) {
+ functionName = name;
ctx.log("调用当前模块函数: " + functionName);
} else {
- ctx.getErrors().add(new SemanticError(callee, "不支持的调用方式: " + callee));
+ ctx.getErrors().add(new SemanticError(callee,
+ "不支持的调用方式: " + callee));
ctx.log("错误: 不支持的调用方式 " + callee);
return BuiltinType.INT;
}
+ // 查找函数签名
FunctionType ft = target.getFunctions().get(functionName);
if (ft == null) {
- ctx.getErrors().add(new SemanticError(callee, "函数未定义: " + functionName));
+ ctx.getErrors().add(new SemanticError(callee,
+ "函数未定义: " + functionName));
ctx.log("错误: 函数未定义 " + functionName);
return BuiltinType.INT;
}
+ // 分析所有实参并收集类型
List args = new ArrayList<>();
for (var argExpr : call.arguments()) {
var argAnalyzer = ctx.getRegistry().getExpressionAnalyzer(argExpr);
args.add(argAnalyzer.analyze(ctx, mi, fn, locals, argExpr));
}
+ // 参数数量检查
if (args.size() != ft.paramTypes().size()) {
- ctx.getErrors().add(new SemanticError(call, "参数数量不匹配: 期望 " + ft.paramTypes().size() + " 个, 实际 " + args.size() + " 个"));
- ctx.log("错误: 参数数量不匹配: 期望 " + ft.paramTypes().size() + ", 实际 " + args.size());
+ ctx.getErrors().add(new SemanticError(call,
+ "参数数量不匹配: 期望 "
+ + ft.paramTypes().size()
+ + " 个, 实际 "
+ + args.size()
+ + " 个"));
+ ctx.log("错误: 参数数量不匹配: 期望 "
+ + ft.paramTypes().size()
+ + ", 实际 "
+ + args.size());
} else {
+ // 参数类型检查
for (int i = 0; i < args.size(); i++) {
- if (!ft.paramTypes().get(i).isCompatible(args.get(i))) {
- ctx.getErrors().add(new SemanticError(call, String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s", i, ft.paramTypes().get(i), args.get(i))));
- ctx.log("错误: 参数类型不匹配 (位置 " + i + "): 期望 " + ft.paramTypes().get(i) + ", 实际 " + args.get(i));
+ Type expected = ft.paramTypes().get(i);
+ Type actual = args.get(i);
+ if (!expected.isCompatible(actual)) {
+ ctx.getErrors().add(new SemanticError(call,
+ String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
+ i, expected, actual)));
+ ctx.log("错误: 参数类型不匹配 (位置 "
+ + i
+ + "): 期望 "
+ + expected
+ + ", 实际 "
+ + actual);
}
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java
index e0c7f47..08cc087 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IdentifierAnalyzer.java
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
-import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -11,15 +11,44 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 标识符表达式分析器:负责对变量或常量等标识符节点进行语义检查,
+ * 并返回其在当前作用域中的类型。
+ *
+ * 如果未在符号表中找到对应的符号,将向错误列表中添加一条
+ * {@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}
+ */
@Override
- public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IdentifierNode id) {
+ public Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ IdentifierNode id) {
+
+ // 查找当前作用域下的符号
Symbol sym = locals.resolve(id.name());
if (sym == null) {
- ctx.getErrors().add(new SemanticError(id, "未声明的标识符: " + id.name()));
+ // 未声明:记录错误并降级
+ 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 57e4ffd..148d6fa 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
@@ -1,17 +1,38 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
-import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 数字字面量分析器:对 {@link NumberLiteralNode} 节点进行类型推导。
+ *
+ * 在本语言中,所有数字字面量均被视为整型,因此直接返回 {@link BuiltinType#INT}。
+ * 本分析器不会产生任何语义错误。
+ */
public class NumberLiteralAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 对数字字面量节点进行语义分析。
+ *
+ * @param ctx 全局上下文,包含模块信息、错误收集、日志输出和分析器注册表等
+ * @param mi 当前模块信息(此分析器不涉及模块间调用,参数未使用)
+ * @param fn 当前函数节点(此分析器不依赖函数上下文,参数未使用)
+ * @param locals 当前作用域的符号表(数字字面量不依赖符号表,参数未使用)
+ * @param expr 待分析的 {@link NumberLiteralNode},表示一个数字字面量
+ * @return 固定返回 {@link BuiltinType#INT}
+ */
@Override
- public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, NumberLiteralNode expr) {
+ public Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ NumberLiteralNode expr) {
return BuiltinType.INT;
}
}
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 13cefeb..34d2793 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
@@ -1,17 +1,38 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.StringLiteralNode;
-import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 字符串字面量分析器:对 {@link StringLiteralNode} 节点进行类型推导。
+ *
+ * 在本语言中,所有字符串字面量均被视为字符串类型,因此直接返回 {@link BuiltinType#STRING}。
+ * 本分析器不会产生任何语义错误。
+ */
public class StringLiteralAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 对字符串字面量节点进行语义分析。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等(此分析器不使用上下文)
+ * @param mi 当前模块信息(此分析器不涉及模块调用,参数未使用)
+ * @param fn 当前函数节点(此分析器不依赖函数上下文,参数未使用)
+ * @param locals 当前作用域的符号表(字符串字面量不依赖符号表,参数未使用)
+ * @param expr 待分析的 {@link StringLiteralNode},表示一个字符串字面量
+ * @return 固定返回 {@link BuiltinType#STRING}
+ */
@Override
- public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, StringLiteralNode expr) {
+ public Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ StringLiteralNode expr) {
return BuiltinType.STRING;
}
}
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 906aedb..4dd427a 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
-import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -10,18 +10,41 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
-// 之前:implements ExpressionAnalyzer
+/**
+ * 默认兜底表达式分析器:用于处理所有未显式注册的 {@link ExpressionNode} 子类型。
+ *
+ * 当遇到不支持或未实现的表达式节点时,本分析器会:
+ *
+ * - 向 {@link Context#getErrors() 错误列表} 中添加一条 {@link SemanticError},
+ * - 在日志中输出对应的错误信息,
+ * - 并返回降级类型 {@link BuiltinType#INT} 以避免后续连锁报错。
+ *
+ *
+ * @param 任何 {@link ExpressionNode} 子类型
+ */
public class UnsupportedExpressionAnalyzer
implements ExpressionAnalyzer {
+ /**
+ * 对未知或不支持的表达式节点进行处理。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等
+ * @param mi 当前模块信息(此分析器不使用模块上下文,参数可忽略)
+ * @param fn 当前函数节点(此分析器不使用函数上下文,参数可忽略)
+ * @param locals 当前作用域的符号表(此分析器不依赖局部符号,参数可忽略)
+ * @param expr 待分析的表达式节点,类型为任意 {@link ExpressionNode} 子类型
+ * @return 固定返回 {@link BuiltinType#INT},作为错误降级类型
+ */
@Override
public Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
E expr) {
- ctx.getErrors().add(new SemanticError(expr,
- "不支持的表达式类型: " + 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 2c9f30d..93c6c44 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.AssignmentNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -11,20 +11,58 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 赋值语句分析器:负责对 {@link AssignmentNode} 节点进行语义检查。
+ *
+ * 分析步骤:
+ *
+ * - 检查目标变量是否已在当前作用域中声明且种类为 {@link SymbolKind#VARIABLE};
+ * - 递归分析赋值右侧表达式,获取其类型;
+ * - 检查右侧表达式类型是否与目标变量类型兼容;
+ * - 在任一检查失败时,向 {@link Context#getErrors() 错误列表} 中添加对应的
+ * {@link SemanticError} 并记录日志。
+ *
+ */
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) {
+ public void analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ AssignmentNode asg) {
ctx.log("赋值检查: " + asg.variable());
+
+ // 1. 检查变量声明
Symbol sym = locals.resolve(asg.variable());
if (sym == null || sym.kind() != SymbolKind.VARIABLE) {
- ctx.getErrors().add(new SemanticError(asg, "未声明的变量: " + asg.variable()));
+ ctx.getErrors().add(new SemanticError(
+ asg,
+ "未声明的变量: " + asg.variable()
+ ));
ctx.log("错误: 未声明的变量 " + asg.variable());
return;
}
+
+ // 2. 分析赋值表达式
var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(asg.value());
Type valType = exprAnalyzer.analyze(ctx, mi, fn, locals, asg.value());
+
+ // 3. 类型兼容性检查
if (!sym.type().isCompatible(valType)) {
- ctx.getErrors().add(new SemanticError(asg, "赋值类型不匹配: 期望 " + sym.type() + ", 实际 " + valType));
+ ctx.getErrors().add(new SemanticError(
+ asg,
+ "赋值类型不匹配: 期望 " + 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 91c25a0..c0fb86e 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -12,29 +12,79 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 变量声明语句分析器:负责对 {@link DeclarationNode} 节点进行语义检查。
+ *
+ * 分析流程:
+ *
+ * - 解析声明类型,如果未知则记录错误并降级为 {@link BuiltinType#INT};
+ * - 在当前符号表中注册新变量,若名称已存在则记录重复声明错误;
+ * - 如存在初始化表达式,递归分析表达式并检查其类型与声明类型的兼容性;
+ * - 兼容性检查失败时记录类型不匹配错误。
+ *
+ */
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) {
+ 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()));
- ctx.log("错误: 参数未知类型 " + decl.getType() + " 在声明 " + decl.getName());
+ ctx.getErrors().add(new SemanticError(
+ decl,
+ "未知类型: " + decl.getType()
+ ));
+ ctx.log("错误: 参数未知类型 " + decl.getType()
+ + " 在声明 " + decl.getName());
varType = BuiltinType.INT;
}
- ctx.log("声明变量: " + decl.getName() + " 类型: " + varType);
+ ctx.log("声明变量: " + decl.getName()
+ + " 类型: " + varType);
- if (!locals.define(new Symbol(decl.getName(), varType, SymbolKind.VARIABLE))) {
- ctx.getErrors().add(new SemanticError(decl, "变量重复声明: " + decl.getName()));
+ // 2. 注册新变量,检查重复声明
+ if (!locals.define(new Symbol(
+ decl.getName(),
+ varType,
+ SymbolKind.VARIABLE
+ ))) {
+ ctx.getErrors().add(new SemanticError(
+ decl,
+ "变量重复声明: " + decl.getName()
+ ));
ctx.log("错误: 变量重复声明 " + decl.getName());
}
- Type finalVarType = varType;
+ // 3. 初始化表达式类型检查
+ Type declaredType = varType;
decl.getInitializer().ifPresent(init -> {
- var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(init);
- Type initType = exprAnalyzer.analyze(ctx, mi, fn, locals, init);
- if (!finalVarType.isCompatible(initType)) {
- ctx.getErrors().add(new SemanticError(decl, "初始化类型不匹配: 期望 " + finalVarType + ", 实际 " + initType));
- ctx.log("错误: 初始化类型不匹配 " + decl.getName());
+ var exprAnalyzer = ctx.getRegistry()
+ .getExpressionAnalyzer(init);
+ Type initType = exprAnalyzer.analyze(
+ ctx, mi, fn, locals, init
+ );
+ if (!declaredType.isCompatible(initType)) {
+ ctx.getErrors().add(new SemanticError(
+ decl,
+ "初始化类型不匹配: 期望 "
+ + declaredType
+ + ", 实际 "
+ + initType
+ ));
+ ctx.log("错误: 初始化类型不匹配 "
+ + 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 b7888fa..2d1ad76 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IfNode;
-import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -10,23 +10,60 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * If 语句分析器:负责对 {@link IfNode} 节点进行语义检查。
+ *
+ * 分析流程:
+ *
+ * - 对条件表达式进行类型推导,要求类型为 {@link BuiltinType#INT};
+ * - 若条件类型不符,记录错误并降级继续分析;
+ * - 分别递归分析 then 分支和 else 分支中的每个子语句;
+ * - 不支持的子语句类型将由注册表中未匹配到的分析器处理(或被记录为错误)。
+ *
+ */
public class IfAnalyzer implements StatementAnalyzer {
+
+ /**
+ * 对给定的 if 节点进行语义分析。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等
+ * @param mi 当前模块信息,用于跨模块调用检查(此分析器未直接使用)
+ * @param fn 当前函数节点,可用于更复杂的上下文校验(此分析器未直接使用)
+ * @param locals 当前作用域的符号表,包含已定义变量及其类型
+ * @param ifn 待分析的 {@link IfNode},包含条件表达式、then 分支和 else 分支
+ */
@Override
- public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IfNode ifn) {
+ public void analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ IfNode ifn) {
+ // 1. 条件表达式类型检查
ctx.log("检查 if 条件");
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.getErrors().add(new SemanticError(
+ ifn,
+ "if 条件必须为 int 类型(表示真假)"
+ ));
ctx.log("错误: if 条件类型不为 int");
}
- ifn.thenBranch().forEach(stmt -> {
+
+ // 2. 递归分析 then 分支
+ for (var stmt : ifn.thenBranch()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
- if (stAnalyzer != null) stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
- });
- ifn.elseBranch().forEach(stmt -> {
+ if (stAnalyzer != null) {
+ stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
+ }
+ }
+
+ // 3. 递归分析 else 分支
+ for (var stmt : ifn.elseBranch()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
- if (stAnalyzer != null) stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
- });
+ if (stAnalyzer != null) {
+ stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
+ }
+ }
}
}
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 ea2d39e..de67823 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.LoopNode;
-import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -10,25 +10,66 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * 循环语句分析器:负责对 {@link LoopNode} 节点进行语义检查。
+ *
+ * 分析流程:
+ *
+ * - 分析并执行初始化语句(initializer);
+ * - 对循环条件表达式进行类型推导,要求为 {@link BuiltinType#INT};
+ * - 分析并执行更新语句(update);
+ * - 递归分析循环体(body)中的每个子语句;
+ * - 在任何步骤发现类型或语义错误时,都会向 {@link Context#getErrors() 错误列表} 中添加对应的
+ * {@link SemanticError} 并在日志中记录。
+ *
+ */
public class LoopAnalyzer implements StatementAnalyzer {
+
+ /**
+ * 对给定的循环节点进行语义分析。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等
+ * @param mi 当前模块信息,用于跨模块调用检查(此分析器未直接使用)
+ * @param fn 当前函数节点,可用于更复杂的上下文校验(此分析器未直接使用)
+ * @param locals 当前作用域的符号表,包含已定义变量及其类型
+ * @param ln 待分析的 {@link LoopNode},包含 initializer、condition、update 和 body
+ */
@Override
- public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, LoopNode ln) {
+ public void analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ LoopNode ln) {
+ // 1. 初始化语句
ctx.log("检查 loop 循环");
var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer());
- if (initAnalyzer != null) initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer());
+ if (initAnalyzer != null) {
+ initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer());
+ }
+ // 2. 条件表达式类型检查
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.getErrors().add(new SemanticError(
+ ln,
+ "loop 条件必须为 int 类型(表示真假)"
+ ));
ctx.log("错误: loop 条件类型不为 int");
}
- var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update());
- if (updateAnalyzer != null) updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update());
- ln.body().forEach(stmt -> {
+ // 3. 更新语句
+ var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update());
+ if (updateAnalyzer != null) {
+ updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update());
+ }
+
+ // 4. 循环体语句
+ for (var stmt : ln.body()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
- if (stAnalyzer != null) stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
- });
+ if (stAnalyzer != null) {
+ stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
+ }
+ }
}
}
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 afcd94a..570842c 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
@@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
-import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer;
+import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
@@ -11,23 +11,67 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.FunctionType;
import org.jcnc.snow.compiler.semantic.type.Type;
+/**
+ * Return 语句分析器:负责对 {@link ReturnNode} 节点进行语义检查。
+ *
+ * 分析过程包括:
+ *
+ * - 获取当前函数的预期返回类型;
+ * - 如果存在返回表达式,则递归分析其类型并与预期类型进行兼容性检查;
+ * - 如果没有返回表达式且预期类型不是 {@link BuiltinType#VOID},则记录缺少返回值的错误;
+ * - 遇到类型不兼容时,会向 {@link Context#getErrors() 错误列表} 中添加 {@link SemanticError},
+ * - 所有错误同时会记录到日志以便调试。
+ *
+ */
public class ReturnAnalyzer implements StatementAnalyzer {
+
+ /**
+ * 对给定的 return 节点进行语义分析。
+ *
+ * @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等
+ * @param mi 当前模块信息,用于查找函数签名和返回类型
+ * @param fn 当前正在分析的函数节点,包含函数名和参数列表
+ * @param locals 当前作用域的符号表(此分析器不使用局部符号表)
+ * @param ret 待分析的 {@link ReturnNode},可能包含一个返回表达式
+ */
@Override
- public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, ReturnNode ret) {
+ public void analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ ReturnNode ret) {
ctx.log("检查 return");
- FunctionType expected = ctx.getModules().get(mi.getName()).getFunctions().get(fn.name());
+
+ // 查找函数的预期返回类型
+ FunctionType expected = ctx.getModules()
+ .get(mi.getName())
+ .getFunctions()
+ .get(fn.name());
+
+ // 如果有返回表达式,则检查其类型
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 类型不匹配");
+ }
+ },
+ // 如果没有返回表达式,但函数应有返回值
+ () -> {
+ 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/Context.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
index a5f0436..04e389a 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
@@ -1,20 +1,33 @@
package org.jcnc.snow.compiler.semantic.core;
-import org.jcnc.snow.compiler.semantic.type.Type;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
+import org.jcnc.snow.compiler.semantic.type.Type;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
/**
- * 公共上下文对象,贯穿整个语义分析过程。
+ * 语义分析公共上下文:在整个分析流程中共享,用于存储模块信息、收集错误、控制日志,以及管理分析器注册表。
*/
public class Context {
+ /** 所有已注册模块名 -> 模块信息 的映射 */
private final Map modules;
+ /** 语义分析过程中收集的所有错误列表 */
private final List errors;
+ /** 是否启用详细日志输出 */
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}
+ */
public Context(Map modules,
List errors,
boolean verbose,
@@ -25,26 +38,56 @@ public class Context {
this.registry = registry;
}
+ /**
+ * 获取所有模块信息的映射。
+ *
+ * @return 模块名到 {@link ModuleInfo} 的 Map
+ */
public Map getModules() {
return modules;
}
+ /**
+ * 获取语义错误列表。
+ * 分析过程中产生的 {@link SemanticError} 会被收集到此列表。
+ *
+ * @return 语义错误列表
+ */
public List getErrors() {
return errors;
}
+ /**
+ * 获取分析器注册表。
+ * 可通过此注册表按 AST 节点类型分发对应的语句或表达式分析器。
+ *
+ * @return {@link AnalyzerRegistry} 实例
+ */
public AnalyzerRegistry getRegistry() {
return registry;
}
+ /**
+ * 打印日志信息,仅在 {@code verbose} 为 true 时输出到标准输出。
+ *
+ * @param msg 要打印的日志内容
+ */
public void log(String msg) {
if (verbose) {
System.out.println("[SemanticAnalyzer] " + msg);
}
}
- /** 根据名称解析为内置类型,未知则返回 null */
+ /**
+ * 将类型名解析为内置类型 {@link Type} 实例。
+ *
+ * 若名称在 {@link SemanticAnalyzer#BUILTIN_TYPES} 中存在,则返回对应类型;
+ * 否则返回 {@code null},调用方可据此决定降级为默认类型并记录错误。
+ *
+ * @param name 类型名称(如 "int", "string", "void")
+ * @return 对应的 {@link Type},或 {@code null} 表示未知类型
+ */
public Type parseType(String name) {
- return SemanticAnalyzer.BUILTIN_TYPES.get(name);
+ return org.jcnc.snow.compiler.semantic.core.SemanticAnalyzer.BUILTIN_TYPES.get(name);
}
}
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 d1107a1..650e2d0 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
@@ -8,29 +8,28 @@ import java.util.Map;
import java.util.Set;
/**
- * 保存模块级别的信息。
+ * 模块信息:保存单个模块在语义分析阶段的元数据。
*
- * 模块信息包括:
+ * 包含以下内容:
*
- * - 模块名(唯一标识一个模块)。
- * - 导入的其他模块名称集合。
- * - 模块中定义的函数签名映射(函数名到 {@link FunctionType} 的对应关系)。
+ * - 模块名称(唯一标识);
+ * - 该模块导入的其他模块名称集合;
+ * - 该模块中定义的函数签名映射,将函数名映射到对应的 {@link FunctionType}。
*
- *
- * 主要用于语义分析阶段,帮助管理模块之间的依赖关系和函数声明信息。
+ * 在语义分析过程中,用于管理模块依赖关系和查找函数签名。
*/
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;
@@ -39,30 +38,30 @@ public class ModuleInfo {
/**
* 获取模块名称。
*
- * @return 模块名称
+ * @return 模块的唯一名称
*/
public String getName() {
return name;
}
/**
- * 获取导入的模块名称集合。
+ * 获取该模块导入的其他模块名称集合。
*
- * 返回的是内部集合的直接引用,可以通过它进行增删操作。
+ * 返回的 Set 为内部对象的直接引用,可对其进行添加或移除操作,以维护导入列表。
*
- * @return 导入模块名称的集合
+ * @return 导入模块名称的可变集合
*/
public Set getImports() {
return imports;
}
/**
- * 获取模块中定义的函数映射表。
+ * 获取模块中定义的函数签名映射表。
*
- * 返回的是函数名到函数类型 {@link FunctionType} 的映射表。
- * 返回的是内部映射的直接引用,可以通过它进行增删操作。
+ * 键为函数名,值为对应的 {@link FunctionType}。
+ * 返回的 Map 为内部对象的直接引用,可对其进行添加或移除操作,以维护函数签名列表。
*
- * @return 函数映射表
+ * @return 函数签名映射表
*/
public Map getFunctions() {
return functions;
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 bc32630..b4dcdd0 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
@@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.*;
+import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.analyzers.expression.*;
@@ -15,100 +16,157 @@ import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.*;
/**
- * 重构后的语义分析器,采用注册表 + 分析器组合的分而治之设计。
+ * 重构后的语义分析器,采用“注册表 + 分析器”组合的分而治之设计。
+ *
+ * 主要职责:
+ *
+ * - 初始化内置模块与类型映射;
+ * - 注册用户模块及其函数签名与导入关系;
+ * - 使用 StatementAnalyzer 与 ExpressionAnalyzer 进行递归语义检查;
+ * - 收集并返回所有发现的语义错误。
+ *
*/
public class SemanticAnalyzer {
/**
- * 内置类型映射表
+ * 内置类型映射表:将类型名称映射到对应的 {@link Type} 实例。
*/
- public static final Map BUILTIN_TYPES = Map.of("int", BuiltinType.INT, "string", BuiltinType.STRING, "void", BuiltinType.VOID);
+ public static final Map BUILTIN_TYPES = Map.of(
+ "int", BuiltinType.INT,
+ "string", BuiltinType.STRING,
+ "void", BuiltinType.VOID
+ );
- // ---------------------------------------------------------------------
+ /** 所有已注册模块名称 -> {@link ModuleInfo} 的映射 */
private final Map modules = new HashMap<>();
+ /** 收集语义分析过程中产生的所有错误 */
private final List errors = new ArrayList<>();
+ /** 是否启用详细日志输出 */
private final boolean verbose;
+ /** 分发语句与表达式分析器的注册表 */
private final AnalyzerRegistry registry = new AnalyzerRegistry();
+ /** 全局上下文对象,封装 modules、errors、verbose 与 registry */
private final Context ctx;
+ /**
+ * 使用默认(关闭日志)构造语义分析器。
+ */
public SemanticAnalyzer() {
this(false);
}
+ /**
+ * 构造语义分析器。
+ *
+ * @param verbose 是否启用详细日志输出
+ */
public SemanticAnalyzer(boolean verbose) {
this.verbose = verbose;
this.ctx = new Context(modules, errors, verbose, registry);
registerAnalyzers();
}
- // ---------------------------------------------------------------------
+ /**
+ * 注册所有语句与表达式分析器到 {@link AnalyzerRegistry}。
+ *
+ * 包括:声明、赋值、分支、循环、返回、表达式语句以及
+ * 数字、字符串、标识符、调用、二元、兜底表达式分析器。
+ */
private void registerAnalyzers() {
- // 语句
+ // 语句分析器
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(ExpressionStatementNode.class, (c, mi, fn, locals, stmt) -> {
- var a = c.getRegistry().getExpressionAnalyzer(stmt.expression());
- a.analyze(c, mi, fn, locals, stmt.expression());
- });
+ 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(ExpressionStatementNode.class,
+ (c, mi, fn, locals, stmt) -> {
+ var exprAn = c.getRegistry().getExpressionAnalyzer(stmt.expression());
+ exprAn.analyze(c, 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(IdentifierNode.class, new IdentifierAnalyzer());
+ registry.registerExpressionAnalyzer(CallExpressionNode.class,new CallExpressionAnalyzer());
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
- // 兜底处理
- // 兜底:所有暂未实现的表达式都用 UnsupportedExpressionAnalyzer 处理
+ // 默认兜底
registry.registerExpressionAnalyzer(MemberExpressionNode.class, new UnsupportedExpressionAnalyzer<>());
}
- // ---------------------------------------------------------------------
+ /**
+ * 对给定的模块 AST 列表执行完整的语义分析。
+ *
+ * @param moduleNodes 所有待分析的 {@link ModuleNode} 列表
+ * @return 收集到的所有 {@link SemanticError},如果无错误则返回空列表
+ */
public List analyze(List moduleNodes) {
ctx.log("开始语义分析");
initBuiltinModule();
ctx.log("内置模块初始化完成");
+
registerUserModules(moduleNodes);
ctx.log("用户模块注册完成: " + modules.keySet());
+
registerSignaturesAndImports(moduleNodes);
ctx.log("函数签名与导入检查完成");
+
checkAllFunctions(moduleNodes);
ctx.log("所有函数检查完成,错误总数: " + errors.size());
+
return errors;
}
- // ---------------------------------------------------------------------
+ /**
+ * 初始化内置工具模块及其函数签名。
+ *
+ * 包括:to_int、to_string、print。
+ */
private void initBuiltinModule() {
ModuleInfo builtin = new ModuleInfo("BuiltinUtils");
- builtin.getFunctions().put("to_int", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.INT));
- builtin.getFunctions().put("to_string", new FunctionType(List.of(BuiltinType.INT), BuiltinType.STRING));
- builtin.getFunctions().put("print", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.VOID));
+ builtin.getFunctions().put("to_int", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.INT));
+ builtin.getFunctions().put("to_string", new FunctionType(List.of(BuiltinType.INT), BuiltinType.STRING));
+ builtin.getFunctions().put("print", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.VOID));
modules.put(builtin.getName(), builtin);
}
+ /**
+ * 注册所有用户模块到模块映射中(只保留名称,用于后续签名与导入检查)。
+ *
+ * @param mods AST 中的模块节点列表
+ */
private void registerUserModules(List mods) {
- for (var mod : mods) {
+ for (ModuleNode mod : mods) {
modules.put(mod.name(), new ModuleInfo(mod.name()));
}
}
+ /**
+ * 为每个模块注册其导入关系与函数签名。
+ *
+ * 对未知模块或类型均会记录 {@link SemanticError} 并进行默认降级处理。
+ *
+ * @param mods AST 中的模块节点列表
+ */
private void registerSignaturesAndImports(List mods) {
- for (var mod : mods) {
+ for (ModuleNode mod : mods) {
ModuleInfo mi = modules.get(mod.name());
- // 导入
- for (var imp : mod.imports()) {
+
+ // 模块导入检查
+ for (ImportNode imp : mod.imports()) {
if (!modules.containsKey(imp.moduleName())) {
errors.add(new SemanticError(imp, "未知模块: " + imp.moduleName()));
- continue;
+ } else {
+ mi.getImports().add(imp.moduleName());
}
- mi.getImports().add(imp.moduleName());
}
- // 函数签名
- for (var fn : mod.functions()) {
+
+ // 函数签名注册
+ for (FunctionNode fn : mod.functions()) {
List params = new ArrayList<>();
- for (var p : fn.parameters()) {
+ for (ParameterNode p : fn.parameters()) {
Type t = ctx.parseType(p.type());
if (t == null) {
errors.add(new SemanticError(p, "未知类型: " + p.type()));
@@ -116,23 +174,33 @@ public class SemanticAnalyzer {
}
params.add(t);
}
- Type ret = Optional.ofNullable(ctx.parseType(fn.returnType())).orElse(BuiltinType.VOID);
- mi.getFunctions().put(fn.name(), new FunctionType(params, ret));
+ Type retType = Optional.ofNullable(ctx.parseType(fn.returnType()))
+ .orElse(BuiltinType.VOID);
+ mi.getFunctions().put(fn.name(), new FunctionType(params, retType));
}
}
}
+ /**
+ * 对所有模块中的每个函数体执行逐语句分析,收集语义错误。
+ *
+ * @param mods AST 中的模块节点列表
+ */
private void checkAllFunctions(List mods) {
- for (var mod : mods) {
+ for (ModuleNode mod : mods) {
ModuleInfo mi = modules.get(mod.name());
- for (var fn : mod.functions()) {
+ for (FunctionNode fn : mod.functions()) {
SymbolTable locals = new SymbolTable(null);
- // 参数注册
- for (var p : fn.parameters()) {
- locals.define(new Symbol(p.name(), ctx.parseType(p.type()), SymbolKind.VARIABLE));
+
+ // 参数预定义为局部变量
+ for (ParameterNode p : fn.parameters()) {
+ locals.define(new Symbol(p.name(),
+ ctx.parseType(p.type()),
+ SymbolKind.VARIABLE));
}
- // 函数体
- for (var stmt : fn.body()) {
+
+ // 逐语句分发给对应的 StatementAnalyzer
+ for (StatementNode stmt : fn.body()) {
var analyzer = registry.getStatementAnalyzer(stmt);
if (analyzer != null) {
analyzer.analyze(ctx, mi, fn, locals, stmt);
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 b0aae5f..0931c27 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
@@ -5,21 +5,25 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
/**
* 表示语义分析过程中发现的错误。
*
- * 每个语义错误包含两个部分:
- * 1. 发生错误的 AST(抽象语法树)节点 {@link Node};
- * 2. 错误的详细描述 {@link String}。
- *
+ * 每个语义错误包含以下内容:
+ *
+ * - {@link Node}:发生错误的 AST 节点,便于定位;
+ * - {@link String message}:错误的详细描述信息。
+ *
+ *
+ * 使用 Java 16+ record 定义,自动生成构造方法、访问器(getters)、equals、hashCode 和 toString 方法。
*
- * 该类以 record 形式定义,自动生成构造方法、访问器方法(getters)、equals、hashCode 以及 toString 方法。
+ * @param node 触发语义错误的 AST 节点
+ * @param message 错误的详细描述
*/
public record SemanticError(Node node, String message) {
/**
- * 返回语义错误的字符串表示。
- *
- * 格式为:"Semantic error at [节点信息]: [错误信息]"。
- * 方便在日志输出或调试过程中快速定位问题。
- *
+ * 返回语义错误的字符串表示,格式如下:
+ *
+ * Semantic error at [节点信息]: [错误信息]
+ *
+ * 便于在日志输出或调试时快速查看定位。
*
* @return 格式化后的错误信息字符串
*/
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 e0374b7..f948938 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,21 +3,25 @@ package org.jcnc.snow.compiler.semantic.symbol;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
- * 表示符号表(Symbol Table)中的一条符号记录。
+ * 符号表中的一条符号记录,表示语言中的命名实体(如变量、函数等)。
*
- * 每条符号记录包含以下三个部分信息:
+ * 每条符号记录包括三个核心属性:
*
- * - name:符号的名称,如变量名、函数名等。
- * - type:符号的类型信息,通常对应变量类型、函数返回值类型等。
- * - kind:符号的种类,指明符号是变量、函数、类等 {@link SymbolKind}。
+ * - name:符号名称,例如变量名或函数名;
+ * - type:符号的类型信息,对于变量是变量类型,对于函数是返回类型;
+ * - kind:符号的种类,由 {@link SymbolKind} 枚举区分(变量、函数等)。
+ *
+ *
+ * 使用 Java 16+ record 定义,自动生成:
+ *
+ * - 构造方法;
+ * - 访问器方法(getters);
+ * - equals、hashCode;
+ * - toString 方法。
*
- *
*
- * 本类使用 Java 记录类型(record)定义,自动生成构造方法、访问器方法(getters)、equals、hashCode 以及 toString 方法。
- *
- * @param name 符号的名称
- * @param type 符号的类型
- * @param kind 符号的种类
+ * @param name 符号名称
+ * @param type 符号类型
+ * @param kind 符号种类
*/
-public record Symbol(String name, Type type, SymbolKind 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 0a13ee2..6cff3e7 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,25 +1,33 @@
package org.jcnc.snow.compiler.semantic.symbol;
/**
- * 表示符号的种类(Symbol Kind)。
+ * 符号种类枚举:用于在符号表中区分不同类型的命名实体。
*
- * 用于在符号表中区分不同类型的符号,例如变量、函数、模块等。
+ * 在语义分析过程中,不同种类的符号需要不同的处理策略,
+ * 例如变量声明、函数调用、模块导入等。
*
*
- * 支持的符号种类包括:
*
- * - VARIABLE:变量符号,例如局部变量、全局变量等。
- * - FUNCTION:函数符号,包括普通函数、方法等。
- * - 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 0cfd3e0..2162216 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,26 @@ import java.util.HashMap;
import java.util.Map;
/**
- * 符号表(Symbol Table),用于管理符号(如变量、函数、模块等)及其作用域信息。
+ * 符号表(Symbol Table):用于管理命名实体(如变量、函数、模块等)的作用域及其类型信息。
*
- * 支持链式作用域(Nested Scope)管理:
+ * 本实现支持链式作用域(Nested Scope):
*
- * - 每个符号表可以有一个父符号表 {@link #parent},用于实现作用域嵌套。
- * - 符号表内部使用 {@link Map} 保存当前作用域内定义的符号。
+ * - 每个 {@code SymbolTable} 可拥有一个父作用域,若查找失败可递归向上查找;
+ * - 当前作用域使用内部 {@link Map} 保存定义的符号;
+ * - 提供符号定义与符号解析两种基本操作。
*
- *
*/
public class SymbolTable {
- /** 父作用域符号表,如果为 null 则表示当前为最外层作用域。 */
+ /** 父作用域符号表;若为 null 则表示当前为最外层作用域。 */
private final SymbolTable parent;
- /** 当前作用域中定义的符号集合,按名称索引。 */
+ /** 当前作用域内定义的符号映射:符号名 -> {@link Symbol}。 */
private final Map symbols = new HashMap<>();
/**
- * 创建一个新的符号表,并指定其父作用域。
+ * 构造一个新的符号表,并可指定其父作用域以支持嵌套查找。
*
- * @param parent 父符号表,若无父作用域则传入 null
+ * @param parent 父作用域的 {@link SymbolTable};若无父作用域,则传入 {@code null}
*/
public SymbolTable(SymbolTable parent) {
this.parent = parent;
@@ -32,12 +32,11 @@ public class SymbolTable {
/**
* 在当前作用域中定义一个新的符号。
*
- * 如果当前作用域中已存在同名符号,则定义失败并返回 {@code false};
- * 否则,将符号添加到符号表中并返回 {@code true}。
- *
+ * 如果当前作用域已有同名符号,则定义失败并返回 {@code false};
+ * 否则将符号添加到当前作用域并返回 {@code true}。
*
- * @param symbol 要定义的符号
- * @return 是否定义成功,若名称冲突则返回 {@code false}
+ * @param symbol 要定义的 {@link Symbol} 对象
+ * @return {@code true} 如果添加成功;{@code false} 表示名称冲突
*/
public boolean define(Symbol symbol) {
if (symbols.containsKey(symbol.name())) {
@@ -48,27 +47,21 @@ public class SymbolTable {
}
/**
- * 根据名称查找符号。
- *
- * 查找顺序为:
+ * 根据名称解析符号,支持嵌套作用域查找:
*
- * - 先在当前作用域中查找。
- * - 如果当前作用域未找到,递归向父作用域查找。
- * - 若最终未找到,返回 {@code null}。
+ * - 优先在当前作用域查找;
+ * - 若未找到且存在父作用域,则递归在父作用域查找;
+ * - 最终未找到则返回 {@code null}。
*
- *
*
- * @param name 要查找的符号名称
- * @return 找到的符号对象;如果不存在则返回 {@code null}
+ * @param name 要解析的符号名称
+ * @return 对应的 {@link Symbol},或 {@code null}(表示未声明)
*/
public Symbol resolve(String name) {
Symbol sym = symbols.get(name);
if (sym != null) {
return sym;
}
- if (parent != null) {
- return parent.resolve(name);
- }
- return null;
+ return (parent != null) ? parent.resolve(name) : null;
}
}
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 e832ab3..164e261 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,20 +1,20 @@
package org.jcnc.snow.compiler.semantic.type;
/**
- * 内置基础类型枚举。
+ * 内置基础类型枚举:定义编译器中最基本的三种类型。
*
- * 本枚举定义了编译器中最基本的三种内置类型:
+ * 枚举值:
*
- * - INT:整数类型,同时也可用于表示布尔值(真假值)。
- * - STRING:字符串类型。
- * - VOID:无返回值类型,通常用于函数无返回值的情况。
+ * - {@link #INT} – 整数类型,也可用于表示布尔值(真/假);
+ * - {@link #STRING} – 字符串类型;
+ * - {@link #VOID} – 空类型,用于表示无返回值的函数。
*
*
- * 实现了 Type 接口,提供了基本的类型兼容性判断和字符串表示方法。
+ * 本枚举实现了 {@link Type} 接口,提供了基本的类型兼容性判断和友好的字符串表示方法。
*/
public enum BuiltinType implements Type {
/**
- * 整数类型,也可用于布尔值。
+ * 整数类型,也可用于表示布尔值(真假值)。
*/
INT,
@@ -24,28 +24,27 @@ public enum BuiltinType implements Type {
STRING,
/**
- * 空类型,用于表示无返回值的情况。
+ * 空类型,通常用于无返回值的函数。
*/
VOID;
/**
* 判断当前类型是否与另一个类型兼容。
*
- * 兼容的条件是两个类型必须完全相同(引用相等)。
+ * 兼容条件:两个类型实例必须相同(同一枚举常量)。
*
* @param other 另一个需要检查兼容性的类型
- * @return 如果类型完全相同则返回 true,否则返回 false
+ * @return 如果类型完全相同则返回 {@code true};否则返回 {@code false}
*/
@Override
public boolean isCompatible(Type other) {
- // 只有当两个类型对象是同一个实例时才认为兼容
return this == other;
}
/**
- * 将类型转换为小写字符串形式。
+ * 返回小写形式的类型名称,便于输出和日志展示。
*
- * 例如:INT 会转换为 "int"。
+ * 例如:{@link #INT} 返回 {@code "int"};{@link #VOID} 返回 {@code "void"}。
*
* @return 当前类型的小写字符串表示
*/
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 f527f76..0a54057 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,20 +4,20 @@ import java.util.List;
import java.util.Objects;
/**
- * 表示函数类型。
+ * 表示函数类型。由参数类型列表和返回类型共同确定。
*
- * 一个函数类型由参数类型列表和返回类型共同确定。
- * 例如:一个接受两个 int 参数并返回 string 的函数,其类型描述为 (int, int) -> string。
+ * 例如:一个接受两个 int 参数并返回 string 的函数,其类型描述为 (int, int) -> string。
*
- * 本类是一个 Java record,自动生成基本的构造方法、访问器、equals 和 hashCode 方法,
- * 并实现了 {@link Type} 接口,支持类型兼容性检查和字符串表示。
+ * 该 record 自动生成了构造方法、访问器、equals、hashCode。
+ * 并实现了 {@link Type} 接口,可用于类型兼容性检查和字符串表示。
+ *
+ * @param paramTypes 参数类型列表
+ * @param returnType 返回类型
*/
public record FunctionType(List paramTypes, Type returnType) implements Type {
+
/**
- * 构造函数类型。
- *
- * {@link java.util.Collections#unmodifiableList(List)} 包装成不可变列表,
- * 保证函数类型对象本身的安全性和不可变性。
+ * 构造函数类型,将参数类型列表包装成不可变列表以保证安全性。
*
* @param paramTypes 参数类型列表
* @param returnType 返回类型
@@ -30,30 +30,30 @@ public record FunctionType(List paramTypes, Type returnType) implements Ty
/**
* 判断当前函数类型是否与另一个类型兼容。
*
- * 兼容条件为:
+ * 兼容条件:
*
- * - 另一类型也是 FunctionType。
- * - 返回类型兼容。
- * - 参数类型列表完全相等(顺序和值一致)。
+ * - 另一对象也为 {@code FunctionType};
+ * - 返回类型兼容;
+ * - 参数类型列表完全相等(顺序和值一致)。
*
*
- * @param other 需要检查兼容性的另一个类型
- * @return 如果兼容返回 true,否则返回 false
+ * @param other 另一个类型
+ * @return 如果兼容则返回 {@code true},否则 {@code false}
*/
@Override
public boolean isCompatible(Type other) {
- if (!(other instanceof FunctionType)) return false;
- FunctionType o = (FunctionType) other;
- return returnType.isCompatible(o.returnType)
- && paramTypes.equals(o.paramTypes);
+ if (!(other instanceof FunctionType(List types, Type type))) return false;
+ return returnType.isCompatible(type)
+ && paramTypes.equals(types);
}
/**
* 返回函数类型的字符串表示。
*
- * 格式为:"(参数类型列表) -> 返回类型",例如:(int, string) -> void。
+ * 格式为:(param1, param2, ...) -> returnType,
+ * 例如 (int, string) -> void。
*
- * @return 函数类型的字符串表示
+ * @return 函数类型的描述字符串
*/
@Override
public String toString() {
@@ -61,32 +61,30 @@ public record FunctionType(List paramTypes, Type returnType) implements Ty
}
/**
- * 判断当前函数类型是否与另一个对象相等。
+ * 判断两个函数类型是否相等。
*
- * 相等条件为:
+ * 相等条件:
*
- * - 对象引用相同,或
- * - 对象是 FunctionType 类型,且参数类型列表和返回类型都相等。
+ * - 引用相同;
+ * - 或都是 {@code FunctionType},且参数列表和返回类型都相同。
*
*
* @param obj 另一个对象
- * @return 如果相等返回 true,否则返回 false
+ * @return 相等返回 {@code true},否则 {@code false}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
- if (!(obj instanceof FunctionType)) return false;
- FunctionType o = (FunctionType) obj;
- return returnType.equals(o.returnType)
- && paramTypes.equals(o.paramTypes);
+ if (!(obj instanceof FunctionType(List types, Type type))) return false;
+ 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 640b5d8..d33c15d 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,11 +1,22 @@
package org.jcnc.snow.compiler.semantic.type;
/**
- * 类型接口。所有具体类型(内置类型、函数类型等)都应实现此接口。
+ * 类型接口:所有类型(包括内置类型、函数类型等)均需实现此接口,
+ * 用于在语义分析中进行类型兼容性检查和统一表示。
*/
public interface Type {
/**
- * 判断此类型是否可接受赋值或兼容另一个类型。
+ * 判断当前类型是否与另一个类型兼容。
+ *
+ * 例如:
+ *
+ * - 对于内置类型,只有完全相同的枚举常量才兼容;
+ * - 对于函数类型,需要参数列表和返回类型同时兼容;
+ * - 对于其他复合类型,可按语言规则自行实现。
+ *
+ *
+ * @param other 要检查兼容性的另一个类型
+ * @return 如果兼容则返回 {@code true},否则返回 {@code false}
*/
boolean isCompatible(Type other);
}