# sx An experimental systems programming language with Jai-inspired syntax, compile-time execution, generics, closures, protocols, and an LLVM backend. > **Status**: Highly experimental. The language and compiler are under active development. ## At a Glance ```sx #import "modules/std.sx"; Point :: struct { x, y: s32; magnitude :: (self: *Point) -> f32 { sqrt(self.x * self.x + self.y * self.y); } } main :: () { p := Point.{ x = 3, y = 4 }; print("point: {}, magnitude: {}\n", p, p.magnitude()); } ``` **Key characteristics:** - Jai-inspired declaration syntax: `name :: value` for constants, `name := value` for variables - Compiles to native code via LLVM 19 - Compile-time execution with `#run` - Generics via monomorphization - First-class closures with value capture - Protocol-based polymorphism (traits) - Pattern matching on enums, optionals, and type categories - C interop via `#foreign` and `#import c` - Targets: macOS (ARM64, x86_64), Linux (x86_64, ARM64), Windows (x86_64), WebAssembly ## Building Requires **Zig 0.16+** and **LLVM 19+**. ```sh zig build ``` On macOS with Homebrew LLVM: ```sh # default path: /opt/homebrew/opt/llvm@19 zig build ``` Custom LLVM path: ```sh zig build -Dllvm-prefix=/path/to/llvm ``` ## Usage ```sh sx run file.sx # compile and run sx build file.sx # compile to binary sx build file.sx -o out # compile with output path sx ir file.sx # emit LLVM IR sx lsp # start language server ``` Options: ``` --target target platform (shortcuts: macos, linux, windows, wasm) --opt optimization: none, less, default, aggressive --cpu target CPU -o output path ``` ## Language Overview ### Types | Type | Description | |------|-------------| | `s8`..`s64`, `u8`..`u64` | Signed/unsigned integers (default: `s64`) | | `f32`, `f64` | Floating point (default: `f32`) | | `bool` | `true` / `false` | | `string` | UTF-8 fat pointer `{ptr, len}` | | `[N]T` | Fixed-size array | | `[]T` | Slice (fat pointer) | | `*T`, `[*]T` | Single / many pointer | | `?T` | Optional | | `struct`, `enum`, `union` | Composite types | | `Closure(args) -> ret` | Closure type | ### Declarations ```sx // Constants (compile-time when possible) PI :: 3.14159; MAX : s32 : 100; // Variables (mutable) x := 42; // inferred type y : s32 = 0; // explicit type z : s32 = ---; // uninitialized ``` ### Structs ```sx Vec3 :: struct { x, y, z: f32; length :: (self: *Vec3) -> f32 { sqrt(self.x * self.x + self.y * self.y + self.z * self.z); } } v := Vec3.{ x = 1, y = 2, z = 3 }; v2 := Vec3.{ 1, 2, 3 }; // positional print("{}\n", v.length()); ``` Structs support field defaults, `#using` for composition, and methods defined in the body. ### Enums (Tagged Unions) ```sx Shape :: enum { circle: f32; rect: struct { w, h: f32; }; none; } area :: (s: Shape) -> f32 { if s == { case .circle: (r) => 3.14159 * r * r; case .rect: (r) => r.w * r.h; case .none: 0; } } ``` Flag enums with power-of-2 values: ```sx Perms :: enum flags { read; write; execute; } rw := Perms.read | Perms.write; ``` ### Optionals ```sx x: ?s32 = 42; y: ?s32 = null; val := x ?? 0; // null coalescing forced := x!; // force unwrap (traps on null) if v := x { // safe unwrap print("{}\n", v); } // Optional chaining node: ?Node = get_node(); name := node?.name ?? "unknown"; ``` ### Generics ```sx max :: (a: $T, b: T) -> T { if a > b then a else b; } List :: struct ($T: Type) { items: [*]T; len: s64; append :: (self: *List(T), item: T) { ... } } ``` Generic constraints via protocols: ```sx are_equal :: ($T: Type/Eq, a: T, b: T) -> bool { a.eq(b); } ``` ### Closures ```sx make_adder :: (n: s64) -> Closure(s64) -> s64 { closure((x: s64) -> s64 => x + n); } add5 := make_adder(5); print("{}\n", add5(100)); // 105 ``` Closures capture by value. Bare functions auto-promote to closures when needed. ### Protocols ```sx Drawable :: protocol { draw :: (x: s32, y: s32); } impl Drawable for Circle { draw :: (self: *Circle, x: s32, y: s32) { ... } } shape : Drawable = xx my_circle; // type erasure via xx shape.draw(10, 20); // dynamic dispatch ``` `#inline` protocols store function pointers directly (no vtable indirection): ```sx Allocator :: protocol #inline { alloc :: (size: s64) -> *void; dealloc :: (ptr: *void); } ``` ### Pattern Matching ```sx // On enums if shape == { case .circle: (r) => print("radius: {}\n", r); case .rect: (r) => print("{}x{}\n", r.w, r.h); case .none: print("nothing\n"); } // On optionals if opt == { case .some: (val) => use(val); case .none: fallback(); } // On type categories (via Any) if type_of(val) == { case int: print("integer\n"); case string: print("string\n"); case struct: print("struct\n"); } ``` ### Control Flow ```sx // Chained comparisons if 0 <= x <= 100 { ... } // While while i < 10 { i += 1; } // For (arrays and slices) for items: (val) { print("{}\n", val); } for items: (val, idx) { print("[{}] = {}\n", idx, val); } // Defer f := open("file.txt"); defer close(f); // Multi-target assignment (atomic swap) a, b = b, a; ``` ### Pipe Operator ```sx result := data |> parse() |> transform() |> serialize(); // equivalent to: serialize(transform(parse(data))) ``` ### Compile-Time Execution ```sx // Evaluate at compile time FIBONACCI_10 :: #run fib(10); // Generate code at compile time #insert #run generate_lookup_table(); ``` ### C Interop Foreign functions: ```sx libc :: #library "c"; printf :: (fmt: [:0]u8, args: ..Any) -> s32 #foreign libc; write_fd :: (fd: s32, buf: [*]u8, count: u64) -> s64 #foreign libc "write"; ``` Direct C header import: ```sx #import c { #include "vendors/mylib/api.h"; #source "vendors/mylib/impl.c"; }; ``` ### Modules ```sx #import "modules/std.sx"; // flat import math :: #import "modules/math.sx"; // namespaced import ``` ### Implicit Context Every program gets an implicit `context` with a default allocator: ```sx // No boilerplate needed — context is auto-initialized main :: () { list := List(s64).create(); // uses context.allocator list.append(42); } // Override allocator for a scope push Context.{ allocator = my_arena } { do_work(); // all allocations use my_arena } ``` ## Quick Sort Example ```sx #import "modules/std.sx"; quick_sort :: (items: []$T) { partition :: (items: []T, lo: s64, hi: s64) -> s64 { pivot := items[hi]; i := lo - 1; j := lo; while j < hi { if items[j] < pivot { i += 1; items[i], items[j] = items[j], items[i]; } j += 1; } i += 1; items[i], items[hi] = items[hi], items[i]; i; } sort :: (items: []T, lo: s64, hi: s64) { if lo < hi { pi := partition(items, lo, hi); sort(items, lo, pi - 1); sort(items, pi + 1, hi); } } sort(items, 0, items.len - 1); } main :: () { arr : []s64 = .[333, 2, 3, 5, 2, 2, 3, 4, 5, 6, 6, 1]; quick_sort(arr); print("{}\n", arr); // [1, 2, 2, 2, 3, 3, 4, 5, 5, 6, 6, 333] } ``` ## Standard Library The standard library (`modules/std.sx`) provides: - **I/O**: `print(fmt, args...)`, `out(str)` - **Collections**: `List($T)` (dynamic array) - **Strings**: `concat`, `substr`, `int_to_string`, `float_to_string`, `cstring` - **Memory**: `Allocator` protocol, `GPA` (general purpose), `Arena` (bump allocator) - **Math**: `sqrt`, `sin`, `cos` - **Introspection**: `type_of`, `type_name`, `field_count`, `field_name`, `field_value`, `size_of` ## Cross-Compilation ```sh sx build app.sx --target linux # Linux x86_64 sx build app.sx --target macos-arm # macOS ARM64 sx build app.sx --target windows # Windows x86_64 sx build app.sx --target wasm # WebAssembly ``` ## Acknowledgments - [Jonathan Blow](https://en.wikipedia.org/wiki/Jonathan_Blow) for Jai, the language that inspired this one - [Andrew Kelley](https://andrewkelley.me) for Zig, which made this compiler a joy to write ## License MIT