From 2a893246711442dd8407264c05991b44d1d1f66f Mon Sep 17 00:00:00 2001 From: Mel Date: Sun, 24 May 2026 20:14:29 +0200 Subject: Emit IR/C increment/decrement in expression position Signed-off-by: Mel --- boot/ir.c | 21 +++++++++++++ boot/lower.c | 49 +++++++++++++----------------- boot/tests/lower/c_for_loop.cskt | 2 +- boot/tests/lower/while_loop.cskt | 6 ++-- boot/tests/transpile/compound_assigns.cskt | 13 +++++--- boot/tests/transpile/loops.cskt | 4 +-- boot/transpile.c | 21 +++++++++++++ boot/visit/ir.c | 23 ++++++++++++++ 8 files changed, 101 insertions(+), 38 deletions(-) (limited to 'boot') diff --git a/boot/ir.c b/boot/ir.c index d8d8aa2..16811ed 100644 --- a/boot/ir.c +++ b/boot/ir.c @@ -248,6 +248,7 @@ enum Expression_Kind EXPRESSION_SUBSCRIPT, EXPRESSION_CAST, EXPRESSION_CONSTRUCT, + EXPRESSION_INCREMENT_DECREMENT, }; struct Expression_Integer_Literal @@ -343,6 +344,13 @@ struct Expression_Construct Array(struct Construct_Field) fields; }; +struct Expression_Increment_Decrement +{ + bool prefix; + enum Increment_Decrement_Operation operation; + struct Expression* subject; +}; + union Expression_Value { struct Expression_Integer_Literal integer_literal; @@ -358,6 +366,7 @@ union Expression_Value struct Expression_Subscript subscript; struct Expression_Cast cast; struct Expression_Construct construct; + struct Expression_Increment_Decrement increment_decrement; }; struct Expression @@ -682,6 +691,18 @@ ir_make_construct(Type_Id type_id, Array(struct Construct_Field) fields, struct return expression_new(EXPRESSION_CONSTRUCT, v, span); } +struct Expression* +ir_make_increment_decrement( + struct Expression* subject, enum Increment_Decrement_Operation op, bool prefix, + struct Span span) +{ + union Expression_Value v = { 0 }; + v.increment_decrement.subject = subject; + v.increment_decrement.operation = op; + v.increment_decrement.prefix = prefix; + return expression_new(EXPRESSION_INCREMENT_DECREMENT, v, span); +} + struct Type_Ref type_ref_bare(Type_Id type_id) { diff --git a/boot/lower.c b/boot/lower.c index f36a124..696f3e4 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -1203,6 +1203,25 @@ lower_expression_function(struct Lower_Context* ctx, struct Tree_Expression* tre return ir_make_name(name, tree_expr->span); } +struct Expression* +lower_expression_increment_decrement(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) +{ + struct Tree_Expression_Increment_Decrement* incdec = &tree_expr->value.increment_decrement; + struct Tree_Expression* subject = incdec->subject; + if (!subject || subject->kind != TREE_EXPRESSION_NAME) { + lower_push_error( + ctx->unit, + (struct Lower_Error){ + .kind = LOWER_ERROR_UNIMPLEMENTED, + .span = tree_expr->span, + .detail = string_from_static_c_string("increment/decrement on non-trivial lvalue"), + }); + return nil; + } + return ir_make_increment_decrement( + lower_expression(ctx, subject), incdec->operation, incdec->prefix, tree_expr->span); +} + // turns a source expression into the lowered form. struct Expression* lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) @@ -1232,9 +1251,10 @@ lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) return lower_expression_type(ctx, tree_expr); case TREE_EXPRESSION_FUNCTION: return lower_expression_function(ctx, tree_expr); + case TREE_EXPRESSION_INCREMENT_DECREMENT: + return lower_expression_increment_decrement(ctx, tree_expr); case TREE_EXPRESSION_SUBSCRIPT: case TREE_EXPRESSION_MEMBER: - case TREE_EXPRESSION_INCREMENT_DECREMENT: case TREE_EXPRESSION_TRY: case TREE_EXPRESSION_MUST: // TODO: implement these @@ -1403,33 +1423,6 @@ lower_statement_expression(struct Lower_Context* ctx, struct Tree_Statement* tre 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_push_error( - ctx->unit, - (struct Lower_Error){ - .kind = LOWER_ERROR_UNIMPLEMENTED, - .span = tree_stmt->span, - .detail = string_from_static_c_string( - "increment/decrement on non-trivial " - "lvalue"), - }); - return nil; - } - - 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); - } - return ir_make_expression_statement(lower_expression(ctx, inner), tree_stmt->span); } diff --git a/boot/tests/lower/c_for_loop.cskt b/boot/tests/lower/c_for_loop.cskt index 98d78f0..0006ca3 100644 --- a/boot/tests/lower/c_for_loop.cskt +++ b/boot/tests/lower/c_for_loop.cskt @@ -28,7 +28,7 @@ main = fun () { (declaration i (ref int) (initializer (expr 0))) (loop (condition (expr (binary < (expr (name i)) (expr 10)))) (block (declaration x (ref int) (initializer (expr (name i)))) - (assign (expr (name i)) (expr (binary + (expr (name i)) (expr 1)))) + (expression-stmt (expr (postfix-++ (expr (name i))))) )) ) ))) diff --git a/boot/tests/lower/while_loop.cskt b/boot/tests/lower/while_loop.cskt index b191df5..38f8609 100644 --- a/boot/tests/lower/while_loop.cskt +++ b/boot/tests/lower/while_loop.cskt @@ -1,7 +1,7 @@ while loops are 1:1 with the intermediate representation's single loop kind. -compound assignments desugar into a simple pair of a simple -assignment and binary operation. +increment/decrement statements lower to an expression statement +wrapping the dedicated inc/dec ir node. <<< @@ -28,7 +28,7 @@ main = fun () { (function 0 main main (returns (ref void)) (block (declaration i (ref int) (initializer (expr 0))) (loop (condition (expr (binary < (expr (name i)) (expr 10)))) (block - (assign (expr (name i)) (expr (binary + (expr (name i)) (expr 1)))) + (expression-stmt (expr (postfix-++ (expr (name i))))) )) ))) (emission_order 0 1 2 3 4 5 6 7)) diff --git a/boot/tests/transpile/compound_assigns.cskt b/boot/tests/transpile/compound_assigns.cskt index e480f31..607268f 100644 --- a/boot/tests/transpile/compound_assigns.cskt +++ b/boot/tests/transpile/compound_assigns.cskt @@ -1,5 +1,6 @@ -compound assigns are desugared during lowering so the c output -only ever has plain assignments. +compound assigns desugar to plain assignment + binary; increment +and decrement (statement or expression position) keep their own +ir node and emit `++` / `--` directly. <<< @@ -10,6 +11,8 @@ main = fun () { x++ x -= 3 x-- + var y int = x++ + var z int = ++x } >>> @@ -20,8 +23,10 @@ void catskill_main(void) { integer x = 0; x = x + 1; x = x * 2; - x = x + 1; + x++; x = x - 3; - x = x - 1; + x--; + integer y = x++; + integer z = ++x; } #include "runtime.c" diff --git a/boot/tests/transpile/loops.cskt b/boot/tests/transpile/loops.cskt index d6493d9..7121538 100644 --- a/boot/tests/transpile/loops.cskt +++ b/boot/tests/transpile/loops.cskt @@ -23,13 +23,13 @@ void catskill_main(void); void catskill_main(void) { integer i = 0; while (i < 10) { - i = i + 1; + i++; } { integer j = 0; while (j < 5) { integer k = j; - j = j + 1; + j++; } } { diff --git a/boot/transpile.c b/boot/transpile.c index d5a718a..0627bcb 100644 --- a/boot/transpile.c +++ b/boot/transpile.c @@ -505,6 +505,26 @@ transpile_visit_expression_construct(struct Visit* visit, struct Expression* exp TRANSPILE_WRITE("}"); } +void +transpile_visit_expression_increment_decrement(struct Visit* visit, struct Expression* expression) +{ + TRANSPILE_PREAMBLE + + const ascii* op = + expression->value.increment_decrement.operation == INCREMENT_DECREMENT_INCREMENT + ? "++" + : "--"; + if (expression->value.increment_decrement.prefix) { + TRANSPILE_WRITE("%s", op); + transpile_emit_expression( + visit, expression->value.increment_decrement.subject, TRANSPILE_PRECEDENCE_UNARY); + } else { + transpile_emit_expression( + visit, expression->value.increment_decrement.subject, TRANSPILE_PRECEDENCE_POSTFIX); + TRANSPILE_WRITE("%s", op); + } +} + // statements always emit their own indentation and trailing newline. // the dispatcher writes the indentation then routes via normal statement apparatus. void @@ -684,6 +704,7 @@ struct Visit_Table transpile_body_visit_functions = { .visit_expression_subscript = transpile_visit_expression_subscript, .visit_expression_cast = transpile_visit_expression_cast, .visit_expression_construct = transpile_visit_expression_construct, + .visit_expression_increment_decrement = transpile_visit_expression_increment_decrement, }; // walk a lowered translation unit and emit c source into the transpiler's output. diff --git a/boot/visit/ir.c b/boot/visit/ir.c index 6748384..036307f 100644 --- a/boot/visit/ir.c +++ b/boot/visit/ir.c @@ -62,6 +62,7 @@ struct Visit_Table void (*visit_expression_subscript)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_cast)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_construct)(struct Visit* visitor, struct Expression* expr); + void (*visit_expression_increment_decrement)(struct Visit* visitor, struct Expression* expr); void (*visit_block)(struct Visit* visitor, struct Block* block); void (*visit_type_ref)(struct Visit* visitor, struct Type_Ref* ref); @@ -285,6 +286,9 @@ walk_expression(struct Visit* visit, struct Expression* expr) case EXPRESSION_CONSTRUCT: VISIT(visit_expression_construct, expr); break; + case EXPRESSION_INCREMENT_DECREMENT: + VISIT(visit_expression_increment_decrement, expr); + break; case EXPRESSION_NONE: break; default: @@ -352,6 +356,12 @@ walk_expression_construct(struct Visit* visit, struct Expression* expr) VISIT(visit_expression, field->value); } +void +walk_expression_increment_decrement(struct Visit* visit, struct Expression* expr) +{ + VISIT(visit_expression, expr->value.increment_decrement.subject); +} + void walk_block(struct Visit* visit, struct Block* block) { @@ -402,6 +412,7 @@ struct Visit_Table walk_functions = { .visit_expression_subscript = walk_expression_subscript, .visit_expression_cast = walk_expression_cast, .visit_expression_construct = walk_expression_construct, + .visit_expression_increment_decrement = walk_expression_increment_decrement, .visit_block = walk_block, .visit_type_ref = walk_type_ref, @@ -920,6 +931,17 @@ printer_visit_expression_construct(struct Visit* visit, struct Expression* expr) PRINT(")"); } +void +printer_visit_expression_increment_decrement(struct Visit* visit, struct Expression* expr) +{ + PRINTER_PREAMBLE + + PRINT("(%s-%s ", expr->value.increment_decrement.prefix ? "prefix" : "postfix", + increment_decrement_operation_to_string(expr->value.increment_decrement.operation)); + VISIT(visit_expression, expr->value.increment_decrement.subject); + PRINT(")"); +} + void printer_visit_block(struct Visit* visit, struct Block* block) { @@ -1057,6 +1079,7 @@ struct Visit_Table printer_visit_functions = { .visit_expression_subscript = printer_visit_expression_subscript, .visit_expression_cast = printer_visit_expression_cast, .visit_expression_construct = printer_visit_expression_construct, + .visit_expression_increment_decrement = printer_visit_expression_increment_decrement, .visit_block = printer_visit_block, .visit_type_ref = printer_visit_type_ref, -- cgit 1.4.1