about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-28 01:17:32 +0200
committerMel <mel@rnrd.eu>2026-05-28 01:17:32 +0200
commita5b24e3eedc8bd48de99cfb90abb3bececa5d29f (patch)
tree8268e61906adde451f010dfe73241841edbec453
parent351433f3f7dc3f1051a9875402502663fe8a0be8 (diff)
downloadcatskill-a5b24e3eedc8bd48de99cfb90abb3bececa5d29f.tar.zst
catskill-a5b24e3eedc8bd48de99cfb90abb3bececa5d29f.zip
Add tests for function value passing and capturing closure behavior
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--boot/tests/lower/closure_capture.cskt39
-rw-r--r--boot/tests/transpile/closure_capture.cskt37
-rw-r--r--boot/tests/transpile/closure_nested.cskt58
-rw-r--r--boot/tests/transpile/function_as_value.cskt43
4 files changed, 177 insertions, 0 deletions
diff --git a/boot/tests/lower/closure_capture.cskt b/boot/tests/lower/closure_capture.cskt
new file mode 100644
index 0000000..f611326
--- /dev/null
+++ b/boot/tests/lower/closure_capture.cskt
@@ -0,0 +1,39 @@
+a lambda that closes over a local from its enclosing function
+synthesizes both a fat-closure type for the signature and a state
+type holding pointers to each captured local.
+the body's references to any captured names should be recognized.
+
+<<<
+
+main = fun () {
+    var bump int = 10
+    var f fun (i int) int = fun (i int) int {
+        return i + bump
+    }
+    var r int = f(5)
+}
+
+>>>
+
+(unit
+	(types
+		(type 0 int primitive)
+		(type 1 uint primitive)
+		(type 2 bool primitive)
+		(type 3 string primitive)
+		(type 4 float primitive)
+		(type 5 byte primitive)
+		(type 6 ascii primitive)
+		(type 7 void primitive)
+		(type 8 __cat_type_0 synthetic function (returns (ref int)) (param (ref int)) (depends_on 0 0))
+		(type 9 __cat_type_1 synthetic structure (field bump (ref & int))))
+	(functions
+		(function 0 main main (returns (ref void)) (block
+			(declaration bump (ref int) (initializer (expr 10)))
+			(declaration f (ref __cat_type_0) (initializer (expr (fn-ref __cat_lambda_0))))
+			(declaration r (ref int) (initializer (expr (call (expr (name f)) (slot 0 (expr 5))))))
+		))
+		(function 1 __cat_lambda_0 synthetic (returns (ref int)) (param i (ref int)) (block
+			(return (expr (binary + (expr (name i)) (expr (capture bump)))))
+		)))
+	(emission_order 0 1 2 3 4 5 6 7 8 9))
diff --git a/boot/tests/transpile/closure_capture.cskt b/boot/tests/transpile/closure_capture.cskt
new file mode 100644
index 0000000..305c58b
--- /dev/null
+++ b/boot/tests/transpile/closure_capture.cskt
@@ -0,0 +1,37 @@
+a capturing closure transpiles to a fat-pair value (state+function)
+and the captured local is accessed via the state.
+lambda should also cast the generic pointer to the known state type.
+
+<<<
+
+main = fun () {
+    var bump int = 10
+    var f fun (i int) int = fun (i int) int {
+        return i + bump
+    }
+    var r int = f(5)
+}
+
+>>>
+
+#include "core.c"
+struct __cat_type_0 {
+    void* state;
+    integer (*call)(void* state, integer);
+};
+struct __cat_type_1;
+struct __cat_type_1 {
+    integer* bump;
+};
+void catskill_main(void);
+integer __cat_lambda_0(void* state_void, integer i);
+void catskill_main(void) {
+    integer bump = 10;
+    struct __cat_type_0 f = (struct __cat_type_0){ .state = &(struct __cat_type_1){ .bump = &bump }, .call = __cat_lambda_0 };
+    integer r = f.call(f.state, 5);
+}
+integer __cat_lambda_0(void* state_void, integer i) {
+    struct __cat_type_1* state = state_void;
+    return i + (*state->bump);
+}
+#include "runtime.c"
diff --git a/boot/tests/transpile/closure_nested.cskt b/boot/tests/transpile/closure_nested.cskt
new file mode 100644
index 0000000..ac913b7
--- /dev/null
+++ b/boot/tests/transpile/closure_nested.cskt
@@ -0,0 +1,58 @@
+support for multi-level closures, and capturing through them.
+when a closure captures a local which is multiple function levels
+away from it, the capture should cascade to every single closure
+between the capturer and the capturee.
+when the closure state is created, the captured local needs
+to be accessed via the state structure of the parent closure,
+and not via a simple reference as usual.
+
+<<<
+
+main = fun () {
+    var a int = 1
+    var outer fun (x int) int = fun (x int) int {
+        var b int = 10
+        var inner fun (y int) int = fun (y int) int {
+            return a + b + x + y
+        }
+        return inner(100)
+    }
+    var r int = outer(1000)
+}
+
+>>>
+
+#include "core.c"
+struct __cat_type_0 {
+    void* state;
+    integer (*call)(void* state, integer);
+};
+struct __cat_type_1;
+struct __cat_type_2;
+struct __cat_type_1 {
+    integer* a;
+    integer* b;
+    integer* x;
+};
+struct __cat_type_2 {
+    integer* a;
+};
+void catskill_main(void);
+integer __cat_lambda_0(void* state_void, integer x);
+integer __cat_lambda_1(void* state_void, integer y);
+void catskill_main(void) {
+    integer a = 1;
+    struct __cat_type_0 outer = (struct __cat_type_0){ .state = &(struct __cat_type_2){ .a = &a }, .call = __cat_lambda_0 };
+    integer r = outer.call(outer.state, 1000);
+}
+integer __cat_lambda_0(void* state_void, integer x) {
+    struct __cat_type_2* state = state_void;
+    integer b = 10;
+    struct __cat_type_0 inner = (struct __cat_type_0){ .state = &(struct __cat_type_1){ .a = state->a, .b = &b, .x = &x }, .call = __cat_lambda_1 };
+    return inner.call(inner.state, 100);
+}
+integer __cat_lambda_1(void* state_void, integer y) {
+    struct __cat_type_1* state = state_void;
+    return (*state->a) + (*state->b) + (*state->x) + y;
+}
+#include "runtime.c"
diff --git a/boot/tests/transpile/function_as_value.cskt b/boot/tests/transpile/function_as_value.cskt
new file mode 100644
index 0000000..864eae2
--- /dev/null
+++ b/boot/tests/transpile/function_as_value.cskt
@@ -0,0 +1,43 @@
+any function can be passed around as a normal value.
+this requires us to create a wrapper around the function, which
+converts the thin (normal) calling convention of the function into
+the fat calling convention of a function containing some state.
+this wrapper is called a "thunk".
+
+<<<
+
+add = fun (a int, b int) int { return a + b }
+
+apply = fun (f fun (a int, b int) int, x int, y int) int {
+    return f(x, y)
+}
+
+main = fun () {
+    var r int = apply(add, 5, 7)
+}
+
+>>>
+
+#include "core.c"
+struct __cat_type_0 {
+    void* state;
+    integer (*call)(void* state, integer, integer);
+};
+integer add(integer a, integer b);
+integer __cat_thunk_add(void* state, integer, integer);
+integer apply(struct __cat_type_0 f, integer x, integer y);
+void catskill_main(void);
+integer add(integer a, integer b) {
+    return a + b;
+}
+integer __cat_thunk_add(void* state, integer a, integer b) {
+    (void)state;
+    return add(a, b);
+}
+integer apply(struct __cat_type_0 f, integer x, integer y) {
+    return f.call(f.state, x, y);
+}
+void catskill_main(void) {
+    integer r = apply((struct __cat_type_0){ .state = nil, .call = __cat_thunk_add }, 5, 7);
+}
+#include "runtime.c"