Stdlib: - `format` / `print` in std.sx — both move from `args: ..Any` to `..args: []Any`. The post-issue-0049 lowering makes this safe across module boundaries. - `open` in fs.sx — `args: ..s32` → `..args: []s32`. Foreign C-variadic semantics are preserved (the trailing `, ...` lands in the generated `declare` regardless of which surface form is used). Examples: - `19-varargs.sx` — `sum` / `print_all` migrated. - `20-any-varargs.sx` — `print_any` / `count` migrated. - `50-smoke.sx` — `typed_sum` migrated. - `120-interp-variadic-any.sx` — comment-only update referencing the new form. - `ffi-foreign-cvariadic.sx` — three C-variadic foreign decls migrated; header comment refreshed. Suite stays at 214/214. The legacy `name: ..T` surface form is still accepted by the parser; rejection follows in a later commit once specs.md catches up.
3215 lines
93 KiB
Plaintext
3215 lines
93 KiB
Plaintext
#import "modules/std.sx";
|
|
#import "modules/math/math.sx";
|
|
#import "modules/compiler.sx";
|
|
#import "modules/test.sx";
|
|
pkg :: #import "modules/testpkg";
|
|
|
|
// ============================================================
|
|
// Comprehensive Smoke Test — exercises every spec feature
|
|
// ============================================================
|
|
|
|
// --- Top-level type declarations ---
|
|
|
|
Point :: struct { x, y: s32; }
|
|
|
|
Color :: enum { red; green; blue; }
|
|
|
|
Shape :: enum {
|
|
circle: f32;
|
|
rect: struct { w, h: f32; };
|
|
none;
|
|
}
|
|
|
|
Overlay :: union {
|
|
f: f32;
|
|
i: s32;
|
|
}
|
|
|
|
Vec2 :: union {
|
|
data: [2]f32;
|
|
struct { x, y: f32; };
|
|
}
|
|
|
|
Defaults :: struct {
|
|
a: s32;
|
|
b: s32 = 99;
|
|
c: s32 = ---;
|
|
}
|
|
|
|
OptNode :: struct {
|
|
value: s32;
|
|
next: ?s32;
|
|
}
|
|
|
|
OptInner :: struct { val: s32; }
|
|
OptOuter :: struct { inner: ?OptInner; }
|
|
|
|
MyFloat :: f64;
|
|
|
|
Perms :: enum flags { read; write; execute; }
|
|
|
|
Status :: enum u8 { ok; err; timeout; }
|
|
|
|
WindowFlags :: enum flags u32 { vsync :: 64; resizable :: 4; hidden :: 128; }
|
|
|
|
// --- Top-level functions ---
|
|
|
|
add :: (a: s32, b: s32) -> s32 { a + b; }
|
|
mul :: (a: s32, b: s32) -> s32 { a * b; }
|
|
|
|
identity :: (x: $T) -> T { x; }
|
|
|
|
pair_add :: (a: $T, b: $U) -> s64 {
|
|
cast(s64) a + cast(s64) b;
|
|
}
|
|
|
|
typed_sum :: (..args: []s32) -> s32 {
|
|
result := 0;
|
|
for args: (it) { result = result + it; }
|
|
result;
|
|
}
|
|
|
|
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
|
|
f(x, y);
|
|
}
|
|
|
|
void_return :: () {
|
|
return;
|
|
}
|
|
|
|
implicit_return :: (x: s32) -> s32 {
|
|
x * 2;
|
|
}
|
|
|
|
early_return :: (x: s32) -> s32 {
|
|
if x > 10 { return 99; }
|
|
x;
|
|
}
|
|
|
|
vec3 :: (x: f32, y: f32, z: f32) -> Vector(3, f32) {
|
|
.[x, y, z];
|
|
}
|
|
|
|
point_sum :: (p: Point) -> s32 { p.x + p.y; }
|
|
|
|
// #run compile-time constants
|
|
CT_VAL :: #run add(10, 15);
|
|
CT_MUL :: #run mul(6, 7);
|
|
CT_CHAIN :: #run add(CT_VAL, 5);
|
|
|
|
// #run compile-time optional tests
|
|
ct_opt_coalesce :: () -> s32 {
|
|
x: ?s32 = 42;
|
|
y: ?s32 = null;
|
|
return (x ?? 0) + (y ?? 99);
|
|
}
|
|
ct_opt_unwrap :: () -> s32 {
|
|
x: ?s32 = 77;
|
|
return x!;
|
|
}
|
|
ct_opt_guard :: () -> s32 {
|
|
x: ?s32 = 10;
|
|
if x == null { return -1; }
|
|
return x;
|
|
}
|
|
CT_OPT_COALESCE :: #run ct_opt_coalesce();
|
|
CT_OPT_UNWRAP :: #run ct_opt_unwrap();
|
|
CT_OPT_GUARD :: #run ct_opt_guard();
|
|
|
|
// #insert helpers
|
|
gen_code :: () -> string {
|
|
return "print(\"insert-ok\\n\");";
|
|
}
|
|
gen_val :: () -> string {
|
|
return "print(\"insert-gen: {}\\n\", 42);";
|
|
}
|
|
|
|
// --- Foreign function binding ---
|
|
libc :: #library "c";
|
|
c_abs :: (n: s32) -> s32 #foreign libc "abs";
|
|
|
|
// --- Protocol declarations (Phase 1: static dispatch only) ---
|
|
|
|
Counter :: protocol {
|
|
inc :: ();
|
|
get :: () -> s32;
|
|
}
|
|
|
|
Summable :: protocol {
|
|
sum :: () -> s32;
|
|
}
|
|
|
|
SimpleCounter :: struct { val: s32; }
|
|
|
|
impl Counter for SimpleCounter {
|
|
inc :: (self: *SimpleCounter) { self.val += 1; }
|
|
get :: (self: *SimpleCounter) -> s32 { self.val; }
|
|
}
|
|
|
|
impl Summable for Point {
|
|
sum :: (self: *Point) -> s32 { self.x + self.y; }
|
|
}
|
|
|
|
// Phase 2: #inline protocol for dynamic dispatch
|
|
Adder :: protocol #inline {
|
|
add :: (n: s32);
|
|
value :: () -> s32;
|
|
}
|
|
|
|
Accumulator :: struct {
|
|
total: s32;
|
|
}
|
|
|
|
impl Adder for Accumulator {
|
|
add :: (self: *Accumulator, n: s32) { self.total += n; }
|
|
value :: (self: *Accumulator) -> s32 { self.total; }
|
|
}
|
|
|
|
Doubler :: struct { val: s32; }
|
|
|
|
impl Adder for Doubler {
|
|
add :: (self: *Doubler, n: s32) { self.val = self.val + n + n; }
|
|
value :: (self: *Doubler) -> s32 { self.val; }
|
|
}
|
|
|
|
// Phase 4: default methods
|
|
Repeater :: protocol {
|
|
say :: (msg: string);
|
|
say_twice :: (msg: string) {
|
|
self.say(msg);
|
|
self.say(msg);
|
|
}
|
|
}
|
|
|
|
Printer :: struct { count: s32; }
|
|
|
|
impl Repeater for Printer {
|
|
say :: (self: *Printer, msg: string) {
|
|
self.count += 1;
|
|
out(msg);
|
|
}
|
|
}
|
|
|
|
// P4 edge: Chained default→default calls
|
|
Chained :: protocol {
|
|
base :: (msg: string) -> s32;
|
|
wrap :: (msg: string) -> s32 {
|
|
self.base(msg) + 1;
|
|
}
|
|
double_wrap :: (msg: string) -> s32 {
|
|
self.wrap(msg) + self.wrap(msg);
|
|
}
|
|
}
|
|
|
|
ChainImpl :: struct { val: s32; }
|
|
impl Chained for ChainImpl {
|
|
base :: (self: *ChainImpl, msg: string) -> s32 {
|
|
self.val += 1;
|
|
msg.len;
|
|
}
|
|
}
|
|
|
|
// Phase 5: Self type
|
|
Eq :: protocol {
|
|
eq :: (other: Self) -> bool;
|
|
}
|
|
|
|
impl Eq for Point {
|
|
eq :: (self: *Point, other: Point) -> bool {
|
|
self.x == other.x and self.y == other.y;
|
|
}
|
|
}
|
|
|
|
Cloneable :: protocol {
|
|
clone :: () -> Self;
|
|
}
|
|
|
|
impl Cloneable for Point {
|
|
clone :: (self: *Point) -> Point {
|
|
Point.{ x = self.x, y = self.y };
|
|
}
|
|
}
|
|
|
|
impl Eq for s64 {
|
|
eq :: (self: *s64, other: s64) -> bool {
|
|
self.* == other;
|
|
}
|
|
}
|
|
|
|
// Phase 6: Generic constraints
|
|
are_equal :: ($T: Type/Eq, a: T, b: T) -> bool {
|
|
a.eq(b);
|
|
}
|
|
|
|
Hashable :: protocol {
|
|
hash :: () -> s64;
|
|
}
|
|
|
|
impl Hashable for Point {
|
|
hash :: (self: *Point) -> s64 {
|
|
xx self.x * 31 + xx self.y;
|
|
}
|
|
}
|
|
|
|
eq_and_hash :: ($T: Type/Eq/Hashable, a: T, b: T) -> bool {
|
|
if a.hash() != b.hash() { return false; }
|
|
a.eq(b);
|
|
}
|
|
|
|
// P6.4: inline constraint syntax ($T/Protocol)
|
|
sum_of_inline :: (a: $T/Summable, b: T) -> s32 {
|
|
a.sum() + b.sum();
|
|
}
|
|
|
|
// Phase 7: Generic struct impls
|
|
Pair :: struct ($T: Type) {
|
|
a: T;
|
|
b: T;
|
|
}
|
|
|
|
impl Summable for Pair($T) {
|
|
sum :: (self: *Pair(T)) -> s32 {
|
|
xx self.a + xx self.b;
|
|
}
|
|
}
|
|
|
|
// P6.5: Struct type param constraints
|
|
SumBox :: struct ($T: Type/Summable) {
|
|
val: T;
|
|
}
|
|
|
|
// ============================================================
|
|
// Struct constants test
|
|
Phys :: struct {
|
|
x, y: f32;
|
|
GRAVITY :f32: 9.81;
|
|
MAX_SPEED :: 100;
|
|
}
|
|
|
|
// Init block test struct
|
|
Builder :: struct {
|
|
total: s32;
|
|
count: s32;
|
|
|
|
add :: (self: *Builder, val: s32) {
|
|
self.total += val;
|
|
self.count += 1;
|
|
}
|
|
}
|
|
|
|
// Global variable for address-of test
|
|
g_smoke_val : s32 = 42;
|
|
|
|
write_to_ptr :: (p: *s32) {
|
|
p.* = 99;
|
|
}
|
|
|
|
main :: () {
|
|
|
|
// ========================================================
|
|
// 1. LITERALS
|
|
// ========================================================
|
|
print("=== 1. Literals ===\n");
|
|
|
|
// Integer literals
|
|
print("decimal: {}\n", 42);
|
|
print("hex: {}\n", 0xFF);
|
|
print("binary: {}\n", 0b1010);
|
|
|
|
// Float literal
|
|
pi := 3.14;
|
|
print("float: {}\n", pi);
|
|
|
|
// Explicit f64
|
|
big : f64 = 2.718281828;
|
|
print("f64: {}\n", big);
|
|
|
|
// Boolean literals
|
|
print("true: {}\n", true);
|
|
print("false: {}\n", false);
|
|
|
|
// String with escapes
|
|
print("escapes: hello\tworld\n");
|
|
|
|
// Multi-line string
|
|
ml := "line1
|
|
line2";
|
|
print("multiline: {}\n", ml);
|
|
|
|
// Heredoc string
|
|
hd := #string END
|
|
raw heredoc
|
|
END;
|
|
print("heredoc: {}\n", hd);
|
|
|
|
// Undefined with type
|
|
undef_val : s32 = ---;
|
|
undef_val = 77;
|
|
print("undef-then-set: {}\n", undef_val);
|
|
|
|
// Enum literal (context-inferred)
|
|
c : Color = .green;
|
|
print("enum-lit: {}\n", c);
|
|
|
|
// Null pointer
|
|
np : *s32 = null;
|
|
print("null-ptr: {}\n", np);
|
|
|
|
// String .len
|
|
slen := "hello";
|
|
print("string-len: {}\n", slen.len);
|
|
|
|
// Empty string .len
|
|
es := "";
|
|
print("empty-string: {}\n", es.len);
|
|
|
|
// ========================================================
|
|
// 2. OPERATORS & PRECEDENCE
|
|
// ========================================================
|
|
print("=== 2. Operators ===\n");
|
|
|
|
// Arithmetic
|
|
print("add: {}\n", 3 + 4);
|
|
print("sub: {}\n", 10 - 3);
|
|
print("mul: {}\n", 6 * 7);
|
|
print("div: {}\n", 20 / 4);
|
|
print("mod: {}\n", 17 % 5);
|
|
print("neg: {}\n", -(5));
|
|
|
|
// Comparisons
|
|
print("eq: {}\n", 5 == 5);
|
|
print("neq: {}\n", 5 != 3);
|
|
print("lt: {}\n", 3 < 5);
|
|
print("gt: {}\n", 5 > 3);
|
|
print("le: {}\n", 5 <= 5);
|
|
print("ge: {}\n", 5 >= 3);
|
|
|
|
// Chained comparisons
|
|
v := 50;
|
|
print("chain: {}\n", 0 <= v <= 100);
|
|
print("chain-gt: {}\n", 100 > v > 0);
|
|
print("chain-mixed: {}\n", 100 > v >= 0);
|
|
|
|
// Equality chains
|
|
print("eq-chain: {}\n", 5 == 5 == 5);
|
|
print("eq-chain-f: {}\n", 5 == 5 == 6);
|
|
|
|
// Bitwise
|
|
print("band: {}\n", 0xFF & 0x0F);
|
|
print("bor: {}\n", 1 | 2 | 4);
|
|
|
|
// Bitwise XOR
|
|
print("bxor: {}\n", 0xFF ^ 0x0F);
|
|
print("bxor2: {}\n", 6 ^ 3);
|
|
|
|
// Bitwise NOT
|
|
print("bnot: {}\n", ~0);
|
|
print("bnot2: {}\n", ~1);
|
|
|
|
// Shifts
|
|
print("shl: {}\n", 1 << 4);
|
|
print("shr: {}\n", 256 >> 4);
|
|
print("shl2: {}\n", 3 << 3);
|
|
print("shr2: {}\n", 255 >> 1);
|
|
|
|
// Bitwise on variables
|
|
bv1 := 0xFF;
|
|
bv2 := 0x0F;
|
|
print("band-var: {}\n", bv1 & bv2);
|
|
bv3 := 1;
|
|
bv4 := 6;
|
|
print("bor-var: {}\n", bv3 | bv4);
|
|
print("bxor-var: {}\n", bv1 ^ bv2);
|
|
print("shl-var: {}\n", bv3 << 4);
|
|
print("shr-var: {}\n", bv1 >> 4);
|
|
print("bnot-var: {}\n", ~bv2);
|
|
|
|
// Bitwise compound assignment
|
|
bca := 0xFF;
|
|
bca &= 0x0F;
|
|
print("and-assign: {}\n", bca);
|
|
bco := 0x0F;
|
|
bco |= 0xF0;
|
|
print("or-assign: {}\n", bco);
|
|
bcx := 0xFF;
|
|
bcx ^= 0x0F;
|
|
print("xor-assign: {}\n", bcx);
|
|
bcs := 1;
|
|
bcs <<= 8;
|
|
print("shl-assign: {}\n", bcs);
|
|
bcr := 256;
|
|
bcr >>= 4;
|
|
print("shr-assign: {}\n", bcr);
|
|
|
|
// Modulo on variables
|
|
mv1 := 17;
|
|
mv2 := 5;
|
|
print("mod-var: {}\n", mv1 % mv2);
|
|
|
|
// Logical (short-circuit)
|
|
print("and: {}\n", true and true);
|
|
print("and-false: {}\n", true and false);
|
|
print("or: {}\n", false or true);
|
|
print("or-false: {}\n", false or false);
|
|
|
|
// Short-circuit verification
|
|
print("short-and: {}\n", false and true);
|
|
print("short-or: {}\n", true or false);
|
|
|
|
// Compound assignment
|
|
ca := 10;
|
|
ca += 5;
|
|
print("ca+=: {}\n", ca);
|
|
ca -= 3;
|
|
print("ca-=: {}\n", ca);
|
|
ca *= 2;
|
|
print("ca*=: {}\n", ca);
|
|
ca /= 6;
|
|
print("ca/=: {}\n", ca);
|
|
|
|
// Precedence
|
|
print("prec1: {}\n", 2 + 3 * 4);
|
|
print("prec2: {}\n", (2 + 3) * 4);
|
|
|
|
// xx explicit cast
|
|
big2 : f64 = 200.7;
|
|
small : u8 = xx big2;
|
|
print("xx-cast: {}\n", small);
|
|
|
|
// Implicit widening conversions
|
|
wu : u8 = 200;
|
|
ws : s64 = wu;
|
|
print("widen-u8-s64: {}\n", ws);
|
|
|
|
wi3 : s32 = 42;
|
|
wf : f64 = wi3;
|
|
print("widen-s32-f64: {}\n", wf);
|
|
|
|
wf32 : f32 = 1.5;
|
|
wf64 : f64 = wf32;
|
|
print("widen-f32-f64: {}\n", wf64);
|
|
|
|
wu2 : u8 = 100;
|
|
ws2 : s16 = wu2;
|
|
print("widen-u8-s16: {}\n", ws2);
|
|
|
|
// More xx narrowing
|
|
xl : s64 = 12345;
|
|
xs : s32 = xx xl;
|
|
print("xx-s64-s32: {}\n", xs);
|
|
|
|
xd : f64 = 1.5;
|
|
xf : f32 = xx xd;
|
|
print("xx-f64-f32: {}\n", xf);
|
|
|
|
xdf : f64 = 7.9;
|
|
xdi : s32 = xx xdf;
|
|
print("xx-f64-s32: {}\n", xdi);
|
|
|
|
// ========================================================
|
|
// 3. TYPE SYSTEM
|
|
// ========================================================
|
|
print("=== 3. Types ===\n");
|
|
|
|
// Primitive types
|
|
v_s8 : s8 = 127;
|
|
v_s16 : s16 = 32000;
|
|
v_s32 : s32 = 100000;
|
|
v_u8 : u8 = 255;
|
|
v_u16 : u16 = 65000;
|
|
v_u32 : u32 = 4000000;
|
|
print("s8: {}\n", v_s8);
|
|
print("s16: {}\n", v_s16);
|
|
print("s32: {}\n", v_s32);
|
|
print("u8: {}\n", v_u8);
|
|
print("u16: {}\n", v_u16);
|
|
print("u32: {}\n", v_u32);
|
|
|
|
// Type alias
|
|
mf : MyFloat = 1.5;
|
|
print("alias: {}\n", mf);
|
|
|
|
// --- Structs ---
|
|
// Positional literal
|
|
p1 : Point = .{ 1, 2 };
|
|
print("struct-pos: {}\n", p1);
|
|
|
|
// Type-prefix literal
|
|
p2 := Point.{ 3, 4 };
|
|
print("struct-prefix: {}\n", p2);
|
|
|
|
// Named fields
|
|
p3 := Point.{ y=10, x=20 };
|
|
print("struct-named: {}\n", p3);
|
|
|
|
// Shorthand (variable name = field name)
|
|
x : s32 = 5;
|
|
y : s32 = 6;
|
|
p4 := Point.{ x, y };
|
|
print("struct-shorthand: {}\n", p4);
|
|
|
|
// Field defaults
|
|
d1 : Defaults;
|
|
print("defaults: a={} b={}\n", d1.a, d1.b);
|
|
|
|
// Field access and assignment
|
|
p5 := Point.{ 0, 0 };
|
|
p5.x = 42;
|
|
p5.y = 99;
|
|
print("field-assign: {}\n", p5);
|
|
|
|
// --- Enum (payload-less) ---
|
|
ec : Color = .red;
|
|
print("enum: {}\n", ec);
|
|
|
|
// Enum comparison
|
|
ce1 : Color = .red;
|
|
ce2 : Color = .red;
|
|
ce3 : Color = .blue;
|
|
print("enum-eq: {}\n", ce1 == ce2);
|
|
print("enum-neq: {}\n", ce1 != ce3);
|
|
|
|
// Backing type
|
|
st : Status = .err;
|
|
print("backing: {}\n", st);
|
|
|
|
// --- Enum (tagged union) ---
|
|
sh : Shape = .circle(3.14);
|
|
print("tagged: {}\n", sh);
|
|
|
|
// Payload access
|
|
radius := sh.circle;
|
|
print("payload: {}\n", radius);
|
|
|
|
// Void variant
|
|
sh = .none;
|
|
print("void-variant: {}\n", sh);
|
|
|
|
// Variant reassignment
|
|
sh = .circle(1.0);
|
|
print("reassign: {}\n", sh);
|
|
sh = .rect(.{ 5, 3 });
|
|
print("reassign2: {}\n", sh);
|
|
|
|
// Type-prefix construction
|
|
tp := Shape.circle(2.5);
|
|
print("enum-prefix: {}\n", tp);
|
|
|
|
// Pattern matching
|
|
sh2 : Shape = .rect(.{ 5, 3 });
|
|
if sh2 == {
|
|
case .circle: print("match: circle\n");
|
|
case .rect: print("match: rect\n");
|
|
case .none: print("match: none\n");
|
|
}
|
|
|
|
// Match as expression
|
|
sh3 : Shape = .circle(1.0);
|
|
ms := if sh3 == {
|
|
case .circle: 10;
|
|
case .rect: 20;
|
|
case .none: 30;
|
|
}
|
|
print("match-expr: {}\n", ms);
|
|
|
|
// Match expression with else
|
|
me_val := 42;
|
|
me_res := if me_val == {
|
|
case 1: 10;
|
|
case 2: 20;
|
|
else: 99;
|
|
}
|
|
print("match-expr-else: {}\n", me_res);
|
|
|
|
// Payload capture (block form)
|
|
sh4 : Shape = .circle(9.5);
|
|
if sh4 == {
|
|
case .circle: (r) { print("capture: {}\n", r); }
|
|
case .rect: (sz) { print("capture: {}\n", sz); }
|
|
case .none: print("capture: none\n");
|
|
}
|
|
|
|
// Payload capture (arrow form)
|
|
sh_ca : Shape = .circle(7.5);
|
|
if sh_ca == {
|
|
case .circle: (r) => print("capture-arrow: {}\n", r);
|
|
case .rect: (sz) => print("capture-arrow: rect\n");
|
|
case .none: print("capture-arrow: none\n");
|
|
}
|
|
|
|
// else arm in match
|
|
num := 42;
|
|
if num == {
|
|
case 1: print("else-match: one\n");
|
|
case 2: print("else-match: two\n");
|
|
else: print("else-match: other\n");
|
|
}
|
|
|
|
// Integer pattern matching
|
|
code := 2;
|
|
if code == {
|
|
case 1: print("int-match: one\n");
|
|
case 2: print("int-match: two\n");
|
|
case 3: print("int-match: three\n");
|
|
}
|
|
|
|
// Integer match with else
|
|
im_code := 99;
|
|
if im_code == {
|
|
case 1: print("int-match-else: one\n");
|
|
case 2: print("int-match-else: two\n");
|
|
else: print("int-match-else: unknown\n");
|
|
}
|
|
|
|
// Bool pattern matching
|
|
bm := true;
|
|
if bm == {
|
|
case true: print("bool-match-t: yes\n");
|
|
case false: print("bool-match-t: no\n");
|
|
}
|
|
bm2 := false;
|
|
if bm2 == {
|
|
case true: print("bool-match-f: yes\n");
|
|
case false: print("bool-match-f: no\n");
|
|
}
|
|
|
|
// Bool conditional
|
|
flag := true;
|
|
if flag { print("bool: true\n"); }
|
|
|
|
// --- Union (untagged) ---
|
|
o : Overlay = ---;
|
|
o.f = 3.14;
|
|
print("union-f: {}\n", o.f);
|
|
// Type punning — read same bits as s32
|
|
print("union-i: {}\n", o.i);
|
|
|
|
// Union member promotion
|
|
uv : Vec2 = ---;
|
|
uv.x = 1.0;
|
|
uv.y = 2.0;
|
|
print("promoted-x: {}\n", uv.x);
|
|
print("promoted-data0: {}\n", uv.data[0]);
|
|
|
|
// --- Arrays ---
|
|
arr : [5]s32 = .[10, 20, 30, 40, 50];
|
|
print("arr[2]: {}\n", arr[2]);
|
|
print("arr.len: {}\n", arr.len);
|
|
|
|
// Array element assignment
|
|
aa : [3]s32 = .[1, 2, 3];
|
|
aa[1] = 99;
|
|
print("arr-assign: {}\n", aa);
|
|
|
|
// --- Slices ---
|
|
sl : []s32 = .[1, 2, 3, 4, 5];
|
|
print("sl[0]: {}\n", sl[0]);
|
|
print("sl.len: {}\n", sl.len);
|
|
|
|
// Slice element write
|
|
sla : []s32 = .[10, 20, 30];
|
|
sla[1] = 55;
|
|
print("sl-assign: {}\n", sla);
|
|
|
|
// Subslicing
|
|
sub := arr[1..4];
|
|
print("sub: {}\n", sub);
|
|
head := arr[..3];
|
|
print("head: {}\n", head);
|
|
tail := arr[2..];
|
|
print("tail: {}\n", tail);
|
|
|
|
// Slice of slice
|
|
sos : []s32 = .[10, 20, 30, 40, 50];
|
|
mid := sos[1..4];
|
|
inner := mid[0..2];
|
|
print("slice-of-slice: {}\n", inner);
|
|
|
|
// String subslicing
|
|
msg := "hello world";
|
|
print("strsub: {}\n", msg[6..11]);
|
|
print("str-prefix: {}\n", msg[..5]);
|
|
print("str-suffix: {}\n", msg[6..]);
|
|
|
|
// --- Pointers ---
|
|
// Address-of global variable
|
|
write_to_ptr(@g_smoke_val);
|
|
print("global-addr-of: {}\n", g_smoke_val);
|
|
|
|
pv := Point.{ 10, 20 };
|
|
ptr := @pv;
|
|
print("deref: {}\n", ptr.*);
|
|
|
|
// Auto-deref
|
|
print("auto-deref: {}\n", ptr.x);
|
|
|
|
// Many-pointer
|
|
mp : [*]s32 = @arr[0];
|
|
print("mp[0]: {}\n", mp[0]);
|
|
print("mp[3]: {}\n", mp[3]);
|
|
|
|
// Many-pointer write
|
|
mpw : [5]s32 = .[10, 20, 30, 40, 50];
|
|
mpw_ptr : [*]s32 = @mpw[0];
|
|
mpw_ptr[2] = 99;
|
|
print("mp-write: {}\n", mpw[2]);
|
|
|
|
// Pointer-null comparison
|
|
np : *s32 = null;
|
|
print("ptr==null: {}\n", np == null);
|
|
print("ptr!=null: {}\n", np != null);
|
|
np2 := @pv.x;
|
|
print("ptr2==null: {}\n", np2 == null);
|
|
print("ptr2!=null: {}\n", np2 != null);
|
|
|
|
// Pointer to nested struct field
|
|
Inner3 :: struct { a: f32; b: f32; c: f32; }
|
|
Outer3 :: struct { key: s32; inner: Inner3; }
|
|
out3 := Outer3.{ key = 42, inner = Inner3.{ a = 1.0, b = 2.0, c = 3.0 } };
|
|
ip3 := @out3.inner;
|
|
print("ptr-nested-field: {} {} {}\n", ip3.a, ip3.b, ip3.c);
|
|
|
|
// Store to many-pointer field must not corrupt adjacent memory
|
|
MpHolder :: struct { items: [*]s64; sentinel: s64; }
|
|
mph := MpHolder.{ items = xx 0, sentinel = 42 };
|
|
mph.items = xx 0;
|
|
print("mp-store-sentinel: {}\n", mph.sentinel);
|
|
|
|
// --- Vectors ---
|
|
vc := vec3(1, 3, 2);
|
|
print("vec-construct: {}\n", vc);
|
|
|
|
va := vec3(1, 2, 3);
|
|
vb := vec3(4, 5, 6);
|
|
print("vec-add: {}\n", va + vb);
|
|
print("vec-sub: {}\n", vec3(5, 5, 5) - vec3(1, 2, 3));
|
|
print("vec-mul: {}\n", vec3(2, 3, 4) * vec3(1, 2, 3));
|
|
print("vec-div: {}\n", vec3(10, 9, 8) / vec3(2, 3, 4));
|
|
|
|
print("vec-scalar: {}\n", vec3(1, 3, 2) * 2.0);
|
|
print("vec-neg: {}\n", -vec3(1, 3, 2));
|
|
|
|
ve := vec3(10, 20, 30);
|
|
print("vec-x: {}\n", ve.x);
|
|
print("vec-y: {}\n", ve.y);
|
|
print("vec-z: {}\n", ve.z);
|
|
print("vec-idx: {}\n", ve[1]);
|
|
|
|
// ========================================================
|
|
// 4. CONTROL FLOW
|
|
// ========================================================
|
|
print("=== 4. Control Flow ===\n");
|
|
|
|
// If-then-else (inline, as expression)
|
|
ite := if true then 1 else 2;
|
|
print("ite: {}\n", ite);
|
|
|
|
// If-then-else both branches
|
|
ie_a := if true then 10 else 20;
|
|
ie_b := if false then 10 else 20;
|
|
print("ite-both: {} {}\n", ie_a, ie_b);
|
|
|
|
// If block
|
|
if 1 < 2 {
|
|
print("if-block: yes\n");
|
|
}
|
|
|
|
// If without else (statement)
|
|
if false { print("should-not-print\n"); }
|
|
print("if-no-else: after\n");
|
|
|
|
// Nested if
|
|
nx := 10;
|
|
if nx > 5 {
|
|
if nx > 8 {
|
|
print("nested-if: deep\n");
|
|
}
|
|
}
|
|
|
|
// If-else-if chain
|
|
eiv := 2;
|
|
if eiv == 1 {
|
|
print("if-else-if: first\n");
|
|
} else if eiv == 2 {
|
|
print("if-else-if: second\n");
|
|
} else {
|
|
print("if-else-if: other\n");
|
|
}
|
|
|
|
// If block as expression
|
|
ibe := 10 + if true { 5; } else { 0; };
|
|
print("if-block-expr: {}\n", ibe);
|
|
|
|
// While basic
|
|
wi := 0;
|
|
while wi < 5 { wi += 1; }
|
|
print("while: {}\n", wi);
|
|
|
|
// While with false condition (never executes)
|
|
while false { print("should-not-print\n"); }
|
|
print("while-false: skipped\n");
|
|
|
|
// While with break
|
|
wb := 0;
|
|
while wb < 100 {
|
|
if wb == 7 { break; }
|
|
wb += 1;
|
|
}
|
|
print("while-break: {}\n", wb);
|
|
|
|
// While with continue
|
|
wsum := 0;
|
|
wc := 0;
|
|
while wc < 10 {
|
|
wc += 1;
|
|
if wc % 2 == 0 { continue; }
|
|
wsum += wc;
|
|
}
|
|
print("while-continue: {}\n", wsum);
|
|
|
|
// While sum 1..10
|
|
wsum2 := 0;
|
|
wi2 := 1;
|
|
while wi2 <= 10 {
|
|
wsum2 += wi2;
|
|
wi2 += 1;
|
|
}
|
|
print("while-sum: {}\n", wsum2);
|
|
|
|
// Nested while
|
|
nw_outer := 0;
|
|
nw_count := 0;
|
|
while nw_outer < 3 {
|
|
nw_inner := 0;
|
|
while nw_inner < 3 {
|
|
nw_count += 1;
|
|
nw_inner += 1;
|
|
}
|
|
nw_outer += 1;
|
|
}
|
|
print("nested-while: {}\n", nw_count);
|
|
|
|
// Nested while with break in inner
|
|
nb_outer := 0;
|
|
nb_icount := 0;
|
|
while nb_outer < 5 {
|
|
nb_i := 0;
|
|
while nb_i < 5 {
|
|
if nb_i == 1 { break; }
|
|
nb_i += 1;
|
|
}
|
|
nb_icount += nb_i;
|
|
nb_outer += 1;
|
|
if nb_outer == 2 { break; }
|
|
}
|
|
print("nested-break: {} {}\n", nb_outer, nb_icount);
|
|
|
|
// For loop basic
|
|
farr : [4]s32 = .[10, 20, 30, 40];
|
|
out("for:");
|
|
for farr: (it) {
|
|
out(" ");
|
|
out(int_to_string(it));
|
|
}
|
|
out("\n");
|
|
|
|
// For with print
|
|
out("for-print:");
|
|
for farr: (it) {
|
|
print(" {}", it);
|
|
}
|
|
out("\n");
|
|
|
|
// For with index
|
|
out("for-idx:");
|
|
for farr: (_, ix) {
|
|
out(" ");
|
|
out(int_to_string(ix));
|
|
}
|
|
out("\n");
|
|
|
|
// For with print two args
|
|
out("for-2arg:");
|
|
for farr: (it, ix) {
|
|
print(" {}@{}", it, ix);
|
|
}
|
|
out("\n");
|
|
|
|
// For with break
|
|
out("for-break:");
|
|
for farr: (it) {
|
|
if it == 30 { break; }
|
|
print(" {}", it);
|
|
}
|
|
out("\n");
|
|
|
|
// For with continue
|
|
out("for-continue:");
|
|
for farr: (it) {
|
|
if it == 20 { continue; }
|
|
print(" {}", it);
|
|
}
|
|
out("\n");
|
|
|
|
// For on slice
|
|
fsl : []s32 = .[10, 20, 30];
|
|
out("for-slice:");
|
|
for fsl: (it) {
|
|
print(" {}", it);
|
|
}
|
|
out("\n");
|
|
|
|
// For on slice with index
|
|
out("for-slice-idx:");
|
|
for fsl: (it, ix) {
|
|
print(" {}:{}", ix, it);
|
|
}
|
|
out("\n");
|
|
|
|
// Nested for
|
|
nf_a : [2]s32 = .[0, 1];
|
|
nf_b : [2]s32 = .[0, 1];
|
|
out("for-nested:");
|
|
for nf_a: (oa) {
|
|
for nf_b: (ob) {
|
|
print(" ({},{})", oa, ob);
|
|
}
|
|
}
|
|
out("\n");
|
|
|
|
// For with break preserving index
|
|
fbi : [5]s32 = .[10, 20, 30, 40, 50];
|
|
fbi_idx := 0;
|
|
for fbi: (it, ix) {
|
|
if it == 30 { fbi_idx = ix; break; }
|
|
}
|
|
print("for-break-idx: {}\n", fbi_idx);
|
|
|
|
// Multiple print placeholders
|
|
print("multi: {} {} {}\n", 1, 2, 3);
|
|
|
|
// ========================================================
|
|
// 5. FUNCTIONS & DECLARATIONS
|
|
// ========================================================
|
|
print("=== 5. Functions ===\n");
|
|
|
|
// Constant binding
|
|
FORTY_TWO :: 42;
|
|
print("const: {}\n", FORTY_TWO);
|
|
|
|
// Typed constant
|
|
TYPED_PI : f64 : 3.14;
|
|
print("typed-const: {}\n", TYPED_PI);
|
|
|
|
// Variable with default init
|
|
di : s32;
|
|
print("default-init: {}\n", di);
|
|
|
|
// Implicit return
|
|
print("implicit-ret: {}\n", implicit_return(21));
|
|
|
|
// Explicit return
|
|
print("early-ret: {}\n", early_return(5));
|
|
print("early-ret2: {}\n", early_return(20));
|
|
|
|
// Void return
|
|
void_return();
|
|
print("void-return: ok\n");
|
|
|
|
// Generic — single param
|
|
print("generic-s32: {}\n", identity(42));
|
|
print("generic-f32: {}\n", identity(1.5));
|
|
print("generic-bool: {}\n", identity(true));
|
|
|
|
// Generic — multiple params
|
|
print("generic-multi: {}\n", pair_add(10, 20));
|
|
|
|
// Lambda
|
|
double :: (x: s32) => x * 2;
|
|
print("lambda: {}\n", double(7));
|
|
|
|
// Lambda with return type
|
|
halve :: (x: f32) -> f32 => x / 2.0;
|
|
print("lambda-ret: {}\n", halve(10.0));
|
|
|
|
// Local function (non-lambda)
|
|
local_add :: (a: s32, b: s32) -> s32 { a + b; }
|
|
print("local-fn: {}\n", local_add(3, 4));
|
|
|
|
// Nested function calls
|
|
print("fn-nested: {}\n", add(mul(2, 3), mul(4, 5)));
|
|
|
|
// Variadic (typed)
|
|
print("varargs: {}\n", typed_sum(1, 2, 3, 4, 5));
|
|
|
|
// Spread
|
|
spread_arr : [3]s32 = .[10, 20, 30];
|
|
print("spread: {}\n", typed_sum(..spread_arr));
|
|
|
|
// Function pointers
|
|
fp : (s32, s32) -> s32 = add;
|
|
print("fp: {}\n", fp(3, 4));
|
|
fp = mul;
|
|
print("fp-reassign: {}\n", fp(3, 4));
|
|
print("fp-apply: {}\n", apply(add, 10, 20));
|
|
|
|
// ========================================================
|
|
// 6. SCOPING & DEFER
|
|
// ========================================================
|
|
print("=== 6. Scoping ===\n");
|
|
|
|
// Scope block with shadowing
|
|
sv := 100;
|
|
{
|
|
sv := 200;
|
|
print("inner: {}\n", sv);
|
|
}
|
|
print("outer: {}\n", sv);
|
|
|
|
// Shadow with different type
|
|
st_v := 42;
|
|
print("shadow-type: {}\n", st_v);
|
|
{
|
|
st_v := 3.14;
|
|
print("shadow-type: {}\n", st_v);
|
|
}
|
|
|
|
// Nested scopes (3 levels)
|
|
nv := 1;
|
|
{
|
|
nv := 2;
|
|
{
|
|
nv := 3;
|
|
print("nest3: {}\n", nv);
|
|
}
|
|
print("nest2: {}\n", nv);
|
|
}
|
|
print("nest1: {}\n", nv);
|
|
|
|
// Scope isolation
|
|
{ iso := 100; print("scope-isolate: {}\n", iso); }
|
|
|
|
// Reuse name after scope exit
|
|
sr := 1;
|
|
print("scope-reuse: {}\n", sr);
|
|
{ sr := 2; print("scope-reuse: {}\n", sr); }
|
|
print("scope-reuse: {}\n", sr);
|
|
|
|
// Multiple defers (LIFO order)
|
|
{
|
|
defer print("defer-c\n");
|
|
defer print("defer-b\n");
|
|
defer print("defer-a\n");
|
|
}
|
|
|
|
// Four defers
|
|
{
|
|
defer print("d1\n");
|
|
defer print("d2\n");
|
|
defer print("d3\n");
|
|
defer print("d4\n");
|
|
}
|
|
|
|
// Defer in nested scopes
|
|
{
|
|
defer print("outer-defer\n");
|
|
{
|
|
defer print("inner-defer\n");
|
|
}
|
|
}
|
|
|
|
// Defer in if block
|
|
if true {
|
|
defer print("defer-in-if: deferred\n");
|
|
print("defer-in-if: body\n");
|
|
}
|
|
|
|
// ========================================================
|
|
// 7. BUILT-IN FUNCTIONS
|
|
// ========================================================
|
|
print("=== 7. Builtins ===\n");
|
|
|
|
// out
|
|
out("out-ok\n");
|
|
|
|
// sqrt
|
|
print("sqrt: {}\n", sqrt(9.0));
|
|
print("sqrt-f64: {}\n", sqrt(16.0));
|
|
|
|
// size_of
|
|
print("sizeof-s32: {}\n", size_of(s32));
|
|
print("sizeof-f64: {}\n", size_of(f64));
|
|
print("sizeof-struct: {}\n", size_of(Point));
|
|
|
|
// align_of
|
|
print("alignof-u8: {}\n", align_of(u8));
|
|
print("alignof-s32: {}\n", align_of(s32));
|
|
print("alignof-s64: {}\n", align_of(s64));
|
|
print("alignof-struct: {}\n", align_of(Point));
|
|
|
|
// type_of + category matching
|
|
tv := 42;
|
|
ttype := type_of(tv);
|
|
if ttype == {
|
|
case int: print("typeof: int\n");
|
|
case float: print("typeof: float\n");
|
|
else: print("typeof: other\n");
|
|
}
|
|
|
|
// type_of — float
|
|
tf := 3.14;
|
|
if type_of(tf) == {
|
|
case float: print("typeof-float: float\n");
|
|
else: print("typeof-float: other\n");
|
|
}
|
|
|
|
// type_of — string
|
|
ts := "hello";
|
|
if type_of(ts) == {
|
|
case string: print("typeof-string: string\n");
|
|
else: print("typeof-string: other\n");
|
|
}
|
|
|
|
// type_of — bool
|
|
tb := true;
|
|
if type_of(tb) == {
|
|
case bool: print("typeof-bool: bool\n");
|
|
else: print("typeof-bool: other\n");
|
|
}
|
|
|
|
// type_of — struct
|
|
tst := Point.{ 1, 2 };
|
|
if type_of(tst) == {
|
|
case struct: print("typeof-struct: struct\n");
|
|
else: print("typeof-struct: other\n");
|
|
}
|
|
|
|
// type_of — enum
|
|
ten : Color = .red;
|
|
if type_of(ten) == {
|
|
case enum: print("typeof-enum: enum\n");
|
|
else: print("typeof-enum: other\n");
|
|
}
|
|
|
|
// type_name
|
|
print("typename: {}\n", type_name(Point));
|
|
|
|
// field_count on struct
|
|
print("fieldcount: {}\n", field_count(Point));
|
|
|
|
// field_count on enum
|
|
print("fieldcount-enum: {}\n", field_count(Color));
|
|
|
|
// field_name on struct
|
|
print("fieldname0: {}\n", field_name(Point, 0));
|
|
print("fieldname1: {}\n", field_name(Point, 1));
|
|
|
|
// field_name on enum
|
|
print("fieldname-enum0: {}\n", field_name(Color, 0));
|
|
print("fieldname-enum2: {}\n", field_name(Color, 2));
|
|
|
|
// field_value (use any_to_string to avoid sext-on-Any bug)
|
|
fv_pt := Point.{ 11, 22 };
|
|
out("fieldval0: ");
|
|
out(any_to_string(field_value(fv_pt, 0)));
|
|
out("\n");
|
|
out("fieldval1: ");
|
|
out(any_to_string(field_value(fv_pt, 1)));
|
|
out("\n");
|
|
|
|
// field_index on plain enum
|
|
fi_c : Color = .green;
|
|
print("fieldidx: {}\n", field_index(Color, fi_c));
|
|
|
|
// field_index on tagged enum
|
|
fi_sh : Shape = .circle(1.0);
|
|
print("fieldidx-tagged: {}\n", field_index(Shape, fi_sh));
|
|
fi_sh2 : Shape = .none;
|
|
print("fieldidx-tagged2: {}\n", field_index(Shape, fi_sh2));
|
|
|
|
// cast
|
|
cval : f64 = 3.7;
|
|
print("cast: {}\n", cast(s32) cval);
|
|
cv2 : s32 = 42;
|
|
print("cast-int-f64: {}\n", cast(f64) cv2);
|
|
|
|
// ========================================================
|
|
// 8. COMPILE-TIME
|
|
// ========================================================
|
|
print("=== 8. Comptime ===\n");
|
|
|
|
// #run constant
|
|
print("run-const: {}\n", CT_VAL);
|
|
|
|
// #run with expression
|
|
print("run-expr: {}\n", CT_MUL);
|
|
|
|
// #run chained dependency
|
|
print("run-chain: {}\n", CT_CHAIN);
|
|
|
|
// #run comptime optionals
|
|
print("ct-opt-coalesce: {}\n", CT_OPT_COALESCE); // ct-opt-coalesce: 141
|
|
print("ct-opt-unwrap: {}\n", CT_OPT_UNWRAP); // ct-opt-unwrap: 77
|
|
print("ct-opt-guard: {}\n", CT_OPT_GUARD); // ct-opt-guard: 10
|
|
|
|
// #insert with function
|
|
#insert gen_code();
|
|
|
|
// #insert additional
|
|
#insert gen_val();
|
|
|
|
// ========================================================
|
|
// 9. FLAGS
|
|
// ========================================================
|
|
print("=== 9. Flags ===\n");
|
|
|
|
// Combine flags
|
|
perm : Perms = .read | .write;
|
|
print("flags: {}\n", perm);
|
|
|
|
// Test flag
|
|
if perm & .read { print("has-read: yes\n"); }
|
|
if perm & .execute { print("has-exec: yes\n"); }
|
|
|
|
// Test flag negative
|
|
pt : Perms = .write;
|
|
if pt & .read {
|
|
print("flags-neg: has-read\n");
|
|
} else {
|
|
print("flags-neg: no-read\n");
|
|
}
|
|
|
|
// Single flag
|
|
ps : Perms = .execute;
|
|
print("flags-single: {}\n", ps);
|
|
|
|
// All flags
|
|
pall : Perms = .read | .write | .execute;
|
|
print("flags-all: {}\n", pall);
|
|
|
|
// Cast to int
|
|
print("flags-raw: {}\n", cast(s64) perm);
|
|
|
|
// Flags with explicit values
|
|
wf : WindowFlags = .vsync | .resizable;
|
|
print("flags-explicit: {}\n", wf);
|
|
print("flags-explicit-raw: {}\n", cast(s64) wf);
|
|
|
|
// --- Multi-target assignment (swap) ---
|
|
print("--- swap ---\n");
|
|
|
|
// Variable swap
|
|
{
|
|
sa := 10;
|
|
sb := 20;
|
|
sa, sb = sb, sa;
|
|
print("var swap: {} {}\n", sa, sb);
|
|
}
|
|
|
|
// Array element swap
|
|
{
|
|
sarr : [3]s64 = .[1, 2, 3];
|
|
sarr[0], sarr[2] = sarr[2], sarr[0];
|
|
print("arr swap: {} {}\n", sarr[0], sarr[2]);
|
|
}
|
|
|
|
// 3-way rotation
|
|
{
|
|
ra := 1;
|
|
rb := 2;
|
|
rc := 3;
|
|
ra, rb, rc = rc, ra, rb;
|
|
print("3-way: {} {} {}\n", ra, rb, rc);
|
|
}
|
|
|
|
// --- Tuple destructuring ---
|
|
print("--- destructure ---\n");
|
|
|
|
// Basic tuple destructuring
|
|
{
|
|
da, db := (10, 20);
|
|
print("basic: {} {}\n", da, db);
|
|
}
|
|
|
|
// Destructure from function return
|
|
{
|
|
dswap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
|
|
dx, dy := dswap(1, 2);
|
|
print("fn: {} {}\n", dx, dy);
|
|
}
|
|
|
|
// Discard with _
|
|
{
|
|
_, dsecond := (100, 200);
|
|
print("discard: {}\n", dsecond);
|
|
}
|
|
|
|
// Three elements
|
|
{
|
|
da3, db3, dc3 := (1, 2, 3);
|
|
print("triple: {} {} {}\n", da3, db3, dc3);
|
|
}
|
|
|
|
// ========================================================
|
|
// 15. FOREIGN FUNCTION BINDING
|
|
// ========================================================
|
|
print("=== 15. Foreign ===\n");
|
|
|
|
// Symbol rename: c_abs maps to C's abs()
|
|
print("foreign-rename: {}\n", c_abs(xx -42));
|
|
|
|
// ========================================================
|
|
// 16. COMPOUND ASSIGNMENT TYPE CONVERSION
|
|
// ========================================================
|
|
print("=== 16. Compound Assign ===\n");
|
|
{
|
|
ca_a : f64 = 10.0;
|
|
ca_b : f32 = 3.0;
|
|
ca_a += ca_b;
|
|
print("f64+=f32: {}\n", ca_a);
|
|
|
|
ca_c : s64 = 100;
|
|
ca_d : s32 = 7;
|
|
ca_c -= ca_d;
|
|
print("s64-=s32: {}\n", ca_c);
|
|
}
|
|
|
|
// ========================================================
|
|
// 17. SLICE/ARRAY .ptr ACCESS
|
|
// ========================================================
|
|
print("=== 17. Slice Ptr ===\n");
|
|
{
|
|
sarr : [5]s32 = .[10, 20, 30, 40, 50];
|
|
ssl := sarr[1..4];
|
|
sp := ssl.ptr;
|
|
print("sl-ptr[0]: {}\n", sp[0]);
|
|
print("sl-ptr[1]: {}\n", sp[1]);
|
|
}
|
|
|
|
// ========================================================
|
|
// 18. ARRAYS OF USER-DEFINED TYPES
|
|
// ========================================================
|
|
print("=== 18. Array of Structs ===\n");
|
|
{
|
|
spts : [2]Point = .[Point.{1, 2}, Point.{3, 4}];
|
|
spt2 := spts[1];
|
|
print("arr-struct-x: {}\n", spt2.x);
|
|
for spts: (it) {
|
|
print("for-struct: {}\n", it);
|
|
}
|
|
}
|
|
|
|
// ========================================================
|
|
// 19. LOCAL FUNCTION RETURNING STRUCT/ENUM
|
|
// ========================================================
|
|
print("=== 19. Local Fn Return ===\n");
|
|
{
|
|
local_pt :: () -> Point { Point.{42, 99}; }
|
|
lp := local_pt();
|
|
print("local-struct: {} {}\n", lp.x, lp.y);
|
|
|
|
local_sh :: () -> Shape { .circle(2.5); }
|
|
ls := local_sh();
|
|
print("local-enum: {}\n", ls);
|
|
}
|
|
|
|
// ========================================================
|
|
// 20. PIPE UFCS RETURN TYPE INFERENCE
|
|
// ========================================================
|
|
print("=== 20. UFCS Return Type ===\n");
|
|
{
|
|
p := Point.{3, 4};
|
|
print("direct: {}\n", point_sum(p));
|
|
print("ufcs: {}\n", p |> point_sum());
|
|
}
|
|
|
|
// ========================================================
|
|
// 21. TYPE-NAMED VARIABLES (s2, u8, etc.)
|
|
// ========================================================
|
|
print("=== 21. Type-Named Vars ===\n");
|
|
{
|
|
s2 := 42;
|
|
print("s2: {}\n", s2);
|
|
s2 = s2 + 1;
|
|
print("s2+1: {}\n", s2);
|
|
}
|
|
|
|
// ========================================================
|
|
// 22. IF-EXPRESSION RETURNING STRUCT
|
|
// ========================================================
|
|
print("=== 22. If-Struct ===\n");
|
|
{
|
|
flag := true;
|
|
p := if flag { Point.{10, 20}; } else { Point.{30, 40}; };
|
|
print("if-struct: {} {}\n", p.x, p.y);
|
|
q := if !flag { Point.{10, 20}; } else { Point.{30, 40}; };
|
|
print("else-struct: {} {}\n", q.x, q.y);
|
|
}
|
|
|
|
// ========================================================
|
|
// 23. NESTED ARRAYS (2D)
|
|
// ========================================================
|
|
print("=== 23. Nested Arrays ===\n");
|
|
{
|
|
matrix : [2][3]s32 = .[ .[1, 2, 3], .[4, 5, 6] ];
|
|
print("m[0][0]: {}\n", matrix[0][0]);
|
|
print("m[0][2]: {}\n", matrix[0][2]);
|
|
print("m[1][0]: {}\n", matrix[1][0]);
|
|
print("m[1][2]: {}\n", matrix[1][2]);
|
|
}
|
|
|
|
// ========================================================
|
|
// 24. STRING COMPARISON
|
|
// ========================================================
|
|
print("=== 24. String Comparison ===\n");
|
|
{
|
|
a := "hello";
|
|
b := "hello";
|
|
c := "world";
|
|
print("str-eq: {}\n", a == b);
|
|
print("str-neq: {}\n", a != c);
|
|
print("str-diff: {}\n", a == c);
|
|
empty := "";
|
|
print("empty-eq: {}\n", empty == "");
|
|
}
|
|
|
|
// ========================================================
|
|
// 25. ARRAY LOOP MUTATION
|
|
// ========================================================
|
|
print("=== 25. Array Loop Mutation ===\n");
|
|
{
|
|
arr : [4]s32 = .[0, 0, 0, 0];
|
|
i := 0;
|
|
while i < 4 {
|
|
arr[i] = xx (i + 1);
|
|
i += 1;
|
|
}
|
|
print("loop-fill: {} {} {} {}\n", arr[0], arr[1], arr[2], arr[3]);
|
|
arr[2] += 10;
|
|
print("compound: {}\n", arr[2]);
|
|
}
|
|
|
|
// === 26. #using struct composition ===
|
|
print("=== 26. #using ===\n");
|
|
{
|
|
UBase :: struct { x: s32; y: s32; }
|
|
UExt :: struct { #using UBase; z: s32; }
|
|
e := UExt.{ x = 1, y = 2, z = 3 };
|
|
print("using-x: {}\n", e.x);
|
|
print("using-y: {}\n", e.y);
|
|
print("using-z: {}\n", e.z);
|
|
|
|
// #using in middle position
|
|
UHeader :: struct { version: s32; }
|
|
UPacket :: struct { id: s32; #using UHeader; payload: s32; }
|
|
p := UPacket.{ id = 10, version = 42, payload = 99 };
|
|
print("pkt-id: {}\n", p.id);
|
|
print("pkt-ver: {}\n", p.version);
|
|
print("pkt-pay: {}\n", p.payload);
|
|
|
|
// Multiple #using
|
|
UPos :: struct { px: s32; py: s32; }
|
|
UCol :: struct { r: s32; g: s32; }
|
|
USprite :: struct { #using UPos; #using UCol; scale: s32; }
|
|
s := USprite.{ px = 10, py = 20, r = 255, g = 128, scale = 1 };
|
|
print("sprite-px: {}\n", s.px);
|
|
print("sprite-r: {}\n", s.r);
|
|
print("sprite-scale: {}\n", s.scale);
|
|
}
|
|
|
|
// --- Comptime format ---
|
|
{
|
|
ct_body :: "hello";
|
|
ct_msg :: format("say: {} (len={})", ct_body, ct_body.len);
|
|
print("{}\n", ct_msg);
|
|
|
|
ct_num :: 42;
|
|
ct_num_msg :: format("n={}", ct_num);
|
|
print("{}\n", ct_num_msg);
|
|
}
|
|
|
|
// --- Tuples ---
|
|
{
|
|
print("=== Tuples ===\n");
|
|
pair := (40, 2);
|
|
print("{}\n", pair.0);
|
|
print("{}\n", pair.1);
|
|
|
|
named := (x: 10, y: 20);
|
|
print("{}\n", named.x);
|
|
print("{}\n", named.0);
|
|
|
|
single := (42,);
|
|
print("{}\n", single.0);
|
|
|
|
zeroed : (s32, s32) = ---;
|
|
print("{}\n", zeroed.0);
|
|
print("{}\n", zeroed.1);
|
|
}
|
|
|
|
// --- UFCS Aliases & Pipe ---
|
|
{
|
|
print("=== UFCS Aliases ===\n");
|
|
|
|
num_sum :: (a: s64, b: s64) -> s64 { a + b; }
|
|
sum :: ufcs num_sum;
|
|
|
|
print("{}\n", num_sum(40, 2)); // 42 — direct call
|
|
print("{}\n", sum(40, 2)); // 42 — alias direct call
|
|
print("{}\n", 40 |> sum(2)); // 42 — pipe UFCS via alias
|
|
|
|
print("{}\n", num_sum(40, 2)); // 42 — direct (was tuple full-splat)
|
|
print("{}\n", 40 |> sum(2)); // 42 — pipe (was tuple partial-splat)
|
|
|
|
compute :: (a: s64, b: s64, c: s64, d: s64) -> s64 { a + b * c - d; }
|
|
calc :: ufcs compute;
|
|
|
|
print("{}\n", compute(1, 2, 3, 4)); // 1+2*3-4 = 3 (was tuple full-splat)
|
|
print("{}\n", compute(1, 2, 3, 4)); // same = 3 (was tuple partial-splat)
|
|
print("{}\n", 1 |> calc(2, 3, 4)); // same = 3 — pipe UFCS
|
|
|
|
// Tuple return type
|
|
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
|
|
s := swap(1, 2);
|
|
a := s.0;
|
|
b := s.1;
|
|
print("{}\n", a); // 2
|
|
print("{}\n", b); // 1
|
|
|
|
wrap :: (x: s64) -> (s64) { (x,); }
|
|
t := wrap(99);
|
|
print("{}\n", t.0); // 99
|
|
}
|
|
|
|
// --- 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(s64, 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(64);
|
|
p2 := a.alloc(128);
|
|
print("gpa allocs: {}\n", gpa.alloc_count); // gpa allocs: 2
|
|
a.dealloc(p1);
|
|
a.dealloc(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(40);
|
|
a2 := a.alloc(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(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(24);
|
|
b2 := a.alloc(24);
|
|
print("buf pos: {}\n", buf.pos); // buf pos: 48
|
|
b3 := a.alloc(200);
|
|
b3_i : s64 = 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: ?s32 = 42;
|
|
y: ?s32 = null;
|
|
print("opt x: {}\n", x); // opt x: 42
|
|
print("opt y: {}\n", y); // opt y: null
|
|
}
|
|
|
|
// Force unwrap
|
|
{
|
|
x: ?s32 = 10;
|
|
val := x!;
|
|
print("unwrap: {}\n", val); // unwrap: 10
|
|
}
|
|
|
|
// Null coalescing
|
|
{
|
|
x: ?s32 = 42;
|
|
y: ?s32 = 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: ?s32 = 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: ?s32 = 7;
|
|
y: ?s32 = 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: ?s32) -> s32 {
|
|
return if v == {
|
|
case .some: (val) { val; }
|
|
case .none: { 0; }
|
|
};
|
|
}
|
|
a: ?s32 = 55;
|
|
b: ?s32 = 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: s32) -> ?s32 {
|
|
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: ?s32) -> s32 {
|
|
return val ?? 0;
|
|
}
|
|
a: ?s32 = 42;
|
|
b: ?s32 = 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(s32, 5, 10)); // generic opt 1: 5
|
|
print("generic opt 2: {}\n", first_pos(s32, 0, 7)); // generic opt 2: 7
|
|
print("generic opt 3: {}\n", first_pos(s32, 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: ?s32 = 42;
|
|
y: ?s32 = null;
|
|
|
|
// if x != null → x is narrowed to s32
|
|
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: ?s32) -> s32 {
|
|
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: ?s32 = 10;
|
|
b: ?s32 = 20;
|
|
c: ?s32 = 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: ?s32, b: ?s32) -> s32 {
|
|
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: ?s32 = 10;
|
|
b: ?s32 = 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: ?s32) -> s32 {
|
|
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: s64, lo: s64, hi: s64) -> s64 {
|
|
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: s32, y: s32) -> s32, a: s32, b: s32) -> s32 {
|
|
return f(a, b);
|
|
}
|
|
add :: (a: s32, b: s32) -> s32 { return a + b; }
|
|
print("named-fn-type: {}\n", apply_named(add, 3, 4)); // named-fn-type: 7
|
|
}
|
|
|
|
// --- xx on function pointers ---
|
|
{
|
|
MyEnv :: struct { n: s32; }
|
|
typed_fn :: (e: *MyEnv, x: s32) -> s32 {
|
|
return x + e.n;
|
|
}
|
|
// xx cast: (*MyEnv, s32) -> s32 → (*void, s32) -> s32
|
|
f : (*void, s32) -> s32 = 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: s32) -> s32 {
|
|
return x * 2;
|
|
}
|
|
fn_ptr : *void = xx dummy_fn;
|
|
null_env : *void = xx 0;
|
|
c : Closure(s32) -> s32 = .{ 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: s32; }
|
|
impl_fn :: (env: *void, x: s32) -> s32 {
|
|
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(s32) -> s32 = .{ fn_ptr = fn_ptr, env = env_ptr };
|
|
print("closure-call: {}\n", c(10));
|
|
}
|
|
|
|
// --- auto-promotion: bare fn → Closure ---
|
|
{
|
|
double :: (x: s32) -> s32 { return x * 2; }
|
|
apply :: (f: Closure(s32) -> s32, x: s32) -> s32 { return f(x); }
|
|
print("auto-promote: {}\n", apply(double, 10));
|
|
|
|
// Named function to Closure variable
|
|
f : Closure(s32) -> s32 = double;
|
|
print("auto-promote-var: {}\n", f(5));
|
|
}
|
|
|
|
// --- closure() intrinsic ---
|
|
{
|
|
// capture scalar
|
|
n := 42;
|
|
f := closure((x: s32) => x + n);
|
|
print("closure-capture: {}\n", f(10));
|
|
|
|
// capture by value is a snapshot
|
|
m := 5;
|
|
g := closure((x: s32) => x + m);
|
|
m = 100;
|
|
print("closure-snapshot: {}\n", g(10));
|
|
|
|
// no captures (null env)
|
|
h := closure((x: s32) => x * 2);
|
|
print("closure-nocap: {}\n", h(7));
|
|
|
|
// multiple captures
|
|
a := 10;
|
|
b := 20;
|
|
multi := closure((x: s32) => x + a + b);
|
|
print("closure-multi: {}\n", multi(3));
|
|
|
|
// block-body closure with return
|
|
offset := 50;
|
|
clamp := closure((x: s64) -> s64 {
|
|
if x < 0 { return 0; }
|
|
if x > 100 { return 100; }
|
|
return x + offset;
|
|
});
|
|
r1 : s64 = clamp(10);
|
|
r2 : s64 = clamp(0 - 5);
|
|
r3 : s64 = 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: s32) -> s32 { return x * 2; }
|
|
apply_cl :: (f2: Closure(s32) -> s32, x: s32) -> s32 { return f2(x); }
|
|
factor : s32 = 3;
|
|
print("closure-hof: {}\n", apply_cl(closure((x: s32) -> s32 => 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 : s32 = 100;
|
|
f_2p := closure((x: s32, y: s32) -> s32 => x + y + base);
|
|
print("closure-2p: {}\n", f_2p(3, 4));
|
|
|
|
// C5.B4: three params
|
|
bias : s32 = 1;
|
|
f_3p := closure((a: s32, b: s32, c2: s32) -> s32 => a + b + c2 + bias);
|
|
print("closure-3p: {}\n", f_3p(10, 20, 30));
|
|
|
|
// C5.B5: mixed param types (string + s32)
|
|
extra : s32 = 5;
|
|
f_mix := closure((name: string, age: s32) {
|
|
print("closure-mix: {} is {}\n", name, age + extra);
|
|
});
|
|
f_mix("Alice", 30);
|
|
|
|
// C5.C3: return bool
|
|
threshold : s32 = 100;
|
|
f_rbool := closure((x: s32) -> bool { return x > threshold; });
|
|
print("closure-rbool: {} {}\n", f_rbool(50), f_rbool(200));
|
|
|
|
// C5.D3: reduce / fold
|
|
reduce :: (arr: []s32, f3: Closure(s32, s32) -> s32, init: s32) -> s32 {
|
|
acc := init;
|
|
i : s64 = 0;
|
|
while i < arr.len { acc = f3(acc, arr[i]); i += 1; }
|
|
return acc;
|
|
}
|
|
r_nums : []s32 = .[1, 2, 3, 4, 5];
|
|
r_bonus : s32 = 100;
|
|
r_total := reduce(r_nums, closure((acc: s32, x: s32) -> s32 => acc + x), r_bonus);
|
|
print("closure-reduce: {}\n", r_total);
|
|
|
|
// C5.G1: factory function
|
|
make_adder :: (n: s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => 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: s64) -> s64 => x + inner_n);
|
|
outer_cl := closure((x: s64) -> s64 => inner_cl(x) * 2);
|
|
print("closure-compose: {}\n", outer_cl(5));
|
|
|
|
// C5.M7: multiple closures from same scope capture independently
|
|
shared : s32 = 10;
|
|
cl_a := closure((x: s32) -> s32 => x + shared);
|
|
cl_b := closure((x: s32) -> s32 => x * shared);
|
|
print("closure-indep: {} {}\n", cl_a(5), cl_b(5));
|
|
|
|
// C6: optional closures
|
|
f_none : ?Closure(s64) -> s64 = null;
|
|
if h := f_none {
|
|
print("should not print: {}\n", h(1));
|
|
} else {
|
|
print("opt-closure: none\n");
|
|
}
|
|
|
|
opt_n := 10;
|
|
f_some : ?Closure(s64) -> s64 = closure((x: s64) -> s64 => 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(s64) -> void; }
|
|
btn_x := 99;
|
|
btn_cl := closure((id: s64) {
|
|
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 : s32 = 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 s32 tag)
|
|
c_a9 : s32 = 2; // simulate enum tag
|
|
f_a9 := closure(() -> s32 => 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: s32, b: s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => m * x + b);
|
|
}
|
|
lin := make_linear(3, 7);
|
|
print("closure-linear: {}\n", lin(10));
|
|
|
|
// C5.G3: factory returning clamper
|
|
make_clamper :: (lo: s32, hi: s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 {
|
|
if x < lo { return lo; }
|
|
if x > hi { return hi; }
|
|
return x;
|
|
});
|
|
}
|
|
clamp_fn := make_clamper(0, 255);
|
|
cv1 : s32 = xx -10;
|
|
cv2 : s32 = 100;
|
|
cv3 : s32 = 999;
|
|
print("closure-clamp: {} {} {}\n", clamp_fn(cv1), clamp_fn(cv2), clamp_fn(cv3));
|
|
|
|
// C5.H2: compose
|
|
compose :: (f_h2: Closure(s32) -> s32, g_h2: Closure(s32) -> s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => f_h2(g_h2(x)));
|
|
}
|
|
one_h2 : s32 = 1;
|
|
two_h2 : s32 = 2;
|
|
add1_h2 := closure((x: s32) -> s32 => x + one_h2);
|
|
mul2_h2 := closure((x: s32) -> s32 => x * two_h2);
|
|
composed := compose(mul2_h2, add1_h2);
|
|
print("closure-compose2: {}\n", composed(5));
|
|
|
|
// C5.H3: chain of closures
|
|
ch_k1 : s32 = 1;
|
|
ch_k2 : s32 = 2;
|
|
ch_k10 : s32 = 10;
|
|
ch_a := closure((x: s32) -> s32 => x + ch_k1);
|
|
ch_b := closure((x: s32) -> s32 => ch_a(x) * ch_k2);
|
|
ch_c := closure((x: s32) -> s32 => ch_b(x) + ch_k10);
|
|
print("closure-chain: {}\n", ch_c(5));
|
|
|
|
// C5.D1: map
|
|
map_cl :: (arr: [*]s32, cnt: s64, f_map: Closure(s32) -> s32, result: [*]s32) {
|
|
i := 0;
|
|
while i < cnt { result[i] = f_map(arr[i]); i += 1; }
|
|
}
|
|
map_src : [5]s32 = .[1, 2, 3, 4, 5];
|
|
map_dst : [5]s32 = .[0, 0, 0, 0, 0];
|
|
factor_d1 : s32 = 3;
|
|
map_cl(xx @map_src, 5, closure((x: s32) -> s32 => 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: [*]s32, cnt: s64, pred: Closure(s32) -> bool, result: [*]s32) -> s64 {
|
|
j := 0;
|
|
i := 0;
|
|
while i < cnt {
|
|
if pred(arr[i]) { result[j] = arr[i]; j += 1; }
|
|
i += 1;
|
|
}
|
|
return j;
|
|
}
|
|
min_val : s32 = 3;
|
|
filt_dst : [5]s32 = .[0, 0, 0, 0, 0];
|
|
kept := filter_cl(xx @map_src, 5, closure((x: s32) -> 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: [*]s32, cnt: s64, less: Closure(s32, s32) -> 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]s32 = .[5, 3, 1, 4, 2];
|
|
descending := true;
|
|
sort_cl(xx @sort_arr, 5, closure((a: s32, b: s32) -> 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: [*]s32, cnt: s64, f_fe: Closure(s32, s64) -> void) {
|
|
i : s64 = 0;
|
|
while i < cnt { f_fe(arr[i], i); i += 1; }
|
|
}
|
|
fe_label := "item";
|
|
fe_arr : [3]s32 = .[10, 20, 30];
|
|
for_each_cl(xx @fe_arr, 3, closure((val: s32, idx: s64) {
|
|
print("closure-fe: {} {}={}\n", fe_label, idx, val);
|
|
}));
|
|
|
|
// C5.D6: find
|
|
find_cl :: (arr: [*]s32, cnt: s64, pred_f: Closure(s32) -> bool) -> s64 {
|
|
i : s64 = 0;
|
|
while i < cnt {
|
|
if pred_f(arr[i]) { return i; }
|
|
i += 1;
|
|
}
|
|
return -1;
|
|
}
|
|
target : s32 = 30;
|
|
found_idx := find_cl(xx @fe_arr, 3, closure((x: s32) -> bool => x == target));
|
|
print("closure-find: {}\n", found_idx);
|
|
|
|
// C5.D7: any
|
|
any_cl :: (arr: [*]s32, cnt: s64, pred_a: Closure(s32) -> bool) -> bool {
|
|
i : s64 = 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: s32) -> bool => x > 100));
|
|
has_20 := any_cl(xx @fe_arr, 3, closure((x: s32) -> bool => x == 20));
|
|
print("closure-any: {} {}\n", has_big, has_20);
|
|
|
|
// C5.E4: auto-promotion in struct field assignment
|
|
Widget :: struct { transform: Closure(s32) -> s32; }
|
|
negate_fn :: (x: s32) -> s32 { 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(s32) -> void;
|
|
}
|
|
btn_x2 := 99;
|
|
btn_cb := closure((id: s32) {
|
|
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 : s32 = 0;
|
|
p_j1 := @state_j1;
|
|
inc_j1 := closure(() -> s32 { p_j1.* += 1; return p_j1.*; });
|
|
print("closure-counter: {} {} {}\n", inc_j1(), inc_j1(), inc_j1());
|
|
|
|
// C5.J2: stateful accumulator
|
|
state_j2 : s32 = 100;
|
|
p_j2 := @state_j2;
|
|
acc_j2 := closure((x: s32) -> s32 { 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 : s32 = 100;
|
|
sum_fn := closure((items: [*]s32, cnt: s64) -> s32 {
|
|
total : s32 = 0;
|
|
i : s64 = 0;
|
|
while i < cnt {
|
|
total += items[i];
|
|
i += 1;
|
|
}
|
|
return total + base_k2;
|
|
});
|
|
k2_arr : [5]s32 = .[1, 2, 3, 4, 5];
|
|
print("closure-loop: {}\n", sum_fn(xx @k2_arr, 5));
|
|
|
|
// C5.M3: reassigning a closure variable
|
|
n_m3 : s32 = 1;
|
|
f_m3 := closure((x: s32) -> s32 => x + n_m3);
|
|
print("closure-reassign: {}\n", f_m3(10));
|
|
m_m3 : s32 = 2;
|
|
f_m3 = closure((x: s32) -> s32 => 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(() -> s32 => 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: s32) -> s32 { return x * 2; }
|
|
base_m2 : Closure(s32) -> s32 = double_m2;
|
|
n_m2 : s32 = 1;
|
|
f_m2 := closure((x: s32) -> s32 => base_m2(x) + n_m2);
|
|
print("closure-cap-promoted: {}\n", f_m2(5));
|
|
|
|
// C5.M5: immediately invoked closure (via temp var)
|
|
n_m5 : s32 = 5;
|
|
iife := closure((x: s32) -> s32 => 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, s32, s32) -> void;
|
|
}
|
|
p_f5_cb := closure((title: string, w: s32, h: s32) {
|
|
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(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(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(s32) -> s32 = ---;
|
|
// i_loop := 0;
|
|
// while i_loop < 5 {
|
|
// val_loop : s32 = xx (i_loop * 10);
|
|
// cl_arr[i_loop] = closure((x: s32) -> s32 => 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 : s32 = 2;
|
|
k_slow : s32 = 10;
|
|
f_fast := closure((x: s32) -> s32 => x * k_fast);
|
|
f_slow := closure((x: s32) -> s32 => x + k_slow);
|
|
f_cond : Closure(s32) -> s32 = 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: s32) -> s32 { return x * 2; }
|
|
f_l3 : Closure(s32) -> s32 = double_l3;
|
|
print("closure-null-env: {}\n", f_l3.env == null);
|
|
|
|
// C5.A7: capture slice (fat pointer like string)
|
|
sl_a7 : [3]s32 = .[10, 20, 30];
|
|
ptr_a7 : [*]s32 = xx @sl_a7;
|
|
f_a7 := closure((i: s64) -> s32 => 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 : s32 = 5;
|
|
f_l1 := closure((x: s32) -> s32 => 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 : s32 = 7;
|
|
result_l2 : s32 = 0;
|
|
push Context.{ allocator = a_l2 } {
|
|
f_l2 := closure((x: s32) -> s32 => x + n_l2);
|
|
result_l2 = f_l2(10);
|
|
a_l2.dealloc(f_l2.env);
|
|
}
|
|
print("closure-gpa: {} allocs={}\n", result_l2, gpa_l2.alloc_count);
|
|
|
|
// C5.A10: capture optional
|
|
val_a10 : ?s32 = 42;
|
|
f_a10 := closure(() -> s32 {
|
|
if v := val_a10 { return v; }
|
|
return 0;
|
|
});
|
|
print("closure-opt: {}\n", f_a10());
|
|
|
|
// C5.C6: return optional
|
|
limit_c6 : s32 = 100;
|
|
f_c6 := closure((x: s32) -> ?s32 {
|
|
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: s32) -> s32 { return x * 2; }
|
|
n_m8 : s32 = 10;
|
|
fns_m8 : [3]Closure(s32) -> s32 = ---;
|
|
fns_m8[0] = double_m8; // auto-promoted
|
|
fns_m8[1] = closure((x: s32) -> s32 => x + n_m8); // captured
|
|
fns_m8[2] = closure((x: s32) -> s32 => 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: s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => 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 : s32 = 1;
|
|
k2_e2 : s32 = 2;
|
|
k100_e2 : s32 = 100;
|
|
f0_e2 := closure((x: s32) -> s32 => x + v_e2);
|
|
f1_e2 := closure((x: s32) -> s32 => f0_e2(x) * k2_e2);
|
|
f2_e2 := closure((x: s32) -> s32 => f1_e2(x) + k100_e2);
|
|
print("closure-deep-chain: {}\n", f2_e2(10));
|
|
|
|
// C5.E3: many captures (stress env struct)
|
|
c1_e3 : s32 = 1;
|
|
c2_e3 : s32 = 2;
|
|
c3_e3 : s32 = 3;
|
|
c4_e3 : s32 = 4;
|
|
c5_e3 : s32 = 5;
|
|
c6_e3 : s32 = 6;
|
|
c7_e3 : s32 = 7;
|
|
c8_e3 : s32 = 8;
|
|
big_env := closure(() -> s32 => 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: s32, b: s32, c: s32, d: s32) -> s32 => a + b + c + d);
|
|
a_e5 : s32 = 1; b_e5 : s32 = 2; c_e5 : s32 = 3; d_e5 : s32 = 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 : s32 = 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: s32) -> s32 => 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(() -> s32 => 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(s32) -> s32 = ---;
|
|
i_e11 := 0;
|
|
while i_e11 < 3 {
|
|
multiplier : s32 = xx (i_e11 + 1);
|
|
fns_e11[i_e11] = closure((x: s32) -> s32 => 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: s32) -> bool {
|
|
if flag_e12 { return x > 0; }
|
|
return x < 0;
|
|
});
|
|
pos_e12 : s32 = 5;
|
|
neg_e12 : s32 = 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(s32) -> s32, val: s32) -> s32 => f_app(val));
|
|
k_e13 : s32 = 100;
|
|
inner_fn := closure((x: s32) -> s32 => 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 : s32 = 10;
|
|
p_e15 := @val_e15;
|
|
read_fn := closure(() -> s32 => 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 : s32 = 100;
|
|
neg_fn := closure((x: s32) -> s32 => x - off_e16);
|
|
val_e16 : s32 = 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: s64) -> *void => a_e17.alloc(size));
|
|
ptr_e17 := alloc_fn(32);
|
|
print("closure-proto-cap: {}\n", ptr_e17 != null);
|
|
a_e17.dealloc(ptr_e17);
|
|
|
|
// C5.E18: chained factory — compose two factories
|
|
make_scaler :: (factor: s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => x * factor);
|
|
}
|
|
make_offset :: (off: s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => 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 : s32 = 50;
|
|
above_fn := closure((x: s32) -> bool => x >= threshold);
|
|
vals_e19 : [5]s32 = .[10, 30, 50, 70, 90];
|
|
count_above : s32 = 0;
|
|
idx_e19 : s64 = 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(s32, s32) -> s32 = closure((a, b) => a + b);
|
|
a_ci1 : s32 = 3;
|
|
b_ci1 : s32 = 4;
|
|
print("closure-infer: {}\n", f_ci1(a_ci1, b_ci1));
|
|
|
|
// CI.2: inferred params from function argument
|
|
apply_ci :: (f: Closure(s32) -> s32, x: s32) -> s32 { return f(x); }
|
|
k_ci : s32 = 10;
|
|
v_ci : s32 = 5;
|
|
print("closure-infer-arg: {}\n", apply_ci(closure((x) => x + k_ci), v_ci));
|
|
|
|
// CI.3: inferred with block body
|
|
h_ci : Closure(s32, s32) -> s32 = closure((a, b) { return a * b; });
|
|
print("closure-infer-block: {}\n", h_ci(a_ci1, b_ci1));
|
|
|
|
// CI.4: inferred with captures
|
|
cap_ci : s32 = 100;
|
|
f_ci4 : Closure(s32) -> s32 = closure((x) => x + cap_ci);
|
|
print("closure-infer-cap: {}\n", f_ci4(v_ci));
|
|
|
|
// CI.5: inferred in factory return
|
|
mk_ci :: (n: s32) -> Closure(s32) -> s32 { 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(s32) -> s32, g: Closure(s32) -> s32) -> Closure(s32) -> s32 {
|
|
return closure((x: s32) -> s32 => f(g(x)));
|
|
}
|
|
one_ci : s32 = 1;
|
|
two_ci : s32 = 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(s32) -> void = closure((x) { print("closure-{}: {}\n", msg_ci, x); });
|
|
cb_ci(42);
|
|
}
|
|
|
|
// ========================================================
|
|
// PROTOCOLS (Phase 1: static dispatch)
|
|
// ========================================================
|
|
print("=== Protocols ===\n");
|
|
|
|
// P1.1: Basic protocol + impl, direct call on concrete type
|
|
{
|
|
sc := SimpleCounter.{ val = 0 };
|
|
sc.inc();
|
|
sc.inc();
|
|
sc.inc();
|
|
print("P1.1: {}\n", sc.get());
|
|
}
|
|
|
|
// P1.2: impl in separate scope (retroactive conformance)
|
|
{
|
|
p := Point.{ x = 10, y = 20 };
|
|
print("P1.2: {}\n", p.sum());
|
|
}
|
|
|
|
// P2.1: #inline protocol — xx conversion + dynamic dispatch
|
|
{
|
|
acc := Accumulator.{ total = 0 };
|
|
a : Adder = xx @acc;
|
|
a.add(10);
|
|
a.add(20);
|
|
a.add(12);
|
|
print("P2.1: {}\n", a.value());
|
|
}
|
|
|
|
// P2.2: pass protocol value to function
|
|
{
|
|
use_adder :: (a: Adder, n: s32) -> s32 {
|
|
a.add(n);
|
|
a.value();
|
|
}
|
|
acc := Accumulator.{ total = 100 };
|
|
result := use_adder(xx @acc, 50);
|
|
print("P2.2: {}\n", result);
|
|
}
|
|
|
|
// P2.3: different impls through same protocol type
|
|
{
|
|
acc := Accumulator.{ total = 0 };
|
|
dbl := Doubler.{ val = 0 };
|
|
a1 : Adder = xx @acc;
|
|
a2 : Adder = xx @dbl;
|
|
a1.add(5);
|
|
a2.add(5);
|
|
print("P2.3: {} {}\n", a1.value(), a2.value());
|
|
}
|
|
|
|
// P3.1: vtable-pointer protocol (default, no #inline)
|
|
{
|
|
sc := SimpleCounter.{ val = 0 };
|
|
c : Counter = xx @sc;
|
|
c.inc();
|
|
c.inc();
|
|
c.inc();
|
|
c.inc();
|
|
c.inc();
|
|
print("P3.1: {}\n", c.get());
|
|
}
|
|
|
|
// P3.2: vtable protocol passed to function
|
|
{
|
|
use_counter :: (c: Counter) -> s32 {
|
|
c.inc();
|
|
c.inc();
|
|
c.get();
|
|
}
|
|
sc := SimpleCounter.{ val = 10 };
|
|
result := use_counter(xx @sc);
|
|
print("P3.2: {}\n", result);
|
|
}
|
|
|
|
// P4.1: default method calls required method (static dispatch)
|
|
{
|
|
pr := Printer.{ count = 0 };
|
|
pr.say_twice("hi ");
|
|
print("\nP4.1: {}\n", pr.count);
|
|
}
|
|
|
|
// P4.2: default method via dynamic dispatch (vtable)
|
|
{
|
|
pr := Printer.{ count = 0 };
|
|
r : Repeater = xx @pr;
|
|
r.say_twice("yo ");
|
|
print("\nP4.2: {}\n", pr.count);
|
|
}
|
|
|
|
// P4.3: chained default→default calls via vtable
|
|
{
|
|
ci := ChainImpl.{ val = 0 };
|
|
ch : Chained = xx @ci;
|
|
// double_wrap calls wrap twice, wrap calls base once each
|
|
// base("hi") returns 2 (len), wrap adds 1 → 3, double_wrap = 3 + 3 = 6
|
|
result := ch.double_wrap("hi");
|
|
// base was called 2 times (once per wrap call)
|
|
print("P4.3: {} {}\n", result, ci.val);
|
|
}
|
|
|
|
// P5.1: Self type in protocol — static dispatch
|
|
{
|
|
p1 := Point.{ x = 1, y = 2 };
|
|
p2 := Point.{ x = 1, y = 2 };
|
|
p3 := Point.{ x = 3, y = 4 };
|
|
print("P5.1: {} {}\n", p1.eq(p2), p1.eq(p3));
|
|
}
|
|
|
|
// P5.2: Self in return position
|
|
{
|
|
p := Point.{ x = 10, y = 20 };
|
|
p2 := p.clone();
|
|
print("P5.2: {} {}\n", p2.x, p2.y);
|
|
}
|
|
|
|
// P5.5: impl for primitive type
|
|
{
|
|
x := 42;
|
|
y := 42;
|
|
z := 99;
|
|
r1 := x.eq(y);
|
|
r2 := x.eq(z);
|
|
print("P5.5: {} {}\n", r1, r2);
|
|
}
|
|
|
|
// P5.3: Self with dynamic dispatch (erased to *void)
|
|
{
|
|
p1 := Point.{ x = 1, y = 2 };
|
|
p2 := Point.{ x = 1, y = 2 };
|
|
p3 := Point.{ x = 3, y = 4 };
|
|
e : Eq = xx p1;
|
|
print("P5.3: {} {}\n", e.eq(p2), e.eq(p3));
|
|
}
|
|
|
|
// P6.1: Single constraint — constrained generic function
|
|
{
|
|
p1 := Point.{ x = 1, y = 2 };
|
|
p2 := Point.{ x = 1, y = 2 };
|
|
p3 := Point.{ x = 3, y = 4 };
|
|
print("P6.1: {} {}\n", are_equal(p1, p2), are_equal(p1, p3));
|
|
}
|
|
|
|
// P6.2: Constraint with primitive type
|
|
{
|
|
print("P6.2: {} {}\n", are_equal(42, 42), are_equal(42, 99));
|
|
}
|
|
|
|
// P6.3: Multiple constraints
|
|
{
|
|
p1 := Point.{ x = 1, y = 2 };
|
|
p2 := Point.{ x = 1, y = 2 };
|
|
p3 := Point.{ x = 3, y = 4 };
|
|
print("P6.3: {} {}\n", eq_and_hash(p1, p2), eq_and_hash(p1, p3));
|
|
}
|
|
|
|
// P6.4: inline constraint syntax ($T/Protocol)
|
|
{
|
|
// sum_of_inline uses $T/Summable inline (not $T: Type/Summable)
|
|
p1 := Point.{ x = 10, y = 20 };
|
|
p2 := Point.{ x = 3, y = 7 };
|
|
print("P6.4: {}\n", sum_of_inline(p1, p2));
|
|
}
|
|
|
|
// P6.5: Struct type param constraints ($T: Type/Summable)
|
|
{
|
|
box := SumBox(Point).{ val = Point.{ x = 5, y = 15 } };
|
|
print("P6.5: {}\n", box.val.sum());
|
|
}
|
|
|
|
// P7.1: impl for generic struct
|
|
{
|
|
p := Pair(s32).{ a = 10, b = 20 };
|
|
print("P7.1: {}\n", p.sum());
|
|
}
|
|
|
|
// P7.2: generic struct impl with different type arg
|
|
{
|
|
p1 := Pair(s32).{ a = 3, b = 7 };
|
|
p2 := Pair(s64).{ a = 100, b = 200 };
|
|
print("P7.2: {} {}\n", p1.sum(), p2.sum());
|
|
}
|
|
|
|
// P2.4: xx in function return position (tested in standalone test_return.sx)
|
|
// Covered by: make_adder :: (acc: *Accumulator) -> Adder { xx acc; }
|
|
|
|
// P2.6: protocol values in arrays
|
|
{
|
|
acc := Accumulator.{ total = 0 };
|
|
dbl := Doubler.{ val = 0 };
|
|
adders : [2]Adder = .[xx @acc, xx @dbl];
|
|
i := 0;
|
|
while i < 2 {
|
|
adders[i].add(5);
|
|
i += 1;
|
|
}
|
|
print("P2.6: {} {}\n", acc.total, dbl.val);
|
|
}
|
|
|
|
// P2.7: xx on inline struct literal (no intermediate variable)
|
|
{
|
|
use_adder :: (a: Adder) -> s32 { a.add(10); a.value(); }
|
|
result := use_adder(xx Accumulator.{ total = 5 });
|
|
print("P2.7: {}\n", result);
|
|
}
|
|
|
|
// P3.3: xx on inline struct literal with vtable protocol
|
|
{
|
|
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get(); }
|
|
result := use_counter(xx SimpleCounter.{ val = 100 });
|
|
print("P3.3: {}\n", result);
|
|
}
|
|
|
|
// --- Auto type erasure (AE) ---
|
|
print("=== Auto Type Erasure ===\n");
|
|
|
|
// AE1: function argument — concrete passed where protocol expected (no xx)
|
|
{
|
|
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get(); }
|
|
sc := SimpleCounter.{ val = 10 };
|
|
result := use_counter(sc);
|
|
print("AE1: {}\n", result);
|
|
}
|
|
|
|
// AE2: variable assignment — concrete to protocol variable (no xx)
|
|
{
|
|
acc := Accumulator.{ total = 0 };
|
|
a: Adder = acc;
|
|
a.add(5);
|
|
a.add(3);
|
|
print("AE2: {}\n", a.value());
|
|
}
|
|
|
|
// AE3: struct literal passed directly (no xx)
|
|
{
|
|
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get(); }
|
|
result := use_counter(SimpleCounter.{ val = 100 });
|
|
print("AE3: {}\n", result);
|
|
}
|
|
|
|
// AE4: explicit xx still works (not broken)
|
|
{
|
|
use_counter :: (c: Counter) -> s32 { c.inc(); c.get(); }
|
|
result := use_counter(xx SimpleCounter.{ val = 50 });
|
|
print("AE4: {}\n", result);
|
|
}
|
|
|
|
// AE5: pointer auto-erasure — *ConcreteType to protocol
|
|
{
|
|
use_adder :: (a: Adder) { a.add(10); }
|
|
acc := Accumulator.{ total = 5 };
|
|
p := @acc;
|
|
use_adder(p);
|
|
print("AE5: {}\n", acc.total);
|
|
}
|
|
|
|
// --- Struct Constants ---
|
|
print("=== Struct Constants ===\n");
|
|
{
|
|
print("gravity: {}\n", Phys.GRAVITY); // gravity: 9.810000
|
|
print("max speed: {}\n", Phys.MAX_SPEED); // max speed: 100
|
|
p := Phys.{ x = 0.0, y = Phys.GRAVITY };
|
|
print("p.y: {}\n", p.y); // p.y: 9.810000
|
|
}
|
|
|
|
// --- Init Blocks (IB) ---
|
|
print("=== Init Blocks ===\n");
|
|
|
|
// IB1: basic init block with struct methods
|
|
{
|
|
b := Builder.{ total = 0, count = 0 } {
|
|
self.add(10);
|
|
self.add(20);
|
|
self.add(30);
|
|
};
|
|
print("IB1: {} {}\n", b.total, b.count);
|
|
}
|
|
|
|
// IB2: nested init blocks (self shadows correctly)
|
|
{
|
|
b1 := Builder.{ total = 0, count = 0 } {
|
|
self.add(100);
|
|
b2 := Builder.{ total = 0, count = 0 } {
|
|
self.add(42);
|
|
};
|
|
self.add(b2.total);
|
|
};
|
|
print("IB2: {} {}\n", b1.total, b1.count);
|
|
}
|
|
|
|
// IB3: empty init block
|
|
{
|
|
b := Builder.{ total = 5, count = 1 } {};
|
|
print("IB3: {} {}\n", b.total, b.count);
|
|
}
|
|
|
|
// IB4: conditional inside init block
|
|
{
|
|
add_extra := true;
|
|
b := Builder.{ total = 0, count = 0 } {
|
|
self.add(10);
|
|
if add_extra {
|
|
self.add(90);
|
|
}
|
|
};
|
|
print("IB4: {}\n", b.total);
|
|
}
|
|
|
|
// IB5: init block + auto type erasure combined
|
|
{
|
|
use_counter :: (c: Counter) -> s32 { c.inc(); c.inc(); c.get(); }
|
|
result := use_counter(SimpleCounter.{ val = 0 } {
|
|
self.val = 50;
|
|
});
|
|
print("IB5: {}\n", result);
|
|
}
|
|
|
|
// ============================================================
|
|
// SECTION: Struct static method shorthand (.method(args) syntax)
|
|
// ============================================================
|
|
print("--- struct static method shorthand ---\n");
|
|
|
|
// SM1: Basic shorthand — .create(args) resolves to Dims.create(args)
|
|
{
|
|
Dims :: struct {
|
|
w: f32;
|
|
h: f32;
|
|
|
|
create :: (w: f32, h: f32) -> Dims {
|
|
Dims.{ w = w, h = h };
|
|
}
|
|
|
|
square :: (size: f32) -> Dims {
|
|
Dims.{ w = size, h = size };
|
|
}
|
|
}
|
|
use_dims :: (d: Dims) { print("SM1: {} {}\n", d.w, d.h); }
|
|
use_dims(.create(16.0, 8.0));
|
|
use_dims(.square(5.0));
|
|
}
|
|
|
|
// SM2: Shorthand in variable declaration with explicit type
|
|
{
|
|
Pair :: struct {
|
|
a: s64;
|
|
b: s64;
|
|
|
|
make :: (a: s64, b: s64) -> Pair {
|
|
Pair.{ a = a, b = b };
|
|
}
|
|
}
|
|
p : Pair = .make(10, 20);
|
|
print("SM2: {} {}\n", p.a, p.b);
|
|
}
|
|
|
|
// ============================================================
|
|
// OPTIONAL IF-ELSE COERCION
|
|
// ============================================================
|
|
{
|
|
print("--- optional if-else coercion ---\n");
|
|
OptF :: struct { width: ?f32; }
|
|
x :f32: 10.0;
|
|
|
|
// null in then branch
|
|
f1 := OptF.{ width = if true then null else x };
|
|
print("opt-if1: {}\n", f1.width ?? 99.0);
|
|
|
|
// value in then branch, null in else
|
|
f2 := OptF.{ width = if true then x else null };
|
|
print("opt-if2: {}\n", f2.width ?? 99.0);
|
|
|
|
// both branches are values
|
|
f3 := OptF.{ width = if false then 5.0 else x };
|
|
print("opt-if3: {}\n", f3.width ?? 99.0);
|
|
|
|
// standalone optional variable
|
|
val: ?f32 = if true then null else 42.0;
|
|
print("opt-if4: {}\n", val ?? 0.0);
|
|
|
|
val2: ?f32 = if false then null else 42.0;
|
|
print("opt-if5: {}\n", val2 ?? 0.0);
|
|
}
|
|
|
|
// --- usize / isize ---
|
|
{
|
|
a : usize = 42;
|
|
b : isize = 0 - 7;
|
|
print("usize: {}\n", a);
|
|
print("isize: {}\n", b);
|
|
|
|
// arithmetic
|
|
c : usize = a + 8;
|
|
print("usize+8: {}\n", c);
|
|
|
|
// coercion from s32
|
|
x : s32 = 10;
|
|
y : usize = xx x;
|
|
print("s32->usize: {}\n", y);
|
|
|
|
// coercion to s64
|
|
z : s64 = xx a;
|
|
print("usize->s64: {}\n", z);
|
|
}
|
|
|
|
// --- inline if (compile-time conditionals) ---
|
|
print("=== inline if ===\n");
|
|
{
|
|
// POINTER_SIZE is 8 on desktop (64-bit)
|
|
inline if POINTER_SIZE == 8 {
|
|
print("64-bit\n");
|
|
} else {
|
|
print("32-bit\n");
|
|
}
|
|
|
|
// OS enum comparison
|
|
inline if OS == .wasm {
|
|
print("wasm\n");
|
|
} else {
|
|
print("not wasm\n");
|
|
}
|
|
|
|
// != comparison
|
|
inline if OS != .unknown {
|
|
print("known os\n");
|
|
} else {
|
|
print("unknown os\n");
|
|
}
|
|
|
|
// nested inline if
|
|
inline if POINTER_SIZE != 4 {
|
|
inline if OS != .wasm {
|
|
print("desktop 64-bit\n");
|
|
} else {
|
|
print("wasm 64-bit??\n");
|
|
}
|
|
}
|
|
|
|
// POINTER_SIZE in regular (non-inline) if expression
|
|
ps := if POINTER_SIZE == 8 then "8" else "4";
|
|
print("pointer size via if: {}\n", ps);
|
|
}
|
|
|
|
// ── Trailing commas ──────────────────────────────────────────
|
|
print("=== Trailing Commas ===\n");
|
|
{
|
|
// Struct literal with trailing comma
|
|
Vec4 :: struct { x: f64; y: f64; z: f64; w: f64; }
|
|
v := Vec4.{
|
|
x = 1.0,
|
|
y = 2.0,
|
|
z = 3.0,
|
|
w = 4.0,
|
|
};
|
|
assert(v.x == 1.0);
|
|
assert(v.w == 4.0);
|
|
|
|
// Function call with trailing comma
|
|
add :: (a: s64, b: s64) -> s64 { return a + b; }
|
|
r := add(10, 20,);
|
|
assert(r == 30);
|
|
|
|
// Array literal with trailing comma
|
|
arr := s64.[1, 2, 3,];
|
|
assert(arr[2] == 3);
|
|
|
|
print("trailing commas ok\n");
|
|
}
|
|
|
|
print("=== DONE ===\n");
|
|
}
|