!25 fix: AOT 编译后无法定位语义错误
Merge pull request !25 from zhangxun/bugfix/aot-semantic-error-unknown-location
This commit is contained in:
commit
84a6b2701c
@ -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);
|
||||
|
||||
@ -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; // 变量暂不处理(后续可扩展符号表查询)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
* 返回数字字面量的字符串形式。
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
* 返回字符串字面量的带引号表示,适用于语法树调试或文本输出。
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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 _,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 形式
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user