about summary refs log tree commit diff
path: root/boot/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/parse.c')
-rw-r--r--boot/parse.c115
1 files changed, 80 insertions, 35 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:;
         }