修改doc
This commit is contained in:
parent
55526d530a
commit
3fb66731df
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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},包含 initializer、condition、update 和 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) {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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<>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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>定义语言中所有可识别的基础类型(如 int、float、string 等);</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());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()));
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 定义,自动生成构造方法、访问器(getters)、equals、hashCode 和 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() {
|
||||||
|
|||||||
@ -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} 枚举表示(例如 VARIABLE、FUNCTION 等)。</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>equals、hashCode;</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) { }
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否为数值类型(byte、short、int、long、float、double)。
|
* 判断当前类型是否为数值类型。
|
||||||
|
* <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() {
|
||||||
|
|||||||
@ -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 自动生成了构造方法、访问器、equals、hashCode。
|
* 例如,一个函数接受两个 {@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() {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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("语义分析通过,没有发现错误。");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user