sdl phase 2
This commit is contained in:
@@ -4,19 +4,27 @@
|
|||||||
|
|
||||||
PI :f32: 3.14159265;
|
PI :f32: 3.14159265;
|
||||||
|
|
||||||
// ---- Matrix math (column-major [16]f32, passed as [*]f32) ----
|
vec4 :: (x: f32, y: f32, z: f32, w: f32) -> Vector(4, f32) {
|
||||||
|
.[x, y, z, w];
|
||||||
|
}
|
||||||
|
|
||||||
mat4_zero :: (m: [*]f32) {
|
// ---- Matrix44: column-major 4×4 matrix ----
|
||||||
|
|
||||||
|
Matrix44 :: struct {
|
||||||
|
data: [16]f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat4_zero :: (m: *Matrix44) {
|
||||||
i := 0;
|
i := 0;
|
||||||
while i < 16 { m[i] = 0.0; i += 1; }
|
while i < 16 { m.data[i] = 0.0; i += 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_identity :: (m: [*]f32) {
|
mat4_identity :: (m: *Matrix44) {
|
||||||
mat4_zero(m);
|
mat4_zero(m);
|
||||||
m[0] = 1.0; m[5] = 1.0; m[10] = 1.0; m[15] = 1.0;
|
m.data[0] = 1.0; m.data[5] = 1.0; m.data[10] = 1.0; m.data[15] = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) {
|
mat4_multiply :: (out: *Matrix44, a: *Matrix44, b: *Matrix44) {
|
||||||
tmp : [16]f32 = ---;
|
tmp : [16]f32 = ---;
|
||||||
j := 0;
|
j := 0;
|
||||||
while j < 4 {
|
while j < 4 {
|
||||||
@@ -25,7 +33,7 @@ mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) {
|
|||||||
sum : f32 = 0.0;
|
sum : f32 = 0.0;
|
||||||
k := 0;
|
k := 0;
|
||||||
while k < 4 {
|
while k < 4 {
|
||||||
sum = sum + a[k * 4 + i] * b[j * 4 + k];
|
sum = sum + a.data[k * 4 + i] * b.data[j * 4 + k];
|
||||||
k += 1;
|
k += 1;
|
||||||
}
|
}
|
||||||
tmp[j * 4 + i] = sum;
|
tmp[j * 4 + i] = sum;
|
||||||
@@ -34,64 +42,63 @@ mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) {
|
|||||||
j += 1;
|
j += 1;
|
||||||
}
|
}
|
||||||
i := 0;
|
i := 0;
|
||||||
while i < 16 { out[i] = tmp[i]; i += 1; }
|
while i < 16 { out.data[i] = tmp[i]; i += 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_perspective :: (m: [*]f32, fov: f32, aspect: f32, near: f32, far: f32) {
|
mat4_perspective :: (m: *Matrix44, fov: f32, aspect: f32, near: f32, far: f32) {
|
||||||
mat4_zero(m);
|
mat4_zero(m);
|
||||||
half := fov / 2.0;
|
half := fov / 2.0;
|
||||||
f := cos(half) / sin(half);
|
f := cos(half) / sin(half);
|
||||||
m[0] = f / aspect;
|
m.data[0] = f / aspect;
|
||||||
m[5] = f;
|
m.data[5] = f;
|
||||||
m[10] = (far + near) / (near - far);
|
m.data[10] = (far + near) / (near - far);
|
||||||
m[11] = -1.0;
|
m.data[11] = -1.0;
|
||||||
m[14] = (2.0 * far * near) / (near - far);
|
m.data[14] = (2.0 * far * near) / (near - far);
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_rotate_y :: (m: [*]f32, angle: f32) {
|
mat4_rotate_y :: (m: *Matrix44, angle: f32) {
|
||||||
mat4_zero(m);
|
mat4_zero(m);
|
||||||
c := cos(angle);
|
c := cos(angle);
|
||||||
s := sin(angle);
|
s := sin(angle);
|
||||||
m[0] = c;
|
m.data[0] = c;
|
||||||
m[2] = 0.0 - s;
|
m.data[2] = 0.0 - s;
|
||||||
m[5] = 1.0;
|
m.data[5] = 1.0;
|
||||||
m[8] = s;
|
m.data[8] = s;
|
||||||
m[10] = c;
|
m.data[10] = c;
|
||||||
m[15] = 1.0;
|
m.data[15] = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_rotate_x :: (m: [*]f32, angle: f32) {
|
mat4_rotate_x :: (m: *Matrix44, angle: f32) {
|
||||||
mat4_zero(m);
|
mat4_zero(m);
|
||||||
c := cos(angle);
|
c := cos(angle);
|
||||||
s := sin(angle);
|
s := sin(angle);
|
||||||
m[0] = 1.0;
|
m.data[0] = 1.0;
|
||||||
m[5] = c;
|
m.data[5] = c;
|
||||||
m[6] = s;
|
m.data[6] = s;
|
||||||
m[9] = 0.0 - s;
|
m.data[9] = 0.0 - s;
|
||||||
m[10] = c;
|
m.data[10] = c;
|
||||||
m[15] = 1.0;
|
m.data[15] = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_translate :: (m: [*]f32, tx: f32, ty: f32, tz: f32) {
|
mat4_translate :: (m: *Matrix44, tx: f32, ty: f32, tz: f32) {
|
||||||
mat4_identity(m);
|
mat4_identity(m);
|
||||||
m[12] = tx;
|
m.data[12] = tx;
|
||||||
m[13] = ty;
|
m.data[13] = ty;
|
||||||
m[14] = tz;
|
m.data[14] = tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Shader helpers ----
|
// ---- Shader helpers ----
|
||||||
|
|
||||||
compile_shader :: (shader_type: u32, source: [:0]u8) -> u32 {
|
compile_shader :: (shader_type: u32, source: [:0]u8) -> u32 {
|
||||||
shader : u32 = glCreateShader(shader_type);
|
shader : u32 = glCreateShader(shader_type);
|
||||||
src_ptr : *void = xx &source[0];
|
glShaderSource(shader, 1, source, null);
|
||||||
glShaderSource(shader, 1, xx &src_ptr, null);
|
|
||||||
glCompileShader(shader);
|
glCompileShader(shader);
|
||||||
|
|
||||||
status : s32 = 0;
|
status : s32 = 0;
|
||||||
glGetShaderiv(shader, GL_COMPILE_STATUS, xx &status);
|
glGetShaderiv(shader, GL_COMPILE_STATUS, status);
|
||||||
if status == GL_FALSE {
|
if status == GL_FALSE {
|
||||||
log_buf : [512]u8 = ---;
|
log_buf : [512]u8 = ---;
|
||||||
glGetShaderInfoLog(shader, 512, null, xx &log_buf[0]);
|
glGetShaderInfoLog(shader, 512, null, log_buf);
|
||||||
print("Shader compile error\n");
|
print("Shader compile error\n");
|
||||||
}
|
}
|
||||||
shader;
|
shader;
|
||||||
@@ -107,10 +114,10 @@ create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 {
|
|||||||
glLinkProgram(prog);
|
glLinkProgram(prog);
|
||||||
|
|
||||||
status : s32 = 0;
|
status : s32 = 0;
|
||||||
glGetProgramiv(prog, GL_LINK_STATUS, xx &status);
|
glGetProgramiv(prog, GL_LINK_STATUS, status);
|
||||||
if status == GL_FALSE {
|
if status == GL_FALSE {
|
||||||
log_buf : [512]u8 = ---;
|
log_buf : [512]u8 = ---;
|
||||||
glGetProgramInfoLog(prog, 512, null, xx &log_buf[0]);
|
glGetProgramInfoLog(prog, 512, null, log_buf);
|
||||||
print("Program link error\n");
|
print("Program link error\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,68 +196,68 @@ GLSL;
|
|||||||
light_loc : s32 = glGetUniformLocation(program, "uLightDir");
|
light_loc : s32 = glGetUniformLocation(program, "uLightDir");
|
||||||
wire_loc : s32 = glGetUniformLocation(program, "uWire");
|
wire_loc : s32 = glGetUniformLocation(program, "uWire");
|
||||||
|
|
||||||
// Cube vertices: position(3) + normal(3), 36 vertices = 216 floats
|
// Cube vertices: pos(vec4 w=1) + normal(vec4 w=0), 36 vertices × 2 vec4s = 72
|
||||||
vertices : [216]f32 = .[
|
vertices : []Vector(4, f32) = .[
|
||||||
// Front face (z = +0.5)
|
// Front face (z = +0.5)
|
||||||
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
|
vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||||
0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
|
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||||
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
|
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||||
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
|
vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||||
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
|
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||||
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
|
vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0),
|
||||||
// Back face (z = -0.5)
|
// Back face (z = -0.5)
|
||||||
0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
|
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||||
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
|
vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||||
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
|
vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||||
0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
|
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||||
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
|
vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||||
0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
|
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0),
|
||||||
// Top face (y = +0.5)
|
// Top face (y = +0.5)
|
||||||
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
|
vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||||
0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
|
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||||
0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
|
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||||
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
|
vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||||
0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
|
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||||
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
|
vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0),
|
||||||
// Bottom face (y = -0.5)
|
// Bottom face (y = -0.5)
|
||||||
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
|
vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||||
0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
|
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||||
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
|
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||||
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
|
vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||||
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
|
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||||
-0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
|
vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0),
|
||||||
// Right face (x = +0.5)
|
// Right face (x = +0.5)
|
||||||
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
|
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||||
0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
|
vec4( 0.5, -0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||||
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
|
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||||
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
|
vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||||
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
|
vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||||
0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
|
vec4( 0.5, 0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0),
|
||||||
// Left face (x = -0.5)
|
// Left face (x = -0.5)
|
||||||
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
|
vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||||
-0.5, -0.5, 0.5, -1.0, 0.0, 0.0,
|
vec4(-0.5, -0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||||
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
|
vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||||
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
|
vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||||
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
|
vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0),
|
||||||
-0.5, 0.5, -0.5, -1.0, 0.0, 0.0
|
vec4(-0.5, 0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0)
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create VAO and VBO
|
// Create VAO and VBO
|
||||||
vao : u32 = 0;
|
vao : u32 = 0;
|
||||||
vbo : u32 = 0;
|
vbo : u32 = 0;
|
||||||
glGenVertexArrays(1, xx &vao);
|
glGenVertexArrays(1, vao);
|
||||||
glGenBuffers(1, xx &vbo);
|
glGenBuffers(1, vbo);
|
||||||
|
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
glBufferData(GL_ARRAY_BUFFER, 864, xx &vertices[0], GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, 1152, xx vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
// Position attribute (location 0): 3 floats, stride 24 bytes, offset 0
|
// Position attribute (location 0): 3 floats, stride 32 bytes, offset 0
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, 0, 24, xx 0);
|
glVertexAttribPointer(0, 3, GL_FLOAT, 0, 32, xx 0);
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
// Normal attribute (location 1): 3 floats, stride 24 bytes, offset 12
|
// Normal attribute (location 1): 3 floats, stride 32 bytes, offset 16
|
||||||
glVertexAttribPointer(1, 3, GL_FLOAT, 0, 24, xx 12);
|
glVertexAttribPointer(1, 3, GL_FLOAT, 0, 32, xx 16);
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
// Set light direction
|
// Set light direction
|
||||||
@@ -275,31 +282,28 @@ GLSL;
|
|||||||
angle := ms * 0.001;
|
angle := ms * 0.001;
|
||||||
|
|
||||||
// Build matrices
|
// Build matrices
|
||||||
proj : [16]f32 = ---;
|
proj : Matrix44 = ---;
|
||||||
mat4_perspective(xx &proj[0], PI / 4.0, 800.0 / 600.0, 0.1, 100.0);
|
mat4_perspective(proj, PI / 4.0, 800.0 / 600.0, 0.1, 100.0);
|
||||||
|
|
||||||
view : [16]f32 = ---;
|
view : Matrix44 = ---;
|
||||||
mat4_translate(xx &view[0], 0.0, 0.0, -3.0);
|
mat4_translate(view, 0.0, 0.0, -3.0);
|
||||||
|
|
||||||
rot_y : [16]f32 = ---;
|
rot_y : Matrix44 = ---;
|
||||||
mat4_rotate_y(xx &rot_y[0], angle);
|
mat4_rotate_y(rot_y, angle);
|
||||||
|
|
||||||
rot_x : [16]f32 = ---;
|
rot_x : Matrix44 = ---;
|
||||||
mat4_rotate_x(xx &rot_x[0], angle * 0.7);
|
mat4_rotate_x(rot_x, angle * 0.7);
|
||||||
|
|
||||||
// model = rot_y * rot_x
|
model : Matrix44 = ---;
|
||||||
model : [16]f32 = ---;
|
mat4_multiply(model, rot_y, rot_x);
|
||||||
mat4_multiply(xx &model[0], xx &rot_y[0], xx &rot_x[0]);
|
|
||||||
|
|
||||||
// view_model = view * model
|
vm : Matrix44 = ---;
|
||||||
vm : [16]f32 = ---;
|
mat4_multiply(vm, view, model);
|
||||||
mat4_multiply(xx &vm[0], xx &view[0], xx &model[0]);
|
|
||||||
|
|
||||||
// mvp = proj * view_model
|
mvp : Matrix44 = ---;
|
||||||
mvp : [16]f32 = ---;
|
mat4_multiply(mvp, proj, vm);
|
||||||
mat4_multiply(xx &mvp[0], xx &proj[0], xx &vm[0]);
|
|
||||||
|
|
||||||
glUniformMatrix4fv(mvp_loc, 1, 0, xx &mvp[0]);
|
glUniformMatrix4fv(mvp_loc, 1, 0, mvp.data);
|
||||||
|
|
||||||
glClearColor(0.1, 0.1, 0.15, 1.0);
|
glClearColor(0.1, 0.1, 0.15, 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
|
||||||
|
|||||||
@@ -34,26 +34,26 @@ glDrawArrays : (u32, s32, s32) -> void = ---;
|
|||||||
glPolygonMode : (u32, u32) -> void = ---;
|
glPolygonMode : (u32, u32) -> void = ---;
|
||||||
glLineWidth : (f32) -> void = ---;
|
glLineWidth : (f32) -> void = ---;
|
||||||
glCreateShader : (u32) -> u32 = ---;
|
glCreateShader : (u32) -> u32 = ---;
|
||||||
glShaderSource : (u32, s32, *void, *void) -> void = ---;
|
glShaderSource : (u32, s32, *[:0]u8, *s32) -> void = ---;
|
||||||
glCompileShader : (u32) -> void = ---;
|
glCompileShader : (u32) -> void = ---;
|
||||||
glGetShaderiv : (u32, u32, *void) -> void = ---;
|
glGetShaderiv : (u32, u32, *s32) -> void = ---;
|
||||||
glGetShaderInfoLog : (u32, s32, *void, *void) -> void = ---;
|
glGetShaderInfoLog : (u32, s32, *s32, [*]u8) -> void = ---;
|
||||||
glCreateProgram : () -> u32 = ---;
|
glCreateProgram : () -> u32 = ---;
|
||||||
glAttachShader : (u32, u32) -> void = ---;
|
glAttachShader : (u32, u32) -> void = ---;
|
||||||
glLinkProgram : (u32) -> void = ---;
|
glLinkProgram : (u32) -> void = ---;
|
||||||
glGetProgramiv : (u32, u32, *void) -> void = ---;
|
glGetProgramiv : (u32, u32, *s32) -> void = ---;
|
||||||
glGetProgramInfoLog : (u32, s32, *void, *void) -> void = ---;
|
glGetProgramInfoLog : (u32, s32, *s32, [*]u8) -> void = ---;
|
||||||
glUseProgram : (u32) -> void = ---;
|
glUseProgram : (u32) -> void = ---;
|
||||||
glDeleteShader : (u32) -> void = ---;
|
glDeleteShader : (u32) -> void = ---;
|
||||||
glGenVertexArrays : (s32, *void) -> void = ---;
|
glGenVertexArrays : (s32, *u32) -> void = ---;
|
||||||
glGenBuffers : (s32, *void) -> void = ---;
|
glGenBuffers : (s32, *u32) -> void = ---;
|
||||||
glBindVertexArray : (u32) -> void = ---;
|
glBindVertexArray : (u32) -> void = ---;
|
||||||
glBindBuffer : (u32, u32) -> void = ---;
|
glBindBuffer : (u32, u32) -> void = ---;
|
||||||
glBufferData : (u32, s64, *void, u32) -> void = ---;
|
glBufferData : (u32, s64, *void, u32) -> void = ---;
|
||||||
glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void = ---;
|
glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void = ---;
|
||||||
glEnableVertexAttribArray : (u32) -> void = ---;
|
glEnableVertexAttribArray : (u32) -> void = ---;
|
||||||
glGetUniformLocation : (u32, [:0]u8) -> s32 = ---;
|
glGetUniformLocation : (u32, [:0]u8) -> s32 = ---;
|
||||||
glUniformMatrix4fv : (s32, s32, u8, *void) -> void = ---;
|
glUniformMatrix4fv : (s32, s32, u8, [16]f32) -> void = ---;
|
||||||
glUniform3f : (s32, f32, f32, f32) -> void = ---;
|
glUniform3f : (s32, f32, f32, f32) -> void = ---;
|
||||||
glDepthFunc : (u32) -> void = ---;
|
glDepthFunc : (u32) -> void = ---;
|
||||||
glUniform1f : (s32, f32) -> void = ---;
|
glUniform1f : (s32, f32) -> void = ---;
|
||||||
|
|||||||
18
specs.md
18
specs.md
@@ -286,8 +286,26 @@ val := mp[2]; // 30
|
|||||||
|
|
||||||
**Implicit conversions**:
|
**Implicit conversions**:
|
||||||
- `*T` → `[*]T` (pointer to element → many-pointer)
|
- `*T` → `[*]T` (pointer to element → many-pointer)
|
||||||
|
- `*[N]T` → `[*]T` (pointer to array → many-pointer)
|
||||||
|
- `[N]T` → `[*]T` at call sites (array decays to many-pointer)
|
||||||
|
- `[]T` → `[*]T` (slice decays to many-pointer, extracts `.ptr`)
|
||||||
|
- `T` → `*T` at call sites (implicit address-of)
|
||||||
- `null` (`*void`) → any `*T`
|
- `null` (`*void`) → any `*T`
|
||||||
|
|
||||||
|
**Fat pointer layout**: `[:0]u8`, `string`, and `[]T` are `{ptr, i64}` structs. The raw pointer is always the first field at offset 0. This means `*[:0]u8` works as C's `char**` — a C function dereferences through the outer pointer and reads the raw `char*` from offset 0.
|
||||||
|
|
||||||
|
### C Interop Type Mapping
|
||||||
|
|
||||||
|
| C type | sx type | Notes |
|
||||||
|
|--------|---------|-------|
|
||||||
|
| `const char*` (input) | `[:0]u8` | compiler extracts `.ptr` at call site |
|
||||||
|
| `char*` (output buffer) | `[*]u8` | raw buffer, no length |
|
||||||
|
| `const char**` | `*[:0]u8` | address of `[:0]u8` — `.ptr` at offset 0 |
|
||||||
|
| `int*` (single out) | `*s32` | |
|
||||||
|
| `unsigned*` (single out) | `*u32` | |
|
||||||
|
| `float*` (buffer) | `[*]f32` | |
|
||||||
|
| `void*` (generic) | `*void` | only for truly opaque/generic data |
|
||||||
|
|
||||||
### Vector Types (SIMD)
|
### Vector Types (SIMD)
|
||||||
LLVM SIMD vectors, parameterized by length and element type.
|
LLVM SIMD vectors, parameterized by length and element type.
|
||||||
```sx
|
```sx
|
||||||
|
|||||||
111
src/codegen.zig
111
src/codegen.zig
@@ -1280,6 +1280,9 @@ pub const CodeGen = struct {
|
|||||||
try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0));
|
try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0));
|
||||||
} else if (is_foreign and sx_ty.isStruct()) {
|
} else if (is_foreign and sx_ty.isStruct()) {
|
||||||
try param_llvm_types.append(self.allocator, self.getForeignParamABIType(sx_ty));
|
try param_llvm_types.append(self.allocator, self.getForeignParamABIType(sx_ty));
|
||||||
|
} else if (is_foreign and sx_ty.isArray()) {
|
||||||
|
// [N]T → pointer in C ABI (C arrays decay to pointers)
|
||||||
|
try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0));
|
||||||
} else {
|
} else {
|
||||||
try param_llvm_types.append(self.allocator, self.typeToLLVM(sx_ty));
|
try param_llvm_types.append(self.allocator, self.typeToLLVM(sx_ty));
|
||||||
}
|
}
|
||||||
@@ -1329,7 +1332,7 @@ pub const CodeGen = struct {
|
|||||||
if (first == .f32 or first == .f64) {
|
if (first == .f32 or first == .f64) {
|
||||||
var all_same = true;
|
var all_same = true;
|
||||||
for (field_types[1..]) |ft| {
|
for (field_types[1..]) |ft| {
|
||||||
if (!std.meta.eql(ft, first)) {
|
if (!ft.eql(first)) {
|
||||||
all_same = false;
|
all_same = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2554,6 +2557,17 @@ pub const CodeGen = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// struct.field[i] = val — GEP through struct to array field, then index
|
||||||
|
if (ie.object.data == .field_access) {
|
||||||
|
const field_ptr = try self.genAddressOf(ie.object);
|
||||||
|
const idx = try self.genExpr(ie.index);
|
||||||
|
const val = try self.genExpr(asgn.value);
|
||||||
|
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0);
|
||||||
|
var indices = [_]c.LLVMValueRef{ zero, idx };
|
||||||
|
const gep_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx");
|
||||||
|
_ = c.LLVMBuildStore(self.builder, val, gep_ptr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (obj_ty.isSlice()) {
|
if (obj_ty.isSlice()) {
|
||||||
const slice_info = obj_ty.slice_type;
|
const slice_info = obj_ty.slice_type;
|
||||||
@@ -2891,6 +2905,16 @@ pub const CodeGen = struct {
|
|||||||
const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
|
const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
|
||||||
return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field");
|
return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field");
|
||||||
}
|
}
|
||||||
|
// &p.field where p is *Struct — auto-deref through pointer
|
||||||
|
if (entry.ty.isPointer()) {
|
||||||
|
const pointee_name = entry.ty.pointer_type.pointee_name;
|
||||||
|
if (self.struct_types.get(pointee_name)) |info| {
|
||||||
|
const loaded_ptr = c.LLVMBuildLoad2(self.builder,
|
||||||
|
c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load");
|
||||||
|
const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name });
|
||||||
|
return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "addr_pfield");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3364,6 +3388,9 @@ pub const CodeGen = struct {
|
|||||||
break :blk entry.ptr;
|
break :blk entry.ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (node.data == .field_access) {
|
||||||
|
break :blk try self.genAddressOf(node);
|
||||||
|
}
|
||||||
// Fallback: generate the expression and hope it returns a pointer
|
// Fallback: generate the expression and hope it returns a pointer
|
||||||
break :blk try self.genExpr(node);
|
break :blk try self.genExpr(node);
|
||||||
};
|
};
|
||||||
@@ -3382,6 +3409,42 @@ pub const CodeGen = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Array to many-pointer coercion: [N]T → [*]T
|
||||||
|
if (target_ty.isManyPointer()) {
|
||||||
|
const src_ty = self.inferType(node);
|
||||||
|
if (src_ty.isArray()) {
|
||||||
|
const arr_info = src_ty.array_type;
|
||||||
|
if (std.mem.eql(u8, arr_info.element_name, target_ty.many_pointer_type.element_name)) {
|
||||||
|
const arr_alloca = blk: {
|
||||||
|
if (node.data == .identifier) {
|
||||||
|
if (self.named_values.get(node.data.identifier.name)) |entry| {
|
||||||
|
break :blk entry.ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.data == .field_access) {
|
||||||
|
break :blk try self.genAddressOf(node);
|
||||||
|
}
|
||||||
|
break :blk try self.genExpr(node);
|
||||||
|
};
|
||||||
|
const i64_ty = c.LLVMInt64TypeInContext(self.context);
|
||||||
|
const zero = c.LLVMConstInt(i64_ty, 0, 0);
|
||||||
|
var indices = [_]c.LLVMValueRef{ zero, zero };
|
||||||
|
return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_decay");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice to many-pointer coercion: []T → [*]T (extract .ptr from fat pointer)
|
||||||
|
if (target_ty.isManyPointer()) {
|
||||||
|
const src_ty = self.inferType(node);
|
||||||
|
if (src_ty.isSlice()) {
|
||||||
|
if (std.mem.eql(u8, src_ty.slice_type.element_name, target_ty.many_pointer_type.element_name)) {
|
||||||
|
const slice_val = try self.genExpr(node);
|
||||||
|
return c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_decay");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implicit address-of: passing T where *T is expected → auto &
|
// Implicit address-of: passing T where *T is expected → auto &
|
||||||
if (target_ty.isPointer()) {
|
if (target_ty.isPointer()) {
|
||||||
const src_ty = self.inferType(node);
|
const src_ty = self.inferType(node);
|
||||||
@@ -3390,6 +3453,8 @@ pub const CodeGen = struct {
|
|||||||
std.mem.eql(u8, src_ty.struct_type, pointee_name) or
|
std.mem.eql(u8, src_ty.struct_type, pointee_name) or
|
||||||
(if (self.type_aliases.get(src_ty.struct_type)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or
|
(if (self.type_aliases.get(src_ty.struct_type)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or
|
||||||
(if (self.type_aliases.get(pointee_name)) |alias| std.mem.eql(u8, alias, src_ty.struct_type) else false)
|
(if (self.type_aliases.get(pointee_name)) |alias| std.mem.eql(u8, alias, src_ty.struct_type) else false)
|
||||||
|
else if (Type.fromName(pointee_name)) |pointee_ty|
|
||||||
|
src_ty.eql(pointee_ty)
|
||||||
else
|
else
|
||||||
false;
|
false;
|
||||||
if (src_matches) {
|
if (src_matches) {
|
||||||
@@ -3442,7 +3507,7 @@ pub const CodeGen = struct {
|
|||||||
/// Convert an LLVM value from src_ty to target_ty, emitting appropriate casts.
|
/// Convert an LLVM value from src_ty to target_ty, emitting appropriate casts.
|
||||||
fn convertValue(self: *CodeGen, val: c.LLVMValueRef, src_ty: Type, target_ty: Type) c.LLVMValueRef {
|
fn convertValue(self: *CodeGen, val: c.LLVMValueRef, src_ty: Type, target_ty: Type) c.LLVMValueRef {
|
||||||
// Same type → return as-is
|
// Same type → return as-is
|
||||||
if (std.meta.eql(src_ty, target_ty)) return val;
|
if (src_ty.eql(target_ty)) return val;
|
||||||
|
|
||||||
// string <-> []u8: identical LLVM type {ptr, i64}, no conversion needed
|
// string <-> []u8: identical LLVM type {ptr, i64}, no conversion needed
|
||||||
if ((src_ty == .string_type and target_ty.isSlice() and
|
if ((src_ty == .string_type and target_ty.isSlice() and
|
||||||
@@ -3574,6 +3639,16 @@ pub const CodeGen = struct {
|
|||||||
return c.LLVMBuildIntToPtr(self.builder, val, c.LLVMPointerTypeInContext(self.context, 0), "inttoptr");
|
return c.LLVMBuildIntToPtr(self.builder, val, c.LLVMPointerTypeInContext(self.context, 0), "inttoptr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slice/string → pointer: extract .ptr from fat pointer
|
||||||
|
if ((src_ty.isSlice() or src_ty == .string_type) and (target_ty.isPointer() or target_ty.isManyPointer())) {
|
||||||
|
return c.LLVMBuildExtractValue(self.builder, val, 0, "slice_to_ptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
// *[N]T → [*]T: pointer to array decays to many-pointer (both opaque ptrs, no-op)
|
||||||
|
if (src_ty.isPointer() and target_ty.isManyPointer()) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
// Pointer → function_type or function_type → pointer: both are opaque pointers, no-op
|
// 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()) {
|
if ((src_ty.isPointer() or src_ty.isManyPointer()) and target_ty.isFunctionType()) {
|
||||||
return val;
|
return val;
|
||||||
@@ -4170,11 +4245,11 @@ pub const CodeGen = struct {
|
|||||||
return c.LLVMBuildExtractElement(self.builder, vec_val, idx, "vidx");
|
return c.LLVMBuildExtractElement(self.builder, vec_val, idx, "vidx");
|
||||||
}
|
}
|
||||||
if (obj_ty.isArray()) {
|
if (obj_ty.isArray()) {
|
||||||
// Array index: load from GEP
|
const arr_info = obj_ty.array_type;
|
||||||
|
const elem_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name});
|
||||||
|
// Array index via identifier: load from GEP
|
||||||
if (ie.object.data == .identifier) {
|
if (ie.object.data == .identifier) {
|
||||||
if (self.named_values.get(ie.object.data.identifier.name)) |entry| {
|
if (self.named_values.get(ie.object.data.identifier.name)) |entry| {
|
||||||
const arr_info = obj_ty.array_type;
|
|
||||||
const elem_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name});
|
|
||||||
const idx = try self.genExpr(ie.index);
|
const idx = try self.genExpr(ie.index);
|
||||||
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0);
|
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0);
|
||||||
var indices = [_]c.LLVMValueRef{ zero, idx };
|
var indices = [_]c.LLVMValueRef{ zero, idx };
|
||||||
@@ -4182,6 +4257,15 @@ pub const CodeGen = struct {
|
|||||||
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "arrval");
|
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "arrval");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Array index via field access: GEP through struct field
|
||||||
|
if (ie.object.data == .field_access) {
|
||||||
|
const field_ptr = try self.genAddressOf(ie.object);
|
||||||
|
const idx = try self.genExpr(ie.index);
|
||||||
|
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0);
|
||||||
|
var indices = [_]c.LLVMValueRef{ zero, idx };
|
||||||
|
const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx");
|
||||||
|
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "field_arrval");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (obj_ty == .string_type) {
|
if (obj_ty == .string_type) {
|
||||||
// String indexing: extract ptr from slice, GEP + load u8
|
// String indexing: extract ptr from slice, GEP + load u8
|
||||||
@@ -4730,9 +4814,11 @@ pub const CodeGen = struct {
|
|||||||
const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr");
|
const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr");
|
||||||
|
|
||||||
// Build LLVM function type from FunctionTypeInfo
|
// Build LLVM function type from FunctionTypeInfo
|
||||||
|
const ptr_ty_llvm = c.LLVMPointerTypeInContext(self.context, 0);
|
||||||
var param_llvm_types: [64]c.LLVMTypeRef = undefined;
|
var param_llvm_types: [64]c.LLVMTypeRef = undefined;
|
||||||
for (fti.param_types, 0..) |pt, i| {
|
for (fti.param_types, 0..) |pt, i| {
|
||||||
param_llvm_types[i] = self.typeToLLVM(pt);
|
// [N]T and [:0]T params are pointers at the ABI level
|
||||||
|
param_llvm_types[i] = if (pt.isArray() or pt == .string_type) ptr_ty_llvm else self.typeToLLVM(pt);
|
||||||
}
|
}
|
||||||
const ret_llvm = self.typeToLLVM(fti.return_type.*);
|
const ret_llvm = self.typeToLLVM(fti.return_type.*);
|
||||||
const fn_type = c.LLVMFunctionType(
|
const fn_type = c.LLVMFunctionType(
|
||||||
@@ -4746,7 +4832,18 @@ pub const CodeGen = struct {
|
|||||||
var arg_vals = std.ArrayList(c.LLVMValueRef).empty;
|
var arg_vals = std.ArrayList(c.LLVMValueRef).empty;
|
||||||
for (call_node.args, 0..) |arg, i| {
|
for (call_node.args, 0..) |arg, i| {
|
||||||
if (i < fti.param_types.len) {
|
if (i < fti.param_types.len) {
|
||||||
try arg_vals.append(self.allocator, try self.genExprAsType(arg, fti.param_types[i]));
|
const pt = fti.param_types[i];
|
||||||
|
// [N]T params: pass pointer via array decay
|
||||||
|
if (pt.isArray()) {
|
||||||
|
const decay_target: Type = .{ .many_pointer_type = .{ .element_name = pt.array_type.element_name } };
|
||||||
|
try arg_vals.append(self.allocator, try self.genExprAsType(arg, decay_target));
|
||||||
|
} else if (pt == .string_type) {
|
||||||
|
// [:0]u8 params: extract .ptr from fat pointer
|
||||||
|
const val = try self.genExprAsType(arg, pt);
|
||||||
|
try arg_vals.append(self.allocator, c.LLVMBuildExtractValue(self.builder, val, 0, "str_ptr"));
|
||||||
|
} else {
|
||||||
|
try arg_vals.append(self.allocator, try self.genExprAsType(arg, pt));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
try arg_vals.append(self.allocator, try self.genExpr(arg));
|
try arg_vals.append(self.allocator, try self.genExpr(arg));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,38 @@ pub const Type = union(enum) {
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Content-based equality: compares string fields by content, not pointer identity.
|
||||||
|
pub fn eql(self: Type, other: Type) bool {
|
||||||
|
const Tag = std.meta.Tag(Type);
|
||||||
|
const self_tag: Tag = self;
|
||||||
|
const other_tag: Tag = other;
|
||||||
|
if (self_tag != other_tag) return false;
|
||||||
|
return switch (self) {
|
||||||
|
.signed => |w| w == other.signed,
|
||||||
|
.unsigned => |w| w == other.unsigned,
|
||||||
|
.f32, .f64, .void_type, .boolean, .string_type, .any_type => true,
|
||||||
|
.enum_type => |n| std.mem.eql(u8, n, other.enum_type),
|
||||||
|
.struct_type => |n| std.mem.eql(u8, n, other.struct_type),
|
||||||
|
.union_type => |n| std.mem.eql(u8, n, other.union_type),
|
||||||
|
.array_type => |info| info.length == other.array_type.length and
|
||||||
|
std.mem.eql(u8, info.element_name, other.array_type.element_name),
|
||||||
|
.slice_type => |info| std.mem.eql(u8, info.element_name, other.slice_type.element_name),
|
||||||
|
.pointer_type => |info| std.mem.eql(u8, info.pointee_name, other.pointer_type.pointee_name),
|
||||||
|
.many_pointer_type => |info| std.mem.eql(u8, info.element_name, other.many_pointer_type.element_name),
|
||||||
|
.vector_type => |info| info.length == other.vector_type.length and
|
||||||
|
std.mem.eql(u8, info.element_name, other.vector_type.element_name),
|
||||||
|
.function_type => |info| {
|
||||||
|
const o = other.function_type;
|
||||||
|
if (info.param_types.len != o.param_types.len) return false;
|
||||||
|
for (info.param_types, o.param_types) |a, b| {
|
||||||
|
if (!a.eql(b)) return false;
|
||||||
|
}
|
||||||
|
return info.return_type.eql(o.return_type.*);
|
||||||
|
},
|
||||||
|
.meta_type => |info| std.mem.eql(u8, info.name, other.meta_type.name),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Convenience constructors
|
// Convenience constructors
|
||||||
pub fn s(width: u8) Type {
|
pub fn s(width: u8) Type {
|
||||||
return .{ .signed = width };
|
return .{ .signed = width };
|
||||||
@@ -91,6 +123,17 @@ pub const Type = union(enum) {
|
|||||||
if (name.len >= 2 and name[0] == '*') {
|
if (name.len >= 2 and name[0] == '*') {
|
||||||
return .{ .pointer_type = .{ .pointee_name = name[1..] } };
|
return .{ .pointer_type = .{ .pointee_name = name[1..] } };
|
||||||
}
|
}
|
||||||
|
// Vector: Vector(N,T)
|
||||||
|
if (name.len >= 10 and std.mem.startsWith(u8, name, "Vector(") and name[name.len - 1] == ')') {
|
||||||
|
const inner = name[7 .. name.len - 1]; // contents between ( and )
|
||||||
|
if (std.mem.indexOfScalar(u8, inner, ',')) |comma| {
|
||||||
|
const length = std.fmt.parseInt(u32, inner[0..comma], 10) catch return null;
|
||||||
|
const elem_name = inner[comma + 1 ..];
|
||||||
|
if (elem_name.len > 0) {
|
||||||
|
return .{ .vector_type = .{ .element_name = elem_name, .length = length } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Variable-width integers: s1..s64, u1..u64
|
// Variable-width integers: s1..s64, u1..u64
|
||||||
if (name.len >= 2 and (name[0] == 's' or name[0] == 'u')) {
|
if (name.len >= 2 and (name[0] == 's' or name[0] == 'u')) {
|
||||||
const width = std.fmt.parseInt(u8, name[1..], 10) catch return null;
|
const width = std.fmt.parseInt(u8, name[1..], 10) catch return null;
|
||||||
@@ -263,29 +306,16 @@ pub const Type = union(enum) {
|
|||||||
/// - Float to wider float (f32 → f64)
|
/// - Float to wider float (f32 → f64)
|
||||||
/// Everything else requires `xx`.
|
/// Everything else requires `xx`.
|
||||||
pub fn isImplicitlyConvertibleTo(self: Type, target: Type) bool {
|
pub fn isImplicitlyConvertibleTo(self: Type, target: Type) bool {
|
||||||
if (std.meta.eql(self, target)) return true;
|
if (self.eql(target)) return true;
|
||||||
// Struct types: compare names by content (not pointer identity)
|
|
||||||
if (self.isStruct() and target.isStruct()) {
|
|
||||||
return std.mem.eql(u8, self.struct_type, target.struct_type);
|
|
||||||
}
|
|
||||||
// Slice types: compare element names by content (not pointer)
|
|
||||||
if (self.isSlice() and target.isSlice()) {
|
|
||||||
return std.mem.eql(u8, self.slice_type.element_name, target.slice_type.element_name);
|
|
||||||
}
|
|
||||||
// string <-> []u8: same layout, bidirectional implicit conversion
|
// string <-> []u8: same layout, bidirectional implicit conversion
|
||||||
if (self == .string_type and target.isSlice() and
|
if (self == .string_type and target.isSlice() and
|
||||||
std.mem.eql(u8, target.slice_type.element_name, "u8")) return true;
|
std.mem.eql(u8, target.slice_type.element_name, "u8")) return true;
|
||||||
if (self.isSlice() and std.mem.eql(u8, self.slice_type.element_name, "u8") and
|
if (self.isSlice() and std.mem.eql(u8, self.slice_type.element_name, "u8") and
|
||||||
target == .string_type) return true;
|
target == .string_type) return true;
|
||||||
// Pointer types: compare pointee names by content, *void is universal (both directions)
|
// *void is universal pointer (both directions)
|
||||||
if (self.isPointer() and target.isPointer()) {
|
if (self.isPointer() and target.isPointer()) {
|
||||||
if (std.mem.eql(u8, self.pointer_type.pointee_name, "void")) return true;
|
if (std.mem.eql(u8, self.pointer_type.pointee_name, "void")) return true;
|
||||||
if (std.mem.eql(u8, target.pointer_type.pointee_name, "void")) return true;
|
if (std.mem.eql(u8, target.pointer_type.pointee_name, "void")) return true;
|
||||||
return std.mem.eql(u8, self.pointer_type.pointee_name, target.pointer_type.pointee_name);
|
|
||||||
}
|
|
||||||
// Many-pointer types: compare element names by content
|
|
||||||
if (self.isManyPointer() and target.isManyPointer()) {
|
|
||||||
return std.mem.eql(u8, self.many_pointer_type.element_name, target.many_pointer_type.element_name);
|
|
||||||
}
|
}
|
||||||
// *T → [*]T: pointer to element is implicitly convertible to many-pointer
|
// *T → [*]T: pointer to element is implicitly convertible to many-pointer
|
||||||
// null (*void) → [*]T is also allowed
|
// null (*void) → [*]T is also allowed
|
||||||
@@ -418,7 +448,7 @@ pub const Type = union(enum) {
|
|||||||
/// Used for arithmetic type promotion (e.g., s16 + s32 → s32, int + float → float).
|
/// Used for arithmetic type promotion (e.g., s16 + s32 → s32, int + float → float).
|
||||||
pub fn widen(a: Type, b: Type) Type {
|
pub fn widen(a: Type, b: Type) Type {
|
||||||
// Same type → return it
|
// Same type → return it
|
||||||
if (std.meta.eql(a, b)) return a;
|
if (a.eql(b)) return a;
|
||||||
|
|
||||||
// Vector + vector of same dimensions → return a
|
// Vector + vector of same dimensions → return a
|
||||||
if (a.isVector() and b.isVector()) return a;
|
if (a.isVector() and b.isVector()) return a;
|
||||||
|
|||||||
Reference in New Issue
Block a user