feat: struct方法 impl (P1 #9)
- lexer: TOK_IMPL 关键字
- ast: AST_IMPL_BLOCK, AST_METHOD_CALL + AST_PROGRAM impls数组
- parser: impl StructName { fn ... } + p.method() 方法调用
- sema: 方法名mangle(StructName$method), self参数, 类型检查
- codegen: METHOD_CALL→mangled函数调用(recv为第一参数)
- 新增集成测试: 19_struct_method.l
P1 4项全部完成: type alias + enum + array + impl
测试: 145 通过 (41+15+65+24)
This commit is contained in:
+106
@@ -1,4 +1,5 @@
|
||||
#include "sema.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// === 类型关系 ===
|
||||
@@ -345,6 +346,66 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_METHOD_CALL: {
|
||||
analyze_expr(node->as.method_call.receiver, scope, errors, a);
|
||||
const char* recv_struct = node->as.method_call.receiver->type.struct_name;
|
||||
if (node->as.method_call.receiver->type.kind != TYPE_STRUCT || !recv_struct) {
|
||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||
"只有结构体类型支持方法调用");
|
||||
node->type.kind = TYPE_ERROR; break;
|
||||
}
|
||||
// 构造改名后的函数名并查找
|
||||
char mangled[256];
|
||||
snprintf(mangled, sizeof(mangled), "%s$%s", recv_struct,
|
||||
node->as.method_call.method_name);
|
||||
Symbol* sym = scope_lookup(scope, mangled);
|
||||
if (!sym || sym->kind != SYM_FUNCTION) {
|
||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||
"结构体 '%s' 没有方法 '%s'", recv_struct,
|
||||
node->as.method_call.method_name);
|
||||
node->type.kind = TYPE_ERROR; break;
|
||||
}
|
||||
// 检查参数数量(用户提供的参数 + 隐含的 self)
|
||||
if (node->as.method_call.arg_count + 1 != sym->param_count) {
|
||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||
"方法 '%s' 需要 %zu 个参数,提供了 %zu 个",
|
||||
node->as.method_call.method_name,
|
||||
sym->param_count > 0 ? sym->param_count - 1 : 0,
|
||||
node->as.method_call.arg_count);
|
||||
node->type.kind = TYPE_ERROR; break;
|
||||
}
|
||||
// 对每个参数进行类型检查(跳过 self 参数,即 sym->param_types[0] 是 self 的类型)
|
||||
for (size_t i = 0; i < node->as.method_call.arg_count; i++) {
|
||||
analyze_expr(node->as.method_call.args[i], scope, errors, a);
|
||||
TypeKind actual = node->as.method_call.args[i]->type.kind;
|
||||
TypeKind expected = sym->param_types[i + 1];
|
||||
if (actual != TYPE_ERROR && actual != expected &&
|
||||
!(expected == TYPE_I64 && actual == TYPE_ENUM)) {
|
||||
if (expected == TYPE_STRUCT) {
|
||||
// 结构体类型参数:比较具体类型名
|
||||
const char* actual_name = node->as.method_call.args[i]->type.struct_name;
|
||||
const char* expected_name = sym->param_struct_names ? sym->param_struct_names[i + 1] : NULL;
|
||||
if (!actual_name || !expected_name || strcmp(actual_name, expected_name) != 0) {
|
||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||
"参数 %zu 类型不匹配: 期望 '%s',得到 '%s'",
|
||||
i + 1,
|
||||
expected_name ? expected_name : "struct",
|
||||
actual_name ? actual_name : type_name(actual));
|
||||
}
|
||||
} else {
|
||||
error_add(errors, "<sema>", node->loc.line, node->loc.col,
|
||||
"参数 %zu 类型不匹配: 期望 '%s',得到 '%s'",
|
||||
i + 1, type_name(expected), type_name(actual));
|
||||
}
|
||||
}
|
||||
}
|
||||
node->type.kind = sym->return_type;
|
||||
if (sym->return_type == TYPE_STRUCT && sym->return_struct_type_name) {
|
||||
node->type.struct_name = sym->return_struct_type_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -391,6 +452,51 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
|
||||
fnames, ftypes, fstruct_names,
|
||||
sd->as.struct_decl.field_count);
|
||||
}
|
||||
// 处理 impl 块:将方法名改写为 StructName$methodName,
|
||||
// 并自动添加 self 参数(第一个参数),然后注册为普通函数。
|
||||
// 同时将改写后的方法追加到程序 functions 数组方便后续 codegen。
|
||||
{
|
||||
// 先统计需要新增多少个函数(impl 中的方法总数)
|
||||
size_t extra_fn = 0;
|
||||
for (size_t i = 0; i < node->as.program.impl_count; i++) {
|
||||
AstNode* impl = node->as.program.impls[i];
|
||||
extra_fn += impl->as.impl_block.method_count;
|
||||
}
|
||||
if (extra_fn > 0) {
|
||||
AstNode** new_fns = (AstNode**)arena_alloc_impl(a,
|
||||
(node->as.program.fn_count + extra_fn) * sizeof(AstNode*));
|
||||
memcpy(new_fns, node->as.program.functions,
|
||||
node->as.program.fn_count * sizeof(AstNode*));
|
||||
size_t write_pos = node->as.program.fn_count;
|
||||
|
||||
for (size_t i = 0; i < node->as.program.impl_count; i++) {
|
||||
AstNode* impl = node->as.program.impls[i];
|
||||
const char* st_name = impl->as.impl_block.struct_name;
|
||||
// 验证目标结构体存在
|
||||
Symbol* st_sym = scope_lookup_struct(scope, st_name);
|
||||
if (!st_sym) {
|
||||
error_add(errors, "<sema>", impl->loc.line, impl->loc.col,
|
||||
"impl 的目标结构体 '%s' 未定义", st_name);
|
||||
continue;
|
||||
}
|
||||
for (size_t j = 0; j < impl->as.impl_block.method_count; j++) {
|
||||
AstNode* method = impl->as.impl_block.methods[j];
|
||||
// 构造改名后的函数名
|
||||
char mangled[256];
|
||||
snprintf(mangled, sizeof(mangled), "%s$%s", st_name,
|
||||
method->as.function.name);
|
||||
method->as.function.name = arena_strdup_impl(a, mangled,
|
||||
strlen(mangled));
|
||||
// 追加到新 functions 数组
|
||||
new_fns[write_pos++] = method;
|
||||
}
|
||||
}
|
||||
// 更新程序节点
|
||||
node->as.program.functions = new_fns;
|
||||
node->as.program.fn_count = node->as.program.fn_count + extra_fn;
|
||||
}
|
||||
}
|
||||
|
||||
// 第二遍:收集所有函数签名
|
||||
for (size_t i = 0; i < node->as.program.fn_count; i++) {
|
||||
AstNode* fn = node->as.program.functions[i];
|
||||
|
||||
Reference in New Issue
Block a user