sdl phase 1
This commit is contained in:
294
examples/28-sdl-graphics.sx
Normal file
294
examples/28-sdl-graphics.sx
Normal file
@@ -0,0 +1,294 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/sdl3.sx";
|
||||
#import "modules/opengl.sx";
|
||||
|
||||
PI :f32: 3.14159265;
|
||||
|
||||
// ---- Matrix math (column-major [16]f32, passed as [*]f32) ----
|
||||
|
||||
mat4_zero :: (m: [*]f32) {
|
||||
i := 0;
|
||||
while i < 16 { m[i] = 0.0; i += 1; }
|
||||
}
|
||||
|
||||
mat4_identity :: (m: [*]f32) {
|
||||
mat4_zero(m);
|
||||
m[0] = 1.0; m[5] = 1.0; m[10] = 1.0; m[15] = 1.0;
|
||||
}
|
||||
|
||||
mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) {
|
||||
tmp : [16]f32 = ---;
|
||||
j := 0;
|
||||
while j < 4 {
|
||||
i := 0;
|
||||
while i < 4 {
|
||||
sum : f32 = 0.0;
|
||||
k := 0;
|
||||
while k < 4 {
|
||||
sum = sum + a[k * 4 + i] * b[j * 4 + k];
|
||||
k += 1;
|
||||
}
|
||||
tmp[j * 4 + i] = sum;
|
||||
i += 1;
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
i := 0;
|
||||
while i < 16 { out[i] = tmp[i]; i += 1; }
|
||||
}
|
||||
|
||||
mat4_perspective :: (m: [*]f32, fov: f32, aspect: f32, near: f32, far: f32) {
|
||||
mat4_zero(m);
|
||||
half := fov / 2.0;
|
||||
f := cos(half) / sin(half);
|
||||
m[0] = f / aspect;
|
||||
m[5] = f;
|
||||
m[10] = (far + near) / (near - far);
|
||||
m[11] = -1.0;
|
||||
m[14] = (2.0 * far * near) / (near - far);
|
||||
}
|
||||
|
||||
mat4_rotate_y :: (m: [*]f32, angle: f32) {
|
||||
mat4_zero(m);
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m[0] = c;
|
||||
m[2] = 0.0 - s;
|
||||
m[5] = 1.0;
|
||||
m[8] = s;
|
||||
m[10] = c;
|
||||
m[15] = 1.0;
|
||||
}
|
||||
|
||||
mat4_rotate_x :: (m: [*]f32, angle: f32) {
|
||||
mat4_zero(m);
|
||||
c := cos(angle);
|
||||
s := sin(angle);
|
||||
m[0] = 1.0;
|
||||
m[5] = c;
|
||||
m[6] = s;
|
||||
m[9] = 0.0 - s;
|
||||
m[10] = c;
|
||||
m[15] = 1.0;
|
||||
}
|
||||
|
||||
mat4_translate :: (m: [*]f32, tx: f32, ty: f32, tz: f32) {
|
||||
mat4_identity(m);
|
||||
m[12] = tx;
|
||||
m[13] = ty;
|
||||
m[14] = tz;
|
||||
}
|
||||
|
||||
// ---- Shader helpers ----
|
||||
|
||||
compile_shader :: (shader_type: u32, source: [:0]u8) -> u32 {
|
||||
shader : u32 = glCreateShader(shader_type);
|
||||
src_ptr : *void = xx &source[0];
|
||||
glShaderSource(shader, 1, xx &src_ptr, null);
|
||||
glCompileShader(shader);
|
||||
|
||||
status : s32 = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, xx &status);
|
||||
if status == GL_FALSE {
|
||||
log_buf : [512]u8 = ---;
|
||||
glGetShaderInfoLog(shader, 512, null, xx &log_buf[0]);
|
||||
print("Shader compile error\n");
|
||||
}
|
||||
shader;
|
||||
}
|
||||
|
||||
create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 {
|
||||
vs : u32 = compile_shader(GL_VERTEX_SHADER, vert_src);
|
||||
fs : u32 = compile_shader(GL_FRAGMENT_SHADER, frag_src);
|
||||
|
||||
prog : u32 = glCreateProgram();
|
||||
glAttachShader(prog, vs);
|
||||
glAttachShader(prog, fs);
|
||||
glLinkProgram(prog);
|
||||
|
||||
status : s32 = 0;
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, xx &status);
|
||||
if status == GL_FALSE {
|
||||
log_buf : [512]u8 = ---;
|
||||
glGetProgramInfoLog(prog, 512, null, xx &log_buf[0]);
|
||||
print("Program link error\n");
|
||||
}
|
||||
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
prog;
|
||||
}
|
||||
|
||||
// ---- Main ----
|
||||
|
||||
main :: () {
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
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);
|
||||
|
||||
window := SDL_CreateWindow("sx GL cube", 800, 600, SDL_WINDOW_OPENGL);
|
||||
gl_ctx := SDL_GL_CreateContext(window);
|
||||
SDL_GL_MakeCurrent(window, gl_ctx);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
load_gl(SDL_GL_GetProcAddress);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Shaders
|
||||
vert_src : [:0]u8 = "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\nuniform mat4 uMVP;\nout vec3 vNormal;\nout vec3 vPos;\nvoid main() {\n gl_Position = uMVP * vec4(aPos, 1.0);\n vNormal = aNormal;\n vPos = aPos;\n}\n";
|
||||
|
||||
frag_src : [:0]u8 = "#version 330 core\nin vec3 vNormal;\nin vec3 vPos;\nout vec4 FragColor;\nuniform vec3 uLightDir;\nuniform float uWire;\nvoid main() {\n if (uWire > 0.5) {\n FragColor = vec4(0.05, 0.05, 0.05, 1.0);\n return;\n }\n vec3 n = normalize(vNormal);\n vec3 l = normalize(uLightDir);\n float diff = max(dot(n, l), 0.15);\n float cx = floor(vPos.x * 2.0 + 0.001);\n float cy = floor(vPos.y * 2.0 + 0.001);\n float cz = floor(vPos.z * 2.0 + 0.001);\n float check = mod(cx + cy + cz, 2.0);\n vec3 col1 = vec3(0.9, 0.5, 0.2);\n vec3 col2 = vec3(0.2, 0.6, 0.9);\n vec3 base = mix(col1, col2, check);\n FragColor = vec4(base * diff, 1.0);\n}\n";
|
||||
|
||||
program : u32 = create_program(vert_src, frag_src);
|
||||
glUseProgram(program);
|
||||
|
||||
mvp_loc : s32 = glGetUniformLocation(program, "uMVP");
|
||||
light_loc : s32 = glGetUniformLocation(program, "uLightDir");
|
||||
wire_loc : s32 = glGetUniformLocation(program, "uWire");
|
||||
|
||||
// Cube vertices: position(3) + normal(3), 36 vertices = 216 floats
|
||||
vertices : [216]f32 = .[
|
||||
// Front face (z = +0.5)
|
||||
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
|
||||
0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
|
||||
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
|
||||
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
|
||||
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
|
||||
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
|
||||
// Back face (z = -0.5)
|
||||
0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
|
||||
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
|
||||
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
|
||||
0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
|
||||
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
|
||||
0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
|
||||
// Top face (y = +0.5)
|
||||
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
|
||||
0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
|
||||
0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
|
||||
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
|
||||
0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
|
||||
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
|
||||
// Bottom face (y = -0.5)
|
||||
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
|
||||
0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
|
||||
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
|
||||
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
|
||||
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
|
||||
-0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
|
||||
// Right face (x = +0.5)
|
||||
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
|
||||
0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
|
||||
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
|
||||
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
|
||||
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
|
||||
// Left face (x = -0.5)
|
||||
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
|
||||
-0.5, -0.5, 0.5, -1.0, 0.0, 0.0,
|
||||
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
|
||||
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
|
||||
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
|
||||
-0.5, 0.5, -0.5, -1.0, 0.0, 0.0
|
||||
];
|
||||
|
||||
// Create VAO and VBO
|
||||
vao : u32 = 0;
|
||||
vbo : u32 = 0;
|
||||
glGenVertexArrays(1, xx &vao);
|
||||
glGenBuffers(1, xx &vbo);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, 864, xx &vertices[0], GL_STATIC_DRAW);
|
||||
|
||||
// Position attribute (location 0): 3 floats, stride 24 bytes, offset 0
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, 0, 24, xx 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
// Normal attribute (location 1): 3 floats, stride 24 bytes, offset 12
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, 0, 24, xx 12);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
// Set light direction
|
||||
glUniform3f(light_loc, 0.5, 0.7, 1.0);
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
|
||||
// Render loop
|
||||
running := true;
|
||||
event : [128]u8 = ---;
|
||||
|
||||
while running {
|
||||
while SDL_PollEvent(xx &event[0]) {
|
||||
etype : u32 = xx event[0];
|
||||
if etype == SDL_EVENT_QUIT {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute rotation angle from time
|
||||
ticks := SDL_GetTicks();
|
||||
ms : f32 = xx ticks;
|
||||
angle := ms * 0.001;
|
||||
|
||||
// Build matrices
|
||||
proj : [16]f32 = ---;
|
||||
mat4_perspective(xx &proj[0], PI / 4.0, 800.0 / 600.0, 0.1, 100.0);
|
||||
|
||||
view : [16]f32 = ---;
|
||||
mat4_translate(xx &view[0], 0.0, 0.0, -3.0);
|
||||
|
||||
rot_y : [16]f32 = ---;
|
||||
mat4_rotate_y(xx &rot_y[0], angle);
|
||||
|
||||
rot_x : [16]f32 = ---;
|
||||
mat4_rotate_x(xx &rot_x[0], angle * 0.7);
|
||||
|
||||
// model = rot_y * rot_x
|
||||
model : [16]f32 = ---;
|
||||
mat4_multiply(xx &model[0], xx &rot_y[0], xx &rot_x[0]);
|
||||
|
||||
// view_model = view * model
|
||||
vm : [16]f32 = ---;
|
||||
mat4_multiply(xx &vm[0], xx &view[0], xx &model[0]);
|
||||
|
||||
// mvp = proj * view_model
|
||||
mvp : [16]f32 = ---;
|
||||
mat4_multiply(xx &mvp[0], xx &proj[0], xx &vm[0]);
|
||||
|
||||
glUniformMatrix4fv(mvp_loc, 1, 0, xx &mvp[0]);
|
||||
|
||||
glClearColor(0.1, 0.1, 0.15, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Solid pass
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
// Wireframe overlay
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glLineWidth(2.0);
|
||||
glUniform1f(wire_loc, 1.0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
// Restore
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDepthFunc(GL_LESS);
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
SDL_GL_DestroyContext(gl_ctx);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
}
|
||||
28
examples/29-fn-pointers.sx
Normal file
28
examples/29-fn-pointers.sx
Normal file
@@ -0,0 +1,28 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
add :: (a: s32, b: s32) -> s32 { a + b; }
|
||||
mul :: (a: s32, b: s32) -> s32 { a * b; }
|
||||
|
||||
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
|
||||
f(x, y);
|
||||
}
|
||||
|
||||
main :: () {
|
||||
// Store function in variable
|
||||
fp : (s32, s32) -> s32 = add;
|
||||
print("fp(3,4) = {}\n", fp(3, 4));
|
||||
|
||||
// Reassign to different function
|
||||
fp = mul;
|
||||
print("fp(3,4) = {}\n", fp(3, 4));
|
||||
|
||||
// Pass function pointer as argument
|
||||
print("apply(add,5,6) = {}\n", apply(add, 5, 6));
|
||||
print("apply(mul,5,6) = {}\n", apply(mul, 5, 6));
|
||||
}
|
||||
|
||||
// ** stdout **
|
||||
//fp(3,4) = 7
|
||||
//fp(3,4) = 12
|
||||
//apply(add,5,6) = 11
|
||||
//apply(mul,5,6) = 30
|
||||
98
examples/modules/opengl.sx
Normal file
98
examples/modules/opengl.sx
Normal file
@@ -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, *void, *void) -> void = ---;
|
||||
glCompileShader : (u32) -> void = ---;
|
||||
glGetShaderiv : (u32, u32, *void) -> void = ---;
|
||||
glGetShaderInfoLog : (u32, s32, *void, *void) -> void = ---;
|
||||
glCreateProgram : () -> u32 = ---;
|
||||
glAttachShader : (u32, u32) -> void = ---;
|
||||
glLinkProgram : (u32) -> void = ---;
|
||||
glGetProgramiv : (u32, u32, *void) -> void = ---;
|
||||
glGetProgramInfoLog : (u32, s32, *void, *void) -> void = ---;
|
||||
glUseProgram : (u32) -> void = ---;
|
||||
glDeleteShader : (u32) -> void = ---;
|
||||
glGenVertexArrays : (s32, *void) -> void = ---;
|
||||
glGenBuffers : (s32, *void) -> 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, *void) -> 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");
|
||||
}
|
||||
41
examples/modules/sdl3.sx
Normal file
41
examples/modules/sdl3.sx
Normal file
@@ -0,0 +1,41 @@
|
||||
#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_EventType
|
||||
SDL_EVENT_QUIT :u32: 0x100;
|
||||
SDL_EVENT_KEY_DOWN :u32: 0x300;
|
||||
|
||||
// Functions
|
||||
SDL_Init :: (flags: u32) -> bool #foreign;
|
||||
SDL_Quit :: () -> void #foreign;
|
||||
SDL_CreateWindow :: (title: [:0]u8, w: s32, h: s32, flags: u64) -> *void #foreign;
|
||||
SDL_DestroyWindow :: (window: *void) -> void #foreign;
|
||||
SDL_GL_SetAttribute :: (attr: s32, value: s32) -> bool #foreign;
|
||||
SDL_GL_CreateContext :: (window: *void) -> *void #foreign;
|
||||
SDL_GL_DestroyContext :: (context: *void) -> bool #foreign;
|
||||
SDL_GL_MakeCurrent :: (window: *void, context: *void) -> bool #foreign;
|
||||
SDL_GL_SwapWindow :: (window: *void) -> bool #foreign;
|
||||
SDL_GL_SetSwapInterval :: (interval: s32) -> bool #foreign;
|
||||
SDL_GL_GetProcAddress :: (proc: [:0]u8) -> *void #foreign;
|
||||
SDL_PollEvent :: (event: *void) -> bool #foreign;
|
||||
SDL_GetTicks :: () -> u64 #foreign;
|
||||
SDL_Delay :: (ms: u32) -> void #foreign;
|
||||
@@ -1,6 +1,8 @@
|
||||
Vector :: ($N: int, $T: Type) -> Type #builtin;
|
||||
write :: (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;
|
||||
alloc :: (size: s64) -> string #builtin;
|
||||
malloc :: (size: s64) -> *void #builtin;
|
||||
|
||||
@@ -63,6 +63,7 @@ pub const Node = struct {
|
||||
builtin_expr: void,
|
||||
foreign_expr: void,
|
||||
library_decl: LibraryDecl,
|
||||
function_type_expr: FunctionTypeExpr,
|
||||
|
||||
pub fn declName(self: Data) ?[]const u8 {
|
||||
return switch (self) {
|
||||
@@ -359,3 +360,8 @@ pub const NamespaceDecl = struct {
|
||||
pub const LibraryDecl = struct {
|
||||
lib_name: []const u8,
|
||||
};
|
||||
|
||||
pub const FunctionTypeExpr = struct {
|
||||
param_types: []const *Node,
|
||||
return_type: ?*Node, // null = void return
|
||||
};
|
||||
|
||||
287
src/codegen.zig
287
src/codegen.zig
@@ -177,6 +177,10 @@ pub const CodeGen = struct {
|
||||
foreign_libraries: std.ArrayList([]const u8),
|
||||
// Set of foreign function names (for ABI lowering at call sites)
|
||||
foreign_fns: std.StringHashMap(void),
|
||||
// Global mutable variables (from top-level var_decl, e.g. function pointers loaded at runtime)
|
||||
global_mutable_vars: std.StringHashMap(NamedValue),
|
||||
// Declared return types for non-generic functions (preserves signedness lost by LLVM round-trip)
|
||||
function_return_types: std.StringHashMap(Type),
|
||||
// Target configuration (triple, cpu, opt level, lib paths, linker)
|
||||
target_config: TargetConfig = .{},
|
||||
|
||||
@@ -293,6 +297,8 @@ pub const CodeGen = struct {
|
||||
.deferred_fn_bodies = std.ArrayList(DeferredFn).empty,
|
||||
.foreign_libraries = std.ArrayList([]const u8).empty,
|
||||
.foreign_fns = std.StringHashMap(void).init(allocator),
|
||||
.global_mutable_vars = std.StringHashMap(NamedValue).init(allocator),
|
||||
.function_return_types = std.StringHashMap(Type).init(allocator),
|
||||
.target_config = target_config,
|
||||
};
|
||||
}
|
||||
@@ -366,7 +372,7 @@ pub const CodeGen = struct {
|
||||
const elem_ty = Type.fromName(info.element_name) orelse unreachable;
|
||||
return c.LLVMVectorType(self.typeToLLVM(elem_ty), info.length);
|
||||
},
|
||||
.pointer_type, .many_pointer_type => c.LLVMPointerTypeInContext(self.context, 0),
|
||||
.pointer_type, .many_pointer_type, .function_type => c.LLVMPointerTypeInContext(self.context, 0),
|
||||
.any_type => self.getAnyStructType(),
|
||||
.meta_type => c.LLVMPointerTypeInContext(self.context, 0),
|
||||
};
|
||||
@@ -741,6 +747,9 @@ pub const CodeGen = struct {
|
||||
.namespace_decl => |ns| {
|
||||
try self.registerNamespace(ns);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
try self.registerGlobalVar(vd);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -937,6 +946,21 @@ pub const CodeGen = struct {
|
||||
const elem_name = elem_type.displayName(self.allocator) catch unreachable;
|
||||
return .{ .many_pointer_type = .{ .element_name = elem_name } };
|
||||
}
|
||||
// Function pointer type: (ParamTypes) -> ReturnType
|
||||
if (tn.data == .function_type_expr) {
|
||||
const fte = tn.data.function_type_expr;
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fte.param_types) |pt| {
|
||||
param_types.append(self.allocator, self.resolveType(pt)) catch return .void_type;
|
||||
}
|
||||
const ret_ty = if (fte.return_type) |rt| self.resolveType(rt) else Type.void_type;
|
||||
const ret_ptr = self.allocator.create(Type) catch return .void_type;
|
||||
ret_ptr.* = ret_ty;
|
||||
return .{ .function_type = .{
|
||||
.param_types = param_types.toOwnedSlice(self.allocator) catch return .void_type,
|
||||
.return_type = ret_ptr,
|
||||
} };
|
||||
}
|
||||
// Parameterized type: Vector(N, T) or generic struct instantiation
|
||||
if (tn.data == .parameterized_type_expr) {
|
||||
const pte = tn.data.parameterized_type_expr;
|
||||
@@ -1482,6 +1506,9 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
try self.fn_param_types.put(llvm_name, try param_types.toOwnedSlice(self.allocator));
|
||||
// Track declared return type (preserves signedness lost by LLVM round-trip)
|
||||
const ret_ty = if (fd.return_type) |rt| self.resolveType(rt) else Type.void_type;
|
||||
try self.function_return_types.put(llvm_name, ret_ty);
|
||||
// Track variadic function info for call site packing
|
||||
for (fd.params, 0..) |param, i| {
|
||||
if (param.is_variadic) {
|
||||
@@ -1553,6 +1580,9 @@ pub const CodeGen = struct {
|
||||
.library_decl => |ld| {
|
||||
try self.foreign_libraries.append(self.allocator, ld.lib_name);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
try self.registerGlobalVar(vd);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -1703,6 +1733,22 @@ pub const CodeGen = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn registerGlobalVar(self: *CodeGen, vd: ast.VarDecl) !void {
|
||||
const ta = vd.type_annotation orelse return;
|
||||
const sx_ty = self.resolveType(ta);
|
||||
if (sx_ty == .void_type) return;
|
||||
|
||||
const llvm_ty = self.typeToLLVM(sx_ty);
|
||||
const name_z = try self.allocator.dupeZ(u8, vd.name);
|
||||
const global = c.LLVMAddGlobal(self.module, llvm_ty, name_z.ptr);
|
||||
// Initialize with undef (will be set at runtime, e.g. by load_gl)
|
||||
c.LLVMSetInitializer(global, c.LLVMGetUndef(llvm_ty));
|
||||
// NOT constant — this is a mutable global
|
||||
c.LLVMSetGlobalConstant(global, 0);
|
||||
|
||||
try self.global_mutable_vars.put(vd.name, .{ .ptr = global, .ty = sx_ty });
|
||||
}
|
||||
|
||||
fn bindParam(self: *CodeGen, function: c.LLVMValueRef, name: []const u8, sx_ty: Type, param_idx: u32) !void {
|
||||
const llvm_ty = self.typeToLLVM(sx_ty);
|
||||
const param_name_z = try self.allocator.dupeZ(u8, name);
|
||||
@@ -2175,6 +2221,34 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Function pointer typed variable
|
||||
if (sx_ty.isFunctionType()) {
|
||||
const llvm_ty = c.LLVMPointerTypeInContext(self.context, 0);
|
||||
const name_z = try self.allocator.dupeZ(u8, vd.name);
|
||||
const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr);
|
||||
|
||||
if (vd.value == null) {
|
||||
_ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(llvm_ty), alloca);
|
||||
} else if (vd.value.?.data == .undef_literal) {
|
||||
_ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(llvm_ty), alloca);
|
||||
} else if (vd.value.?.data == .unary_op and vd.value.?.data.unary_op.op == .xx) {
|
||||
// xx cast: e.g. xx SDL_GL_GetProcAddress("glClear")
|
||||
const inner = vd.value.?.data.unary_op.operand;
|
||||
const val = try self.genExpr(inner);
|
||||
const src_ty = self.inferType(inner);
|
||||
const converted = self.convertValue(val, src_ty, sx_ty);
|
||||
_ = c.LLVMBuildStore(self.builder, converted, alloca);
|
||||
} else {
|
||||
// Direct assignment: identifier (function name) or other expression
|
||||
const val = try self.genExpr(vd.value.?);
|
||||
_ = c.LLVMBuildStore(self.builder, val, alloca);
|
||||
}
|
||||
|
||||
try self.saveShadowed(vd.name);
|
||||
try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty });
|
||||
return null;
|
||||
}
|
||||
|
||||
// Guard: void type cannot be allocated (would crash LLVM)
|
||||
if (sx_ty == .void_type) {
|
||||
return self.emitErrorFmt("cannot declare variable '{s}' with void type", .{vd.name});
|
||||
@@ -2261,6 +2335,26 @@ pub const CodeGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Function pointer typed constant
|
||||
if (sx_ty.isFunctionType()) {
|
||||
const llvm_ty = c.LLVMPointerTypeInContext(self.context, 0);
|
||||
const name_z = try self.allocator.dupeZ(u8, cd.name);
|
||||
const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr);
|
||||
if (cd.value.data == .unary_op and cd.value.data.unary_op.op == .xx) {
|
||||
const inner = cd.value.data.unary_op.operand;
|
||||
const val = try self.genExpr(inner);
|
||||
const src_inner_ty = self.inferType(inner);
|
||||
const converted = self.convertValue(val, src_inner_ty, sx_ty);
|
||||
_ = c.LLVMBuildStore(self.builder, converted, alloca);
|
||||
} else {
|
||||
const val = try self.genExpr(cd.value);
|
||||
_ = c.LLVMBuildStore(self.builder, val, alloca);
|
||||
}
|
||||
try self.saveShadowed(cd.name);
|
||||
try self.named_values.put(cd.name, .{ .ptr = alloca, .ty = sx_ty });
|
||||
return null;
|
||||
}
|
||||
|
||||
const enum_name: ?[]const u8 = if (sx_ty.isEnum()) sx_ty.enum_type else null;
|
||||
const init_val = if (cd.value.data == .enum_literal and enum_name != null)
|
||||
self.genEnumLiteral(cd.value.data.enum_literal.name, enum_name.?)
|
||||
@@ -2313,7 +2407,8 @@ pub const CodeGen = struct {
|
||||
// Target must be an identifier
|
||||
if (asgn.target.data != .identifier) return self.emitError("assignment target must be a variable");
|
||||
const name = asgn.target.data.identifier.name;
|
||||
const entry = self.named_values.get(name) orelse {
|
||||
const entry = self.named_values.get(name) orelse
|
||||
self.global_mutable_vars.get(name) orelse {
|
||||
return self.emitErrorFmt("undefined variable '{s}'", .{name});
|
||||
};
|
||||
|
||||
@@ -2339,6 +2434,21 @@ pub const CodeGen = struct {
|
||||
return self.emitErrorFmt("cannot assign non-type value to Type variable '{s}'", .{name});
|
||||
}
|
||||
|
||||
// Function pointer reassignment
|
||||
if (entry.ty.isFunctionType() and asgn.op == .assign) {
|
||||
if (asgn.value.data == .unary_op and asgn.value.data.unary_op.op == .xx) {
|
||||
const inner = asgn.value.data.unary_op.operand;
|
||||
const val = try self.genExpr(inner);
|
||||
const src_ty = self.inferType(inner);
|
||||
const converted = self.convertValue(val, src_ty, entry.ty);
|
||||
_ = c.LLVMBuildStore(self.builder, converted, entry.ptr);
|
||||
} else {
|
||||
const val = try self.genExpr(asgn.value);
|
||||
_ = c.LLVMBuildStore(self.builder, val, entry.ptr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Union reassignment: s = .circle(3.14) or s = .none
|
||||
if (entry.ty.isUnion() and asgn.op == .assign) {
|
||||
const new_alloca = try self.genExprAsType(asgn.value, entry.ty);
|
||||
@@ -2556,6 +2666,25 @@ pub const CodeGen = struct {
|
||||
const llvm_ty = self.typeToLLVM(ct.ty);
|
||||
return c.LLVMBuildLoad2(self.builder, llvm_ty, ct.global, "ct_load");
|
||||
}
|
||||
// Fall back to global mutable variables (e.g. function pointers from opengl.sx)
|
||||
if (self.global_mutable_vars.get(ident.name)) |entry| {
|
||||
const llvm_ty = self.typeToLLVM(entry.ty);
|
||||
return c.LLVMBuildLoad2(self.builder, llvm_ty, entry.ptr, "global_load");
|
||||
}
|
||||
// Fall back to function name → function pointer value
|
||||
{
|
||||
const name_z = try self.allocator.dupeZ(u8, ident.name);
|
||||
var fn_val = c.LLVMGetNamedFunction(self.module, name_z.ptr);
|
||||
if (fn_val == null) {
|
||||
// Try qualified name with current namespace
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, ident.name });
|
||||
const q_z = try self.allocator.dupeZ(u8, qualified);
|
||||
fn_val = c.LLVMGetNamedFunction(self.module, q_z.ptr);
|
||||
}
|
||||
}
|
||||
if (fn_val != null) return fn_val.?;
|
||||
}
|
||||
return self.emitErrorFmt("undefined identifier '{s}'", .{ident.name});
|
||||
},
|
||||
.binary_op => |binop| {
|
||||
@@ -3115,6 +3244,11 @@ pub const CodeGen = struct {
|
||||
return self.convertValue(val, src_ty, target_ty);
|
||||
}
|
||||
|
||||
// Function pointer target: bypass narrowing check, just produce the pointer value
|
||||
if (target_ty.isFunctionType()) {
|
||||
return try self.genExpr(node);
|
||||
}
|
||||
|
||||
// String literal → pointer context: produce raw pointer directly (no {ptr, len} wrapping)
|
||||
if (node.data == .string_literal and target_ty.isPointer()) {
|
||||
const unescaped = try unescapeString(self.allocator, node.data.string_literal.raw);
|
||||
@@ -3431,6 +3565,19 @@ pub const CodeGen = struct {
|
||||
return val;
|
||||
}
|
||||
|
||||
// Int → pointer/function_type: IntToPtr (for xx cast from integer to pointer)
|
||||
if (src_ty.isInt() and (target_ty.isPointer() or target_ty.isManyPointer() or target_ty.isFunctionType())) {
|
||||
return c.LLVMBuildIntToPtr(self.builder, val, c.LLVMPointerTypeInContext(self.context, 0), "inttoptr");
|
||||
}
|
||||
|
||||
// Pointer → function_type or function_type → pointer: both are opaque pointers, no-op
|
||||
if ((src_ty.isPointer() or src_ty.isManyPointer()) and target_ty.isFunctionType()) {
|
||||
return val;
|
||||
}
|
||||
if (src_ty.isFunctionType() and (target_ty.isPointer() or target_ty.isManyPointer())) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -3482,6 +3629,50 @@ pub const CodeGen = struct {
|
||||
);
|
||||
}
|
||||
|
||||
fn genSin(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
|
||||
if (call_node.args.len != 1) return self.emitError("sin expects exactly 1 argument");
|
||||
const arg_val = try self.genExpr(call_node.args[0]);
|
||||
const arg_ty = self.inferType(call_node.args[0]);
|
||||
|
||||
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.sin.f64" else "llvm.sin.f32";
|
||||
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64))
|
||||
c.LLVMDoubleTypeInContext(self.context)
|
||||
else
|
||||
c.LLVMFloatTypeInContext(self.context);
|
||||
|
||||
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
|
||||
if (intrinsic_fn == null) {
|
||||
var param_types = [_]c.LLVMTypeRef{llvm_float_ty};
|
||||
const fn_type = c.LLVMFunctionType(llvm_float_ty, ¶m_types, 1, 0);
|
||||
intrinsic_fn = c.LLVMAddFunction(self.module, intrinsic_name, fn_type);
|
||||
}
|
||||
|
||||
var args = [_]c.LLVMValueRef{arg_val};
|
||||
return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, "sin");
|
||||
}
|
||||
|
||||
fn genCos(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
|
||||
if (call_node.args.len != 1) return self.emitError("cos expects exactly 1 argument");
|
||||
const arg_val = try self.genExpr(call_node.args[0]);
|
||||
const arg_ty = self.inferType(call_node.args[0]);
|
||||
|
||||
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.cos.f64" else "llvm.cos.f32";
|
||||
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64))
|
||||
c.LLVMDoubleTypeInContext(self.context)
|
||||
else
|
||||
c.LLVMFloatTypeInContext(self.context);
|
||||
|
||||
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
|
||||
if (intrinsic_fn == null) {
|
||||
var param_types = [_]c.LLVMTypeRef{llvm_float_ty};
|
||||
const fn_type = c.LLVMFunctionType(llvm_float_ty, ¶m_types, 1, 0);
|
||||
intrinsic_fn = c.LLVMAddFunction(self.module, intrinsic_name, fn_type);
|
||||
}
|
||||
|
||||
var args = [_]c.LLVMValueRef{arg_val};
|
||||
return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, "cos");
|
||||
}
|
||||
|
||||
fn genSizeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
|
||||
if (call_node.args.len != 1) return self.emitError("size_of expects exactly 1 argument");
|
||||
const ty = self.resolveType(call_node.args[0]);
|
||||
@@ -4318,6 +4509,12 @@ pub const CodeGen = struct {
|
||||
if (std.mem.eql(u8, callee_name, "sqrt")) {
|
||||
return self.genSqrt(call_node);
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "sin")) {
|
||||
return self.genSin(call_node);
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "cos")) {
|
||||
return self.genCos(call_node);
|
||||
}
|
||||
if (std.mem.eql(u8, callee_name, "cast")) {
|
||||
return self.genCast(call_node);
|
||||
}
|
||||
@@ -4349,7 +4546,17 @@ pub const CodeGen = struct {
|
||||
callee_fn = c.LLVMGetNamedFunction(self.module, qualified_z.ptr);
|
||||
}
|
||||
}
|
||||
if (callee_fn == null) return self.emitErrorFmt("undefined function '{s}'", .{callee_name});
|
||||
// Function pointer indirect call: callee is a variable with function_type
|
||||
if (callee_fn == null) {
|
||||
const fp_entry = if (self.named_values.get(callee_name)) |e| e
|
||||
else self.global_mutable_vars.get(callee_name);
|
||||
if (fp_entry) |entry| {
|
||||
if (entry.ty.isFunctionType()) {
|
||||
return self.genIndirectCall(entry, call_node);
|
||||
}
|
||||
}
|
||||
return self.emitErrorFmt("undefined function '{s}'", .{callee_name});
|
||||
}
|
||||
|
||||
// Get function type (opaque pointers: use LLVMGlobalGetValueType)
|
||||
const fn_type = c.LLVMGlobalGetValueType(callee_fn.?);
|
||||
@@ -4511,6 +4718,48 @@ pub const CodeGen = struct {
|
||||
);
|
||||
}
|
||||
|
||||
fn genIndirectCall(self: *CodeGen, entry: NamedValue, call_node: ast.Call) !c.LLVMValueRef {
|
||||
const fti = entry.ty.function_type;
|
||||
|
||||
// Load the function pointer from the alloca
|
||||
const ptr_ty = c.LLVMPointerTypeInContext(self.context, 0);
|
||||
const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr");
|
||||
|
||||
// Build LLVM function type from FunctionTypeInfo
|
||||
var param_llvm_types: [64]c.LLVMTypeRef = undefined;
|
||||
for (fti.param_types, 0..) |pt, i| {
|
||||
param_llvm_types[i] = self.typeToLLVM(pt);
|
||||
}
|
||||
const ret_llvm = self.typeToLLVM(fti.return_type.*);
|
||||
const fn_type = c.LLVMFunctionType(
|
||||
ret_llvm,
|
||||
if (fti.param_types.len > 0) ¶m_llvm_types else null,
|
||||
@intCast(fti.param_types.len),
|
||||
0,
|
||||
);
|
||||
|
||||
// Generate arguments with type conversion
|
||||
var arg_vals = std.ArrayList(c.LLVMValueRef).empty;
|
||||
for (call_node.args, 0..) |arg, i| {
|
||||
if (i < fti.param_types.len) {
|
||||
try arg_vals.append(self.allocator, try self.genExprAsType(arg, fti.param_types[i]));
|
||||
} else {
|
||||
try arg_vals.append(self.allocator, try self.genExpr(arg));
|
||||
}
|
||||
}
|
||||
const args_slice = try arg_vals.toOwnedSlice(self.allocator);
|
||||
|
||||
const call_name: [*c]const u8 = if (ret_llvm == c.LLVMVoidTypeInContext(self.context)) "" else "calltmp";
|
||||
return c.LLVMBuildCall2(
|
||||
self.builder,
|
||||
fn_type,
|
||||
fn_ptr,
|
||||
if (args_slice.len > 0) args_slice.ptr else null,
|
||||
@intCast(args_slice.len),
|
||||
call_name,
|
||||
);
|
||||
}
|
||||
|
||||
fn genGenericCall(self: *CodeGen, qualified_name: []const u8, template: GenericTemplate, call_node: ast.Call) !c.LLVMValueRef {
|
||||
const fd = template.fd;
|
||||
|
||||
@@ -5690,6 +5939,8 @@ pub const CodeGen = struct {
|
||||
const base = if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx + 1 ..] else name;
|
||||
if (std.mem.eql(u8, base, "write")) return self.genWriteCall(call_node.args);
|
||||
if (std.mem.eql(u8, base, "sqrt")) return self.genSqrt(call_node);
|
||||
if (std.mem.eql(u8, base, "sin")) return self.genSin(call_node);
|
||||
if (std.mem.eql(u8, base, "cos")) return self.genCos(call_node);
|
||||
if (std.mem.eql(u8, base, "size_of")) return self.genSizeOf(call_node);
|
||||
if (std.mem.eql(u8, base, "cast")) return self.genCast(call_node);
|
||||
if (std.mem.eql(u8, base, "alloc")) return self.genAlloc(call_node.args);
|
||||
@@ -5871,6 +6122,7 @@ pub const CodeGen = struct {
|
||||
.identifier => |ident| {
|
||||
if (self.named_values.get(ident.name)) |entry| return entry.ty;
|
||||
if (self.comptime_globals.get(ident.name)) |ct| return ct.ty;
|
||||
if (self.global_mutable_vars.get(ident.name)) |entry| return entry.ty;
|
||||
return Type.s(64);
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
@@ -5915,8 +6167,11 @@ pub const CodeGen = struct {
|
||||
}
|
||||
const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(64);
|
||||
const base_name = if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |idx| callee_name[idx + 1 ..] else callee_name;
|
||||
// Built-in: sqrt returns same type as argument
|
||||
if (std.mem.eql(u8, base_name, "sqrt")) {
|
||||
// Built-in: sqrt/sin/cos returns same type as argument
|
||||
if (std.mem.eql(u8, base_name, "sqrt") or
|
||||
std.mem.eql(u8, base_name, "sin") or
|
||||
std.mem.eql(u8, base_name, "cos"))
|
||||
{
|
||||
if (call_node.args.len > 0) return self.inferType(call_node.args[0]);
|
||||
return .f32;
|
||||
}
|
||||
@@ -5985,13 +6240,19 @@ pub const CodeGen = struct {
|
||||
}
|
||||
return Type.s(64);
|
||||
}
|
||||
// Check non-generic LLVM functions
|
||||
// Check declared return types (preserves signedness)
|
||||
if (self.function_return_types.get(callee_name)) |ret_ty| return ret_ty;
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(64);
|
||||
if (self.function_return_types.get(qualified)) |ret_ty| return ret_ty;
|
||||
}
|
||||
// Fallback: check non-generic LLVM functions
|
||||
const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(64);
|
||||
var callee_fn_opt = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr);
|
||||
// Intra-namespace fallback
|
||||
if (callee_fn_opt == null) {
|
||||
if (self.current_namespace) |ns| {
|
||||
const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(64);
|
||||
if (self.current_namespace) |ns2| {
|
||||
const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns2, callee_name }) catch return Type.s(64);
|
||||
const qz = self.allocator.dupeZ(u8, q) catch return Type.s(64);
|
||||
callee_fn_opt = c.LLVMGetNamedFunction(self.module, qz.ptr);
|
||||
}
|
||||
@@ -6001,6 +6262,16 @@ pub const CodeGen = struct {
|
||||
const ret_llvm = c.LLVMGetReturnType(fn_type);
|
||||
return self.llvmTypeToSxType(ret_llvm);
|
||||
}
|
||||
// Check if callee is a variable with function pointer type
|
||||
{
|
||||
const fp_entry = if (self.named_values.get(callee_name)) |e| e
|
||||
else self.global_mutable_vars.get(callee_name);
|
||||
if (fp_entry) |entry| {
|
||||
if (entry.ty.isFunctionType()) {
|
||||
return entry.ty.function_type.return_type.*;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Type.s(64);
|
||||
},
|
||||
.unary_op => |unop| {
|
||||
|
||||
@@ -292,6 +292,28 @@ pub const Parser = struct {
|
||||
self.advance();
|
||||
return try self.createNode(start, .{ .type_expr = .{ .name = name, .is_generic = true } });
|
||||
}
|
||||
// Function pointer type: (ParamTypes) -> ReturnType
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
var param_types = std.ArrayList(*Node).empty;
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (param_types.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
}
|
||||
try param_types.append(self.allocator, try self.parseTypeExpr());
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
var return_type: ?*Node = null;
|
||||
if (self.current.tag == .arrow) {
|
||||
self.advance(); // skip '->'
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
return try self.createNode(start, .{ .function_type_expr = .{
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.return_type = return_type,
|
||||
} });
|
||||
}
|
||||
|
||||
if (self.current.tag.isTypeKeyword() or self.current.tag == .identifier) {
|
||||
var name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
|
||||
24
src/sema.zig
24
src/sema.zig
@@ -245,6 +245,21 @@ pub const Analyzer = struct {
|
||||
const elem_name = elem_type.displayName(self.allocator) catch return .void_type;
|
||||
return .{ .many_pointer_type = .{ .element_name = elem_name } };
|
||||
}
|
||||
// Function pointer type: (ParamTypes) -> ReturnType
|
||||
if (tn.data == .function_type_expr) {
|
||||
const fte = tn.data.function_type_expr;
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fte.param_types) |pt| {
|
||||
param_types.append(self.allocator, self.resolveTypeNode(pt)) catch return .void_type;
|
||||
}
|
||||
const ret_ty = if (fte.return_type) |rt| self.resolveTypeNode(rt) else Type.void_type;
|
||||
const ret_ptr = self.allocator.create(Type) catch return .void_type;
|
||||
ret_ptr.* = ret_ty;
|
||||
return .{ .function_type = .{
|
||||
.param_types = param_types.toOwnedSlice(self.allocator) catch return .void_type,
|
||||
.return_type = ret_ptr,
|
||||
} };
|
||||
}
|
||||
// Sema does not resolve generics; codegen handles instantiation
|
||||
if (tn.data == .parameterized_type_expr) {
|
||||
return .void_type;
|
||||
@@ -320,9 +335,12 @@ pub const Analyzer = struct {
|
||||
if (self.fn_signatures.get(callee_name)) |sig| {
|
||||
return sig.return_type;
|
||||
}
|
||||
// Built-in: sqrt returns same type as argument
|
||||
// Built-in: sqrt/sin/cos returns same type as argument
|
||||
const base = if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |idx| callee_name[idx + 1 ..] else callee_name;
|
||||
if (std.mem.eql(u8, base, "sqrt")) {
|
||||
if (std.mem.eql(u8, base, "sqrt") or
|
||||
std.mem.eql(u8, base, "sin") or
|
||||
std.mem.eql(u8, base, "cos"))
|
||||
{
|
||||
if (call_node.args.len > 0) return self.inferExprType(call_node.args[0]);
|
||||
return .f32;
|
||||
}
|
||||
@@ -673,6 +691,7 @@ pub const Analyzer = struct {
|
||||
.builtin_expr,
|
||||
.foreign_expr,
|
||||
.library_decl,
|
||||
.function_type_expr,
|
||||
.import_decl,
|
||||
.array_type_expr,
|
||||
.slice_type_expr,
|
||||
@@ -929,6 +948,7 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
.builtin_expr,
|
||||
.foreign_expr,
|
||||
.library_decl,
|
||||
.function_type_expr,
|
||||
.enum_decl,
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
|
||||
@@ -21,6 +21,7 @@ pub const Type = union(enum) {
|
||||
pointer_type: PointerTypeInfo,
|
||||
many_pointer_type: ManyPointerTypeInfo,
|
||||
vector_type: VectorTypeInfo,
|
||||
function_type: FunctionTypeInfo,
|
||||
any_type,
|
||||
meta_type: MetaTypeInfo,
|
||||
|
||||
@@ -36,6 +37,11 @@ pub const Type = union(enum) {
|
||||
element_name: []const u8,
|
||||
};
|
||||
|
||||
pub const FunctionTypeInfo = struct {
|
||||
param_types: []const Type,
|
||||
return_type: *const Type,
|
||||
};
|
||||
|
||||
pub const ArrayTypeInfo = struct {
|
||||
element_name: []const u8,
|
||||
length: u32,
|
||||
@@ -182,6 +188,13 @@ pub const Type = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isFunctionType(self: Type) bool {
|
||||
return switch (self) {
|
||||
.function_type => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isArray(self: Type) bool {
|
||||
return switch (self) {
|
||||
.array_type => true,
|
||||
@@ -235,6 +248,7 @@ pub const Type = union(enum) {
|
||||
.f32 => 32,
|
||||
.f64 => 64,
|
||||
.boolean => 1,
|
||||
.pointer_type, .many_pointer_type, .function_type => 64,
|
||||
else => 0,
|
||||
};
|
||||
}
|
||||
@@ -382,6 +396,20 @@ pub const Type = union(enum) {
|
||||
try buf.append(allocator, ')');
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
},
|
||||
.function_type => |info| {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
try buf.append(allocator, '(');
|
||||
for (info.param_types, 0..) |pt, i| {
|
||||
if (i > 0) try buf.appendSlice(allocator, ", ");
|
||||
try buf.appendSlice(allocator, try pt.displayName(allocator));
|
||||
}
|
||||
try buf.append(allocator, ')');
|
||||
if (!std.meta.eql(info.return_type.*, Type.void_type)) {
|
||||
try buf.appendSlice(allocator, " -> ");
|
||||
try buf.appendSlice(allocator, try info.return_type.displayName(allocator));
|
||||
}
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
},
|
||||
.meta_type => |info| info.name,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user