From b7d0a532d7557b1efa74e6dbc35863e4b112a146 Mon Sep 17 00:00:00 2001 From: Mel Date: Mon, 4 May 2026 21:50:23 +0200 Subject: Lower all styles of source loops Signed-off-by: Mel --- boot/lower.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file 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); -- cgit 1.4.1