about summary refs log tree commit diff
path: root/boot/parse.c
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-07-05 22:48:09 +0200
committerMel <mel@rnrd.eu>2025-07-05 22:48:09 +0200
commit620d82a5e314a82784e02b4af387a67d53242149 (patch)
treee91774653130cb34d19901aeaa7cdd6be97f3e93 /boot/parse.c
parentb71f85a7c3ee9e36b6eab9f113436fa990f6e561 (diff)
downloadcatskill-620d82a5e314a82784e02b4af387a67d53242149.tar.zst
catskill-620d82a5e314a82784e02b4af387a67d53242149.zip
Correctly recurse over self-containing non-homogenous postfix-type expressions
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/parse.c')
-rw-r--r--boot/parse.c208
1 files changed, 106 insertions, 102 deletions
diff --git a/boot/parse.c b/boot/parse.c
index c08d247..5d839a8 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -806,158 +806,162 @@ parser_expression_primary(struct Parser* p, struct Parser_Error* error)
 }
 
 struct Expression*
-parser_expression_member(struct Parser* p, struct Parser_Error* error)
+parser_expression_postfix_member(
+    struct Parser* p, struct Expression* subject, struct Parser_Error* error)
 {
-    struct Expression* left = CHECK(parser_expression_primary(p, error));
-
-    // 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));
-        struct String name = name_token.value.name;
+    CHECK(parser_need(p, TOKEN_DOT, error));
 
-        struct Span span = span_merge(left->span, name_token.span);
-        union Expression_Value value = { .member = { left, name } };
-        left = expression_new(EXPRESSION_MEMBER, value, span, name_token.location);
-    }
+    struct Token name_token = CHECK(parser_need(p, TOKEN_NAME, error));
+    struct String name = name_token.value.name;
 
-    return left;
+    struct Span span = span_merge(subject->span, name_token.span);
+    union Expression_Value value = { .member = { subject, name } };
+    return expression_new(EXPRESSION_MEMBER, value, span, name_token.location);
 }
 
 struct Expression*
 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/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(parser_need(p, TOKEN_ROUND_OPEN, error));
 
-            // 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;
+    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;
+        }
 
-            // 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 Expression* argument = CHECK(parser_expression(p, error));
+        if (!arguments_head)
+            arguments_head = argument;
+        else
+            arguments_current->next = argument;
+        arguments_current = argument;
 
-            if (parser_probe(p, TOKEN_COMMA)) parser_next(p);
-        }
+        // 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 Token token = CHECK(parser_need(p, TOKEN_ROUND_CLOSE, error));
-        struct Span span = span_merge(subject->span, token.span);
-        union Expression_Value value = {
-            .call_or_construct = { subject, arguments_head, argument_names }
-        };
-        subject = expression_new(EXPRESSION_CALL_OR_CONSTRUCT, value, span, token.location);
+        if (parser_probe(p, TOKEN_COMMA)) parser_next(p);
     }
 
-    return subject;
+    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_or_construct = { subject, arguments_head, argument_names }
+    };
+    return expression_new(EXPRESSION_CALL_OR_CONSTRUCT, value, span, token.location);
 }
 
 struct Expression*
 parser_expression_postfix_subscript(
     struct Parser* p, struct Expression* subject, struct Parser_Error* error)
 {
-    // NOTE: see `parser_expression_postfix_call_or_construct`.
-    while (parser_probe(p, TOKEN_SQUARE_OPEN)) {
-        parser_next(p);
+    CHECK(parser_need(p, TOKEN_SQUARE_OPEN, error));
 
-        struct Expression* index = CHECK(parser_expression(p, error));
-        struct Token token = CHECK(parser_need(p, TOKEN_SQUARE_CLOSE, error));
+    struct Expression* index = CHECK(parser_expression(p, error));
+    struct Token token = CHECK(parser_need(p, TOKEN_SQUARE_CLOSE, error));
 
-        struct Span span = span_merge(subject->span, span_merge(index->span, token.span));
-        union Expression_Value value = { .subscript = { subject, index } };
-        subject = expression_new(EXPRESSION_SUBSCRIPT, value, span, token.location);
-    }
-    return subject;
+    struct Span span = span_merge(subject->span, span_merge(index->span, token.span));
+    union Expression_Value value = { .subscript = { subject, index } };
+    return expression_new(EXPRESSION_SUBSCRIPT, value, span, token.location);
+}
+
+struct Expression*
+parser_expression_postfix_increment_decrement(
+    struct Parser* p, struct Expression* subject, struct Parser_Error* error)
+{
+    struct Token token = parser_peek(p);
+    enum Increment_Decrement_Operation operation = increment_decrement_operation_from_token(&token);
+
+    struct Expression_Increment_Decrement inc_dec = {
+        .prefix = false,
+        .subject = subject,
+        .operation = operation,
+    };
+
+    struct Span span = span_merge(subject->span, token.span);
+    union Expression_Value value = { .increment_decrement = inc_dec };
+    return expression_new(EXPRESSION_INCREMENT_DECREMENT, value, span, token.location);
 }
 
 struct Expression*
 parser_expression_postfix_try(
     struct Parser* p, struct Expression* subject, struct Parser_Error* error)
 {
-    while (parser_probe(p, TOKEN_QUESTION)) {
-        struct Token question_token = parser_next(p);
+    struct Token question_token = parser_next(p);
 
-        struct Span span = span_merge(subject->span, question_token.span);
-        union Expression_Value value = { .try = { subject } };
-        subject = expression_new(EXPRESSION_TRY, value, span, subject->location);
-    }
-    return subject;
+    struct Span span = span_merge(subject->span, question_token.span);
+    union Expression_Value value = { .try = { subject } };
+    return expression_new(EXPRESSION_TRY, value, span, subject->location);
 }
 
 struct Expression*
 parser_expression_postfix_must(
     struct Parser* p, struct Expression* subject, struct Parser_Error* error)
 {
-    while (parser_probe(p, TOKEN_BANG)) {
-        struct Token bang_token = parser_next(p);
+    struct Token bang_token = CHECK(parser_need(p, TOKEN_BANG, error));
 
-        struct Span span = span_merge(subject->span, bang_token.span);
-        union Expression_Value value = { .try = { subject } };
-        subject = expression_new(EXPRESSION_MUST, value, span, subject->location);
-    }
-    return subject;
+    struct Span span = span_merge(subject->span, bang_token.span);
+    union Expression_Value value = { .try = { subject } };
+    return expression_new(EXPRESSION_MUST, value, span, subject->location);
 }
 
 struct Expression*
 parser_expression_postfix(struct Parser* p, struct Parser_Error* error)
 {
-    struct Expression* expression = CHECK(parser_expression_member(p, error));
+    struct Expression* expression = CHECK(parser_expression_primary(p, error));
 
-    struct Token token = parser_peek(p);
-    switch (token.kind) {
-    case TOKEN_ROUND_OPEN:
-        return parser_expression_postfix_call_or_construct(p, expression, error);
-    case TOKEN_SQUARE_OPEN:
-        return parser_expression_postfix_subscript(p, expression, error);
+    // NOTE: we have to parse all subsequent postfix expressions non-recursively.
+    for (;;) {
+        struct Token token = parser_peek(p);
+        switch (token.kind) {
+        case TOKEN_ROUND_OPEN:
+            expression = parser_expression_postfix_call_or_construct(p, expression, error);
+            continue;
+        case TOKEN_SQUARE_OPEN:
+            expression = parser_expression_postfix_subscript(p, expression, error);
+            continue;
+        case TOKEN_DOT:
+            expression = parser_expression_postfix_member(p, expression, error);
+            continue;
+        case TOKEN_QUESTION:
+            expression = parser_expression_postfix_try(p, expression, error);
+            continue;
+        case TOKEN_BANG:
+            expression = parser_expression_postfix_must(p, expression, error);
+            continue;
+        default:;
+        }
 
-    case TOKEN_QUESTION:
-        return parser_expression_postfix_try(p, expression, error);
-    case TOKEN_BANG:
-        return parser_expression_postfix_must(p, expression, error);
-    default:;
-    }
+        enum Increment_Decrement_Operation inc_dec_op =
+            increment_decrement_operation_from_token(&token);
+        if (inc_dec_op) {
+            parser_next(p);
+            struct Expression_Increment_Decrement inc_dec = {
+                .prefix = false,
+                .subject = expression,
+                .operation = inc_dec_op,
+            };
 
-    enum Increment_Decrement_Operation inc_dec_op =
-        increment_decrement_operation_from_token(&token);
-    if (inc_dec_op) {
-        parser_next(p);
-        struct Expression_Increment_Decrement inc_dec = {
-            .prefix = false,
-            .subject = expression,
-            .operation = inc_dec_op,
-        };
+            struct Span span = span_merge(expression->span, token.span);
+            union Expression_Value value = { .increment_decrement = inc_dec };
+            expression =
+                expression_new(EXPRESSION_INCREMENT_DECREMENT, value, span, token.location);
+            continue;
+        }
 
-        struct Span span = span_merge(expression->span, token.span);
-        union Expression_Value value = { .increment_decrement = inc_dec };
-        return expression_new(EXPRESSION_INCREMENT_DECREMENT, value, span, token.location);
+        return expression;
     }
-
-    return expression;
 }
 
 struct Expression*