From 47c5a0ecc6fccc3bfe0b5394d7297e635ab23184 Mon Sep 17 00:00:00 2001 From: Mel Date: Tue, 24 Jun 2025 20:07:41 +0200 Subject: Parse named arguments, thus enabling type constructions Signed-off-by: Mel --- boot/parse.c | 36 +++++++++++++++++++++++++++++------- boot/tree.c | 32 +++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/boot/parse.c b/boot/parse.c index 3d2706f..ac324f9 100644 --- a/boot/parse.c +++ b/boot/parse.c @@ -705,7 +705,7 @@ parser_expression_member(struct Parser* p, struct Parser_Error* error) { struct Expression* left = CHECK(parser_expression_primary(p, error)); - // NOTE: see `parser_expression_postfix_call`. + // NOTE: see `parser_expression_postfix_call_or_construct`. while (parser_probe(p, TOKEN_DOT)) { parser_next(p); struct Token name_token = CHECK(parser_need(p, TOKEN_NAME, error)); @@ -720,31 +720,53 @@ parser_expression_member(struct Parser* p, struct Parser_Error* error) } struct Expression* -parser_expression_postfix_call( +parser_expression_postfix_call_or_construct( struct Parser* p, struct Expression* subject, struct Parser_Error* error) { // NOTE: because of the way the parser works, we have to parse all subsequent - // call expressions in the same loop. + // call/construct expressions in the same loop. // this is the case with an expression like `meow_function()(123)`, // where the hypothetical `meow_function` returns a function pointer. + + // calls and constructs look identical in the parser. + // * call: `meow_function(123, "hello")` + // * construct: `Meow(num = 123, str = "hello")` while (parser_probe(p, TOKEN_ROUND_OPEN)) { parser_next(p); + struct String_Array argument_names = string_array_new(); struct Expression *arguments_head = nil, *arguments_current = nil; while (!parser_probe(p, TOKEN_ROUND_CLOSE)) { + + // check if we have a named argument. + struct String name = string_empty(); + struct Token name_token = parser_peek(p), next = parser_peek_further(p); + if (token_is(&name_token, TOKEN_NAME) && token_is(&next, TOKEN_ASSIGN)) { + parser_next(p); + parser_next(p); + name = name_token.value.name; + } + struct Expression* argument = CHECK(parser_expression(p, error)); if (!arguments_head) arguments_head = argument; else arguments_current->next = argument; arguments_current = argument; + + // if we have a named argument, we need to add it to the names array, + // otherwise we just add an empty string. + string_array_add(&argument_names, name); + if (parser_probe(p, TOKEN_COMMA)) parser_next(p); } struct Token token = CHECK(parser_need(p, TOKEN_ROUND_CLOSE, error)); struct Span span = span_merge(subject->span, token.span); - union Expression_Value value = { .call = { subject, arguments_head } }; - subject = expression_new(EXPRESSION_CALL, value, span, token.location); + union Expression_Value value = { + .call_or_construct = { subject, arguments_head, argument_names } + }; + subject = expression_new(EXPRESSION_CALL_OR_CONSTRUCT, value, span, token.location); } return subject; @@ -754,7 +776,7 @@ struct Expression* parser_expression_postfix_subscript( struct Parser* p, struct Expression* subject, struct Parser_Error* error) { - // NOTE: see `parser_expression_postfix_call`. + // NOTE: see `parser_expression_postfix_call_or_construct`. while (parser_probe(p, TOKEN_SQUARE_OPEN)) { parser_next(p); @@ -776,7 +798,7 @@ parser_expression_postfix(struct Parser* p, struct Parser_Error* error) struct Token token = parser_peek(p); switch (token.kind) { case TOKEN_ROUND_OPEN: - return parser_expression_postfix_call(p, expression, error); + return parser_expression_postfix_call_or_construct(p, expression, error); case TOKEN_SQUARE_OPEN: return parser_expression_postfix_subscript(p, expression, error); default:; diff --git a/boot/tree.c b/boot/tree.c index e4d0272..2e046da 100644 --- a/boot/tree.c +++ b/boot/tree.c @@ -665,7 +665,7 @@ enum Expression_Kind EXPRESSION_BINARY_OPERATION, EXPRESSION_GROUP, - EXPRESSION_CALL, + EXPRESSION_CALL_OR_CONSTRUCT, EXPRESSION_SUBSCRIPT, EXPRESSION_MEMBER, EXPRESSION_INCREMENT_DECREMENT, @@ -717,10 +717,14 @@ struct Expression_Group struct Expression* inner_expression; }; -struct Expression_Call +struct Expression_Call_Or_Construct { struct Expression* subject; - struct Expression* arguments; // linked list of expressions. + // linked list of argument expressions. + struct Expression* arguments; + // names of the arguments, if given. + // an unnamed argument is represented as an empty string. + struct String_Array argument_names; }; struct Expression_Subscript @@ -764,7 +768,7 @@ union Expression_Value struct Expression_Unary_Operator unary_operator; struct Expression_Binary_Operator binary_operator; struct Expression_Group group; - struct Expression_Call call; + struct Expression_Call_Or_Construct call_or_construct; struct Expression_Subscript subscript; struct Expression_Member member; struct Expression_Increment_Decrement increment_decrement; @@ -867,16 +871,26 @@ expression_print(const struct Expression* expression) expression_print(expression->value.group.inner_expression); printf(")"); break; - case EXPRESSION_CALL: - printf("(call "); - expression_print(expression->value.call.subject); - FOR_EACH(struct Expression*, argument, expression->value.call.arguments) + case EXPRESSION_CALL_OR_CONSTRUCT: { + const struct Expression_Call_Or_Construct* coc = + &expression->value.call_or_construct; + printf("(call/construct "); + expression_print(coc->subject); + uint i = 0; + FOR_EACH(struct Expression*, argument, coc->arguments) { - printf(" "); + struct String name = string_array_at(&coc->argument_names, i++); + if (!string_is_empty(name)) { + printf(" (named arg '%s' ", name.data); + } else { + printf(" (arg "); + } expression_print(argument); + printf(")"); } printf(")"); break; + } case EXPRESSION_SUBSCRIPT: printf("(subscript "); expression_print(expression->value.subscript.subject); -- cgit 1.4.1