diff --git a/build.zig b/build.zig index c918e88..63abd4b 100644 --- a/build.zig +++ b/build.zig @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const static_llvm = b.option(bool, "static-llvm", "Statically link LLVM (self-contained binary, no LLVM needed at runtime)") orelse false; - const llvm_prefix = b.option([]const u8, "llvm-prefix", "Path to LLVM installation") orelse "/opt/homebrew/opt/llvm@18"; + const llvm_prefix = b.option([]const u8, "llvm-prefix", "Path to LLVM installation") orelse "/opt/homebrew/opt/llvm@19"; const include_dir = b.fmt("{s}/include", .{llvm_prefix}); const lib_dir = b.fmt("{s}/lib", .{llvm_prefix}); @@ -136,7 +136,7 @@ pub fn build(b: *std.Build) void { mod.link_libcpp = true; } } else { - mod.linkSystemLibrary("LLVM-18", .{}); + mod.linkSystemLibrary("LLVM-19", .{}); mod.linkSystemLibrary("clang-cpp", .{}); // clang-cpp is C++ — need libc++ on macOS if (target_os != .windows and target_os != .linux) { diff --git a/clang_shim.cpp b/clang_shim.cpp index 8c1eb31..947da85 100644 --- a/clang_shim.cpp +++ b/clang_shim.cpp @@ -128,9 +128,15 @@ buildCompilerInstance(const char *filename, driver_args.push_back("-w"); #ifdef SX_LLVM_PREFIX - static std::string resource_dir = std::string(SX_LLVM_PREFIX) + "/lib/clang/18"; + static std::string resource_dir = std::string(SX_LLVM_PREFIX) + "/lib/clang/19"; driver_args.push_back("-resource-dir"); driver_args.push_back(resource_dir.c_str()); + + // On macOS, ensure system SDK headers are found +#ifdef __APPLE__ + driver_args.push_back("-isysroot"); + driver_args.push_back("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"); +#endif #endif for (const auto *f : extra_flags) diff --git a/examples/38-build-config.sx b/examples/38-build-config.sx new file mode 100644 index 0000000..f5ce892 --- /dev/null +++ b/examples/38-build-config.sx @@ -0,0 +1,45 @@ +#import "modules/std.sx"; +#import "modules/compiler.sx"; + +// --- #run build configuration --- +// build_options() returns a BuildOptions struct at compile time. +// Methods on it (add_link_flag, set_output_path) are compiler builtins +// that configure the build without runtime cost. + +configure_build :: () { + opts := build_options(); + // These calls are intercepted by the compiler at compile time. + // On a normal (non-wasm) target, inline if gates them off. + inline if OS == .wasm { + opts.set_output_path("sx-out/wasm/test.html"); + opts.add_link_flag("-sUSE_SDL=3"); + } +} +#run configure_build(); + +// --- inline if with compiler constants --- + +main :: () { + // Verify #run configure_build() executed without error + print("build config: ok\n"); + + // Verify compiler constants are available + print("pointer size: {}\n", POINTER_SIZE); + + // Verify inline if with OS/ARCH works + inline if OS == { + case .macos: { print("os: macos\n"); } + case .linux: { print("os: linux\n"); } + case .windows: { print("os: windows\n"); } + case .wasm: { print("os: wasm\n"); } + else: { print("os: unknown\n"); } + } + + // Verify POINTER_SIZE is usable in inline if + inline if POINTER_SIZE == 8 { + print("64-bit platform\n"); + } + inline if POINTER_SIZE == 4 { + print("32-bit platform\n"); + } +} diff --git a/examples/issue-0002.sx b/examples/issue-0002.sx deleted file mode 100644 index 2bdaa10..0000000 --- a/examples/issue-0002.sx +++ /dev/null @@ -1,25 +0,0 @@ -#import "modules/std.sx"; - -// Issue: nested field assignment through pointer -// self.inner.field = value should work when self is a pointer - -Inner :: struct { - len: s64; - cap: s64; -} - -Outer :: struct { - inner: Inner; - count: s64; - - reset :: (self: *Outer) { - self.inner.len = 0; // error: field assignment target must be a variable - self.count += 1; - } -} - -main :: () { - o := Outer.{ inner = Inner.{ len = 5, cap = 10 }, count = 0 }; - o.reset(); - print("{}\n", o.inner.len); -} diff --git a/examples/issue-0003.sx b/examples/issue-0003.sx deleted file mode 100644 index f09f301..0000000 --- a/examples/issue-0003.sx +++ /dev/null @@ -1,25 +0,0 @@ -// Issue: enum literal inference in match expression used as assignment RHS -// When a match expression is assigned to a field with a known enum type, -// the enum literals in case arms should infer their type from the assignment target. - -Color :: enum { - red; - green; - blue; - none; -} - -Thing :: struct { - color: Color; -} - -main :: () { - t : Thing = ---; - value : u8 = 1; - t.color = if value == { - case 1: .red; // error: cannot infer enum type for literal - case 2: .green; - case 3: .blue; - else: .none; - }; -} diff --git a/examples/issue-0004-defs.sx b/examples/issue-0004-defs.sx deleted file mode 100644 index a217eb3..0000000 --- a/examples/issue-0004-defs.sx +++ /dev/null @@ -1,18 +0,0 @@ -#import "modules/std.sx"; - -Color :: struct { - r, g, b, a: u8; -} - -COLOR_WHITE :: Color.{ r = 255, g = 255, b = 255, a = 255 }; - -// Additional case: struct constant with enum-typed fields -HAlign :: enum { leading; center; trailing; } -VAlign :: enum { top; center; bottom; } - -Alignment :: struct { - h: HAlign; - v: VAlign; -} - -ALIGN_CENTER :: Alignment.{ h = .center, v = .center }; diff --git a/examples/issue-0004.sx b/examples/issue-0004.sx deleted file mode 100644 index 0cdcf9c..0000000 --- a/examples/issue-0004.sx +++ /dev/null @@ -1,20 +0,0 @@ -// Issue: top-level constants from imported files are not visible -// COLOR_WHITE works after fix, but ALIGN_CENTER (struct with enum fields) does not. -// Error: undefined identifier 'ALIGN_CENTER' - -#import "modules/std.sx"; -#import "examples/issue-0004-defs.sx"; - -Thing :: struct { - color: Color; - alignment: Alignment; - - make :: () -> Thing { - Thing.{ color = COLOR_WHITE, alignment = ALIGN_CENTER }; - } -} - -main :: () { - t := Thing.make(); - print("{}\n", t.color.r); -} diff --git a/examples/issue-0006.sx b/examples/issue-0006.sx deleted file mode 100644 index 796149d..0000000 --- a/examples/issue-0006.sx +++ /dev/null @@ -1,23 +0,0 @@ -// Issue: match on u8 value with enum result assigned to typed field -// The switch value is u8 but case constants are s64 (default int literal type). -// Compiler should cast case constants to match the switch value type. -// LLVM error: Switch constants must all be same type as switch value! - -out :: (str: string) -> void #builtin; - -Button :: enum { - none; - left; - middle; - right; -} - -main :: () { - val : u8 = 2; - result : Button = if val == { - case 1: .left; - case 2: .middle; - case 3: .right; - else: .none; - }; -} diff --git a/examples/issue-0007.sx b/examples/issue-0007.sx deleted file mode 100644 index c086fec..0000000 --- a/examples/issue-0007.sx +++ /dev/null @@ -1,39 +0,0 @@ -// Issue: chained method call on struct field operates on a copy -// `a.field.method()` where method takes *Self creates a temporary copy of `field` -// instead of borrowing `a.field` as a pointer. -// The mutation is lost because it modifies the copy, not the original. - -out :: (str: string) -> void #builtin; - -Counter :: struct { - value: s64; - - inc :: (self: *Counter) { - self.value += 1; - } -} - -Parent :: struct { - counter: Counter; -} - -main :: () { - p := Parent.{ counter = Counter.{ value = 0 } }; - - // This should increment p.counter.value, but the mutation is lost: - p.counter.inc(); - - if p.counter.value == 0 { - out("BUG: p.counter.value is still 0 after inc()\n"); - } else { - out("OK: p.counter.value is 1\n"); - } - - // Workaround: take explicit pointer - cp := @p.counter; - cp.inc(); - - if p.counter.value == 1 { - out("OK: workaround via pointer works\n"); - } -} diff --git a/examples/issue-0008.sx b/examples/issue-0008.sx deleted file mode 100644 index 022a572..0000000 --- a/examples/issue-0008.sx +++ /dev/null @@ -1,26 +0,0 @@ -// Issue 0008: Chained ?? (null coalescing) doesn't work -// -// `a ?? b ?? c` where a: ?f32, b: ?f32, c: f32 fails with: -// "narrowing conversion from '?f32' to 'f32' requires explicit 'xx' cast" -// -// It parses as (a ?? b) ?? c, and the first ?? rejects ?f32 as the rhs. -// -// Expected: ?? should either be right-associative so it parses as a ?? (b ?? c), -// or allow ?T as the rhs (returning ?T when rhs is optional, T when rhs is concrete). -// -// Workaround: use parentheses — a ?? (b ?? c) - -Foo :: struct { - x: ?f32; - y: ?f32; -} - -main :: () -> void { - f := Foo.{ x = 1.0, y = 2.0 }; - - // This works: - ok := f.x ?? (f.y ?? 0.0); - - // This should also work but fails: - bad := f.x ?? f.y ?? 0.0; -} diff --git a/examples/issue-0009.sx b/examples/issue-0009.sx deleted file mode 100644 index 2068a16..0000000 --- a/examples/issue-0009.sx +++ /dev/null @@ -1,20 +0,0 @@ -// Issue 0009: Struct-level constant declarations -// -// Constants declared inside a struct body with `NAME :Type: value;` syntax -// fail with "expected field name in struct". -// -// Expected: structs should support constant declarations alongside fields and methods. - -Foo :: struct { - x: f32; - - // This method works: - get_x :: (self: *Foo) -> f32 { self.x; } - - // This constant should work but fails: - DEFAULT_X :f32: 42.0; -} - -main :: () -> void { - f := Foo.{ x = Foo.DEFAULT_X }; -} diff --git a/examples/issue-0010.sx b/examples/issue-0010.sx deleted file mode 100644 index 20dbb53..0000000 --- a/examples/issue-0010.sx +++ /dev/null @@ -1,38 +0,0 @@ -// Issue 0010: inline if-else in struct literal field produces type error -// The `null` branch is typed as `*void` instead of being coerced to `?f32` -// -// Error: narrowing conversion from '*void' to 'f32' requires explicit 'xx' cast - -#import "modules/std.sx"; - -Foo :: struct { - width: ?f32; -} - -main :: () -> void { - x :f32: 10.0; - - // null in then branch, value in else - f1 := Foo.{ width = if true then null else x }; - print("{}\n", f1.width ?? 99.0); - - // value in then branch, null in else - f2 := Foo.{ width = if true then x else null }; - print("{}\n", f2.width ?? 99.0); - - // both branches are values - f3 := Foo.{ width = if false then 5.0 else x }; - print("{}\n", f3.width ?? 99.0); - - // standalone variable, not just struct fields - val: ?f32 = if true then null else 42.0; - print("{}\n", val ?? 0.0); - - val2: ?f32 = if false then null else 42.0; - print("{}\n", val2 ?? 0.0); - - // negation in condition - cond := false; - val3: ?f32 = if !cond then null else 42.0; - print("{}\n", val3 ?? 0.0); -} diff --git a/examples/issue-01.sx b/examples/issue-01.sx deleted file mode 100644 index 074626d..0000000 --- a/examples/issue-01.sx +++ /dev/null @@ -1,41 +0,0 @@ -#import "modules/std.sx"; - -// Forward references: types, struct fields, methods, and free functions -// can reference types declared later in the file. - -// Free function referencing types declared later -make_frame :: (e: EdgeInsets) -> Frame { - Frame.{ x = e.left, y = e.top, - w = 100.0 - e.left - e.right, - h = 100.0 - e.top - e.bottom }; -} - -// Struct with a field whose type is declared later -Container :: struct { - frame: Frame; - insets: EdgeInsets; -} - -Frame :: struct { - x, y, w, h: f32; - - inset :: (self: Frame, insets: EdgeInsets) -> Frame { - Frame.{ x = self.x + insets.left, y = self.y + insets.top, - w = self.w - insets.left - insets.right, - h = self.h - insets.top - insets.bottom }; - } -} - -EdgeInsets :: struct { - top, left, bottom, right: f32; -} - -main :: () { - e := EdgeInsets.{ top = 10.0, left = 10.0, bottom = 10.0, right = 10.0 }; - f := make_frame(e); - r := f.inset(e); - c := Container.{ frame = f, insets = e }; - print("{}", r.x); - print(" {}", r.w); - print(" {}", c.frame.x); -} diff --git a/examples/modules/compiler.sx b/examples/modules/compiler.sx index 80f831b..6a3cf38 100644 --- a/examples/modules/compiler.sx +++ b/examples/modules/compiler.sx @@ -1,6 +1,19 @@ OperatingSystem :: enum { macos; linux; windows; wasm; unknown; } -Architecture :: enum { aarch64; x86_64; wasm32; unknown; } +Architecture :: enum { aarch64; x86_64; wasm32; wasm64; unknown; } OS : OperatingSystem = .unknown; ARCH : Architecture = .unknown; POINTER_SIZE : s64 = 8; + +BuildOptions :: struct { + add_link_flag :: (self: BuildOptions, flag: [:0]u8) { + // Compiler builtin — intercepted at compile time + } + set_output_path :: (self: BuildOptions, path: [:0]u8) { + // Compiler builtin — intercepted at compile time + } +} + +build_options :: () -> BuildOptions { + return BuildOptions.{}; +} diff --git a/examples/smoke_a.sx b/examples/smoke_a.sx deleted file mode 100644 index 752a437..0000000 --- a/examples/smoke_a.sx +++ /dev/null @@ -1,1611 +0,0 @@ -#import "modules/std.sx"; -#import "modules/math/math.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; - } -} - -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 --- - 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); - - // --- 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)); - - // 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); - } - - // ======================================================== - // 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 - } - - print("=== DONE === -"); -} diff --git a/examples/smoke_b.sx b/examples/smoke_b.sx deleted file mode 100644 index c013ed8..0000000 --- a/examples/smoke_b.sx +++ /dev/null @@ -1,1931 +0,0 @@ -#import "modules/std.sx"; -#import "modules/math/math.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; - } -} - -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 --- - 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); - - // --- 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)); - - // 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); - } - - // ======================================================== - // 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_state : GPA = .{ alloc_count = 0 }; - gpa := gpa_state.create(); - p1 := gpa.alloc(64); - p2 := gpa.alloc(128); - print("gpa allocs: {}\n", gpa_state.alloc_count); // gpa allocs: 2 - gpa.dealloc(p1); - gpa.dealloc(p2); - print("gpa final: {}\n", gpa_state.alloc_count); // gpa final: 0 - } - - // ── Arena backed by GPA (multi-chunk) ─────────────────── - { - gpa_state3 : GPA = .{ alloc_count = 0 }; - gpa3 := gpa_state3.create(); - arena_state : Arena = ---; - arena := arena_state.create(gpa3, 32); - // First chunk fits 80 usable bytes - a1 := arena.alloc(40); - a2 := arena.alloc(40); - print("arena chunks: {}\n", gpa_state3.alloc_count); // arena chunks: 1 - // Overflow → new chunk - a3 := arena.alloc(16); - print("arena overflow: {}\n", gpa_state3.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 newest chunk - arena_state.reset(); - print("arena reset idx: {}\n", arena_state.end_index); // arena reset idx: 0 - print("arena reset gpa: {}\n", gpa_state3.alloc_count);// arena reset gpa: 1 - // Deinit frees all - arena_state.deinit(); - print("arena deinit: {}\n", gpa_state3.alloc_count); // arena deinit: 0 - } - - // ── BufAlloc from stack array ─────────────────────────── - { - stack_buf : [128]u8 = ---; - buf_state : BufAlloc = ---; - bufalloc := buf_state.create(@stack_buf[0], 128); - b1 := bufalloc.alloc(24); - b2 := bufalloc.alloc(24); - print("buf pos: {}\n", buf_state.pos); // buf pos: 48 - b3 := bufalloc.alloc(200); - b3_i : s64 = xx b3; - print("buf overflow: {}\n", b3_i); // buf overflow: 0 - buf_state.reset(); - print("buf reset: {}\n", buf_state.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 - } - - // 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 - } - - - print("=== DONE === -"); -} diff --git a/examples/smoke_c.sx b/examples/smoke_c.sx deleted file mode 100644 index 687a4ee..0000000 --- a/examples/smoke_c.sx +++ /dev/null @@ -1,2667 +0,0 @@ -#import "modules/std.sx"; -#import "modules/math/math.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; - } -} - -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 --- - 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); - - // --- 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)); - - // 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); - } - - // ======================================================== - // 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_state : GPA = .{ alloc_count = 0 }; - gpa := gpa_state.create(); - p1 := gpa.alloc(64); - p2 := gpa.alloc(128); - print("gpa allocs: {}\n", gpa_state.alloc_count); // gpa allocs: 2 - gpa.dealloc(p1); - gpa.dealloc(p2); - print("gpa final: {}\n", gpa_state.alloc_count); // gpa final: 0 - } - - // ── Arena backed by GPA (multi-chunk) ─────────────────── - { - gpa_state3 : GPA = .{ alloc_count = 0 }; - gpa3 := gpa_state3.create(); - arena_state : Arena = ---; - arena := arena_state.create(gpa3, 32); - // First chunk fits 80 usable bytes - a1 := arena.alloc(40); - a2 := arena.alloc(40); - print("arena chunks: {}\n", gpa_state3.alloc_count); // arena chunks: 1 - // Overflow → new chunk - a3 := arena.alloc(16); - print("arena overflow: {}\n", gpa_state3.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 newest chunk - arena_state.reset(); - print("arena reset idx: {}\n", arena_state.end_index); // arena reset idx: 0 - print("arena reset gpa: {}\n", gpa_state3.alloc_count);// arena reset gpa: 1 - // Deinit frees all - arena_state.deinit(); - print("arena deinit: {}\n", gpa_state3.alloc_count); // arena deinit: 0 - } - - // ── BufAlloc from stack array ─────────────────────────── - { - stack_buf : [128]u8 = ---; - buf_state : BufAlloc = ---; - bufalloc := buf_state.create(@stack_buf[0], 128); - b1 := bufalloc.alloc(24); - b2 := bufalloc.alloc(24); - print("buf pos: {}\n", buf_state.pos); // buf pos: 48 - b3 := bufalloc.alloc(200); - b3_i : s64 = xx b3; - print("buf overflow: {}\n", b3_i); // buf overflow: 0 - buf_state.reset(); - print("buf reset: {}\n", buf_state.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 - } - - // 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 = .{ alloc_count = 0 }; - 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 = .{ alloc_count = 0 }; - a_l1 : Allocator = xx @gpa_l1; - arena_l1 : Arena = ---; - arena_alloc := arena_l1.create(a_l1, 4096); - push Context.{ allocator = arena_alloc } { - 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 = .{ alloc_count = 0 }; - 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 = .{ alloc_count = 0 }; - 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); - } - - print("=== DONE === -"); -} diff --git a/examples/smoke_d.sx b/examples/smoke_d.sx deleted file mode 100644 index 95dc876..0000000 --- a/examples/smoke_d.sx +++ /dev/null @@ -1,2343 +0,0 @@ -#import "modules/std.sx"; -#import "modules/math/math.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; - } -} - -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 --- - 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, 5lf.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; - } -} - -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: {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; - } -} - -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: {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; - } -} - -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 - }\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 - }\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 --- - 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 = .[1 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 --- - 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 = .[1 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 --- - 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, 20, 30, 40, 50, 20, 30, 40, 5 \ No newline at end of file diff --git a/examples/test_arena2.sx b/examples/test_arena2.sx deleted file mode 100644 index b0a9292..0000000 --- a/examples/test_arena2.sx +++ /dev/null @@ -1,12 +0,0 @@ -#import "modules/std.sx"; - -Phys :: struct { - x, y: f32; - GRAVITY :f32: 9.81; - MAX_SPEED :: 100; -} - -main :: () { - print("gravity: {}\n", Phys.GRAVITY); - print("max speed: {}\n", Phys.MAX_SPEED); -} diff --git a/examples/test_bus.sx b/examples/test_bus.sx deleted file mode 100644 index e7e0f7d..0000000 --- a/examples/test_bus.sx +++ /dev/null @@ -1,80 +0,0 @@ -#import "modules/std.sx"; - -main :: () { - n1 : s32 = 1; - n2 : s32 = 2; - n3 : s32 = 3; - - f1 := closure((x: s32) -> s32 => x + n1); - f2 := closure((x: s32) -> s32 => x + n2); - f3 := closure((x: s32) -> s32 => x + n3); - - print("f1: {}\n", f1(10)); - print("f2: {}\n", f2(10)); - print("f3: {}\n", f3(10)); - - // closure struct field - Button :: struct { - label: string; - on_press: Closure(s32) -> void; - } - btn_val := 99; - btn_cb := closure((id: s32) { - print("btn: {} {}\n", id, btn_val); - }); - btn := Button.{ label = "OK", on_press = btn_cb }; - btn.on_press(1); - - // optional closure - f_none : ?Closure(s64) -> s64 = null; - if f_none != null { print("should not print\n"); } - else { print("opt-closure: none\n"); } - - // closure factory - make_adder :: (n: s32) -> Closure(s32) -> s32 { - closure((x: s32) -> s32 => x + n); - } - add5 := make_adder(5); - add10 := make_adder(10); - print("factory: {} {}\n", add5(100), add10(100)); - - // HOF compose - compose :: (f: Closure(s32) -> s32, g: Closure(s32) -> s32) -> Closure(s32) -> s32 { - closure((x: s32) -> s32 => f(g(x))); - } - double :: (x: s32) -> s32 { return x * 2; } - cf := compose(add5, double); - print("compose: {}\n", cf(10)); - - // closure with array - sort_bubble :: (arr: [*]s32, cnt: s64, less: Closure(s32, s32) -> bool) { - i : s64 = 0; - while i < cnt { - j : s64 = 0; - while j < cnt - 1 { - 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]; - sort_bubble(xx @sort_arr, 5, closure((a: s32, b: s32) -> bool { - return a < b; - })); - print("sort: {} {} {} {} {}\n", sort_arr[0], sort_arr[1], sort_arr[2], sort_arr[3], sort_arr[4]); - - // Many closures with string captures - tag1 := "hello"; - tag2 := "world"; - sf1 := closure((x: s32) { print("sf1: {} {}\n", tag1, x); }); - sf2 := closure((x: s32) { print("sf2: {} {}\n", tag2, x); }); - sf1(1); - sf2(2); - - print("=== DONE ===\n"); -} diff --git a/examples/test_closure.sx b/examples/test_closure.sx deleted file mode 100644 index 09b0dc8..0000000 --- a/examples/test_closure.sx +++ /dev/null @@ -1,8 +0,0 @@ -#import "modules/std.sx"; - -main :: () { - n := 42; - f := closure((x: s64) -> s64 { x + n; }); - r := f(10); - print("r: {}\n", r); -} diff --git a/examples/test_format.sx b/examples/test_format.sx deleted file mode 100644 index 8e089a5..0000000 --- a/examples/test_format.sx +++ /dev/null @@ -1,5 +0,0 @@ -#import "modules/std.sx"; -greet :: () -> string { format("hello"); } -main :: () { - print("{}\n", greet()); -} diff --git a/examples/test_generic.sx b/examples/test_generic.sx deleted file mode 100644 index 2237cc2..0000000 --- a/examples/test_generic.sx +++ /dev/null @@ -1,6 +0,0 @@ -#import "modules/std.sx"; -main :: () -> s32 { - v := Vector(3,f32).[1,2,3]; - print("{} -", v); -} diff --git a/examples/test_proto.sx b/examples/test_proto.sx deleted file mode 100644 index aa87611..0000000 --- a/examples/test_proto.sx +++ /dev/null @@ -1,24 +0,0 @@ -#import "modules/std.sx"; - -Point :: struct { x: s32; y: s32; } - -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; - } -} - -are_equal :: ($T: Type/Eq, a: T, b: T) -> bool { - a.eq(b); -} - -main :: () { - 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)); -} diff --git a/examples/test_str.sx b/examples/test_str.sx deleted file mode 100644 index 6dc1830..0000000 --- a/examples/test_str.sx +++ /dev/null @@ -1,5 +0,0 @@ -#import "modules/std.sx"; -main :: () { - s := ""; - print("{}\n", s); -} diff --git a/examples/test_union_array.sx b/examples/test_union_array.sx deleted file mode 100644 index f72f8f1..0000000 --- a/examples/test_union_array.sx +++ /dev/null @@ -1,14 +0,0 @@ -#import "modules/std.sx"; - -Vec2 :: union { - data: [2]f32; - struct { x, y: f32; }; -} - -main :: () { - uv : Vec2 = ---; - uv.x = 1.0; - uv.y = 2.0; - print("promoted-x: {}\n", uv.x); - print("promoted-data0: {}\n", uv.data[0]); -} diff --git a/modules/test_c.sx b/modules/test_c.sx deleted file mode 100644 index 6cd4ba1..0000000 --- a/modules/test_c.sx +++ /dev/null @@ -1,4 +0,0 @@ -#import c { - #include "vendors/test_c/test.h"; - #source "vendors/test_c/test.c"; -}; diff --git a/specs.md b/specs.md index a86876f..f1b4185 100644 --- a/specs.md +++ b/specs.md @@ -1582,6 +1582,58 @@ response :: format("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", body.len, This works for any function, not just `format`. The mechanism is general: the VM compiles the function body (including `#insert` directives, variadic `..Any` args, and calls to other functions) and executes it entirely at compile time. If the VM encounters something it cannot evaluate (e.g., foreign function calls, unsupported operations), it silently falls through to runtime codegen. +### Build Configuration + +The `BuildOptions` struct (from `modules/compiler.sx`) provides compile-time build configuration via `#run`. Methods on `BuildOptions` are compiler builtins intercepted during compilation — they have no runtime cost. + +```sx +#import "modules/compiler.sx"; + +configure_build :: () { + opts := build_options(); + opts.add_link_flag("-lm"); + opts.set_output_path("out/my_program"); + + inline if OS == .wasm { + opts.set_output_path("sx-out/wasm/app.html"); + opts.add_link_flag("-sUSE_SDL=3"); + opts.add_link_flag("-sALLOW_MEMORY_GROWTH=1"); + } +} +#run configure_build(); +``` + +**API:** + +| Method | Description | +|--------|-------------| +| `build_options()` | Returns a `BuildOptions` value for the current compilation | +| `opts.add_link_flag(flag)` | Appends a linker flag (merged with CLI flags) | +| `opts.set_output_path(path)` | Sets the output binary path (overridden by CLI `-o`) | + +Build flags from `add_link_flag` are merged with any flags passed on the command line. Duplicate library flags (e.g., `-lSDL3` from multiple imports) are automatically deduplicated. + +### Compiler Constants + +The `modules/compiler.sx` module provides compile-time constants set by the compiler based on the target: + +| Constant | Type | Description | +|----------|------|-------------| +| `OS` | `OperatingSystem` | Target OS: `.macos`, `.linux`, `.windows`, `.wasm`, `.unknown` | +| `ARCH` | `Architecture` | Target arch: `.aarch64`, `.x86_64`, `.wasm32`, `.unknown` | +| `POINTER_SIZE` | `s64` | Pointer width in bytes (8 for 64-bit, 4 for wasm32) | + +These are used with `inline if` for compile-time conditional compilation: + +```sx +inline if OS == .wasm { + // Only compiled when targeting wasm +} +inline if POINTER_SIZE == 8 { + // Only compiled on 64-bit platforms +} +``` + --- ## 9. Modules / Imports @@ -1658,7 +1710,43 @@ main :: () -> s32 { --- -## 10. Program Structure +## 10. CLI & Cross-Compilation + +### Commands + +``` +sx run Compile and run +sx build Compile to binary +sx lsp Start language server (LSP) +``` + +### Options + +| Flag | Description | +|------|-------------| +| `--target ` | Target triple or shorthand (default: host) | +| `--cpu ` | CPU name (default: generic) | +| `--opt ` | Optimization: `none`/`0`, `less`/`1`, `default`/`2`, `aggressive`/`3` | +| `-o ` | Output path (overrides `set_output_path`) | + +### Target Shorthands + +The `--target` flag accepts shorthand aliases for common targets: + +| Shorthand | Expands to | +|-----------|-----------| +| `wasm`, `emscripten` | `wasm32-unknown-emscripten` | +| `macos`, `macos-arm` | `aarch64-apple-macos` | +| `macos-x86` | `x86_64-apple-macos` | +| `linux`, `linux-x86` | `x86_64-unknown-linux-gnu` | +| `linux-arm` | `aarch64-unknown-linux-gnu` | +| `windows` | `x86_64-windows-msvc` | + +Full triples are also accepted and passed through as-is. + +--- + +## 11. Program Structure A program is a sequence of top-level declarations and `#import` directives. Execution begins at `main`. @@ -1672,7 +1760,7 @@ main :: () { --- -## 11. Grammar (informal) +## 12. Grammar (informal) ``` program = top_level* @@ -1731,7 +1819,7 @@ type = '$' IDENT | 's32' | 'f32' | 'f64' | 'bool' | 'string' --- -## 12. Open Questions +## 13. Open Questions - **Nested functions**: Can functions be defined inside other functions? - **Operator overloading**: Not shown — presumably no. diff --git a/src/ast.zig b/src/ast.zig index a4ddb9c..0a52db7 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -215,6 +215,7 @@ pub const IfExpr = struct { pub const MatchExpr = struct { subject: *Node, arms: []const MatchArm, + is_comptime: bool = false, }; pub const MatchArm = struct { diff --git a/src/c_import.zig b/src/c_import.zig index 04a13d4..24990f7 100644 --- a/src/c_import.zig +++ b/src/c_import.zig @@ -309,6 +309,8 @@ pub fn compileCWithEmcc( allocator: std.mem.Allocator, io: std.Io, infos: []const CImportInfo, + target_config: @import("target.zig").TargetConfig, + tmp_dir: []const u8, ) ![]const []const u8 { var paths = std.ArrayList([]const u8).empty; var obj_idx: usize = 0; @@ -317,11 +319,15 @@ pub fn compileCWithEmcc( if (info.sources.len == 0) continue; for (info.sources) |src| { - const out_path = try std.fmt.allocPrint(allocator, "/tmp/sx_emcc_{d}.o", .{obj_idx}); + const out_path = try std.fmt.allocPrint(allocator, "{s}/sx_emcc_{d}.o", .{ tmp_dir, obj_idx }); obj_idx += 1; var argv = std.ArrayList([]const u8).empty; try argv.appendSlice(allocator, &.{ "emcc", "-c", "-O2", src, "-o", out_path }); + // wasm64: compile C sources with memory64 support + if (target_config.isWasm64()) { + try argv.append(allocator, "-sMEMORY64"); + } // Add include paths for (info.includes) |inc| { try argv.append(allocator, try std.fmt.allocPrint(allocator, "-I{s}", .{dirName(inc)})); @@ -355,11 +361,12 @@ pub fn writeCObjectFiles( allocator: std.mem.Allocator, io: std.Io, obj_bufs: []c.LLVMMemoryBufferRef, + tmp_dir: []const u8, ) ![]const []const u8 { var paths = std.ArrayList([]const u8).empty; for (obj_bufs, 0..) |buf, i| { - const path = try std.fmt.allocPrint(allocator, "/tmp/sx_c_{d}.o", .{i}); + const path = try std.fmt.allocPrint(allocator, "{s}/sx_c_{d}.o", .{ tmp_dir, i }); const start = c.LLVMGetBufferStart(buf); const size = c.LLVMGetBufferSize(buf); const data = @as([*]const u8, @ptrCast(start))[0..size]; diff --git a/src/core.zig b/src/core.zig index d96680c..fbf8908 100644 --- a/src/core.zig +++ b/src/core.zig @@ -106,6 +106,18 @@ pub const Compilation = struct { self.ir_emitter = emitter; } + /// Get link flags accumulated from #run build blocks. + pub fn getBuildLinkFlags(self: *Compilation) []const []const u8 { + if (self.ir_emitter) |*e| return e.build_config.link_flags.items; + return &.{}; + } + + /// Get output path set from #run build blocks, if any. + pub fn getBuildOutputPath(self: *Compilation) ?[]const u8 { + if (self.ir_emitter) |*e| return e.build_config.output_path; + return null; + } + /// Collect C import source info from the resolved AST. pub fn collectCImportSources(self: *Compilation) ![]c_import.CImportInfo { const root = self.resolved_root orelse self.root orelse return &.{}; @@ -117,7 +129,7 @@ pub const Compilation = struct { const root = self.resolved_root orelse self.root orelse return ir.Module.init(self.allocator); var module = ir.Module.init(self.allocator); //TODO: find a better place for this - if (self.target_config.isWasm()) { + if (self.target_config.isWasm32()) { module.types.pointer_size = 4; } var lowering = ir.Lowering.init(&module); diff --git a/src/ir/emit_llvm.zig b/src/ir/emit_llvm.zig index 0335e91..a756229 100644 --- a/src/ir/emit_llvm.zig +++ b/src/ir/emit_llvm.zig @@ -86,6 +86,9 @@ pub const LLVMEmitter = struct { // Target configuration (stored for ABI decisions during emission) target_config: TargetConfig, + // Build configuration accumulated from #run blocks + build_config: interp_mod.BuildConfig, + const PendingPhi = struct { phi: c.LLVMValueRef, block_id: BlockId, // the block this phi belongs to @@ -158,10 +161,12 @@ pub const LLVMEmitter = struct { .closure_struct_type = null, .field_name_arrays = std.AutoHashMap(u32, c.LLVMValueRef).init(alloc), .target_config = target_config, + .build_config = .{}, }; } pub fn deinit(self: *LLVMEmitter) void { + self.build_config.deinit(self.alloc); self.ref_map.deinit(); self.func_map.deinit(); self.field_name_arrays.deinit(); @@ -199,9 +204,9 @@ pub const LLVMEmitter = struct { /// Compare IR typeSizeBytes against LLVMABISizeOfType for all user-defined types. fn verifySizes(self: *LLVMEmitter) void { - // Skip for WASM: wasm32 has 4-byte pointers vs IR's assumed 8-byte, + // Skip for wasm32: 4-byte pointers vs IR's assumed 8-byte, // so struct sizes will differ. LLVM handles emission correctly. - if (self.target_config.isWasm()) return; + if (self.target_config.isWasm32()) return; const dl = c.LLVMGetModuleDataLayout(self.llvm_module); if (dl == null) return; const type_count = self.ir_mod.types.infos.items.len; @@ -241,6 +246,7 @@ pub const LLVMEmitter = struct { // Run the side-effect function via interpreter const func_id = ir_inst.FuncId.fromIndex(@intCast(i)); var interp_inst = Interpreter.init(self.ir_mod, self.alloc); + interp_inst.build_config = &self.build_config; _ = interp_inst.call(func_id, &.{}) catch {}; // Write comptime output to stderr (same as old comptime VM) if (interp_inst.output.items.len > 0) { @@ -263,6 +269,7 @@ pub const LLVMEmitter = struct { // Evaluate comptime initializer if present if (global.comptime_func) |func_id| { var interp_inst = Interpreter.init(self.ir_mod, self.alloc); + interp_inst.build_config = &self.build_config; const result = interp_inst.call(func_id, &.{}) catch .void_val; const init_val = self.valueToLLVMConst(result, llvm_ty); c.LLVMSetInitializer(llvm_global, init_val); @@ -793,6 +800,7 @@ pub const LLVMEmitter = struct { const callee_func = &self.ir_mod.functions.items[call_op.callee.index()]; if (callee_func.is_comptime and call_op.args.len == 0) { var interp_inst = Interpreter.init(self.ir_mod, self.alloc); + interp_inst.build_config = &self.build_config; defer interp_inst.deinit(); if (interp_inst.call(call_op.callee, &.{})) |result| { if (result.asInt()) |v| { @@ -1427,7 +1435,7 @@ pub const LLVMEmitter = struct { const raw_ptr = c.LLVMBuildExtractValue(self.builder, str_val, 0, "str.ptr"); const str_len = c.LLVMBuildExtractValue(self.builder, str_val, 1, "str.len"); // On wasm32, count param is i32 (size_t) - const count = if (self.target_config.isWasm()) + const count = if (self.target_config.isWasm32()) c.LLVMBuildTrunc(self.builder, str_len, self.cached_i32, "len.tr") else str_len; @@ -2132,9 +2140,9 @@ pub const LLVMEmitter = struct { return c.LLVMAddFunction(self.llvm_module, "free", fn_ty); } - /// Returns the LLVM type for C `size_t`: i32 on wasm32, i64 on 64-bit targets. + /// Returns the LLVM type for C `size_t`: i32 on wasm32, i64 on 64-bit targets (including wasm64). fn sizeType(self: *LLVMEmitter) c.LLVMTypeRef { - return if (self.target_config.isWasm()) self.cached_i32 else self.cached_i64; + return if (self.target_config.isWasm32()) self.cached_i32 else self.cached_i64; } fn getMallocType(self: *LLVMEmitter) c.LLVMTypeRef { @@ -2540,7 +2548,7 @@ pub const LLVMEmitter = struct { .string => self.getStringStructType(), .any => self.getAnyStructType(), .noreturn => self.cached_void, - .isize, .usize => if (self.target_config.isWasm()) self.cached_i32 else self.cached_i64, + .isize, .usize => if (self.target_config.isWasm32()) self.cached_i32 else self.cached_i64, else => self.toLLVMTypeInfo(ty), }; } @@ -2667,7 +2675,7 @@ pub const LLVMEmitter = struct { // For now, use opaque ptr return self.cached_ptr; }, - .usize, .isize => if (self.target_config.isWasm()) self.cached_i32 else self.cached_i64, + .usize, .isize => if (self.target_config.isWasm32()) self.cached_i32 else self.cached_i64, }; } @@ -2690,7 +2698,7 @@ pub const LLVMEmitter = struct { // WASM32: usize/isize are pointer-sized (i32 on wasm32). // Other integer types (s64, u64) keep their declared size — they represent // genuinely 64-bit values (SDL_WindowFlags, timestamps, etc.). - if (self.target_config.isWasm()) { + if (self.target_config.isWasm32()) { if (ir_ty == .usize or ir_ty == .isize) return self.cached_i32; return llvm_ty; } @@ -3007,6 +3015,13 @@ pub const LLVMEmitter = struct { return self.emitToFile(output_path, c.LLVMAssemblyFile); } + /// Emit the module as LLVM bitcode to disk (for emcc to recompile with a newer LLVM). + pub fn emitBitcode(self: *LLVMEmitter, output_path: [*:0]const u8) !void { + if (c.LLVMWriteBitcodeToFile(self.llvm_module, output_path) != 0) { + return error.EmitFailed; + } + } + /// Dump the LLVM IR to a file for debugging. pub fn dumpIRToFile(self: *LLVMEmitter, path: [*:0]const u8) void { _ = c.LLVMPrintModuleToFile(self.llvm_module, path, null); diff --git a/src/ir/inst.zig b/src/ir/inst.zig index 17023d5..17bec08 100644 --- a/src/ir/inst.zig +++ b/src/ir/inst.zig @@ -302,6 +302,9 @@ pub const BuiltinId = enum(u16) { type_of, alloc, dealloc, + build_options, + build_options_add_link_flag, + build_options_set_output_path, }; pub const ProtocolCall = struct { diff --git a/src/ir/interp.zig b/src/ir/interp.zig index 9b8c5f4..1708cb1 100644 --- a/src/ir/interp.zig +++ b/src/ir/interp.zig @@ -106,6 +106,18 @@ pub const InterpError = error{ Unreachable, }; +// ── BuildConfig ───────────────────────────────────────────────────────── +// Mutable build configuration accumulated by #run blocks via BuildOptions methods. + +pub const BuildConfig = struct { + link_flags: std.ArrayList([]const u8) = .empty, + output_path: ?[]const u8 = null, + + pub fn deinit(self: *BuildConfig, alloc: Allocator) void { + self.link_flags.deinit(alloc); + } +}; + // ── Interpreter ───────────────────────────────────────────────────────── pub const Interpreter = struct { @@ -121,6 +133,9 @@ pub const Interpreter = struct { // Global values: evaluated comptime globals, indexed by GlobalId global_values: std.AutoHashMap(u32, Value), + // Mutable build configuration — set by LLVMEmitter, written by #run blocks + build_config: ?*BuildConfig = null, + pub fn init(module: *const Module, alloc: Allocator) Interpreter { return .{ .module = module, @@ -1242,6 +1257,30 @@ pub const Interpreter = struct { const f = val.asFloat() orelse return error.TypeError; return .{ .value = .{ .float = @floor(f) } }; }, + .build_options => { + // Returns a void sentinel — the "handle" to BuildConfig + return .{ .value = .void_val }; + }, + .build_options_add_link_flag => { + // args: [opts_handle, flag_string] + const str_val = frame.getRef(bi.args[1]); + if (str_val.asString(self)) |s| { + if (self.build_config) |bc| { + bc.link_flags.append(self.alloc, self.alloc.dupe(u8, s) catch return error.CannotEvalComptime) catch return error.CannotEvalComptime; + } + } + return .{ .value = .void_val }; + }, + .build_options_set_output_path => { + // args: [opts_handle, path_string] + const str_val = frame.getRef(bi.args[1]); + if (str_val.asString(self)) |s| { + if (self.build_config) |bc| { + bc.output_path = self.alloc.dupe(u8, s) catch return error.CannotEvalComptime; + } + } + return .{ .value = .void_val }; + }, .cast, .type_of, .alloc, .dealloc => { return error.CannotEvalComptime; }, diff --git a/src/ir/lower.zig b/src/ir/lower.zig index fb4a207..537d22e 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -204,13 +204,15 @@ pub const Lowering = struct { } } - // ARCH: Architecture enum { aarch64; x86_64; wasm32; unknown; } + // ARCH: Architecture enum { aarch64; x86_64; wasm32; wasm64; unknown; } const arch_name_id = self.module.types.internString("Architecture"); if (self.module.types.findByName(arch_name_id)) |arch_ty| { const arch_info = self.module.types.get(arch_ty); if (arch_info == .@"enum") { - const tag: u32 = if (tc.isWasm()) + const tag: u32 = if (tc.isWasm32()) self.findVariantIndex(arch_info.@"enum".variants, "wasm32") + else if (tc.isWasm64()) + self.findVariantIndex(arch_info.@"enum".variants, "wasm64") else if (tc.isAarch64()) self.findVariantIndex(arch_info.@"enum".variants, "aarch64") else if (tc.isX86_64()) @@ -221,8 +223,8 @@ pub const Lowering = struct { } } - // POINTER_SIZE: s64 (4 for wasm, 8 otherwise) - const ptr_size: i64 = if (tc.isWasm()) 4 else 8; + // POINTER_SIZE: s64 (4 for wasm32, 8 for wasm64 and other 64-bit targets) + const ptr_size: i64 = if (tc.isWasm32()) 4 else 8; self.comptime_constants.put("POINTER_SIZE", .{ .int_val = ptr_size }) catch {}; } @@ -2126,6 +2128,52 @@ pub const Lowering = struct { } } + /// Evaluate a compile-time match expression for `inline if ... == { case ... }`. + /// Returns the body of the matching arm, or null if the match can't be resolved. + fn evalComptimeMatch(self: *Lowering, me: *const ast.MatchExpr) ?*const Node { + // Subject must be a comptime constant identifier + const name = switch (me.subject.data) { + .identifier => |id| id.name, + else => return null, + }; + const cv = self.comptime_constants.get(name) orelse return null; + + switch (cv) { + .enum_tag => |et| { + const enum_info = self.module.types.get(et.ty); + if (enum_info != .@"enum") return null; + for (me.arms) |arm| { + if (arm.pattern == null) continue; // default arm + const variant_name = switch (arm.pattern.?.data) { + .enum_literal => |el| el.name, + else => continue, + }; + const variant_idx = self.findVariantIndex(enum_info.@"enum".variants, variant_name); + if (et.tag == variant_idx) return arm.body; + } + // No match — try default arm + for (me.arms) |arm| { + if (arm.pattern == null) return arm.body; + } + return null; + }, + .int_val => |iv| { + for (me.arms) |arm| { + if (arm.pattern == null) continue; + const rhs_val: i64 = switch (arm.pattern.?.data) { + .int_literal => |il| il.value, + else => continue, + }; + if (iv == rhs_val) return arm.body; + } + for (me.arms) |arm| { + if (arm.pattern == null) return arm.body; + } + return null; + }, + } + } + fn lowerWhile(self: *Lowering, we: *const ast.WhileExpr) Ref { const header_bb = self.freshBlock("while.hdr"); const body_bb = self.freshBlock("while.body"); @@ -2240,6 +2288,14 @@ pub const Lowering = struct { } fn lowerMatch(self: *Lowering, me: *const ast.MatchExpr) Ref { + // inline if match: evaluate at compile time, only lower the matching arm + if (me.is_comptime) { + if (self.evalComptimeMatch(me)) |arm_body| { + return self.lowerInlineBranch(arm_body); + } + // Couldn't evaluate — fall through to runtime + } + const is_type_match = isTypeCategoryMatch(me); const subject = self.lowerExpr(me.subject); @@ -3904,6 +3960,15 @@ pub const Lowering = struct { // Try to resolve the method by struct type name const struct_name = self.getStructTypeName(obj_ty); if (struct_name) |sname| { + // Intercept BuildOptions compiler builtins + if (std.mem.eql(u8, sname, "BuildOptions")) { + if (std.mem.eql(u8, fa.field, "add_link_flag")) { + return self.builder.callBuiltin(.build_options_add_link_flag, method_args.items, .void); + } else if (std.mem.eql(u8, fa.field, "set_output_path")) { + return self.builder.callBuiltin(.build_options_set_output_path, method_args.items, .void); + } + } + // Try direct qualified name: StructName.method const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sname, fa.field }) catch fa.field; @@ -7530,6 +7595,12 @@ pub const Lowering = struct { const oi = self.module.types.get(obj_ty); if (oi == .@"struct") { const struct_name = self.module.types.getString(oi.@"struct".name); + // Intercept BuildOptions compiler builtins + if (std.mem.eql(u8, struct_name, "BuildOptions")) { + if (std.mem.eql(u8, cfa.field, "add_link_flag") or std.mem.eql(u8, cfa.field, "set_output_path")) { + return .void; + } + } const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ struct_name, cfa.field }) catch cfa.field; if (self.resolveFuncByName(qualified)) |fid| { return self.module.functions.items[@intFromEnum(fid)].ret; diff --git a/src/llvm_api.zig b/src/llvm_api.zig index 1598922..9e0e158 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -8,6 +8,7 @@ pub const c = @cImport({ @cInclude("llvm-c/Orc.h"); @cInclude("llvm-c/Error.h"); @cInclude("llvm-c/BitReader.h"); + @cInclude("llvm-c/BitWriter.h"); @cInclude("llvm-c/Linker.h"); // Clang shim for C header parsing + source compilation diff --git a/src/main.zig b/src/main.zig index 3f31d67..a4999d5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -34,7 +34,25 @@ pub fn main(init: std.process.Init) !void { if (std.mem.eql(u8, arg, "--target")) { i += 1; if (i >= args.len) { std.debug.print("error: --target requires a value\n", .{}); return; } - target_config.triple = (try allocator.dupeZ(u8, args[i])).ptr; + const raw = args[i]; + // Shorthand aliases for common targets + const expanded = if (std.mem.eql(u8, raw, "wasm") or std.mem.eql(u8, raw, "wasm32") or std.mem.eql(u8, raw, "emscripten")) + "wasm32-unknown-emscripten" + else if (std.mem.eql(u8, raw, "wasm64")) + "wasm64-unknown-emscripten" + else if (std.mem.eql(u8, raw, "macos") or std.mem.eql(u8, raw, "macos-arm")) + "aarch64-apple-macos" + else if (std.mem.eql(u8, raw, "macos-x86")) + "x86_64-apple-macos" + else if (std.mem.eql(u8, raw, "linux") or std.mem.eql(u8, raw, "linux-x86")) + "x86_64-unknown-linux-gnu" + else if (std.mem.eql(u8, raw, "linux-arm")) + "aarch64-unknown-linux-gnu" + else if (std.mem.eql(u8, raw, "windows")) + "x86_64-windows-msvc" + else + raw; + target_config.triple = (try allocator.dupeZ(u8, expanded)).ptr; } else if (std.mem.eql(u8, arg, "--cpu")) { i += 1; if (i >= args.len) { std.debug.print("error: --cpu requires a value\n", .{}); return; } @@ -100,7 +118,6 @@ pub fn main(init: std.process.Init) !void { break :blk base; }; compile(allocator, io, path, output_name, target_config, show_timing, enable_cache) catch return; - std.debug.print("compiled: {s}\n", .{output_name}); } else if (std.mem.eql(u8, command, "ir")) { emitIR(allocator, io, path, target_config) catch return; } else if (std.mem.eql(u8, command, "ir-dump")) { @@ -219,17 +236,17 @@ fn compileCForJIT(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compi } /// Compile C sources from #import c blocks to .o files for linking. -fn compileCForBuild(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compilation) ![]const []const u8 { +fn compileCForBuild(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compilation, tmp_dir: []const u8) ![]const []const u8 { const c_infos = try comp.collectCImportSources(); if (c_infos.len == 0) return &.{}; // For Emscripten targets, use emcc to cross-compile C sources if (comp.target_config.isEmscripten()) { - return try sx.c_import.compileCWithEmcc(allocator, io, c_infos); + return try sx.c_import.compileCWithEmcc(allocator, io, c_infos, comp.target_config, tmp_dir); } const obj_bufs = try sx.c_import.compileCToObjects(allocator, c_infos); - return try sx.c_import.writeCObjectFiles(allocator, io, obj_bufs); + return try sx.c_import.writeCObjectFiles(allocator, io, obj_bufs, tmp_dir); } fn parseOptLevel(s: []const u8) ?sx.target.TargetConfig.OptLevel { @@ -252,7 +269,7 @@ fn printUsage() void { \\ lsp Start language server (LSP) \\ \\Options: - \\ --target Target triple (default: host) + \\ --target Target triple or shorthand: wasm, macos, linux, windows (default: host) \\ --cpu CPU name (default: generic) \\ --opt Optimization: none/0, less/1, default/2, aggressive/3 \\ -o Output path @@ -408,7 +425,11 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons const root = comp.resolved_root orelse comp.root orelse return error.CompileError; const libs = try extractLibraries(allocator, root); - const obj_path = try std.fmt.allocPrintSentinel(allocator, "{s}.o", .{output_path}, 0); + // Create temp directory for build artifacts + const tmp_dir: []const u8 = ".sx-tmp"; + std.Io.Dir.createDirPath(.cwd(), io, tmp_dir) catch {}; + + const obj_path = try std.fmt.allocPrintSentinel(allocator, "{s}/main.o", .{tmp_dir}, 0); // Cache: compute key and check for cached binary/.o const key = computeCacheKey(source, &comp.import_sources, target_config); @@ -453,15 +474,37 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons // Compile C sources from #import c blocks to .o files timer.mark(); - const c_obj_paths = compileCForBuild(allocator, io, &comp) catch { + const c_obj_paths = compileCForBuild(allocator, io, &comp, tmp_dir) catch { std.debug.print("error: C import compilation failed\n", .{}); return error.CompileError; }; timer.record("c-import"); + // Merge build config (from #run blocks) with CLI config + var merged_config = target_config; + const build_flags = comp.getBuildLinkFlags(); + if (build_flags.len > 0) { + var all_flags: std.ArrayList([]const u8) = .empty; + for (target_config.extra_link_flags) |f| try all_flags.append(allocator, f); + for (build_flags) |f| try all_flags.append(allocator, f); + merged_config.extra_link_flags = try all_flags.toOwnedSlice(allocator); + } + // Override output path from #run if set (and no explicit -o was given on CLI) + const final_output = if (target_config.output_path == null) + (comp.getBuildOutputPath() orelse output_path) + else + output_path; + + // Ensure output directory exists + if (std.mem.lastIndexOfScalar(u8, final_output, '/')) |sep| { + if (sep > 0) { + std.Io.Dir.createDirPath(.cwd(), io, final_output[0..sep]) catch {}; + } + } + // Link (sx .o + C .o files) timer.mark(); - sx.target.link(allocator, io, obj_path, c_obj_paths, output_path, libs, target_config) catch { + sx.target.link(allocator, io, obj_path, c_obj_paths, final_output, libs, merged_config) catch { std.debug.print("error: linking failed\n", .{}); return error.CompileError; }; @@ -472,11 +515,16 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons std.Io.Dir.copyFile(.cwd(), output_path, .cwd(), cache_bin, io, .{ .make_path = true }) catch {}; } - // Clean up object files + std.debug.print("compiled: {s}\n", .{final_output}); + + // Clean up temp directory and all build artifacts std.Io.Dir.deleteFile(.cwd(), io, obj_path) catch {}; + const shell_tmp = std.fmt.allocPrint(allocator, "{s}.shell.html", .{obj_path}) catch null; + if (shell_tmp) |sp| std.Io.Dir.deleteFile(.cwd(), io, sp) catch {}; for (c_obj_paths) |cop| { std.Io.Dir.deleteFile(.cwd(), io, cop) catch {}; } + std.Io.Dir.deleteDir(.cwd(), io, tmp_dir) catch {}; } fn runAOT(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, target_config: sx.target.TargetConfig, timer: *Timing, enable_cache: bool) !void { @@ -556,13 +604,21 @@ fn hasTopLevelRun(root: *const sx.ast.Node) bool { fn extractLibraries(allocator: std.mem.Allocator, root: *const sx.ast.Node) ![]const []const u8 { var libs = std.ArrayList([]const u8).empty; + var seen = std.StringHashMap(void).init(allocator); + const addLib = struct { + fn f(l: *std.ArrayList([]const u8), s: *std.StringHashMap(void), a: std.mem.Allocator, name: []const u8) !void { + if (s.contains(name)) return; + try s.put(name, {}); + try l.append(a, name); + } + }.f; for (root.data.root.decls) |decl| { switch (decl.data) { - .library_decl => |ld| try libs.append(allocator, ld.lib_name), + .library_decl => |ld| try addLib(&libs, &seen, allocator, ld.lib_name), .namespace_decl => |ns| { for (ns.decls) |nd| { switch (nd.data) { - .library_decl => |ld| try libs.append(allocator, ld.lib_name), + .library_decl => |ld| try addLib(&libs, &seen, allocator, ld.lib_name), else => {}, } } diff --git a/src/parser.zig b/src/parser.zig index 4addeda..b2a7c8e 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -89,6 +89,8 @@ pub const Parser = struct { const expr = try self.parseIfExpr(); if (expr.data == .if_expr) { expr.data.if_expr.is_comptime = true; + } else if (expr.data == .match_expr) { + expr.data.match_expr.is_comptime = true; } return expr; } @@ -1394,6 +1396,8 @@ pub const Parser = struct { const expr = try self.parseIfExpr(); if (expr.data == .if_expr) { expr.data.if_expr.is_comptime = true; + } else if (expr.data == .match_expr) { + expr.data.match_expr.is_comptime = true; } try self.expectSemicolonAfter(expr); return expr; diff --git a/src/target.zig b/src/target.zig index f5f14f8..e51b15d 100644 --- a/src/target.zig +++ b/src/target.zig @@ -58,6 +58,16 @@ pub const TargetConfig = struct { return self.tripleHasPrefix("wasm32", "wasm64"); } + /// Check if target triple indicates wasm32 specifically (4-byte pointers, i32 size_t). + pub fn isWasm32(self: TargetConfig) bool { + return self.tripleHasPrefix("wasm32", "wasm32"); + } + + /// Check if target triple indicates wasm64 specifically (8-byte pointers, i64 size_t). + pub fn isWasm64(self: TargetConfig) bool { + return self.tripleHasPrefix("wasm64", "wasm64"); + } + /// Check if target triple indicates macOS/Darwin. pub fn isMacOS(self: TargetConfig) bool { return self.tripleContains("darwin") or self.tripleContains("macos"); @@ -177,9 +187,26 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex // Skip -l flags for Emscripten: libraries like SDL3 are provided via // -sUSE_SDL=3, not -lSDL3. User provides everything via --lflags. - // Extra linker flags (e.g. -sUSE_SDL=3, -sUSE_WEBGL2=1, --preload-file) + // wasm64: automatically add -sMEMORY64 for the linker + if (target_config.isWasm64()) { + try argv.append(allocator, "-sMEMORY64"); + } + + // Use the built-in sx HTML shell template (write to temp file for emcc) + if (std.mem.endsWith(u8, output_bin, ".html")) { + const shell_html = @embedFile("wasm_shell.html"); + const shell_path = try std.fmt.allocPrint(allocator, "{s}.shell.html", .{output_obj}); + std.Io.Dir.writeFile(.cwd(), io, .{ .sub_path = shell_path, .data = shell_html }) catch {}; + try argv.appendSlice(allocator, &.{ "--shell-file", shell_path }); + } + + // Extra linker flags (e.g. -sUSE_SDL=3, -sUSE_WEBGL2=1, --preload-file assets) + // Split space-separated flags into individual argv entries. for (target_config.extra_link_flags) |flag| { - try argv.append(allocator, flag); + var it = std.mem.tokenizeScalar(u8, flag, ' '); + while (it.next()) |part| { + try argv.append(allocator, part); + } } } else if (target_config.isWindows()) { // Windows: MSVC-style linker flags @@ -219,9 +246,12 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex try argv.append(allocator, try std.fmt.allocPrint(allocator, "-l{s}", .{lib})); } - // Extra linker flags + // Extra linker flags — split space-separated flags into individual argv entries. for (target_config.extra_link_flags) |flag| { - try argv.append(allocator, flag); + var it = std.mem.tokenizeScalar(u8, flag, ' '); + while (it.next()) |part| { + try argv.append(allocator, part); + } } } diff --git a/src/wasm_shell.html b/src/wasm_shell.html new file mode 100644 index 0000000..f6a8964 --- /dev/null +++ b/src/wasm_shell.html @@ -0,0 +1,49 @@ + + + + + +sx + + + +
+
+
Loading…
+
+ + +{{{ SCRIPT }}} + + diff --git a/tests/expected/38-build-config.exit b/tests/expected/38-build-config.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/38-build-config.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/38-build-config.txt b/tests/expected/38-build-config.txt new file mode 100644 index 0000000..c7e4b60 --- /dev/null +++ b/tests/expected/38-build-config.txt @@ -0,0 +1,4 @@ +build config: ok +pointer size: 8 +os: macos +64-bit platform