From a37262c61fd577e0e620e024007e0139929e3926 Mon Sep 17 00:00:00 2001 From: Mel Date: Fri, 18 Jul 2025 01:39:28 +0200 Subject: Separate call/construct expression back into parts This makes it easier to decide whether subject should be interpreted as a type of expression. Signed-off-by: Mel --- boot/parse.c | 115 +++++++++++++++++++++++---------- boot/tests/parse/postfix_chaining.cskt | 2 +- boot/tree.c | 35 +++++++--- boot/visit.c | 95 +++++++++++++++++++-------- 4 files changed, 177 insertions(+), 70 deletions(-) diff --git a/boot/parse.c b/boot/parse.c index 6363379..4403024 100644 --- a/boot/parse.c +++ b/boot/parse.c @@ -30,6 +30,7 @@ enum Parser_Error_Kind PARSER_ERROR_EXPECTED_STATEMENT_END, PARSER_ERROR_EXPECTED_PRIMARY_EXPRESSION, PARSER_ERROR_EXPECTED_TYPE, + PARSER_ERROR_EXPECTED_ARGUMENTS, PARSER_ERROR_EXPECTED_PRAGMA, PARSER_ERROR_EXPECTED_PRAGMA_ARGUMENT }; @@ -50,6 +51,8 @@ parser_error_kind_to_string(enum Parser_Error_Kind error_kind) return "expected primary expression"; case PARSER_ERROR_EXPECTED_TYPE: return "expected type"; + case PARSER_ERROR_EXPECTED_ARGUMENTS: + return "expected arguments"; case PARSER_ERROR_EXPECTED_PRAGMA: return "expected pragma"; case PARSER_ERROR_EXPECTED_PRAGMA_ARGUMENT: @@ -467,6 +470,45 @@ parser_function_header_node(struct Parser* p, struct Parser_Error* error) return header; } +struct Argument_Group_Node +parser_argument_group_node(struct Parser* p, struct Parser_Error* error) +{ + struct String_Array argument_names = string_array_new(); + struct Expression *arguments_head = nil, *arguments_current = nil; + for (;;) { + // 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_RETURN(parser_expression(p, error), struct Argument_Group_Node); + 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); + } else { + break; + } + } + + return (struct Argument_Group_Node){ + .arguments = arguments_head, .argument_names = argument_names + }; +} + struct Bare_Declaration_Node parser_bare_declaration_node(struct Parser* p, struct Parser_Error* error) { @@ -990,46 +1032,46 @@ parser_expression_postfix_member( } struct Expression* -parser_expression_postfix_call_or_construct( +parser_expression_postfix_call( struct Parser* p, struct Expression* subject, struct Parser_Error* error) { - // calls and constructs look identical in the parser. - // * call: `meow_function(123, "hello")` - // * construct: `Meow(num = 123, str = "hello")` CHECK(parser_need(p, TOKEN_ROUND_OPEN, error)); + parser_unglue(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 Argument_Group_Node argument_group = parser_argument_group_node(p, error); + if (!parser_error_is_none(error)) { + parser_error_wrap(error, PARSER_ERROR_EXPECTED_ARGUMENTS); + return nil; + } - struct Expression* argument = CHECK(parser_expression(p, error)); - if (!arguments_head) - arguments_head = argument; - else - arguments_current->next = argument; - arguments_current = argument; + parser_unglue(p); + struct Token close_token = CHECK(parser_need(p, TOKEN_ROUND_CLOSE, error)); - // 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); + struct Span span = span_merge(subject->span, close_token.span); + union Expression_Value value = { .call = { subject, argument_group } }; + return expression_new(EXPRESSION_CALL, value, span, close_token.location); +} - if (parser_probe(p, TOKEN_COMMA)) parser_next(p); +struct Expression* +parser_expression_postfix_construct( + struct Parser* p, struct Expression* subject, struct Parser_Error* error) +{ + CHECK(parser_need(p, TOKEN_CURLY_OPEN, error)); + parser_unglue(p); + + struct Argument_Group_Node argument_group = parser_argument_group_node(p, error); + if (!parser_error_is_none(error)) { + parser_error_wrap(error, PARSER_ERROR_EXPECTED_ARGUMENTS); + return nil; } - struct Token token = CHECK(parser_need(p, TOKEN_ROUND_CLOSE, error)); + parser_unglue(p); + struct Token token = CHECK(parser_need(p, TOKEN_CURLY_CLOSE, error)); + struct Span span = span_merge(subject->span, token.span); - union Expression_Value value = { - .call_or_construct = { subject, arguments_head, argument_names } - }; - return expression_new(EXPRESSION_CALL_OR_CONSTRUCT, value, span, token.location); + // TODO: convert the `subject` expression to a type node? somehow? + union Expression_Value value = { .construct = { subject, argument_group } }; + return expression_new(EXPRESSION_CONSTRUCT, value, span, token.location); } struct Expression* @@ -1096,19 +1138,22 @@ parser_expression_postfix(struct Parser* p, struct Parser_Error* error) struct Token token = parser_peek(p); switch (token.kind) { case TOKEN_ROUND_OPEN: - expression = parser_expression_postfix_call_or_construct(p, expression, error); + expression = CHECK(parser_expression_postfix_call(p, expression, error)); + continue; + case TOKEN_CURLY_OPEN: + expression = CHECK(parser_expression_postfix_construct(p, expression, error)); continue; case TOKEN_SQUARE_OPEN: - expression = parser_expression_postfix_subscript(p, expression, error); + expression = CHECK(parser_expression_postfix_subscript(p, expression, error)); continue; case TOKEN_DOT: - expression = parser_expression_postfix_member(p, expression, error); + expression = CHECK(parser_expression_postfix_member(p, expression, error)); continue; case TOKEN_QUESTION: - expression = parser_expression_postfix_try(p, expression, error); + expression = CHECK(parser_expression_postfix_try(p, expression, error)); continue; case TOKEN_BANG: - expression = parser_expression_postfix_must(p, expression, error); + expression = CHECK(parser_expression_postfix_must(p, expression, error)); continue; default:; } diff --git a/boot/tests/parse/postfix_chaining.cskt b/boot/tests/parse/postfix_chaining.cskt index c2267b4..cbc3072 100644 --- a/boot/tests/parse/postfix_chaining.cskt +++ b/boot/tests/parse/postfix_chaining.cskt @@ -10,4 +10,4 @@ a.b("hi")!?++-- >>> (expr (subscript (expr (subscript (expr (subscript (expr (name a)) (expr 1))) (expr 2))) (expr 3))) -(expr (increment/decrement -- postfix (expr (increment/decrement ++ postfix (expr (try (expr (must (expr (call/construct (expr (member of (expr (name a)) named b)) (arg (expr "hi")))))))))))) \ No newline at end of file +(expr (increment/decrement -- postfix (expr (increment/decrement ++ postfix (expr (try (expr (must (expr (call (expr (member of (expr (name a)) named b)) (arg (expr "hi")))))))))))) \ No newline at end of file diff --git a/boot/tree.c b/boot/tree.c index 4320279..228bbd4 100644 --- a/boot/tree.c +++ b/boot/tree.c @@ -423,6 +423,19 @@ struct Function_Header_Node struct Cursor location; }; +// a group of arguments, used in function calls and +// constructions. +// styled as either `1, 2, 3` or, when optional names +// are given `a = 1, b = 2, c = 3`. +struct Argument_Group_Node +{ + // 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; +}; + // a declaration of a variable, constant, or other binding, without a mutability // signifier, like `let` or `var`. // the mutability is determined by some outside context, where @@ -698,7 +711,8 @@ enum Expression_Kind EXPRESSION_BINARY_OPERATION, EXPRESSION_GROUP, - EXPRESSION_CALL_OR_CONSTRUCT, + EXPRESSION_CONSTRUCT, + EXPRESSION_CALL, EXPRESSION_SUBSCRIPT, EXPRESSION_MEMBER, EXPRESSION_INCREMENT_DECREMENT, @@ -752,14 +766,18 @@ struct Expression_Group struct Expression* inner_expression; }; -struct Expression_Call_Or_Construct +struct Expression_Call { struct Expression* subject; - // 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 Argument_Group_Node argument_group; +}; + +struct Expression_Construct +{ + // this should be the type to construct, e.g. `int` or `string` or a generic like `Maybe(X)` + // right now, we can't guarantee it fully. + struct Expression* subject; + struct Argument_Group_Node argument_group; }; struct Expression_Subscript @@ -813,7 +831,8 @@ union Expression_Value struct Expression_Unary_Operator unary_operator; struct Expression_Binary_Operator binary_operator; struct Expression_Group group; - struct Expression_Call_Or_Construct call_or_construct; + struct Expression_Call call; + struct Expression_Construct construct; struct Expression_Subscript subscript; struct Expression_Member member; struct Expression_Increment_Decrement increment_decrement; diff --git a/boot/visit.c b/boot/visit.c index 68e95bf..fa49a7d 100644 --- a/boot/visit.c +++ b/boot/visit.c @@ -41,7 +41,8 @@ struct Visit_Table void (*visit_expression_unary_operation)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_binary_operation)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_group)(struct Visit* visitor, struct Expression* expr); - void (*visit_expression_call_or_construct)(struct Visit* visitor, struct Expression* expr); + void (*visit_expression_call)(struct Visit* visitor, struct Expression* expr); + void (*visit_expression_construct)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_subscript)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_member)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_increment_decrement)(struct Visit* visitor, struct Expression* expr); @@ -64,6 +65,7 @@ struct Visit_Table void (*visit_block_node)(struct Visit* visitor, struct Block_Node* node); void (*visit_bare_declaration_node)(struct Visit* visitor, struct Bare_Declaration_Node* node); void (*visit_function_header_node)(struct Visit* visitor, struct Function_Header_Node* header); + void (*visit_argument_group_node)(struct Visit* visitor, struct Argument_Group_Node* node); void (*visit_pragma_node)(struct Visit* visitor, struct Pragma_Node* node); }; @@ -209,8 +211,11 @@ walk_expression(struct Visit* visit, struct Expression* expression) case EXPRESSION_GROUP: VISIT(visit_expression_group, expression); break; - case EXPRESSION_CALL_OR_CONSTRUCT: - VISIT(visit_expression_call_or_construct, expression); + case EXPRESSION_CALL: + VISIT(visit_expression_call, expression); + break; + case EXPRESSION_CONSTRUCT: + VISIT(visit_expression_construct, expression); break; case EXPRESSION_SUBSCRIPT: VISIT(visit_expression_subscript, expression); @@ -268,11 +273,19 @@ walk_expression_group(struct Visit* visit, struct Expression* expression) } void -walk_expression_call_or_construct(struct Visit* visit, struct Expression* expression) +walk_expression_call(struct Visit* visit, struct Expression* expression) +{ + struct Expression_Call* call = &expression->value.call; + VISIT(visit_expression, call->subject); + VISIT(visit_argument_group_node, &call->argument_group); +} + +void +walk_expression_construct(struct Visit* visit, struct Expression* expression) { - struct Expression_Call_Or_Construct* coc = &expression->value.call_or_construct; - VISIT(visit_expression, coc->subject); - FOR_EACH (struct Expression*, argument, coc->arguments) { VISIT(visit_expression, argument); } + struct Expression_Construct* construct = &expression->value.construct; + VISIT(visit_expression, construct->subject); + VISIT(visit_argument_group_node, &construct->argument_group); } void @@ -433,6 +446,12 @@ walk_function_header_node(struct Visit* visit, struct Function_Header_Node* head VISIT_MAYBE(visit_type_node, header->return_type); } +void +walk_argument_group_node(struct Visit* visit, struct Argument_Group_Node* node) +{ + FOR_EACH (struct Expression*, argument, node->arguments) { VISIT(visit_expression, argument); } +} + void walk_pragma_node(struct Visit* visit, struct Pragma_Node* node) { @@ -461,7 +480,8 @@ struct Visit_Table walk_functions = { .visit_expression_unary_operation = walk_expression_unary_operation, .visit_expression_binary_operation = walk_expression_binary_operation, .visit_expression_group = walk_expression_group, - .visit_expression_call_or_construct = walk_expression_call_or_construct, + .visit_expression_call = walk_expression_call, + .visit_expression_construct = walk_expression_construct, .visit_expression_subscript = walk_expression_subscript, .visit_expression_member = walk_expression_member, .visit_expression_increment_decrement = walk_expression_increment_decrement, @@ -482,6 +502,7 @@ struct Visit_Table walk_functions = { .visit_block_node = walk_block_node, .visit_bare_declaration_node = walk_bare_declaration_node, .visit_function_header_node = walk_function_header_node, + .visit_argument_group_node = walk_argument_group_node, .visit_pragma_node = walk_pragma_node, }; @@ -769,25 +790,28 @@ printer_visit_expression_group(struct Visit* visit, struct Expression* expr) } void -printer_visit_expression_call_or_construct(struct Visit* visit, struct Expression* expr) +printer_visit_expression_call(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE - struct Expression_Call_Or_Construct* coc = &expr->value.call_or_construct; - PRINT("(call/construct "); - VISIT(visit_expression, coc->subject); + struct Expression_Call* call = &expr->value.call; + PRINT("(call "); + VISIT(visit_expression, call->subject); + VISIT(visit_argument_group_node, &call->argument_group); + + PRINT(")"); +} + +void +printer_visit_expression_construct(struct Visit* visit, struct Expression* expr) +{ + TREE_PRINTER_PREAMBLE + + struct Expression_Construct* construct = &expr->value.construct; + PRINT("(construct "); + VISIT(visit_expression, construct->subject); + VISIT(visit_argument_group_node, &construct->argument_group); - uint i = 0; - FOR_EACH (struct Expression*, argument, coc->arguments) { - struct String name = string_array_at(&coc->argument_names, i++); - if (name.data && name.data[0] != '\0') { - PRINT(" (named arg '%s' ", name.data); - } else { - PRINT(" (arg "); - } - VISIT(visit_expression, argument); - PRINT(")"); - } PRINT(")"); } @@ -864,8 +888,7 @@ printer_visit_type_node(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("(type "); - if (node->variadic) - PRINT("variadic "); + if (node->variadic) PRINT("variadic "); walk_type_node(visit, node); PRINT(")"); } @@ -1077,6 +1100,24 @@ printer_visit_pragma_node(struct Visit* visit, struct Pragma_Node* node) } } +void +printer_visit_argument_group_node(struct Visit* visit, struct Argument_Group_Node* node) +{ + TREE_PRINTER_PREAMBLE + + uint i = 0; + FOR_EACH (struct Expression*, argument, node->arguments) { + struct String name = string_array_at(&node->argument_names, i++); + if (name.data && name.data[0] != '\0') { + PRINT(" (named arg '%s' ", name.data); + } else { + PRINT(" (arg "); + } + VISIT(visit_expression, argument); + PRINT(")"); + } +} + struct Visit_Table printer_visit_functions = { .visit_tree = printer_visit_tree, @@ -1098,7 +1139,8 @@ struct Visit_Table printer_visit_functions = { .visit_expression_unary_operation = printer_visit_expression_unary_operation, .visit_expression_binary_operation = printer_visit_expression_binary_operation, .visit_expression_group = printer_visit_expression_group, - .visit_expression_call_or_construct = printer_visit_expression_call_or_construct, + .visit_expression_call = printer_visit_expression_call, + .visit_expression_construct = printer_visit_expression_construct, .visit_expression_subscript = printer_visit_expression_subscript, .visit_expression_member = printer_visit_expression_member, .visit_expression_increment_decrement = printer_visit_expression_increment_decrement, @@ -1121,6 +1163,7 @@ struct Visit_Table printer_visit_functions = { .visit_block_node = printer_visit_block_node, .visit_bare_declaration_node = printer_visit_bare_declaration_node, .visit_function_header_node = printer_visit_function_header, + .visit_argument_group_node = printer_visit_argument_group_node, .visit_pragma_node = printer_visit_pragma_node, }; -- cgit 1.4.1