!25 fix: AOT 编译后无法定位语义错误

Merge pull request !25 from zhangxun/bugfix/aot-semantic-error-unknown-location
This commit is contained in:
Luke 2025-07-08 02:35:36 +00:00 committed by Gitee
commit 84a6b2701c
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
14 changed files with 74 additions and 38 deletions

View File

@ -66,12 +66,12 @@ public class StatementBuilder {
buildIf(ifNode);
return;
}
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, _, _, _)) {
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, int _, int _, String _)) {
// 纯表达式语句 foo();
expr.build(exp);
return;
}
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, _, _, _)) {
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, int _, int _, String _)) {
// 赋值语句 a = b + 1;
final String type = ctx.getScope().lookupType(var);

View File

@ -41,7 +41,7 @@ public final class ComparisonUtils {
/* ------------ 内部工具 ------------ */
private static boolean isLongLiteral(ExpressionNode node) {
if (node instanceof NumberLiteralNode(String value)) {
if (node instanceof NumberLiteralNode(String value, int _, int _, String _)) {
return value.endsWith("L") || value.endsWith("l");
}
return false; // 变量暂不处理后续可扩展符号表查询

View File

@ -127,7 +127,7 @@ public final class ExpressionUtils {
/** 递归推断单个表达式节点的类型后缀b/s/i/l/f/d。 */
private static char typeChar(ExpressionNode node) {
if (node instanceof NumberLiteralNode(String value)) {
if (node instanceof NumberLiteralNode(String value, int _, int _, String _)) {
char last = Character.toLowerCase(value.charAt(value.length() - 1));
return switch (last) {
case 'b','s','i','l','f','d' -> last;

View File

@ -9,9 +9,17 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* 表达布尔类型的字面量常量 "true" "false"
* </p>
*
* @param value 字面量的布尔值
* @param value 字面量的布尔值
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
*/
public record BoolLiteralNode(boolean value) implements ExpressionNode {
public record BoolLiteralNode(
boolean value,
int line,
int column,
String file
) implements ExpressionNode {
/**
* 使用布尔字面量字符串构造一个 {@code BoolLiteralNode} 实例
@ -22,8 +30,8 @@ public record BoolLiteralNode(boolean value) implements ExpressionNode {
*
* @param lexeme 布尔字面量的字符串表示
*/
public BoolLiteralNode(String lexeme) {
this(Boolean.parseBoolean(lexeme));
public BoolLiteralNode(String lexeme, int line, int column, String file) {
this(Boolean.parseBoolean(lexeme), line, column, file);
}
/**

View File

@ -10,9 +10,17 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* 在语义分析或类型推导阶段再行解析为具体数值类型
* </p>
*
* @param value 数字字面量的原始字符串表示
* @param value 数字字面量的原始字符串表示
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
*/
public record NumberLiteralNode(String value) implements ExpressionNode {
public record NumberLiteralNode(
String value,
int line,
int column,
String file
) implements ExpressionNode {
/**
* 返回数字字面量的字符串形式

View File

@ -9,9 +9,17 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* 节点内部仅保存不带引号的字符串内容便于后续语义处理或编码
* </p>
*
* @param value 字符串常量的内容原始值中不包含双引号
* @param value 字符串常量的内容原始值中不包含双引号
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
*/
public record StringLiteralNode(String value) implements ExpressionNode {
public record StringLiteralNode(
String value,
int line,
int column,
String file
) implements ExpressionNode {
/**
* 返回字符串字面量的带引号表示适用于语法树调试或文本输出

View File

@ -3,7 +3,7 @@ package org.jcnc.snow.compiler.parser.ast.base;
/**
* {@code Node} 是抽象语法树AST中所有语法节点的统一根接口
* <p>
* 作为标记接口Marker Interface该接口不定义任何方法
* 作为标记接口Marker Interface该接口定义 3 个方法line()column() file() 用于定位错误
* 主要用于统一标识并组织 AST 体系中的各种语法构件节点包括
* </p>
* <ul>
@ -15,4 +15,25 @@ package org.jcnc.snow.compiler.parser.ast.base;
* 所有 AST 处理逻辑如遍历分析代码生成均可基于该接口实现统一调度和类型判定
* </p>
*/
public interface Node {}
public interface Node {
/**
* 获取当前表达式所在的行号
*
* @return 当前表达式的行号
*/
int line();
/**
* 获取当前表达式所在的列号
*
* @return 当前表达式的列号
*/
int column();
/**
* 获取当前表达式所在的文件名
*
* @return 当前表达式所在的文件名
*/
String file();
}

View File

@ -28,6 +28,6 @@ public class BoolLiteralParselet implements PrefixParselet {
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
return new BoolLiteralNode(token.getLexeme());
return new BoolLiteralNode(token.getLexeme(), token.getLine(), token.getCol(), ctx.getSourceName());
}
}

View File

@ -24,6 +24,6 @@ public class NumberLiteralParselet implements PrefixParselet {
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
return new NumberLiteralNode(token.getLexeme());
return new NumberLiteralNode(token.getLexeme(), token.getLine(), token.getCol(), ctx.getSourceName());
}
}

View File

@ -27,6 +27,6 @@ public class StringLiteralParselet implements PrefixParselet {
public ExpressionNode parse(ParserContext ctx, Token token) {
String raw = token.getRaw();
String content = raw.substring(1, raw.length() - 1);
return new StringLiteralNode(content);
return new StringLiteralNode(content, token.getLine(), token.getCol(), ctx.getSourceName());
}
}

View File

@ -67,7 +67,7 @@ public class ASTPrinter {
}
case FunctionNode(
String name, List<ParameterNode> parameters, String returnType, List<StatementNode> body
, _, _, _
, int _, int _, String _
) -> {
System.out.println(pad + "function " + name
+ "(params=" + parameters + ", return=" + returnType + ")");
@ -82,7 +82,7 @@ public class ASTPrinter {
.orElse("");
System.out.println(pad + "declare " + d.getName() + ":" + d.getType() + init);
}
case AssignmentNode(String variable, ExpressionNode value, _, int _, String _) ->
case AssignmentNode(String variable, ExpressionNode value, int _, int _, String _) ->
System.out.println(pad + variable + " = " + value);
case IfNode(
ExpressionNode condition, List<StatementNode> thenBranch, List<StatementNode> elseBranch, int _,

View File

@ -194,15 +194,15 @@ public class ASTJsonSerializer {
"operand", exprToMap(operand)
);
// 布尔字面量
case BoolLiteralNode(boolean value) -> exprMap("BoolLiteral", "value", value);
case BoolLiteralNode(boolean value, int _, int _, String _) -> exprMap("BoolLiteral", "value", value);
// 标识符
case IdentifierNode(String name, int _, int _, String _) -> exprMap("Identifier", "name", name);
// 数字字面量
case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
case NumberLiteralNode(String value, int _, int _, String _) -> exprMap("NumberLiteral", "value", value);
// 字符串字面量
case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
case StringLiteralNode(String value, int _, int _, String _) -> exprMap("StringLiteral", "value", value);
// 调用表达式
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, _, _, _) -> {
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, int _, int _, String _) -> {
List<Object> args = new ArrayList<>(arguments.size());
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);

View File

@ -51,8 +51,8 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
ExpressionNode callee = call.callee();
// 支持模块调用形式ModuleName.FunctionName(...)
if (callee instanceof MemberExpressionNode(var obj, String member, _, _, _)
&& obj instanceof IdentifierNode(String mod, _, _, _)) {
if (callee instanceof MemberExpressionNode(var obj, String member, int _, int _, String _)
&& obj instanceof IdentifierNode(String mod, int _, int _, String _)) {
// 验证模块是否存在并已导入
if (!ctx.getModules().containsKey(mod)
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
@ -65,7 +65,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
functionName = member;
// 简单函数名形式func(...)
} else if (callee instanceof IdentifierNode(String name, _, _, _)) {
} else if (callee instanceof IdentifierNode(String name, int _, int _, String _)) {
functionName = name;
// 不支持的 callee 形式

View File

@ -42,18 +42,9 @@ public record SemanticError(Node node, String message) {
String file = null;
if (node != null) {
try {
line = (int) node.getClass().getMethod("line").invoke(node);
} catch (Exception ignored) {
}
try {
col = (int) node.getClass().getMethod("column").invoke(node);
} catch (Exception ignored) {
}
try {
file = (String) node.getClass().getMethod("file").invoke(node);
} catch (Exception ignored) {
}
line = node.line();
col = node.column();
file = node.file();
}
StringBuilder sb = new StringBuilder();