增加注释

This commit is contained in:
Luke 2025-04-28 11:55:46 +08:00
parent 35efce2dd4
commit 48751436fa
26 changed files with 948 additions and 296 deletions

View File

@ -2,34 +2,90 @@ package org.jcnc.snow.compiler.semantic.analyzers;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.analyzers.expression.UnsupportedExpressionAnalyzer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Analyzer 注册表负责按节点类型分发对应的 Analyzer * Analyzer 注册表负责维护并按 AST 节点类型分发对应的 StatementAnalyzer ExpressionAnalyzer
* <p>
* 支持
* <ul>
* <li>注册任意自定义的语句或表达式分析器</li>
* <li>按节点实例动态查找并返回对应分析器</li>
* <li>对未注册的表达式节点提供默认兜底分析器 {@link UnsupportedExpressionAnalyzer}</li>
* </ul>
*/ */
public class AnalyzerRegistry { public class AnalyzerRegistry {
/** 语句节点类型 -> 分析器 映射 */
private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>(); private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
/** 表达式节点类型 -> 分析器 映射 */
private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>(); private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
/** 默认兜底表达式分析器,处理所有未显式注册的 ExpressionNode 子类型 */
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported =
new UnsupportedExpressionAnalyzer<>();
// --- 注册方法 ----------------------------------------------------------- // --- 注册方法 -----------------------------------------------------------
public <S extends StatementNode> void registerStatementAnalyzer(Class<S> cls, StatementAnalyzer<S> analyzer) {
/**
* 注册一个 StatementAnalyzer用于处理指定类型的语句节点
*
* @param cls 语句节点的 Class 对象
* @param analyzer 针对该类型节点的分析器实例
* @param <S> 具体的 StatementNode 子类型
*/
public <S extends StatementNode> void registerStatementAnalyzer(
Class<S> cls,
StatementAnalyzer<S> analyzer
) {
stmtAnalyzers.put(cls, analyzer); stmtAnalyzers.put(cls, analyzer);
} }
public <E extends ExpressionNode> void registerExpressionAnalyzer(Class<E> cls, ExpressionAnalyzer<E> analyzer) { /**
* 注册一个 ExpressionAnalyzer用于处理指定类型的表达式节点
*
* @param cls 表达式节点的 Class 对象
* @param analyzer 针对该类型节点的分析器实例
* @param <E> 具体的 ExpressionNode 子类型
*/
public <E extends ExpressionNode> void registerExpressionAnalyzer(
Class<E> cls,
ExpressionAnalyzer<E> analyzer
) {
exprAnalyzers.put(cls, analyzer); exprAnalyzers.put(cls, analyzer);
} }
// --- 获取方法 ----------------------------------------------------------- // --- 获取方法 -----------------------------------------------------------
/**
* 根据给定的语句节点实例返回对应的 StatementAnalyzer
* <p>
* 若未注册任何分析器则返回 null
*
* @param stmt 要分析的 StatementNode 实例
* @param <S> 具体的 StatementNode 子类型
* @return 对应的 StatementAnalyzer null
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) { public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) {
return (StatementAnalyzer<S>) stmtAnalyzers.get(stmt.getClass()); return (StatementAnalyzer<S>) stmtAnalyzers.get(stmt.getClass());
} }
/**
* 根据给定的表达式节点实例返回对应的 ExpressionAnalyzer
* <p>
* 若未注册任何分析器则返回 {@link #defaultUnsupported} 兜底
*
* @param expr 要分析的 ExpressionNode 实例
* @param <E> 具体的 ExpressionNode 子类型
* @return 对应的 ExpressionAnalyzer可能是默认兜底分析器
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) { public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) {
return (ExpressionAnalyzer<E>) exprAnalyzers.get(expr.getClass()); return (ExpressionAnalyzer<E>)
exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
} }
} }

View File

@ -1,15 +0,0 @@
package org.jcnc.snow.compiler.semantic.analyzers;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 表达式分析器接口
*/
public interface ExpressionAnalyzer<E extends ExpressionNode> {
Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, E expr);
}

View File

@ -1,14 +0,0 @@
package org.jcnc.snow.compiler.semantic.analyzers;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
/**
* 语句分析器接口
*/
public interface StatementAnalyzer<S extends StatementNode> {
void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, S stmt);
}

View File

@ -0,0 +1,44 @@
package org.jcnc.snow.compiler.semantic.analyzers.base;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 表达式分析器接口定义了对 AST 中表达式节点进行语义分析的通用契约
* <p>
* 各种具体的表达式分析器如调用二元运算标识符字面量等需实现此接口
* {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, ExpressionNode)}
* 方法中完成类型推导语义检查并将发现的错误记录到上下文中
*
* @param <E> 要分析的具体表达式节点类型必须是 {@link ExpressionNode} 的子类型
*/
public interface ExpressionAnalyzer<E extends ExpressionNode> {
/**
* 对给定的表达式节点进行语义分析并返回推导出的类型
* <p>
* 实现者应在分析过程中根据节点语义
* <ul>
* <li>校验子表达式类型并递归调用对应的分析器</li>
* <li>检查函数调用运算符合法性</li>
* <li>必要时向 {@link Context#getErrors()} 添加 {@link org.jcnc.snow.compiler.semantic.error.SemanticError}</li>
* <li>返回最终推导出的 {@link Type}以供上层表达式或语句分析使用</li>
* </ul>
*
* @param ctx 全局上下文提供模块注册表错误收集日志输出及分析器注册表等
* @param mi 当前模块信息用于跨模块调用和函数签名查找
* @param fn 当前函数节点可用于返回类型校验或其他函数级上下文
* @param locals 当前作用域的符号表包含已声明的变量及其类型
* @param expr 待分析的表达式节点
* @return 表达式的推导类型用于后续类型兼容性检查和类型传播
*/
Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
E expr);
}

View File

@ -0,0 +1,40 @@
package org.jcnc.snow.compiler.semantic.analyzers.base;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
/**
* 语句分析器接口定义如何对 AST 中的语句节点执行语义检查
* <p>
* 各具体的语句分析器如声明赋值分支循环返回等需实现此接口
* {@link #analyze(Context, ModuleInfo, FunctionNode, SymbolTable, StatementNode)}
* 方法中完成
* <ul>
* <li>对自身语义结构进行校验变量声明类型匹配作用域检查等</li>
* <li>递归调用其他已注册的语句或表达式分析器</li>
* <li>在发现错误时通过 {@link Context#getErrors()} 记录 {@link org.jcnc.snow.compiler.semantic.error.SemanticError}</li>
* <li>在上下文需要时记录日志以辅助调试</li>
* </ul>
*
* @param <S> 要分析的具体语句节点类型必须是 {@link StatementNode} 的子类型
*/
public interface StatementAnalyzer<S extends StatementNode> {
/**
* 对给定的语句节点执行语义分析
*
* @param ctx 全局上下文提供模块注册表错误收集日志输出及分析器注册表等
* @param mi 当前模块信息用于检查模块导入和函数签名等上下文
* @param fn 当前函数节点可用于检查返回类型或函数级别作用域
* @param locals 当前作用域的符号表包含已声明的变量及其类型
* @param stmt 待分析的语句节点实例
*/
void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
S stmt);
}

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode; import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -10,19 +10,57 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 二元表达式分析器负责对形如 <code>left op right</code> 的表达式进行类型推导和类型检查
* <p>
* 支持以下运算符
* <ul>
* <li><code>+</code>如果任一操作数为 {@link BuiltinType#STRING}则结果为字符串拼接否则如果都是整数则结果为整数</li>
* <li><code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>要求两个操作数都是整数结果为整数</li>
* <li><code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, <code>&gt;=</code>, <code>==</code>, <code>!=</code>
* 要求两个操作数都是整数结果为整数表示真假</li>
* </ul>
* 对于未知运算符或类型不匹配的情况会在 {@link Context#getErrors() 错误列表} 中加入对应的
* {@link SemanticError}并将结果类型降级为 {@link BuiltinType#INT} 以避免后续连锁错误
*/
public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpressionNode> { public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpressionNode> {
/**
* 对给定的二元表达式节点进行语义分析返回推导出的类型
*
* @param ctx 全局上下文包含模块表错误收集日志开关注册表等
* @param mi 当前正在分析的模块信息用于查找导入函数签名等
* @param fn 当前正在分析的函数节点用于处理返回类型检查等可用于更复杂场景
* @param locals 当前作用域的符号表包含已定义的局部变量及其类型
* @param bin 待分析的 {@link BinaryExpressionNode} 节点包含左子表达式运算符右子表达式
* @return 推导出的 {@link Type}
* <ul>
* <li>符合运算规则时返回 {@link BuiltinType#INT} {@link BuiltinType#STRING}</li>
* <li>类型不匹配或未知运算符时降级返回 {@link BuiltinType#INT}</li>
* </ul>
*/
@Override @Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, BinaryExpressionNode bin) { public Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
BinaryExpressionNode bin) {
ctx.log("检查二元表达式: " + bin.operator()); ctx.log("检查二元表达式: " + bin.operator());
var leftA = ctx.getRegistry().getExpressionAnalyzer(bin.left());
Type left = leftA.analyze(ctx, mi, fn, locals, bin.left()); // 递归分析左右两边的子表达式
var rightA = ctx.getRegistry().getExpressionAnalyzer(bin.right()); var leftAnalyzer = ctx.getRegistry().getExpressionAnalyzer(bin.left());
Type right = rightA.analyze(ctx, mi, fn, locals, bin.right()); Type left = leftAnalyzer.analyze(ctx, mi, fn, locals, bin.left());
var rightAnalyzer = ctx.getRegistry().getExpressionAnalyzer(bin.right());
Type right = rightAnalyzer.analyze(ctx, mi, fn, locals, bin.right());
String op = bin.operator(); String op = bin.operator();
Type result; Type result;
// 根据运算符做类型推导
switch (op) { switch (op) {
case "+" -> { case "+" -> {
// 字符串拼接 整数相加
if (left == BuiltinType.STRING || right == BuiltinType.STRING) { if (left == BuiltinType.STRING || right == BuiltinType.STRING) {
result = BuiltinType.STRING; result = BuiltinType.STRING;
} else if (left == BuiltinType.INT && right == BuiltinType.INT) { } else if (left == BuiltinType.INT && right == BuiltinType.INT) {
@ -32,6 +70,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
} }
} }
case "-", "*", "/", "%" -> { case "-", "*", "/", "%" -> {
// 数学运算要求整数操作数
if (left == BuiltinType.INT && right == BuiltinType.INT) { if (left == BuiltinType.INT && right == BuiltinType.INT) {
result = BuiltinType.INT; result = BuiltinType.INT;
} else { } else {
@ -39,6 +78,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
} }
} }
case "<", "<=", ">", ">=", "==", "!=" -> { case "<", "<=", ">", ">=", "==", "!=" -> {
// 比较运算要求整数操作数返回整数表示真假
if (left == BuiltinType.INT && right == BuiltinType.INT) { if (left == BuiltinType.INT && right == BuiltinType.INT) {
result = BuiltinType.INT; result = BuiltinType.INT;
} else { } else {
@ -46,19 +86,25 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
} }
} }
default -> { default -> {
// 未知运算符
ctx.getErrors().add(new SemanticError(bin, "未知运算符: " + op)); ctx.getErrors().add(new SemanticError(bin, "未知运算符: " + op));
ctx.log("错误: 未知运算符 " + op); ctx.log("错误: 未知运算符 " + op);
return BuiltinType.INT; return BuiltinType.INT;
} }
} }
// 如果推导失败类型不匹配记录错误并降级
if (result == null) { if (result == null) {
ctx.getErrors().add(new SemanticError(bin, String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right))); ctx.getErrors().add(new SemanticError(
bin,
String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right))
);
ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right); ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right);
result = BuiltinType.INT; result = BuiltinType.INT;
} else { } else {
ctx.log("二元表达式推导类型: " + result); ctx.log("二元表达式推导类型: " + result);
} }
return result; return result;
} }
} }

View File

@ -5,7 +5,7 @@ import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode; import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode; import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -17,62 +17,126 @@ import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* 函数调用表达式分析器负责对 <code>callee(arg1, arg2, ...)</code> 形式的调用进行
* 语义检查和类型推导
* <p>
* 支持两种调用方式
* <ul>
* <li>模块函数调用<code>ModuleName.func(...)</code>要求模块已注册且已导入或与当前模块同名</li>
* <li>本模块函数调用<code>func(...)</code></li>
* </ul>
* <p>
* 分析内容包括
* <ol>
* <li>解析目标模块与函数名</li>
* <li>检查函数是否已定义</li>
* <li>递归分析并收集所有实参的类型</li>
* <li>检查参数个数与参数类型是否与函数签名匹配</li>
* <li>返回函数签名中的返回类型</li>
* <li>在任何错误未知模块未导入模块调用方式不支持函数未定义
* 参数数量或类型不匹配记录对应的 {@link SemanticError}
* 并降级返回 {@link BuiltinType#INT} 以避免后续连锁错误</li>
* </ol>
*/
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> { public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
/**
* 对给定的调用表达式节点进行语义分析返回推导出的类型
*
* @param ctx 全局上下文包含模块表错误收集日志输出分析器注册表等
* @param mi 当前正在分析的模块信息用于查找导入列表和函数签名
* @param fn 当前正在分析的函数节点可用于更复杂的上下文校验
* @param locals 当前作用域的符号表包含已定义的变量及其类型
* @param call 待分析的 {@link CallExpressionNode}包含被调用的表达式和实参列表
* @return 推导出的函数返回类型若发生任何错误则降级返回 {@link BuiltinType#INT}
*/
@Override @Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, CallExpressionNode call) { public Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
CallExpressionNode call) {
ctx.log("检查函数调用: " + call.callee()); ctx.log("检查函数调用: " + call.callee());
ModuleInfo target = mi; ModuleInfo target = mi;
String functionName; String functionName;
ExpressionNode callee = call.callee(); ExpressionNode callee = call.callee();
// 处理模块限定调用ModuleName.func(...)
if (callee instanceof MemberExpressionNode(var object, String member)) { if (callee instanceof MemberExpressionNode(var object, String member)) {
if (object instanceof IdentifierNode id) { if (object instanceof IdentifierNode(String name)) {
String moduleName = id.name(); if (!ctx.getModules().containsKey(name)
if (!ctx.getModules().containsKey(moduleName) || || (!mi.getImports().contains(name) && !mi.getName().equals(name))) {
(!mi.getImports().contains(moduleName) && !mi.getName().equals(moduleName))) { ctx.getErrors().add(new SemanticError(callee,
ctx.getErrors().add(new SemanticError(callee, "未知或未导入模块: " + moduleName)); "未知或未导入模块: " + name));
ctx.log("错误: 未导入模块 " + moduleName); ctx.log("错误: 未导入模块 " + name);
return BuiltinType.INT; return BuiltinType.INT;
} }
target = ctx.getModules().get(moduleName); target = ctx.getModules().get(name);
functionName = member; functionName = member;
ctx.log("调用模块函数: " + moduleName + "." + member); ctx.log("调用模块函数: " + name + "." + member);
} else { } else {
ctx.getErrors().add(new SemanticError(callee, "不支持的调用方式: " + callee)); ctx.getErrors().add(new SemanticError(callee,
"不支持的调用方式: " + callee));
ctx.log("错误: 不支持的调用方式 " + callee); ctx.log("错误: 不支持的调用方式 " + callee);
return BuiltinType.INT; return BuiltinType.INT;
} }
} else if (callee instanceof IdentifierNode id) {
functionName = id.name(); // 处理本模块函数调用func(...)
} else if (callee instanceof IdentifierNode(String name)) {
functionName = name;
ctx.log("调用当前模块函数: " + functionName); ctx.log("调用当前模块函数: " + functionName);
} else { } else {
ctx.getErrors().add(new SemanticError(callee, "不支持的调用方式: " + callee)); ctx.getErrors().add(new SemanticError(callee,
"不支持的调用方式: " + callee));
ctx.log("错误: 不支持的调用方式 " + callee); ctx.log("错误: 不支持的调用方式 " + callee);
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, "函数未定义: " + functionName)); ctx.getErrors().add(new SemanticError(callee,
"函数未定义: " + functionName));
ctx.log("错误: 函数未定义 " + functionName); ctx.log("错误: 函数未定义 " + functionName);
return BuiltinType.INT; return BuiltinType.INT;
} }
// 分析所有实参并收集类型
List<Type> args = new ArrayList<>(); List<Type> args = new ArrayList<>();
for (var argExpr : call.arguments()) { for (var argExpr : call.arguments()) {
var argAnalyzer = ctx.getRegistry().getExpressionAnalyzer(argExpr); var argAnalyzer = ctx.getRegistry().getExpressionAnalyzer(argExpr);
args.add(argAnalyzer.analyze(ctx, mi, fn, locals, argExpr)); args.add(argAnalyzer.analyze(ctx, mi, fn, locals, argExpr));
} }
// 参数数量检查
if (args.size() != ft.paramTypes().size()) { if (args.size() != ft.paramTypes().size()) {
ctx.getErrors().add(new SemanticError(call, "参数数量不匹配: 期望 " + ft.paramTypes().size() + " 个, 实际 " + args.size() + "")); ctx.getErrors().add(new SemanticError(call,
ctx.log("错误: 参数数量不匹配: 期望 " + ft.paramTypes().size() + ", 实际 " + args.size()); "参数数量不匹配: 期望 "
+ ft.paramTypes().size()
+ " 个, 实际 "
+ args.size()
+ ""));
ctx.log("错误: 参数数量不匹配: 期望 "
+ ft.paramTypes().size()
+ ", 实际 "
+ args.size());
} else { } else {
// 参数类型检查
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
if (!ft.paramTypes().get(i).isCompatible(args.get(i))) { Type expected = ft.paramTypes().get(i);
ctx.getErrors().add(new SemanticError(call, String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s", i, ft.paramTypes().get(i), args.get(i)))); Type actual = args.get(i);
ctx.log("错误: 参数类型不匹配 (位置 " + i + "): 期望 " + ft.paramTypes().get(i) + ", 实际 " + args.get(i)); if (!expected.isCompatible(actual)) {
ctx.getErrors().add(new SemanticError(call,
String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
i, expected, actual)));
ctx.log("错误: 参数类型不匹配 (位置 "
+ i
+ "): 期望 "
+ expected
+ ", 实际 "
+ actual);
} }
} }
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode; import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -11,15 +11,44 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 标识符表达式分析器负责对变量或常量等标识符节点进行语义检查
* 并返回其在当前作用域中的类型
* <p>
* 如果未在符号表中找到对应的符号将向错误列表中添加一条
* {@link SemanticError}并返回 {@link BuiltinType#INT} 作为降级类型
* 以避免后续分析因缺失类型而连锁报错
*/
public class IdentifierAnalyzer implements ExpressionAnalyzer<IdentifierNode> { public class IdentifierAnalyzer implements ExpressionAnalyzer<IdentifierNode> {
/**
* 对给定的标识符节点进行语义分析
*
* @param ctx 全局上下文包含模块信息错误收集和分析器注册表等
* @param mi 当前模块信息用于跨模块引用时的额外检查此处暂未使用
* @param fn 当前函数节点可用于更复杂的上下文校验此处暂未使用
* @param locals 当前作用域的符号表包含已声明的变量及其类型
* @param id 待分析的 {@link IdentifierNode}包含标识符名称
* @return 如果符号已声明则返回其 {@link Symbol#type()}否则返回降级类型 {@link BuiltinType#INT}
*/
@Override @Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IdentifierNode id) { public Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
IdentifierNode id) {
// 查找当前作用域下的符号
Symbol sym = locals.resolve(id.name()); Symbol sym = locals.resolve(id.name());
if (sym == null) { if (sym == null) {
ctx.getErrors().add(new SemanticError(id, "未声明的标识符: " + id.name())); // 未声明记录错误并降级
ctx.getErrors().add(new SemanticError(id,
"未声明的标识符: " + id.name()));
ctx.log("错误: 未声明的标识符 " + id.name()); ctx.log("错误: 未声明的标识符 " + id.name());
return BuiltinType.INT; return BuiltinType.INT;
} }
// 已声明返回符号所持有的类型
return sym.type(); return sym.type();
} }
} }

View File

@ -1,17 +1,38 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression; package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode; import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; 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} 节点进行类型推导
* <p>
* 在本语言中所有数字字面量均被视为整型因此直接返回 {@link BuiltinType#INT}
* 本分析器不会产生任何语义错误
*/
public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> { public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> {
/**
* 对数字字面量节点进行语义分析
*
* @param ctx 全局上下文包含模块信息错误收集日志输出和分析器注册表等
* @param mi 当前模块信息此分析器不涉及模块间调用参数未使用
* @param fn 当前函数节点此分析器不依赖函数上下文参数未使用
* @param locals 当前作用域的符号表数字字面量不依赖符号表参数未使用
* @param expr 待分析的 {@link NumberLiteralNode}表示一个数字字面量
* @return 固定返回 {@link BuiltinType#INT}
*/
@Override @Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, NumberLiteralNode expr) { public Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
NumberLiteralNode expr) {
return BuiltinType.INT; return BuiltinType.INT;
} }
} }

View File

@ -1,17 +1,38 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression; package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.StringLiteralNode; import org.jcnc.snow.compiler.parser.ast.StringLiteralNode;
import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; 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} 节点进行类型推导
* <p>
* 在本语言中所有字符串字面量均被视为字符串类型因此直接返回 {@link BuiltinType#STRING}
* 本分析器不会产生任何语义错误
*/
public class StringLiteralAnalyzer implements ExpressionAnalyzer<StringLiteralNode> { public class StringLiteralAnalyzer implements ExpressionAnalyzer<StringLiteralNode> {
/**
* 对字符串字面量节点进行语义分析
*
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等此分析器不使用上下文
* @param mi 当前模块信息此分析器不涉及模块调用参数未使用
* @param fn 当前函数节点此分析器不依赖函数上下文参数未使用
* @param locals 当前作用域的符号表字符串字面量不依赖符号表参数未使用
* @param expr 待分析的 {@link StringLiteralNode}表示一个字符串字面量
* @return 固定返回 {@link BuiltinType#STRING}
*/
@Override @Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, StringLiteralNode expr) { public Type analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
StringLiteralNode expr) {
return BuiltinType.STRING; return BuiltinType.STRING;
} }
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.semantic.analyzers.ExpressionAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -10,18 +10,41 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
// 之前implements ExpressionAnalyzer<ExpressionNode> /**
* 默认兜底表达式分析器用于处理所有未显式注册的 {@link ExpressionNode} 子类型
* <p>
* 当遇到不支持或未实现的表达式节点时本分析器会
* <ol>
* <li> {@link Context#getErrors() 错误列表} 中添加一条 {@link SemanticError}</li>
* <li>在日志中输出对应的错误信息</li>
* <li>并返回降级类型 {@link BuiltinType#INT} 以避免后续连锁报错</li>
* </ol>
*
* @param <E> 任何 {@link ExpressionNode} 子类型
*/
public class UnsupportedExpressionAnalyzer<E extends ExpressionNode> public class UnsupportedExpressionAnalyzer<E extends ExpressionNode>
implements ExpressionAnalyzer<E> { implements ExpressionAnalyzer<E> {
/**
* 对未知或不支持的表达式节点进行处理
*
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等
* @param mi 当前模块信息此分析器不使用模块上下文参数可忽略
* @param fn 当前函数节点此分析器不使用函数上下文参数可忽略
* @param locals 当前作用域的符号表此分析器不依赖局部符号参数可忽略
* @param expr 待分析的表达式节点类型为任意 {@link ExpressionNode} 子类型
* @return 固定返回 {@link BuiltinType#INT}作为错误降级类型
*/
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
ModuleInfo mi, ModuleInfo mi,
FunctionNode fn, FunctionNode fn,
SymbolTable locals, SymbolTable locals,
E expr) { E expr) {
ctx.getErrors().add(new SemanticError(expr, ctx.getErrors().add(new SemanticError(
"不支持的表达式类型: " + expr)); expr,
"不支持的表达式类型: " + expr
));
ctx.log("错误: 不支持的表达式类型 " + expr); ctx.log("错误: 不支持的表达式类型 " + expr);
return BuiltinType.INT; return BuiltinType.INT;
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.AssignmentNode; import org.jcnc.snow.compiler.parser.ast.AssignmentNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -11,20 +11,58 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 赋值语句分析器负责对 {@link AssignmentNode} 节点进行语义检查
* <p>
* 分析步骤
* <ol>
* <li>检查目标变量是否已在当前作用域中声明且种类为 {@link SymbolKind#VARIABLE}</li>
* <li>递归分析赋值右侧表达式获取其类型</li>
* <li>检查右侧表达式类型是否与目标变量类型兼容</li>
* <li>在任一检查失败时 {@link Context#getErrors() 错误列表} 中添加对应的
* {@link SemanticError} 并记录日志</li>
* </ol>
*/
public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> { 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, ModuleInfo mi, FunctionNode fn, SymbolTable locals, AssignmentNode asg) { public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
AssignmentNode asg) {
ctx.log("赋值检查: " + asg.variable()); ctx.log("赋值检查: " + asg.variable());
// 1. 检查变量声明
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, "未声明的变量: " + asg.variable())); ctx.getErrors().add(new SemanticError(
asg,
"未声明的变量: " + asg.variable()
));
ctx.log("错误: 未声明的变量 " + asg.variable()); ctx.log("错误: 未声明的变量 " + asg.variable());
return; return;
} }
// 2. 分析赋值表达式
var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(asg.value()); var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(asg.value());
Type valType = exprAnalyzer.analyze(ctx, mi, fn, locals, asg.value()); Type valType = exprAnalyzer.analyze(ctx, mi, fn, locals, asg.value());
// 3. 类型兼容性检查
if (!sym.type().isCompatible(valType)) { if (!sym.type().isCompatible(valType)) {
ctx.getErrors().add(new SemanticError(asg, "赋值类型不匹配: 期望 " + sym.type() + ", 实际 " + valType)); ctx.getErrors().add(new SemanticError(
asg,
"赋值类型不匹配: 期望 " + sym.type() + ", 实际 " + valType
));
ctx.log("错误: 赋值类型不匹配 " + asg.variable()); ctx.log("错误: 赋值类型不匹配 " + asg.variable());
} }
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.DeclarationNode; import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -12,29 +12,79 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 变量声明语句分析器负责对 {@link DeclarationNode} 节点进行语义检查
* <p>
* 分析流程
* <ol>
* <li>解析声明类型如果未知则记录错误并降级为 {@link BuiltinType#INT}</li>
* <li>在当前符号表中注册新变量若名称已存在则记录重复声明错误</li>
* <li>如存在初始化表达式递归分析表达式并检查其类型与声明类型的兼容性</li>
* <li>兼容性检查失败时记录类型不匹配错误</li>
* </ol>
*/
public class DeclarationAnalyzer implements StatementAnalyzer<DeclarationNode> { 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, ModuleInfo mi, FunctionNode fn, SymbolTable locals, DeclarationNode decl) { public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
DeclarationNode decl) {
// 1. 类型解析与降级
Type varType = ctx.parseType(decl.getType()); Type varType = ctx.parseType(decl.getType());
if (varType == null) { if (varType == null) {
ctx.getErrors().add(new SemanticError(decl, "未知类型: " + decl.getType())); ctx.getErrors().add(new SemanticError(
ctx.log("错误: 参数未知类型 " + decl.getType() + " 在声明 " + decl.getName()); decl,
"未知类型: " + decl.getType()
));
ctx.log("错误: 参数未知类型 " + decl.getType()
+ " 在声明 " + decl.getName());
varType = BuiltinType.INT; varType = BuiltinType.INT;
} }
ctx.log("声明变量: " + decl.getName() + " 类型: " + varType); ctx.log("声明变量: " + decl.getName()
+ " 类型: " + varType);
if (!locals.define(new Symbol(decl.getName(), varType, SymbolKind.VARIABLE))) { // 2. 注册新变量检查重复声明
ctx.getErrors().add(new SemanticError(decl, "变量重复声明: " + decl.getName())); if (!locals.define(new Symbol(
decl.getName(),
varType,
SymbolKind.VARIABLE
))) {
ctx.getErrors().add(new SemanticError(
decl,
"变量重复声明: " + decl.getName()
));
ctx.log("错误: 变量重复声明 " + decl.getName()); ctx.log("错误: 变量重复声明 " + decl.getName());
} }
Type finalVarType = varType; // 3. 初始化表达式类型检查
Type declaredType = varType;
decl.getInitializer().ifPresent(init -> { decl.getInitializer().ifPresent(init -> {
var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(init); var exprAnalyzer = ctx.getRegistry()
Type initType = exprAnalyzer.analyze(ctx, mi, fn, locals, init); .getExpressionAnalyzer(init);
if (!finalVarType.isCompatible(initType)) { Type initType = exprAnalyzer.analyze(
ctx.getErrors().add(new SemanticError(decl, "初始化类型不匹配: 期望 " + finalVarType + ", 实际 " + initType)); ctx, mi, fn, locals, init
ctx.log("错误: 初始化类型不匹配 " + decl.getName()); );
if (!declaredType.isCompatible(initType)) {
ctx.getErrors().add(new SemanticError(
decl,
"初始化类型不匹配: 期望 "
+ declaredType
+ ", 实际 "
+ initType
));
ctx.log("错误: 初始化类型不匹配 "
+ decl.getName());
} }
}); });
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IfNode; import org.jcnc.snow.compiler.parser.ast.IfNode;
import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -10,23 +10,60 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* If 语句分析器负责对 {@link IfNode} 节点进行语义检查
* <p>
* 分析流程
* <ol>
* <li>对条件表达式进行类型推导要求类型为 {@link BuiltinType#INT}</li>
* <li>若条件类型不符记录错误并降级继续分析</li>
* <li>分别递归分析 then 分支和 else 分支中的每个子语句</li>
* <li>不支持的子语句类型将由注册表中未匹配到的分析器处理或被记录为错误</li>
* </ol>
*/
public class IfAnalyzer implements StatementAnalyzer<IfNode> { public class IfAnalyzer implements StatementAnalyzer<IfNode> {
/**
* 对给定的 if 节点进行语义分析
*
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等
* @param mi 当前模块信息用于跨模块调用检查此分析器未直接使用
* @param fn 当前函数节点可用于更复杂的上下文校验此分析器未直接使用
* @param locals 当前作用域的符号表包含已定义变量及其类型
* @param ifn 待分析的 {@link IfNode}包含条件表达式then 分支和 else 分支
*/
@Override @Override
public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IfNode ifn) { public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
IfNode ifn) {
// 1. 条件表达式类型检查
ctx.log("检查 if 条件"); 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());
if (cond != BuiltinType.INT) { if (cond != BuiltinType.INT) {
ctx.getErrors().add(new SemanticError(ifn, "if 条件必须为 int 类型(表示真假)")); ctx.getErrors().add(new SemanticError(
ifn,
"if 条件必须为 int 类型(表示真假)"
));
ctx.log("错误: if 条件类型不为 int"); ctx.log("错误: if 条件类型不为 int");
} }
ifn.thenBranch().forEach(stmt -> {
// 2. 递归分析 then 分支
for (var stmt : ifn.thenBranch()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (stAnalyzer != null) stAnalyzer.analyze(ctx, mi, fn, locals, stmt); if (stAnalyzer != null) {
}); stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
ifn.elseBranch().forEach(stmt -> { }
}
// 3. 递归分析 else 分支
for (var stmt : ifn.elseBranch()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (stAnalyzer != null) stAnalyzer.analyze(ctx, mi, fn, locals, stmt); if (stAnalyzer != null) {
}); stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
}
}
} }
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.LoopNode; import org.jcnc.snow.compiler.parser.ast.LoopNode;
import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -10,25 +10,66 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* 循环语句分析器负责对 {@link LoopNode} 节点进行语义检查
* <p>
* 分析流程
* <ol>
* <li>分析并执行初始化语句initializer</li>
* <li>对循环条件表达式进行类型推导要求为 {@link BuiltinType#INT}</li>
* <li>分析并执行更新语句update</li>
* <li>递归分析循环体body中的每个子语句</li>
* <li>在任何步骤发现类型或语义错误时都会向 {@link Context#getErrors() 错误列表} 中添加对应的
* {@link SemanticError} 并在日志中记录</li>
* </ol>
*/
public class LoopAnalyzer implements StatementAnalyzer<LoopNode> { public class LoopAnalyzer implements StatementAnalyzer<LoopNode> {
/**
* 对给定的循环节点进行语义分析
*
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等
* @param mi 当前模块信息用于跨模块调用检查此分析器未直接使用
* @param fn 当前函数节点可用于更复杂的上下文校验此分析器未直接使用
* @param locals 当前作用域的符号表包含已定义变量及其类型
* @param ln 待分析的 {@link LoopNode}包含 initializerconditionupdate body
*/
@Override @Override
public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, LoopNode ln) { public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
LoopNode ln) {
// 1. 初始化语句
ctx.log("检查 loop 循环"); ctx.log("检查 loop 循环");
var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer()); var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer());
if (initAnalyzer != null) initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer()); if (initAnalyzer != null) {
initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer());
}
// 2. 条件表达式类型检查
var condAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ln.condition()); 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) {
ctx.getErrors().add(new SemanticError(ln, "loop 条件必须为 int 类型(表示真假)")); ctx.getErrors().add(new SemanticError(
ln,
"loop 条件必须为 int 类型(表示真假)"
));
ctx.log("错误: loop 条件类型不为 int"); ctx.log("错误: loop 条件类型不为 int");
} }
var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update());
if (updateAnalyzer != null) updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update());
ln.body().forEach(stmt -> { // 3. 更新语句
var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update());
if (updateAnalyzer != null) {
updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update());
}
// 4. 循环体语句
for (var stmt : ln.body()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt); var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (stAnalyzer != null) stAnalyzer.analyze(ctx, mi, fn, locals, stmt); if (stAnalyzer != null) {
}); stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
}
}
} }
} }

View File

@ -2,7 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ReturnNode; import org.jcnc.snow.compiler.parser.ast.ReturnNode;
import org.jcnc.snow.compiler.semantic.analyzers.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -11,23 +11,67 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.FunctionType; import org.jcnc.snow.compiler.semantic.type.FunctionType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/**
* Return 语句分析器负责对 {@link ReturnNode} 节点进行语义检查
* <p>
* 分析过程包括
* <ol>
* <li>获取当前函数的预期返回类型</li>
* <li>如果存在返回表达式则递归分析其类型并与预期类型进行兼容性检查</li>
* <li>如果没有返回表达式且预期类型不是 {@link BuiltinType#VOID}则记录缺少返回值的错误</li>
* <li>遇到类型不兼容时会向 {@link Context#getErrors() 错误列表} 中添加 {@link SemanticError}</li>
* <li>所有错误同时会记录到日志以便调试</li>
* </ol>
*/
public class ReturnAnalyzer implements StatementAnalyzer<ReturnNode> { public class ReturnAnalyzer implements StatementAnalyzer<ReturnNode> {
/**
* 对给定的 return 节点进行语义分析
*
* @param ctx 全局上下文包含模块表错误收集日志输出和分析器注册表等
* @param mi 当前模块信息用于查找函数签名和返回类型
* @param fn 当前正在分析的函数节点包含函数名和参数列表
* @param locals 当前作用域的符号表此分析器不使用局部符号表
* @param ret 待分析的 {@link ReturnNode}可能包含一个返回表达式
*/
@Override @Override
public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, ReturnNode ret) { public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
ReturnNode ret) {
ctx.log("检查 return"); ctx.log("检查 return");
FunctionType expected = ctx.getModules().get(mi.getName()).getFunctions().get(fn.name());
// 查找函数的预期返回类型
FunctionType expected = ctx.getModules()
.get(mi.getName())
.getFunctions()
.get(fn.name());
// 如果有返回表达式则检查其类型
ret.getExpression().ifPresentOrElse(exp -> { 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(ret, "return 类型不匹配: 期望 " + expected.returnType() + ", 实际 " + actual)); ctx.getErrors().add(new SemanticError(
ctx.log("错误: return 类型不匹配"); ret,
} "return 类型不匹配: 期望 "
}, () -> { + expected.returnType()
if (expected.returnType() != BuiltinType.VOID) { + ", 实际 "
ctx.getErrors().add(new SemanticError(ret, "非 void 函数必须返回值")); + actual
ctx.log("错误: 非 void 函数缺少返回值"); ));
} ctx.log("错误: return 类型不匹配");
}); }
},
// 如果没有返回表达式但函数应有返回值
() -> {
if (expected.returnType() != BuiltinType.VOID) {
ctx.getErrors().add(new SemanticError(
ret,
"非 void 函数必须返回值"
));
ctx.log("错误: 非 void 函数缺少返回值");
}
});
} }
} }

View File

@ -1,20 +1,33 @@
package org.jcnc.snow.compiler.semantic.core; package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.semantic.type.Type;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry; import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.*; import java.util.List;
import java.util.Map;
/** /**
* 公共上下文对象贯穿整个语义分析过程 * 语义分析公共上下文在整个分析流程中共享用于存储模块信息收集错误控制日志以及管理分析器注册表
*/ */
public class Context { public class Context {
/** 所有已注册模块名 -> 模块信息 的映射 */
private final Map<String, ModuleInfo> modules; private final Map<String, ModuleInfo> modules;
/** 语义分析过程中收集的所有错误列表 */
private final List<SemanticError> errors; private final List<SemanticError> errors;
/** 是否启用详细日志输出 */
private final boolean verbose; private final boolean verbose;
/** 存放并分发各类语句/表达式分析器的注册表 */
private final AnalyzerRegistry registry; private final AnalyzerRegistry registry;
/**
* 构造一个新的 Context 实例
*
* @param modules 已初始化的模块信息映射用于查找模块签名导入关系等
* @param errors 用于收集语义分析过程中发现的所有 {@link SemanticError}
* @param verbose 是否启用详细日志当为 true 调用 {@link #log(String)} 会打印日志
* @param registry 已设置好所有分析器的 {@link AnalyzerRegistry}
*/
public Context(Map<String, ModuleInfo> modules, public Context(Map<String, ModuleInfo> modules,
List<SemanticError> errors, List<SemanticError> errors,
boolean verbose, boolean verbose,
@ -25,26 +38,56 @@ public class Context {
this.registry = registry; this.registry = registry;
} }
/**
* 获取所有模块信息的映射
*
* @return 模块名到 {@link ModuleInfo} Map
*/
public Map<String, ModuleInfo> getModules() { public Map<String, ModuleInfo> getModules() {
return modules; return modules;
} }
/**
* 获取语义错误列表
* 分析过程中产生的 {@link SemanticError} 会被收集到此列表
*
* @return 语义错误列表
*/
public List<SemanticError> getErrors() { public List<SemanticError> getErrors() {
return errors; return errors;
} }
/**
* 获取分析器注册表
* 可通过此注册表按 AST 节点类型分发对应的语句或表达式分析器
*
* @return {@link AnalyzerRegistry} 实例
*/
public AnalyzerRegistry getRegistry() { public AnalyzerRegistry getRegistry() {
return registry; return registry;
} }
/**
* 打印日志信息仅在 {@code verbose} true 时输出到标准输出
*
* @param msg 要打印的日志内容
*/
public void log(String msg) { public void log(String msg) {
if (verbose) { if (verbose) {
System.out.println("[SemanticAnalyzer] " + msg); System.out.println("[SemanticAnalyzer] " + msg);
} }
} }
/** 根据名称解析为内置类型,未知则返回 null */ /**
* 将类型名解析为内置类型 {@link Type} 实例
* <p>
* 若名称在 {@link SemanticAnalyzer#BUILTIN_TYPES} 中存在则返回对应类型
* 否则返回 {@code null}调用方可据此决定降级为默认类型并记录错误
*
* @param name 类型名称 "int", "string", "void"
* @return 对应的 {@link Type} {@code null} 表示未知类型
*/
public Type parseType(String name) { public Type parseType(String name) {
return SemanticAnalyzer.BUILTIN_TYPES.get(name); return org.jcnc.snow.compiler.semantic.core.SemanticAnalyzer.BUILTIN_TYPES.get(name);
} }
} }

View File

@ -8,29 +8,28 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* 保存模块级别的信息 * 模块信息保存单个模块在语义分析阶段的元数据
* <p> * <p>
* 模块信息包括 * 包含以下内容
* <ul> * <ul>
* <li>模块名唯一标识一个模块</li> * <li>模块名唯一标识</li>
* <li>导入的其他模块名称集合</li> * <li>该模块导入的其他模块名称集合</li>
* <li>模块中定义的函数签名映射函数名到 {@link FunctionType} 的对应关系</li> * <li>该模块中定义的函数签名映射将函数名映射到对应的 {@link FunctionType}</li>
* </ul> * </ul>
* <p> * 在语义分析过程中用于管理模块依赖关系和查找函数签名
* 主要用于语义分析阶段帮助管理模块之间的依赖关系和函数声明信息
*/ */
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;
@ -39,30 +38,30 @@ public class ModuleInfo {
/** /**
* 获取模块名称 * 获取模块名称
* *
* @return 模块名称 * @return 模块的唯一名称
*/ */
public String getName() { public String getName() {
return name; return name;
} }
/** /**
* 获取导入的模块名称集合 * 获取该模块导入的其他模块名称集合
* <p> * <p>
* 返回的是内部集合的直接引用可以通过它进行增删操作 * 返回的 Set 为内部对象的直接引用可对其进行添加或移除操作以维护导入列表
* *
* @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;

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.semantic.core; 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.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry; import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.analyzers.expression.*; import org.jcnc.snow.compiler.semantic.analyzers.expression.*;
@ -15,100 +16,157 @@ import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.*; import java.util.*;
/** /**
* 重构后的语义分析器采用注册表 + 分析器组合的分而治之设计 * 重构后的语义分析器采用注册表 + 分析器组合的分而治之设计
* <p>
* 主要职责
* <ul>
* <li>初始化内置模块与类型映射</li>
* <li>注册用户模块及其函数签名与导入关系</li>
* <li>使用 StatementAnalyzer ExpressionAnalyzer 进行递归语义检查</li>
* <li>收集并返回所有发现的语义错误</li>
* </ul>
*/ */
public class SemanticAnalyzer { public class SemanticAnalyzer {
/** /**
* 内置类型映射表 * 内置类型映射表将类型名称映射到对应的 {@link Type} 实例
*/ */
public static final Map<String, Type> BUILTIN_TYPES = Map.of("int", BuiltinType.INT, "string", BuiltinType.STRING, "void", BuiltinType.VOID); public static final Map<String, Type> BUILTIN_TYPES = Map.of(
"int", BuiltinType.INT,
"string", BuiltinType.STRING,
"void", BuiltinType.VOID
);
// --------------------------------------------------------------------- /** 所有已注册模块名称 -> {@link ModuleInfo} 的映射 */
private final Map<String, ModuleInfo> modules = new HashMap<>(); private final Map<String, ModuleInfo> modules = new HashMap<>();
/** 收集语义分析过程中产生的所有错误 */
private final List<SemanticError> errors = new ArrayList<>(); private final List<SemanticError> errors = new ArrayList<>();
/** 是否启用详细日志输出 */
private final boolean verbose; private final boolean verbose;
/** 分发语句与表达式分析器的注册表 */
private final AnalyzerRegistry registry = new AnalyzerRegistry(); private final AnalyzerRegistry registry = new AnalyzerRegistry();
/** 全局上下文对象,封装 modules、errors、verbose 与 registry */
private final Context ctx; private final Context ctx;
/**
* 使用默认关闭日志构造语义分析器
*/
public SemanticAnalyzer() { public SemanticAnalyzer() {
this(false); this(false);
} }
/**
* 构造语义分析器
*
* @param verbose 是否启用详细日志输出
*/
public SemanticAnalyzer(boolean verbose) { public SemanticAnalyzer(boolean verbose) {
this.verbose = verbose; this.verbose = verbose;
this.ctx = new Context(modules, errors, verbose, registry); this.ctx = new Context(modules, errors, verbose, registry);
registerAnalyzers(); registerAnalyzers();
} }
// --------------------------------------------------------------------- /**
* 注册所有语句与表达式分析器到 {@link AnalyzerRegistry}
* <p>
* 包括声明赋值分支循环返回表达式语句以及
* 数字字符串标识符调用二元兜底表达式分析器
*/
private void registerAnalyzers() { private void registerAnalyzers() {
// 语句 // 语句分析器
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());
registry.registerStatementAnalyzer(ExpressionStatementNode.class, (c, mi, fn, locals, stmt) -> { registry.registerStatementAnalyzer(ExpressionStatementNode.class,
var a = c.getRegistry().getExpressionAnalyzer(stmt.expression()); (c, mi, fn, locals, stmt) -> {
a.analyze(c, mi, fn, locals, stmt.expression()); var exprAn = c.getRegistry().getExpressionAnalyzer(stmt.expression());
}); exprAn.analyze(c, 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());
// 兜底处理 // 默认兜底
// 兜底所有暂未实现的表达式都用 UnsupportedExpressionAnalyzer 处理
registry.registerExpressionAnalyzer(MemberExpressionNode.class, new UnsupportedExpressionAnalyzer<>()); registry.registerExpressionAnalyzer(MemberExpressionNode.class, new UnsupportedExpressionAnalyzer<>());
} }
// --------------------------------------------------------------------- /**
* 对给定的模块 AST 列表执行完整的语义分析
*
* @param moduleNodes 所有待分析的 {@link ModuleNode} 列表
* @return 收集到的所有 {@link SemanticError}如果无错误则返回空列表
*/
public List<SemanticError> analyze(List<ModuleNode> moduleNodes) { public List<SemanticError> analyze(List<ModuleNode> moduleNodes) {
ctx.log("开始语义分析"); ctx.log("开始语义分析");
initBuiltinModule(); initBuiltinModule();
ctx.log("内置模块初始化完成"); ctx.log("内置模块初始化完成");
registerUserModules(moduleNodes); registerUserModules(moduleNodes);
ctx.log("用户模块注册完成: " + modules.keySet()); ctx.log("用户模块注册完成: " + modules.keySet());
registerSignaturesAndImports(moduleNodes); registerSignaturesAndImports(moduleNodes);
ctx.log("函数签名与导入检查完成"); ctx.log("函数签名与导入检查完成");
checkAllFunctions(moduleNodes); checkAllFunctions(moduleNodes);
ctx.log("所有函数检查完成,错误总数: " + errors.size()); ctx.log("所有函数检查完成,错误总数: " + errors.size());
return errors; return errors;
} }
// --------------------------------------------------------------------- /**
* 初始化内置工具模块及其函数签名
* <p>
* 包括to_intto_stringprint
*/
private void initBuiltinModule() { private void initBuiltinModule() {
ModuleInfo builtin = new ModuleInfo("BuiltinUtils"); ModuleInfo builtin = new ModuleInfo("BuiltinUtils");
builtin.getFunctions().put("to_int", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.INT)); builtin.getFunctions().put("to_int", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.INT));
builtin.getFunctions().put("to_string", new FunctionType(List.of(BuiltinType.INT), BuiltinType.STRING)); builtin.getFunctions().put("to_string", new FunctionType(List.of(BuiltinType.INT), BuiltinType.STRING));
builtin.getFunctions().put("print", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.VOID)); builtin.getFunctions().put("print", new FunctionType(List.of(BuiltinType.STRING), BuiltinType.VOID));
modules.put(builtin.getName(), builtin); modules.put(builtin.getName(), builtin);
} }
/**
* 注册所有用户模块到模块映射中只保留名称用于后续签名与导入检查
*
* @param mods AST 中的模块节点列表
*/
private void registerUserModules(List<ModuleNode> mods) { private void registerUserModules(List<ModuleNode> mods) {
for (var mod : mods) { for (ModuleNode mod : mods) {
modules.put(mod.name(), new ModuleInfo(mod.name())); modules.put(mod.name(), new ModuleInfo(mod.name()));
} }
} }
/**
* 为每个模块注册其导入关系与函数签名
* <p>
* 对未知模块或类型均会记录 {@link SemanticError} 并进行默认降级处理
*
* @param mods AST 中的模块节点列表
*/
private void registerSignaturesAndImports(List<ModuleNode> mods) { private void registerSignaturesAndImports(List<ModuleNode> mods) {
for (var mod : mods) { for (ModuleNode mod : mods) {
ModuleInfo mi = modules.get(mod.name()); ModuleInfo mi = modules.get(mod.name());
// 导入
for (var imp : mod.imports()) { // 模块导入检查
for (ImportNode imp : mod.imports()) {
if (!modules.containsKey(imp.moduleName())) { if (!modules.containsKey(imp.moduleName())) {
errors.add(new SemanticError(imp, "未知模块: " + imp.moduleName())); errors.add(new SemanticError(imp, "未知模块: " + imp.moduleName()));
continue; } else {
mi.getImports().add(imp.moduleName());
} }
mi.getImports().add(imp.moduleName());
} }
// 函数签名
for (var fn : mod.functions()) { // 函数签名注册
for (FunctionNode fn : mod.functions()) {
List<Type> params = new ArrayList<>(); List<Type> params = new ArrayList<>();
for (var p : fn.parameters()) { for (ParameterNode p : fn.parameters()) {
Type t = ctx.parseType(p.type()); Type t = ctx.parseType(p.type());
if (t == null) { if (t == null) {
errors.add(new SemanticError(p, "未知类型: " + p.type())); errors.add(new SemanticError(p, "未知类型: " + p.type()));
@ -116,23 +174,33 @@ public class SemanticAnalyzer {
} }
params.add(t); params.add(t);
} }
Type ret = Optional.ofNullable(ctx.parseType(fn.returnType())).orElse(BuiltinType.VOID); Type retType = Optional.ofNullable(ctx.parseType(fn.returnType()))
mi.getFunctions().put(fn.name(), new FunctionType(params, ret)); .orElse(BuiltinType.VOID);
mi.getFunctions().put(fn.name(), new FunctionType(params, retType));
} }
} }
} }
/**
* 对所有模块中的每个函数体执行逐语句分析收集语义错误
*
* @param mods AST 中的模块节点列表
*/
private void checkAllFunctions(List<ModuleNode> mods) { private void checkAllFunctions(List<ModuleNode> mods) {
for (var mod : mods) { for (ModuleNode mod : mods) {
ModuleInfo mi = modules.get(mod.name()); ModuleInfo mi = modules.get(mod.name());
for (var fn : mod.functions()) { for (FunctionNode fn : mod.functions()) {
SymbolTable locals = new SymbolTable(null); SymbolTable locals = new SymbolTable(null);
// 参数注册
for (var p : fn.parameters()) { // 参数预定义为局部变量
locals.define(new Symbol(p.name(), ctx.parseType(p.type()), SymbolKind.VARIABLE)); for (ParameterNode p : fn.parameters()) {
locals.define(new Symbol(p.name(),
ctx.parseType(p.type()),
SymbolKind.VARIABLE));
} }
// 函数体
for (var stmt : fn.body()) { // 逐语句分发给对应的 StatementAnalyzer
for (StatementNode stmt : fn.body()) {
var analyzer = registry.getStatementAnalyzer(stmt); var analyzer = registry.getStatementAnalyzer(stmt);
if (analyzer != null) { if (analyzer != null) {
analyzer.analyze(ctx, mi, fn, locals, stmt); analyzer.analyze(ctx, mi, fn, locals, stmt);

View File

@ -5,21 +5,25 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
/** /**
* 表示语义分析过程中发现的错误 * 表示语义分析过程中发现的错误
* <p> * <p>
* 每个语义错误包含两个部分 * 每个语义错误包含以下内容
* 1. 发生错误的 AST抽象语法树节点 {@link Node} * <ul>
* 2. 错误的详细描述 {@link String} * <li>{@link Node}发生错误的 AST 节点便于定位</li>
* </p> * <li>{@link String message}错误的详细描述信息</li>
* </ul>
* <p>
* 使用 Java 16+ record 定义自动生成构造方法访问器gettersequalshashCode toString 方法
* *
* 该类以 record 形式定义自动生成构造方法访问器方法gettersequalshashCode 以及 toString 方法 * @param node 触发语义错误的 AST 节点
* @param message 错误的详细描述
*/ */
public record SemanticError(Node node, String message) { public record SemanticError(Node node, String message) {
/** /**
* 返回语义错误的字符串表示 * 返回语义错误的字符串表示格式如下
* <p> * <pre>
* 格式为"Semantic error at [节点信息]: [错误信息]" * Semantic error at [节点信息]: [错误信息]
* 方便在日志输出或调试过程中快速定位问题 * </pre>
* </p> * 便于在日志输出或调试时快速查看定位
* *
* @return 格式化后的错误信息字符串 * @return 格式化后的错误信息字符串
*/ */

View File

@ -3,21 +3,25 @@ package org.jcnc.snow.compiler.semantic.symbol;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
/** /**
* 表示符号表Symbol Table中的一条符号记录 * 符号表中的一条符号记录表示语言中的命名实体如变量函数等
* <p> * <p>
* 每条符号记录包含以下三个部分信息 * 每条符号记录包括三个核心属性
* <ul> * <ul>
* <li><b>name</b>符号的名称如变量名函数名等</li> * <li><b>name</b>符号名称例如变量名或函数名</li>
* <li><b>type</b>符号的类型信息通常对应变量类型函数返回值类型等</li> * <li><b>type</b>符号的类型信息对于变量是变量类型对于函数是返回类型</li>
* <li><b>kind</b>符号的种类指明符号是变量函数类等 {@link SymbolKind}</li> * <li><b>kind</b>符号的种类 {@link SymbolKind} 枚举区分变量函数等</li>
* </ul>
* <p>
* 使用 Java 16+ record 定义自动生成
* <ul>
* <li>构造方法</li>
* <li>访问器方法getters</li>
* <li>equalshashCode</li>
* <li>toString 方法</li>
* </ul> * </ul>
* </p>
* *
* 本类使用 Java 记录类型record定义自动生成构造方法访问器方法gettersequalshashCode 以及 toString 方法 * @param name 符号名称
* * @param type 符号类型
* @param name 符号的名称 * @param kind 符号种类
* @param type 符号的类型
* @param kind 符号的种类
*/ */
public record Symbol(String name, Type type, SymbolKind kind) { public record Symbol(String name, Type type, SymbolKind kind) { }
}

View File

@ -1,25 +1,33 @@
package org.jcnc.snow.compiler.semantic.symbol; package org.jcnc.snow.compiler.semantic.symbol;
/** /**
* 表示符号的种类Symbol Kind * 符号种类枚举用于在符号表中区分不同类型的命名实体
* <p> * <p>
* 用于在符号表中区分不同类型的符号例如变量函数模块等 * 在语义分析过程中不同种类的符号需要不同的处理策略
* 例如变量声明函数调用模块导入等
* </p> * </p>
* *
* 支持的符号种类包括
* <ul> * <ul>
* <li><b>VARIABLE</b>变量符号例如局部变量全局变量等</li> * <li>{@link #VARIABLE} 变量符号局部变量全局变量成员变量等</li>
* <li><b>FUNCTION</b>函数符号包括普通函数方法等</li> * <li>{@link #FUNCTION} 函数符号自由函数方法构造函数等</li>
* <li><b>MODULE</b>模块符号表示一个模块或命名空间</li> * <li>{@link #MODULE} 模块符号表示一个命名空间或模块</li>
* </ul> * </ul>
*/ */
public enum SymbolKind { public enum SymbolKind {
/** 变量符号,例如局部变量、全局变量等。 */ /**
* 变量符号例如在函数或全局作用域中声明的变量
*/
VARIABLE, VARIABLE,
/** 函数符号,表示一个可调用的功能单元。 */ /**
* 函数符号表示可调用的函数或方法
* 用于函数签名注册和调用时的类型匹配
*/
FUNCTION, FUNCTION,
/** 模块符号,表示一个模块或命名空间。 */ /**
* 模块符号表示一个模块或命名空间
* 用于管理模块导入和跨模块引用
*/
MODULE MODULE
} }

View File

@ -4,26 +4,26 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* 符号表Symbol Table用于管理符号如变量函数模块等及其作用域信息 * 符号表Symbol Table用于管理命名实体如变量函数模块等的作用域及其类型信息
* <p> * <p>
* 支持链式作用域Nested Scope管理 * 本实现支持链式作用域Nested Scope
* <ul> * <ul>
* <li>每个符号表可以有一个父符号表 {@link #parent}用于实现作用域嵌套</li> * <li>每个 {@code SymbolTable} 可拥有一个父作用域若查找失败可递归向上查找</li>
* <li>符号表内部使用 {@link Map} 保存当前作用域内定义的符号</li> * <li>当前作用域使用内部 {@link Map} 保存定义的符号</li>
* <li>提供符号定义与符号解析两种基本操作</li>
* </ul> * </ul>
* </p>
*/ */
public class SymbolTable { public class SymbolTable {
/** 父作用域符号表,如果为 null 则表示当前为最外层作用域。 */ /** 父作用域符号表;若为 null 则表示当前为最外层作用域。 */
private final SymbolTable parent; private final SymbolTable parent;
/** 当前作用域中定义的符号集合,按名称索引。 */ /** 当前作用域内定义的符号映射:符号名 -> {@link Symbol}。 */
private final Map<String, Symbol> symbols = new HashMap<>(); private final Map<String, Symbol> symbols = new HashMap<>();
/** /**
* 创建一个新的符号表并指定其父作用域 * 构造一个新的符号表并可指定其父作用域以支持嵌套查找
* *
* @param parent 符号表若无父作用域则传入 null * @param parent 作用域的 {@link SymbolTable}若无父作用域则传入 {@code null}
*/ */
public SymbolTable(SymbolTable parent) { public SymbolTable(SymbolTable parent) {
this.parent = parent; this.parent = parent;
@ -32,12 +32,11 @@ public class SymbolTable {
/** /**
* 在当前作用域中定义一个新的符号 * 在当前作用域中定义一个新的符号
* <p> * <p>
* 如果当前作用域中已存在同名符号则定义失败并返回 {@code false} * 如果当前作用域已有同名符号则定义失败并返回 {@code false}
* 否则将符号添加到符号表中并返回 {@code true} * 否则将符号添加到当前作用域并返回 {@code true}
* </p>
* *
* @param symbol 要定义的符号 * @param symbol 要定义的 {@link Symbol} 对象
* @return 是否定义成功若名称冲突则返回 {@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())) {
@ -48,27 +47,21 @@ public class SymbolTable {
} }
/** /**
* 根据名称查找符号 * 根据名称解析符号支持嵌套作用域查找
* <p>
* 查找顺序为
* <ol> * <ol>
* <li>先在当前作用域中查找</li> * <li>优先在当前作用域查找</li>
* <li>如果当前作用域未找到递归向父作用域查找</li> * <li>若未找到且存在父作用域则递归在父作用域查找</li>
* <li>若最终未找到返回 {@code null}</li> * <li>最终未找到则返回 {@code null}</li>
* </ol> * </ol>
* </p>
* *
* @param name 查找的符号名称 * @param name 解析的符号名称
* @return 找到的符号对象如果不存在则返回 {@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);
if (sym != null) { if (sym != null) {
return sym; return sym;
} }
if (parent != null) { return (parent != null) ? parent.resolve(name) : null;
return parent.resolve(name);
}
return null;
} }
} }

View File

@ -1,20 +1,20 @@
package org.jcnc.snow.compiler.semantic.type; package org.jcnc.snow.compiler.semantic.type;
/** /**
* 内置基础类型枚举 * 内置基础类型枚举定义编译器中最基本的三种类型
* <p> * <p>
* 本枚举定义了编译器中最基本的三种内置类型 * 枚举值
* <ul> * <ul>
* <li>INT整数类型同时也可用于表示布尔值真假值</li> * <li>{@link #INT} 整数类型也可用于表示布尔值/</li>
* <li>STRING字符串类型</li> * <li>{@link #STRING} 字符串类型</li>
* <li>VOID无返回值类型通常用于函数无返回值的情况</li> * <li>{@link #VOID} 空类型用于表示无返回值的函数</li>
* </ul> * </ul>
* <p> * <p>
* 实现了 Type 接口提供了基本的类型兼容性判断和字符串表示方法 * 本枚举实现了 {@link Type} 接口提供了基本的类型兼容性判断和友好的字符串表示方法
*/ */
public enum BuiltinType implements Type { public enum BuiltinType implements Type {
/** /**
* 整数类型也可用于布尔值 * 整数类型也可用于表示布尔值真假值
*/ */
INT, INT,
@ -24,28 +24,27 @@ public enum BuiltinType implements Type {
STRING, STRING,
/** /**
* 空类型用于表示无返回值的情况 * 空类型通常用于无返回值的函数
*/ */
VOID; VOID;
/** /**
* 判断当前类型是否与另一个类型兼容 * 判断当前类型是否与另一个类型兼容
* <p> * <p>
* 兼容的条件是两个类型必须完全相同引用相等 * 兼容条件两个类型实例必须相同同一枚举常量
* *
* @param other 另一个需要检查兼容性的类型 * @param other 另一个需要检查兼容性的类型
* @return 如果类型完全相同则返回 true否则返回 false * @return 如果类型完全相同则返回 {@code true}否则返回 {@code false}
*/ */
@Override @Override
public boolean isCompatible(Type other) { public boolean isCompatible(Type other) {
// 只有当两个类型对象是同一个实例时才认为兼容
return this == other; return this == other;
} }
/** /**
* 将类型转换为小写字符串形式 * 返回小写形式的类型名称便于输出和日志展示
* <p> * <p>
* 例如INT 会转换为 "int" * 例如{@link #INT} 返回 {@code "int"}{@link #VOID} 返回 {@code "void"}
* *
* @return 当前类型的小写字符串表示 * @return 当前类型的小写字符串表示
*/ */

View File

@ -4,20 +4,20 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* 表示函数类型 * 表示函数类型由参数类型列表和返回类型共同确定
* <p> * <p>
* 一个函数类型由参数类型列表和返回类型共同确定 * 例如一个接受两个 int 参数并返回 string 的函数其类型描述为 <code>(int, int) -> string</code>
* 例如一个接受两个 int 参数并返回 string 的函数其类型描述为 (int, int) -> string
* <p> * <p>
* 本类是一个 Java record自动生成基本的构造方法访问器equals hashCode 方法 * record 自动生成了构造方法访问器equalshashCode
* 并实现了 {@link Type} 接口支持类型兼容性检查和字符串表示 * 并实现了 {@link Type} 接口可用于类型兼容性检查和字符串表示
*
* @param paramTypes 参数类型列表
* @param returnType 返回类型
*/ */
public record FunctionType(List<Type> paramTypes, Type returnType) implements Type { public record FunctionType(List<Type> paramTypes, Type returnType) implements Type {
/** /**
* 构造函数类型 * 构造函数类型将参数类型列表包装成不可变列表以保证安全性
* <p>
* {@link java.util.Collections#unmodifiableList(List)} 包装成不可变列表
* 保证函数类型对象本身的安全性和不可变性
* *
* @param paramTypes 参数类型列表 * @param paramTypes 参数类型列表
* @param returnType 返回类型 * @param returnType 返回类型
@ -30,30 +30,30 @@ public record FunctionType(List<Type> paramTypes, Type returnType) implements Ty
/** /**
* 判断当前函数类型是否与另一个类型兼容 * 判断当前函数类型是否与另一个类型兼容
* <p> * <p>
* 兼容条件 * 兼容条件
* <ul> * <ul>
* <li>另一类型也是 FunctionType</li> * <li>另一对象也为 {@code FunctionType}</li>
* <li>返回类型兼容</li> * <li>返回类型兼容</li>
* <li>参数类型列表完全相等顺序和值一致</li> * <li>参数类型列表完全相等顺序和值一致</li>
* </ul> * </ul>
* *
* @param other 需要检查兼容性的另一个类型 * @param other 另一个类型
* @return 如果兼容返回 true否则返回 false * @return 如果兼容则返回 {@code true}否则 {@code false}
*/ */
@Override @Override
public boolean isCompatible(Type other) { public boolean isCompatible(Type other) {
if (!(other instanceof FunctionType)) return false; if (!(other instanceof FunctionType(List<Type> types, Type type))) return false;
FunctionType o = (FunctionType) other; return returnType.isCompatible(type)
return returnType.isCompatible(o.returnType) && paramTypes.equals(types);
&& paramTypes.equals(o.paramTypes);
} }
/** /**
* 返回函数类型的字符串表示 * 返回函数类型的字符串表示
* <p> * <p>
* 格式为"(参数类型列表) -> 返回类型"例如<code>(int, string) -> void</code> * 格式为<code>(param1, param2, ...) -> returnType</code>
* 例如 <code>(int, string) -> void</code>
* *
* @return 函数类型的字符串表示 * @return 函数类型的描述字符串
*/ */
@Override @Override
public String toString() { public String toString() {
@ -61,32 +61,30 @@ public record FunctionType(List<Type> paramTypes, Type returnType) implements Ty
} }
/** /**
* 判断当前函数类型是否与另一个对象相等 * 判断两个函数类型是否相等
* <p> * <p>
* 相等条件 * 相等条件
* <ul> * <ul>
* <li>对象引用相同</li> * <li>引用相同</li>
* <li>对象是 FunctionType 类型且参数类型列表和返回类型都相等</li> * <li>或都是 {@code FunctionType}且参数列表和返回类型都相同</li>
* </ul> * </ul>
* *
* @param obj 另一个对象 * @param obj 另一个对象
* @return 如果相等返回 true否则返回 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)) return false; if (!(obj instanceof FunctionType(List<Type> types, Type type))) return false;
FunctionType o = (FunctionType) obj; return returnType.equals(type)
return returnType.equals(o.returnType) && paramTypes.equals(types);
&& paramTypes.equals(o.paramTypes);
} }
/** /**
* 返回函数类型的哈希码 * 计算函数类型的哈希码基于参数类型列表和返回类型
* <p> * {@link #equals(Object)} 保持一致
* 计算规则是基于参数类型列表和返回类型保证与 {@link #equals(Object)} 方法一致
* *
* @return 函数类型的哈希值 * @return 哈希码
*/ */
@Override @Override
public int hashCode() { public int hashCode() {

View File

@ -1,11 +1,22 @@
package org.jcnc.snow.compiler.semantic.type; package org.jcnc.snow.compiler.semantic.type;
/** /**
* 类型接口所有具体类型内置类型函数类型等都应实现此接口 * 类型接口所有类型包括内置类型函数类型等均需实现此接口
* 用于在语义分析中进行类型兼容性检查和统一表示
*/ */
public interface Type { public interface Type {
/** /**
* 判断此类型是否可接受赋值或兼容另一个类型 * 判断当前类型是否与另一个类型兼容
* <p>
* 例如
* <ul>
* <li>对于内置类型只有完全相同的枚举常量才兼容</li>
* <li>对于函数类型需要参数列表和返回类型同时兼容</li>
* <li>对于其他复合类型可按语言规则自行实现</li>
* </ul>
*
* @param other 要检查兼容性的另一个类型
* @return 如果兼容则返回 {@code true}否则返回 {@code false}
*/ */
boolean isCompatible(Type other); boolean isCompatible(Type other);
} }