feat: 命名参数 draw_rect(width: 10, height: 20)

This commit is contained in:
2026-06-05 20:54:58 +08:00
parent c6e492662e
commit 6b6925b2b8
9 changed files with 272 additions and 24 deletions
+4 -3
View File
@@ -110,9 +110,9 @@ AstNode* ast_make_unary(void* alloc, BinaryOp op, AstNode* operand, SourceLoc lo
return n;
}
AstNode* ast_make_call(void* alloc, const char* name, AstNode** args, size_t count, SourceLoc loc) {
AstNode* ast_make_call(void* alloc, const char* name, AstNode** args, const char** arg_names, size_t count, SourceLoc loc) {
NEW(alloc, AST_CALL_EXPR);
n->as.call.name = name; n->as.call.args = args; n->as.call.arg_count = count;
n->as.call.name = name; n->as.call.args = args; n->as.call.arg_names = arg_names; n->as.call.arg_count = count;
return n;
}
@@ -242,11 +242,12 @@ AstNode* ast_make_impl_block(void* alloc, const char* struct_name, AstNode** met
}
AstNode* ast_make_method_call(void* alloc, AstNode* receiver, const char* method,
AstNode** args, size_t count, SourceLoc loc) {
AstNode** args, const char** arg_names, size_t count, SourceLoc loc) {
NEW(alloc, AST_METHOD_CALL);
n->as.method_call.receiver = receiver;
n->as.method_call.method_name = method;
n->as.method_call.args = args;
n->as.method_call.arg_names = arg_names;
n->as.method_call.arg_count = count;
return n;
}
+4 -4
View File
@@ -89,7 +89,7 @@ struct AstNode {
// AST_UNARY_EXPR
struct { BinaryOp op; struct AstNode* operand; } unary;
// AST_CALL_EXPR
struct { const char* name; struct AstNode** args; size_t arg_count; } call;
struct { const char* name; struct AstNode** args; const char** arg_names; size_t arg_count; } call;
// AST_LITERAL_EXPR
struct { TypeKind lit_type; union { int64_t i64_val; double f64_val; bool bool_val; const char* str_val; }; } literal;
// AST_IDENT_EXPR
@@ -114,7 +114,7 @@ struct AstNode {
// AST_IMPL_BLOCK
struct { const char* struct_name; struct AstNode** methods; size_t method_count; } impl_block;
// AST_METHOD_CALL
struct { struct AstNode* receiver; const char* method_name; struct AstNode** args; size_t arg_count; } method_call;
struct { struct AstNode* receiver; const char* method_name; struct AstNode** args; const char** arg_names; size_t arg_count; } method_call;
} as;
};
@@ -138,7 +138,7 @@ AstNode* ast_make_return(void* alloc, AstNode* expr, SourceLoc loc);
AstNode* ast_make_expr_stmt(void* alloc, AstNode* expr, SourceLoc loc);
AstNode* ast_make_binary(void* alloc, BinaryOp op, AstNode* left, AstNode* right, SourceLoc loc);
AstNode* ast_make_unary(void* alloc, BinaryOp op, AstNode* operand, SourceLoc loc);
AstNode* ast_make_call(void* alloc, const char* name, AstNode** args, size_t count, SourceLoc loc);
AstNode* ast_make_call(void* alloc, const char* name, AstNode** args, const char** arg_names, size_t count, SourceLoc loc);
AstNode* ast_make_literal_i64(void* alloc, int64_t val, SourceLoc loc);
AstNode* ast_make_literal_f64(void* alloc, double val, SourceLoc loc);
AstNode* ast_make_literal_bool(void* alloc, bool val, SourceLoc loc);
@@ -155,6 +155,6 @@ AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, const char* v
AstNode* ast_make_index_expr(void* alloc, AstNode* array, AstNode* index, SourceLoc loc);
AstNode* ast_make_array_assign(void* alloc, const char* name, AstNode* index, AstNode* value, SourceLoc loc);
AstNode* ast_make_impl_block(void* alloc, const char* struct_name, AstNode** methods, size_t count, SourceLoc loc);
AstNode* ast_make_method_call(void* alloc, AstNode* receiver, const char* method, AstNode** args, size_t count, SourceLoc loc);
AstNode* ast_make_method_call(void* alloc, AstNode* receiver, const char* method, AstNode** args, const char** arg_names, size_t count, SourceLoc loc);
#endif
+32 -4
View File
@@ -185,12 +185,25 @@ static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) {
// 函数调用: name(...)
if (match(p, TOK_LPAREN)) {
AstNode* args[16]; int arg_count = 0;
AstNode* args[16]; const char* arg_names[16]; int arg_count = 0;
bool seen_named = false;
while (peek(p)->kind != TOK_RPAREN && !error->message) {
if (arg_count >= 16) {
error->message = "函数参数过多"; error->filename = p->filename;
error->line = peek(p)->line; error->col = peek(p)->col; return NULL;
}
// 命名参数: name: expr
if (peek(p)->kind == TOK_IDENT && (p->tokens[p->pos + 1].kind == TOK_COLON)) {
const Token* aname = advance(p); advance(p); // 跳过标识符和 ':'
arg_names[arg_count] = arena_strdup_impl(p->arena, aname->start, aname->length);
seen_named = true;
} else {
if (seen_named) {
error->message = "命名参数必须放在位置参数之后"; error->filename = p->filename;
error->line = peek(p)->line; error->col = peek(p)->col; return NULL;
}
arg_names[arg_count] = NULL;
}
args[arg_count] = parse_expr(p, error);
if (!args[arg_count]) return NULL;
arg_count++;
@@ -200,8 +213,11 @@ static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) {
if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL;
AstNode** arg_arr = arena_alloc_impl(p->arena, arg_count * sizeof(AstNode*));
memcpy(arg_arr, args, arg_count * sizeof(AstNode*));
const char** name_arr = seen_named
? memcpy(arena_alloc_impl(p->arena, arg_count * sizeof(const char*)), arg_names, arg_count * sizeof(const char*))
: NULL;
return ast_make_call(p->arena, arena_strdup_impl(p->arena, name->start, name->length),
arg_arr, arg_count, tok_loc(name));
arg_arr, name_arr, arg_count, tok_loc(name));
}
return ast_make_ident(p->arena,
arena_strdup_impl(p->arena, name->start, name->length),
@@ -245,9 +261,18 @@ static AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error
// 方法调用: expr.method(args)
if (peek(p)->kind == TOK_LPAREN) {
advance(p); // 跳过 '('
AstNode* args[16]; int arg_count = 0;
AstNode* args[16]; const char* arg_names[16]; int arg_count = 0;
bool seen_named = false;
while (peek(p)->kind != TOK_RPAREN && !error->message) {
if (arg_count >= 16) { error->message = "参数过多"; error->filename = p->filename; error->line = peek(p)->line; error->col = peek(p)->col; return NULL; }
if (peek(p)->kind == TOK_IDENT && (p->tokens[p->pos + 1].kind == TOK_COLON)) {
const Token* aname = advance(p); advance(p);
arg_names[arg_count] = arena_strdup_impl(p->arena, aname->start, aname->length);
seen_named = true;
} else {
if (seen_named) { error->message = "命名参数必须放在位置参数之后"; error->filename = p->filename; error->line = peek(p)->line; error->col = peek(p)->col; return NULL; }
arg_names[arg_count] = NULL;
}
args[arg_count] = parse_expr(p, error);
if (!args[arg_count]) return NULL;
arg_count++;
@@ -256,7 +281,10 @@ static AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error
if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL;
AstNode** arg_arr = arena_alloc_impl(p->arena, arg_count * sizeof(AstNode*));
memcpy(arg_arr, args, arg_count * sizeof(AstNode*));
left = ast_make_method_call(p->arena, left, member_name, arg_arr, arg_count, tok_loc(field));
const char** name_arr = seen_named
? memcpy(arena_alloc_impl(p->arena, arg_count * sizeof(const char*)), arg_names, arg_count * sizeof(const char*))
: NULL;
left = ast_make_method_call(p->arena, left, member_name, arg_arr, name_arr, arg_count, tok_loc(field));
} else {
left = ast_make_field_access(p->arena, left, member_name, tok_loc(field));
}
+61 -5
View File
@@ -191,6 +191,34 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
}
break;
}
// 命名参数重排序: 将命名 arg 按参数名映射到正确位置
if (node->as.call.arg_names) {
AstNode* reordered[16] = {0};
for (size_t i = 0; i < node->as.call.arg_count; i++) {
if (node->as.call.arg_names[i]) {
// 查找参数名匹配
bool found = false;
for (size_t j = 0; j < sym->param_count; j++) {
if (sym->param_names && sym->param_names[j] &&
strcmp(node->as.call.arg_names[i], sym->param_names[j]) == 0) {
reordered[j] = node->as.call.args[i];
found = true; break;
}
}
if (!found) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"函数 '%s' 没有名为 '%s' 的参数",
node->as.call.name, node->as.call.arg_names[i]);
node->type.kind = TYPE_ERROR; return;
}
} else {
// 位置参数保持原位
reordered[i] = node->as.call.args[i];
}
}
// 填充未指定的命名参数(用 NULL 跳过,后续检查会报错)
memcpy(node->as.call.args, reordered, node->as.call.arg_count * sizeof(AstNode*));
}
for (size_t i = 0; i < node->as.call.arg_count; i++) {
analyze_expr(node->as.call.args[i], scope, errors, a);
TypeKind actual = node->as.call.args[i]->type.kind;
@@ -402,6 +430,31 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
node->as.method_call.arg_count);
node->type.kind = TYPE_ERROR; break;
}
// 命名参数重排序(同 CALL_EXPR 逻辑)
if (node->as.method_call.arg_names) {
AstNode* reordered[16] = {0};
for (size_t i = 0; i < node->as.method_call.arg_count; i++) {
if (node->as.method_call.arg_names[i]) {
bool found = false;
for (size_t j = 1; j < sym->param_count; j++) { // skip self
if (sym->param_names && sym->param_names[j] &&
strcmp(node->as.method_call.arg_names[i], sym->param_names[j]) == 0) {
reordered[j - 1] = node->as.method_call.args[i];
found = true; break;
}
}
if (!found) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"方法 '%s' 没有名为 '%s' 的参数",
node->as.method_call.method_name, node->as.method_call.arg_names[i]);
node->type.kind = TYPE_ERROR; return;
}
} else {
reordered[i] = node->as.method_call.args[i];
}
}
memcpy(node->as.method_call.args, reordered, node->as.method_call.arg_count * sizeof(AstNode*));
}
// 对每个参数进行类型检查(跳过 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);
@@ -530,10 +583,12 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
for (size_t i = 0; i < node->as.program.fn_count; i++) {
AstNode* fn = node->as.program.functions[i];
TypeKind* pts = (TypeKind*)arena_alloc_impl(a, fn->as.function.param_count * sizeof(TypeKind));
const char** pnames = (const char**)arena_alloc_impl(a, fn->as.function.param_count * sizeof(const char*));
const char** pstruct_names = (const char**)arena_alloc_impl(a, fn->as.function.param_count * sizeof(const char*));
for (size_t j = 0; j < fn->as.function.param_count; j++) {
TypeKind pt = fn->as.function.params[j]->as.parameter.type;
const char* psn = fn->as.function.params[j]->as.parameter.struct_type_name;
const char* pn = fn->as.function.params[j]->as.parameter.name;
// 解析参数类型的别名
if (psn) {
Symbol* as = scope_lookup(scope, psn);
@@ -543,6 +598,7 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
}
}
pts[j] = pt;
pnames[j] = pn;
pstruct_names[j] = psn;
}
// 解析返回类型的别名
@@ -557,7 +613,7 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
}
scope_insert_function(scope, a, fn->as.function.name,
ret_t, ret_sn,
pts, pstruct_names,
pts, pnames, pstruct_names,
fn->as.function.param_count);
}
// 第三遍:分析每个函数体
@@ -840,13 +896,13 @@ void sema_analyze(AstNode* ast, ErrorList* errors, Arena* arena) {
// 注册内置函数
TypeKind params_i64[] = {TYPE_I64};
scope_insert_function(global_scope, arena, "print_i64", TYPE_VOID, NULL, params_i64, NULL, 1);
scope_insert_function(global_scope, arena, "print_i64", TYPE_VOID, NULL, params_i64, NULL, NULL, 1);
TypeKind params_f64[] = {TYPE_F64};
scope_insert_function(global_scope, arena, "print_f64", TYPE_VOID, NULL, params_f64, NULL, 1);
scope_insert_function(global_scope, arena, "print_f64", TYPE_VOID, NULL, params_f64, NULL, NULL, 1);
TypeKind params_bool[] = {TYPE_BOOL};
scope_insert_function(global_scope, arena, "print_bool", TYPE_VOID, NULL, params_bool, NULL, 1);
scope_insert_function(global_scope, arena, "print_bool", TYPE_VOID, NULL, params_bool, NULL, NULL, 1);
TypeKind params_str[] = {TYPE_STR};
scope_insert_function(global_scope, arena, "print_str", TYPE_VOID, NULL, params_str, NULL, 1);
scope_insert_function(global_scope, arena, "print_str", TYPE_VOID, NULL, params_str, NULL, NULL, 1);
analyze_node(ast, global_scope, errors, arena);
}
+6 -4
View File
@@ -30,7 +30,7 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name,
if (!sym) return NULL;
sym->name = name; sym->kind = kind; sym->type = type;
sym->is_mut = false; sym->return_type = TYPE_VOID;
sym->param_types = NULL; sym->param_count = 0;
sym->param_types = NULL; sym->param_names = NULL; sym->param_count = 0;
sym->struct_field_names = NULL;
sym->struct_field_types = NULL;
sym->struct_field_count = 0;
@@ -46,7 +46,8 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name,
Symbol* scope_insert_function(Scope* scope, void* alloc, const char* name,
TypeKind ret, const char* ret_struct_name,
TypeKind* pt, const char** pstruct_names, size_t pc) {
TypeKind* pt, const char** pnames,
const char** pstruct_names, size_t pc) {
if (scope->head) {
for (Symbol* sym = scope->head; sym; sym = sym->next) {
if (strcmp(sym->name, name) == 0) return NULL;
@@ -58,6 +59,7 @@ Symbol* scope_insert_function(Scope* scope, void* alloc, const char* name,
sym->return_type = ret;
sym->return_struct_type_name = ret_struct_name;
sym->param_types = pt;
sym->param_names = pnames;
sym->param_struct_names = pstruct_names;
sym->param_count = pc;
sym->struct_field_names = NULL;
@@ -85,7 +87,7 @@ Symbol* scope_insert_struct(Scope* scope, void* alloc, const char* name,
if (!sym) return NULL;
sym->name = name; sym->kind = SYM_STRUCT; sym->type = TYPE_STRUCT;
sym->is_mut = false; sym->return_type = TYPE_VOID;
sym->param_types = NULL; sym->param_count = 0;
sym->param_types = NULL; sym->param_names = NULL; sym->param_count = 0;
sym->struct_field_names = fnames;
sym->struct_field_types = ftypes;
sym->struct_field_struct_names = fstruct_names;
@@ -130,7 +132,7 @@ Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name,
if (!sym) return NULL;
sym->name = name; sym->kind = SYM_ENUM; sym->type = TYPE_ENUM;
sym->is_mut = false; sym->return_type = TYPE_VOID;
sym->param_types = NULL; sym->param_count = 0;
sym->param_types = NULL; sym->param_names = NULL; sym->param_count = 0;
sym->struct_field_names = vnames;
sym->struct_field_types = NULL;
sym->struct_field_struct_names = NULL;
+3 -1
View File
@@ -15,6 +15,7 @@ typedef struct Symbol {
TypeKind return_type;
const char* return_struct_type_name; // 返回类型为 struct 时的类型名
TypeKind* param_types;
const char** param_names; // 参数名(用于命名参数匹配)
const char** param_struct_names; // 参数为 struct 时的类型名
size_t param_count;
// 结构体特有(SYM_STRUCT
@@ -52,7 +53,8 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name,
// 插入函数符号
Symbol* scope_insert_function(Scope* scope, void* alloc, const char* name,
TypeKind ret, const char* ret_struct_name,
TypeKind* pt, const char** pstruct_names, size_t pc);
TypeKind* pt, const char** pnames,
const char** pstruct_names, size_t pc);
// 插入结构体符号
Symbol* scope_insert_struct(Scope* scope, void* alloc, const char* name,