agra d415bcceaa mem: drop matchContextAllocCall — interp reaches real memory through libc
Comptime now runs the full Allocator-protocol dispatch chain — the
same IR codegen emits — instead of being short-circuited at lowering
by an AST pattern-match. `context.allocator.alloc(size)` flows
through the protocol thunk into `CAllocator.alloc → libc_malloc`,
returning a real host-libc pointer. The interp picks it up as a raw
`.int` Value and treats it as memory.

The pieces:

- `evalComptimeString` now uses the parent module instead of spinning
  up a fresh ct_module. The parent already has every type, protocol,
  impl, and thunk registered (Allocator, CAllocator, Context, the
  GPA/Tracker thunks), so the dispatch chain runs without a separate
  scan pass. The comptime function is appended to the parent module;
  it's `is_comptime` so codegen skips it.

- Interp gains raw-pointer paths:
  - `index_gep(.aggregate{.int data_ptr, .int len}, idx)` produces a
    new `.byte_ptr` (a new Value variant) — byte-granular pointer that
    `store` writes 1 byte through. Mirrors the existing heap_ptr
    semantics for the same op shape.
  - `index_gep(.int, idx)` returns `.int = p + idx` (byte-addressed).
  - `store(.int_ptr, val)` writes val's bytes via `@ptrFromInt`.
    Handles int (8B), float (8B), bool (1B), null_val (8B of zeros).
  - `store(.byte_ptr, val)` writes a single byte.
  - `marshalForeignArg` handles `.aggregate{.int data, .int len}` and
    `.byte_ptr` — both copy bytes into a null-terminated tmp buffer
    for the C-side call.
  - `asString` reads `len` bytes from a `.int` data field via
    `@ptrFromInt`.
  - `resolveFieldLoad` / `resolveFieldStore` reject field-pointer
    aggregates whose first field is a wide integer (would otherwise
    mis-trigger on a struct stored on the stack with an int pointer
    in field 0).

- `lowerFunction` / `lazyLowerFunction` / `synthesizeJniMainStub`
  bind `current_ctx_ref = &__sx_default_context` for every
  callconv(.c) sx entry — not just `isExportedEntryName`. The JNI
  stubs need this so `context.X` in the body resolves through
  current_ctx_ref now that the pattern-match is gone.

- `matchContextAllocCall` and its dispatch site are deleted.

11 JNI/ObjC `.ir` snapshots regen — the comptime function appended to
the parent module shifts string-pool indices. 153/153 example tests
pass, chess green on macOS / iOS sim / Android.
2026-05-25 10:57:38 +03:00
...
2026-02-12 10:13:36 +02:00
...
2026-02-18 16:18:31 +02:00
2026-02-17 16:57:12 +02:00
2026-05-17 13:19:08 +03:00
2026-02-09 18:07:41 +02:00
sm
2026-03-02 21:00:55 +02:00
2026-02-22 17:24:04 +02:00
2026-02-09 18:07:41 +02:00
2026-02-09 18:07:41 +02:00
...
2026-03-02 21:08:09 +02:00
2026-05-18 17:40:10 +03:00
2026-05-18 17:40:10 +03:00
2026-05-18 17:40:10 +03:00
2026-05-18 17:40:10 +03:00

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

#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+.

zig build

On macOS with Homebrew LLVM:

# default path: /opt/homebrew/opt/llvm@19
zig build

Custom LLVM path:

zig build -Dllvm-prefix=/path/to/llvm

Usage

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 <triple>   target platform (shortcuts: macos, linux, windows, wasm)
--opt <level>       optimization: none, less, default, aggressive
--cpu <name>        target CPU
-o <path>           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

// 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

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)

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:

Perms :: enum flags { read; write; execute; }
rw := Perms.read | Perms.write;

Optionals

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

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:

are_equal :: ($T: Type/Eq, a: T, b: T) -> bool { a.eq(b); }

Closures

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

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):

Allocator :: protocol #inline {
    alloc :: (size: s64) -> *void;
    dealloc :: (ptr: *void);
}

Pattern Matching

// 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

// 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

result := data |> parse() |> transform() |> serialize();
// equivalent to: serialize(transform(parse(data)))

Compile-Time Execution

// Evaluate at compile time
FIBONACCI_10 :: #run fib(10);

// Generate code at compile time
#insert #run generate_lookup_table();

C Interop

Foreign functions:

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:

#import c {
    #include "vendors/mylib/api.h";
    #source "vendors/mylib/impl.c";
};

Modules

#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:

// 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

#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

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

License

MIT

Description
No description provided
Readme MIT 65 MiB
Languages
Zig 98.9%
Shell 0.6%
C++ 0.4%