about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-04 21:50:23 +0200
committerMel <mel@rnrd.eu>2026-05-04 21:50:23 +0200
commitb7d0a532d7557b1efa74e6dbc35863e4b112a146 (patch)
treea77ee46db91bbfacd82f09c7b5ddd005665fd60f /boot
parent81c9e336eb88bc2d010a7cd776bb14789977fb4a (diff)
downloadcatskill-b7d0a532d7557b1efa74e6dbc35863e4b112a146.tar.zst
catskill-b7d0a532d7557b1efa74e6dbc35863e4b112a146.zip
Lower all styles of source loops
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
-rw-r--r--boot/lower.c190
1 files changed, 178 insertions, 12 deletions
diff --git a/boot/lower.c b/boot/lower.c
index 9880d6e..aea49ce 100644
--- a/boot/lower.c
+++ b/boot/lower.c
@@ -713,6 +713,38 @@ lower_pass_1(struct Lower_Context* ctx, struct Tree* tree)
 struct Block* lower_block(struct Lower_Context* ctx, struct Tree_Block* tree_block);
 struct Statement* lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt);
 
+// small helpers used when synthesizing nodes when de-sugaring source.
+
+struct Expression*
+lower_make_name_expression(struct String name, struct Span span)
+{
+    union Expression_Value v = { 0 };
+    v.name.name = name;
+
+    return expression_new(EXPRESSION_NAME, v, span);
+}
+
+struct Expression*
+lower_make_integer_literal_expression(int64 value, struct Span span)
+{
+    union Expression_Value v = { 0 };
+    v.integer_literal.value = value;
+
+    return expression_new(EXPRESSION_INTEGER_LITERAL, v, span);
+}
+
+struct Expression*
+lower_make_binary_expression(
+    enum Binary_Operation op, struct Expression* left, struct Expression* right, struct Span span)
+{
+    union Expression_Value v = { 0 };
+    v.binary_operator.operation = op;
+    v.binary_operator.left_operand = left;
+    v.binary_operator.right_operand = right;
+
+    return expression_new(EXPRESSION_BINARY_OPERATION, v, span);
+}
+
 // turns a source expression into the lowered form.
 struct Expression*
 lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr)
@@ -1071,33 +1103,167 @@ lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt)
     }
     case TREE_STATEMENT_LOOP: {
         struct Tree_Statement_Value_Loop* loop = &tree_stmt->value.loop;
-        union Statement_Value v = { 0 };
 
         switch (loop->style) {
-        case TREE_STATEMENT_LOOP_STYLE_WHILE:
+        case TREE_STATEMENT_LOOP_STYLE_WHILE: {
+            union Statement_Value v = { 0 };
             v.loop.condition = lower_expression(ctx, loop->condition);
             v.loop.body = lower_block(ctx, &loop->body);
-
-            break;
+            return statement_new(STATEMENT_LOOP, v, tree_stmt->span);
+        }
         case TREE_STATEMENT_LOOP_STYLE_ENDLESS: {
-            // synthesize a `true` literal
+            // synthesize a `true` literal so the ir always has a real
+            // condition to emit.
             union Expression_Value tv = { 0 };
             tv.bool_literal.value = true;
+            union Statement_Value v = { 0 };
             v.loop.condition = expression_new(EXPRESSION_BOOLEAN_LITERAL, tv, tree_stmt->span);
             v.loop.body = lower_block(ctx, &loop->body);
-
-            break;
+            return statement_new(STATEMENT_LOOP, v, tree_stmt->span);
         }
         case TREE_STATEMENT_LOOP_STYLE_C:
-        case TREE_STATEMENT_LOOP_STYLE_FOR_EACH:
-            lower_emit_error_c(
-                ctx->unit, tree_stmt->span, "unimplemented: c-style and for-each loops");
-            return nil;
+        case TREE_STATEMENT_LOOP_STYLE_FOR_EACH: {
+            // both styles become a basic while loop
+            // TODO: right now we only support for-each for ranges,
+            // we want to add iteration over real containers later.
+
+            struct Tree_Bare_Declaration* decl = &loop->declaration;
+
+            struct Tree_Expression* init = nil;
+            struct Tree_Expression* cond_tree = loop->condition;
+            struct Tree_Expression* iter_tree = loop->iteration;
+            struct Tree_Expression* synth_cond = nil;
+            struct Statement* synth_iter = nil;
+
+            if (loop->style == TREE_STATEMENT_LOOP_STYLE_FOR_EACH) {
+                if (!decl->initializer
+                    || decl->initializer->kind != TREE_EXPRESSION_BINARY_OPERATION
+                    || decl->initializer->value.binary_operator.operation != BINARY_RANGE) {
+                    lower_emit_error_c(
+                        ctx->unit, tree_stmt->span,
+                        "unimplemented: for-each over non-range collections");
+                    return nil;
+                }
+                init = decl->initializer->value.binary_operator.left_operand;
+
+                // condition and iteration step are synthesized below
+            } else {
+                init = decl->initializer;
+            }
+
+            if (array_length(&decl->names) != 1) {
+                lower_emit_error_c(
+                    ctx->unit, tree_stmt->span,
+                    "unimplemented: for-loop with multi-name declaration");
+                return nil;
+            }
+            struct String name = *array_at(struct String, &decl->names, 0);
+
+            // open outer block scope.
+            // this stores the iterator variable.
+            lower_push_scope(ctx, SCOPE_TYPE_BLOCK);
+
+            if (lower_is_shadowing(ctx, name)) {
+                lower_emit_error(
+                    ctx->unit, tree_stmt->span,
+                    string_concatenate(
+                        ARG_ASCII, "name '", ARG_STRING, name, ARG_ASCII,
+                        "' shadows an existing binding", ARG_END));
+                lower_pop_scope(ctx, SCOPE_TYPE_BLOCK);
+                return nil;
+            }
+
+            struct Type_Ref iter_type = lower_intern_type_ref(ctx, decl->type);
+            lower_declare_local(ctx, name, iter_type);
+
+            // build the iteration variable
+            union Statement_Value decl_v = { 0 };
+            decl_v.declaration.name = name;
+            decl_v.declaration.type = iter_type;
+            decl_v.declaration.initializer = init ? lower_expression(ctx, init) : nil;
+            struct Statement* decl_stmt = statement_new(STATEMENT_DECLARATION, decl_v, decl->span);
+
+            // for range for-each
+            // we synthesize the right condition and step.
+            if (loop->style == TREE_STATEMENT_LOOP_STYLE_FOR_EACH) {
+                struct Tree_Expression* hi = decl->initializer->value.binary_operator.right_operand;
+                struct Expression* lhs = lower_make_name_expression(name, tree_stmt->span);
+                struct Expression* rhs = lower_expression(ctx, hi);
+                synth_cond = nil; // unused; we go straight to the ir form
+                (void)synth_cond;
+
+                struct Expression* cond_expr =
+                    lower_make_binary_expression(BINARY_LESS_THAN, lhs, rhs, tree_stmt->span);
+
+                // step: x = x + 1
+                struct Expression* iter_lhs = lower_make_name_expression(name, tree_stmt->span);
+                struct Expression* iter_rhs_left =
+                    lower_make_name_expression(name, tree_stmt->span);
+                struct Expression* one = lower_make_integer_literal_expression(1, tree_stmt->span);
+                struct Expression* iter_rhs =
+                    lower_make_binary_expression(BINARY_PLUS, iter_rhs_left, one, tree_stmt->span);
+                union Statement_Value iter_v = { 0 };
+                iter_v.assign.lhs = iter_lhs;
+                iter_v.assign.rhs = iter_rhs;
+                synth_iter = statement_new(STATEMENT_ASSIGN, iter_v, tree_stmt->span);
+
+                // rest of the construction continues below
+                struct Block* body = lower_block(ctx, &loop->body);
+                if (synth_iter) array_push(&body->statements, &synth_iter);
+
+                union Statement_Value while_v = { 0 };
+                while_v.loop.condition = cond_expr;
+                while_v.loop.body = body;
+                struct Statement* while_stmt =
+                    statement_new(STATEMENT_LOOP, while_v, tree_stmt->span);
+
+                struct Block* outer = block_new();
+                outer->statements = array_new(struct Statement*, 4);
+                array_push(&outer->statements, &decl_stmt);
+                array_push(&outer->statements, &while_stmt);
+
+                lower_pop_scope(ctx, SCOPE_TYPE_BLOCK);
+
+                union Statement_Value v = { 0 };
+                v.block.inner = outer;
+
+                return statement_new(STATEMENT_BLOCK, v, tree_stmt->span);
+            }
+
+            // c-style
+            // no synthesis needed!
+            struct Block* body = lower_block(ctx, &loop->body);
+            if (iter_tree) {
+                struct Tree_Statement iter_node = {
+                    .kind = TREE_STATEMENT_EXPRESSION,
+                    .value = { .expression = { .inner = iter_tree } },
+                    .span = iter_tree->span,
+                };
+                struct Statement* iter_stmt = lower_statement(ctx, &iter_node);
+                if (iter_stmt) array_push(&body->statements, &iter_stmt);
+            }
+
+            union Statement_Value while_v = { 0 };
+            while_v.loop.condition = cond_tree ? lower_expression(ctx, cond_tree) : nil;
+            while_v.loop.body = body;
+            struct Statement* while_stmt = statement_new(STATEMENT_LOOP, while_v, tree_stmt->span);
+
+            struct Block* outer = block_new();
+            outer->statements = array_new(struct Statement*, 4);
+            array_push(&outer->statements, &decl_stmt);
+            array_push(&outer->statements, &while_stmt);
+
+            lower_pop_scope(ctx, SCOPE_TYPE_BLOCK);
+
+            union Statement_Value v = { 0 };
+            v.block.inner = outer;
+
+            return statement_new(STATEMENT_BLOCK, v, tree_stmt->span);
+        }
         default:
             lower_emit_error_c(ctx->unit, tree_stmt->span, "unknown loop style");
             return nil;
         }
-        return statement_new(STATEMENT_LOOP, v, tree_stmt->span);
     }
     case TREE_STATEMENT_BREAK:
         return statement_new(STATEMENT_BREAK, (union Statement_Value){ 0 }, tree_stmt->span);