diff options
| author | Mel <mel@rnrd.eu> | 2026-05-19 21:25:20 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-05-19 21:25:20 +0200 |
| commit | 8c4c219c945dfb41415e04654f488f7880e58ca2 (patch) | |
| tree | 7f6d179701fc0e0aa1f30d752bdb6d4af41aa44d /boot | |
| parent | 0e4ce87b90b28cdf044a6a1dd8ce1690813faf92 (diff) | |
| download | catskill-8c4c219c945dfb41415e04654f488f7880e58ca2.tar.zst catskill-8c4c219c945dfb41415e04654f488f7880e58ca2.zip | |
Break up large IR lowering functions into sub-functions to clean up
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/lower.c | 770 |
1 files changed, 419 insertions, 351 deletions
diff --git a/boot/lower.c b/boot/lower.c index b2b2349..01762a5 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -679,7 +679,6 @@ lower_pass_1_fill_bodies(struct Lower_Context* ctx, struct Tree* tree) struct Tree_Expression* fn_expr; if (lower_match_function_decl(stmt, &name, &fn_expr)) { Function_Id id; - // TODO: actually go into the body, only the signature for now. if (lower_function_lookup_by_name(ctx->unit, name, &id)) { struct Function* fn = *array_at(struct Function*, &ctx->unit->functions.entries, id); @@ -719,162 +718,199 @@ 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); +struct Expression* lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr); -// turns a source expression into the lowered form. struct Expression* -lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +lower_expression_integer_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) { - switch (tree_expr->kind) { - case TREE_EXPRESSION_INTEGER_LITERAL: { - union Expression_Value v = { 0 }; - v.integer_literal.value = tree_expr->value.integer_literal.value; + (void)ctx; + return ir_make_integer(tree_expr->value.integer_literal.value, tree_expr->span); +} - return expression_new(EXPRESSION_INTEGER_LITERAL, v, tree_expr->span); - } - case TREE_EXPRESSION_FLOAT_LITERAL: { - union Expression_Value v = { 0 }; - v.float_literal.value = tree_expr->value.float_literal.value; +struct Expression* +lower_expression_float_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + (void)ctx; + return ir_make_float(tree_expr->value.float_literal.value, tree_expr->span); +} - return expression_new(EXPRESSION_FLOAT_LITERAL, v, tree_expr->span); - } - case TREE_EXPRESSION_STRING_LITERAL: { - union Expression_Value v = { 0 }; - v.string_literal.value = tree_expr->value.string_literal.value; +struct Expression* +lower_expression_string_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + (void)ctx; + return ir_make_string(tree_expr->value.string_literal.value, tree_expr->span); +} - return expression_new(EXPRESSION_STRING_LITERAL, v, tree_expr->span); - } - case TREE_EXPRESSION_BOOLEAN_LITERAL: { - union Expression_Value v = { 0 }; - v.bool_literal.value = tree_expr->value.bool_literal.value; +struct Expression* +lower_expression_boolean_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + (void)ctx; + return ir_make_bool(tree_expr->value.bool_literal.value, tree_expr->span); +} - return expression_new(EXPRESSION_BOOLEAN_LITERAL, v, tree_expr->span); - } - case TREE_EXPRESSION_NAME: { - union Expression_Value v = { 0 }; - v.name.name = tree_expr->value.name.name; +struct Expression* +lower_expression_name(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + (void)ctx; + return ir_make_name(tree_expr->value.name.name, tree_expr->span); +} + +// any groups are discarded in the lowered representation, their presence +// just yields different expression constructions. +// if required by precedence the final transpiler will handle them by itself. +struct Expression* +lower_expression_group(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + return lower_expression(ctx, tree_expr->value.group.inner_expression); +} + +struct Expression* +lower_expression_unary_operation(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + return ir_make_unary( + tree_expr->value.unary_operator.operation, + lower_expression(ctx, tree_expr->value.unary_operator.operand), tree_expr->span); +} - return expression_new(EXPRESSION_NAME, v, tree_expr->span); +struct Expression* +lower_expression_binary_operation(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + enum Binary_Operation op = tree_expr->value.binary_operator.operation; + // TODO: maybe we want to support assignment expressions some day. today they aren't. + if (op >= BINARY_ASSIGN) { + lower_emit_error_c(ctx->unit, tree_expr->span, "assignment cannot appear as an expression"); + return nil; } - case TREE_EXPRESSION_GROUP: - // any groups are discarded in the lowered representation, their presence - // just yields different expression constructions. - // if required by precedence the final transpiler will handle them by itself. - return lower_expression(ctx, tree_expr->value.group.inner_expression); - case TREE_EXPRESSION_UNARY_OPERATION: { - union Expression_Value v = { 0 }; - v.unary_operator.operation = tree_expr->value.unary_operator.operation; - v.unary_operator.operand = lower_expression(ctx, tree_expr->value.unary_operator.operand); - - return expression_new(EXPRESSION_UNARY_OPERATION, v, tree_expr->span); + if (op == BINARY_RANGE) { + lower_emit_error_c( + ctx->unit, tree_expr->span, "range expressions are only valid in loop initializers"); + return nil; } - case TREE_EXPRESSION_BINARY_OPERATION: { - enum Binary_Operation op = tree_expr->value.binary_operator.operation; - // TODO: maybe we want to support assignment expressions? for now they're not supported. - if (op >= BINARY_ASSIGN) { - lower_emit_error_c( - ctx->unit, tree_expr->span, "assignment cannot appear as an expression"); - return nil; - } - if (op == BINARY_RANGE) { - lower_emit_error_c( - ctx->unit, tree_expr->span, - "range expressions are only valid in loop initializers"); + return ir_make_binary( + op, lower_expression(ctx, tree_expr->value.binary_operator.left_operand), + lower_expression(ctx, tree_expr->value.binary_operator.right_operand), tree_expr->span); +} + +struct Expression* +lower_expression_call(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + struct Tree_Argument_Group* group = &tree_expr->value.call.argument_group; + + // TODO: named arguments need lookup against the callee's parameter + // table to reorder, we only handle positional arguments today. + FOR_EACH_ARRAY (struct String, name, &group->argument_names) { + if (name->length > 0) { + lower_emit_error_c(ctx->unit, tree_expr->span, "unimplemented: named call arguments"); return nil; } - union Expression_Value v = { 0 }; - v.binary_operator.operation = op; - v.binary_operator.left_operand = - lower_expression(ctx, tree_expr->value.binary_operator.left_operand); - v.binary_operator.right_operand = - lower_expression(ctx, tree_expr->value.binary_operator.right_operand); - - return expression_new(EXPRESSION_BINARY_OPERATION, v, tree_expr->span); } - case TREE_EXPRESSION_CALL: { - struct Tree_Argument_Group* group = &tree_expr->value.call.argument_group; - // named arguments need lookup against the callee's parameter table to - // reorder; not in this commit. positional-only is fine for now. - FOR_EACH_ARRAY (struct String, name, &group->argument_names) { - if (name->length > 0) { - lower_emit_error_c( - ctx->unit, tree_expr->span, "unimplemented: named call arguments"); - return nil; - } - } + struct Expression* subject = lower_expression(ctx, tree_expr->value.call.subject); + Array(struct Expression*) arguments = array_new(struct Expression*, 16); + FOR_EACH (struct Tree_Expression*, arg, group->arguments) { + struct Expression* lowered = lower_expression(ctx, arg); + array_push(&arguments, &lowered); + } + return ir_make_call(subject, arguments, tree_expr->span); +} - union Expression_Value v = { 0 }; - v.call.subject = lower_expression(ctx, tree_expr->value.call.subject); - v.call.arguments = array_new(struct Expression*, 16); - FOR_EACH (struct Tree_Expression*, arg, group->arguments) { - struct Expression* lowered = lower_expression(ctx, arg); - array_push(&v.call.arguments, &lowered); - } +struct Expression* +lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + struct Tree_Expression* subject = tree_expr->value.construct.subject; + if (!subject || subject->kind != TREE_EXPRESSION_NAME) { + lower_emit_error_c(ctx->unit, tree_expr->span, "construction subject must be a type name"); + return nil; + } + Type_Id type_id; + if (!lower_type_lookup_by_name(ctx->unit, subject->value.name.name, &type_id)) { + lower_emit_error( + ctx->unit, subject->span, + string_concatenate( + ARG_ASCII, "undefined type '", ARG_STRING, subject->value.name.name, ARG_ASCII, "'", + ARG_END)); + return nil; + } - return expression_new(EXPRESSION_CALL, v, tree_expr->span); + Array(struct Construct_Field) fields = array_new(struct Construct_Field, 16); + struct Tree_Argument_Group* group = &tree_expr->value.construct.argument_group; + uint i = 0; + FOR_EACH (struct Tree_Expression*, arg, group->arguments) { + struct String name = string_empty(); + if (i < array_length(&group->argument_names)) + name = *array_at(struct String, &group->argument_names, i); + struct Construct_Field field = { + .name = name, + .value = lower_expression(ctx, arg), + }; + array_push(&fields, &field); + ++i; } - case TREE_EXPRESSION_CONSTRUCT: { - struct Tree_Expression* subject = tree_expr->value.construct.subject; - if (!subject || subject->kind != TREE_EXPRESSION_NAME) { - lower_emit_error_c( - ctx->unit, tree_expr->span, "construction subject must be a type name"); - return nil; - } - Type_Id type_id; - if (!lower_type_lookup_by_name(ctx->unit, subject->value.name.name, &type_id)) { - lower_emit_error( - ctx->unit, subject->span, - string_concatenate( - ARG_ASCII, "undefined type '", ARG_STRING, subject->value.name.name, ARG_ASCII, - "'", ARG_END)); - return nil; - } + return ir_make_construct(type_id, fields, tree_expr->span); +} - union Expression_Value v = { 0 }; - v.construct.type_id = type_id; - v.construct.fields = array_new(struct Construct_Field, 16); - - struct Tree_Argument_Group* group = &tree_expr->value.construct.argument_group; - uint i = 0; - FOR_EACH (struct Tree_Expression*, arg, group->arguments) { - struct String name = string_empty(); - if (i < array_length(&group->argument_names)) - name = *array_at(struct String, &group->argument_names, i); - struct Construct_Field field = { - .name = name, - .value = lower_expression(ctx, arg), - }; - array_push(&v.construct.fields, &field); - ++i; - } +// type-as-expression is only valid as the right-side of a top-level binding; +// anywhere else it is an error. +struct Expression* +lower_expression_type(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + lower_emit_error_c( + ctx->unit, tree_expr->span, "type expression not allowed inside a function body"); + return nil; +} - return expression_new(EXPRESSION_CONSTRUCT, v, tree_expr->span); - } - case TREE_EXPRESSION_TYPE: - // type-as-expression is only valid as the right-sided of a top-level binding. - lower_emit_error_c( - ctx->unit, tree_expr->span, "type expression not allowed inside a function body"); - return nil; - case TREE_EXPRESSION_FUNCTION: { - // anonymous function: lift it up as a synthetic function table entry, - // replace the expression with a name reference. - // TODO: should we have a better kind of reference here than a name? - // maybe a function reference somehow? - struct String name = lower_synthesize_name(ctx, "lambda"); +// anonymous function: lift it up as a synthetic function table entry, +// replace the expression with a name reference. +// TODO: should we have a better kind of reference here than a name? +// maybe a function reference somehow? +struct Expression* +lower_expression_function(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + struct String name = lower_synthesize_name(ctx, "lambda"); - // NOTE: we are allowed to register functions whenever we want, - // even while iterating over them, the pass will eventually get to them. - if (!lower_register_function_shell(ctx->unit, name, tree_expr->span)) return nil; + // NOTE: we are allowed to register functions whenever we want, + // even while iterating over them, the pass will eventually get to them. + if (!lower_register_function_shell(ctx->unit, name, tree_expr->span)) return nil; - Function_Id id; - lower_function_lookup_by_name(ctx->unit, name, &id); + Function_Id id; + lower_function_lookup_by_name(ctx->unit, name, &id); - struct Function* fn = *array_at(struct Function*, &ctx->unit->functions.entries, id); - fn->synthetic = true; - lower_fill_function_signature(ctx, fn, tree_expr); + struct Function* fn = *array_at(struct Function*, &ctx->unit->functions.entries, id); + fn->synthetic = true; + lower_fill_function_signature(ctx, fn, tree_expr); - return ir_make_name(name, tree_expr->span); - } + return ir_make_name(name, tree_expr->span); +} + +// turns a source expression into the lowered form. +struct Expression* +lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + switch (tree_expr->kind) { + case TREE_EXPRESSION_INTEGER_LITERAL: + return lower_expression_integer_literal(ctx, tree_expr); + case TREE_EXPRESSION_FLOAT_LITERAL: + return lower_expression_float_literal(ctx, tree_expr); + case TREE_EXPRESSION_STRING_LITERAL: + return lower_expression_string_literal(ctx, tree_expr); + case TREE_EXPRESSION_BOOLEAN_LITERAL: + return lower_expression_boolean_literal(ctx, tree_expr); + case TREE_EXPRESSION_NAME: + return lower_expression_name(ctx, tree_expr); + case TREE_EXPRESSION_GROUP: + return lower_expression_group(ctx, tree_expr); + case TREE_EXPRESSION_UNARY_OPERATION: + return lower_expression_unary_operation(ctx, tree_expr); + case TREE_EXPRESSION_BINARY_OPERATION: + return lower_expression_binary_operation(ctx, tree_expr); + case TREE_EXPRESSION_CALL: + return lower_expression_call(ctx, tree_expr); + case TREE_EXPRESSION_CONSTRUCT: + return lower_expression_construct(ctx, tree_expr); + case TREE_EXPRESSION_TYPE: + return lower_expression_type(ctx, tree_expr); + case TREE_EXPRESSION_FUNCTION: + return lower_expression_function(ctx, tree_expr); case TREE_EXPRESSION_SUBSCRIPT: case TREE_EXPRESSION_MEMBER: case TREE_EXPRESSION_INCREMENT_DECREMENT: @@ -943,261 +979,293 @@ lower_is_shadowing(struct Lower_Context* ctx, struct String name) return false; } -// turns a source statement into the lowered form. struct Statement* -lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +lower_statement_return(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) { - switch (tree_stmt->kind) { - case TREE_STATEMENT_RETURN: { - union Statement_Value v = { 0 }; - if (tree_stmt->value.return_value.value) - v.return_value.value = lower_expression(ctx, tree_stmt->value.return_value.value); + struct Expression* value = + tree_stmt->value.return_value.value + ? lower_expression(ctx, tree_stmt->value.return_value.value) + : nil; + return ir_make_return(value, tree_stmt->span); +} + +struct Statement* +lower_statement_declaration(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + struct Tree_Bare_Declaration* decl = &tree_stmt->value.declaration.inner; - return statement_new(STATEMENT_RETURN, v, tree_stmt->span); + // TODO: multi-name decls like `var a, b int = ...` need to split into + // N sibling declarations or introduce a temporary for the initializer. + if (array_length(&decl->names) != 1) { + lower_emit_error_c(ctx->unit, tree_stmt->span, "unimplemented: multi-name declarations"); + return nil; } - case TREE_STATEMENT_DECLARATION: { - struct Tree_Bare_Declaration* decl = &tree_stmt->value.declaration.inner; - // multi-name decls (`var a, b int = …`) need either splitting into N - // sibling declarations (no side effect on the initializer) or a - // temporary; both are deferred for now. - if (array_length(&decl->names) != 1) { - lower_emit_error_c( - ctx->unit, tree_stmt->span, "unimplemented: multi-name declarations"); - return nil; - } + struct String name = *array_at(struct String, &decl->names, 0); + 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)); + return nil; + } + struct Type_Ref type = lower_intern_type_ref(ctx, decl->type); + lower_declare_local(ctx, name, type); - struct String name = *array_at(struct String, &decl->names, 0); - 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)); - return nil; - } - struct Type_Ref type = lower_intern_type_ref(ctx, decl->type); - lower_declare_local(ctx, name, type); + struct Expression* initializer = + decl->initializer ? lower_expression(ctx, decl->initializer) : nil; + return ir_make_declaration(name, type, initializer, tree_stmt->span); +} + +struct Statement* +lower_statement_expression(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + struct Tree_Expression* inner = tree_stmt->value.expression.inner; - union Statement_Value v = { 0 }; - v.declaration.name = name; - v.declaration.type = type; - if (decl->initializer) v.declaration.initializer = lower_expression(ctx, decl->initializer); + if (inner && inner->kind == TREE_EXPRESSION_BINARY_OPERATION) { + enum Binary_Operation op = inner->value.binary_operator.operation; + struct Tree_Expression* lhs_tree = inner->value.binary_operator.left_operand; + struct Tree_Expression* rhs_tree = inner->value.binary_operator.right_operand; - return statement_new(STATEMENT_DECLARATION, v, tree_stmt->span); - } - case TREE_STATEMENT_EXPRESSION: { - struct Tree_Expression* inner = tree_stmt->value.expression.inner; - - if (inner && inner->kind == TREE_EXPRESSION_BINARY_OPERATION) { - enum Binary_Operation op = inner->value.binary_operator.operation; - struct Tree_Expression* lhs_tree = inner->value.binary_operator.left_operand; - struct Tree_Expression* rhs_tree = inner->value.binary_operator.right_operand; - - if (op == BINARY_ASSIGN) { - struct Expression* lhs = lower_expression(ctx, lhs_tree); - struct Expression* rhs = lower_expression(ctx, rhs_tree); - return ir_make_assign(lhs, rhs, tree_stmt->span); - } - if (op > BINARY_ASSIGN) { - // compound assigns always synthesize into a basic assignment - // to a simple binary operation. - if (!lhs_tree || lhs_tree->kind != TREE_EXPRESSION_NAME) { - // TODO: implement non-trivial lhs assignments like function - // calls for example. not too common but sometimes necessary. - lower_emit_error_c( - ctx->unit, tree_stmt->span, - "unimplemented: compound assignment with non-trivial lvalue"); - return nil; - } - - enum Binary_Operation simple_op = binary_operation_strip_assign(op); - if (simple_op == BINARY_NONE) { - lower_emit_error_c( - ctx->unit, tree_stmt->span, "unknown compound assignment operator"); - return nil; - } - - struct Expression* binary = ir_make_binary( - simple_op, lower_expression(ctx, lhs_tree), lower_expression(ctx, rhs_tree), - inner->span); - return ir_make_assign(lower_expression(ctx, lhs_tree), binary, tree_stmt->span); - } + if (op == BINARY_ASSIGN) { + struct Expression* lhs = lower_expression(ctx, lhs_tree); + struct Expression* rhs = lower_expression(ctx, rhs_tree); + return ir_make_assign(lhs, rhs, tree_stmt->span); } - if (inner && inner->kind == TREE_EXPRESSION_INCREMENT_DECREMENT) { - // synthesize into assignment and binary operation - struct Tree_Expression_Increment_Decrement* incdec = &inner->value.increment_decrement; - struct Tree_Expression* subject = incdec->subject; - if (!subject || subject->kind != TREE_EXPRESSION_NAME) { - // TODO: handle this too, like compound assignments + if (op > BINARY_ASSIGN) { + // compound assigns always synthesize into a basic assignment + // to a simple binary operation. + if (!lhs_tree || lhs_tree->kind != TREE_EXPRESSION_NAME) { + // TODO: implement non-trivial lhs assignments like function + // calls for example. not too common but sometimes necessary. lower_emit_error_c( ctx->unit, tree_stmt->span, - "unimplemented: increment/decrement on non-trivial lvalue"); + "unimplemented: compound assignment with non-trivial lvalue"); return nil; } - enum Binary_Operation simple_op = - incdec->operation == INCREMENT_DECREMENT_INCREMENT ? BINARY_PLUS : BINARY_MINUS; + enum Binary_Operation simple_op = binary_operation_strip_assign(op); + if (simple_op == BINARY_NONE) { + lower_emit_error_c( + ctx->unit, tree_stmt->span, "unknown compound assignment operator"); + return nil; + } - struct Expression* one = ir_make_integer(1, inner->span); - struct Expression* binary = - ir_make_binary(simple_op, lower_expression(ctx, subject), one, inner->span); - return ir_make_assign(lower_expression(ctx, subject), binary, tree_stmt->span); + struct Expression* binary = ir_make_binary( + simple_op, lower_expression(ctx, lhs_tree), lower_expression(ctx, rhs_tree), + inner->span); + return ir_make_assign(lower_expression(ctx, lhs_tree), binary, tree_stmt->span); + } + } + if (inner && inner->kind == TREE_EXPRESSION_INCREMENT_DECREMENT) { + // synthesize into assignment and binary operation + struct Tree_Expression_Increment_Decrement* incdec = &inner->value.increment_decrement; + struct Tree_Expression* subject = incdec->subject; + if (!subject || subject->kind != TREE_EXPRESSION_NAME) { + // TODO: handle this too, like compound assignments + lower_emit_error_c( + ctx->unit, tree_stmt->span, + "unimplemented: increment/decrement on non-trivial lvalue"); + return nil; } - return ir_make_expression_statement(lower_expression(ctx, inner), tree_stmt->span); + enum Binary_Operation simple_op = + incdec->operation == INCREMENT_DECREMENT_INCREMENT ? BINARY_PLUS : BINARY_MINUS; + + struct Expression* one = ir_make_integer(1, inner->span); + struct Expression* binary = + ir_make_binary(simple_op, lower_expression(ctx, subject), one, inner->span); + return ir_make_assign(lower_expression(ctx, subject), binary, tree_stmt->span); } - case TREE_STATEMENT_BLOCK: { - union Statement_Value v = { 0 }; - v.block.inner = lower_block(ctx, &tree_stmt->value.block.inner); - return statement_new(STATEMENT_BLOCK, v, tree_stmt->span); + return ir_make_expression_statement(lower_expression(ctx, inner), tree_stmt->span); +} + +struct Statement* +lower_statement_block(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + return ir_make_block_statement( + lower_block(ctx, &tree_stmt->value.block.inner), tree_stmt->span); +} + +struct Statement* +lower_statement_conditional(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + struct Tree_Statement_Value_Conditional* cond = &tree_stmt->value.conditional; + Array(struct If_Branch) branches = array_new(struct If_Branch, 8); + + for (uint i = 0; i < cond->condition_count; ++i) { + struct If_Branch branch = { + .condition = + cond->conditions[i].when ? lower_expression(ctx, cond->conditions[i].when) : nil, + .body = lower_block(ctx, &cond->conditions[i].then), + }; + array_push(&branches, &branch); } - case TREE_STATEMENT_CONDITIONAL: { - struct Tree_Statement_Value_Conditional* cond = &tree_stmt->value.conditional; - union Statement_Value v = { 0 }; - v.conditional.branches = array_new(struct If_Branch, 8); - - for (uint i = 0; i < cond->condition_count; ++i) { - struct If_Branch branch = { - .condition = - cond->conditions[i].when - ? lower_expression(ctx, cond->conditions[i].when) - : nil, - .body = lower_block(ctx, &cond->conditions[i].then), - }; - array_push(&v.conditional.branches, &branch); - } - return statement_new(STATEMENT_CONDITIONAL, v, tree_stmt->span); + return ir_make_conditional(branches, tree_stmt->span); +} + +struct Statement* +lower_statement_loop(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + struct Tree_Statement_Value_Loop* loop = &tree_stmt->value.loop; + + switch (loop->style) { + case TREE_STATEMENT_LOOP_STYLE_WHILE: { + struct Expression* cond = lower_expression(ctx, loop->condition); + struct Block* body = lower_block(ctx, &loop->body); + return ir_make_loop(cond, body, tree_stmt->span); } - case TREE_STATEMENT_LOOP: { - struct Tree_Statement_Value_Loop* loop = &tree_stmt->value.loop; - - switch (loop->style) { - case TREE_STATEMENT_LOOP_STYLE_WHILE: { - struct Expression* cond = lower_expression(ctx, loop->condition); - struct Block* body = lower_block(ctx, &loop->body); - return ir_make_loop(cond, body, tree_stmt->span); - } - case TREE_STATEMENT_LOOP_STYLE_ENDLESS: { - // synthesize a `true` literal so the ir always has a real - // condition to emit. - struct Expression* cond = ir_make_bool(true, tree_stmt->span); - struct Block* body = lower_block(ctx, &loop->body); - return ir_make_loop(cond, body, tree_stmt->span); - } - case TREE_STATEMENT_LOOP_STYLE_C: - 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; - - 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; - } + case TREE_STATEMENT_LOOP_STYLE_ENDLESS: { + // synthesize a `true` literal so the ir always has a real + // condition to emit. + struct Expression* cond = ir_make_bool(true, tree_stmt->span); + struct Block* body = lower_block(ctx, &loop->body); + return ir_make_loop(cond, body, tree_stmt->span); + } + case TREE_STATEMENT_LOOP_STYLE_C: + 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; - if (array_length(&decl->names) != 1) { + struct Tree_Expression* init = nil; + struct Tree_Expression* cond_tree = loop->condition; + struct Tree_Expression* iter_tree = loop->iteration; + + 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-loop with multi-name declaration"); + "unimplemented: for-each over non-range collections"); return nil; } - struct String name = *array_at(struct String, &decl->names, 0); + init = decl->initializer->value.binary_operator.left_operand; + // condition and iteration step are synthesized below + } else { + init = decl->initializer; + } - // open outer block scope. - // this stores the iterator variable. - lower_push_scope(ctx, SCOPE_TYPE_BLOCK); + 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); - 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; - } + // 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 declaration - struct Statement* decl_stmt = ir_make_declaration( - name, iter_type, init ? lower_expression(ctx, init) : nil, decl->span); - - // condition and iteration step: for c-style we lower the source - // ones, for for-each over a range we synthesize `i < hi` and - // `i = i + 1`. - struct Expression* cond_expr = nil; - struct Statement* iter_stmt = nil; - if (loop->style == TREE_STATEMENT_LOOP_STYLE_FOR_EACH) { - struct Tree_Expression* hi = decl->initializer->value.binary_operator.right_operand; - // condition: i < hi - cond_expr = ir_make_binary( - BINARY_LESS_THAN, ir_make_name(name, tree_stmt->span), - lower_expression(ctx, hi), tree_stmt->span); - // step: i = i + 1 - iter_stmt = ir_make_assign( - ir_make_name(name, tree_stmt->span), - ir_make_binary( - BINARY_PLUS, ir_make_name(name, tree_stmt->span), - ir_make_integer(1, tree_stmt->span), tree_stmt->span), - tree_stmt->span); - } else { - cond_expr = cond_tree ? lower_expression(ctx, cond_tree) : nil; - if (iter_tree) { - struct Tree_Statement iter_node = { - .kind = TREE_STATEMENT_EXPRESSION, - .value = { .expression = { .inner = iter_tree } }, - .span = iter_tree->span, - }; - iter_stmt = lower_statement(ctx, &iter_node); - } + struct Type_Ref iter_type = lower_intern_type_ref(ctx, decl->type); + lower_declare_local(ctx, name, iter_type); + + // build the iteration variable declaration + struct Statement* decl_stmt = ir_make_declaration( + name, iter_type, init ? lower_expression(ctx, init) : nil, decl->span); + + // condition and iteration step: for c-style we lower the source + // ones, for for-each over a range we synthesize `i < hi` and + // `i = i + 1`. + struct Expression* cond_expr = nil; + struct Statement* iter_stmt = nil; + if (loop->style == TREE_STATEMENT_LOOP_STYLE_FOR_EACH) { + struct Tree_Expression* hi = decl->initializer->value.binary_operator.right_operand; + // condition: i < hi + cond_expr = ir_make_binary( + BINARY_LESS_THAN, ir_make_name(name, tree_stmt->span), lower_expression(ctx, hi), + tree_stmt->span); + // step: i = i + 1 + iter_stmt = ir_make_assign( + ir_make_name(name, tree_stmt->span), + ir_make_binary( + BINARY_PLUS, ir_make_name(name, tree_stmt->span), + ir_make_integer(1, tree_stmt->span), tree_stmt->span), + tree_stmt->span); + } else { + cond_expr = cond_tree ? lower_expression(ctx, cond_tree) : nil; + if (iter_tree) { + struct Tree_Statement iter_node = { + .kind = TREE_STATEMENT_EXPRESSION, + .value = { .expression = { .inner = iter_tree } }, + .span = iter_tree->span, + }; + iter_stmt = lower_statement(ctx, &iter_node); } + } - // assemble the body, appending the iteration step at the end so - // both for-styles share the same `{ decl; while { body; iter; } }` - // shape. - struct Block* body = lower_block(ctx, &loop->body); - if (iter_stmt) array_push(&body->statements, &iter_stmt); + // assemble the body, appending the iteration step at the end so + // both for-styles share the same `{ decl; while { body; iter; } }` + // shape. + struct Block* body = lower_block(ctx, &loop->body); + if (iter_stmt) array_push(&body->statements, &iter_stmt); - struct Statement* while_stmt = ir_make_loop(cond_expr, body, tree_stmt->span); + struct Statement* while_stmt = ir_make_loop(cond_expr, body, 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); + 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); - return ir_make_block_statement(outer, tree_stmt->span); - } - default: - lower_emit_error_c(ctx->unit, tree_stmt->span, "unknown loop style"); - return nil; - } + lower_pop_scope(ctx, SCOPE_TYPE_BLOCK); + return ir_make_block_statement(outer, tree_stmt->span); } + default: + lower_emit_error_c(ctx->unit, tree_stmt->span, "unknown loop style"); + return nil; + } +} + +struct Statement* +lower_statement_break(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + (void)ctx; + return ir_make_break(tree_stmt->span); +} + +struct Statement* +lower_statement_continue(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + (void)ctx; + return ir_make_continue(tree_stmt->span); +} + +// turns a source statement into the lowered form. +struct Statement* +lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt) +{ + switch (tree_stmt->kind) { + case TREE_STATEMENT_RETURN: + return lower_statement_return(ctx, tree_stmt); + case TREE_STATEMENT_DECLARATION: + return lower_statement_declaration(ctx, tree_stmt); + case TREE_STATEMENT_EXPRESSION: + return lower_statement_expression(ctx, tree_stmt); + case TREE_STATEMENT_BLOCK: + return lower_statement_block(ctx, tree_stmt); + case TREE_STATEMENT_CONDITIONAL: + return lower_statement_conditional(ctx, tree_stmt); + case TREE_STATEMENT_LOOP: + return lower_statement_loop(ctx, tree_stmt); case TREE_STATEMENT_BREAK: - return ir_make_break(tree_stmt->span); + return lower_statement_break(ctx, tree_stmt); case TREE_STATEMENT_CONTINUE: - return ir_make_continue(tree_stmt->span); + return lower_statement_continue(ctx, tree_stmt); default: lower_emit_error_c(ctx->unit, tree_stmt->span, "unimplemented: this statement kind"); return nil; |
