feat: 增强函数调用表达式的语义分析

- 支持三种函数调用形式:模块函数调用、结构体实例方法调用、普通函数调用
- 增加对结构体方法调用的支持
- 优化模块和函数解析逻辑
- 增强错误处理,包括对私有函数访问控制的检查
- 代码注释和文档进行了详细补充,以提高可读性和可维护性
This commit is contained in:
Luke 2025-08-29 15:26:39 +08:00
parent 50f4fbb82d
commit 86a78bd1c6

View File

@ -1,44 +1,74 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression; package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.parser.ast.CallExpressionNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
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.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.semantic.analyzers.base.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;
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
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.FunctionType; import org.jcnc.snow.compiler.semantic.type.FunctionType;
import org.jcnc.snow.compiler.semantic.type.StructType;
import org.jcnc.snow.compiler.semantic.type.Type; 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 CallExpressionAnalyzer} 是函数调用表达式的语义分析器 * {@code CallExpressionAnalyzer} 是函数调用表达式 ({@link CallExpressionNode}) 的语义分析器
* <p> *
* 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式执行如下操作: * <p>它负责处理所有形式的调用表达式 {@code callee(arg1, arg2, ...)}并执行如下操作
* <ul> * <ul>
* <li>识别调用目标支持模块成员函数调用和当前模块函数调用也支持自动在所有已导入模块中查找唯一同名函数</li> * <li>识别调用目标支持三种调用方式
* <li>根据被调用函数的参数签名检查实参数量和类型的兼容性</li> * <ol>
* <li>支持数值参数的宽化转换 int double</li> * <li>模块函数调用 {@code module.func(...)} </li>
* <li>支持数值到字符串的隐式转换自动视为调用 {@code to_string}</li> * <li>结构体实例方法调用 {@code instance.method(...)} </li>
* <li>在发生类型不匹配未导入模块或函数未定义等情况下记录语义错误</li> * <li>普通函数调用当前模块或导入模块 {@code func(...)} </li>
* <li>新增"_"开头的函数名只允许在本模块访问禁止跨模块访问</li> * </ol>
* </li>
* <li>在函数解析时遵循如下规则
* <ul>
* <li>若是模块调用必须确认模块已导入</li>
* <li>若是结构体实例调用需先解析左侧表达式类型并确认方法存在</li>
* <li>若是普通函数调用优先在当前模块中查找若未找到则尝试唯一导入模块解析</li>
* </ul> * </ul>
* </li>
* <li>参数检查与类型推断
* <ul>
* <li>检查实参与形参数量是否一致</li>
* <li>检查类型兼容性支持数值宽化转换 (int double)</li>
* <li>支持数值到字符串的隐式转换自动视为调用 {@code to_string}</li>
* </ul>
* </li>
* <li>错误处理
* <ul>
* <li>函数/方法未定义时记录 {@link SemanticError}</li>
* <li>访问未导入的模块时报错</li>
* <li>跨模块访问私有函数 "_" 开头时报错</li>
* <li>参数数量或类型不匹配时报错</li>
* </ul>
* </li>
* <li>最终返回函数的返回类型若分析过程中存在错误返回 {@link BuiltinType#INT} 作为默认回退类型</li>
* </ul>
*
* <p>此分析器是编译器语义分析阶段的重要组成部分确保调用表达式在类型系统和模块作用域中合法</p>
*/ */
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> { public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
/** /**
* 分析函数调用表达式并推断其类型 * 分析函数调用表达式推断返回类型并执行语义检查
* *
* @param ctx 当前语义分析上下文提供日志错误记录模块访问等功能 * @param ctx 当前语义分析上下文提供日志错误记录模块访问等功能
* @param mi 当前模块信息用于函数查找及模块依赖判断 * @param mi 当前模块信息用于函数查找及模块依赖判断
* @param fn 当前分析的函数节点 * @param fn 当前正在分析的函数节点函数作用域
* @param locals 局部符号表用于变量查找 * @param locals 局部符号表用于变量和结构体实例查找
* @param call 待分析的函数调用表达式节点 * @param call 待分析的函数调用表达式节点
* @return 表达式的返回类型如果存在语义错误默认返回 {@code BuiltinType.INT} * @return 调用表达式的返回类型若存在语义错误返回 {@link BuiltinType#INT}
*/ */
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
@ -46,88 +76,148 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
FunctionNode fn, FunctionNode fn,
SymbolTable locals, SymbolTable locals,
CallExpressionNode call) { CallExpressionNode call) {
ctx.log("检查函数调用: " + call.callee()); ctx.log("检查函数/方法调用: " + call.callee());
ModuleInfo target = mi; // 默认目标模块为当前模块 ExpressionNode callee = call.callee(); // 被调用的表达式函数名或成员访问
String functionName; ModuleInfo targetModule = mi; // 初始假设目标模块为当前模块
ExpressionNode callee = call.callee(); String functionName; // 被调用函数的名称
FunctionType funcType; // 被调用函数的类型签名
// 支持模块调用形式: ModuleName.FunctionName(...) // ========== 支持三种调用形式 ==========
if (callee instanceof MemberExpressionNode(var obj, String member, NodeContext _) // 1. 模块.函数(...)
&& obj instanceof IdentifierNode(String mod, NodeContext _)) { // 2. 结构体实例.方法(...)
// 验证模块是否存在并已导入 // 3. 普通函数(...)
if (!ctx.getModules().containsKey(mod)
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) { if (callee instanceof MemberExpressionNode memberExpr) {
// -------- 情况1 & 情况2: 成员调用表达式 --------
ExpressionNode left = memberExpr.object();
String member = memberExpr.member();
if (left instanceof IdentifierNode idNode) {
// 左侧是标识符可能是模块名或结构体变量
String leftName = idNode.name();
// (1) 模块.函数调用
if (ctx.getModules().containsKey(leftName) &&
(mi.getImports().contains(leftName) || mi.getName().equals(leftName))) {
targetModule = ctx.getModules().get(leftName);
functionName = member;
funcType = targetModule.getFunctions().get(functionName);
} else {
// (2) 结构体实例.方法调用
Symbol sym = locals.resolve(leftName);
if (sym != null && sym.type() instanceof StructType structType) {
funcType = structType.getMethods().get(member);
functionName = member;
if (funcType == null) {
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
"未知或未导入模块: " + mod)); "结构体方法未定义: " + structType + "." + member));
ctx.log("错误: 未导入模块 " + mod); ctx.log("错误: 结构体方法未定义 " + structType + "." + member);
return BuiltinType.INT; return BuiltinType.INT;
} }
target = ctx.getModules().get(mod); } else {
ctx.getErrors().add(new SemanticError(callee,
"未知或未导入模块: " + leftName));
ctx.log("错误: 未导入模块或未声明变量 " + leftName);
return BuiltinType.INT;
}
}
} else {
// (2 扩展) 任意表达式.方法调用
Type leftType = ctx.getRegistry()
.getExpressionAnalyzer(left)
.analyze(ctx, mi, fn, locals, left);
if (leftType instanceof StructType structType) {
funcType = structType.getMethods().get(member);
functionName = member; functionName = member;
if (funcType == null) {
ctx.getErrors().add(new SemanticError(callee,
"结构体方法未定义: " + structType + "." + member));
ctx.log("错误: 结构体方法未定义 " + structType + "." + member);
return BuiltinType.INT;
} }
// 简单函数名形式: func(...) } else {
else if (callee instanceof IdentifierNode(String name, NodeContext _)) { ctx.getErrors().add(new SemanticError(callee,
functionName = name; "不支持的成员调用对象类型: " + leftType));
ctx.log("错误: 不支持的成员调用对象类型 " + leftType);
return BuiltinType.INT;
} }
// 不支持的 callee 形式 }
else { } else if (callee instanceof IdentifierNode idNode) {
// -------- 情况3: 普通函数调用 --------
functionName = idNode.name();
funcType = mi.getFunctions().get(functionName); // 优先在当前模块查找
// 若当前模块未定义则尝试在导入模块中唯一解析
if (funcType == null) {
ModuleInfo unique = null;
for (String imp : mi.getImports()) {
ModuleInfo mod = ctx.getModules().get(imp);
if (mod != null && mod.getFunctions().containsKey(functionName)) {
if (unique != null) {
unique = null;
break;
} // 冲突: 不唯一
unique = mod;
}
}
if (unique != null) {
targetModule = unique;
funcType = unique.getFunctions().get(functionName);
}
}
} else {
// 不支持的调用方式 (如直接 lambda/表达式调用)
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
"不支持的调用方式: " + callee)); "不支持的调用方式: " + callee));
ctx.log("错误: 不支持的调用方式 " + callee); ctx.log("错误: 不支持的调用方式 " + callee);
return BuiltinType.INT; return BuiltinType.INT;
} }
// ------------------------- // -------- 访问控制检查 --------
// 私有函数访问控制 if (functionName != null && funcType != null &&
// ------------------------- functionName.startsWith("_") && !targetModule.getName().equals(mi.getName())) {
// 如果函数名以"_"开头且不是在本模块调用则报错
if (functionName.startsWith("_") && !target.getName().equals(mi.getName())) {
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
"无法访问模块私有函数: " + target.getName() + "." + functionName "无法访问模块私有函数: " + targetModule.getName() + "." + functionName
+ "(下划线开头的函数只允许在定义模块内访问)")); + "(下划线开头的函数只允许在定义模块内访问)"));
ctx.log("错误: 试图跨模块访问私有函数 " + target.getName() + "." + functionName); ctx.log("错误: 试图跨模块访问私有函数 " + targetModule.getName() + "." + functionName);
return BuiltinType.INT; return BuiltinType.INT;
} }
// 查找目标函数签名先在当前模块/指定模块查找 // -------- 函数是否存在 --------
FunctionType ft = target.getFunctions().get(functionName); if (funcType == null) {
// 未找到则报错
if (ft == null) {
ctx.getErrors().add(new SemanticError(callee, ctx.getErrors().add(new SemanticError(callee,
"函数未定义: " + functionName)); "函数未定义: " + functionName));
ctx.log("错误: 函数未定义 " + functionName); ctx.log("错误: 函数未定义 " + functionName);
return BuiltinType.INT; return BuiltinType.INT;
} }
// 分析所有实参并获取类型 // -------- 分析实参类型 --------
List<Type> args = new ArrayList<>(); List<Type> args = new ArrayList<>();
for (ExpressionNode arg : call.arguments()) { for (ExpressionNode arg : call.arguments()) {
args.add(ctx.getRegistry().getExpressionAnalyzer(arg) args.add(ctx.getRegistry().getExpressionAnalyzer(arg)
.analyze(ctx, mi, fn, locals, arg)); .analyze(ctx, mi, fn, locals, arg));
} }
// 参数数量检查 // -------- 参数数量检查 --------
if (args.size() != ft.paramTypes().size()) { if (args.size() != funcType.paramTypes().size()) {
ctx.getErrors().add(new SemanticError(call, ctx.getErrors().add(new SemanticError(call,
"参数数量不匹配: 期望 " + ft.paramTypes().size() "参数数量不匹配: 期望 " + funcType.paramTypes().size()
+ " 个, 实际 " + args.size() + "")); + " 个, 实际 " + args.size() + ""));
ctx.log("错误: 参数数量不匹配: 期望 " ctx.log("错误: 参数数量不匹配: 期望 "
+ ft.paramTypes().size() + ", 实际 " + args.size()); + funcType.paramTypes().size() + ", 实际 " + args.size());
} else { } else {
// 参数类型检查与转换支持 // -------- 参数类型检查与转换 --------
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
Type expected = ft.paramTypes().get(i); Type expected = funcType.paramTypes().get(i);
Type actual = args.get(i); Type actual = args.get(i);
// 完全兼容或数值宽化转换
boolean ok = expected.isCompatible(actual) boolean ok = expected.isCompatible(actual)
|| (expected.isNumeric() && actual.isNumeric() || (expected.isNumeric() && actual.isNumeric()
&& Type.widen(actual, expected) == expected); && Type.widen(actual, expected) == expected);
// 支持将数值自动转换为字符串 // 特殊情况数值自动转 string
if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) { if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) {
ctx.log(String.format( ctx.log(String.format(
"隐式将参数 %d 的数值类型 %s 转换为 string (to_string)", "隐式将参数 %d 的数值类型 %s 转换为 string (to_string)",
@ -136,7 +226,6 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
ok = true; ok = true;
} }
// 类型不匹配记录语义错误
if (!ok) { if (!ok) {
ctx.getErrors().add(new SemanticError(call, ctx.getErrors().add(new SemanticError(call,
String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s", String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
@ -147,8 +236,8 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
} }
} }
// 返回函数的返回类型作为整个调用表达式的类型 // -------- 返回类型 --------
ctx.log("函数调用类型: 返回 " + ft.returnType()); ctx.log("函数调用类型: 返回 " + funcType.returnType());
return ft.returnType(); return funcType.returnType();
} }
} }