about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-07-18 01:39:28 +0200
committerMel <mel@rnrd.eu>2025-07-18 01:39:28 +0200
commita37262c61fd577e0e620e024007e0139929e3926 (patch)
treef8069c772d6649067c9165863184fb9e4fe5cdee
parent9a89772396f13ef27f29e71901a75e4760930cf2 (diff)
downloadcatskill-a37262c61fd577e0e620e024007e0139929e3926.tar.zst
catskill-a37262c61fd577e0e620e024007e0139929e3926.zip
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 <mel@rnrd.eu>
-rw-r--r--boot/parse.c115
-rw-r--r--boot/tests/parse/postfix_chaining.cskt2
-rw-r--r--boot/tree.c35
-rw-r--r--boot/visit.c95
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
@@ -434,6 +447,12 @@ walk_function_header_node(struct Visit* visit, struct Function_Header_Node* head
 }
 
 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)
 {
     // visit each pragma node in the linked list.
@@ -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,
 };