Protocol method declarations now declare their receiver explicitly as the first parameter — 'self: *Self' (or 'self: Self') — matching the impl method signature, instead of the old implicit-receiver form where the listed params were only the extra args. That asymmetry repeatedly caused confusion over whether the first param was the receiver or an argument. The parser validates the first param is 'self' typed Self/*Self, then strips it, so all downstream lowering and the dispatch ABI are unchanged (impl blocks and call sites are unaffected). A protocol method missing the receiver is now a parse error. Migrated all 129 protocol method signatures across library + examples (+ one inline-sx test in sema.zig) to the explicit form. Updated specs.md + readme.md. New: examples/0418-protocols-explicit-receiver.sx (feature), examples/1190-diagnostics-protocol-missing-receiver.sx (negative/diagnostic).
1202 lines
43 KiB
Plaintext
1202 lines
43 KiB
Plaintext
#import "modules/std.sx";
|
|
#import "modules/std/mem.sx"; // `Allocator` is non-transitive: name it, import it.
|
|
#import "modules/math";
|
|
#import "modules/build.sx";
|
|
#import "modules/std/test.sx";
|
|
pkg :: #import "tests/fixtures/testpkg";
|
|
|
|
Point :: struct { x, y: i32; }
|
|
|
|
OptNode :: struct {
|
|
value: i32;
|
|
next: ?i32;
|
|
}
|
|
|
|
OptInner :: struct { val: i32; }
|
|
|
|
OptOuter :: struct { inner: ?OptInner; }
|
|
|
|
add :: (a: i32, b: i32) -> i32 { a + b }
|
|
|
|
mul :: (a: i32, b: i32) -> i32 { a * b }
|
|
|
|
identity :: (x: $T) -> T { x }
|
|
|
|
apply :: (f: (i32, i32) -> i32, x: i32, y: i32) -> i32 {
|
|
f(x, y)
|
|
}
|
|
|
|
// P4 edge: Chained default→default calls
|
|
Chained :: protocol {
|
|
base :: (self: *Self, msg: string) -> i32;
|
|
wrap :: (self: *Self, msg: string) -> i32 {
|
|
self.base(msg) + 1
|
|
}
|
|
double_wrap :: (self: *Self, msg: string) -> i32 {
|
|
self.wrap(msg) + self.wrap(msg)
|
|
}
|
|
}
|
|
|
|
main :: () {
|
|
|
|
// --- Tuple Operators ---
|
|
{
|
|
print("=== Tuple Operators ===\n");
|
|
|
|
// Equality
|
|
print("{}\n", (1, 2) == (1, 2)); // true
|
|
print("{}\n", (1, 2) == (1, 3)); // false
|
|
print("{}\n", (1, 2) != (1, 3)); // true
|
|
print("{}\n", (1, 2) != (1, 2)); // false
|
|
|
|
// Concatenation
|
|
c := (1, 2) + (3, 4);
|
|
print("{}\n", c.0); // 1
|
|
print("{}\n", c.1); // 2
|
|
print("{}\n", c.2); // 3
|
|
print("{}\n", c.3); // 4
|
|
|
|
// Repetition
|
|
r := (1, 2) * 3;
|
|
print("{}\n", r.0); // 1
|
|
print("{}\n", r.1); // 2
|
|
print("{}\n", r.2); // 1
|
|
print("{}\n", r.3); // 2
|
|
print("{}\n", r.4); // 1
|
|
print("{}\n", r.5); // 2
|
|
|
|
// Lexicographic comparison
|
|
print("{}\n", (1, 2) < (1, 3)); // true
|
|
print("{}\n", (1, 3) < (1, 2)); // false
|
|
print("{}\n", (1, 2) < (1, 2)); // false
|
|
print("{}\n", (1, 2) <= (1, 2)); // true
|
|
print("{}\n", (2, 0) > (1, 9)); // true
|
|
print("{}\n", (1, 2) >= (1, 2)); // true
|
|
|
|
// Membership
|
|
print("{}\n", 2 in (1, 2, 3)); // true
|
|
print("{}\n", 5 in (1, 2, 3)); // false
|
|
}
|
|
|
|
// --- Directory imports ---
|
|
{
|
|
print("--- directory imports ---\n");
|
|
print("{}\n", pkg.add(3, 4)); // 7
|
|
print("{}\n", pkg.mul(5, 6)); // 30
|
|
print("{}\n", pkg.hello()); // hello from testpkg
|
|
print("{}\n", pkg.cwd_greet()); // cwd-import-ok
|
|
}
|
|
|
|
// --- Pipe operator ---
|
|
{
|
|
print("--- pipe operator ---\n");
|
|
// Basic: a |> f(b) → f(a, b)
|
|
print("{}\n", 3 |> pkg.add(4)); // 7
|
|
print("{}\n", 5 |> pkg.mul(6)); // 30
|
|
|
|
// Chaining: a |> f(b) |> g(c) → g(f(a, b), c)
|
|
print("{}\n", 3 |> pkg.add(4) |> pkg.mul(2)); // 14
|
|
|
|
// With non-namespaced functions
|
|
print("{}\n", "hello" |> concat(" world")); // hello world
|
|
|
|
// Chained string ops
|
|
print("{}\n", "piped" |> concat(" ok") |> concat("!")); // piped ok!
|
|
}
|
|
|
|
// ── alloc_slice ──────────────────────────────────────────
|
|
{
|
|
items := alloc_slice(i64, 5);
|
|
items[0] = 10;
|
|
items[1] = 20;
|
|
items[2] = 30;
|
|
items[3] = 40;
|
|
items[4] = 50;
|
|
print("alloc len: {}\n", items.len); // alloc len: 5
|
|
print("alloc[0]: {}\n", items[0]); // alloc[0]: 10
|
|
print("alloc[4]: {}\n", items[4]); // alloc[4]: 50
|
|
|
|
// alloc_slice with u8
|
|
bytes := alloc_slice(u8, 3);
|
|
bytes[0] = 65;
|
|
bytes[1] = 66;
|
|
bytes[2] = 67;
|
|
print("bytes len: {}\n", bytes.len); // bytes len: 3
|
|
}
|
|
|
|
// ========================================================
|
|
// ALLOCATORS
|
|
// ========================================================
|
|
print("--- allocators ---\n");
|
|
|
|
// ── GPA ─────────────────────────────────────────────────
|
|
{
|
|
gpa := GPA.init();
|
|
a : Allocator = xx gpa;
|
|
p1 := a.alloc_bytes(64);
|
|
p2 := a.alloc_bytes(128);
|
|
print("gpa allocs: {}\n", gpa.alloc_count); // gpa allocs: 2
|
|
a.dealloc_bytes(p1);
|
|
a.dealloc_bytes(p2);
|
|
print("gpa final: {}\n", gpa.alloc_count); // gpa final: 0
|
|
}
|
|
|
|
// ── Arena backed by GPA (multi-chunk) ───────────────────
|
|
{
|
|
gpa3 := GPA.init();
|
|
arena := Arena.init(xx gpa3, 32);
|
|
a : Allocator = xx arena;
|
|
// First chunk fits 80 usable bytes
|
|
a1 := a.alloc_bytes(40);
|
|
a2 := a.alloc_bytes(40);
|
|
// Counts: just the first chunk = 1. Arena.init returns the
|
|
// state by value; the local IS the Arena struct, no parent
|
|
// allocation for the state itself.
|
|
print("arena chunks: {}\n", gpa3.alloc_count); // arena chunks: 1
|
|
// Overflow → new chunk
|
|
a3 := a.alloc_bytes(16);
|
|
print("arena overflow: {}\n", gpa3.alloc_count); // arena overflow: 2
|
|
// Verify memory works across chunks
|
|
p1 : [*]u8 = xx a1;
|
|
p3 : [*]u8 = xx a3;
|
|
p1[0] = 42;
|
|
p3[0] = 99;
|
|
print("arena a1: {}\n", p1[0]); // arena a1: 42
|
|
print("arena a3: {}\n", p3[0]); // arena a3: 99
|
|
// Reset retains the first chunk
|
|
arena.reset();
|
|
print("arena reset idx: {}\n", arena.end_index); // arena reset idx: 0
|
|
print("arena reset gpa: {}\n", gpa3.alloc_count); // arena reset gpa: 1
|
|
// Deinit frees all chunks (caller's local is the state — no
|
|
// dealloc of the struct itself).
|
|
arena.deinit();
|
|
print("arena deinit: {}\n", gpa3.alloc_count); // arena deinit: 0
|
|
}
|
|
|
|
// ── BufAlloc from stack array ───────────────────────────
|
|
{
|
|
stack_buf : [128]u8 = ---;
|
|
buf := BufAlloc.init(@stack_buf[0], 128);
|
|
a : Allocator = xx buf;
|
|
b1 := a.alloc_bytes(24);
|
|
b2 := a.alloc_bytes(24);
|
|
print("buf pos: {}\n", buf.pos); // buf pos: 48
|
|
b3 := a.alloc_bytes(200);
|
|
b3_i : i64 = xx b3;
|
|
print("buf overflow: {}\n", b3_i); // buf overflow: 0
|
|
buf.reset();
|
|
print("buf reset: {}\n", buf.pos); // buf reset: 0
|
|
}
|
|
|
|
{
|
|
if 1 == (1,) {
|
|
print("1 == (1)\n");
|
|
}
|
|
|
|
if (1,) == (1) {
|
|
print("(1) == 1\n");
|
|
}
|
|
|
|
if (1,) == 1 {
|
|
print("1 == 1\n");
|
|
}
|
|
}
|
|
|
|
// ========================================================
|
|
// OPTIONALS
|
|
// ========================================================
|
|
print("--- optionals ---\n");
|
|
|
|
// Basic optional creation and null
|
|
{
|
|
x: ?i32 = 42;
|
|
y: ?i32 = null;
|
|
print("opt x: {}\n", x); // opt x: 42
|
|
print("opt y: {}\n", y); // opt y: null
|
|
}
|
|
|
|
// Force unwrap
|
|
{
|
|
x: ?i32 = 10;
|
|
val := x!;
|
|
print("unwrap: {}\n", val); // unwrap: 10
|
|
}
|
|
|
|
// Null coalescing
|
|
{
|
|
x: ?i32 = 42;
|
|
y: ?i32 = null;
|
|
a := x ?? 0;
|
|
b := y ?? 99;
|
|
print("coalesce a: {}\n", a); // coalesce a: 42
|
|
print("coalesce b: {}\n", b); // coalesce b: 99
|
|
|
|
// Chained ?? (right-associative): a ?? b ?? c
|
|
z: ?i32 = null;
|
|
c := x ?? y ?? 0;
|
|
d := z ?? y ?? 99;
|
|
e := z ?? z ?? 0;
|
|
print("chained ?? c: {}\n", c); // chained ?? c: 42
|
|
print("chained ?? d: {}\n", d); // chained ?? d: 99
|
|
print("chained ?? e: {}\n", e); // chained ?? e: 0
|
|
}
|
|
|
|
// If-binding (safe unwrap)
|
|
{
|
|
x: ?i32 = 7;
|
|
y: ?i32 = null;
|
|
if val := x {
|
|
print("if-bind x: {}\n", val); // if-bind x: 7
|
|
}
|
|
if val := y {
|
|
print("if-bind y: should not print\n");
|
|
} else {
|
|
print("if-bind y: none\n"); // if-bind y: none
|
|
}
|
|
}
|
|
|
|
// Pattern matching on optionals
|
|
{
|
|
check :: (v: ?i32) -> i32 {
|
|
return if v == {
|
|
case .some: (val) { val }
|
|
case .none: { 0 }
|
|
};
|
|
}
|
|
a: ?i32 = 55;
|
|
b: ?i32 = null;
|
|
print("match some: {}\n", check(a)); // match some: 55
|
|
print("match none: {}\n", check(b)); // match none: 0
|
|
}
|
|
|
|
// Optional with implicit wrapping
|
|
{
|
|
opt_wrap :: (n: i32) -> ?i32 {
|
|
if n > 0 {
|
|
return n;
|
|
}
|
|
return null;
|
|
}
|
|
r1 := opt_wrap(5);
|
|
r2 := opt_wrap(0);
|
|
print("wrap pos: {}\n", r1); // wrap pos: 5
|
|
print("wrap neg: {}\n", r2); // wrap neg: null
|
|
}
|
|
|
|
// Struct field defaults for ?T
|
|
{
|
|
n := OptNode.{ value = 10 };
|
|
print("opt field default: {}\n", n.next); // opt field default: null
|
|
m := OptNode.{ value = 20, next = 42 };
|
|
print("opt field set: {}\n", m.next); // opt field set: 42
|
|
}
|
|
|
|
// ?T as function parameter
|
|
{
|
|
opt_process :: (val: ?i32) -> i32 {
|
|
return val ?? 0;
|
|
}
|
|
a: ?i32 = 42;
|
|
b: ?i32 = null;
|
|
print("opt param a: {}\n", opt_process(a)); // opt param a: 42
|
|
print("opt param b: {}\n", opt_process(b)); // opt param b: 0
|
|
print("opt param 7: {}\n", opt_process(7)); // opt param 7: 7
|
|
}
|
|
|
|
// Assignment to optional variable (f32 → ?f32)
|
|
{
|
|
iw: ?f32 = null;
|
|
w: f32 = 42.5;
|
|
iw = w;
|
|
print("opt reassign: {}\n", iw ?? 0.0); // opt reassign: 42.5
|
|
|
|
// Assignment of computed value to optional
|
|
iw2: ?f32 = null;
|
|
a: ?f32 = 10.0;
|
|
if v := a { iw2 = v + 5.0; }
|
|
print("opt compute assign: {}\n", iw2 ?? 0.0); // opt compute assign: 15.0
|
|
|
|
// Re-assign optional back to null
|
|
iw2 = null;
|
|
print("opt re-null: {}\n", iw2 ?? 99.0); // opt re-null: 99.0
|
|
}
|
|
|
|
// Generic function with ?T return
|
|
{
|
|
first_pos :: ($T: Type, a: T, b: T) -> ?T {
|
|
if a > 0 { return a; }
|
|
if b > 0 { return b; }
|
|
return null;
|
|
}
|
|
print("generic opt 1: {}\n", first_pos(i32, 5, 10)); // generic opt 1: 5
|
|
print("generic opt 2: {}\n", first_pos(i32, 0, 7)); // generic opt 2: 7
|
|
print("generic opt 3: {}\n", first_pos(i32, 0, 0)); // generic opt 3: null
|
|
}
|
|
|
|
// Optional chaining (?.)
|
|
{
|
|
p: ?OptNode = OptNode.{ value = 10, next = 20 };
|
|
q: ?OptNode = null;
|
|
print("chain some: {}\n", p?.value ?? 0); // chain some: 10
|
|
print("chain none: {}\n", q?.value ?? 0); // chain none: 0
|
|
print("chain print: {}\n", p?.next); // chain print: 20
|
|
print("chain null: {}\n", q?.next); // chain null: null
|
|
|
|
// Chained: obj.field?.field
|
|
o1 := OptOuter.{ inner = OptInner.{ val = 99 } };
|
|
o2 := OptOuter.{ inner = null };
|
|
print("deep chain 1: {}\n", o1.inner?.val ?? 0); // deep chain 1: 99
|
|
print("deep chain 2: {}\n", o2.inner?.val ?? 0); // deep chain 2: 0
|
|
}
|
|
|
|
// Flow-sensitive narrowing
|
|
{
|
|
x: ?i32 = 42;
|
|
y: ?i32 = null;
|
|
|
|
// if x != null → x is narrowed to i32
|
|
if x != null {
|
|
print("narrow x: {}\n", x); // narrow x: 42
|
|
}
|
|
|
|
// if y != null → not entered
|
|
if y != null {
|
|
print("should not print\n");
|
|
} else {
|
|
print("narrow y else: null\n"); // narrow y else: null
|
|
}
|
|
|
|
// if x == null ... else → else-branch narrowed
|
|
if x == null {
|
|
print("should not print\n");
|
|
} else {
|
|
print("narrow else x: {}\n", x); // narrow else x: 42
|
|
}
|
|
}
|
|
|
|
// Guard narrowing
|
|
{
|
|
guard_fn :: (v: ?i32) -> i32 {
|
|
if v == null { return 0; }
|
|
return v;
|
|
}
|
|
print("guard some: {}\n", guard_fn(42)); // guard some: 42
|
|
print("guard none: {}\n", guard_fn(null)); // guard none: 0
|
|
}
|
|
|
|
// Compound narrowing: && chains
|
|
{
|
|
a: ?i32 = 10;
|
|
b: ?i32 = 20;
|
|
c: ?i32 = null;
|
|
if a != null and b != null {
|
|
print("and both: {} {}\n", a, b); // and both: 10 20
|
|
}
|
|
if a != null and c != null {
|
|
print("should not print\n");
|
|
} else {
|
|
print("and one null\n"); // and one null
|
|
}
|
|
}
|
|
|
|
// Compound guard narrowing: || chains
|
|
{
|
|
guard2 :: (a: ?i32, b: ?i32) -> i32 {
|
|
if a == null or b == null { return 0; }
|
|
return a + b;
|
|
}
|
|
print("or guard: {}\n", guard2(3, 4)); // or guard: 7
|
|
print("or guard null: {}\n", guard2(3, null)); // or guard null: 0
|
|
}
|
|
|
|
// Nested if narrowing
|
|
{
|
|
a: ?i32 = 10;
|
|
b: ?i32 = 20;
|
|
if a != null {
|
|
if b != null {
|
|
print("nested narrow: {} {}\n", a, b); // nested narrow: 10 20
|
|
}
|
|
}
|
|
}
|
|
|
|
// Guard narrowing used in loop
|
|
{
|
|
guard_loop :: (v: ?i32) -> i32 {
|
|
if v == null { return 0; }
|
|
sum := 0;
|
|
i := 0;
|
|
while i < v {
|
|
sum = sum + 1;
|
|
i = i + 1;
|
|
}
|
|
return sum;
|
|
}
|
|
print("guard loop: {}\n", guard_loop(3)); // guard loop: 3
|
|
}
|
|
|
|
// --- block-body lambdas ---
|
|
{
|
|
// block-body lambda with return type
|
|
clamp := (x: i64, lo: i64, hi: i64) -> i64 {
|
|
if x < lo { return lo; }
|
|
if x > hi { return hi; }
|
|
return x;
|
|
};
|
|
print("block-lambda: {}\n", clamp(50, 0, 100)); // block-lambda: 50
|
|
print("block-lambda: {}\n", clamp(-10, 0, 100)); // block-lambda: 0
|
|
print("block-lambda: {}\n", clamp(999, 0, 100)); // block-lambda: 100
|
|
|
|
// block-body lambda without return type annotation
|
|
greet := (name: string) {
|
|
print("hello {}\n", name);
|
|
};
|
|
greet("block"); // hello block
|
|
}
|
|
|
|
// --- named params in function types ---
|
|
{
|
|
// Named params are documentation only — ignored for type identity
|
|
apply_named :: (f: (x: i32, y: i32) -> i32, a: i32, b: i32) -> i32 {
|
|
return f(a, b);
|
|
}
|
|
add :: (a: i32, b: i32) -> i32 { return a + b; }
|
|
print("named-fn-type: {}\n", apply_named(add, 3, 4)); // named-fn-type: 7
|
|
}
|
|
|
|
// --- xx on function pointers ---
|
|
{
|
|
MyEnv :: struct { n: i32; }
|
|
typed_fn :: (e: *MyEnv, x: i32) -> i32 {
|
|
return x + e.n;
|
|
}
|
|
// xx cast: (*MyEnv, i32) -> i32 → (*void, i32) -> i32
|
|
f : (*void, i32) -> i32 = xx typed_fn;
|
|
env := MyEnv.{ n = 100 };
|
|
print("xx-fnptr: {}\n", f(xx @env, 42)); // xx-fnptr: 142
|
|
}
|
|
|
|
// --- closure type: construct and access fields ---
|
|
{
|
|
dummy_fn :: (env: *void, x: i32) -> i32 {
|
|
return x * 2;
|
|
}
|
|
fn_ptr : *void = xx dummy_fn;
|
|
null_env : *void = xx 0;
|
|
c : Closure(i32) -> i32 = .{ fn_ptr = fn_ptr, env = null_env };
|
|
print("closure-type: fn_ptr-nonnull={}\n", c.fn_ptr != null_env);
|
|
print("closure-type: env-null={}\n", c.env == null_env);
|
|
}
|
|
|
|
// --- closure calling convention ---
|
|
{
|
|
Env :: struct { n: i32; }
|
|
impl_fn :: (env: *void, x: i32) -> i32 {
|
|
e : *Env = xx env;
|
|
return x + e.n;
|
|
}
|
|
env := Env.{ n = 5 };
|
|
fn_ptr : *void = xx impl_fn;
|
|
env_ptr : *void = xx @env;
|
|
c : Closure(i32) -> i32 = .{ fn_ptr = fn_ptr, env = env_ptr };
|
|
print("closure-call: {}\n", c(10));
|
|
}
|
|
|
|
// --- auto-promotion: bare fn → Closure ---
|
|
{
|
|
double :: (x: i32) -> i32 { return x * 2; }
|
|
apply :: (f: Closure(i32) -> i32, x: i32) -> i32 { return f(x); }
|
|
print("auto-promote: {}\n", apply(double, 10));
|
|
|
|
// Named function to Closure variable
|
|
f : Closure(i32) -> i32 = double;
|
|
print("auto-promote-var: {}\n", f(5));
|
|
}
|
|
|
|
// --- closure() intrinsic ---
|
|
{
|
|
// capture scalar
|
|
n := 42;
|
|
f := closure((x: i32) => x + n);
|
|
print("closure-capture: {}\n", f(10));
|
|
|
|
// capture by value is a snapshot
|
|
m := 5;
|
|
g := closure((x: i32) => x + m);
|
|
m = 100;
|
|
print("closure-snapshot: {}\n", g(10));
|
|
|
|
// no captures (null env)
|
|
h := closure((x: i32) => x * 2);
|
|
print("closure-nocap: {}\n", h(7));
|
|
|
|
// multiple captures
|
|
a := 10;
|
|
b := 20;
|
|
multi := closure((x: i32) => x + a + b);
|
|
print("closure-multi: {}\n", multi(3));
|
|
|
|
// block-body closure with return
|
|
offset := 50;
|
|
clamp := closure((x: i64) -> i64 {
|
|
if x < 0 { return 0; }
|
|
if x > 100 { return 100; }
|
|
return x + offset;
|
|
});
|
|
r1 : i64 = clamp(10);
|
|
r2 : i64 = clamp(0 - 5);
|
|
r3 : i64 = clamp(999);
|
|
print("closure-block: {}\n", r1);
|
|
print("closure-block: {}\n", r2);
|
|
print("closure-block: {}\n", r3);
|
|
|
|
// void closure
|
|
tag := "LOG";
|
|
logger := closure((msg: string) {
|
|
print("[{}] {}\n", tag, msg);
|
|
});
|
|
logger("hello");
|
|
|
|
// pass closure to higher-order function
|
|
dbl :: (x: i32) -> i32 { return x * 2; }
|
|
apply_cl :: (f2: Closure(i32) -> i32, x: i32) -> i32 { return f2(x); }
|
|
factor : i32 = 3;
|
|
print("closure-hof: {}\n", apply_cl(closure((x: i32) -> i32 => x * factor), 10));
|
|
|
|
// auto-promoted bare fn passed alongside closures
|
|
print("closure-hof-bare: {}\n", apply_cl(dbl, 10));
|
|
|
|
// C5.A2: capture f32
|
|
scale := 2.5;
|
|
f_f32 := closure((x: f32) -> f32 => x * scale);
|
|
print("closure-f32: {}\n", f_f32(4.0));
|
|
|
|
// C5.A3: capture bool
|
|
verbose := true;
|
|
f_bool := closure((msg: string) {
|
|
if verbose { print("closure-bool: {}\n", msg); }
|
|
});
|
|
f_bool("hello");
|
|
|
|
// C5.B3: two params
|
|
base : i32 = 100;
|
|
f_2p := closure((x: i32, y: i32) -> i32 => x + y + base);
|
|
print("closure-2p: {}\n", f_2p(3, 4));
|
|
|
|
// C5.B4: three params
|
|
bias : i32 = 1;
|
|
f_3p := closure((a: i32, b: i32, c2: i32) -> i32 => a + b + c2 + bias);
|
|
print("closure-3p: {}\n", f_3p(10, 20, 30));
|
|
|
|
// C5.B5: mixed param types (string + i32)
|
|
extra : i32 = 5;
|
|
f_mix := closure((name: string, age: i32) {
|
|
print("closure-mix: {} is {}\n", name, age + extra);
|
|
});
|
|
f_mix("Alice", 30);
|
|
|
|
// C5.C3: return bool
|
|
threshold : i32 = 100;
|
|
f_rbool := closure((x: i32) -> bool { return x > threshold; });
|
|
print("closure-rbool: {} {}\n", f_rbool(50), f_rbool(200));
|
|
|
|
// C5.D3: reduce / fold
|
|
reduce :: (arr: []i32, f3: Closure(i32, i32) -> i32, init: i32) -> i32 {
|
|
acc := init;
|
|
i : i64 = 0;
|
|
while i < arr.len { acc = f3(acc, arr[i]); i += 1; }
|
|
return acc;
|
|
}
|
|
r_nums : []i32 = .[1, 2, 3, 4, 5];
|
|
r_bonus : i32 = 100;
|
|
r_total := reduce(r_nums, closure((acc: i32, x: i32) -> i32 => acc + x), r_bonus);
|
|
print("closure-reduce: {}\n", r_total);
|
|
|
|
// C5.G1: factory function
|
|
make_adder :: (n: i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => x + n);
|
|
}
|
|
add5 := make_adder(5);
|
|
add10 := make_adder(10);
|
|
print("closure-factory: {} {}\n", add5(100), add10(100));
|
|
|
|
// C5.A5: capture struct
|
|
origin := Point.{ x = 10, y = 20 };
|
|
f_st := closure(() {
|
|
print("closure-struct: {} {}\n", origin.x, origin.y);
|
|
});
|
|
f_st();
|
|
|
|
// C5.H1: closure captures another closure
|
|
inner_n := 10;
|
|
inner_cl := closure((x: i64) -> i64 => x + inner_n);
|
|
outer_cl := closure((x: i64) -> i64 => inner_cl(x) * 2);
|
|
print("closure-compose: {}\n", outer_cl(5));
|
|
|
|
// C5.M7: multiple closures from same scope capture independently
|
|
shared : i32 = 10;
|
|
cl_a := closure((x: i32) -> i32 => x + shared);
|
|
cl_b := closure((x: i32) -> i32 => x * shared);
|
|
print("closure-indep: {} {}\n", cl_a(5), cl_b(5));
|
|
|
|
// C6: optional closures
|
|
f_none : ?Closure(i64) -> i64 = null;
|
|
if h := f_none {
|
|
print("should not print: {}\n", h(1));
|
|
} else {
|
|
print("opt-closure: none\n");
|
|
}
|
|
|
|
opt_n := 10;
|
|
f_some : ?Closure(i64) -> i64 = closure((x: i64) -> i64 => x + opt_n);
|
|
if h := f_some {
|
|
print("opt-closure: {}\n", h(5));
|
|
} else {
|
|
print("should not print\n");
|
|
}
|
|
|
|
// Struct with optional closure callback
|
|
Btn :: struct { label: string; on_click: ?Closure(i64) -> void; }
|
|
btn_x := 99;
|
|
btn_cl := closure((id: i64) {
|
|
print("opt-closure-btn: {} {}\n", id, btn_x);
|
|
});
|
|
btn1 := Btn.{ label = "OK", on_click = btn_cl };
|
|
btn2 := Btn.{ label = "Cancel", on_click = null };
|
|
if h := btn1.on_click { h(1); }
|
|
if h := btn2.on_click { h(2); } else { print("opt-closure-btn: null\n"); }
|
|
|
|
// C5.A6: capture pointer (shared mutable state)
|
|
count_a6 : i32 = 0;
|
|
p_a6 := @count_a6;
|
|
inc_fn := closure(() { p_a6.* += 1; });
|
|
inc_fn(); inc_fn(); inc_fn();
|
|
print("closure-ptr: {}\n", count_a6);
|
|
|
|
// C5.A9: capture enum value (as i32 tag)
|
|
c_a9 : i32 = 2; // simulate enum tag
|
|
f_a9 := closure(() -> i32 => c_a9);
|
|
print("closure-enum: {}\n", f_a9());
|
|
|
|
// C5.C4: return string
|
|
tag_c4 := "INFO";
|
|
f_c4 := closure((msg: string) -> string => format("[{}] {}", tag_c4, msg));
|
|
print("closure-rstr: {}\n", f_c4("ok"));
|
|
|
|
// C5.C5: return struct
|
|
off_c5 := Point.{ x = 10, y = 20 };
|
|
f_c5 := closure((p: Point) -> Point => Point.{ x = p.x + off_c5.x, y = p.y + off_c5.y });
|
|
res_c5 := f_c5(Point.{ x = 1, y = 2 });
|
|
print("closure-rstruct: {} {}\n", res_c5.x, res_c5.y);
|
|
|
|
// C5.G2: factory with multiple captures
|
|
make_linear :: (m: i32, b: i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => m * x + b);
|
|
}
|
|
lin := make_linear(3, 7);
|
|
print("closure-linear: {}\n", lin(10));
|
|
|
|
// C5.G3: factory returning clamper
|
|
make_clamper :: (lo: i32, hi: i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 {
|
|
if x < lo { return lo; }
|
|
if x > hi { return hi; }
|
|
return x;
|
|
});
|
|
}
|
|
clamp_fn := make_clamper(0, 255);
|
|
cv1 : i32 = xx -10;
|
|
cv2 : i32 = 100;
|
|
cv3 : i32 = 999;
|
|
print("closure-clamp: {} {} {}\n", clamp_fn(cv1), clamp_fn(cv2), clamp_fn(cv3));
|
|
|
|
// C5.H2: compose
|
|
compose :: (f_h2: Closure(i32) -> i32, g_h2: Closure(i32) -> i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => f_h2(g_h2(x)));
|
|
}
|
|
one_h2 : i32 = 1;
|
|
two_h2 : i32 = 2;
|
|
add1_h2 := closure((x: i32) -> i32 => x + one_h2);
|
|
mul2_h2 := closure((x: i32) -> i32 => x * two_h2);
|
|
composed := compose(mul2_h2, add1_h2);
|
|
print("closure-compose2: {}\n", composed(5));
|
|
|
|
// C5.H3: chain of closures
|
|
ch_k1 : i32 = 1;
|
|
ch_k2 : i32 = 2;
|
|
ch_k10 : i32 = 10;
|
|
ch_a := closure((x: i32) -> i32 => x + ch_k1);
|
|
ch_b := closure((x: i32) -> i32 => ch_a(x) * ch_k2);
|
|
ch_c := closure((x: i32) -> i32 => ch_b(x) + ch_k10);
|
|
print("closure-chain: {}\n", ch_c(5));
|
|
|
|
// C5.D1: map
|
|
map_cl :: (arr: [*]i32, cnt: i64, f_map: Closure(i32) -> i32, result: [*]i32) {
|
|
i := 0;
|
|
while i < cnt { result[i] = f_map(arr[i]); i += 1; }
|
|
}
|
|
map_src : [5]i32 = .[1, 2, 3, 4, 5];
|
|
map_dst : [5]i32 = .[0, 0, 0, 0, 0];
|
|
factor_d1 : i32 = 3;
|
|
map_cl(xx @map_src, 5, closure((x: i32) -> i32 => x * factor_d1), xx @map_dst);
|
|
print("closure-map: {} {} {} {} {}\n", map_dst[0], map_dst[1], map_dst[2], map_dst[3], map_dst[4]);
|
|
|
|
// C5.D2: filter
|
|
filter_cl :: (arr: [*]i32, cnt: i64, pred: Closure(i32) -> bool, result: [*]i32) -> i64 {
|
|
j := 0;
|
|
i := 0;
|
|
while i < cnt {
|
|
if pred(arr[i]) { result[j] = arr[i]; j += 1; }
|
|
i += 1;
|
|
}
|
|
return j;
|
|
}
|
|
min_val : i32 = 3;
|
|
filt_dst : [5]i32 = .[0, 0, 0, 0, 0];
|
|
kept := filter_cl(xx @map_src, 5, closure((x: i32) -> bool => x >= min_val), xx @filt_dst);
|
|
print("closure-filter: {} [{} {} {}]\n", kept, filt_dst[0], filt_dst[1], filt_dst[2]);
|
|
|
|
// C5.D4: sort comparator (bubble sort)
|
|
sort_cl :: (arr: [*]i32, cnt: i64, less: Closure(i32, i32) -> bool) {
|
|
i := 0;
|
|
while i < cnt {
|
|
j := 0;
|
|
while j < cnt - 1 - i {
|
|
if less(arr[j + 1], arr[j]) {
|
|
tmp := arr[j];
|
|
arr[j] = arr[j + 1];
|
|
arr[j + 1] = tmp;
|
|
}
|
|
j += 1;
|
|
}
|
|
i += 1;
|
|
}
|
|
}
|
|
sort_arr : [5]i32 = .[5, 3, 1, 4, 2];
|
|
descending := true;
|
|
sort_cl(xx @sort_arr, 5, closure((a: i32, b: i32) -> bool {
|
|
if descending { return a > b; }
|
|
return a < b;
|
|
}));
|
|
print("closure-sort: {} {} {} {} {}\n", sort_arr[0], sort_arr[1], sort_arr[2], sort_arr[3], sort_arr[4]);
|
|
|
|
// C5.D5: for_each with index
|
|
for_each_cl :: (arr: [*]i32, cnt: i64, f_fe: Closure(i32, i64) -> void) {
|
|
i : i64 = 0;
|
|
while i < cnt { f_fe(arr[i], i); i += 1; }
|
|
}
|
|
fe_label := "item";
|
|
fe_arr : [3]i32 = .[10, 20, 30];
|
|
for_each_cl(xx @fe_arr, 3, closure((val: i32, idx: i64) {
|
|
print("closure-fe: {} {}={}\n", fe_label, idx, val);
|
|
}));
|
|
|
|
// C5.D6: find
|
|
find_cl :: (arr: [*]i32, cnt: i64, pred_f: Closure(i32) -> bool) -> i64 {
|
|
i : i64 = 0;
|
|
while i < cnt {
|
|
if pred_f(arr[i]) { return i; }
|
|
i += 1;
|
|
}
|
|
return -1;
|
|
}
|
|
target : i32 = 30;
|
|
found_idx := find_cl(xx @fe_arr, 3, closure((x: i32) -> bool => x == target));
|
|
print("closure-find: {}\n", found_idx);
|
|
|
|
// C5.D7: any
|
|
any_cl :: (arr: [*]i32, cnt: i64, pred_a: Closure(i32) -> bool) -> bool {
|
|
i : i64 = 0;
|
|
while i < cnt {
|
|
if pred_a(arr[i]) { return true; }
|
|
i += 1;
|
|
}
|
|
return false;
|
|
}
|
|
has_big := any_cl(xx @fe_arr, 3, closure((x: i32) -> bool => x > 100));
|
|
has_20 := any_cl(xx @fe_arr, 3, closure((x: i32) -> bool => x == 20));
|
|
print("closure-any: {} {}\n", has_big, has_20);
|
|
|
|
// C5.E4: auto-promotion in struct field assignment
|
|
Widget :: struct { transform: Closure(i32) -> i32; }
|
|
negate_fn :: (x: i32) -> i32 { return 0 - x; }
|
|
w_e4 := Widget.{ transform = negate_fn };
|
|
print("closure-struct-field: {}\n", w_e4.transform(5));
|
|
|
|
// C5.F1: single closure callback in struct
|
|
Button :: struct {
|
|
label: string;
|
|
on_press: Closure(i32) -> void;
|
|
}
|
|
btn_x2 := 99;
|
|
btn_cb := closure((id: i32) {
|
|
print("closure-btn: {} {}\n", id, btn_x2);
|
|
});
|
|
btn3 := Button.{ label = "OK", on_press = btn_cb };
|
|
btn3.on_press(1);
|
|
|
|
// C5.J1: stateful counter via pointer capture
|
|
state_j1 : i32 = 0;
|
|
p_j1 := @state_j1;
|
|
inc_j1 := closure(() -> i32 { p_j1.* += 1; return p_j1.*; });
|
|
print("closure-counter: {} {} {}\n", inc_j1(), inc_j1(), inc_j1());
|
|
|
|
// C5.J2: stateful accumulator
|
|
state_j2 : i32 = 100;
|
|
p_j2 := @state_j2;
|
|
acc_j2 := closure((x: i32) -> i32 { p_j2.* += x; return p_j2.*; });
|
|
print("closure-acc: {} {}\n", acc_j2(5), acc_j2(10));
|
|
|
|
// C5.K2: block-body with local variables and loops
|
|
base_k2 : i32 = 100;
|
|
sum_fn := closure((items: [*]i32, cnt: i64) -> i32 {
|
|
total : i32 = 0;
|
|
i : i64 = 0;
|
|
while i < cnt {
|
|
total += items[i];
|
|
i += 1;
|
|
}
|
|
return total + base_k2;
|
|
});
|
|
k2_arr : [5]i32 = .[1, 2, 3, 4, 5];
|
|
print("closure-loop: {}\n", sum_fn(xx @k2_arr, 5));
|
|
|
|
// C5.M3: reassigning a closure variable
|
|
n_m3 : i32 = 1;
|
|
f_m3 := closure((x: i32) -> i32 => x + n_m3);
|
|
print("closure-reassign: {}\n", f_m3(10));
|
|
m_m3 : i32 = 2;
|
|
f_m3 = closure((x: i32) -> i32 => x * m_m3);
|
|
print("closure-reassign: {}\n", f_m3(10));
|
|
|
|
// C5.M6b: snapshot verified with struct capture
|
|
pt_m6 := Point.{ x = 5, y = 10 };
|
|
f_m6 := closure(() -> i32 => pt_m6.x + pt_m6.y);
|
|
pt_m6 = Point.{ x = 99, y = 99 };
|
|
print("closure-snapstruct: {}\n", f_m6());
|
|
|
|
// C5.M2: closure capturing auto-promoted closure
|
|
double_m2 :: (x: i32) -> i32 { return x * 2; }
|
|
base_m2 : Closure(i32) -> i32 = double_m2;
|
|
n_m2 : i32 = 1;
|
|
f_m2 := closure((x: i32) -> i32 => base_m2(x) + n_m2);
|
|
print("closure-cap-promoted: {}\n", f_m2(5));
|
|
|
|
// C5.M5: immediately invoked closure (via temp var)
|
|
n_m5 : i32 = 5;
|
|
iife := closure((x: i32) -> i32 => x + n_m5);
|
|
result_m5 := iife(10);
|
|
print("closure-iife: {}\n", result_m5);
|
|
|
|
// C5.F2: optional callback (none)
|
|
Toggle :: struct { on_change: ?Closure(bool) -> void; }
|
|
t_f2 := Toggle.{ on_change = null };
|
|
if h := t_f2.on_change { h(true); } else { print("closure-toggle: none\n"); }
|
|
|
|
// C5.F3: optional callback (some)
|
|
t_f3_cb := closure((enabled: bool) { print("closure-toggle: {}\n", enabled); });
|
|
t_f3 := Toggle.{ on_change = t_f3_cb };
|
|
if h := t_f3.on_change { h(true); }
|
|
|
|
// C5.F5: callback receiving caller context
|
|
Panel :: struct {
|
|
title: string;
|
|
on_resize: Closure(string, i32, i32) -> void;
|
|
}
|
|
p_f5_cb := closure((title: string, w: i32, h: i32) {
|
|
print("closure-panel: {} {}x{}\n", title, w, h);
|
|
});
|
|
p_f5 := Panel.{ title = "main", on_resize = p_f5_cb };
|
|
p_f5.on_resize(p_f5.title, 800, 600);
|
|
|
|
// C5.E6: protocol value passed through multiple function calls
|
|
step3 :: (a: Allocator) -> *void { a.alloc_bytes(8) }
|
|
step2 :: (a: Allocator) -> *void { step3(a) }
|
|
step1 :: (a: Allocator) -> *void { step2(a) }
|
|
gpa_e6 := GPA.init();
|
|
a_e6 : Allocator = xx gpa_e6;
|
|
ptr_e6 := step1(a_e6);
|
|
print("closure-chain-call: {}\n", ptr_e6 != null);
|
|
a_e6.dealloc_bytes(ptr_e6);
|
|
|
|
// C5.I1: creating closures in a loop (each captures different value)
|
|
// TEMPORARILY DISABLED — closure-in-loop causes infinite loop (index_gep element size issue?)
|
|
// cl_arr : [5]Closure(i32) -> i32 = ---;
|
|
// i_loop := 0;
|
|
// while i_loop < 5 {
|
|
// val_loop : i32 = xx (i_loop * 10);
|
|
// cl_arr[i_loop] = closure((x: i32) -> i32 => x + val_loop);
|
|
// i_loop += 1;
|
|
// }
|
|
// I2: calling closures from array
|
|
// tmp_cl := cl_arr[0]; print("closure-loop-0: {}\n", tmp_cl(1));
|
|
// tmp_cl = cl_arr[1]; print("closure-loop-1: {}\n", tmp_cl(1));
|
|
// tmp_cl = cl_arr[4]; print("closure-loop-4: {}\n", tmp_cl(1));
|
|
|
|
// C5.M4: closure in conditional expression (via temp var)
|
|
use_fast := true;
|
|
k_fast : i32 = 2;
|
|
k_slow : i32 = 10;
|
|
f_fast := closure((x: i32) -> i32 => x * k_fast);
|
|
f_slow := closure((x: i32) -> i32 => x + k_slow);
|
|
f_cond : Closure(i32) -> i32 = if use_fast then f_fast else f_slow;
|
|
print("closure-cond: {}\n", f_cond(5));
|
|
|
|
// C5.F4: multiple callbacks on one struct
|
|
Form :: struct {
|
|
on_submit: ?Closure() -> void;
|
|
on_cancel: ?Closure() -> void;
|
|
}
|
|
msg_f4 := "submitted";
|
|
sub_cb := closure(() { print("closure-form: {}\n", msg_f4); });
|
|
form_f4 := Form.{ on_submit = sub_cb, on_cancel = null };
|
|
if h := form_f4.on_submit { h(); }
|
|
if h := form_f4.on_cancel { h(); } else { print("closure-form: no cancel\n"); }
|
|
|
|
// C5.L3: auto-promoted closure env is null (no free needed)
|
|
double_l3 :: (x: i32) -> i32 { return x * 2; }
|
|
f_l3 : Closure(i32) -> i32 = double_l3;
|
|
print("closure-null-env: {}\n", f_l3.env == null);
|
|
|
|
// C5.A7: capture slice (fat pointer like string)
|
|
sl_a7 : [3]i32 = .[10, 20, 30];
|
|
ptr_a7 : [*]i32 = xx @sl_a7;
|
|
f_a7 := closure((i: i64) -> i32 => ptr_a7[i]);
|
|
print("closure-slice: {} {} {}\n", f_a7(0), f_a7(1), f_a7(2));
|
|
|
|
// C5.L1: arena bulk free (closures allocated on arena, freed in bulk)
|
|
gpa_l1 := GPA.init();
|
|
arena_l1 := Arena.init(xx gpa_l1, 4096);
|
|
push Context.{ allocator = xx arena_l1 } {
|
|
n_l1 : i32 = 5;
|
|
f_l1 := closure((x: i32) -> i32 => x + n_l1);
|
|
print("closure-arena: {}\n", f_l1(10));
|
|
}
|
|
arena_l1.deinit();
|
|
|
|
// C5.L2: GPA manual free (verify env alloc/dealloc)
|
|
gpa_l2 := GPA.init();
|
|
a_l2 : Allocator = xx gpa_l2;
|
|
n_l2 : i32 = 7;
|
|
result_l2 : i32 = 0;
|
|
push Context.{ allocator = a_l2 } {
|
|
f_l2 := closure((x: i32) -> i32 => x + n_l2);
|
|
result_l2 = f_l2(10);
|
|
a_l2.dealloc_bytes(f_l2.env);
|
|
}
|
|
print("closure-gpa: {} allocs={}\n", result_l2, gpa_l2.alloc_count);
|
|
|
|
// C5.A10: capture optional
|
|
val_a10 : ?i32 = 42;
|
|
f_a10 := closure(() -> i32 {
|
|
if v := val_a10 { return v; }
|
|
return 0;
|
|
});
|
|
print("closure-opt: {}\n", f_a10());
|
|
|
|
// C5.C6: return optional
|
|
limit_c6 : i32 = 100;
|
|
f_c6 := closure((x: i32) -> ?i32 {
|
|
if x > limit_c6 { return null; }
|
|
return x;
|
|
});
|
|
r1_c6 := f_c6(50);
|
|
r2_c6 := f_c6(200);
|
|
if v := r1_c6 { print("closure-ropt: {}\n", v); }
|
|
if v := r2_c6 { print("should-not-print\n"); } else { print("closure-ropt: none\n"); }
|
|
|
|
// C5.M8: array of closures with mixed origins
|
|
double_m8 :: (x: i32) -> i32 { return x * 2; }
|
|
n_m8 : i32 = 10;
|
|
fns_m8 : [3]Closure(i32) -> i32 = ---;
|
|
fns_m8[0] = double_m8; // auto-promoted
|
|
fns_m8[1] = closure((x: i32) -> i32 => x + n_m8); // captured
|
|
fns_m8[2] = closure((x: i32) -> i32 => x * x); // no capture
|
|
tmp_m8 := fns_m8[0]; print("closure-mixed: {}\n", tmp_m8(5));
|
|
tmp_m8 = fns_m8[1]; print("closure-mixed: {}\n", tmp_m8(5));
|
|
tmp_m8 = fns_m8[2]; print("closure-mixed: {}\n", tmp_m8(5));
|
|
|
|
// C5.E1: independent closures from same factory (each has own env)
|
|
mk_e1 :: (n: i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => x * n);
|
|
}
|
|
f1_e1 := mk_e1(2);
|
|
f2_e1 := mk_e1(3);
|
|
f3_e1 := mk_e1(4);
|
|
print("closure-factory-indep: {} {} {}\n", f1_e1(10), f2_e1(10), f3_e1(10));
|
|
|
|
// C5.E2: deep chain — closure capturing closure capturing closure
|
|
v_e2 : i32 = 1;
|
|
k2_e2 : i32 = 2;
|
|
k100_e2 : i32 = 100;
|
|
f0_e2 := closure((x: i32) -> i32 => x + v_e2);
|
|
f1_e2 := closure((x: i32) -> i32 => f0_e2(x) * k2_e2);
|
|
f2_e2 := closure((x: i32) -> i32 => f1_e2(x) + k100_e2);
|
|
print("closure-deep-chain: {}\n", f2_e2(10));
|
|
|
|
// C5.E3: many captures (stress env struct)
|
|
c1_e3 : i32 = 1;
|
|
c2_e3 : i32 = 2;
|
|
c3_e3 : i32 = 3;
|
|
c4_e3 : i32 = 4;
|
|
c5_e3 : i32 = 5;
|
|
c6_e3 : i32 = 6;
|
|
c7_e3 : i32 = 7;
|
|
c8_e3 : i32 = 8;
|
|
big_env := closure(() -> i32 => c1_e3 + c2_e3 + c3_e3 + c4_e3 + c5_e3 + c6_e3 + c7_e3 + c8_e3);
|
|
print("closure-8cap: {}\n", big_env());
|
|
|
|
// C5.E5: closure with many parameters (4 params)
|
|
multi_param := closure((a: i32, b: i32, c: i32, d: i32) -> i32 => a + b + c + d);
|
|
a_e5 : i32 = 1; b_e5 : i32 = 2; c_e5 : i32 = 3; d_e5 : i32 = 4;
|
|
print("closure-4param: {}\n", multi_param(a_e5, b_e5, c_e5, d_e5));
|
|
|
|
// C5.E7: two closures sharing the same captured pointer
|
|
shared : i32 = 0;
|
|
shared_p := @shared;
|
|
inc_shared := closure(() { shared_p.* += 1; });
|
|
add5_shared := closure(() { shared_p.* += 5; });
|
|
inc_shared();
|
|
add5_shared();
|
|
inc_shared();
|
|
print("closure-shared-ptr: {}\n", shared);
|
|
|
|
// C5.E8: closure with f64 arithmetic
|
|
pi_e8 : f64 = 3.14159;
|
|
area_fn := closure((r: f64) -> f64 => pi_e8 * r * r);
|
|
a_e8 := area_fn(10.0);
|
|
print("closure-f64: {}\n", a_e8 > 314.0);
|
|
|
|
// C5.E9: zero-capture closure (env should be null, like auto-promoted)
|
|
no_cap := closure((x: i32) -> i32 => x * x);
|
|
print("closure-zerocap: {} {}\n", no_cap(7), no_cap.env == null);
|
|
|
|
// C5.E10: closure capturing and calling struct method
|
|
pt_e10 := Point.{ x = 3, y = 4 };
|
|
p_e10 := @pt_e10;
|
|
get_xy := closure(() -> i32 => p_e10.x + p_e10.y);
|
|
print("closure-struct-method: {}\n", get_xy());
|
|
|
|
// C5.E11: multiple closures from same factory with different captures
|
|
fns_e11 : [3]Closure(i32) -> i32 = ---;
|
|
i_e11 := 0;
|
|
while i_e11 < 3 {
|
|
multiplier : i32 = xx (i_e11 + 1);
|
|
fns_e11[i_e11] = closure((x: i32) -> i32 => x * multiplier);
|
|
i_e11 += 1;
|
|
}
|
|
t_e11 := fns_e11[0]; print("closure-multi-factory: {}\n", t_e11(10));
|
|
t_e11 = fns_e11[1]; print("closure-multi-factory: {}\n", t_e11(10));
|
|
t_e11 = fns_e11[2]; print("closure-multi-factory: {}\n", t_e11(10));
|
|
|
|
// C5.E12: closure capturing bool
|
|
flag_e12 := true;
|
|
check_fn := closure((x: i32) -> bool {
|
|
if flag_e12 { return x > 0; }
|
|
return x < 0;
|
|
});
|
|
pos_e12 : i32 = 5;
|
|
neg_e12 : i32 = xx -3;
|
|
print("closure-bool-cap: {} {}\n", check_fn(pos_e12), check_fn(neg_e12));
|
|
|
|
// C5.E13: closure as argument to another closure
|
|
apply_fn := closure((f_app: Closure(i32) -> i32, val: i32) -> i32 => f_app(val));
|
|
k_e13 : i32 = 100;
|
|
inner_fn := closure((x: i32) -> i32 => x + k_e13);
|
|
print("closure-as-arg: {}\n", apply_fn(inner_fn, 42));
|
|
|
|
// C5.E14: closure capturing string and formatting
|
|
prefix_e14 := "hello";
|
|
greet_fn := closure((name: string) -> string => format("{} {}", prefix_e14, name));
|
|
print("closure-strfmt: {}\n", greet_fn("world"));
|
|
|
|
// C5.E15: reassigning shared pointer target between closure calls
|
|
val_e15 : i32 = 10;
|
|
p_e15 := @val_e15;
|
|
read_fn := closure(() -> i32 => p_e15.*);
|
|
print("closure-ptr-before: {}\n", read_fn());
|
|
val_e15 = 42;
|
|
print("closure-ptr-after: {}\n", read_fn());
|
|
|
|
// C5.E16: closure returning negative value
|
|
off_e16 : i32 = 100;
|
|
neg_fn := closure((x: i32) -> i32 => x - off_e16);
|
|
val_e16 : i32 = 30;
|
|
print("closure-neg: {}\n", neg_fn(val_e16));
|
|
|
|
// C5.E17: closure with protocol value capture (#inline protocol)
|
|
gpa_e17 := GPA.init();
|
|
a_e17 : Allocator = xx gpa_e17;
|
|
alloc_fn := closure((size: i64) -> *void => a_e17.alloc_bytes(size));
|
|
ptr_e17 := alloc_fn(32);
|
|
print("closure-proto-cap: {}\n", ptr_e17 != null);
|
|
a_e17.dealloc_bytes(ptr_e17);
|
|
|
|
// C5.E18: chained factory — compose two factories
|
|
make_scaler :: (factor: i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => x * factor);
|
|
}
|
|
make_offset :: (off: i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => x + off);
|
|
}
|
|
s_fn := make_scaler(3);
|
|
o_fn := make_offset(7);
|
|
// manually compose: scale then offset
|
|
print("closure-chain-factory: {}\n", o_fn(s_fn(10)));
|
|
|
|
// C5.E19: closure in while loop condition helper
|
|
threshold : i32 = 50;
|
|
above_fn := closure((x: i32) -> bool => x >= threshold);
|
|
vals_e19 : [5]i32 = .[10, 30, 50, 70, 90];
|
|
count_above : i32 = 0;
|
|
idx_e19 : i64 = 0;
|
|
while idx_e19 < 5 {
|
|
if above_fn(vals_e19[idx_e19]) { count_above += 1; }
|
|
idx_e19 += 1;
|
|
}
|
|
print("closure-while-cond: {}\n", count_above);
|
|
|
|
// ---- Inferred closure parameter types ----
|
|
|
|
// CI.1: inferred params from typed variable
|
|
f_ci1 : Closure(i32, i32) -> i32 = closure((a, b) => a + b);
|
|
a_ci1 : i32 = 3;
|
|
b_ci1 : i32 = 4;
|
|
print("closure-infer: {}\n", f_ci1(a_ci1, b_ci1));
|
|
|
|
// CI.2: inferred params from function argument
|
|
apply_ci :: (f: Closure(i32) -> i32, x: i32) -> i32 { return f(x); }
|
|
k_ci : i32 = 10;
|
|
v_ci : i32 = 5;
|
|
print("closure-infer-arg: {}\n", apply_ci(closure((x) => x + k_ci), v_ci));
|
|
|
|
// CI.3: inferred with block body
|
|
h_ci : Closure(i32, i32) -> i32 = closure((a, b) { return a * b; });
|
|
print("closure-infer-block: {}\n", h_ci(a_ci1, b_ci1));
|
|
|
|
// CI.4: inferred with captures
|
|
cap_ci : i32 = 100;
|
|
f_ci4 : Closure(i32) -> i32 = closure((x) => x + cap_ci);
|
|
print("closure-infer-cap: {}\n", f_ci4(v_ci));
|
|
|
|
// CI.5: inferred in factory return
|
|
mk_ci :: (n: i32) -> Closure(i32) -> i32 { return closure((x) => x * n); }
|
|
f_ci5 := mk_ci(7);
|
|
print("closure-infer-factory: {}\n", f_ci5(v_ci));
|
|
|
|
// CI.6: inferred with higher-order (closure taking closure)
|
|
compose_ci :: (f: Closure(i32) -> i32, g: Closure(i32) -> i32) -> Closure(i32) -> i32 {
|
|
return closure((x: i32) -> i32 => f(g(x)));
|
|
}
|
|
one_ci : i32 = 1;
|
|
two_ci : i32 = 2;
|
|
c_ci := compose_ci(closure((x) => x + one_ci), closure((x) => x * two_ci));
|
|
print("closure-infer-compose: {}\n", c_ci(v_ci));
|
|
|
|
// CI.7: inferred void return
|
|
msg_ci := "infer-void";
|
|
cb_ci : Closure(i32) -> void = closure((x) { print("closure-{}: {}\n", msg_ci, x); });
|
|
cb_ci(42);
|
|
}
|
|
}
|