fix:改进语义分析

This commit is contained in:
Luke 2025-05-16 16:50:34 +08:00
parent 1c9cfc3b4c
commit 4f2ee4a9f0
4 changed files with 84 additions and 112 deletions

View File

@ -11,28 +11,10 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* {@code IfAnalyzer} 是用于分析 {@link IfNode} 条件语句的语义分析器
* <p>
* 它负责验证 if 条件表达式的类型合法性并递归分析 then 分支和 else 分支中的所有子语句
* 分析规则如下
* <ul>
* <li>if 条件必须为 {@link BuiltinType#INT} 类型用于表示布尔真值</li>
* <li>若条件类型不为 int将报告语义错误并继续分析</li>
* <li>then else 分支中的语句将分别递归进行语义分析</li>
* <li>若某个子语句无匹配分析器将回退到默认处理器或报告不支持</li>
* </ul>
* 改进版 {@code IfAnalyzer} then/else 分支提供独立块级作用域
*/
public class IfAnalyzer implements StatementAnalyzer<IfNode> {
/**
* 执行 if 语句的语义分析包括条件类型检查和分支分析
*
* @param ctx 当前语义分析上下文提供注册表错误收集日志等服务
* @param mi 当前模块信息本实现未直接使用
* @param fn 当前所在函数节点本实现未直接使用
* @param locals 当前作用域的符号表提供变量查找支持
* @param ifn 要分析的 {@link IfNode} 语法节点包含条件表达式及两个分支语句块
*/
@Override
public void analyze(Context ctx,
ModuleInfo mi,
@ -40,31 +22,34 @@ public class IfAnalyzer implements StatementAnalyzer<IfNode> {
SymbolTable locals,
IfNode ifn) {
// 1. 分析并校验条件表达式类型
ctx.log("检查 if 条件");
// 1. 条件类型检查
var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ifn.condition());
Type cond = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition());
if (cond != BuiltinType.INT) {
ctx.getErrors().add(new SemanticError(
ifn,
"if 条件必须为 int 类型(表示真假)"
));
ctx.log("错误: if 条件类型不为 int");
Type condType = exprAnalyzer.analyze(ctx, mi, fn, locals, ifn.condition());
if (condType != BuiltinType.INT) {
ctx.getErrors().add(new SemanticError(ifn, "if 条件必须为 int 类型(表示真假)"));
}
// 2. 递归分析 then 分支语句
// 2. then 分支
SymbolTable thenScope = new SymbolTable(locals);
for (var stmt : ifn.thenBranch()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (stAnalyzer != null) {
stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
stAnalyzer.analyze(ctx, mi, fn, thenScope, stmt);
} else {
ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
}
}
// 3. 递归分析 else 分支语句若存在
// 3. else 分支可选
if (!ifn.elseBranch().isEmpty()) {
SymbolTable elseScope = new SymbolTable(locals);
for (var stmt : ifn.elseBranch()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (stAnalyzer != null) {
stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
stAnalyzer.analyze(ctx, mi, fn, elseScope, stmt);
} else {
ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
}
}
}
}

View File

@ -11,30 +11,10 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* {@code LoopAnalyzer} 是用于分析 {@link LoopNode} 循环结构的语义分析器
* <p>
* 支持的循环结构包括典型的 C 风格 for-loop由初始化语句条件表达式更新语句和循环体组成
* <p>
* 分析流程如下
* <ol>
* <li>分析初始化语句 {@code initializer}</li>
* <li>分析并验证条件表达式 {@code condition} 类型是否为 {@link BuiltinType#INT}</li>
* <li>分析更新语句 {@code update}</li>
* <li>递归分析循环体 {@code body} 中的每个子语句</li>
* <li>所有阶段若发现语义错误均记录至 {@link Context#getErrors()}并写入日志</li>
* </ol>
* 改进版 {@code LoopAnalyzer}为循环结构提供块级作用域支持
*/
public class LoopAnalyzer implements StatementAnalyzer<LoopNode> {
/**
* 执行循环结构的语义分析
*
* @param ctx 当前语义分析上下文提供模块信息错误记录日志输出和分析器注册表等服务
* @param mi 当前模块信息本方法中未直接使用保留用于接口一致性
* @param fn 当前函数节点本方法中未直接使用保留用于可能的上下文分析扩展
* @param locals 当前作用域的符号表记录变量类型及其可见性
* @param ln 待分析的 {@link LoopNode} 节点包含初始化语句条件表达式更新语句和循环体
*/
@Override
public void analyze(Context ctx,
ModuleInfo mi,
@ -42,35 +22,35 @@ public class LoopAnalyzer implements StatementAnalyzer<LoopNode> {
SymbolTable locals,
LoopNode ln) {
// 1. 分析初始化语句
ctx.log("检查 loop 循环");
// 1. 整个循环的块级作用域
SymbolTable loopScope = new SymbolTable(locals);
// 2. 初始化语句
var initAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.initializer());
if (initAnalyzer != null) {
initAnalyzer.analyze(ctx, mi, fn, locals, ln.initializer());
initAnalyzer.analyze(ctx, mi, fn, loopScope, ln.initializer());
}
// 2. 分析条件表达式并检查类型
// 3. 条件表达式
var condAnalyzer = ctx.getRegistry().getExpressionAnalyzer(ln.condition());
Type cond = condAnalyzer.analyze(ctx, mi, fn, locals, ln.condition());
if (cond != BuiltinType.INT) {
ctx.getErrors().add(new SemanticError(
ln,
"loop 条件必须为 int 类型(表示真假)"
));
ctx.log("错误: loop 条件类型不为 int");
Type condType = condAnalyzer.analyze(ctx, mi, fn, loopScope, ln.condition());
if (condType != BuiltinType.INT) {
ctx.getErrors().add(new SemanticError(ln, "loop 条件必须为 int 类型(表示真假)"));
}
// 3. 分析更新语句
// 4. 更新语句
var updateAnalyzer = ctx.getRegistry().getStatementAnalyzer(ln.update());
if (updateAnalyzer != null) {
updateAnalyzer.analyze(ctx, mi, fn, locals, ln.update());
updateAnalyzer.analyze(ctx, mi, fn, loopScope, ln.update());
}
// 4. 递归分析循环体中的每个语句
// 5. 循环体
for (var stmt : ln.body()) {
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
if (stAnalyzer != null) {
stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
stAnalyzer.analyze(ctx, mi, fn, loopScope, stmt);
} else {
ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
}
}
}

View File

@ -5,51 +5,47 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError;
import java.util.List;
/**
* {@code SemanticAnalysisReporter} 是语义分析阶段的结果报告工具类
* <p>
* 用于统一处理语义分析阶段产生的错误信息输出与流程终止逻辑
* 通常作为语义分析器的收尾阶段调用
*
* <p>主要职责包括
* {@code SemanticAnalysisReporter} 用于在语义分析结束后汇总并打印所有收集到的
* {@link SemanticError}为了同时满足完整错误收集按需快速失败两种使用场景
* 现在提供两个公共 API
* <ul>
* <li>打印所有收集到的 {@link SemanticError}</li>
* <li>若存在错误使用 {@code System.exit(1)} 终止编译流程</li>
* <li>若无错误输出分析通过提示</li>
* <li>{@link #report(List)} 仅打印不终止</li>
* <li>{@link #reportAndExitIfNecessary(List)} 若存在错误则 <b>打印并退出</b></li>
* </ul>
*
* <p>该类为工具类禁止实例化方法均为静态调用
* 调用方可根据需求选择合适方法
*/
public final class SemanticAnalysisReporter {
// 禁止实例化
private SemanticAnalysisReporter() {
}
private SemanticAnalysisReporter() { }
/**
* 打印语义分析结果并在必要时终止程序
* <p>
* 如果错误列表非空
* <ul>
* <li>逐条打印错误信息含位置与描述</li>
* <li>使用 {@code System.exit(1)} 退出表示语义分析失败</li>
* </ul>
* 如果错误列表为空
* <ul>
* <li>打印语义分析通过提示</li>
* </ul>
* 打印语义分析结果<b>不会</b>退出进程
*
* @param errors 语义分析阶段收集到的错误列表允许为 null
* @param errors 语义分析阶段收集到的错误列表允许为 {@code null}
*/
public static void reportAndExitIfNecessary(List<SemanticError> errors) {
if (errors != null && !errors.isEmpty()) {
System.err.println("语义分析发现错误:");
for (SemanticError error : errors) {
System.err.println(" " + error);
}
System.exit(1); // 非正常退出阻止后续编译流程
public static void report(List<SemanticError> errors) {
if (hasErrors(errors)) {
System.err.println("语义分析发现 " + errors.size() + " 个错误:");
errors.forEach(err -> System.err.println(" " + err));
} else {
System.out.println("语义分析通过,没有发现错误。");
}
}
/**
* 打印语义分析结果如有错误立即以状态码 <code>1</code> 结束进程
* 适用于 CLI 工具需要立即中止后续编译阶段的场景
*
* @param errors 语义分析阶段收集到的错误列表允许为 {@code null}
*/
public static void reportAndExitIfNecessary(List<SemanticError> errors) {
report(errors);
if (hasErrors(errors)) {
System.exit(1);
}
}
private static boolean hasErrors(List<SemanticError> errors) {
return errors != null && !errors.isEmpty();
}
}

21
test
View File

@ -5,20 +5,31 @@ module: CommonTasks
return_type:int
body:
declare x : int
declare x : string
y = 5
x = "hello"
return CommonTasks.test(3,1)
if x then
declare z : float
declare z : int
else
z = 3.14
end if
return CommonTasks.test(3, "a")
end body
end function
function:test
function: test
parameter:
declare num1:int
declare num2:int
declare num1 : int
declare num2 : int
return_type:int
body:
declare result : int
result = num1 + num2
result = num1 + "hello"
return result
end body
end function