stdlib: relocate modules under library/

- examples/modules/ -> library/modules/ (top-level, no more
  symlink hacks in consumer projects)
- compiler discovers stdlib via _NSGetExecutablePath / readlink
  /proc/self/exe; searches dev layout (../../library), install
  layout (../library), and alongside-binary fallback
- SX_STDLIB_PATH env var overrides for tests / dev convenience
- SX_DEBUG_STDLIB env var dumps the discovery results
- build.zig installs library/ alongside the binary
- Compilation gains stdlib_paths field threaded through resolveImports
- 50 tests pass; consumer projects can now build from any cwd
This commit is contained in:
agra
2026-05-17 13:49:25 +03:00
parent 535e7b9c24
commit c027e1969b
26 changed files with 130 additions and 35 deletions

View File

@@ -0,0 +1,139 @@
#import "std.sx";
// --- Allocator protocol (inline: ctx + fn-ptrs, no vtable indirection) ---
Allocator :: protocol #inline {
alloc :: (size: s64) -> *void;
dealloc :: (ptr: *void);
}
// --- GPA: general purpose allocator (malloc/free wrapper) ---
GPA :: struct {
alloc_count: s64;
create :: (gpa: *GPA) -> Allocator {
xx gpa;
}
}
impl Allocator for GPA {
alloc :: (self: *GPA, size: s64) -> *void {
self.alloc_count += 1;
malloc(size);
}
dealloc :: (self: *GPA, ptr: *void) {
self.alloc_count -= 1;
free(ptr);
}
}
// --- Arena: multi-chunk bump allocator ---
ArenaChunk :: struct {
next: *ArenaChunk;
cap: s64;
}
Arena :: struct {
first: *ArenaChunk;
end_index: s64;
parent: Allocator;
add_chunk :: (a: *Arena, min_size: s64) {
prev_cap := if a.first != null then a.first.cap else 0;
needed := min_size + 16 + 16;
len := (prev_cap + needed) * 3 / 2;
raw := a.parent.alloc(len);
chunk : *ArenaChunk = xx raw;
chunk.next = a.first;
chunk.cap = len;
a.first = chunk;
a.end_index = 0;
}
create :: (a: *Arena, parent: Allocator, size: s64) -> Allocator {
a.first = null;
a.end_index = 0;
a.parent = parent;
a.add_chunk(size);
xx a;
}
reset :: (a: *Arena) {
if a.first != null {
it := a.first.next;
while it != null {
next := it.next;
a.parent.dealloc(it);
it = next;
}
a.first.next = null;
}
a.end_index = 0;
}
deinit :: (a: *Arena) {
it := a.first;
while it != null {
next := it.next;
a.parent.dealloc(it);
it = next;
}
a.first = null;
a.end_index = 0;
}
}
impl Allocator for Arena {
alloc :: (self: *Arena, size: s64) -> *void {
aligned := (size + 7) & (0 - 8);
if self.first != null {
usable := self.first.cap - 16;
if self.end_index + aligned <= usable {
buf : [*]u8 = xx self.first;
ptr := @buf[16 + self.end_index];
self.end_index = self.end_index + aligned;
return ptr;
}
}
self.add_chunk(aligned);
buf : [*]u8 = xx self.first;
ptr := @buf[16 + self.end_index];
self.end_index = self.end_index + aligned;
ptr;
}
dealloc :: (self: *Arena, ptr: *void) {}
}
// --- BufAlloc: bump allocator backed by a user-provided slice ---
BufAlloc :: struct {
buf: [*]u8;
len: s64;
pos: s64;
create :: (b: *BufAlloc, buf: [*]u8, len: s64) -> Allocator {
b.buf = buf;
b.len = len;
b.pos = 0;
xx b;
}
reset :: (b: *BufAlloc) {
b.pos = 0;
}
}
impl Allocator for BufAlloc {
alloc :: (self: *BufAlloc, size: s64) -> *void {
aligned := (size + 7) & (0 - 8);
if self.pos + aligned > self.len {
return null;
}
ptr := @self.buf[self.pos];
self.pos = self.pos + aligned;
ptr;
}
dealloc :: (self: *BufAlloc, ptr: *void) {}
}

View File

@@ -0,0 +1,15 @@
OperatingSystem :: enum { macos; linux; windows; wasm; ios; 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;
add_framework :: (self: BuildOptions, name: [:0]u8) #compiler;
set_output_path :: (self: BuildOptions, path: [:0]u8) #compiler;
set_wasm_shell :: (self: BuildOptions, path: [:0]u8) #compiler;
}
build_options :: () -> BuildOptions #compiler;

View File

@@ -0,0 +1,37 @@
PI :f32: 3.14159265;
TAU :f32: 6.28318530;
DEG2RAD :f32: 0.01745329;
RAD2DEG :f32: 57.2957795;
sqrt :: (x: $T) -> T #builtin;
sin :: (x: $T) -> T #builtin;
cos :: (x: $T) -> T #builtin;
floor :: (x: $T) -> T #builtin;
min :: (a: $T, b: T) -> T {
if a < b then a else b;
}
max :: (a: $T, b: T) -> T {
if a > b then a else b;
}
clamp :: (val: $T, lo: T, hi: T) -> T {
if val < lo then lo
else if val > hi then hi
else val;
}
abs :: (x: $T) -> T {
if x < 0 then 0 - x else x;
}
lerp :: (a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t;
}
sign :: (x: $T) -> T {
if x > 0 then 1
else if x < 0 then 0 - 1
else 0;
}

View File

@@ -0,0 +1,117 @@
// Column-major 4x4 float matrix (OpenGL convention)
// data[col * 4 + row]
Mat4 :: struct {
data: [16]f32;
identity :: () -> Mat4 {
Mat4.{ data = .[
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
]};
}
zero :: () -> Mat4 {
Mat4.{ data = .[
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0
]};
}
mul :: (self: Mat4, b: Mat4) -> Mat4 {
r := Mat4.zero();
col := 0;
while col < 4 {
row := 0;
while row < 4 {
sum : f32 = 0.0;
k := 0;
while k < 4 {
sum = sum + self.data[k * 4 + row] * b.data[col * 4 + k];
k += 1;
}
r.data[col * 4 + row] = sum;
row += 1;
}
col += 1;
}
r;
}
translate :: (x: f32, y: f32, z: f32) -> Mat4 {
m := Mat4.identity();
m.data[12] = x;
m.data[13] = y;
m.data[14] = z;
m;
}
scale :: (x: f32, y: f32, z: f32) -> Mat4 {
m := Mat4.zero();
m.data[0] = x;
m.data[5] = y;
m.data[10] = z;
m.data[15] = 1.0;
m;
}
rotate_x :: (angle: f32) -> Mat4 {
c := cos(angle);
s := sin(angle);
m := Mat4.identity();
m.data[5] = c;
m.data[6] = s;
m.data[9] = 0.0 - s;
m.data[10] = c;
m;
}
rotate_y :: (angle: f32) -> Mat4 {
c := cos(angle);
s := sin(angle);
m := Mat4.identity();
m.data[0] = c;
m.data[2] = 0.0 - s;
m.data[8] = s;
m.data[10] = c;
m;
}
rotate_z :: (angle: f32) -> Mat4 {
c := cos(angle);
s := sin(angle);
m := Mat4.identity();
m.data[0] = c;
m.data[1] = s;
m.data[4] = 0.0 - s;
m.data[5] = c;
m;
}
ortho :: (left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4 {
m := Mat4.zero();
m.data[0] = 2.0 / (right - left);
m.data[5] = 2.0 / (top - bottom);
m.data[10] = 0.0 - 2.0 / (far - near);
m.data[12] = 0.0 - (right + left) / (right - left);
m.data[13] = 0.0 - (top + bottom) / (top - bottom);
m.data[14] = 0.0 - (far + near) / (far - near);
m.data[15] = 1.0;
m;
}
perspective :: (fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
half_tan := sin(fov * 0.5) / cos(fov * 0.5);
m := Mat4.zero();
m.data[0] = 1.0 / (aspect * half_tan);
m.data[5] = 1.0 / half_tan;
m.data[10] = 0.0 - (far + near) / (far - near);
m.data[11] = 0.0 - 1.0;
m.data[14] = 0.0 - 2.0 * far * near / (far - near);
m;
}
}

View File

@@ -0,0 +1,49 @@
Vec2 :: struct {
x, y: f32;
zero :: () -> Vec2 { Vec2.{ x = 0.0, y = 0.0 }; }
add :: (self: Vec2, b: Vec2) -> Vec2 {
Vec2.{ x = self.x + b.x, y = self.y + b.y };
}
sub :: (self: Vec2, b: Vec2) -> Vec2 {
Vec2.{ x = self.x - b.x, y = self.y - b.y };
}
scale :: (self: Vec2, s: f32) -> Vec2 {
Vec2.{ x = self.x * s, y = self.y * s };
}
dot :: (self: Vec2, b: Vec2) -> f32 {
self.x * b.x + self.y * b.y;
}
length :: (self: Vec2) -> f32 {
sqrt(self.x * self.x + self.y * self.y);
}
normalize :: (self: Vec2) -> Vec2 {
len := self.length();
if len > 0.0 {
return Vec2.{ x = self.x / len, y = self.y / len };
}
Vec2.zero();
}
lerp :: (self: Vec2, b: Vec2, t: f32) -> Vec2 {
Vec2.{ x = self.x + (b.x - self.x) * t, y = self.y + (b.y - self.y) * t };
}
distance :: (self: Vec2, b: Vec2) -> f32 {
self.sub(b).length();
}
negate :: (self: Vec2) -> Vec2 {
Vec2.{ x = 0.0 - self.x, y = 0.0 - self.y };
}
equals :: (self: Vec2, b: Vec2) -> bool {
self.x == b.x and self.y == b.y;
}
}

184
library/modules/opengl.sx Normal file
View File

@@ -0,0 +1,184 @@
// OpenGL 3.3 Core — runtime-loaded function pointers
// No #library needed — caller provides a loader (e.g. SDL_GL_GetProcAddress)
// Constants
GL_FALSE :s32: 0;
GL_TRUE :s32: 1;
GL_DEPTH_TEST :u32: 0x0B71;
GL_CULL_FACE :u32: 0x0B44;
GL_BLEND :u32: 0x0BE2;
GL_TRIANGLES :u32: 4;
GL_LINES :u32: 1;
GL_FLOAT :u32: 0x1406;
GL_UNSIGNED_INT :u32: 0x1405;
GL_VERTEX_SHADER :u32: 0x8B31;
GL_FRAGMENT_SHADER :u32: 0x8B30;
GL_COMPILE_STATUS :u32: 0x8B81;
GL_LINK_STATUS :u32: 0x8B82;
GL_ARRAY_BUFFER :u32: 0x8892;
GL_ELEMENT_ARRAY_BUFFER :u32: 0x8893;
GL_STATIC_DRAW :u32: 0x88E4;
GL_COLOR_BUFFER_BIT :u32: 0x4000;
GL_DEPTH_BUFFER_BIT :u32: 0x0100;
GL_FRONT_AND_BACK :u32: 0x0408;
GL_LINE :u32: 0x1B01;
GL_FILL :u32: 0x1B02;
// Function pointer variables (mutable, loaded at runtime)
glClearColor : (f32, f32, f32, f32) -> void = ---;
glClear : (u32) -> void = ---;
glEnable : (u32) -> void = ---;
glDisable : (u32) -> void = ---;
glViewport : (s32, s32, s32, s32) -> void = ---;
glFlush : () -> void = ---;
glDrawArrays : (u32, s32, s32) -> void = ---;
glPolygonMode : (u32, u32) -> void = ---;
glLineWidth : (f32) -> void = ---;
glCreateShader : (u32) -> u32 = ---;
glShaderSource : (u32, s32, *[:0]u8, *s32) -> void = ---;
glCompileShader : (u32) -> void = ---;
glGetShaderiv : (u32, u32, *s32) -> void = ---;
glGetShaderInfoLog : (u32, s32, *s32, [*]u8) -> void = ---;
glCreateProgram : () -> u32 = ---;
glAttachShader : (u32, u32) -> void = ---;
glLinkProgram : (u32) -> void = ---;
glGetProgramiv : (u32, u32, *s32) -> void = ---;
glGetProgramInfoLog : (u32, s32, *s32, [*]u8) -> void = ---;
glUseProgram : (u32) -> void = ---;
glDeleteShader : (u32) -> void = ---;
glGenVertexArrays : (s32, *u32) -> void = ---;
glGenBuffers : (s32, *u32) -> void = ---;
glBindVertexArray : (u32) -> void = ---;
glBindBuffer : (u32, u32) -> void = ---;
glBufferData : (u32, isize, *void, u32) -> void = ---;
glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void = ---;
glEnableVertexAttribArray : (u32) -> void = ---;
glGetUniformLocation : (u32, [*]u8) -> s32 = ---;
glUniformMatrix4fv : (s32, s32, u8, [*]f32) -> void = ---;
glUniform3f : (s32, f32, f32, f32) -> void = ---;
glDepthFunc : (u32) -> void = ---;
glUniform1f : (s32, f32) -> void = ---;
GL_LESS :u32: 0x0201;
GL_LEQUAL :u32: 0x0203;
GL_SCISSOR_TEST :u32: 0x0C11;
GL_DYNAMIC_DRAW :u32: 0x88E8;
GL_TEXTURE_2D :u32: 0x0DE1;
GL_TEXTURE_MIN_FILTER :u32: 0x2801;
GL_TEXTURE_MAG_FILTER :u32: 0x2800;
GL_NEAREST :u32: 0x2600;
GL_RGBA :u32: 0x1908;
GL_UNSIGNED_BYTE :u32: 0x1401;
GL_SRC_ALPHA :u32: 0x0302;
GL_ONE_MINUS_SRC_ALPHA :u32: 0x0303;
GL_TEXTURE0 :u32: 0x84C0;
GL_LINEAR :u32: 0x2601;
GL_RED :u32: 0x1903;
GL_R8 :u32: 0x8229;
GL_UNPACK_ALIGNMENT :u32: 0x0CF5;
glScissor : (s32, s32, s32, s32) -> void = ---;
glBufferSubData : (u32, isize, isize, *void) -> void = ---;
glGenTextures : (s32, *u32) -> void = ---;
glBindTexture : (u32, u32) -> void = ---;
glTexImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void = ---;
glTexParameteri : (u32, u32, s32) -> void = ---;
glBlendFunc : (u32, u32) -> void = ---;
glReadPixels : (s32, s32, s32, s32, u32, u32, *void) -> void = ---;
glActiveTexture : (u32) -> void = ---;
glUniform1i : (s32, s32) -> void = ---;
glPixelStorei : (u32, s32) -> void = ---;
glTexSubImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void = ---;
glDeleteTextures : (s32, *u32) -> void = ---;
GL_TEXTURE_WRAP_S :u32: 0x2802;
GL_TEXTURE_WRAP_T :u32: 0x2803;
GL_CLAMP_TO_EDGE :u32: 0x812F;
// Loader: call once after creating GL context
// Pass in a proc loader (e.g. SDL_GL_GetProcAddress)
load_gl :: (get_proc: ([*]u8) -> *void) {
glClearColor = xx get_proc("glClearColor");
glClear = xx get_proc("glClear");
glEnable = xx get_proc("glEnable");
glDisable = xx get_proc("glDisable");
glViewport = xx get_proc("glViewport");
glFlush = xx get_proc("glFlush");
glDrawArrays = xx get_proc("glDrawArrays");
glPolygonMode = xx get_proc("glPolygonMode");
glLineWidth = xx get_proc("glLineWidth");
glCreateShader = xx get_proc("glCreateShader");
glShaderSource = xx get_proc("glShaderSource");
glCompileShader = xx get_proc("glCompileShader");
glGetShaderiv = xx get_proc("glGetShaderiv");
glGetShaderInfoLog = xx get_proc("glGetShaderInfoLog");
glCreateProgram = xx get_proc("glCreateProgram");
glAttachShader = xx get_proc("glAttachShader");
glLinkProgram = xx get_proc("glLinkProgram");
glGetProgramiv = xx get_proc("glGetProgramiv");
glGetProgramInfoLog = xx get_proc("glGetProgramInfoLog");
glUseProgram = xx get_proc("glUseProgram");
glDeleteShader = xx get_proc("glDeleteShader");
glGenVertexArrays = xx get_proc("glGenVertexArrays");
glGenBuffers = xx get_proc("glGenBuffers");
glBindVertexArray = xx get_proc("glBindVertexArray");
glBindBuffer = xx get_proc("glBindBuffer");
glBufferData = xx get_proc("glBufferData");
glVertexAttribPointer = xx get_proc("glVertexAttribPointer");
glEnableVertexAttribArray = xx get_proc("glEnableVertexAttribArray");
glGetUniformLocation = xx get_proc("glGetUniformLocation");
glUniformMatrix4fv = xx get_proc("glUniformMatrix4fv");
glUniform3f = xx get_proc("glUniform3f");
glDepthFunc = xx get_proc("glDepthFunc");
glUniform1f = xx get_proc("glUniform1f");
glScissor = xx get_proc("glScissor");
glBufferSubData = xx get_proc("glBufferSubData");
glGenTextures = xx get_proc("glGenTextures");
glBindTexture = xx get_proc("glBindTexture");
glTexImage2D = xx get_proc("glTexImage2D");
glTexParameteri = xx get_proc("glTexParameteri");
glBlendFunc = xx get_proc("glBlendFunc");
glReadPixels = xx get_proc("glReadPixels");
glActiveTexture = xx get_proc("glActiveTexture");
glUniform1i = xx get_proc("glUniform1i");
glPixelStorei = xx get_proc("glPixelStorei");
glTexSubImage2D = xx get_proc("glTexSubImage2D");
glDeleteTextures = xx get_proc("glDeleteTextures");
}
// --- Shader utilities ---
create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 {
vs := compile_shader(GL_VERTEX_SHADER, vert_src);
fs := compile_shader(GL_FRAGMENT_SHADER, frag_src);
prog := glCreateProgram();
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);
status : s32 = 0;
glGetProgramiv(prog, GL_LINK_STATUS, @status);
if status == GL_FALSE {
log_buf: [512]u8 = ---;
glGetProgramInfoLog(prog, 512, null, log_buf);
}
glDeleteShader(vs);
glDeleteShader(fs);
return prog;
}
compile_shader :: (shader_type : u32, source: [:0]u8) -> u32 {
shader := glCreateShader(shader_type);
glShaderSource(shader, 1, source, null);
glCompileShader(shader);
status : s32 = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, @status);
if status == GL_FALSE {
log_buf : [512]u8 = ---;
glGetShaderInfoLog(shader, 512, null, log_buf);
}
return shader;
}

17
library/modules/raylib.sx Normal file
View File

@@ -0,0 +1,17 @@
raylib :: #library "raylib";
Color :: struct {
r, g, b, a: u8;
}
Vector2 :: struct {
x, y: f32;
}
InitWindow :: (width: s32, height: s32, title: [:0]u8) -> void #foreign raylib;
CloseWindow :: () -> void #foreign raylib;
WindowShouldClose :: () -> bool #foreign raylib;
BeginDrawing :: () -> void #foreign raylib;
EndDrawing :: () -> void #foreign raylib;
ClearBackground :: (color: Color) -> void #foreign raylib;
DrawTriangle :: (v1: Vector2, v2: Vector2, v3: Vector2, color: Color) -> void #foreign raylib;

353
library/modules/sdl3.sx Normal file
View File

@@ -0,0 +1,353 @@
sdl3 :: #library "SDL3";
// SDL_InitFlags
SDL_INIT_VIDEO :u32: 0x20;
// SDL_WindowFlags
SDL_WINDOW_OPENGL :u64: 0x2;
SDL_WINDOW_RESIZABLE :u64: 0x20;
SDL_WINDOW_HIGH_PIXEL_DENSITY :u64: 0x2000;
// SDL_GLAttr (enum starting at 0)
SDL_GL_DOUBLEBUFFER :s32: 5;
SDL_GL_DEPTH_SIZE :s32: 6;
SDL_GL_CONTEXT_MAJOR_VERSION :s32: 17;
SDL_GL_CONTEXT_MINOR_VERSION :s32: 18;
SDL_GL_CONTEXT_FLAGS :s32: 19;
SDL_GL_CONTEXT_PROFILE_MASK :s32: 20;
// SDL_GLProfile
SDL_GL_CONTEXT_PROFILE_CORE :s32: 0x1;
SDL_GL_CONTEXT_PROFILE_ES :s32: 0x4;
// SDL_GLContextFlag
SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG :s32: 0x2;
// SDL_Keycode — virtual key codes (layout-dependent)
SDL_Keycode :: enum u32 {
// Common
unknown :: 0x00;
return_key :: 0x0D;
escape :: 0x1B;
backspace :: 0x08;
tab :: 0x09;
space :: 0x20;
delete_key :: 0x7F;
// Punctuation
exclaim :: 0x21;
double_quote :: 0x22;
hash :: 0x23;
dollar :: 0x24;
percent :: 0x25;
ampersand :: 0x26;
apostrophe :: 0x27;
leftparen :: 0x28;
rightparen :: 0x29;
asterisk :: 0x2A;
plus :: 0x2B;
comma :: 0x2C;
minus :: 0x2D;
period :: 0x2E;
slash :: 0x2F;
colon :: 0x3A;
semicolon :: 0x3B;
less :: 0x3C;
equals :: 0x3D;
greater :: 0x3E;
question :: 0x3F;
at :: 0x40;
leftbracket :: 0x5B;
backslash :: 0x5C;
rightbracket :: 0x5D;
caret :: 0x5E;
underscore :: 0x5F;
grave :: 0x60;
leftbrace :: 0x7B;
pipe :: 0x7C;
rightbrace :: 0x7D;
tilde :: 0x7E;
plusminus :: 0xB1;
// Numbers
key_0 :: 0x30;
key_1 :: 0x31;
key_2 :: 0x32;
key_3 :: 0x33;
key_4 :: 0x34;
key_5 :: 0x35;
key_6 :: 0x36;
key_7 :: 0x37;
key_8 :: 0x38;
key_9 :: 0x39;
// Letters
a :: 0x61;
b :: 0x62;
c :: 0x63;
d :: 0x64;
e :: 0x65;
f :: 0x66;
g :: 0x67;
h :: 0x68;
i :: 0x69;
j :: 0x6A;
k :: 0x6B;
l :: 0x6C;
m :: 0x6D;
n :: 0x6E;
o :: 0x6F;
p :: 0x70;
q :: 0x71;
r :: 0x72;
s :: 0x73;
t :: 0x74;
u :: 0x75;
v :: 0x76;
w :: 0x77;
x :: 0x78;
y :: 0x79;
z :: 0x7A;
// Function keys
f1 :: 0x4000003A;
f2 :: 0x4000003B;
f3 :: 0x4000003C;
f4 :: 0x4000003D;
f5 :: 0x4000003E;
f6 :: 0x4000003F;
f7 :: 0x40000040;
f8 :: 0x40000041;
f9 :: 0x40000042;
f10 :: 0x40000043;
f11 :: 0x40000044;
f12 :: 0x40000045;
f13 :: 0x40000068;
f14 :: 0x40000069;
f15 :: 0x4000006A;
f16 :: 0x4000006B;
f17 :: 0x4000006C;
f18 :: 0x4000006D;
f19 :: 0x4000006E;
f20 :: 0x4000006F;
f21 :: 0x40000070;
f22 :: 0x40000071;
f23 :: 0x40000072;
f24 :: 0x40000073;
// Navigation
capslock :: 0x40000039;
printscreen :: 0x40000046;
scrolllock :: 0x40000047;
pause :: 0x40000048;
insert :: 0x40000049;
home :: 0x4000004A;
pageup :: 0x4000004B;
end :: 0x4000004D;
pagedown :: 0x4000004E;
right :: 0x4000004F;
left :: 0x40000050;
down :: 0x40000051;
up :: 0x40000052;
// Keypad
numlock :: 0x40000053;
kp_divide :: 0x40000054;
kp_multiply :: 0x40000055;
kp_minus :: 0x40000056;
kp_plus :: 0x40000057;
kp_enter :: 0x40000058;
kp_1 :: 0x40000059;
kp_2 :: 0x4000005A;
kp_3 :: 0x4000005B;
kp_4 :: 0x4000005C;
kp_5 :: 0x4000005D;
kp_6 :: 0x4000005E;
kp_7 :: 0x4000005F;
kp_8 :: 0x40000060;
kp_9 :: 0x40000061;
kp_0 :: 0x40000062;
kp_period :: 0x40000063;
kp_equals :: 0x40000067;
kp_comma :: 0x40000085;
// Modifiers
lctrl :: 0x400000E0;
lshift :: 0x400000E1;
lalt :: 0x400000E2;
lgui :: 0x400000E3;
rctrl :: 0x400000E4;
rshift :: 0x400000E5;
ralt :: 0x400000E6;
rgui :: 0x400000E7;
mode :: 0x40000101;
// Editing
undo :: 0x4000007A;
cut :: 0x4000007B;
copy :: 0x4000007C;
paste :: 0x4000007D;
find :: 0x4000007E;
// Media
mute :: 0x4000007F;
volumeup :: 0x40000080;
volumedown :: 0x40000081;
media_play :: 0x40000106;
media_pause :: 0x40000107;
media_fast_forward :: 0x40000109;
media_rewind :: 0x4000010A;
media_next_track :: 0x4000010B;
media_previous_track :: 0x4000010C;
media_stop :: 0x4000010D;
media_eject :: 0x4000010E;
media_play_pause :: 0x4000010F;
// System
application :: 0x40000065;
power :: 0x40000066;
execute :: 0x40000074;
help :: 0x40000075;
menu :: 0x40000076;
select :: 0x40000077;
sleep :: 0x40000102;
wake :: 0x40000103;
}
// Event payload structs — match SDL3 layout from byte 4 onward (after the u32 type tag)
// Common header: reserved (u32), timestamp (u64)
SDL_WindowData :: struct {
timestamp: u64; // event time in nanoseconds
window_id: u32;
data1: s32; // event-dependent: x position for moved, width for resized
data2: s32; // event-dependent: y position for moved, height for resized
}
SDL_Keymod :: enum flags u16 {
lshift :: 0b0000_0000_0000_0001; // left Shift
rshift :: 0b0000_0000_0000_0010; // right Shift
level5 :: 0b0000_0000_0000_0100; // Level 5 Shift
lctrl :: 0b0000_0000_0100_0000; // left Ctrl
rctrl :: 0b0000_0000_1000_0000; // right Ctrl
lalt :: 0b0000_0001_0000_0000; // left Alt
ralt :: 0b0000_0010_0000_0000; // right Alt
lgui :: 0b0000_0100_0000_0000; // left GUI (Windows/Cmd key)
rgui :: 0b0000_1000_0000_0000; // right GUI (Windows/Cmd key)
num :: 0b0001_0000_0000_0000; // Num Lock
caps :: 0b0010_0000_0000_0000; // Caps Lock
mode :: 0b0100_0000_0000_0000; // AltGr
scroll :: 0b1000_0000_0000_0000; // Scroll Lock
}
SDL_KeyData :: struct {
timestamp: u64; // event time in nanoseconds
window_id: u32; // window with keyboard focus
which: u32; // keyboard instance id, 0 if unknown or virtual
scancode: u32; // physical key code (layout-independent)
key: SDL_Keycode; // virtual key code (layout-dependent)
mod: SDL_Keymod; // active modifier keys
raw: u16; // platform-specific scancode
down: u8; // 1 if pressed, 0 if released
repeat: u8; // 1 if this is a key repeat
}
SDL_MouseMotionData :: struct {
timestamp: u64; // event time in nanoseconds
window_id: u32; // window with mouse focus
which: u32; // mouse instance id, 0 for touch events
state: u32; // button state bitmask (bit 0 = left, 1 = middle, 2 = right)
x: f32; // x position relative to window
y: f32; // y position relative to window
xrel: f32; // relative motion in x
yrel: f32; // relative motion in y
}
SDL_MouseButtonData :: struct {
timestamp: u64; // event time in nanoseconds
window_id: u32; // window with mouse focus
which: u32; // mouse instance id, 0 for touch events
button: u8; // button index (1 = left, 2 = middle, 3 = right)
down: u8; // 1 if pressed, 0 if released
clicks: u8; // 1 for single-click, 2 for double-click, etc.
_: u8;
x: f32; // x position relative to window
y: f32; // y position relative to window
}
SDL_MouseWheelData :: struct {
timestamp: u64; // event time in nanoseconds
window_id: u32; // window with mouse focus
which: u32; // mouse instance id
x: f32; // horizontal scroll (positive = right)
y: f32; // vertical scroll (positive = away from user)
direction: u32; // 0 = normal, 1 = flipped (multiply by -1 to normalize)
mouse_x: f32; // mouse x position relative to window
mouse_y: f32; // mouse y position relative to window
}
SDL_Event :: enum struct { tag: u32; _: u32; payload: [30]u32; } {
none :: 0;
// Application
quit :: 0x100;
// Window
window_shown :: 0x202: SDL_WindowData;
window_hidden :: 0x203: SDL_WindowData;
window_exposed :: 0x204: SDL_WindowData;
window_moved :: 0x205: SDL_WindowData;
window_resized :: 0x206: SDL_WindowData;
window_minimized :: 0x209: SDL_WindowData;
window_maximized :: 0x20A: SDL_WindowData;
window_restored :: 0x20B: SDL_WindowData;
window_mouse_enter :: 0x20C: SDL_WindowData;
window_mouse_leave :: 0x20D: SDL_WindowData;
window_focus_gained :: 0x20E: SDL_WindowData;
window_focus_lost :: 0x20F: SDL_WindowData;
window_close_requested :: 0x210: SDL_WindowData;
window_destroyed :: 0x219: SDL_WindowData;
// Keyboard
key_down :: 0x300: SDL_KeyData;
key_up :: 0x301: SDL_KeyData;
// Mouse
mouse_motion :: 0x400: SDL_MouseMotionData;
mouse_button_down :: 0x401: SDL_MouseButtonData;
mouse_button_up :: 0x402: SDL_MouseButtonData;
mouse_wheel :: 0x403: SDL_MouseWheelData;
}
// Functions
SDL_Init :: (flags: u32) -> bool #foreign sdl3;
SDL_Quit :: () -> void #foreign sdl3;
SDL_CreateWindow :: (title: [:0]u8, w: s32, h: s32, flags: u64) -> *void #foreign sdl3;
SDL_DestroyWindow :: (window: *void) -> void #foreign sdl3;
SDL_GL_SetAttribute :: (attr: s32, value: s32) -> bool #foreign sdl3;
SDL_GL_CreateContext :: (window: *void) -> *void #foreign sdl3;
SDL_GL_DestroyContext :: (context: *void) -> bool #foreign sdl3;
SDL_GL_MakeCurrent :: (window: *void, context: *void) -> bool #foreign sdl3;
SDL_GL_SwapWindow :: (window: *void) -> bool #foreign sdl3;
SDL_GL_SetSwapInterval :: (interval: s32) -> bool #foreign sdl3;
SDL_GL_GetProcAddress :: (proc: [:0]u8) -> *void #foreign sdl3;
SDL_PollEvent :: (event: *SDL_Event) -> bool #foreign sdl3;
SDL_AddEventWatch :: (filter: *void, userdata: *void) -> bool #foreign sdl3;
SDL_GetTicks :: () -> u64 #foreign sdl3;
SDL_GetPerformanceCounter :: () -> u64 #foreign sdl3;
SDL_GetPerformanceFrequency :: () -> u64 #foreign sdl3;
SDL_Delay :: (ms: u32) -> void #foreign sdl3;
SDL_GetWindowDisplayScale :: (window: *void) -> f32 #foreign sdl3;
SDL_GetWindowSize :: (window: *void, w: *s32, h: *s32) -> bool #foreign sdl3;
SDL_SetWindowSize :: (window: *void, w: s32, h: s32) -> bool #foreign sdl3;
SDL_GetWindowSizeInPixels :: (window: *void, w: *s32, h: *s32) -> bool #foreign sdl3;
SDL_Rect :: struct {
x: s32;
y: s32;
w: s32;
h: s32;
}
SDL_GetPrimaryDisplay :: () -> u32 #foreign sdl3;
SDL_GetDisplayUsableBounds :: (display_id: u32, rect: *SDL_Rect) -> bool #foreign sdl3;

33
library/modules/socket.sx Normal file
View File

@@ -0,0 +1,33 @@
// POSIX socket module (macOS only)
// sockaddr_in layout and constants are platform-specific.
libc :: #library "c";
// POSIX socket API
socket :: (domain: s32, kind: s32, protocol: s32) -> s32 #foreign libc;
setsockopt :: (fd: s32, level: s32, optname: s32, optval: *s32, optlen: u32) -> s32 #foreign libc;
bind :: (fd: s32, addr: *SockAddr, addrlen: u32) -> s32 #foreign libc;
listen :: (fd: s32, backlog: s32) -> s32 #foreign libc;
accept :: (fd: s32, addr: *SockAddr, addrlen: *u32) -> s32 #foreign libc;
read :: (fd: s32, buf: [*]u8, count: usize) -> isize #foreign libc;
write :: (fd: s32, buf: [*]u8, count: usize) -> isize #foreign libc;
close :: (fd: s32) -> s32 #foreign libc;
// Constants (macOS)
AF_INET :s32: 2;
SOCK_STREAM :s32: 1;
SOL_SOCKET :s32: 0xFFFF;
SO_REUSEADDR :s32: 0x4;
// macOS sockaddr_in (16 bytes, has sin_len field)
SockAddr :: struct {
sin_len: u8;
sin_family: u8;
sin_port: u16;
sin_addr: u32 = 0;
sin_zero: u64 = 0;
}
htons :: (port: s64) -> u16 {
cast(u16) (((port & 0xFF) << 8) | ((port >> 8) & 0xFF));
}

7
library/modules/stb.sx Normal file
View File

@@ -0,0 +1,7 @@
#import c {
#include "vendors/stb_image/stb_image.h";
#source "vendors/stb_image/stb_image_impl.c";
#include "vendors/stb_image/stb_image_write.h";
#source "vendors/stb_image/stb_image_write_impl.c";
};

View File

@@ -0,0 +1,10 @@
#import c {
#include "vendors/stb_truetype/stb_truetype.h";
#source "vendors/stb_truetype/stb_truetype_impl.c";
#include "vendors/file_utils/file_utils.h";
#source "vendors/file_utils/file_utils.c";
#include "vendors/kb_text_shape/kbts_api.h";
#source "vendors/kb_text_shape/kb_text_shape_impl.c";
};

351
library/modules/std.sx Normal file
View File

@@ -0,0 +1,351 @@
Vector :: ($N: int, $T: Type) -> Type #builtin;
out :: (str: string) -> void #builtin;
// sqrt :: (x: $T) -> T #builtin;
// sin :: (x: $T) -> T #builtin;
// cos :: (x: $T) -> T #builtin;
size_of :: ($T: Type) -> s64 #builtin;
malloc :: (size: s64) -> *void #builtin;
memcpy :: (dst: *void, src: *void, size: s64) -> *void #builtin;
memset :: (dst: *void, val: s64, size: s64) -> void #builtin;
free :: (ptr: *void) -> void #builtin;
type_of :: (val: $T) -> Type #builtin;
type_name :: ($T: Type) -> string #builtin;
field_count :: ($T: Type) -> s64 #builtin;
field_name :: ($T: Type, idx: s64) -> string #builtin;
field_value :: (s: $T, idx: s64) -> Any #builtin;
is_flags :: ($T: Type) -> bool #builtin;
field_value_int :: ($T: Type, idx: s64) -> s64 #builtin;
field_index :: ($T: Type, val: T) -> s64 #builtin;
string :: []u8 #builtin;
#import "allocators.sx";
// --- Context ---
Context :: struct {
allocator: Allocator;
data: *void;
}
context : Context = ---;
// --- Slice & string allocation ---
cstring :: (size: s64) -> string {
raw := context.allocator.alloc(size + 1);
memset(raw, 0, size + 1);
s : string = ---;
s.ptr = xx raw;
s.len = size;
s;
}
alloc_slice :: ($T: Type, count: s64) -> []T {
raw := context.allocator.alloc(count * size_of(T));
memset(raw, 0, count * size_of(T));
s : []T = ---;
s.ptr = xx raw;
s.len = count;
s;
}
int_to_string :: (n: s64) -> string {
if n == 0 { return "0"; }
neg := n < 0;
v := if neg then 0 - n else n;
// Single pass: fill digits backwards into temp string, then substr
tmp := cstring(20);
i := 19;
while v > 0 {
tmp[i] = (v % 10) + 48;
v = v / 10;
i -= 1;
}
if neg { tmp[i] = 45; i -= 1; }
substr(tmp, i + 1, 20 - i - 1);
}
bool_to_string :: (b: bool) -> string {
if b then "true" else "false";
}
float_to_string :: (f: f64) -> string {
neg := f < 0.0;
v := if neg then 0.0 - f else f;
int_part := cast(s64) v;
frac := cast(s64) ((v - cast(f64) int_part) * 1000000.0);
if frac < 0 { frac = 0 - frac; }
istr := int_to_string(int_part);
fstr := int_to_string(frac);
il := istr.len;
fl := fstr.len;
prefix := if neg then 1 else 0;
total := prefix + il + 1 + 6;
buf := cstring(total);
pos := 0;
if neg { buf[0] = 45; pos = 1; }
memcpy(@buf[pos], istr.ptr, il);
pos = pos + il;
buf[pos] = 46;
pos += 1;
pad := 6 - fl;
memset(@buf[pos], 48, pad);
pos = pos + pad;
memcpy(@buf[pos], fstr.ptr, fl);
buf;
}
hex_group :: (buf: string, offset: s64, val: s64) {
i := offset + 3;
v := val;
while i >= offset {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
}
int_to_hex_string :: (n: s64) -> string {
if n == 0 { return "0"; }
// Split into four 16-bit groups for correct unsigned treatment
g0 := n % 65536;
if g0 < 0 { g0 = g0 + 65536; }
r1 := (n - g0) / 65536;
g1 := r1 % 65536;
if g1 < 0 { g1 = g1 + 65536; }
r2 := (r1 - g1) / 65536;
g2 := r2 % 65536;
if g2 < 0 { g2 = g2 + 65536; }
r3 := (r2 - g2) / 65536;
g3 := r3 % 65536;
if g3 < 0 { g3 = g3 + 65536; }
buf := cstring(16);
hex_group(buf, 0, g3);
hex_group(buf, 4, g2);
hex_group(buf, 8, g1);
hex_group(buf, 12, g0);
// Skip leading zeros (keep at least 1 digit)
start := 0;
while start < 15 {
if buf[start] != 48 { break; }
start += 1;
}
substr(buf, start, 16 - start);
}
concat :: (a: string, b: string) -> string {
al := a.len;
bl := b.len;
buf := cstring(al + bl);
memcpy(buf.ptr, a.ptr, al);
memcpy(@buf[al], b.ptr, bl);
buf;
}
substr :: (s: string, start: s64, len: s64) -> string {
buf := cstring(len);
memcpy(buf.ptr, @s[start], len);
buf;
}
struct_to_string :: (s: $T) -> string {
result := concat(type_name(T), "{");
i := 0;
while i < field_count(T) {
if i > 0 { result = concat(result, ", "); }
result = concat(result, field_name(T, i));
result = concat(result, ": ");
result = concat(result, any_to_string(field_value(s, i)));
i += 1;
}
concat(result, "}");
}
vector_to_string :: (v: $T) -> string {
result := "[";
i := 0;
while i < field_count(T) {
if i > 0 { result = concat(result, ", "); }
result = concat(result, any_to_string(field_value(v, i)));
i += 1;
}
concat(result, "]");
}
array_to_string :: (a: $T) -> string {
result := "[";
i := 0;
while i < field_count(T) {
if i > 0 { result = concat(result, ", "); }
result = concat(result, any_to_string(field_value(a, i)));
i += 1;
}
concat(result, "]");
}
slice_to_string :: (items: []$T) -> string {
result := "[";
i := 0;
while i < items.len {
if i > 0 { result = concat(result, ", "); }
result = concat(result, any_to_string(field_value(items, i)));
i += 1;
}
concat(result, "]");
}
pointer_to_string :: (p: $T) -> string {
addr : s64 = xx p;
if addr == 0 { "null"; } else {
concat(type_name(T), concat("@0x", int_to_hex_string(addr)));
}
}
flags_to_string :: (val: $T) -> string {
v := cast(s64) val;
result := "";
i := 0;
while i < field_count(T) {
fv := field_value_int(T, i);
if v & fv {
if result.len > 0 { result = concat(result, " | "); }
result = concat(result, concat(".", field_name(T, i)));
}
i += 1;
}
if result.len == 0 { result = "0"; }
result;
}
enum_to_string :: (u: $T) -> string {
if is_flags(T) { return flags_to_string(u); }
idx := field_index(T, u);
result := concat(".", field_name(T, idx));
payload := field_value(u, idx);
pstr := any_to_string(payload);
if pstr.len > 0 {
result = concat(result, concat("(", concat(pstr, ")")));
}
result;
}
any_to_string :: (val: Any) -> string {
result := "<?>";
type := type_of(val);
if type == {
case void: result = "";
case int: result = int_to_string(xx val);
case string: { s : string = xx val; result = s; }
case bool: result = bool_to_string(xx val);
case float: result = float_to_string(xx val);
case struct: result = struct_to_string(cast(type) val);
case enum: result = enum_to_string(cast(type) val);
case vector: result = vector_to_string(cast(type) val);
case array: result = array_to_string(cast(type) val);
case slice: result = slice_to_string(cast(type) val);
case pointer: result = pointer_to_string(cast(type) val);
case type: { s : string = xx val; result = s; }
}
result;
}
build_format :: (fmt: string) -> string {
code := "result := \"\"; ";
seg_start := 0;
i := 0;
arg_idx := 0;
while i < fmt.len {
if fmt[i] == 123 {
if i + 1 < fmt.len {
if fmt[i + 1] == 125 {
if i > seg_start {
code = concat(code, "result = concat(result, substr(fmt, ");
code = concat(code, int_to_string(seg_start));
code = concat(code, ", ");
code = concat(code, int_to_string(i - seg_start));
code = concat(code, ")); ");
}
code = concat(code, "result = concat(result, any_to_string(args[");
code = concat(code, int_to_string(arg_idx));
code = concat(code, "])); ");
arg_idx += 1;
i += 2;
seg_start = i;
} else if fmt[i + 1] == 123 {
code = concat(code, "result = concat(result, substr(fmt, ");
code = concat(code, int_to_string(seg_start));
code = concat(code, ", ");
code = concat(code, int_to_string(i - seg_start + 1));
code = concat(code, ")); ");
i += 2;
seg_start = i;
} else {
i += 1;
}
} else {
i += 1;
}
} else if fmt[i] == 125 {
if i + 1 < fmt.len {
if fmt[i + 1] == 125 {
code = concat(code, "result = concat(result, substr(fmt, ");
code = concat(code, int_to_string(seg_start));
code = concat(code, ", ");
code = concat(code, int_to_string(i - seg_start + 1));
code = concat(code, ")); ");
i += 2;
seg_start = i;
} else {
i += 1;
}
} else {
i += 1;
}
} else {
i += 1;
}
}
if seg_start < fmt.len {
code = concat(code, "result = concat(result, substr(fmt, ");
code = concat(code, int_to_string(seg_start));
code = concat(code, ", ");
code = concat(code, int_to_string(fmt.len - seg_start));
code = concat(code, ")); ");
}
code;
}
format :: ($fmt: string, args: ..Any) -> string {
#insert build_format(fmt);
#insert "result;";
}
print :: ($fmt: string, args: ..Any) {
#insert build_format(fmt);
#insert "out(result);";
}
List :: struct ($T: Type) {
items: [*]T = null;
len: s64 = 0;
cap: s64 = 0;
append :: (list: *List(T), item: T) {
if list.len >= list.cap {
new_cap := if list.cap == 0 then 4 else list.cap * 2;
new_items : [*]T = xx context.allocator.alloc(new_cap * size_of(T));
if list.len > 0 {
memcpy(new_items, list.items, list.len * size_of(T));
context.allocator.dealloc(list.items);
}
list.items = new_items;
list.cap = new_cap;
}
list.items[list.len] = item;
list.len += 1;
}
}

View File

@@ -0,0 +1,75 @@
// Obj-C runtime FFI primitives.
//
// `*void` stands in for the Obj-C `id`/`Class`/`SEL` types. There's no
// sx-level type alias yet, so naming discipline at call sites is the only
// thing keeping them apart.
//
// objc_msgSend has the standard ARM64 calling convention (no varargs path).
// Each call site must invoke through a function pointer of the *exact*
// argument and return shape. The idiom:
//
// msg_fn : (recv: *void, sel: *void, arg: [*]u8) -> *void = xx objc_msgSend;
// result := msg_fn(receiver, selector, c_string);
// On macOS libobjc is auto-loaded by libSystem; on iOS it isn't, so we
// link it explicitly. Foundation registers NSString etc. with the runtime,
// also auto-loaded on macOS and required as an explicit framework on iOS.
objc :: #library "objc";
#framework "Foundation";
objc_getClass :: (name: [*]u8) -> *void #foreign objc;
objc_lookUpClass :: (name: [*]u8) -> *void #foreign objc;
sel_registerName :: (name: [*]u8) -> *void #foreign objc;
class_createInstance :: (cls: *void, extra: usize) -> *void #foreign objc;
object_getClass :: (obj: *void) -> *void #foreign objc;
// Declared with the simplest non-variadic shape. Cast per call site.
objc_msgSend :: (recv: *void, sel: *void) -> *void #foreign objc;
// ─── Dynamic class registration ─────────────────────────────────────────
// Define a new Obj-C class at runtime: allocate the pair, attach methods +
// protocols, then finalize with `objc_registerClassPair`. The class is then
// usable via `class_createInstance` and Obj-C dispatch.
//
// IMPs (method implementations) are function pointers with the implicit
// Obj-C method shape: `(self: *void, _cmd: *void, ...args) -> ret` with
// `callconv(.c)` so they land args in the standard C registers.
//
// Method type encoding strings follow Apple's runtime DSL:
// v = void c = char/BOOL i = int l = long f = float d = double
// @ = id (object) : = SEL # = Class
// Return type comes first, then receiver (`@`), then `_cmd` (`:`), then args.
// Examples:
// "v@:" -> void method(id, SEL)
// "c@:" -> BOOL method(id, SEL)
// "@@:@" -> id method(id, SEL, id)
// "B@:@@" -> BOOL method(id, SEL, id, id)
objc_allocateClassPair :: (super: *void, name: [*]u8, extra: usize) -> *void #foreign objc;
class_addMethod :: (cls: *void, sel: *void, imp: *void, types: [*]u8) -> bool #foreign objc;
class_addProtocol :: (cls: *void, proto: *void) -> bool #foreign objc;
objc_getProtocol :: (name: [*]u8) -> *void #foreign objc;
objc_registerClassPair :: (cls: *void) -> void #foreign objc;
// Foundation C-API helpers (Foundation is already linked above via #framework).
// NSLog takes an NSString format; the variadic tail is not exposed here.
NSLog :: (fmt: *void) #foreign;
// ─── Convenience helpers ────────────────────────────────────────────────
// These hide the typed-fn-pointer cast for the most common shapes. They
// re-register selectors per call — if you're in a tight loop, cache the SEL.
// Wrap a C string in an autoreleased NSString.
ns_string :: (s: [*]u8) -> *void {
cls := objc_getClass("NSString".ptr);
sel := sel_registerName("stringWithUTF8String:".ptr);
fn_ptr : (*void, *void, [*]u8) -> *void = xx objc_msgSend;
return fn_ptr(cls, sel, s);
}
// View an NSString's bytes as a C string. The returned pointer's lifetime is
// tied to the NSString; don't free it.
c_string :: (ns: *void) -> [*]u8 {
sel := sel_registerName("UTF8String".ptr);
fn_ptr : (*void, *void) -> [*]u8 = xx objc_msgSend;
return fn_ptr(ns, sel);
}

View File

@@ -0,0 +1,17 @@
// UIKit framework bindings — iOS only.
//
// Consumers `#import "modules/std/uikit.sx";` and inherit the
// `#framework "UIKit"` link directive plus any C-API declarations exposed
// here. Obj-C class/method dispatch goes through `modules/std/objc.sx`
// which we re-import so users only need this one file.
#import "objc.sx";
#framework "UIKit";
// int UIApplicationMain(int argc, char *_Nullable argv[_Nonnull],
// NSString *_Nullable principalClassName,
// NSString *_Nullable delegateClassName);
//
// Blocks. Drives the iOS run loop. Normally never returns.
UIApplicationMain :: (argc: s32, argv: *void, principal_class: *void, delegate_class: *void) -> s32 #foreign;

5
library/modules/test.sx Normal file
View File

@@ -0,0 +1,5 @@
assert :: (condition: bool) {
if !condition {
out("assertion failed\n");
}
}

View File

@@ -0,0 +1,4 @@
#import c {
#include "vendors/test_c/test.h";
#source "vendors/test_c/test.c";
};

View File

@@ -0,0 +1,4 @@
// This file lives in modules/testpkg/ and imports std relative to its directory.
#import "../std.sx";
cwd_greet :: () -> string { format("cwd-import-ok"); }

View File

@@ -0,0 +1 @@
hello :: () -> string { "hello from testpkg"; }

View File

@@ -0,0 +1,2 @@
add :: (a: s32, b: s32) -> s32 { a + b; }
mul :: (a: s32, b: s32) -> s32 { a * b; }

4
library/modules/wasm.sx Normal file
View File

@@ -0,0 +1,4 @@
libc :: #library "c";
emscripten_set_main_loop :: (func: *void, fps: s32, sim_infinite: s32) #foreign libc;
emscripten_run_script_int :: (script: [:0]u8) -> s32 #foreign libc;