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