This commit is contained in:
agra
2026-02-20 21:50:49 +02:00
parent 2f95810f9d
commit efa60fa670
12 changed files with 148 additions and 103 deletions

View File

@@ -15,8 +15,7 @@ Matrix44 :: struct {
}
mat4_zero :: (m: *Matrix44) {
i := 0;
while i < 16 { m.data[i] = 0.0; i += 1; }
memset(xx @m.data[0], 0, 16 * size_of(f32));
}
mat4_identity :: (m: *Matrix44) {
@@ -41,8 +40,7 @@ mat4_multiply :: (out: *Matrix44, a: *Matrix44, b: *Matrix44) {
}
j += 1;
}
i := 0;
while i < 16 { out.data[i] = tmp[i]; i += 1; }
out.data = tmp;
}
mat4_perspective :: (m: *Matrix44, fov: f32, aspect: f32, near: f32, far: f32) {
@@ -149,7 +147,7 @@ main :: () {
glDepthFunc(GL_LESS);
// Shaders
vert_src : [:0]u8 = #string GLSL
vert_src : [:0]u8 = #string glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
@@ -162,7 +160,7 @@ void main() {
vNormal = aNormal;
vPos = aPos;
}
GLSL;
glsl;
frag_src : [:0]u8 = #string GLSL
#version 330 core

View File

@@ -58,7 +58,7 @@ main :: () -> s32 {
close(client);
}
arena_destroy(@arena);
arena_deinit(@arena);
close(fd);
0;
}

View File

@@ -545,6 +545,14 @@ END;
mpw_ptr[2] = 99;
print("mp-write: {}\n", mpw[2]);
// Pointer-null comparison
np : *s32 = null;
print("ptr==null: {}\n", np == null);
print("ptr!=null: {}\n", np != null);
np2 := @pv.x;
print("ptr2==null: {}\n", np2 == null);
print("ptr2!=null: {}\n", np2 != null);
// --- Vectors ---
vc := vec3(1, 3, 2);
print("vec-construct: {}\n", vc);

View File

@@ -27,53 +27,49 @@ gpa_free :: (ctx: *void, ptr: *void) {
}
gpa_create :: (gpa: *GPA) -> Allocator {
ctx : *void = xx gpa;
Allocator.{ ctx = ctx, alloc = gpa_alloc, free = gpa_free };
Allocator.{ ctx = gpa, alloc = gpa_alloc, free = gpa_free };
}
// --- Arena: multi-chunk bump allocator (Zig-inspired) ---
ArenaChunk :: struct {
next: *void; // *ArenaChunk
cap: s64; // total chunk size including this header
next: *ArenaChunk;
cap: s64;
}
Arena :: struct {
first: *void; // *ArenaChunk — head of list (newest first)
end_index: s64; // bump position within current chunk's usable area
parent: Allocator; // backing allocator
first: *ArenaChunk;
end_index: s64;
parent: Allocator;
}
arena_add_chunk :: (a: *Arena, min_size: s64) {
first_i : s64 = xx a.first;
prev_cap := if first_i != 0 then { c : *ArenaChunk = xx a.first; c.cap; } else 0;
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(a.parent.ctx, len);
chunk : *ArenaChunk = xx raw;
chunk.next = a.first;
chunk.cap = len;
a.first = xx chunk;
a.first = chunk;
a.end_index = 0;
}
arena_alloc :: (ctx: *void, size: s64) -> *void {
a : *Arena = xx ctx;
aligned := (size + 7) & (0 - 8);
first_i : s64 = xx a.first;
if first_i != 0 {
chunk : *ArenaChunk = xx a.first;
usable := chunk.cap - 16;
if a.first != null {
usable := a.first.cap - 16;
if a.end_index + aligned <= usable {
buf : [*]u8 = xx a.first;
ptr : *void = xx @buf[16 + a.end_index];
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 : *void = xx @buf[16 + a.end_index];
ptr := @buf[16 + a.end_index];
a.end_index = a.end_index + aligned;
ptr;
}
@@ -86,34 +82,29 @@ arena_create :: (a: *Arena, parent: Allocator, size: s64) -> Allocator {
a.end_index = 0;
a.parent = parent;
arena_add_chunk(a, size);
ctx : *void = xx a;
Allocator.{ ctx = ctx, alloc = arena_alloc, free = arena_free };
Allocator.{ ctx = a, alloc = arena_alloc, free = arena_free };
}
arena_reset :: (a: *Arena) {
// Keep first chunk (newest/largest), free the rest
first_i : s64 = xx a.first;
if first_i != 0 {
chunk : *ArenaChunk = xx a.first;
it : s64 = xx chunk.next;
while it != 0 {
c : *ArenaChunk = xx it;
next_i : s64 = xx c.next;
a.parent.free(a.parent.ctx, xx c);
it = next_i;
if a.first != null {
it := a.first.next;
while it != null {
next := it.next;
a.parent.free(a.parent.ctx, it);
it = next;
}
chunk.next = null;
a.first.next = null;
}
a.end_index = 0;
}
arena_deinit :: (a: *Arena) {
it : s64 = xx a.first;
while it != 0 {
c : *ArenaChunk = xx it;
next_i : s64 = xx c.next;
a.parent.free(a.parent.ctx, xx c);
it = next_i;
it := a.first;
while it != null {
next := it.next;
a.parent.free(a.parent.ctx, it);
it = next;
}
a.first = null;
a.end_index = 0;
@@ -133,7 +124,7 @@ buf_alloc :: (ctx: *void, size: s64) -> *void {
if b.pos + aligned > b.len {
return null;
}
ptr : *void = xx @b.buf[b.pos];
ptr := @b.buf[b.pos];
b.pos = b.pos + aligned;
ptr;
}
@@ -145,8 +136,7 @@ buf_create :: (b: *BufAlloc, buf: [*]u8, len: s64) -> Allocator {
b.buf = buf;
b.len = len;
b.pos = 0;
ctx : *void = xx b;
Allocator.{ ctx = ctx, alloc = buf_alloc, free = buf_free };
Allocator.{ ctx = b, alloc = buf_alloc, free = buf_free };
}
buf_reset :: (b: *BufAlloc) {

View File

@@ -29,5 +29,5 @@ SockAddr :: struct {
}
htons :: (port: s64) -> u16 {
cast(u16) ((port / 256) | ((port % 256) * 256));
cast(u16) (((port & 0xFF) << 8) | ((port >> 8) & 0xFF));
}

View File

@@ -32,8 +32,7 @@ context : Context = ---;
// --- Slice & string allocation ---
cstring :: (size: s64) -> string {
p : s64 = xx context.allocator.ctx;
raw := if p != 0 then context.allocator.alloc(context.allocator.ctx, size + 1) else malloc(size + 1);
raw := if context.allocator.ctx != null then context.allocator.alloc(context.allocator.ctx, size + 1) else malloc(size + 1);
memset(raw, 0, size + 1);
s : string = ---;
s.ptr = xx raw;
@@ -42,8 +41,7 @@ cstring :: (size: s64) -> string {
}
alloc_slice :: ($T: Type, count: s64) -> []T {
p : s64 = xx context.allocator.ctx;
raw := if p != 0 then context.allocator.alloc(context.allocator.ctx, count * size_of(T)) else malloc(count * size_of(T));
raw := if context.allocator.ctx != null then context.allocator.alloc(context.allocator.ctx, count * size_of(T)) else malloc(count * size_of(T));
memset(raw, 0, count * size_of(T));
s : []T = ---;
s.ptr = xx raw;
@@ -55,19 +53,16 @@ int_to_string :: (n: s64) -> string {
if n == 0 { return "0"; }
neg := n < 0;
v := if neg then 0 - n else n;
tmp := v;
len := 0;
while tmp > 0 { len += 1; tmp = tmp / 10; }
total := if neg then len + 1 else len;
buf := cstring(total);
i := total - 1;
// Single pass: fill digits backwards into temp string, then substr
tmp := cstring(20);
i := 19;
while v > 0 {
buf[i] = (v % 10) + 48;
tmp[i] = (v % 10) + 48;
v = v / 10;
i -= 1;
}
if neg { buf[0] = 45; }
buf;
if neg { tmp[i] = 45; i -= 1; }
substr(tmp, i + 1, 20 - i - 1);
}
bool_to_string :: (b: bool) -> string {
@@ -100,6 +95,17 @@ float_to_string :: (f: f64) -> string {
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"; }
@@ -117,42 +123,11 @@ int_to_hex_string :: (n: s64) -> string {
if g3 < 0 { g3 = g3 + 65536; }
buf := cstring(16);
// Group 3: digits 0-3 (bits 48-63)
i := 3;
v := g3;
while i >= 0 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Group 2: digits 4-7 (bits 32-47)
i = 7;
v = g2;
while i >= 4 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Group 1: digits 8-11 (bits 16-31)
i = 11;
v = g1;
while i >= 8 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Group 0: digits 12-15 (bits 0-15)
i = 15;
v = g0;
while i >= 12 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
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 {

View File

@@ -775,7 +775,7 @@ pub const CodeGen = struct {
},
.vector_type, .array_type => self.allocaStoreAsI64(self.typeToLLVM(ty), val, "any_vec"),
.slice_type => self.allocaStoreAsI64(self.getStringStructType(), val, "any_slice"),
.pointer_type, .many_pointer_type => self.ptrToInt(val, "any_ptr"),
.pointer_type, .many_pointer_type, .function_type => self.ptrToInt(val, "any_ptr"),
.meta_type => |mt| self.allocaStoreAsI64(self.getStringStructType(), self.buildStringSlice(val, self.constInt64(mt.name.len)), "any_type"),
else => self.sExt(val, i64_ty, "any_val"),
};
@@ -3299,8 +3299,27 @@ pub const CodeGen = struct {
fn genFieldAssignment(self: *CodeGen, asgn: ast.Assignment) !c.LLVMValueRef {
const fa = asgn.target.data.field_access;
// Object must be an identifier for now
if (fa.object.data != .identifier) return self.emitError("field assignment target must be a variable");
// Non-identifier object (e.g. a.first.next = val — chained pointer field assignment)
if (fa.object.data != .identifier) {
const obj_ty = self.inferType(fa.object);
if (obj_ty.isPointer()) {
// Chained pointer auto-deref: expr.field = val where expr is *Struct
const pointee_ty = self.resolveTypeFromName(obj_ty.pointer_type.pointee_name) orelse
return self.emitError("unknown pointee type for chained field assignment");
if (pointee_ty.isStruct()) {
const sname = pointee_ty.struct_type;
const info = try self.getStructInfo(sname);
const fi = try self.findFieldIndex(info.field_names, fa.field, sname);
const field_ty = info.field_types[fi];
const ptr_val = try self.genExpr(fa.object);
const gep = self.structGEP(info.llvm_type, ptr_val, @intCast(fi), "chain_pfield_ptr");
const rhs = try self.genExprAsType(asgn.value, field_ty);
self.storeOrCompound(asgn.op, gep, rhs, field_ty, "chain_pcur");
return null;
}
}
return self.emitError("field assignment target must be a variable");
}
const obj_name = fa.object.data.identifier.name;
const entry = self.getNamedOrGlobal(obj_name) orelse return self.emitErrorFmt("undefined variable '{s}'", .{obj_name});
@@ -3970,6 +3989,14 @@ pub const CodeGen = struct {
return;
}
// Forward declaration: register name early so self-referential *T works
try self.type_registry.put(sd.name, .{ .struct_info = .{
.field_names = &.{},
.field_types = &.{},
.field_defaults = &.{},
.llvm_type = null,
} });
// Pre-pass: hoist inline type declarations from field types
for (sd.field_types, 0..) |ft, i| {
try self.hoistInlineTypeDecl(sd.name, sd.field_names[i], ft);
@@ -4008,6 +4035,15 @@ pub const CodeGen = struct {
}
fn registerTaggedEnum(self: *CodeGen, ud: ast.EnumDecl) !void {
// Forward declaration: register name early so self-referential *T works
try self.type_registry.put(ud.name, .{ .tagged_enum = .{
.variant_names = &.{},
.variant_types = &.{},
.llvm_type = null,
.max_payload_size = 0,
.payload_field_index = 0,
} });
// Pre-pass: hoist inline type declarations from variant types
for (ud.variant_types, 0..) |vt_opt, i| {
if (vt_opt) |vt| {
@@ -4189,6 +4225,15 @@ pub const CodeGen = struct {
}
fn registerUnionType(self: *CodeGen, ud: ast.UnionDecl) !void {
// Forward declaration: register name early so self-referential *T works
try self.type_registry.put(ud.name, .{ .union_info = .{
.field_names = &.{},
.field_types = &.{},
.llvm_type = null,
.total_size = 0,
.promoted_fields = std.StringHashMap(PromotedField).init(self.allocator),
} });
// Hoist inline type declarations from field types
for (ud.field_types, 0..) |ft, i| {
try self.hoistInlineTypeDecl(ud.name, ud.field_names[i], ft);
@@ -5524,6 +5569,24 @@ pub const CodeGen = struct {
// Non-identifier object: evaluate expression and check type
const obj_val = try self.genExpr(fa.object);
const obj_ty = self.inferType(fa.object);
// Pointer auto-deref: expr.field where expr is *T → load through pointer
if (obj_ty.isPointer()) {
const pointee_ty = self.resolveTypeFromName(obj_ty.pointer_type.pointee_name) orelse
return self.emitError("unknown pointee type for auto-deref");
if (pointee_ty.isStruct()) {
const sname = pointee_ty.struct_type;
const info = try self.getStructInfo(sname);
const idx = self.findNameIndex(info.field_names, fa.field) orelse
return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
const gep = self.structGEP(info.llvm_type, obj_val, @intCast(idx), "pfield");
return self.loadTyped(info.field_types[idx], gep, "pfieldval");
}
if (pointee_ty.isSlice()) {
const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), obj_val, "pslice_load");
return self.extractFatPtrField(slice_val, fa.field, "*slice");
}
return self.emitErrorFmt("no field '{s}' on pointer", .{fa.field});
}
if (obj_ty.isVector()) {
return self.genVectorExtract(obj_val, fa.field);
}

View File

@@ -1122,9 +1122,9 @@ pub const VM = struct {
alloc_field_names[0] = "ctx";
alloc_field_names[1] = "alloc";
alloc_field_names[2] = "free";
alloc_fields[0] = .{ .int_val = 0 }; // null ctx pointer
alloc_fields[1] = .{ .int_val = 0 }; // null alloc
alloc_fields[2] = .{ .int_val = 0 }; // null free
alloc_fields[0] = .{ .null_val = {} }; // null ctx pointer
alloc_fields[1] = .{ .null_val = {} }; // null alloc
alloc_fields[2] = .{ .null_val = {} }; // null free
const ctx_fields = try self.allocator.alloc(Value, 2);
const ctx_field_names = try self.allocator.alloc([]const u8, 2);
@@ -1771,8 +1771,10 @@ pub const VM = struct {
if (a == .int_val and b == .int_val) return a.int_val == b.int_val;
if (a == .bool_val and b == .bool_val) return a.bool_val == b.bool_val;
if (a == .string_val and b == .string_val) return std.mem.eql(u8, a.string_val, b.string_val);
// Pointer comparison
// Pointer comparison (null_val == int_val(0) for null pointer compatibility)
if (a == .null_val and b == .null_val) return true;
if (a == .null_val and b == .int_val) return b.int_val == 0;
if (a == .int_val and b == .null_val) return a.int_val == 0;
if (a == .null_val or b == .null_val) return false;
if (a == .pointer_val and b == .pointer_val) return a.pointer_val.target == b.pointer_val.target;
// Float comparison

View File

@@ -197,7 +197,7 @@ pub const DocumentStore = struct {
.ty = sym.ty,
.def_span = sym.def_span,
.scope_depth = 0,
.origin = file_path,
.origin = sym.origin orelse file_path,
});
}
}
@@ -263,7 +263,7 @@ pub const DocumentStore = struct {
.ty = sym.ty,
.def_span = sym.def_span,
.scope_depth = 0,
.origin = imp.path,
.origin = sym.origin orelse imp.path,
});
}
}

View File

@@ -555,6 +555,8 @@ pub const Analyzer = struct {
for (indices.items) |idx| {
if (idx >= scope_start) {
const sym = self.symbols.items[idx];
// Skip imported symbols — local declarations are allowed to shadow them
if (sym.origin != null) continue;
if (sym.scope_depth == self.scope_depth) {
try self.diagnostics.append(self.allocator, .{
.level = .warn,

View File

@@ -531,6 +531,9 @@ pub const Type = union(enum) {
return Type.s(capped);
}
// Pointer types: both are pointers → return first (all are opaque ptr at LLVM level)
if ((a.isPointer() or a.isManyPointer()) and (b.isPointer() or b.isManyPointer())) return a;
return a;
}
};

View File

@@ -133,6 +133,10 @@ auto-deref: 10
mp[0]: 10
mp[3]: 40
mp-write: 99
ptr==null: true
ptr!=null: false
ptr2==null: false
ptr2!=null: true
vec-construct: [1.000000, 3.000000, 2.000000]
vec-add: [5.000000, 7.000000, 9.000000]
vec-sub: [4.000000, 3.000000, 2.000000]