增加注释
This commit is contained in:
parent
35efce2dd4
commit
48751436fa
@ -2,34 +2,90 @@ package org.jcnc.snow.compiler.semantic.analyzers;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.expression.UnsupportedExpressionAnalyzer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Analyzer 注册表,负责按节点类型分发对应的 Analyzer。
|
||||
* Analyzer 注册表:负责维护并按 AST 节点类型分发对应的 StatementAnalyzer 或 ExpressionAnalyzer。
|
||||
* <p>
|
||||
* 支持:
|
||||
* <ul>
|
||||
* <li>注册任意自定义的语句或表达式分析器;</li>
|
||||
* <li>按节点实例动态查找并返回对应分析器;</li>
|
||||
* <li>对未注册的表达式节点提供默认兜底分析器 {@link UnsupportedExpressionAnalyzer},</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class AnalyzerRegistry {
|
||||
/** 语句节点类型 -> 分析器 映射 */
|
||||
private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
|
||||
/** 表达式节点类型 -> 分析器 映射 */
|
||||
private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
|
||||
/** 默认兜底表达式分析器,处理所有未显式注册的 ExpressionNode 子类型 */
|
||||
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported =
|
||||
new UnsupportedExpressionAnalyzer<>();
|
||||
|
||||
// --- 注册方法 -----------------------------------------------------------
|
||||
public <S extends StatementNode> void registerStatementAnalyzer(Class<S> cls, StatementAnalyzer<S> analyzer) {
|
||||
|
||||
/**
|
||||
* 注册一个 StatementAnalyzer,用于处理指定类型的语句节点。
|
||||
*
|
||||
* @param cls 语句节点的 Class 对象
|
||||
* @param analyzer 针对该类型节点的分析器实例
|
||||
* @param <S> 具体的 StatementNode 子类型
|
||||
*/
|
||||
public <S extends StatementNode> void registerStatementAnalyzer(
|
||||
Class<S> cls,
|
||||
StatementAnalyzer<S> analyzer
|
||||
) {
|
||||
stmtAnalyzers.put(cls, analyzer);
|
||||
}
|
||||
|
||||
public <E extends ExpressionNode> void registerExpressionAnalyzer(Class<E> cls, ExpressionAnalyzer<E> analyzer) {
|
||||
/**
|
||||
* 注册一个 ExpressionAnalyzer,用于处理指定类型的表达式节点。
|
||||
*
|
||||
* @param cls 表达式节点的 Class 对象
|
||||
* @param analyzer 针对该类型节点的分析器实例
|
||||
* @param <E> 具体的 ExpressionNode 子类型
|
||||
*/
|
||||
public <E extends ExpressionNode> void registerExpressionAnalyzer(
|
||||
Class<E> cls,
|
||||
ExpressionAnalyzer<E> analyzer
|
||||
) {
|
||||
exprAnalyzers.put(cls, analyzer);
|
||||
}
|
||||
|
||||
// --- 获取方法 -----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 根据给定的语句节点实例,返回对应的 StatementAnalyzer。
|
||||
* <p>
|
||||
* 若未注册任何分析器,则返回 null。
|
||||
*
|
||||
* @param stmt 要分析的 StatementNode 实例
|
||||
* @param <S> 具体的 StatementNode 子类型
|
||||
* @return 对应的 StatementAnalyzer,或 null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) {
|
||||
return (StatementAnalyzer<S>) stmtAnalyzers.get(stmt.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的表达式节点实例,返回对应的 ExpressionAnalyzer。
|
||||
* <p>
|
||||
* 若未注册任何分析器,则返回 {@link #defaultUnsupported} 兜底。
|
||||
*
|
||||
* @param expr 要分析的 ExpressionNode 实例
|
||||
* @param <E> 具体的 ExpressionNode 子类型
|
||||
* @return 对应的 ExpressionAnalyzer(可能是默认兜底分析器)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) {
|
||||
return (ExpressionAnalyzer<E>) exprAnalyzers.get(expr.getClass());
|
||||
return (ExpressionAnalyzer<E>)
|
||||
exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<E extends ExpressionNode> {
|
||||
Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, E expr);
|
||||
}
|
||||
@ -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<S extends StatementNode> {
|
||||
void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, S stmt);
|
||||
}
|
||||
@ -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 中表达式节点进行语义分析的通用契约。
|
||||
* <p>
|
||||
* 各种具体的表达式分析器(如调用、二元运算、标识符、字面量等)需实现此接口,
|
||||
* 在 {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, ExpressionNode)}
|
||||
* 方法中完成类型推导、语义检查,并将发现的错误记录到上下文中。
|
||||
*
|
||||
* @param <E> 要分析的具体表达式节点类型,必须是 {@link ExpressionNode} 的子类型
|
||||
*/
|
||||
public interface ExpressionAnalyzer<E extends ExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对给定的表达式节点进行语义分析,并返回推导出的类型。
|
||||
* <p>
|
||||
* 实现者应在分析过程中根据节点语义:
|
||||
* <ul>
|
||||
* <li>校验子表达式类型并递归调用对应的分析器;</li>
|
||||
* <li>检查函数调用、运算符合法性;</li>
|
||||
* <li>必要时向 {@link Context#getErrors()} 添加 {@link org.jcnc.snow.compiler.semantic.error.SemanticError};</li>
|
||||
* <li>返回最终推导出的 {@link Type},以供上层表达式或语句分析使用。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ctx 全局上下文,提供模块注册表、错误收集、日志输出及分析器注册表等
|
||||
* @param mi 当前模块信息,用于跨模块调用和函数签名查找
|
||||
* @param fn 当前函数节点,可用于返回类型校验或其他函数级上下文
|
||||
* @param locals 当前作用域的符号表,包含已声明的变量及其类型
|
||||
* @param expr 待分析的表达式节点
|
||||
* @return 表达式的推导类型,用于后续类型兼容性检查和类型传播
|
||||
*/
|
||||
Type analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
FunctionNode fn,
|
||||
SymbolTable locals,
|
||||
E expr);
|
||||
}
|
||||
@ -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 中的语句节点执行语义检查。
|
||||
* <p>
|
||||
* 各具体的语句分析器(如声明、赋值、分支、循环、返回等)需实现此接口,
|
||||
* 在 {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, StatementNode)}
|
||||
* 方法中完成:
|
||||
* <ul>
|
||||
* <li>对自身语义结构进行校验(变量声明、类型匹配、作用域检查等);</li>
|
||||
* <li>递归调用其他已注册的语句或表达式分析器;</li>
|
||||
* <li>在发现错误时,通过 {@link Context#getErrors()} 记录 {@link org.jcnc.snow.compiler.semantic.error.SemanticError};</li>
|
||||
* <li>在上下文需要时记录日志以辅助调试。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <S> 要分析的具体语句节点类型,必须是 {@link StatementNode} 的子类型
|
||||
*/
|
||||
public interface StatementAnalyzer<S extends StatementNode> {
|
||||
|
||||
/**
|
||||
* 对给定的语句节点执行语义分析。
|
||||
*
|
||||
* @param ctx 全局上下文,提供模块注册表、错误收集、日志输出及分析器注册表等
|
||||
* @param mi 当前模块信息,用于检查模块导入和函数签名等上下文
|
||||
* @param fn 当前函数节点,可用于检查返回类型或函数级别作用域
|
||||
* @param locals 当前作用域的符号表,包含已声明的变量及其类型
|
||||
* @param stmt 待分析的语句节点实例
|
||||
*/
|
||||
void analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
FunctionNode fn,
|
||||
SymbolTable locals,
|
||||
S stmt);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 二元表达式分析器:负责对形如 <code>left op right</code> 的表达式进行类型推导和类型检查。
|
||||
* <p>
|
||||
* 支持以下运算符:
|
||||
* <ul>
|
||||
* <li><code>+</code>:如果任一操作数为 {@link BuiltinType#STRING},则结果为字符串拼接,否则如果都是整数则结果为整数</li>
|
||||
* <li><code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>:要求两个操作数都是整数,结果为整数</li>
|
||||
* <li><code><</code>, <code><=</code>, <code>></code>, <code>>=</code>, <code>==</code>, <code>!=</code>:
|
||||
* 要求两个操作数都是整数,结果为整数(表示真假)</li>
|
||||
* </ul>
|
||||
* 对于未知运算符或类型不匹配的情况,会在 {@link Context#getErrors() 错误列表} 中加入对应的
|
||||
* {@link SemanticError},并将结果类型降级为 {@link BuiltinType#INT} 以避免后续连锁错误。
|
||||
*/
|
||||
public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对给定的二元表达式节点进行语义分析,返回推导出的类型。
|
||||
*
|
||||
* @param ctx 全局上下文,包含模块表、错误收集、日志开关、注册表等
|
||||
* @param mi 当前正在分析的模块信息,用于查找导入、函数签名等
|
||||
* @param fn 当前正在分析的函数节点,用于处理返回类型检查等(可用于更复杂场景)
|
||||
* @param locals 当前作用域的符号表,包含已定义的局部变量及其类型
|
||||
* @param bin 待分析的 {@link BinaryExpressionNode} 节点,包含左子表达式、运算符、右子表达式
|
||||
* @return 推导出的 {@link Type}:
|
||||
* <ul>
|
||||
* <li>符合运算规则时返回 {@link BuiltinType#INT} 或 {@link BuiltinType#STRING}</li>
|
||||
* <li>类型不匹配或未知运算符时降级返回 {@link BuiltinType#INT}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@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<BinaryExpres
|
||||
}
|
||||
}
|
||||
case "-", "*", "/", "%" -> {
|
||||
// 数学运算,要求整数操作数
|
||||
if (left == BuiltinType.INT && right == BuiltinType.INT) {
|
||||
result = BuiltinType.INT;
|
||||
} else {
|
||||
@ -39,6 +78,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
|
||||
}
|
||||
}
|
||||
case "<", "<=", ">", ">=", "==", "!=" -> {
|
||||
// 比较运算,要求整数操作数,返回整数表示真假
|
||||
if (left == BuiltinType.INT && right == BuiltinType.INT) {
|
||||
result = BuiltinType.INT;
|
||||
} else {
|
||||
@ -46,19 +86,25 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
// 未知运算符
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 函数调用表达式分析器:负责对 <code>callee(arg1, arg2, ...)</code> 形式的调用进行
|
||||
* 语义检查和类型推导。
|
||||
* <p>
|
||||
* 支持两种调用方式:
|
||||
* <ul>
|
||||
* <li>模块函数调用:<code>ModuleName.func(...)</code>,要求模块已注册且已导入,或与当前模块同名</li>
|
||||
* <li>本模块函数调用:<code>func(...)</code></li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 分析内容包括:
|
||||
* <ol>
|
||||
* <li>解析目标模块与函数名;</li>
|
||||
* <li>检查函数是否已定义;</li>
|
||||
* <li>递归分析并收集所有实参的类型;</li>
|
||||
* <li>检查参数个数与参数类型是否与函数签名匹配;</li>
|
||||
* <li>返回函数签名中的返回类型;</li>
|
||||
* <li>在任何错误(未知模块、未导入模块、调用方式不支持、函数未定义、
|
||||
* 参数数量或类型不匹配)时,记录对应的 {@link SemanticError},
|
||||
* 并降级返回 {@link BuiltinType#INT} 以避免后续连锁错误。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对给定的调用表达式节点进行语义分析,返回推导出的类型。
|
||||
*
|
||||
* @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.log("错误: 不支持的调用方式 " + callee);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
} else if (callee instanceof IdentifierNode id) {
|
||||
functionName = id.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;
|
||||
}
|
||||
|
||||
// 处理本模块函数调用:func(...)
|
||||
} else if (callee instanceof IdentifierNode(String name)) {
|
||||
functionName = name;
|
||||
ctx.log("调用当前模块函数: " + functionName);
|
||||
} else {
|
||||
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<Type> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 标识符表达式分析器:负责对变量或常量等标识符节点进行语义检查,
|
||||
* 并返回其在当前作用域中的类型。
|
||||
* <p>
|
||||
* 如果未在符号表中找到对应的符号,将向错误列表中添加一条
|
||||
* {@link SemanticError},并返回 {@link BuiltinType#INT} 作为降级类型,
|
||||
* 以避免后续分析因缺失类型而连锁报错。
|
||||
*/
|
||||
public class IdentifierAnalyzer implements ExpressionAnalyzer<IdentifierNode> {
|
||||
|
||||
/**
|
||||
* 对给定的标识符节点进行语义分析。
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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} 节点进行类型推导。
|
||||
* <p>
|
||||
* 在本语言中,所有数字字面量均被视为整型,因此直接返回 {@link BuiltinType#INT}。
|
||||
* 本分析器不会产生任何语义错误。
|
||||
*/
|
||||
public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> {
|
||||
|
||||
/**
|
||||
* 对数字字面量节点进行语义分析。
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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} 节点进行类型推导。
|
||||
* <p>
|
||||
* 在本语言中,所有字符串字面量均被视为字符串类型,因此直接返回 {@link BuiltinType#STRING}。
|
||||
* 本分析器不会产生任何语义错误。
|
||||
*/
|
||||
public class StringLiteralAnalyzer implements ExpressionAnalyzer<StringLiteralNode> {
|
||||
|
||||
/**
|
||||
* 对字符串字面量节点进行语义分析。
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<ExpressionNode>
|
||||
/**
|
||||
* 默认兜底表达式分析器:用于处理所有未显式注册的 {@link ExpressionNode} 子类型。
|
||||
* <p>
|
||||
* 当遇到不支持或未实现的表达式节点时,本分析器会:
|
||||
* <ol>
|
||||
* <li>向 {@link Context#getErrors() 错误列表} 中添加一条 {@link SemanticError},</li>
|
||||
* <li>在日志中输出对应的错误信息,</li>
|
||||
* <li>并返回降级类型 {@link BuiltinType#INT} 以避免后续连锁报错。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param <E> 任何 {@link ExpressionNode} 子类型
|
||||
*/
|
||||
public class UnsupportedExpressionAnalyzer<E extends ExpressionNode>
|
||||
implements ExpressionAnalyzer<E> {
|
||||
|
||||
/**
|
||||
* 对未知或不支持的表达式节点进行处理。
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
@ -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} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析步骤:
|
||||
* <ol>
|
||||
* <li>检查目标变量是否已在当前作用域中声明且种类为 {@link SymbolKind#VARIABLE};</li>
|
||||
* <li>递归分析赋值右侧表达式,获取其类型;</li>
|
||||
* <li>检查右侧表达式类型是否与目标变量类型兼容;</li>
|
||||
* <li>在任一检查失败时,向 {@link Context#getErrors() 错误列表} 中添加对应的
|
||||
* {@link SemanticError} 并记录日志。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
|
||||
|
||||
/**
|
||||
* 对给定的赋值语句节点进行语义分析。
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析流程:
|
||||
* <ol>
|
||||
* <li>解析声明类型,如果未知则记录错误并降级为 {@link BuiltinType#INT};</li>
|
||||
* <li>在当前符号表中注册新变量,若名称已存在则记录重复声明错误;</li>
|
||||
* <li>如存在初始化表达式,递归分析表达式并检查其类型与声明类型的兼容性;</li>
|
||||
* <li>兼容性检查失败时记录类型不匹配错误。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class DeclarationAnalyzer implements StatementAnalyzer<DeclarationNode> {
|
||||
|
||||
/**
|
||||
* 对给定的声明节点进行语义分析。
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析流程:
|
||||
* <ol>
|
||||
* <li>对条件表达式进行类型推导,要求类型为 {@link BuiltinType#INT};</li>
|
||||
* <li>若条件类型不符,记录错误并降级继续分析;</li>
|
||||
* <li>分别递归分析 then 分支和 else 分支中的每个子语句;</li>
|
||||
* <li>不支持的子语句类型将由注册表中未匹配到的分析器处理(或被记录为错误)。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class IfAnalyzer implements StatementAnalyzer<IfNode> {
|
||||
|
||||
/**
|
||||
* 对给定的 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 -> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 递归分析 else 分支
|
||||
for (var stmt : ifn.elseBranch()) {
|
||||
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||
if (stAnalyzer != null) {
|
||||
stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析流程:
|
||||
* <ol>
|
||||
* <li>分析并执行初始化语句(initializer);</li>
|
||||
* <li>对循环条件表达式进行类型推导,要求为 {@link BuiltinType#INT};</li>
|
||||
* <li>分析并执行更新语句(update);</li>
|
||||
* <li>递归分析循环体(body)中的每个子语句;</li>
|
||||
* <li>在任何步骤发现类型或语义错误时,都会向 {@link Context#getErrors() 错误列表} 中添加对应的
|
||||
* {@link SemanticError} 并在日志中记录。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class LoopAnalyzer implements StatementAnalyzer<LoopNode> {
|
||||
|
||||
/**
|
||||
* 对给定的循环节点进行语义分析。
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,21 +11,65 @@ 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} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析过程包括:
|
||||
* <ol>
|
||||
* <li>获取当前函数的预期返回类型;</li>
|
||||
* <li>如果存在返回表达式,则递归分析其类型并与预期类型进行兼容性检查;</li>
|
||||
* <li>如果没有返回表达式且预期类型不是 {@link BuiltinType#VOID},则记录缺少返回值的错误;</li>
|
||||
* <li>遇到类型不兼容时,会向 {@link Context#getErrors() 错误列表} 中添加 {@link SemanticError},</li>
|
||||
* <li>所有错误同时会记录到日志以便调试。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class ReturnAnalyzer implements StatementAnalyzer<ReturnNode> {
|
||||
|
||||
/**
|
||||
* 对给定的 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.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.getErrors().add(new SemanticError(
|
||||
ret,
|
||||
"非 void 函数必须返回值"
|
||||
));
|
||||
ctx.log("错误: 非 void 函数缺少返回值");
|
||||
}
|
||||
});
|
||||
|
||||
@ -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<String, ModuleInfo> modules;
|
||||
/** 语义分析过程中收集的所有错误列表 */
|
||||
private final List<SemanticError> 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<String, ModuleInfo> modules,
|
||||
List<SemanticError> errors,
|
||||
boolean verbose,
|
||||
@ -25,26 +38,56 @@ public class Context {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有模块信息的映射。
|
||||
*
|
||||
* @return 模块名到 {@link ModuleInfo} 的 Map
|
||||
*/
|
||||
public Map<String, ModuleInfo> getModules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语义错误列表。
|
||||
* 分析过程中产生的 {@link SemanticError} 会被收集到此列表。
|
||||
*
|
||||
* @return 语义错误列表
|
||||
*/
|
||||
public List<SemanticError> 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} 实例。
|
||||
* <p>
|
||||
* 若名称在 {@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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,29 +8,28 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 保存模块级别的信息。
|
||||
* 模块信息:保存单个模块在语义分析阶段的元数据。
|
||||
* <p>
|
||||
* 模块信息包括:
|
||||
* 包含以下内容:
|
||||
* <ul>
|
||||
* <li>模块名(唯一标识一个模块)。</li>
|
||||
* <li>导入的其他模块名称集合。</li>
|
||||
* <li>模块中定义的函数签名映射(函数名到 {@link FunctionType} 的对应关系)。</li>
|
||||
* <li>模块名称(唯一标识);</li>
|
||||
* <li>该模块导入的其他模块名称集合;</li>
|
||||
* <li>该模块中定义的函数签名映射,将函数名映射到对应的 {@link FunctionType}。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 主要用于语义分析阶段,帮助管理模块之间的依赖关系和函数声明信息。
|
||||
* 在语义分析过程中,用于管理模块依赖关系和查找函数签名。
|
||||
*/
|
||||
public class ModuleInfo {
|
||||
/** 模块名称 */
|
||||
private final String name;
|
||||
/** 导入的模块名称集合 */
|
||||
/** 导入的其他模块名称集合 */
|
||||
private final Set<String> imports = new HashSet<>();
|
||||
/** 模块中定义的函数映射表:函数名 -> 函数类型 */
|
||||
/** 模块中定义的函数签名映射:函数名 -> 函数类型 */
|
||||
private final Map<String, FunctionType> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取导入的模块名称集合。
|
||||
* 获取该模块导入的其他模块名称集合。
|
||||
* <p>
|
||||
* 返回的是内部集合的直接引用,可以通过它进行增删操作。
|
||||
* 返回的 Set 为内部对象的直接引用,可对其进行添加或移除操作,以维护导入列表。
|
||||
*
|
||||
* @return 导入模块名称的集合
|
||||
* @return 导入模块名称的可变集合
|
||||
*/
|
||||
public Set<String> getImports() {
|
||||
return imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块中定义的函数映射表。
|
||||
* 获取模块中定义的函数签名映射表。
|
||||
* <p>
|
||||
* 返回的是函数名到函数类型 {@link FunctionType} 的映射表。
|
||||
* 返回的是内部映射的直接引用,可以通过它进行增删操作。
|
||||
* 键为函数名,值为对应的 {@link FunctionType}。
|
||||
* 返回的 Map 为内部对象的直接引用,可对其进行添加或移除操作,以维护函数签名列表。
|
||||
*
|
||||
* @return 函数映射表
|
||||
* @return 函数签名映射表
|
||||
*/
|
||||
public Map<String, FunctionType> getFunctions() {
|
||||
return functions;
|
||||
|
||||
@ -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,71 +16,114 @@ import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 重构后的语义分析器,采用注册表 + 分析器组合的分而治之设计。
|
||||
* 重构后的语义分析器,采用“注册表 + 分析器”组合的分而治之设计。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>初始化内置模块与类型映射;</li>
|
||||
* <li>注册用户模块及其函数签名与导入关系;</li>
|
||||
* <li>使用 StatementAnalyzer 与 ExpressionAnalyzer 进行递归语义检查;</li>
|
||||
* <li>收集并返回所有发现的语义错误。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SemanticAnalyzer {
|
||||
/**
|
||||
* 内置类型映射表
|
||||
* 内置类型映射表:将类型名称映射到对应的 {@link Type} 实例。
|
||||
*/
|
||||
public static final Map<String, Type> BUILTIN_TYPES = Map.of("int", BuiltinType.INT, "string", BuiltinType.STRING, "void", BuiltinType.VOID);
|
||||
public static final Map<String, Type> BUILTIN_TYPES = Map.of(
|
||||
"int", BuiltinType.INT,
|
||||
"string", BuiltinType.STRING,
|
||||
"void", BuiltinType.VOID
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
/** 所有已注册模块名称 -> {@link ModuleInfo} 的映射 */
|
||||
private final Map<String, ModuleInfo> modules = new HashMap<>();
|
||||
/** 收集语义分析过程中产生的所有错误 */
|
||||
private final List<SemanticError> 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}。
|
||||
* <p>
|
||||
* 包括:声明、赋值、分支、循环、返回、表达式语句以及
|
||||
* 数字、字符串、标识符、调用、二元、兜底表达式分析器。
|
||||
*/
|
||||
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(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(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
|
||||
// 兜底处理
|
||||
// 兜底:所有暂未实现的表达式都用 UnsupportedExpressionAnalyzer 处理
|
||||
// 默认兜底
|
||||
registry.registerExpressionAnalyzer(MemberExpressionNode.class, new UnsupportedExpressionAnalyzer<>());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
/**
|
||||
* 对给定的模块 AST 列表执行完整的语义分析。
|
||||
*
|
||||
* @param moduleNodes 所有待分析的 {@link ModuleNode} 列表
|
||||
* @return 收集到的所有 {@link SemanticError},如果无错误则返回空列表
|
||||
*/
|
||||
public List<SemanticError> analyze(List<ModuleNode> moduleNodes) {
|
||||
ctx.log("开始语义分析");
|
||||
initBuiltinModule();
|
||||
ctx.log("内置模块初始化完成");
|
||||
|
||||
registerUserModules(moduleNodes);
|
||||
ctx.log("用户模块注册完成: " + modules.keySet());
|
||||
|
||||
registerSignaturesAndImports(moduleNodes);
|
||||
ctx.log("函数签名与导入检查完成");
|
||||
|
||||
checkAllFunctions(moduleNodes);
|
||||
ctx.log("所有函数检查完成,错误总数: " + errors.size());
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
/**
|
||||
* 初始化内置工具模块及其函数签名。
|
||||
* <p>
|
||||
* 包括: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));
|
||||
@ -88,27 +132,41 @@ public class SemanticAnalyzer {
|
||||
modules.put(builtin.getName(), builtin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册所有用户模块到模块映射中(只保留名称,用于后续签名与导入检查)。
|
||||
*
|
||||
* @param mods AST 中的模块节点列表
|
||||
*/
|
||||
private void registerUserModules(List<ModuleNode> mods) {
|
||||
for (var mod : mods) {
|
||||
for (ModuleNode mod : mods) {
|
||||
modules.put(mod.name(), new ModuleInfo(mod.name()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为每个模块注册其导入关系与函数签名。
|
||||
* <p>
|
||||
* 对未知模块或类型均会记录 {@link SemanticError} 并进行默认降级处理。
|
||||
*
|
||||
* @param mods AST 中的模块节点列表
|
||||
*/
|
||||
private void registerSignaturesAndImports(List<ModuleNode> 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());
|
||||
}
|
||||
// 函数签名
|
||||
for (var fn : mod.functions()) {
|
||||
}
|
||||
|
||||
// 函数签名注册
|
||||
for (FunctionNode fn : mod.functions()) {
|
||||
List<Type> 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<ModuleNode> 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);
|
||||
|
||||
@ -5,21 +5,25 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
/**
|
||||
* 表示语义分析过程中发现的错误。
|
||||
* <p>
|
||||
* 每个语义错误包含两个部分:
|
||||
* 1. 发生错误的 AST(抽象语法树)节点 {@link Node};
|
||||
* 2. 错误的详细描述 {@link String}。
|
||||
* </p>
|
||||
* 每个语义错误包含以下内容:
|
||||
* <ul>
|
||||
* <li>{@link Node}:发生错误的 AST 节点,便于定位;</li>
|
||||
* <li>{@link String message}:错误的详细描述信息。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 使用 Java 16+ record 定义,自动生成构造方法、访问器(getters)、equals、hashCode 和 toString 方法。
|
||||
*
|
||||
* 该类以 record 形式定义,自动生成构造方法、访问器方法(getters)、equals、hashCode 以及 toString 方法。
|
||||
* @param node 触发语义错误的 AST 节点
|
||||
* @param message 错误的详细描述
|
||||
*/
|
||||
public record SemanticError(Node node, String message) {
|
||||
|
||||
/**
|
||||
* 返回语义错误的字符串表示。
|
||||
* <p>
|
||||
* 格式为:"Semantic error at [节点信息]: [错误信息]"。
|
||||
* 方便在日志输出或调试过程中快速定位问题。
|
||||
* </p>
|
||||
* 返回语义错误的字符串表示,格式如下:
|
||||
* <pre>
|
||||
* Semantic error at [节点信息]: [错误信息]
|
||||
* </pre>
|
||||
* 便于在日志输出或调试时快速查看定位。
|
||||
*
|
||||
* @return 格式化后的错误信息字符串
|
||||
*/
|
||||
|
||||
@ -3,21 +3,25 @@ package org.jcnc.snow.compiler.semantic.symbol;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* 表示符号表(Symbol Table)中的一条符号记录。
|
||||
* 符号表中的一条符号记录,表示语言中的命名实体(如变量、函数等)。
|
||||
* <p>
|
||||
* 每条符号记录包含以下三个部分信息:
|
||||
* 每条符号记录包括三个核心属性:
|
||||
* <ul>
|
||||
* <li><b>name</b>:符号的名称,如变量名、函数名等。</li>
|
||||
* <li><b>type</b>:符号的类型信息,通常对应变量类型、函数返回值类型等。</li>
|
||||
* <li><b>kind</b>:符号的种类,指明符号是变量、函数、类等 {@link SymbolKind}。</li>
|
||||
* <li><b>name</b>:符号名称,例如变量名或函数名;</li>
|
||||
* <li><b>type</b>:符号的类型信息,对于变量是变量类型,对于函数是返回类型;</li>
|
||||
* <li><b>kind</b>:符号的种类,由 {@link SymbolKind} 枚举区分(变量、函数等)。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 使用 Java 16+ record 定义,自动生成:
|
||||
* <ul>
|
||||
* <li>构造方法;</li>
|
||||
* <li>访问器方法(getters);</li>
|
||||
* <li>equals、hashCode;</li>
|
||||
* <li>toString 方法。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* 本类使用 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) { }
|
||||
|
||||
@ -1,25 +1,33 @@
|
||||
package org.jcnc.snow.compiler.semantic.symbol;
|
||||
|
||||
/**
|
||||
* 表示符号的种类(Symbol Kind)。
|
||||
* 符号种类枚举:用于在符号表中区分不同类型的命名实体。
|
||||
* <p>
|
||||
* 用于在符号表中区分不同类型的符号,例如变量、函数、模块等。
|
||||
* 在语义分析过程中,不同种类的符号需要不同的处理策略,
|
||||
* 例如变量声明、函数调用、模块导入等。
|
||||
* </p>
|
||||
*
|
||||
* 支持的符号种类包括:
|
||||
* <ul>
|
||||
* <li><b>VARIABLE</b>:变量符号,例如局部变量、全局变量等。</li>
|
||||
* <li><b>FUNCTION</b>:函数符号,包括普通函数、方法等。</li>
|
||||
* <li><b>MODULE</b>:模块符号,表示一个模块或命名空间。</li>
|
||||
* <li>{@link #VARIABLE} – 变量符号(局部变量、全局变量、成员变量等);</li>
|
||||
* <li>{@link #FUNCTION} – 函数符号(自由函数、方法、构造函数等);</li>
|
||||
* <li>{@link #MODULE} – 模块符号,表示一个命名空间或模块;</li>
|
||||
* </ul>
|
||||
*/
|
||||
public enum SymbolKind {
|
||||
/** 变量符号,例如局部变量、全局变量等。 */
|
||||
/**
|
||||
* 变量符号,例如在函数或全局作用域中声明的变量。
|
||||
*/
|
||||
VARIABLE,
|
||||
|
||||
/** 函数符号,表示一个可调用的功能单元。 */
|
||||
/**
|
||||
* 函数符号,表示可调用的函数或方法,
|
||||
* 用于函数签名注册和调用时的类型匹配。
|
||||
*/
|
||||
FUNCTION,
|
||||
|
||||
/** 模块符号,表示一个模块或命名空间。 */
|
||||
/**
|
||||
* 模块符号,表示一个模块或命名空间,
|
||||
* 用于管理模块导入和跨模块引用。
|
||||
*/
|
||||
MODULE
|
||||
}
|
||||
|
||||
@ -4,26 +4,26 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 符号表(Symbol Table),用于管理符号(如变量、函数、模块等)及其作用域信息。
|
||||
* 符号表(Symbol Table):用于管理命名实体(如变量、函数、模块等)的作用域及其类型信息。
|
||||
* <p>
|
||||
* 支持链式作用域(Nested Scope)管理:
|
||||
* 本实现支持链式作用域(Nested Scope):
|
||||
* <ul>
|
||||
* <li>每个符号表可以有一个父符号表 {@link #parent},用于实现作用域嵌套。</li>
|
||||
* <li>符号表内部使用 {@link Map} 保存当前作用域内定义的符号。</li>
|
||||
* <li>每个 {@code SymbolTable} 可拥有一个父作用域,若查找失败可递归向上查找;</li>
|
||||
* <li>当前作用域使用内部 {@link Map} 保存定义的符号;</li>
|
||||
* <li>提供符号定义与符号解析两种基本操作。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class SymbolTable {
|
||||
/** 父作用域符号表,如果为 null 则表示当前为最外层作用域。 */
|
||||
/** 父作用域符号表;若为 null 则表示当前为最外层作用域。 */
|
||||
private final SymbolTable parent;
|
||||
|
||||
/** 当前作用域中定义的符号集合,按名称索引。 */
|
||||
/** 当前作用域内定义的符号映射:符号名 -> {@link Symbol}。 */
|
||||
private final Map<String, Symbol> 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 {
|
||||
/**
|
||||
* 在当前作用域中定义一个新的符号。
|
||||
* <p>
|
||||
* 如果当前作用域中已存在同名符号,则定义失败并返回 {@code false};
|
||||
* 否则,将符号添加到符号表中并返回 {@code true}。
|
||||
* </p>
|
||||
* 如果当前作用域已有同名符号,则定义失败并返回 {@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 {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找符号。
|
||||
* <p>
|
||||
* 查找顺序为:
|
||||
* 根据名称解析符号,支持嵌套作用域查找:
|
||||
* <ol>
|
||||
* <li>先在当前作用域中查找。</li>
|
||||
* <li>如果当前作用域未找到,递归向父作用域查找。</li>
|
||||
* <li>若最终未找到,返回 {@code null}。</li>
|
||||
* <li>优先在当前作用域查找;</li>
|
||||
* <li>若未找到且存在父作用域,则递归在父作用域查找;</li>
|
||||
* <li>最终未找到则返回 {@code null}。</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
package org.jcnc.snow.compiler.semantic.type;
|
||||
|
||||
/**
|
||||
* 内置基础类型枚举。
|
||||
* 内置基础类型枚举:定义编译器中最基本的三种类型。
|
||||
* <p>
|
||||
* 本枚举定义了编译器中最基本的三种内置类型:
|
||||
* 枚举值:
|
||||
* <ul>
|
||||
* <li>INT:整数类型,同时也可用于表示布尔值(真假值)。</li>
|
||||
* <li>STRING:字符串类型。</li>
|
||||
* <li>VOID:无返回值类型,通常用于函数无返回值的情况。</li>
|
||||
* <li>{@link #INT} – 整数类型,也可用于表示布尔值(真/假);</li>
|
||||
* <li>{@link #STRING} – 字符串类型;</li>
|
||||
* <li>{@link #VOID} – 空类型,用于表示无返回值的函数。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 实现了 Type 接口,提供了基本的类型兼容性判断和字符串表示方法。
|
||||
* 本枚举实现了 {@link Type} 接口,提供了基本的类型兼容性判断和友好的字符串表示方法。
|
||||
*/
|
||||
public enum BuiltinType implements Type {
|
||||
/**
|
||||
* 整数类型,也可用于布尔值。
|
||||
* 整数类型,也可用于表示布尔值(真假值)。
|
||||
*/
|
||||
INT,
|
||||
|
||||
@ -24,28 +24,27 @@ public enum BuiltinType implements Type {
|
||||
STRING,
|
||||
|
||||
/**
|
||||
* 空类型,用于表示无返回值的情况。
|
||||
* 空类型,通常用于无返回值的函数。
|
||||
*/
|
||||
VOID;
|
||||
|
||||
/**
|
||||
* 判断当前类型是否与另一个类型兼容。
|
||||
* <p>
|
||||
* 兼容的条件是两个类型必须完全相同(引用相等)。
|
||||
* 兼容条件:两个类型实例必须相同(同一枚举常量)。
|
||||
*
|
||||
* @param other 另一个需要检查兼容性的类型
|
||||
* @return 如果类型完全相同则返回 true,否则返回 false
|
||||
* @return 如果类型完全相同则返回 {@code true};否则返回 {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean isCompatible(Type other) {
|
||||
// 只有当两个类型对象是同一个实例时才认为兼容
|
||||
return this == other;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将类型转换为小写字符串形式。
|
||||
* 返回小写形式的类型名称,便于输出和日志展示。
|
||||
* <p>
|
||||
* 例如:INT 会转换为 "int"。
|
||||
* 例如:{@link #INT} 返回 {@code "int"};{@link #VOID} 返回 {@code "void"}。
|
||||
*
|
||||
* @return 当前类型的小写字符串表示
|
||||
*/
|
||||
|
||||
@ -4,20 +4,20 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 表示函数类型。
|
||||
* 表示函数类型。由参数类型列表和返回类型共同确定。
|
||||
* <p>
|
||||
* 一个函数类型由参数类型列表和返回类型共同确定。
|
||||
* 例如:一个接受两个 int 参数并返回 string 的函数,其类型描述为 (int, int) -> string。
|
||||
* 例如:一个接受两个 int 参数并返回 string 的函数,其类型描述为 <code>(int, int) -> string</code>。
|
||||
* <p>
|
||||
* 本类是一个 Java record,自动生成基本的构造方法、访问器、equals 和 hashCode 方法,
|
||||
* 并实现了 {@link Type} 接口,支持类型兼容性检查和字符串表示。
|
||||
* 该 record 自动生成了构造方法、访问器、equals、hashCode。
|
||||
* 并实现了 {@link Type} 接口,可用于类型兼容性检查和字符串表示。
|
||||
*
|
||||
* @param paramTypes 参数类型列表
|
||||
* @param returnType 返回类型
|
||||
*/
|
||||
public record FunctionType(List<Type> paramTypes, Type returnType) implements Type {
|
||||
|
||||
/**
|
||||
* 构造函数类型。
|
||||
* <p>
|
||||
* {@link java.util.Collections#unmodifiableList(List)} 包装成不可变列表,
|
||||
* 保证函数类型对象本身的安全性和不可变性。
|
||||
* 构造函数类型,将参数类型列表包装成不可变列表以保证安全性。
|
||||
*
|
||||
* @param paramTypes 参数类型列表
|
||||
* @param returnType 返回类型
|
||||
@ -30,30 +30,30 @@ public record FunctionType(List<Type> paramTypes, Type returnType) implements Ty
|
||||
/**
|
||||
* 判断当前函数类型是否与另一个类型兼容。
|
||||
* <p>
|
||||
* 兼容条件为:
|
||||
* 兼容条件:
|
||||
* <ul>
|
||||
* <li>另一类型也是 FunctionType。</li>
|
||||
* <li>返回类型兼容。</li>
|
||||
* <li>另一对象也为 {@code FunctionType};</li>
|
||||
* <li>返回类型兼容;</li>
|
||||
* <li>参数类型列表完全相等(顺序和值一致)。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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<Type> types, Type type))) return false;
|
||||
return returnType.isCompatible(type)
|
||||
&& paramTypes.equals(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回函数类型的字符串表示。
|
||||
* <p>
|
||||
* 格式为:"(参数类型列表) -> 返回类型",例如:<code>(int, string) -> void</code>。
|
||||
* 格式为:<code>(param1, param2, ...) -> returnType</code>,
|
||||
* 例如 <code>(int, string) -> void</code>。
|
||||
*
|
||||
* @return 函数类型的字符串表示
|
||||
* @return 函数类型的描述字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -61,32 +61,30 @@ public record FunctionType(List<Type> paramTypes, Type returnType) implements Ty
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前函数类型是否与另一个对象相等。
|
||||
* 判断两个函数类型是否相等。
|
||||
* <p>
|
||||
* 相等条件为:
|
||||
* 相等条件:
|
||||
* <ul>
|
||||
* <li>对象引用相同,或</li>
|
||||
* <li>对象是 FunctionType 类型,且参数类型列表和返回类型都相等。</li>
|
||||
* <li>引用相同;</li>
|
||||
* <li>或都是 {@code FunctionType},且参数列表和返回类型都相同。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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<Type> types, Type type))) return false;
|
||||
return returnType.equals(type)
|
||||
&& paramTypes.equals(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回函数类型的哈希码。
|
||||
* <p>
|
||||
* 计算规则是基于参数类型列表和返回类型,保证与 {@link #equals(Object)} 方法一致。
|
||||
* 计算函数类型的哈希码。基于参数类型列表和返回类型,
|
||||
* 与 {@link #equals(Object)} 保持一致。
|
||||
*
|
||||
* @return 函数类型的哈希值
|
||||
* @return 哈希码
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
@ -1,11 +1,22 @@
|
||||
package org.jcnc.snow.compiler.semantic.type;
|
||||
|
||||
/**
|
||||
* 类型接口。所有具体类型(内置类型、函数类型等)都应实现此接口。
|
||||
* 类型接口:所有类型(包括内置类型、函数类型等)均需实现此接口,
|
||||
* 用于在语义分析中进行类型兼容性检查和统一表示。
|
||||
*/
|
||||
public interface Type {
|
||||
/**
|
||||
* 判断此类型是否可接受赋值或兼容另一个类型。
|
||||
* 判断当前类型是否与另一个类型兼容。
|
||||
* <p>
|
||||
* 例如:
|
||||
* <ul>
|
||||
* <li>对于内置类型,只有完全相同的枚举常量才兼容;</li>
|
||||
* <li>对于函数类型,需要参数列表和返回类型同时兼容;</li>
|
||||
* <li>对于其他复合类型,可按语言规则自行实现。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param other 要检查兼容性的另一个类型
|
||||
* @return 如果兼容则返回 {@code true},否则返回 {@code false}
|
||||
*/
|
||||
boolean isCompatible(Type other);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user