diff options
| author | Mel <mel@rnrd.eu> | 2026-05-28 01:17:32 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-05-28 01:17:32 +0200 |
| commit | a5b24e3eedc8bd48de99cfb90abb3bececa5d29f (patch) | |
| tree | 8268e61906adde451f010dfe73241841edbec453 | |
| parent | 351433f3f7dc3f1051a9875402502663fe8a0be8 (diff) | |
| download | catskill-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.cskt | 39 | ||||
| -rw-r--r-- | boot/tests/transpile/closure_capture.cskt | 37 | ||||
| -rw-r--r-- | boot/tests/transpile/closure_nested.cskt | 58 | ||||
| -rw-r--r-- | boot/tests/transpile/function_as_value.cskt | 43 |
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" |
