aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-19 21:25:20 +0200
committerMel <mel@rnrd.eu>2026-05-19 21:25:20 +0200
commit8c4c219c945dfb41415e04654f488f7880e58ca2 (patch)
tree7f6d179701fc0e0aa1f30d752bdb6d4af41aa44d /boot
parent0e4ce87b90b28cdf044a6a1dd8ce1690813faf92 (diff)
downloadcatskill-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.c752
1 files changed, 410 insertions, 342 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);
+}
- return expression_new(EXPRESSION_NAME, v, tree_expr->span);
- }
- 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);
+// 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);
+}
- return expression_new(EXPRESSION_UNARY_OPERATION, v, tree_expr->span);
+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);
+}
+
+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_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");
+ if (op == BINARY_RANGE) {
+ lower_emit_error_c(
+ ctx->unit, tree_expr->span, "range expressions are only valid in loop initializers");
+ return nil;
+ }
+ 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);
+// 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;
+}
- 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;
- }
+// 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");
- 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");
+ // 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);
-
- return statement_new(STATEMENT_RETURN, v, tree_stmt->span);
- }
- 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 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 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 Statement*
+lower_statement_declaration(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt)
+{
+ struct Tree_Bare_Declaration* decl = &tree_stmt->value.declaration.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);
+ // 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;
+ }
- return statement_new(STATEMENT_DECLARATION, v, tree_stmt->span);
+ 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;
}
- case TREE_STATEMENT_EXPRESSION: {
- struct Tree_Expression* inner = tree_stmt->value.expression.inner;
+ struct Type_Ref type = lower_intern_type_ref(ctx, decl->type);
+ lower_declare_local(ctx, name, type);
- 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;
+ struct Expression* initializer =
+ decl->initializer ? lower_expression(ctx, decl->initializer) : nil;
+ return ir_make_declaration(name, type, initializer, 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 (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;
- }
+struct Statement*
+lower_statement_expression(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt)
+{
+ struct Tree_Expression* inner = tree_stmt->value.expression.inner;
- 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;
- }
+ 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;
- 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);
}
-
- return ir_make_expression_statement(lower_expression(ctx, inner), tree_stmt->span);
}
- case TREE_STATEMENT_BLOCK: {
- union Statement_Value v = { 0 };
- v.block.inner = lower_block(ctx, &tree_stmt->value.block.inner);
+ 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;
+ }
+
+ enum Binary_Operation simple_op =
+ incdec->operation == INCREMENT_DECREMENT_INCREMENT ? BINARY_PLUS : BINARY_MINUS;
- return statement_new(STATEMENT_BLOCK, v, tree_stmt->span);
+ 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_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 ir_make_expression_statement(lower_expression(ctx, inner), tree_stmt->span);
+}
- return statement_new(STATEMENT_CONDITIONAL, v, 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_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.
+ 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;
- struct Tree_Bare_Declaration* decl = &loop->declaration;
+ 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_Expression* init = nil;
- struct Tree_Expression* cond_tree = loop->condition;
- struct Tree_Expression* iter_tree = loop->iteration;
+ struct Tree_Bare_Declaration* decl = &loop->declaration;
- 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;
- }
+ struct Tree_Expression* init = nil;
+ struct Tree_Expression* cond_tree = loop->condition;
+ struct Tree_Expression* iter_tree = loop->iteration;
- if (array_length(&decl->names) != 1) {
+ 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);
+ 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);
+ // 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);
- }
+ // 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;