feat: 命名参数 draw_rect(width: 10, height: 20)
This commit is contained in:
+4
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user