修改doc

This commit is contained in:
Luke 2025-05-13 18:23:25 +08:00
parent 55526d530a
commit 3fb66731df
29 changed files with 913 additions and 433 deletions

View File

@ -10,32 +10,38 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Analyzer 注册表负责维护并按 AST 节点类型分发对应的 StatementAnalyzer ExpressionAnalyzer * {@code AnalyzerRegistry} 是语义分析器的注册与分发中心
* <p> * <p>
* 支持 * 它负责根据 AST 节点的类型查找并返回相应的 {@link StatementAnalyzer} {@link ExpressionAnalyzer} 实例
* 同时支持注册自定义分析器并在未找到对应表达式分析器时提供默认兜底处理器
* <p>
* 主要职责
* <ul> * <ul>
* <li>注册任意自定义的语句或表达式分析器</li> * <li>支持注册语句和表达式节点类型对应的分析器</li>
* <li>按节点实例动态查找并返回对应分析器</li> * <li>在语义分析阶段根据 AST 节点动态查找对应的分析器</li>
* <li>对未注册的表达式节点提供默认兜底分析器 {@link UnsupportedExpressionAnalyzer}</li> * <li>为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer}</li>
* <li>不为语句提供默认兜底分析器未注册类型将返回 {@code null}</li>
* </ul> * </ul>
*/ */
public class AnalyzerRegistry { public class AnalyzerRegistry {
/** 语句节点类型 -> 分析器 映射 */ /** Statement 节点类型 → 对应语义分析器映射表 */
private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>(); private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
/** 表达式节点类型 -> 分析器 映射 */
/** Expression 节点类型 → 对应语义分析器映射表 */
private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>(); private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
/** 默认兜底表达式分析器,处理所有未显式注册的 ExpressionNode 子类型 */
/** 默认兜底表达式分析器,用于处理未注册的表达式类型 */
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported = private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported =
new UnsupportedExpressionAnalyzer<>(); new UnsupportedExpressionAnalyzer<>();
// --- 注册方法 ----------------------------------------------------------- // ========================= 注册方法 =========================
/** /**
* 注册一个 StatementAnalyzer用于处理指定类型的语句节点 * 注册一个 {@link StatementAnalyzer} 实例用于处理指定类型的语句节点
* *
* @param cls 语句节点的 Class 对象 * @param cls 要注册的语句节点类型Class 对象
* @param analyzer 针对该类型节点的分析器实例 * @param analyzer 与该类型匹配的分析器实例
* @param <S> 具体的 StatementNode 子类型 * @param <S> {@link StatementNode} 的具体子类
*/ */
public <S extends StatementNode> void registerStatementAnalyzer( public <S extends StatementNode> void registerStatementAnalyzer(
Class<S> cls, Class<S> cls,
@ -45,11 +51,11 @@ public class AnalyzerRegistry {
} }
/** /**
* 注册一个 ExpressionAnalyzer用于处理指定类型的表达式节点 * 注册一个 {@link ExpressionAnalyzer} 实例用于处理指定类型的表达式节点
* *
* @param cls 表达式节点的 Class 对象 * @param cls 要注册的表达式节点类型Class 对象
* @param analyzer 针对该类型节点的分析器实例 * @param analyzer 与该类型匹配的分析器实例
* @param <E> 具体的 ExpressionNode 子类型 * @param <E> {@link ExpressionNode} 的具体子类
*/ */
public <E extends ExpressionNode> void registerExpressionAnalyzer( public <E extends ExpressionNode> void registerExpressionAnalyzer(
Class<E> cls, Class<E> cls,
@ -58,16 +64,16 @@ public class AnalyzerRegistry {
exprAnalyzers.put(cls, analyzer); exprAnalyzers.put(cls, analyzer);
} }
// --- 获取方法 ----------------------------------------------------------- // ========================= 获取方法 =========================
/** /**
* 根据给定的语句节点实例返回对应的 StatementAnalyzer * 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}
* <p> * <p>
* 未注册任何分析器返回 null * 节点类型未注册返回 {@code null}
* *
* @param stmt 要分析的 StatementNode 实例 * @param stmt 要分析的语句节点实例
* @param <S> 具体的 StatementNode 子类型 * @param <S> 语句类型推断自参数
* @return 对应的 StatementAnalyzer null * @return 与该节点类型对应的分析器若未注册则为 {@code null}
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) { public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) {
@ -75,13 +81,13 @@ public class AnalyzerRegistry {
} }
/** /**
* 根据给定的表达式节点实例返回对应的 ExpressionAnalyzer * 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}
* <p> * <p>
* 未注册任何分析器返回 {@link #defaultUnsupported} 兜底 * 节点类型未注册返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}
* *
* @param expr 要分析的 ExpressionNode 实例 * @param expr 要分析的表达式节点实例
* @param <E> 具体的 ExpressionNode 子类型 * @param <E> 表达式类型推断自参数
* @return 对应的 ExpressionAnalyzer可能是默认兜底分析器 * @return 与该节点类型对应的分析器或默认兜底分析器
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) { public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) {

View File

@ -11,9 +11,31 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 二元表达式分析器支持数值宽化转换及字符串拼接 * {@code BinaryExpressionAnalyzer} 是一个用于分析二元表达式的语义分析器
* <p>
* 支持的特性包括
* <ul>
* <li>字符串拼接当运算符为加号 "+" 且任一操作数为字符串类型时</li>
* <li>数值类型的自动宽化转换 int float 运算将转换为 float</li>
* <li>基本的数值运算符 +, -, *, /, %</li>
* <li>关系运算符和比较运算符 <, <=, >, >=, ==, !=</li>
* </ul>
* 对于不支持的运算符或不兼容的类型组合将记录语义错误并默认返回 {@code BuiltinType.INT} 以保持分析过程的连续性
*
* 实现类遵循 {@code ExpressionAnalyzer<BinaryExpressionNode>} 接口规范
*/ */
public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpressionNode> { public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpressionNode> {
/**
* 分析给定的二元表达式节点返回其表达式类型
*
* @param ctx 当前语义分析上下文用于访问日志记录错误收集注册表等服务
* @param mi 当前模块信息包含模块级别的符号与类型定义
* @param fn 当前正在分析的函数节点
* @param locals 当前函数作用域内的符号表用于变量查找
* @param bin 要分析的二元表达式节点
* @return 分析后推断出的表达式类型
*/
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
ModuleInfo mi, ModuleInfo mi,
@ -22,40 +44,46 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
BinaryExpressionNode bin) { BinaryExpressionNode bin) {
ctx.log("检查二元表达式: " + bin.operator()); ctx.log("检查二元表达式: " + bin.operator());
// 递归获取左右子表达式类型 // 获取左侧表达式类型
Type left = ctx.getRegistry().getExpressionAnalyzer(bin.left()) Type left = ctx.getRegistry().getExpressionAnalyzer(bin.left())
.analyze(ctx, mi, fn, locals, bin.left()); .analyze(ctx, mi, fn, locals, bin.left());
// 获取右侧表达式类型
Type right = ctx.getRegistry().getExpressionAnalyzer(bin.right()) Type right = ctx.getRegistry().getExpressionAnalyzer(bin.right())
.analyze(ctx, mi, fn, locals, bin.right()); .analyze(ctx, mi, fn, locals, bin.right());
String op = bin.operator(); String op = bin.operator();
// 字符串拼接 // 情况 1字符串拼接+ 操作符且任一操作数为字符串类型
if (op.equals("+") && if (op.equals("+") &&
(left == BuiltinType.STRING || right == BuiltinType.STRING)) { (left == BuiltinType.STRING || right == BuiltinType.STRING)) {
return BuiltinType.STRING; return BuiltinType.STRING;
} }
// 数值运算或比较 // 情况 2数值类型运算或比较
if ("+-*/%".contains(op) || if ("+-*/%".contains(op) || ("<<=>>===!=").contains(op)) {
("<<=>>===!=").contains(op)) {
if (left.isNumeric() && right.isNumeric()) { if (left.isNumeric() && right.isNumeric()) {
// 自动宽化到更宽的数值类型 // 自动宽化到更宽的数值类型 int + float => float
Type wide = Type.widen(left, right); Type wide = Type.widen(left, right);
if (wide == null) wide = BuiltinType.INT; // 容错降级 if (wide == null) wide = BuiltinType.INT; // 容错降级为 int
// 比较运算返回 int
// 若为比较运算符统一返回 int 类型作为布尔值表示
if ("< <= > >= == !=".contains(op)) { if ("< <= > >= == !=".contains(op)) {
return BuiltinType.INT; return BuiltinType.INT;
} }
return wide; return wide;
} }
} }
// 未知或不支持的运算符/类型 // 情况 3不支持的类型组合记录语义错误
ctx.getErrors().add(new SemanticError( ctx.getErrors().add(new SemanticError(
bin, bin,
String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right) String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right)
)); ));
ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right); ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right);
// 错误情况下默认返回 int 类型以保证语义分析不中断
return BuiltinType.INT; return BuiltinType.INT;
} }
} }

View File

@ -15,14 +15,29 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 函数调用表达式分析器负责对 <code>callee(arg1, arg2, ...)</code> 形式的调用进行 * {@code CallExpressionAnalyzer} 是函数调用表达式的语义分析器
* 语义检查和类型推导并支持 * <p>
* 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式执行如下操作
* <ul> * <ul>
* <li>数值到字符串的隐式转换自动插入 to_string</li> * <li>识别调用目标支持模块成员函数调用和当前模块函数调用</li>
* <li>数值参数的宽化转换 int double</li> * <li>根据被调用函数的参数签名检查实参数量和类型的兼容性</li>
* <li>支持数值参数的宽化转换 int double</li>
* <li>支持数值到字符串的隐式转换自动视为调用 {@code to_string}</li>
* <li>在发生类型不匹配未导入模块或函数未定义等情况下记录语义错误</li>
* </ul> * </ul>
*/ */
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> { public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
/**
* 分析函数调用表达式并推断其类型
*
* @param ctx 当前语义分析上下文提供日志错误记录模块访问等功能
* @param mi 当前模块信息用于函数查找及模块依赖判断
* @param fn 当前分析的函数节点
* @param locals 局部符号表用于变量查找
* @param call 待分析的函数调用表达式节点
* @return 表达式的返回类型如果存在语义错误默认返回 {@code BuiltinType.INT}
*/
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
ModuleInfo mi, ModuleInfo mi,
@ -30,13 +45,15 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
SymbolTable locals, SymbolTable locals,
CallExpressionNode call) { CallExpressionNode call) {
ctx.log("检查函数调用: " + call.callee()); ctx.log("检查函数调用: " + call.callee());
ModuleInfo target = mi;
String functionName;
// 解析 callee支持 Module.func(...) 本模块 func(...) ModuleInfo target = mi; // 默认目标模块为当前模块
String functionName;
ExpressionNode callee = call.callee(); ExpressionNode callee = call.callee();
// 支持模块调用形式ModuleName.FunctionName(...)
if (callee instanceof MemberExpressionNode(var obj, String member) if (callee instanceof MemberExpressionNode(var obj, String member)
&& obj instanceof IdentifierNode(String mod)) { && obj instanceof IdentifierNode(String mod)) {
// 验证模块是否存在并已导入
if (!ctx.getModules().containsKey(mod) if (!ctx.getModules().containsKey(mod)
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) { || (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
@ -47,9 +64,11 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
target = ctx.getModules().get(mod); target = ctx.getModules().get(mod);
functionName = member; functionName = member;
// 简单函数名形式func(...)
} else if (callee instanceof IdentifierNode(String name)) { } else if (callee instanceof IdentifierNode(String name)) {
functionName = name; functionName = name;
// 不支持的 callee 形式
} else { } else {
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
"不支持的调用方式: " + callee)); "不支持的调用方式: " + callee));
@ -57,7 +76,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
return BuiltinType.INT; return BuiltinType.INT;
} }
// 查找函数签名 // 查找目标函数签名
FunctionType ft = target.getFunctions().get(functionName); FunctionType ft = target.getFunctions().get(functionName);
if (ft == null) { if (ft == null) {
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
@ -66,14 +85,14 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
return BuiltinType.INT; return BuiltinType.INT;
} }
// 分析所有实参并收集类型 // 分析所有实参并获取类型
List<Type> args = new ArrayList<>(); List<Type> args = new ArrayList<>();
for (ExpressionNode arg : call.arguments()) { for (ExpressionNode arg : call.arguments()) {
args.add(ctx.getRegistry().getExpressionAnalyzer(arg) args.add(ctx.getRegistry().getExpressionAnalyzer(arg)
.analyze(ctx, mi, fn, locals, arg)); .analyze(ctx, mi, fn, locals, arg));
} }
// 参数检查数量 + 类型兼容 / 宽化 / 数值->字符串隐式转换 // 参数数量检查
if (args.size() != ft.paramTypes().size()) { if (args.size() != ft.paramTypes().size()) {
ctx.getErrors().add(new SemanticError(call, ctx.getErrors().add(new SemanticError(call,
"参数数量不匹配: 期望 " + ft.paramTypes().size() "参数数量不匹配: 期望 " + ft.paramTypes().size()
@ -82,19 +101,18 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
+ ft.paramTypes().size() + ", 实际 " + args.size()); + ft.paramTypes().size() + ", 实际 " + args.size());
} else { } else {
// 参数类型检查与转换支持
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
Type expected = ft.paramTypes().get(i); Type expected = ft.paramTypes().get(i);
Type actual = args.get(i); Type actual = args.get(i);
// 完全兼容或宽化兼容 // 完全兼容或数值宽化转换
boolean ok = expected.isCompatible(actual) boolean ok = expected.isCompatible(actual)
|| (expected.isNumeric() && actual.isNumeric() || (expected.isNumeric() && actual.isNumeric()
&& Type.widen(actual, expected) == expected); && Type.widen(actual, expected) == expected);
// 数值到字符串的隐式转换 // 支持将数值自动转换为字符串
if (!ok if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) {
&& expected == BuiltinType.STRING
&& actual.isNumeric()) {
ctx.log(String.format( ctx.log(String.format(
"隐式将参数 %d 的数值类型 %s 转换为 string (to_string)", "隐式将参数 %d 的数值类型 %s 转换为 string (to_string)",
i, actual i, actual
@ -102,6 +120,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
ok = true; ok = true;
} }
// 类型不匹配记录语义错误
if (!ok) { if (!ok) {
ctx.getErrors().add(new SemanticError(call, ctx.getErrors().add(new SemanticError(call,
String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s", String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
@ -112,6 +131,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
} }
} }
// 返回函数的返回类型作为整个调用表达式的类型
ctx.log("函数调用类型: 返回 " + ft.returnType()); ctx.log("函数调用类型: 返回 " + ft.returnType());
return ft.returnType(); return ft.returnType();
} }

View File

@ -12,24 +12,29 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 标识符表达式分析器负责对变量或常量等标识符节点进行语义检查 * {@code IdentifierAnalyzer} 是用于分析标识符表达式如变量名常量名的语义分析器
* 并返回其在当前作用域中的类型
* <p> * <p>
* 如果未在符号表中找到对应的符号将向错误列表中添加一条 * 它的主要职责是在给定的局部作用域中查找标识符对应的符号定义并返回其类型信息
* {@link SemanticError}并返回 {@link BuiltinType#INT} 作为降级类型 * 如果标识符未在当前作用域内声明
* 以避免后续分析因缺失类型而连锁报错 * <ul>
* <li>向语义错误列表中添加一条 {@link SemanticError} 记录</li>
* <li>为保证分析过程的连续性默认返回 {@link BuiltinType#INT} 类型作为降级处理</li>
* </ul>
* <p>
* 该分析器通常用于处理表达式中的变量引用或常量引用场景
*/ */
public class IdentifierAnalyzer implements ExpressionAnalyzer<IdentifierNode> { public class IdentifierAnalyzer implements ExpressionAnalyzer<IdentifierNode> {
/** /**
* 对给定的标识符节点进行语义分析 * 标识符表达式进行语义分析判断其是否在当前作用域中已声明并返回其类型
* *
* @param ctx 全局上下文包含模块信息错误收集和分析器注册表等 * @param ctx 当前语义分析上下文对象提供模块信息错误收集日志记录等服务
* @param mi 当前模块信息用于跨模块引用时的额外检查此处暂未使用 * @param mi 当前模块信息此实现中未使用但保留用于扩展
* @param fn 当前函数节点可用于更复杂的上下文校验此处暂未使用 * @param fn 当前分析的函数节点此实现中未使用但保留用于扩展
* @param locals 当前作用域的符号表包含已声明的变量及其类型 * @param locals 当前函数或代码块的符号表记录已声明的变量及其类型信息
* @param id 待分析的 {@link IdentifierNode}包含标识符名称 * @param id 表达式中出现的 {@link IdentifierNode} 实例表示待解析的标识符名称
* @return 如果符号已声明则返回其 {@link Symbol#type()}否则返回降级类型 {@link BuiltinType#INT} * @return 若标识符已在符号表中声明则返回对应的 {@link Type}
* 否则返回 {@link BuiltinType#INT} 作为错误降级类型
*/ */
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
@ -38,17 +43,17 @@ public class IdentifierAnalyzer implements ExpressionAnalyzer<IdentifierNode> {
SymbolTable locals, SymbolTable locals,
IdentifierNode id) { IdentifierNode id) {
// 查找当前作用域下的符号 // 在当前作用域中查找符号变量或常量
Symbol sym = locals.resolve(id.name()); Symbol sym = locals.resolve(id.name());
if (sym == null) { if (sym == null) {
// 未声明记录错误并降级 // 未声明标识符记录语义错误返回降级类型
ctx.getErrors().add(new SemanticError(id, ctx.getErrors().add(new SemanticError(id,
"未声明的标识符: " + id.name())); "未声明的标识符: " + id.name()));
ctx.log("错误: 未声明的标识符 " + id.name()); ctx.log("错误: 未声明的标识符 " + id.name());
return BuiltinType.INT; return BuiltinType.INT;
} }
// 已声明返回符号所持有的类型 // 返回符号的类型信息
return sym.type(); return sym.type();
} }
} }

View File

@ -10,22 +10,28 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 数字字面量分析器 {@link NumberLiteralNode} 节点进行类型推导 * {@code NumberLiteralAnalyzer} 是用于处理数字字面量表达式如整数常量的语义分析器
* <p> * <p>
* 在本语言中所有数字字面量均被视为整型因此直接返回 {@link BuiltinType#INT} * 在该语言的设计中所有数字字面量 {@code 42}, {@code 0}, {@code -7}统一视为整型
* 本分析器不会产生任何语义错误 * 因此该分析器总是返回 {@link BuiltinType#INT} 类型
* <p>
* 特点
* <ul>
* <li>不依赖符号表函数上下文或模块信息属于上下文无关表达式分析器</li>
* <li>不会引发任何语义错误或日志输出语义稳定处理简单</li>
* </ul>
*/ */
public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> { public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> {
/** /**
* 对数字字面量节点进行语义分析 * 分析数字字面量表达式并返回其固定类型
* *
* @param ctx 全局上下文包含模块信息错误收集日志输出和分析器注册表等 * @param ctx 当前语义分析上下文本分析器不使用该参数但为接口统一性保留
* @param mi 当前模块信息此分析器不涉及模块间调用参数未使用 * @param mi 当前模块信息未使用因字面量无模块依赖
* @param fn 当前函数节点此分析器不依赖函数上下文参数未使用 * @param fn 当前所在的函数节点未使用因字面量无函数依赖
* @param locals 当前作用域的符号表数字字面量不依赖符号表参数未使用 * @param locals 当前作用域的符号表未使用因字面量不引用符号
* @param expr 待分析的 {@link NumberLiteralNode}表示一个数字字面量 * @param expr 要分析的 {@link NumberLiteralNode} 节点
* @return 固定返回 {@link BuiltinType#INT} * @return 始终返回 {@link BuiltinType#INT} 类型表示整型字面量
*/ */
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,

View File

@ -10,22 +10,29 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 字符串字面量分析器 {@link StringLiteralNode} 节点进行类型推导 * {@code StringLiteralAnalyzer} 是字符串字面量表达式的语义分析器
* <p> * <p>
* 在本语言中所有字符串字面量均被视为字符串类型因此直接返回 {@link BuiltinType#STRING} * 负责分析源代码中的字符串字面量例如 {@code "hello"}{@code ""}
* 本分析器不会产生任何语义错误 * 并确定其类型根据语言规范所有字符串字面量默认视为 {@link BuiltinType#STRING} 类型
* <p>
* 特点如下
* <ul>
* <li>不依赖符号表函数上下文或模块信息属于上下文无关表达式分析器</li>
* <li>恒定返回 {@code STRING} 类型</li>
* <li>不产生任何语义错误具备语义稳定性</li>
* </ul>
*/ */
public class StringLiteralAnalyzer implements ExpressionAnalyzer<StringLiteralNode> { public class StringLiteralAnalyzer implements ExpressionAnalyzer<StringLiteralNode> {
/** /**
* 对字符串字面量节点进行语义分析 * 分析字符串字面量表达式并返回其类型
* *
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等此分析器不使用上下文 * @param ctx 当前语义分析上下文对象本分析器不使用该参数保留用于接口一致性
* @param mi 当前模块信息此分析器不涉及模块调用参数未使用 * @param mi 当前模块信息未使用因字面量无模块依赖
* @param fn 当前函数节点此分析器不依赖函数上下文参数未使用 * @param fn 当前所在的函数节点未使用因字面量无函数依赖
* @param locals 当前作用域的符号表字符串字面量不依赖符号表参数未使用 * @param locals 当前作用域的符号表未使用因字面量不引用符号
* @param expr 待分析的 {@link StringLiteralNode}表示一个字符串字面量 * @param expr 要分析的 {@link StringLiteralNode} 节点
* @return 固定返回 {@link BuiltinType#STRING} * @return 始终返回 {@link BuiltinType#STRING} 类型表示字符串字面量
*/ */
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,

View File

@ -11,29 +11,33 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 默认兜底表达式分析器用于处理所有未显式注册的 {@link ExpressionNode} 子类型 * {@code UnsupportedExpressionAnalyzer} 是一个通用兜底表达式分析器
* 用于处理所有未显式注册的 {@link ExpressionNode} 子类型
* <p> * <p>
* 当遇到不支持或未实现的表达式节点时本分析器会 * 在语义分析阶段当分析器注册表中找不到对应节点类型的处理器时
* <ol> * 将回退使用本类进行统一处理以确保编译流程不中断
* <li> {@link Context#getErrors() 错误列表} 中添加一条 {@link SemanticError}</li> * <p>
* <li>在日志中输出对应的错误信息</li> * 特性说明
* <li>并返回降级类型 {@link BuiltinType#INT} 以避免后续连锁报错</li> * <ul>
* </ol> * <li>适用于所有未知或暂未实现的表达式类型</li>
* <li>自动记录语义错误并打印日志方便定位与扩展</li>
* <li>返回统一的降级类型 {@link BuiltinType#INT}以避免类型缺失造成后续分析失败</li>
* </ul>
* *
* @param <E> 任何 {@link ExpressionNode} 子类型 * @param <E> {@link ExpressionNode} 子类支持泛兜底匹配
*/ */
public class UnsupportedExpressionAnalyzer<E extends ExpressionNode> public class UnsupportedExpressionAnalyzer<E extends ExpressionNode>
implements ExpressionAnalyzer<E> { implements ExpressionAnalyzer<E> {
/** /**
* 未知或不支持的表达式节点进行处理 * 不支持或未实现的表达式节点执行兜底处理
* *
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等 * @param ctx 当前语义分析上下文对象用于错误记录与日志输出
* @param mi 当前模块信息分析器不使用模块上下文参数可忽略 * @param mi 当前模块信息方法中未使用保留用于接口一致性
* @param fn 当前函数节点分析器不使用函数上下文参数可忽略 * @param fn 当前函数节点方法中未使用保留用于接口一致性
* @param locals 当前作用域的符号表此分析器不依赖局部符号参数可忽略 * @param locals 当前局部作用域符号表此方法中未使用因不解析具体含义
* @param expr 待分析的表达式节点类型为任意 {@link ExpressionNode} 子类型 * @param expr 不支持的表达式节点
* @return 固定返回 {@link BuiltinType#INT}作为错误降级类型 * @return 固定返回 {@link BuiltinType#INT} 类型作为占位降级类型
*/ */
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
@ -41,11 +45,16 @@ public class UnsupportedExpressionAnalyzer<E extends ExpressionNode>
FunctionNode fn, FunctionNode fn,
SymbolTable locals, SymbolTable locals,
E expr) { E expr) {
// 记录语义错误
ctx.getErrors().add(new SemanticError( ctx.getErrors().add(new SemanticError(
expr, expr,
"不支持的表达式类型: " + expr "不支持的表达式类型: " + expr
)); ));
// 输出错误日志以便调试或后续支持扩展
ctx.log("错误: 不支持的表达式类型 " + expr); ctx.log("错误: 不支持的表达式类型 " + expr);
// 返回默认类型以避免连锁报错
return BuiltinType.INT; return BuiltinType.INT;
} }
} }

View File

@ -10,17 +10,39 @@ import org.jcnc.snow.compiler.semantic.symbol.*;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 赋值语句分析器支持数值宽化自动转换 * {@code AssignmentAnalyzer} 是赋值语句的语义分析器
* <p>
* 负责分析和验证赋值语句的合法性包括
* <ul>
* <li>变量是否已声明且可赋值必须为 {@link SymbolKind#VARIABLE} 类型</li>
* <li>赋值右值的类型是否与变量类型兼容</li>
* <li>是否允许进行数值类型的自动宽化转换 {@code int float}</li>
* </ul>
* 若类型不兼容且无法自动宽化则将记录语义错误并输出日志信息
*/ */
public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> { public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
/**
* 分析赋值语句的语义有效性包括左值合法性与类型匹配性
*
* @param ctx 当前语义分析上下文包含模块表错误收集日志输出等服务
* @param mi 当前模块信息用于支持跨模块语义校验当前未涉及
* @param fn 当前分析的函数节点提供局部作用域上下文
* @param locals 当前函数或代码块的符号表用于变量解析与类型信息获取
* @param asg 要分析的赋值语句节点 {@link AssignmentNode}
*/
@Override @Override
public void analyze(Context ctx, public void analyze(Context ctx,
ModuleInfo mi, ModuleInfo mi,
FunctionNode fn, FunctionNode fn,
SymbolTable locals, SymbolTable locals,
AssignmentNode asg) { AssignmentNode asg) {
// 获取赋值左值变量名并进行符号解析
ctx.log("赋值检查: " + asg.variable()); ctx.log("赋值检查: " + asg.variable());
Symbol sym = locals.resolve(asg.variable()); Symbol sym = locals.resolve(asg.variable());
// 检查变量是否已声明且为可赋值的变量类型
if (sym == null || sym.kind() != SymbolKind.VARIABLE) { if (sym == null || sym.kind() != SymbolKind.VARIABLE) {
ctx.getErrors().add(new SemanticError(asg, ctx.getErrors().add(new SemanticError(asg,
"未声明的变量: " + asg.variable())); "未声明的变量: " + asg.variable()));
@ -28,11 +50,13 @@ public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
return; return;
} }
// 分析右值表达式类型
Type valType = ctx.getRegistry().getExpressionAnalyzer(asg.value()) 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)) { if (!sym.type().isCompatible(valType)) {
// 数值宽化允许 // 数值类型允许自动宽化转换 int double
if (!(sym.type().isNumeric() && valType.isNumeric() 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, ctx.getErrors().add(new SemanticError(asg,

View File

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

View File

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

View File

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

View File

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

View File

@ -6,30 +6,55 @@ import org.jcnc.snow.compiler.semantic.analyzers.expression.*;
import org.jcnc.snow.compiler.semantic.analyzers.statement.*; import org.jcnc.snow.compiler.semantic.analyzers.statement.*;
/** /**
* 负责一次性把所有语句/表达式分析器注册进 `AnalyzerRegistry` * {@code AnalyzerRegistrar} 负责将所有语句与表达式的语义分析器
* 统一注册到 {@link AnalyzerRegistry}
* <p>
* 本类为静态工具类不可实例化其唯一公开方法 {@link #registerAll(AnalyzerRegistry)}
* 应在语义分析初始化阶段调用一次确保所有节点类型都能正确分发到对应分析器
* <p>
* 注册内容包括
* <ul>
* <li>所有标准语句节点如变量声明赋值条件循环返回等的分析器</li>
* <li>所有标准表达式节点如字面量标识符函数调用二元表达式等的分析器</li>
* <li>对不支持或未实现的表达式节点提供兜底分析器 {@link UnsupportedExpressionAnalyzer}</li>
* </ul>
*/ */
public final class AnalyzerRegistrar { public final class AnalyzerRegistrar {
/**
* 私有构造函数禁止实例化
*/
private AnalyzerRegistrar() { } private AnalyzerRegistrar() { }
/**
* 向指定 {@link AnalyzerRegistry} 注册所有语法分析器实例
*
* @param registry 待注册的分析器注册表实例
*/
public static void registerAll(AnalyzerRegistry registry) { public static void registerAll(AnalyzerRegistry registry) {
// ---------- 语句分析器 ---------- // ---------- 注册语句分析器 ----------
registry.registerStatementAnalyzer(DeclarationNode.class, new DeclarationAnalyzer()); registry.registerStatementAnalyzer(DeclarationNode.class, new DeclarationAnalyzer());
registry.registerStatementAnalyzer(AssignmentNode.class, new AssignmentAnalyzer()); registry.registerStatementAnalyzer(AssignmentNode.class, new AssignmentAnalyzer());
registry.registerStatementAnalyzer(IfNode.class, new IfAnalyzer()); registry.registerStatementAnalyzer(IfNode.class, new IfAnalyzer());
registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer()); registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer());
registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer());
// 特殊处理表达式语句 "foo();"作为语句包装表达式
registry.registerStatementAnalyzer(ExpressionStatementNode.class, registry.registerStatementAnalyzer(ExpressionStatementNode.class,
(ctx, mi, fn, locals, stmt) -> (ctx, mi, fn, locals, stmt) ->
registry.getExpressionAnalyzer(stmt.expression()) registry.getExpressionAnalyzer(stmt.expression())
.analyze(ctx, mi, fn, locals, stmt.expression()) .analyze(ctx, mi, fn, locals, stmt.expression())
); );
// ---------- 表达式分析器 ---------- // ---------- 注册表达式分析器 ----------
registry.registerExpressionAnalyzer(NumberLiteralNode.class, new NumberLiteralAnalyzer()); registry.registerExpressionAnalyzer(NumberLiteralNode.class, new NumberLiteralAnalyzer());
registry.registerExpressionAnalyzer(StringLiteralNode.class, new StringLiteralAnalyzer()); registry.registerExpressionAnalyzer(StringLiteralNode.class, new StringLiteralAnalyzer());
registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer()); registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer());
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer()); registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
registry.registerExpressionAnalyzer(MemberExpressionNode.class,new UnsupportedExpressionAnalyzer<>());
// 对尚未实现的表达式类型使用兜底处理器 MemberExpression
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
new UnsupportedExpressionAnalyzer<>());
} }
} }

View File

@ -5,9 +5,24 @@ import org.jcnc.snow.compiler.semantic.type.*;
import java.util.Map; import java.util.Map;
/** /**
* 统一维护内置类型与内置模块 BuiltinUtils * {@code BuiltinTypeRegistry} 是内置类型和内置模块的集中注册中心
* <p>
* 本类主要负责
* <ul>
* <li>定义语言中所有可识别的基础类型 intfloatstring </li>
* <li>在语义分析初始化时将内置模块 {@code BuiltinUtils}注册到上下文中</li>
* <li>提供对内置类型的快速查找支持</li>
* </ul>
* 该类为纯工具类所有成员均为静态不可实例化
*/ */
public final class BuiltinTypeRegistry { public final class BuiltinTypeRegistry {
/**
* 内置类型映射表将类型名称字符串映射到对应的 {@link Type} 实例
* <p>
* 用于类型解析过程如解析变量声明或函数返回类型
* 将用户源码中的类型字符串转换为语义类型对象
*/
public static final Map<String, Type> BUILTIN_TYPES = Map.of( public static final Map<String, Type> BUILTIN_TYPES = Map.of(
"int", BuiltinType.INT, "int", BuiltinType.INT,
"long", BuiltinType.LONG, "long", BuiltinType.LONG,
@ -19,8 +34,19 @@ public final class BuiltinTypeRegistry {
"void", BuiltinType.VOID "void", BuiltinType.VOID
); );
/**
* 私有构造函数禁止实例化
*/
private BuiltinTypeRegistry() { } private BuiltinTypeRegistry() { }
/**
* 初始化语义上下文中与内置模块相关的内容
* <p>
* 当前实现将内置模块 {@code BuiltinUtils} 注册至上下文模块表中
* 使其在用户代码中可被访问 {@code BuiltinUtils.to_string(...)}
*
* @param ctx 当前语义分析上下文
*/
public static void init(Context ctx) { public static void init(Context ctx) {
ctx.modules().put("BuiltinUtils", ModuleInfo.builtin()); ctx.modules().put("BuiltinUtils", ModuleInfo.builtin());
} }

View File

@ -8,25 +8,36 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 语义分析公共上下文在整个分析流程中共享用于存储模块信息收集错误控制日志以及管理分析器注册表 * {@code Context} 表示语义分析阶段的共享上下文环境
* <p>
* 它贯穿整个语义分析流程用于维护并提供以下核心服务
* <ul>
* <li>模块信息管理包含所有已加载模块源模块与内置模块</li>
* <li>错误收集集中存储语义分析期间产生的 {@link SemanticError}</li>
* <li>日志控制支持按需输出详细调试日志</li>
* <li>分析器调度通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发</li>
* </ul>
*/ */
public class Context { public class Context {
/** 所有已注册模块名 -> 模块信息 的映射 */ /** 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */
private final Map<String, ModuleInfo> modules; private final Map<String, ModuleInfo> modules;
/** 语义分析过程中收集的所有错误列表 */
/** 错误列表:语义分析过程中收集的所有 {@link SemanticError} */
private final List<SemanticError> errors; private final List<SemanticError> errors;
/** 是否启用详细日志输出 */
/** 日志开关:若为 true将启用 {@link #log(String)} 输出日志信息 */
private final boolean verbose; private final boolean verbose;
/** 存放并分发各类语句/表达式分析器的注册表 */
/** 语义分析器注册表:用于按节点类型动态调度分析器 */
private final AnalyzerRegistry registry; private final AnalyzerRegistry registry;
/** /**
* 构造一个新的 Context 实例 * 构造语义分析上下文对象
* *
* @param modules 初始化的模块信息映射用于查找模块签名导入关系等 * @param modules 注册的模块信息集合
* @param errors 用于收集语义分析过程中发现的所有 {@link SemanticError} * @param errors 错误收集器分析器将所有语义错误写入此列表
* @param verbose 是否启用详细日志当为 true 调用 {@link #log(String)} 会打印日志 * @param verbose 是否启用调试日志输出
* @param registry 已设置好所有分析器的 {@link AnalyzerRegistry} * @param registry 分析器注册表提供类型到分析器的映射与调度能力
*/ */
public Context(Map<String, ModuleInfo> modules, public Context(Map<String, ModuleInfo> modules,
List<SemanticError> errors, List<SemanticError> errors,
@ -38,31 +49,42 @@ public class Context {
this.registry = registry; this.registry = registry;
} }
// ------------------ 模块信息 ------------------
/** /**
* 获取所有模块信息的映射 * 获取所有模块信息映射
* *
* @return 模块名 {@link ModuleInfo} Map * @return 模块名 模块信息 {@link ModuleInfo} 的映射
*/ */
public Map<String, ModuleInfo> getModules() { public Map<String, ModuleInfo> getModules() {
return modules; return modules;
} }
public Map<String, ModuleInfo> modules() { return modules; } /** @return 模块信息(快捷方式) */
public List<SemanticError> errors() { return errors; } public Map<String, ModuleInfo> modules() {
public AnalyzerRegistry registry() { return registry; } return modules;
}
// ------------------ 错误收集 ------------------
/** /**
* 获取语义错误列表 * 获取语义分析过程中记录的所有错误
* 分析过程中产生的 {@link SemanticError} 会被收集到此列表
* *
* @return 语义错误列表 * @return 错误列表
*/ */
public List<SemanticError> getErrors() { public List<SemanticError> getErrors() {
return errors; return errors;
} }
/** @return 错误列表(快捷方式) */
public List<SemanticError> errors() {
return errors;
}
// ------------------ 分析器注册表 ------------------
/** /**
* 获取分析器注册表 * 获取分析器注册表用于分发语句与表达式分析器
* 可通过此注册表按 AST 节点类型分发对应的语句或表达式分析器
* *
* @return {@link AnalyzerRegistry} 实例 * @return {@link AnalyzerRegistry} 实例
*/ */
@ -70,10 +92,17 @@ public class Context {
return registry; return registry;
} }
/** @return 注册表(快捷方式) */
public AnalyzerRegistry registry() {
return registry;
}
// ------------------ 日志输出 ------------------
/** /**
* 打印日志信息仅在 {@code verbose} true 时输出到标准输出 * 打印日志信息 {@code verbose} true 生效
* *
* @param msg 要打印的日志内容 * @param msg 日志内容
*/ */
public void log(String msg) { public void log(String msg) {
if (verbose) { if (verbose) {
@ -81,14 +110,16 @@ public class Context {
} }
} }
// ------------------ 工具函数 ------------------
/** /**
* 将类型名解析为内置类型 {@link Type} 实例 * 将类型名称字符串解析为对应的内置 {@link Type} 实例
* <p> * <p>
* 名称 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在则返回对应类型 * 类型 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在则返回对应类型
* 否则返回 {@code null}调用方可据此决定降级为默认类型并记录错误 * 否则返回 {@code null}调用方可据此决定是否降级处理
* *
* @param name 类型名称 "int", "string", "void" * @param name 类型名称 "int", "float", "void", "string"
* @return 对应的 {@link Type} {@code null} 表示未知类型 * @return 匹配的 {@link Type}若无匹配项则返回 {@code null}
*/ */
public Type parseType(String name) { public Type parseType(String name) {
return BuiltinTypeRegistry.BUILTIN_TYPES.get(name); return BuiltinTypeRegistry.BUILTIN_TYPES.get(name);

View File

@ -3,28 +3,78 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.*; import org.jcnc.snow.compiler.semantic.symbol.*;
import java.util.*;
/** /**
* 遍历每个函数体并通过分发器执行语句/表达式分析 * {@code FunctionChecker} 是语义分析阶段中用于检查函数体语句合法性的调度器
* <p>
* 它逐个遍历所有模块中的函数定义并对函数体中的每一条语句调用对应的语义分析器
* 执行类型检查作用域验证错误记录等任务
* <p>
* 核心职责包括
* <ul>
* <li>为每个函数构建局部符号表并注册函数参数为变量</li>
* <li>分发函数体语句至相应的 {@link org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer}</li>
* <li>记录未支持语句类型为语义错误</li>
* <li>依赖上下文 {@link Context} 提供模块信息类型解析错误收集等服务</li>
* </ul>
*/ */
public class FunctionChecker { 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;
}
/**
* 执行函数体检查流程
* <p>
* 对所有模块中的所有函数依次进行处理
* <ol>
* <li>查找模块对应的 {@link ModuleInfo}</li>
* <li>创建函数局部符号表 {@link SymbolTable}并注册所有参数变量</li>
* <li>对函数体中的每一条语句分发到已注册的分析器进行语义分析</li>
* <li>若某条语句无可用分析器则记录为 {@link SemanticError}</li>
* </ol>
*
* @param mods 所有模块的 AST 根节点集合
*/
public void check(Iterable<ModuleNode> mods) { public void check(Iterable<ModuleNode> mods) {
for (ModuleNode mod : mods) { for (ModuleNode mod : mods) {
// 获取当前模块对应的语义信息
ModuleInfo mi = ctx.modules().get(mod.name()); 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()) { for (var stmt : fn.body()) {
var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt); var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (analyzer != null) { if (analyzer != null) {
analyzer.analyze(ctx, mi, fn, locals, stmt); analyzer.analyze(ctx, mi, fn, locals, stmt);
} else { } else {
ctx.errors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt)); ctx.errors().add(new SemanticError(
stmt,
"不支持的语句类型: " + stmt
));
} }
} }
} }

View File

@ -6,28 +6,33 @@ import org.jcnc.snow.compiler.semantic.type.FunctionType;
import java.util.*; import java.util.*;
/** /**
* 模块信息保存单个模块在语义分析阶段的元数据 * {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装
* <p> * <p>
* 包含以下内容 * 用于在分析期间管理模块间依赖函数签名查找等关键任务
* 每个模块对应一个唯一的 {@code ModuleInfo} 实例
* <p>
* 包含信息包括
* <ul> * <ul>
* <li>模块名称唯一标识</li> * <li>模块名称唯一标识</li>
* <li>该模块导入的其他模块名称集合</li> * <li>该模块导入的其他模块名集合</li>
* <li>该模块中定义的函数签名映射将函数名映射到对应的 {@link FunctionType}</li> * <li>该模块中定义的所有函数签名 {@code Map<String, FunctionType>}</li>
* </ul> * </ul>
* 在语义分析过程中用于管理模块依赖关系和查找函数签名
*/ */
public class ModuleInfo { public class ModuleInfo {
/** 模块名称 */
/** 模块名称,作为全局唯一标识 */
private final String name; private final String name;
/** 导入的其他模块名称集合 */
/** 该模块显式导入的模块名集合(用于跨模块访问符号) */
private final Set<String> imports = new HashSet<>(); private final Set<String> imports = new HashSet<>();
/** 模块中定义的函数签名映射:函数名 -> 函数类型 */
/** 该模块中定义的函数名 → 函数类型映射 */
private final Map<String, FunctionType> functions = new HashMap<>(); private final Map<String, FunctionType> functions = new HashMap<>();
/** /**
* 构造一个新的模块信息对象 * 构造模块信息对象
* *
* @param name 模块名称必须唯一 * @param name 模块名称必须唯一且不可为空
*/ */
public ModuleInfo(String name) { public ModuleInfo(String name) {
this.name = name; this.name = name;
@ -36,40 +41,59 @@ public class ModuleInfo {
/** /**
* 获取模块名称 * 获取模块名称
* *
* @return 模块的唯一名称 * @return 当前模块的唯一名称
*/ */
public String getName() { public String getName() {
return name; return name;
} }
/** /**
* 获取该模块导入的其他模块名称集合 * 获取该模块导入的模块名称集合
* <p> * <p>
* 返回 Set 为内部对象的直接引用可对其进行添加或移除操作以维护导入列表 * 返回集合为内部数据的直接引用调用方可通过 {@code add/remove} 方法动态维护导入信息
* *
* @return 导入模块名称的可变集合 * @return 可变集合包含所有导入模块名
*/ */
public Set<String> getImports() { public Set<String> getImports() {
return imports; return imports;
} }
/** /**
* 获取模块中定义的函数签名映射 * 获取模块中已声明的函数签名
* <p> * <p>
* 键为函数名值为对应的 {@link FunctionType} * 映射键为函数名值为对应的 {@link FunctionType}
* 返回 Map 为内部对象的直接引用可对其进行添加或移除操作以维护函数签名列表 * 返回对象为内部引用可用于添加修改或删除函数定义
* *
* @return 函数签名映射表 * @return 模块内函数定义映射表
*/ */
public Map<String, FunctionType> getFunctions() { public Map<String, FunctionType> getFunctions() {
return functions; return functions;
} }
/**
* 创建并返回一个用于注册内置函数的内置模块 {@code BuiltinUtils}
* <p>
* 该模块提供如下内置函数签名
* <ul>
* <li>{@code to_int(string): int}</li>
* <li>{@code to_string(int): string}</li>
* <li>{@code print(string): void}</li>
* </ul>
*
* @return 一个预构建的 {@code BuiltinUtils} 模块信息对象
*/
public static ModuleInfo builtin() { public static ModuleInfo builtin() {
ModuleInfo mi = new ModuleInfo("BuiltinUtils"); 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("to_int", new FunctionType(
mi.getFunctions().put("print", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.VOID)); 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; return mi;
} }
} }

View File

@ -3,12 +3,37 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.ModuleNode; import org.jcnc.snow.compiler.parser.ast.ModuleNode;
/** /**
* 负责将用户模块名称填入 `modules` 映射 * {@code ModuleRegistry} 负责将用户源码中声明的模块名称注册至全局语义上下文
* <p>
* 它会遍历语法树中的所有模块节点 {@link ModuleNode}并将其模块名填入
* {@link Context#modules()} 映射中为后续语义分析阶段中的模块查找
* 导入验证和跨模块调用提供支持
* <p>
* 注册结果为 {@code Map<String, ModuleInfo>}键为模块名值为新建的 {@link ModuleInfo}
* 若某模块名已存在如内置模块则不会重复注册
*/ */
public class ModuleRegistry { 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;
}
/**
* 遍历并注册所有用户定义模块
* <p>
* 对于每个模块节点将其名称注册到 {@code ctx.modules()} 中作为键
* 若模块已存在例如内置模块则不会覆盖
*
* @param mods 所有模块节点的集合
*/
public void registerUserModules(Iterable<ModuleNode> mods) { public void registerUserModules(Iterable<ModuleNode> mods) {
for (ModuleNode mod : mods) { for (ModuleNode mod : mods) {
ctx.modules().putIfAbsent(mod.name(), new ModuleInfo(mod.name())); ctx.modules().putIfAbsent(mod.name(), new ModuleInfo(mod.name()));

View File

@ -7,25 +7,53 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError;
import java.util.*; import java.util.*;
/** /**
* 语义分析总控仅负责调用各独立组件完成完整流程 * {@code SemanticAnalyzer} 是编译器语义分析阶段的顶层调度器
* <p>
* 它负责统一协调模块注册函数签名登记和函数体语义检查等子任务构建并维护语义上下文 {@link Context}
* 并最终输出所有收集到的语义错误列表 {@link SemanticError}
* <p>
* 语义分析流程分为三个阶段
* <ol>
* <li>模块注册将所有用户模块的名称添加至全局模块表中供后续导入检查与引用</li>
* <li>函数签名注册提取函数定义的签名名称与类型填入每个模块对应的 {@link ModuleInfo}</li>
* <li>函数体检查遍历每个函数体对所有语句与表达式执行类型检查和语义验证</li>
* </ol>
* <p>
* 内部使用组件
* <ul>
* <li>{@link ModuleRegistry}注册用户模块</li>
* <li>{@link SignatureRegistrar}提取函数签名</li>
* <li>{@link FunctionChecker}分析函数体内语句</li>
* <li>{@link BuiltinTypeRegistry}初始化内置模块和类型</li>
* <li>{@link AnalyzerRegistrar}注册语句和表达式分析器</li>
* </ul>
*/ */
public class SemanticAnalyzer { public class SemanticAnalyzer {
/** 全局语义分析上下文,包含模块信息、错误记录、分析器注册表等 */
private final Context ctx; private final Context ctx;
/** 分析器注册表,管理语法节点与分析器之间的映射关系 */
private final AnalyzerRegistry registry = new AnalyzerRegistry(); private final AnalyzerRegistry registry = new AnalyzerRegistry();
// 组件 // 分析流程中用到的核心子组件
private final ModuleRegistry moduleRegistry; private final ModuleRegistry moduleRegistry;
private final SignatureRegistrar signatureRegistrar; private final SignatureRegistrar signatureRegistrar;
private final FunctionChecker functionChecker; private final FunctionChecker functionChecker;
/**
* 构造语义分析器并完成初始配置
*
* @param verbose 是否启用日志输出
*/
public SemanticAnalyzer(boolean verbose) { public SemanticAnalyzer(boolean verbose) {
this.ctx = new Context(new HashMap<>(), new ArrayList<>(), verbose, registry); this.ctx = new Context(new HashMap<>(), new ArrayList<>(), verbose, registry);
// 初始化内置模块与分析器注册表 // 初始化内置模块分析器注册表
BuiltinTypeRegistry.init(ctx); BuiltinTypeRegistry.init(ctx);
AnalyzerRegistrar.registerAll(registry); AnalyzerRegistrar.registerAll(registry);
// 其余组件 // 构造核心组件
this.moduleRegistry = new ModuleRegistry(ctx); this.moduleRegistry = new ModuleRegistry(ctx);
this.signatureRegistrar = new SignatureRegistrar(ctx); this.signatureRegistrar = new SignatureRegistrar(ctx);
this.functionChecker = new FunctionChecker(ctx); this.functionChecker = new FunctionChecker(ctx);
@ -33,13 +61,18 @@ public class SemanticAnalyzer {
/** /**
* 执行完整语义分析流程 * 执行完整语义分析流程
* <p>
* 输入为用户的模块语法树集合输出为分析阶段产生的语义错误列表
*
* @param modules 所有用户模块语法树
* @return 所有语义错误的列表若分析无误则为空
*/ */
public List<SemanticError> analyze(List<ModuleNode> modules) { public List<SemanticError> analyze(List<ModuleNode> modules) {
ctx.log("开始语义分析"); ctx.log("开始语义分析");
moduleRegistry.registerUserModules(modules); moduleRegistry.registerUserModules(modules); // 注册模块名
signatureRegistrar.register(modules); signatureRegistrar.register(modules); // 提取函数签名
functionChecker.check(modules); functionChecker.check(modules); // 分析函数体
ctx.log("分析完成,错误总数: " + ctx.errors().size()); ctx.log("分析完成,错误总数: " + ctx.errors().size());
return ctx.errors(); return ctx.errors();

View File

@ -9,23 +9,46 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 语义分析统一入口封装模块筛选分析器调用错误报告 * {@code SemanticAnalyzerRunner} 是语义分析的统一入口
* <p>
* 负责从原始 AST 中提取模块节点调用语义分析主流程
* 并在出现语义错误时统一报告并终止编译流程
* <p>
* 使用方式
* <pre>{@code
* SemanticAnalyzerRunner.runSemanticAnalysis(ast, true);
* }</pre>
* <p>
* 功能概述
* <ul>
* <li>筛选出所有 {@link ModuleNode} 节点模块级入口</li>
* <li>调用 {@link SemanticAnalyzer} 执行完整语义分析流程</li>
* <li>将收集到的 {@link SemanticError} 列表交由报告器处理</li>
* <li>若存在语义错误调用 {@link SemanticAnalysisReporter#reportAndExitIfNecessary(List)} 自动中止流程</li>
* </ul>
*/ */
public class SemanticAnalyzerRunner { public class SemanticAnalyzerRunner {
/** /**
* 对给定 AST 执行语义分析并在有错误时终止程序 * 对输入的语法树执行语义分析
* <p>
* 语法树应为编译前阶段如解析器产出的 AST 列表
* 本方法会自动筛选其中的 {@link ModuleNode} 节点并调用语义分析器执行完整分析
* *
* @param ast AST 根节点列表 * @param ast 根节点列表应包含一个或多个 {@link ModuleNode}
* @param verbose 是否启用详细日志输出 * @param verbose 是否启用详细日志将控制内部 {@link Context#log(String)} 的行为
*/ */
public static void runSemanticAnalysis(List<Node> ast, boolean verbose) { public static void runSemanticAnalysis(List<Node> ast, boolean verbose) {
// 1. 提取模块节点
List<ModuleNode> modules = ast.stream() List<ModuleNode> modules = ast.stream()
.filter(ModuleNode.class::isInstance) .filter(ModuleNode.class::isInstance)
.map(ModuleNode.class::cast) .map(ModuleNode.class::cast)
.collect(Collectors.toList()); .collect(Collectors.toList());
// 2. 执行语义分析
List<SemanticError> errors = new SemanticAnalyzer(verbose).analyze(modules); List<SemanticError> errors = new SemanticAnalyzer(verbose).analyze(modules);
// 3. 报告并在必要时终止
SemanticAnalysisReporter.reportAndExitIfNecessary(errors); SemanticAnalysisReporter.reportAndExitIfNecessary(errors);
} }
} }

View File

@ -3,41 +3,77 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.type.*; import org.jcnc.snow.compiler.semantic.type.*;
import java.util.*; import java.util.*;
/** /**
* 负责函数签名登记与导入合法性检查 * {@code SignatureRegistrar} 负责函数签名登记与导入语义检查
* <p>
* 在语义分析初期阶段它遍历每个模块完成以下任务
* <ul>
* <li>验证所有 {@link ImportNode} 导入的模块是否存在于全局模块表 {@link Context#modules()} </li>
* <li>将每个 {@link FunctionNode} 的函数签名参数类型和返回类型注册到对应 {@link ModuleInfo} </li>
* <li>在参数或返回类型无法识别时记录 {@link SemanticError}并进行容错降级</li>
* </ul>
* 本组件作为语义分析的准备阶段为后续函数体检查提供函数类型上下文
*/ */
public class SignatureRegistrar { 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<ModuleNode> mods) { public void register(Iterable<ModuleNode> mods) {
for (ModuleNode mod : mods) { for (ModuleNode mod : mods) {
ModuleInfo mi = ctx.modules().get(mod.name()); ModuleInfo mi = ctx.modules().get(mod.name());
// 导入检查 // ---------- 1. 模块导入检查 ----------
for (ImportNode imp : mod.imports()) { for (ImportNode imp : mod.imports()) {
if (!ctx.modules().containsKey(imp.moduleName())) { if (!ctx.modules().containsKey(imp.moduleName())) {
ctx.errors().add(new SemanticError(imp, "未知模块: " + imp.moduleName())); ctx.errors().add(new SemanticError(
imp,
"未知模块: " + imp.moduleName()
));
} else { } else {
mi.getImports().add(imp.moduleName()); mi.getImports().add(imp.moduleName());
} }
} }
// 函数签名 // ---------- 2. 函数签名注册 ----------
for (FunctionNode fn : mod.functions()) { for (FunctionNode fn : mod.functions()) {
List<Type> params = new ArrayList<>(); List<Type> params = new ArrayList<>();
// 参数类型解析
for (ParameterNode p : fn.parameters()) { for (ParameterNode p : fn.parameters()) {
Type t = Optional.ofNullable(ctx.parseType(p.type())) Type t = Optional.ofNullable(ctx.parseType(p.type()))
.orElseGet(() -> { .orElseGet(() -> {
ctx.errors().add(new SemanticError(p, "未知类型: " + p.type())); ctx.errors().add(new SemanticError(
return BuiltinType.INT; p,
"未知类型: " + p.type()
));
return BuiltinType.INT; // 容错降级
}); });
params.add(t); params.add(t);
} }
// 返回类型解析默认降级为 void
Type ret = Optional.ofNullable(ctx.parseType(fn.returnType())) Type ret = Optional.ofNullable(ctx.parseType(fn.returnType()))
.orElse(BuiltinType.VOID); .orElse(BuiltinType.VOID);
// 注册函数签名
mi.getFunctions().put(fn.name(), new FunctionType(params, ret)); mi.getFunctions().put(fn.name(), new FunctionType(params, ret));
} }
} }

View File

@ -3,29 +3,39 @@ package org.jcnc.snow.compiler.semantic.error;
import org.jcnc.snow.compiler.parser.ast.base.Node; import org.jcnc.snow.compiler.parser.ast.base.Node;
/** /**
* 表示语义分析过程中发现的错误 * {@code SemanticError} 表示语义分析过程中发现的错误信息
* <p> * <p>
* 每个语义错误包含以下内容 * 语义错误是编译器无法接受的程序逻辑问题例如
* <ul> * <ul>
* <li>{@link Node}发生错误的 AST 节点便于定位</li> * <li>使用了未声明的变量</li>
* <li>{@link String message}错误的详细描述信息</li> * <li>类型不兼容的赋值</li>
* <li>函数返回类型与实际返回值不一致</li>
* <li>调用了不存在的函数或模块</li>
* </ul> * </ul>
* <p> * <p>
* 使用 Java 16+ record 定义自动生成构造方法访问器gettersequalshashCode toString 方法 * 访问器方法{@code node()} / {@code message()}相等性判断等功能
* *
* @param node 触发语义错误的 AST 节点 * <p>主要字段说明
* @param message 错误的详细描述 * <ul>
* <li>{@code node}发生语义错误的 AST 节点可用于定位源代码位置</li>
* <li>{@code message}具体的错误描述适合用于报错提示日志输出IDE 集成等</li>
* </ul>
*
* @param node 发生错误的抽象语法树节点 {@link Node}
* @param message 错误描述信息
*/ */
public record SemanticError(Node node, String message) { public record SemanticError(Node node, String message) {
/** /**
* 返回语义错误的字符串表示格式如下 * 返回格式化后的语义错误信息字符串
* <p>
* 输出格式
* <pre> * <pre>
* Semantic error at [节点信息]: [错误信息] * Semantic error at [节点]: [错误信息]
* </pre> * </pre>
* 便于在日志输出或调试时快速查看定位 * 适用于命令行编译器输出调试日志或错误收集器展示
* *
* @return 格式化的错误信息字符串 * @return 格式化的错误信息字符串
*/ */
@Override @Override
public String toString() { public String toString() {

View File

@ -3,25 +3,22 @@ package org.jcnc.snow.compiler.semantic.symbol;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 符号表中的一条符号记录表示语言中的命名实体如变量函数等 * {@code Symbol} 表示符号表中的一条符号记录描述语言中命名实体的语义信息
* <p> * <p>
* 每条符号记录包括三个核心属性 * 符号是语义分析中的基础单元通常用于表示变量函数参数等具名元素
* 每个符号具备以下三个核心属性
* <ul> * <ul>
* <li><b>name</b>符号名称例如变量名或函数名</li> * <li><b>name</b>符号的名称例如变量名或函数名</li>
* <li><b>type</b>符号的类型信息对于变量是变量类型对于函数是返回类型</li> * <li><b>type</b>符号的类型信息通常为 {@link Type} 的子类实例</li>
* <li><b>kind</b>符号的种类 {@link SymbolKind} 枚举区分变量函数等</li> * <li><b>kind</b>符号的种类 {@link SymbolKind} 枚举表示例如 VARIABLEFUNCTION </li>
* </ul>
* <p>
* 使用 Java 16+ record 定义自动生成
* <ul> * <ul>
* <li>构造方法</li> * <li>构造器 {@code Symbol(String, Type, SymbolKind)}</li>
* <li>访问器方法getters</li> * <li>访问器方法 {@code name()}, {@code type()}, {@code kind()}</li>
* <li>equalshashCode</li> * <li>标准的 {@code equals()}{@code hashCode()} {@code toString()} 实现</li>
* <li>toString 方法</li>
* </ul> * </ul>
* *
* @param name 符号名称 * @param name 符号名称必须唯一且非空
* @param type 符号类型 * @param type 符号所代表的类型可为基础类型函数类型等
* @param kind 符号种类 * @param kind 符号种类决定其语义用途例如变量函数参数等
*/ */
public record Symbol(String name, Type type, SymbolKind kind) { } public record Symbol(String name, Type type, SymbolKind kind) { }

View File

@ -1,33 +1,40 @@
package org.jcnc.snow.compiler.semantic.symbol; package org.jcnc.snow.compiler.semantic.symbol;
/** /**
* 符号种类枚举用于在符号表中区分不同类型的命名实体 * {@code SymbolKind} 枚举用于标识符号表中不同类型的命名实体
* <p> * <p>
* 在语义分析过程中不同种类的符号需要不同的处理策略 * 在语义分析过程中编译器需要根据符号的种类Kind采用不同的处理策略
* 例如变量声明函数调用模块导入 * 例如变量参与类型推导函数用于调用匹配模块用于跨作用域引用
* </p> * <p>
* * 当前支持的符号种类包括
* <ul> * <ul>
* <li>{@link #VARIABLE} 变量符号局部变量全局变量成员变量等</li> * <li>{@link #VARIABLE}变量符号局部变量全局变量成员变量等</li>
* <li>{@link #FUNCTION} 函数符号自由函数方法构造函数等</li> * <li>{@link #FUNCTION}函数符号自由函数方法构造函数等</li>
* <li>{@link #MODULE} 模块符号表示一个命名空间或模块</li> * <li>{@link #MODULE}模块符号代表命名空间库或逻辑模块</li>
* </ul> * </ul>
*/ */
public enum SymbolKind { public enum SymbolKind {
/** /**
* 变量符号例如在函数或全局作用域中声明的变量 * 变量符号表示在作用域中声明的可赋值实体
* <p>
* 包括函数参数局部变量全局变量常量等
* 分析器会基于其类型参与表达式类型校验和赋值检查
*/ */
VARIABLE, VARIABLE,
/** /**
* 函数符号表示可调用的函数或方法 * 函数符号表示可调用的过程实体
* 用于函数签名注册和调用时的类型匹配 * <p>
* 包括普通函数方法构造器等
* 用于函数签名注册函数调用检查及返回值推导
*/ */
FUNCTION, FUNCTION,
/** /**
* 模块符号表示一个模块或命名空间 * 模块符号表示一个命名空间或模块单元
* 用于管理模块导入和跨模块引用 * <p>
* 在跨模块调用导入语句校验作用域隔离中发挥作用
*/ */
MODULE MODULE
} }

View File

@ -4,26 +4,31 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* 符号表Symbol Table用于管理命名实体如变量函数模块等作用域及其类型信息 * {@code SymbolTable} 表示一个语义作用域用于管理命名实体如变量函数模块等符号信息
* <p> * <p>
* 本实现支持链式作用域Nested Scope * 本类支持嵌套作用域结构Nested Scope适用于块级作用域函数作用域模块作用域等语义环境建模
* 每个符号表可挂接一个父作用域以支持多层级作用域链上的符号解析
*
* <p>核心特性包括
* <ul> * <ul>
* <li>每个 {@code SymbolTable} 可拥有一个父作用域若查找失败可递归向上查找</li> * <li>符号定义局部作用域内唯一</li>
* <li>当前作用域使用内部 {@link Map} 保存定义的符号</li> * <li>符号查找向上查找父作用域</li>
* <li>提供符号定义与符号解析两种基本操作</li> * <li>嵌套作用域支持通过 parent 引用</li>
* <li>可用于语义分析类型检查作用域验证等场景</li>
* </ul> * </ul>
*/ */
public class SymbolTable { public class SymbolTable {
/** 父作用域符号表;若为 null 则表示当前为最外层作用域。 */
/** 父作用域引用,若为 {@code null} 则表示为最外层作用域(全局或根作用域) */
private final SymbolTable parent; private final SymbolTable parent;
/** 当前作用域内定义的符号映射:符号名 -> {@link Symbol}。 */ /** 当前作用域内定义的符号映射表(符号名 → {@link Symbol} */
private final Map<String, Symbol> symbols = new HashMap<>(); private final Map<String, Symbol> symbols = new HashMap<>();
/** /**
* 构造一个新的符号表并可指定其父作用域以支持嵌套查找 * 创建一个新的符号表实例
* *
* @param parent 父作用域 {@link SymbolTable}若无父作用域则传入 {@code null} * @param parent 父作用域符号表若无父作用域则传入 {@code null}
*/ */
public SymbolTable(SymbolTable parent) { public SymbolTable(SymbolTable parent) {
this.parent = parent; this.parent = parent;
@ -32,11 +37,11 @@ public class SymbolTable {
/** /**
* 在当前作用域中定义一个新的符号 * 在当前作用域中定义一个新的符号
* <p> * <p>
* 如果当前作用域已有同名符号则定义失败并返回 {@code false} * 若符号名称在当前作用域中已存在则定义失败并返回 {@code false}
* 否则将符号添加到当前作用域并返回 {@code true} * 否则将该符号添加至当前符号映射中并返回 {@code true}
* *
* @param symbol 要定义的 {@link Symbol} 对象 * @param symbol 待定义的符号实体
* @return {@code true} 如果添加成功{@code false} 表示名称冲突 * @return 若定义成功则返回 {@code true}否则返回 {@code false}
*/ */
public boolean define(Symbol symbol) { public boolean define(Symbol symbol) {
if (symbols.containsKey(symbol.name())) { if (symbols.containsKey(symbol.name())) {
@ -47,15 +52,16 @@ public class SymbolTable {
} }
/** /**
* 根据名称解析符号支持嵌套作用域查找 * 根据名称解析符号支持作用域链向上递归查找
* <p>查找策略
* <ol> * <ol>
* <li>优先在当前作用域查找</li> * <li>优先在当前作用域查找该符号名称</li>
* <li>若未找到且存在父作用域则递归在父作用域查找</li> * <li>若未找到且存在父作用域则递归向上查找</li>
* <li>最终未找到则返回 {@code null}</li> * <li>若所有作用域中均未找到则返回 {@code null}</li>
* </ol> * </ol>
* *
* @param name 要解析的符号名称 * @param name 要解析的符号名称
* @return 对应的 {@link Symbol} {@code null}表示未声明 * @return 对应的 {@link Symbol} 实例若未找到则返回 {@code null}
*/ */
public Symbol resolve(String name) { public Symbol resolve(String name) {
Symbol sym = symbols.get(name); Symbol sym = symbols.get(name);

View File

@ -1,48 +1,58 @@
package org.jcnc.snow.compiler.semantic.type; package org.jcnc.snow.compiler.semantic.type;
/** /**
* 内置基础类型枚举支持多种数值类型以及字符串和 void * {@code BuiltinType} 枚举定义了本语言支持的所有内置基础类型
* <p> * <p>
* 枚举值 * 类型涵盖整数浮点字符串及 void 类型广泛应用于变量声明
* 表达式类型推导函数签名类型检查等语义分析环节
*
* <p>支持的类型包括
* <ul> * <ul>
* <li>{@link #BYTE} 8 位整数</li> * <li>{@link #BYTE} - 8 位整数</li>
* <li>{@link #SHORT} 16 位整数</li> * <li>{@link #SHORT} - 16 位整数</li>
* <li>{@link #INT} 32 位整数</li> * <li>{@link #INT} - 32 位整数</li>
* <li>{@link #LONG} 64 位整数</li> * <li>{@link #LONG} - 64 位整数</li>
* <li>{@link #FLOAT} 单精度浮点数</li> * <li>{@link #FLOAT} - 单精度浮点数</li>
* <li>{@link #DOUBLE} 双精度浮点数</li> * <li>{@link #DOUBLE} - 双精度浮点数</li>
* <li>{@link #STRING} 字符串类型</li> * <li>{@link #STRING} - 字符串类型</li>
* <li>{@link #VOID} 空类型用于表示无返回值的函数</li> * <li>{@link #VOID} - 空类型用于表示无返回值的函数</li>
* </ul>
* <p>
* 本枚举实现了 {@link Type} 接口提供了数值宽化和兼容性判断
*/
public enum BuiltinType implements Type {
BYTE,
SHORT,
INT,
LONG,
FLOAT,
DOUBLE,
STRING,
VOID;
/**
* 判断当前类型是否与另一个类型兼容
* <p>
* 兼容条件
* <ul>
* <li>完全相同</li>
* <li>对于数值类型允许自动宽化转换narrow wide</li>
* </ul> * </ul>
* *
* @param other 另一个要检查的类型 * <p>每个枚举实例实现了 {@link Type} 接口提供以下语义特性
* @return 如果兼容返回 true否则 false * <ul>
* <li>数值类型判断 {@link #isNumeric()}</li>
* <li>类型兼容性判断 {@link #isCompatible(Type)}</li>
* <li>自动数值宽化支持通过 {@link Type#widen(Type, Type)} 实现</li>
* </ul>
*/
public enum BuiltinType implements Type {
BYTE, // 8 位有符号整数
SHORT, // 16 位有符号整数
INT, // 32 位有符号整数默认整数类型
LONG, // 64 位有符号整数
FLOAT, // 单精度浮点数
DOUBLE, // 双精度浮点数
STRING, // 字符串类型
VOID; // 空类型用于表示函数无返回值
/**
* 判断当前类型是否与指定类型兼容
* <p>
* 兼容判断规则
* <ul>
* <li>类型完全相同视为兼容</li>
* <li>对于数值类型若目标类型为宽类型 int double视为兼容</li>
* <li>其他情况视为不兼容</li>
* </ul>
*
* @param other 要比较的类型
* @return 若兼容返回 {@code true}否则返回 {@code false}
*/ */
@Override @Override
public boolean isCompatible(Type other) { public boolean isCompatible(Type other) {
if (this == other) return true; if (this == other) return true;
// 如果两者都是数值类型允许宽化 // 数值类型间允许自动宽化
if (this.isNumeric() && other.isNumeric()) { if (this.isNumeric() && other.isNumeric()) {
return Type.widen(other, this) == this; return Type.widen(other, this) == this;
} }
@ -50,9 +60,13 @@ public enum BuiltinType implements Type {
} }
/** /**
* 判断是否为数值类型byteshortintlongfloatdouble * 判断当前类型是否为数值类型
* <p>
* 数值类型包括
* {@link #BYTE}{@link #SHORT}{@link #INT}{@link #LONG}
* {@link #FLOAT}{@link #DOUBLE}
* *
* @return 如果是数值类型返回 true否则 false * @return 若为数值类型返回 {@code true}否则返回 {@code false}
*/ */
@Override @Override
public boolean isNumeric() { public boolean isNumeric() {
@ -63,9 +77,11 @@ public enum BuiltinType implements Type {
} }
/** /**
* 小写形式的类型名称便于日志和错误输出 * 获取当前类型的名称小写形式
* <p>
* 用于日志输出错误提示等语义描述场景
* *
* @return 小写的类型名称 * @return 当前类型的名称字符串 "int", "string", "void"
*/ */
@Override @Override
public String toString() { public String toString() {

View File

@ -4,26 +4,39 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* 表示函数类型由参数类型列表和返回类型共同确定 * {@code FunctionType} 表示函数的类型信息<strong>参数类型列表</strong><strong>返回类型</strong>组成
* <p> * <p>
* 例如一个接受两个 int 参数并返回 string 的函数其类型描述为 <code>(int, int) -> string</code> * 适用于函数声明函数调用类型检查等语义分析场景
* <p> * <p>
* record 自动生成了构造方法访问器equalshashCode * 例如一个函数接受两个 {@code int} 参数并返回 {@code string}其函数类型为
* 并实现了 {@link Type} 接口可用于类型兼容性检查和字符串表示 * <pre>
* (int, int) -> string
* </pre>
* *
* @param paramTypes 参数类型列表 * <p>该类使用 Java 16+ {@code record} 语法定义自动提供
* @param returnType 返回类型 * <ul>
* <li>构造方法</li>
* <li>访问器 {@code paramTypes()} {@code returnType()}</li>
* <li>{@code equals()}, {@code hashCode()}, {@code toString()} 方法</li>
* </ul>
*
* <p>实现接口{@link Type}
*
* @param paramTypes 参数类型列表顺序敏感不可为 null
* @param returnType 返回类型不可为 null
*/ */
public record FunctionType(List<Type> paramTypes, Type returnType) implements Type { public record FunctionType(List<Type> paramTypes, Type returnType) implements Type {
/** /**
* 构造函数类型将参数类型列表包装成不可变列表以保证安全性 * 构造函数类型对象
* <p>
* 参数类型列表将被包装为不可变以确保函数类型不可修改
* *
* @param paramTypes 参数类型列表 * @param paramTypes 参数类型列表
* @param returnType 返回类型 * @param returnType 返回类型
*/ */
public FunctionType(List<Type> paramTypes, Type returnType) { public FunctionType(List<Type> paramTypes, Type returnType) {
this.paramTypes = List.copyOf(paramTypes); this.paramTypes = List.copyOf(paramTypes); // 确保不可变
this.returnType = returnType; this.returnType = returnType;
} }
@ -32,28 +45,30 @@ public record FunctionType(List<Type> paramTypes, Type returnType) implements Ty
* <p> * <p>
* 兼容条件 * 兼容条件
* <ul> * <ul>
* <li>另一对象也为 {@code FunctionType}</li> * <li>对方也是 {@code FunctionType}</li>
* <li>返回类型兼容</li> * <li>返回类型兼容可宽化或完全匹配</li>
* <li>参数类型列表完全相等顺序和值一致</li> * <li>参数列表完全相等类型与顺序严格一致</li>
* </ul> * </ul>
* *
* @param other 另一个类型 * @param other 要检查的另一个类型
* @return 如果兼容则返回 {@code true}否则 {@code false} * @return 若兼容返回 {@code true}否则 {@code false}
*/ */
@Override @Override
public boolean isCompatible(Type other) { public boolean isCompatible(Type other) {
if (!(other instanceof FunctionType(List<Type> types, Type type))) return false; if (!(other instanceof FunctionType(List<Type> types, Type type))) return false;
return returnType.isCompatible(type) return returnType.isCompatible(type) && paramTypes.equals(types);
&& paramTypes.equals(types);
} }
/** /**
* 返回函数类型的字符串表示 * 返回函数类型的标准字符串表示形式
* <p> * <p>
* 格式为<code>(param1, param2, ...) -> returnType</code> * 格式为
* 例如 <code>(int, string) -> void</code> * <pre>
* (param1, param2, ...) -> returnType
* </pre>
* 例如 {@code (int, string) -> void}
* *
* @return 函数类型的描述字符串 * @return 函数类型的可读性描述
*/ */
@Override @Override
public String toString() { public String toString() {
@ -63,28 +78,26 @@ public record FunctionType(List<Type> paramTypes, Type returnType) implements Ty
/** /**
* 判断两个函数类型是否相等 * 判断两个函数类型是否相等
* <p> * <p>
* 相等条件 * 相等条件
* <ul> * <ul>
* <li>引用相同</li> * <li>引用相同</li>
* <li>都是 {@code FunctionType}且参数列表和返回类型都相同</li> * <li>参数类型列表完全相等且返回类型也相等</li>
* </ul> * </ul>
* *
* @param obj 另一个对象 * @param obj 待比较的对象
* @return 相等返回 {@code true}否则 {@code false} * @return 相等返回 {@code true}否则 {@code false}
*/ */
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) return true; if (this == obj) return true;
if (!(obj instanceof FunctionType(List<Type> types, Type type))) return false; if (!(obj instanceof FunctionType(List<Type> types, Type type))) return false;
return returnType.equals(type) return returnType.equals(type) && paramTypes.equals(types);
&& paramTypes.equals(types);
} }
/** /**
* 计算函数类型的哈希码基于参数类型列表和返回类型 * 计算哈希码确保与 {@link #equals(Object)} 保持一致性
* {@link #equals(Object)} 保持一致
* *
* @return 哈希 * @return 哈希
*/ */
@Override @Override
public int hashCode() { public int hashCode() {

View File

@ -1,4 +1,3 @@
// File: src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
package org.jcnc.snow.compiler.semantic.type; package org.jcnc.snow.compiler.semantic.type;
/** /**

View File

@ -5,15 +5,39 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError;
import java.util.List; import java.util.List;
/** /**
* 用于统一处理语义分析结果的输出与终止逻辑 * {@code SemanticAnalysisReporter} 是语义分析阶段的结果报告工具类
* <p>
* 用于统一处理语义分析阶段产生的错误信息输出与流程终止逻辑
* 通常作为语义分析器的收尾阶段调用
*
* <p>主要职责包括
* <ul>
* <li>打印所有收集到的 {@link SemanticError}</li>
* <li>若存在错误使用 {@code System.exit(1)} 终止编译流程</li>
* <li>若无错误输出分析通过提示</li>
* </ul>
*
* <p>该类为工具类禁止实例化方法均为静态调用
*/ */
public final class SemanticAnalysisReporter { public final class SemanticAnalysisReporter {
// 禁止实例化
private SemanticAnalysisReporter() { }
/** /**
* 根据语义错误列表打印分析结果 * 打印语义分析结果并在必要时终止程序
* 如果存在错误则打印错误详情并退出程序 * <p>
* 如果无错误则打印成功信息 * 如果错误列表非空
* <ul>
* <li>逐条打印错误信息含位置与描述</li>
* <li>使用 {@code System.exit(1)} 退出表示语义分析失败</li>
* </ul>
* 如果错误列表为空
* <ul>
* <li>打印语义分析通过提示</li>
* </ul>
* *
* @param errors 语义分析过程中收集到的错误列表 * @param errors 语义分析阶段收集到的错误列表允许为 null
*/ */
public static void reportAndExitIfNecessary(List<SemanticError> errors) { public static void reportAndExitIfNecessary(List<SemanticError> errors) {
if (errors != null && !errors.isEmpty()) { if (errors != null && !errors.isEmpty()) {
@ -21,7 +45,7 @@ public final class SemanticAnalysisReporter {
for (SemanticError error : errors) { for (SemanticError error : errors) {
System.err.println(" " + error); System.err.println(" " + error);
} }
System.exit(1); System.exit(1); // 非正常退出阻止后续编译流程
} else { } else {
System.out.println("语义分析通过,没有发现错误。"); System.out.println("语义分析通过,没有发现错误。");
} }