aboutsummaryrefslogtreecommitdiff
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
parent81c9e336eb88bc2d010a7cd776bb14789977fb4a (diff)
downloadcatskill-b7d0a532d7557b1efa74e6dbc35863e4b112a146.tar.zst
catskill-b7d0a532d7557b1efa74e6dbc35863e4b112a146.zip
Lower all styles of source loops
Signed-off-by: Mel <mel@rnrd.eu>
-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);