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:
@@ -0,0 +1,13 @@
|
||||
struct Point { x: i64, y: i64 }
|
||||
|
||||
impl Point {
|
||||
fn get_x(self: Point) -> i64 {
|
||||
return self.x;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> i64 {
|
||||
let p: Point = Point { x: 42, y: 0 };
|
||||
print_i64(p.get_x());
|
||||
return 0;
|
||||
}
|
||||
+79
-8
@@ -13,7 +13,7 @@ void test_codegen_simple_function() {
|
||||
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx = NULL;
|
||||
@@ -47,7 +47,7 @@ void test_codegen_if_else() {
|
||||
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx2 = NULL;
|
||||
@@ -78,7 +78,7 @@ void test_codegen_binary_ops() {
|
||||
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx3 = NULL;
|
||||
@@ -108,7 +108,7 @@ void test_codegen_while_loop() {
|
||||
AstNode* fn_body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, fn_body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx4 = NULL;
|
||||
@@ -162,7 +162,7 @@ void test_codegen_struct_decl() {
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx = NULL;
|
||||
@@ -217,7 +217,7 @@ void test_codegen_struct_field_access() {
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx = NULL;
|
||||
@@ -267,7 +267,7 @@ void test_codegen_enum() {
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx = NULL;
|
||||
@@ -331,7 +331,7 @@ void test_codegen_array() {
|
||||
AstNode* body = ast_make_block(&a, stmts, 4, loc_at(1, 1));
|
||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
AstNode* fns[] = { fn };
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx = NULL;
|
||||
@@ -348,6 +348,76 @@ void test_codegen_array() {
|
||||
arena_destroy(&a);
|
||||
}
|
||||
|
||||
/* === 方法调用代码生成测试 === */
|
||||
|
||||
void test_codegen_method_call() {
|
||||
Arena a = arena_create(1);
|
||||
|
||||
/* struct Point { x: i64, y: i64 } */
|
||||
AstNode* fields[2];
|
||||
fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, loc_at(1, 1));
|
||||
fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, loc_at(1, 1));
|
||||
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
|
||||
AstNode* structs[] = { struct_decl };
|
||||
|
||||
/* fn Point$get_x(self: Point) -> i64 { return self.x; } */
|
||||
AstNode* self_param = ast_make_parameter(&a, "self", TYPE_STRUCT, "Point", loc_at(1, 1));
|
||||
AstNode* params[] = { self_param };
|
||||
AstNode* self_ident = ast_make_ident(&a, "self", loc_at(1, 1));
|
||||
self_ident->type.kind = TYPE_STRUCT;
|
||||
self_ident->type.struct_name = "Point";
|
||||
AstNode* field_x = ast_make_field_access(&a, self_ident, "x", loc_at(1, 1));
|
||||
field_x->as.field_access.field_index = 0;
|
||||
field_x->type.kind = TYPE_I64;
|
||||
AstNode* ret_body = ast_make_return(&a, field_x, loc_at(1, 1));
|
||||
AstNode* ret_stmts[] = { ret_body };
|
||||
AstNode* body = ast_make_block(&a, ret_stmts, 1, loc_at(1, 1));
|
||||
AstNode* get_x_fn = ast_make_function(&a, "Point$get_x", params, 1, TYPE_I64, NULL, body, loc_at(1, 1));
|
||||
|
||||
/* fn main() -> i64 {
|
||||
let p = Point { x: 42, y: 0 };
|
||||
return p.get_x();
|
||||
} */
|
||||
const char* fnames[] = {"x", "y"};
|
||||
AstNode* fvals[] = {
|
||||
ast_make_literal_i64(&a, 42, loc_at(1, 1)),
|
||||
ast_make_literal_i64(&a, 0, loc_at(1, 1))
|
||||
};
|
||||
AstNode* init = ast_make_struct_init(&a, "Point", fnames, fvals, 2, loc_at(1, 1));
|
||||
init->type.kind = TYPE_STRUCT;
|
||||
init->type.struct_name = "Point";
|
||||
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
|
||||
init, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
|
||||
p_ident->type.kind = TYPE_STRUCT;
|
||||
p_ident->type.struct_name = "Point";
|
||||
AstNode* method_call = ast_make_method_call(&a, p_ident, "get_x", NULL, 0, loc_at(1, 1));
|
||||
method_call->type.kind = TYPE_I64;
|
||||
|
||||
AstNode* ret_main = ast_make_return(&a, method_call, loc_at(1, 1));
|
||||
AstNode* main_stmts[] = { let_stmt, ret_main };
|
||||
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
|
||||
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, loc_at(1, 1));
|
||||
|
||||
AstNode* fns[] = { get_x_fn, main_fn };
|
||||
AstNode* prog = ast_make_program(&a, fns, 2, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||
|
||||
const char* err = NULL;
|
||||
LLVMContextRef ctx = NULL;
|
||||
LLVMModuleRef mod = codegen_module(prog, &a, "test_method", &err, &ctx);
|
||||
ASSERT(mod != NULL);
|
||||
ASSERT(err == NULL);
|
||||
|
||||
char* verify_err = NULL;
|
||||
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
|
||||
ASSERT(!failed);
|
||||
|
||||
LLVMDisposeModule(mod);
|
||||
LLVMContextDispose(ctx);
|
||||
arena_destroy(&a);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
TEST_RUN(test_codegen_simple_function);
|
||||
TEST_RUN(test_codegen_if_else);
|
||||
@@ -357,5 +427,6 @@ int main(void) {
|
||||
TEST_RUN(test_codegen_struct_field_access);
|
||||
TEST_RUN(test_codegen_enum);
|
||||
TEST_RUN(test_codegen_array);
|
||||
TEST_RUN(test_codegen_method_call);
|
||||
return test_summary();
|
||||
}
|
||||
|
||||
@@ -329,6 +329,42 @@ void test_array_assign_ok() {
|
||||
arena_destroy(&a);
|
||||
}
|
||||
|
||||
/* === 方法调用语义分析测试 === */
|
||||
|
||||
void test_method_call_ok() {
|
||||
Arena a = arena_create(1);
|
||||
size_t tc; ErrorInfo lex_err = {0};
|
||||
Token* toks = lex(&a,
|
||||
"struct Point { x: i64, y: i64 } impl Point { fn get_x(self: Point) -> i64 { return self.x; } } fn main() -> i64 { let p: Point = Point { x: 42, y: 0 }; return p.get_x(); }",
|
||||
"test", &tc, &lex_err);
|
||||
ASSERT(toks != NULL);
|
||||
ErrorInfo parse_err = {0};
|
||||
AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
|
||||
ASSERT(ast != NULL);
|
||||
|
||||
ErrorList errors; error_init(&errors);
|
||||
sema_analyze(ast, &errors, &a);
|
||||
ASSERT(errors.count == 0);
|
||||
arena_destroy(&a);
|
||||
}
|
||||
|
||||
void test_method_undefined() {
|
||||
Arena a = arena_create(1);
|
||||
size_t tc; ErrorInfo lex_err = {0};
|
||||
Token* toks = lex(&a,
|
||||
"struct Point { x: i64, y: i64 } fn main() -> i64 { let p: Point = Point { x: 42, y: 0 }; return p.nope(); }",
|
||||
"test", &tc, &lex_err);
|
||||
ASSERT(toks != NULL);
|
||||
ErrorInfo parse_err = {0};
|
||||
AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
|
||||
ASSERT(ast != NULL);
|
||||
|
||||
ErrorList errors; error_init(&errors);
|
||||
sema_analyze(ast, &errors, &a);
|
||||
ASSERT(errors.count > 0); // nope 不是 Point 的方法
|
||||
arena_destroy(&a);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
TEST_RUN(test_type_error);
|
||||
TEST_RUN(test_undefined_var);
|
||||
@@ -349,5 +385,7 @@ int main(void) {
|
||||
TEST_RUN(test_array_index_type_error);
|
||||
TEST_RUN(test_array_not_indexable);
|
||||
TEST_RUN(test_array_assign_ok);
|
||||
TEST_RUN(test_method_call_ok);
|
||||
TEST_RUN(test_method_undefined);
|
||||
return test_summary();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user