about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-06-01 00:47:39 +0200
committerMel <mel@rnrd.eu>2025-06-01 00:47:39 +0200
commitcea333644191567bcfdd1e24e4606aafbf34e3cb (patch)
tree42d50d37059a7f23cd659823fb3fba7695c83615 /boot
parentef66c99536a631ff2bd1dab4a825ce16d2efa530 (diff)
downloadcatskill-cea333644191567bcfdd1e24e4606aafbf34e3cb.tar.zst
catskill-cea333644191567bcfdd1e24e4606aafbf34e3cb.zip
Parse if/else if/else statements
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
-rw-r--r--boot/parse.c51
-rw-r--r--boot/tree.c55
2 files changed, 100 insertions, 6 deletions
diff --git a/boot/parse.c b/boot/parse.c
index 5bfa006..ed00f94 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -531,6 +531,55 @@ parser_statement_declaration(struct Parser* p, struct Parser_Error* error)
 }
 
 struct Statement*
+parser_statement_conditional(struct Parser* p, struct Parser_Error* error)
+{
+    struct Statement_Value_Conditional conditional = { 0 };
+    struct Token if_token = parser_need(p, TOKEN_WORD_IF, error);
+
+    // primary if condition + block.
+    struct Expression* if_condition = CHECK(parser_expression(p, error));
+    struct Block_Node then_block = CHECK(parser_block_node(p, error));
+    conditional.conditions[conditional.condition_count++] = (struct Statement_Conditional_Branch){
+        .when = if_condition,
+        .then = then_block,
+    };
+
+    struct Span span = span_merge(if_token.span, then_block.span);
+    while (parser_probe(p, TOKEN_WORD_ELSE)) {
+        check(conditional.condition_count < STATEMENT_VALUE_CONDITIONAL_MAX,
+              "too many conditional branches");
+        parser_next(p);
+
+        struct Statement_Conditional_Branch branch = { 0 };
+        if (parser_probe(p, TOKEN_WORD_IF)) {
+            // else if condition + block.
+            parser_next(p);
+            struct Expression* else_condition = CHECK(parser_expression(p, error));
+            struct Block_Node else_block = CHECK(parser_block_node(p, error));
+
+            branch = (struct Statement_Conditional_Branch){
+                .when = else_condition,
+                .then = else_block,
+            };
+        } else {
+            // else block.
+            struct Block_Node else_block = CHECK(parser_block_node(p, error));
+            branch = (struct Statement_Conditional_Branch){
+                .when = nil,
+                .then = else_block,
+            };
+        }
+
+        conditional.conditions[conditional.condition_count++] = branch;
+        span = span_merge(span, branch.then.span);
+    }
+
+    return statement_new(
+        STATEMENT_CONDITIONAL, (union Statement_Value){ .conditional = conditional }, span,
+        if_token.location);
+}
+
+struct Statement*
 parser_statement(struct Parser* p, struct Parser_Error* error)
 {
     struct Token token = parser_peek(p);
@@ -553,6 +602,8 @@ parser_statement(struct Parser* p, struct Parser_Error* error)
             return parser_statement_declaration(p, error);
     }
 
+    if (token_is(&token, TOKEN_WORD_IF)) return parser_statement_conditional(p, error);
+
     if (token_is(&token, TOKEN_CURLY_OPEN)) {
         // a block statement.
         struct Block_Node block = CHECK(parser_block_node(p, error));
diff --git a/boot/tree.c b/boot/tree.c
index 7dd6447..dcda4da 100644
--- a/boot/tree.c
+++ b/boot/tree.c
@@ -549,6 +549,7 @@ enum Statement_Kind
     STATEMENT_DECLARATION,
     // NOTE: a block could be an expression in the future.
     STATEMENT_BLOCK,
+    STATEMENT_CONDITIONAL,
 };
 
 struct Statement_Value_Expression
@@ -570,11 +571,25 @@ struct Statement_Value_Block
     struct Block_Node inner; // the block of statements.
 };
 
+#define STATEMENT_VALUE_CONDITIONAL_MAX 8
+
+struct Statement_Value_Conditional
+{
+    struct Statement_Conditional_Branch
+    {
+        // if nil, the condition is always true.
+        struct Expression* when;
+        struct Block_Node then;
+    } conditions[STATEMENT_VALUE_CONDITIONAL_MAX];
+    uint condition_count;
+};
+
 union Statement_Value
 {
     struct Statement_Value_Expression expression;
     struct Statement_Value_Declaration declaration;
     struct Statement_Value_Block block;
+    struct Statement_Value_Conditional conditional;
 };
 
 struct Statement
@@ -592,6 +607,21 @@ struct Statement
 
 REGION(struct Statement, statement)
 
+void statement_print(const struct Statement* statement);
+
+void
+block_node_print(const struct Block_Node* block)
+{
+    printf("(block \n");
+    FOR_EACH(struct Statement*, statement, block->statements)
+    {
+        printf("\t");
+        statement_print(statement);
+        printf("\n");
+    }
+    printf(")");
+}
+
 struct Statement*
 statement_new(
     enum Statement_Kind kind, union Statement_Value value, struct Span span, struct Cursor location)
@@ -639,15 +669,28 @@ statement_print(const struct Statement* statement)
         break;
     }
     case STATEMENT_BLOCK:
-        printf("(block \n");
-        FOR_EACH(struct Statement*, sub_statement, statement->value.block.inner.statements)
-        {
-            printf("\t");
-            statement_print(sub_statement);
-            printf("\n");
+        block_node_print(&statement->value.block.inner);
+        break;
+    case STATEMENT_CONDITIONAL: {
+        printf("(conditional");
+        for (uint i = 0; i < statement->value.conditional.condition_count; ++i) {
+            const struct Statement_Conditional_Branch* branch =
+                &statement->value.conditional.conditions[i];
+
+            printf(" ");
+            if (branch->when) {
+                printf("(when ");
+                expression_print(branch->when);
+                printf(") ");
+            } else {
+                printf("(always) ");
+            }
+
+            block_node_print(&branch->then);
         }
         printf(")");
         break;
+    }
     default:
         failure("unexpected statement kind passed to `statement_print`");
         break;