From eba94d3c533387e8f790e8416b823537a5a45f79 Mon Sep 17 00:00:00 2001 From: agra Date: Sun, 22 Feb 2026 13:22:29 +0200 Subject: [PATCH] red pill --- .gitignore | 1 + main.sx | 244 +++++++++++++++++++++++++++ modules/allocators.sx | 155 +++++++++++++++++ modules/math/math.sx | 5 + modules/math/matrix44.sx | 59 +++++++ modules/math/vector3.sx | 22 +++ modules/opengl.sx | 98 +++++++++++ modules/raylib.sx | 17 ++ modules/sdl3.sx | 334 +++++++++++++++++++++++++++++++++++++ modules/socket.sx | 33 ++++ modules/std.sx | 352 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 1320 insertions(+) create mode 100644 .gitignore create mode 100644 main.sx create mode 100644 modules/allocators.sx create mode 100644 modules/math/math.sx create mode 100644 modules/math/matrix44.sx create mode 100644 modules/math/vector3.sx create mode 100644 modules/opengl.sx create mode 100644 modules/raylib.sx create mode 100644 modules/sdl3.sx create mode 100644 modules/socket.sx create mode 100644 modules/std.sx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..45337d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.sx-cache \ No newline at end of file diff --git a/main.sx b/main.sx new file mode 100644 index 0000000..c307a4a --- /dev/null +++ b/main.sx @@ -0,0 +1,244 @@ +#import "modules/std.sx"; +#import "modules/sdl3.sx"; +#import "modules/opengl.sx"; +#import "modules/math"; + + +WIDTH :f32: 800; +HEIGHT :f32: 600; + +main :: () { + print("init video: \n"); + SDL_Init(SDL_INIT_VIDEO); + + print("init opengl: \n"); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + + print("init opengl profile: \n"); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + print("create window: \n"); + window := SDL_CreateWindow("SX Game", xx WIDTH, xx HEIGHT, SDL_WINDOW_OPENGL); + gl_ctx := SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, gl_ctx); + SDL_GL_SetSwapInterval(1); + + print("load gl: \n"); + load_gl(SDL_GL_GetProcAddress); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + print("create program: {}\n{}\n", VERT_SHADER_SRC, FRAG_SHADER_SRC); + program := create_program(VERT_SHADER_SRC, FRAG_SHADER_SRC); + glUseProgram(program); + + print("uniform locations: \n"); + mvp_loc := glGetUniformLocation(program, "uMVP"); + light_loc := glGetUniformLocation(program, "uLightDir"); + wire_loc := glGetUniformLocation(program, "uWire"); + + print("vertices\n"); + // Cube vertices: pos(vec4 w=1) + normal(vec4 w=0), 36 vertices × 2 vec4s = 72 + vertices : []Vector(4, f32) = .[ + // Front face (z = +0.5) + .[-0.5, -0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0], + .[ 0.5, -0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0], + .[ 0.5, 0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0], + .[-0.5, -0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0], + .[ 0.5, 0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0], + .[-0.5, 0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0], + // Back face (z = -0.5) + .[ 0.5, -0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0], + .[-0.5, -0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0], + .[-0.5, 0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0], + .[ 0.5, -0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0], + .[-0.5, 0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0], + .[ 0.5, 0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0], + // Top face (y = +0.5) + .[-0.5, 0.5, 0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0], + .[ 0.5, 0.5, 0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0], + .[ 0.5, 0.5, -0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0], + .[-0.5, 0.5, 0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0], + .[ 0.5, 0.5, -0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0], + .[-0.5, 0.5, -0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0], + // Bottom face (y = -0.5) + .[-0.5, -0.5, -0.5, 1.0], .[0.0, -1.0, 0.0, 0.0], + .[ 0.5, -0.5, -0.5, 1.0], .[0.0, -1.0, 0.0, 0.0], + .[ 0.5, -0.5, 0.5, 1.0], .[0.0, -1.0, 0.0, 0.0], + .[-0.5, -0.5, -0.5, 1.0], .[0.0, -1.0, 0.0, 0.0], + .[ 0.5, -0.5, 0.5, 1.0], .[0.0, -1.0, 0.0, 0.0], + .[-0.5, -0.5, 0.5, 1.0], .[0.0, -1.0, 0.0, 0.0], + // Right face (x = +0.5) + .[ 0.5, -0.5, 0.5, 1.0], .[1.0, 0.0, 0.0, 0.0], + .[ 0.5, -0.5, -0.5, 1.0], .[1.0, 0.0, 0.0, 0.0], + .[ 0.5, 0.5, -0.5, 1.0], .[1.0, 0.0, 0.0, 0.0], + .[ 0.5, -0.5, 0.5, 1.0], .[1.0, 0.0, 0.0, 0.0], + .[ 0.5, 0.5, -0.5, 1.0], .[1.0, 0.0, 0.0, 0.0], + .[ 0.5, 0.5, 0.5, 1.0], .[1.0, 0.0, 0.0, 0.0], + // Left face (x = -0.5) + .[-0.5, -0.5, -0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0], + .[-0.5, -0.5, 0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0], + .[-0.5, 0.5, 0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0], + .[-0.5, -0.5, -0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0], + .[-0.5, 0.5, 0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0], + .[-0.5, 0.5, -0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0] + ]; + + print("cube buffer: \n"); + vao : u32 = 0; + vbo : u32 = 0; + glGenVertexArrays(1, @vao); + glGenBuffers(1, @vbo); + + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, 1152, @vertices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 32, xx 0); + glEnableVertexAttribArray(0); + + glVertexAttribPointer(1, 3, GL_FLOAT, 0, 32, xx 16); + glEnableVertexAttribArray(1); + + glUniform3f(light_loc, 0.5, 0.7, 1.0); + glUniform1f(wire_loc, 0.0); + + running := true; + event : SDL_Event = .none; + + print("loop: \n"); + while running { + while SDL_PollEvent(event) { + if event == { + case .quit: running = false; + case .key_up: (e) { + if e.key == { + case .escape: running = false; + } + } + } + } + + ticks := SDL_GetTicks(); + ms : f32 = xx ticks; + angle := ms * 0.001; + + proj := mat4_perspective(PI/ 4.0, WIDTH / HEIGHT, 0.1, 100.0); + view := mat4_translate(0.0, 0.0, -3.0); + rot_y := mat4_rotate_y(angle); + rot_x := mat4_rotate_x(angle * 0.7); + model := mat4_multiply(rot_y, rot_x); + vm := mat4_multiply(view, model); + mvp := mat4_multiply(proj, vm); + + glUniformMatrix4fv(mvp_loc, 1, 0, mvp.data); + + glClearColor(0.1, 0.1, 0.15, 1.0); + glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT); + + glUniform1f(wire_loc, 0.0); + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, 36); + + glDepthFunc(GL_LEQUAL); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glLineWidth(2.0); + glUniform1f(wire_loc, 1.0); + glDrawArrays(GL_TRIANGLES, 0, 36); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDepthFunc(GL_LESS); + glUniform1f(wire_loc, 0.0); + + print("{}\n", ms); + + SDL_GL_SwapWindow(window); + } + + SDL_GL_DestroyContext(gl_ctx); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +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); + print("error program link\n"); + } + + glDeleteShader(vs); + glDeleteShader(fs); + return prog; +} + +compile_shader :: (shader_type : u32, source: [:0]u8) -> u32 { + shader : u32 = 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); + print("error compile shader\n"); + } + return shader; +} + +VERT_SHADER_SRC : string = #string GLSL +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +uniform mat4 uMVP; +out vec3 vNormal; +out vec3 vPos; + +void main() { + gl_Position = uMVP * vec4(aPos, 1.0); + vNormal = aNormal; + vPos = aPos; +} +GLSL; + +FRAG_SHADER_SRC : string = #string GLSL +#version 330 core +in vec3 vNormal; +in vec3 vPos; +out vec4 FragColor; +uniform vec3 uLightDir; +uniform float uWire; +void main() { + if (uWire > 0.5) { + FragColor = vec4(0.05, 0.05, 0.05, 1.0); + return; + } + vec3 n = normalize(vNormal); + vec3 l = normalize(uLightDir); + float diff = max(dot(n, l), 0.15); + float cx = floor(vPos.x * 2.0 + 0.001); + float cy = floor(vPos.y * 2.0 + 0.001); + float cz = floor(vPos.z * 2.0 + 0.001); + float check = mod(cx + cy + cz, 2.0); + vec3 col1 = vec3(0.9, 0.5, 0.2); + vec3 col2 = vec3(0.2, 0.6, 0.9); + vec3 base = mix(col1, col2, check); + FragColor = vec4(base * diff, 1.0); +} +GLSL; diff --git a/modules/allocators.sx b/modules/allocators.sx new file mode 100644 index 0000000..1cf3b84 --- /dev/null +++ b/modules/allocators.sx @@ -0,0 +1,155 @@ +#import "std.sx"; + +// --- Allocator protocol --- + +Allocator :: struct { + ctx: *void; + alloc_fn: (*void, s64) -> *void; + free_fn: (*void, *void) -> void; +} + +allocator_alloc :: (a: Allocator, size: s64) -> *void { + a.alloc_fn(a.ctx, size); +} + +allocator_dealloc :: (a: Allocator, ptr: *void) { + a.free_fn(a.ctx, ptr); +} + +alloc :: ufcs allocator_alloc; +dealloc :: ufcs allocator_dealloc; + +// --- GPA: general purpose allocator (malloc/free wrapper) --- + +GPA :: struct { + alloc_count: s64; +} + +gpa_alloc :: (ctx: *void, size: s64) -> *void { + gpa : *GPA = xx ctx; + gpa.alloc_count += 1; + malloc(size); +} + +gpa_free :: (ctx: *void, ptr: *void) { + gpa : *GPA = xx ctx; + gpa.alloc_count -= 1; + free(ptr); +} + +gpa_create :: (gpa: *GPA) -> Allocator { + Allocator.{ ctx = gpa, alloc_fn = gpa_alloc, free_fn = gpa_free }; +} + +// --- Arena: multi-chunk bump allocator (Zig-inspired) --- + +ArenaChunk :: struct { + next: *ArenaChunk; + cap: s64; +} + +Arena :: struct { + first: *ArenaChunk; + end_index: s64; + parent: Allocator; +} + +arena_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; +} + +arena_alloc :: (ctx: *void, size: s64) -> *void { + a : *Arena = xx ctx; + aligned := (size + 7) & (0 - 8); + if a.first != null { + usable := a.first.cap - 16; + if a.end_index + aligned <= usable { + buf : [*]u8 = xx a.first; + ptr := @buf[16 + a.end_index]; + a.end_index = a.end_index + aligned; + return ptr; + } + } + arena_add_chunk(a, aligned); + buf : [*]u8 = xx a.first; + ptr := @buf[16 + a.end_index]; + a.end_index = a.end_index + aligned; + ptr; +} + +arena_free :: (ctx: *void, ptr: *void) { +} + +arena_create :: (a: *Arena, parent: Allocator, size: s64) -> Allocator { + a.first = null; + a.end_index = 0; + a.parent = parent; + arena_add_chunk(a, size); + Allocator.{ ctx = a, alloc_fn = arena_alloc, free_fn = arena_free }; +} + +arena_reset :: (a: *Arena) { + // Keep first chunk (newest/largest), free the rest + 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; +} + +arena_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; +} + +// --- BufAlloc: bump allocator backed by a user-provided slice --- + +BufAlloc :: struct { + buf: [*]u8; + len: s64; + pos: s64; +} + +buf_alloc :: (ctx: *void, size: s64) -> *void { + b : *BufAlloc = xx ctx; + aligned := (size + 7) & (0 - 8); + if b.pos + aligned > b.len { + return null; + } + ptr := @b.buf[b.pos]; + b.pos = b.pos + aligned; + ptr; +} + +buf_free :: (ctx: *void, ptr: *void) { +} + +buf_create :: (b: *BufAlloc, buf: [*]u8, len: s64) -> Allocator { + b.buf = buf; + b.len = len; + b.pos = 0; + Allocator.{ ctx = b, alloc_fn = buf_alloc, free_fn = buf_free }; +} + +buf_reset :: (b: *BufAlloc) { + b.pos = 0; +} diff --git a/modules/math/math.sx b/modules/math/math.sx new file mode 100644 index 0000000..c341b0f --- /dev/null +++ b/modules/math/math.sx @@ -0,0 +1,5 @@ +PI :f32: 3.14159265; + +sqrt :: (x: $T) -> T #builtin; +sin :: (x: $T) -> T #builtin; +cos :: (x: $T) -> T #builtin; diff --git a/modules/math/matrix44.sx b/modules/math/matrix44.sx new file mode 100644 index 0000000..300e8d4 --- /dev/null +++ b/modules/math/matrix44.sx @@ -0,0 +1,59 @@ +#import "math.sx"; + +Matrix44 :: union { + data: [16]f32; + struct { c0, c1, c2, c3: Vector(4, f32); }; +} + +mat4_perspective :: (fov: f32, aspect: f32, near: f32, far: f32) -> Matrix44 { + half := fov / 2.0; + f := cos(half) / sin(half); + m : Matrix44 = ---; + m.c0 = .[f / aspect, 0.0, 0.0, 0.0]; + m.c1 = .[0.0, f, 0.0, 0.0]; + m.c2 = .[0.0, 0.0, (far + near) / (near - far), -1.0]; + m.c3 = .[0.0, 0.0, (2.0 * far * near) / (near - far), 0.0]; + return m; +} + +mat4_translate :: (tx: f32, ty: f32, tz: f32) -> Matrix44 { + m : Matrix44 = ---; + m.c0 = .[1.0, 0.0, 0.0, 0.0]; + m.c1 = .[0.0, 1.0, 0.0, 0.0]; + m.c2 = .[0.0, 0.0, 1.0, 0.0]; + m.c3 = .[tx, ty, tz, 1.0]; + return m; +} + +mat4_rotate_y :: (angle: f32) -> Matrix44 { + c := cos(angle); + s := sin(angle); + m : Matrix44 = ---; + m.c0 = .[c, 0.0, 0.0 - s, 0.0]; + m.c1 = .[0.0, 1.0, 0.0, 0.0]; + m.c2 = .[s, 0.0, c, 0.0]; + m.c3 = .[0.0, 0.0, 0.0, 1.0]; + return m; +} + +mat4_rotate_x :: (angle: f32) -> Matrix44 { + c := cos(angle); + s := sin(angle); + m : Matrix44 = ---; + m.c0 = .[1.0, 0.0, 0.0, 0.0]; + m.c1 = .[0.0, c, s, 0.0]; + m.c2 = .[0.0, 0.0 - s, c, 0.0]; + m.c3 = .[0.0, 0.0, 0.0, 1.0]; + m; +} + + +mat4_multiply :: (a: *Matrix44, b: *Matrix44) -> Matrix44 { + out: Matrix44 = ---; + out.c0 = a.c0 * b.c0.x + a.c1 * b.c0.y + a.c2 * b.c0.z + a.c3 * b.c0.w; + out.c1 = a.c0 * b.c1.x + a.c1 * b.c1.y + a.c2 * b.c1.z + a.c3 * b.c1.w; + out.c2 = a.c0 * b.c2.x + a.c1 * b.c2.y + a.c2 * b.c2.z + a.c3 * b.c2.w; + out.c3 = a.c0 * b.c3.x + a.c1 * b.c3.y + a.c2 * b.c3.z + a.c3 * b.c3.w; + return out; +} +multiply :: ufcs mat4_multiply; diff --git a/modules/math/vector3.sx b/modules/math/vector3.sx new file mode 100644 index 0000000..3a423ee --- /dev/null +++ b/modules/math/vector3.sx @@ -0,0 +1,22 @@ +#import "math.sx"; + +vec3_dot :: (a: Vector(3,f32), b: Vector(3,f32)) -> f32 { + return a.x*b.x + a.y*b.y + a.z*b.z; +} +dot :: ufcs vec3_dot; + +vec3_cross :: (a: Vector(3,f32), b: Vector(3,f32)) -> Vector(3,f32) { + .[a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x]; +} +cross :: ufcs vec3_cross; + +vec3_length :: (v: Vector(3,f32)) -> f32 { + return sqrt(dot(v, v)); +} +length :: ufcs vec3_length; + + +vec3_normal :: (v: Vector(3,f32)) -> Vector(3,f32) { + return v / length(v); +} +normal :: ufcs vec3_normal; diff --git a/modules/opengl.sx b/modules/opengl.sx new file mode 100644 index 0000000..06b0878 --- /dev/null +++ b/modules/opengl.sx @@ -0,0 +1,98 @@ +// 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 = ---; +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, s64, *void, u32) -> void = ---; +glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void = ---; +glEnableVertexAttribArray : (u32) -> void = ---; +glGetUniformLocation : (u32, [:0]u8) -> s32 = ---; +glUniformMatrix4fv : (s32, s32, u8, [16]f32) -> void = ---; +glUniform3f : (s32, f32, f32, f32) -> void = ---; +glDepthFunc : (u32) -> void = ---; +glUniform1f : (s32, f32) -> void = ---; +GL_LESS :u32: 0x0201; +GL_LEQUAL :u32: 0x0203; + +// Loader: call once after creating GL context +// Pass in a proc loader (e.g. SDL_GL_GetProcAddress) +load_gl :: (get_proc: ([:0]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"); + 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"); +} diff --git a/modules/raylib.sx b/modules/raylib.sx new file mode 100644 index 0000000..80e0d3d --- /dev/null +++ b/modules/raylib.sx @@ -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; diff --git a/modules/sdl3.sx b/modules/sdl3.sx new file mode 100644 index 0000000..6a3f612 --- /dev/null +++ b/modules/sdl3.sx @@ -0,0 +1,334 @@ +sdl3 :: #library "SDL3"; + +// SDL_InitFlags +SDL_INIT_VIDEO :u32: 0x20; + +// SDL_WindowFlags +SDL_WINDOW_OPENGL :u64: 0x2; + +// 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_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_GetTicks :: () -> u64 #foreign sdl3; +SDL_Delay :: (ms: u32) -> void #foreign sdl3; +SDL_GetError :: () -> [*]u8 #foreign sdl3; diff --git a/modules/socket.sx b/modules/socket.sx new file mode 100644 index 0000000..ebc0eab --- /dev/null +++ b/modules/socket.sx @@ -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: s64) -> s64 #foreign libc; +write :: (fd: s32, buf: [*]u8, count: s64) -> s64 #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)); +} diff --git a/modules/std.sx b/modules/std.sx new file mode 100644 index 0000000..8eb4bd4 --- /dev/null +++ b/modules/std.sx @@ -0,0 +1,352 @@ +Vector :: ($N: int, $T: Type) -> Type #builtin; +out :: (str: string) -> void #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 --- + +context_alloc :: (size: s64) -> *void { + if context.allocator.ctx != null then context.allocator.alloc(size) else malloc(size); +} + +cstring :: (size: s64) -> string { + raw := context_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_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 malloc(new_cap * size_of(T)); + if list.len > 0 { + memcpy(new_items, list.items, list.len * size_of(T)); + free(list.items); + } + list.items = new_items; + list.cap = new_cap; + } + list.items[list.len] = item; + list.len += 1; +} \ No newline at end of file