about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-24 20:14:29 +0200
committerMel <mel@rnrd.eu>2026-05-24 20:14:29 +0200
commit2a893246711442dd8407264c05991b44d1d1f66f (patch)
treea0d4a621994ebb259331b7082a977e4ae2861bde /boot
parentf028bc7ef3101facfa004c21f3aa5feb3dc6f107 (diff)
downloadcatskill-2a893246711442dd8407264c05991b44d1d1f66f.tar.zst
catskill-2a893246711442dd8407264c05991b44d1d1f66f.zip
Emit IR/C increment/decrement in expression position
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
-rw-r--r--boot/ir.c21
-rw-r--r--boot/lower.c49
-rw-r--r--boot/tests/lower/c_for_loop.cskt2
-rw-r--r--boot/tests/lower/while_loop.cskt6
-rw-r--r--boot/tests/transpile/compound_assigns.cskt13
-rw-r--r--boot/tests/transpile/loops.cskt4
-rw-r--r--boot/transpile.c21
-rw-r--r--boot/visit/ir.c23
8 files changed, 101 insertions, 38 deletions
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:
@@ -353,6 +357,12 @@ walk_expression_construct(struct Visit* visit, struct Expression* expr)
 }
 
 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)
 {
     FOR_EACH_ARRAY (struct Statement*, statement, &block->statements)
@@ -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,
@@ -921,6 +932,17 @@ printer_visit_expression_construct(struct Visit* visit, struct Expression* expr)
 }
 
 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)
 {
     PRINTER_PREAMBLE
@@ -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,