diff --git a/readme.md b/readme.md index 1191178..24cff5b 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,322 @@ # sx -*** HIGHLY EXPERIMENTAL *** DON'T USE *** +An experimental systems programming language with Jai-inspired syntax, compile-time execution, generics, closures, protocols, and an LLVM backend. -This experiment is trying to answer a few questions: +> **Status**: Highly experimental. The language and compiler are under active development. -Q: Can we have an system language to build declarative ui ? +## At a Glance +```sx +#import "modules/std.sx"; -NOTE: -> i hope you have memory... currently it doesn't free anything :D +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 @@ -47,34 +355,35 @@ 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] } ``` -## Building +## Standard Library -Requires **Zig 0.16+** and **LLVM 19**. +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 -zig build -``` - -## Usage - -```sh -# compile to binary -sx build examples/06-generic.sx - -# compile and run -sx run examples/06-generic.sx - -# emit LLVM IR -sx ir examples/06-generic.sx - -# start the language server -sx lsp +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 +- [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