fix:改进语义分析
This commit is contained in:
parent
1c9cfc3b4c
commit
4f2ee4a9f0
@ -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,32 +22,35 @@ 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 分支语句(若存在)
|
||||
for (var stmt : ifn.elseBranch()) {
|
||||
var stAnalyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||
if (stAnalyzer != null) {
|
||||
stAnalyzer.analyze(ctx, mi, fn, locals, stmt);
|
||||
// 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, elseScope, stmt);
|
||||
} else {
|
||||
ctx.getErrors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
21
test
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user