From cd4ad810a0c7519d6ca392557ce0821cbf5982ed Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Sun, 7 Sep 2025 15:25:57 -0600 Subject: [PATCH] various changes --- +internal/allocators.jai | 76 ---------- +internal/arenas.jai | 259 ++++++++++++++++++++++++++++++++++ +internal/array.jai | 177 +++++++++++------------ +internal/builtin.jai | 38 ++++- +internal/keywords.jai | 53 ++----- +internal/{kv.jai => map.jai} | 71 +++++----- +internal/memory.jai | 65 +++++++-- +internal/testing.jai | 57 ++++---- +internal/type_info.jai | 31 ++-- .gitignore | 1 + PLAN.Judah | 17 ++- fmt/base64.jai | 26 ++-- math/common.jai | 2 +- math/ease.jai | 2 +- math/module.jai | 2 +- math/vec.jai | 24 ++-- module.jai | 7 +- 17 files changed, 569 insertions(+), 339 deletions(-) delete mode 100644 +internal/allocators.jai create mode 100644 +internal/arenas.jai rename +internal/{kv.jai => map.jai} (67%) diff --git a/+internal/allocators.jai b/+internal/allocators.jai deleted file mode 100644 index 7ff9518..0000000 --- a/+internal/allocators.jai +++ /dev/null @@ -1,76 +0,0 @@ -AllocatorMode :: enum { - using Allocator_Mode; - - // Compatability - Allocate :: ALLOCATE; - Resize :: RESIZE; - Free :: FREE; - Startup :: STARTUP; - Shutdown :: SHUTDOWN; - ThreadStart :: THREAD_START; - ThreadStop :: THREAD_STOP; - CreateHeap :: CREATE_HEAP; - DestroyHeap :: DESTROY_HEAP; - IsThisYours :: IS_THIS_YOURS; - Capabilities :: CAPS; -} - -/// PanicAllocator causes the program to panic when used. -PanicAllocator :: () -> Allocator { - return .{ proc = xx PanicAllocatorProc }; -} - -TrySetAllocator :: (thing: *$T, allocator := context.allocator) #modify { - info := T.(*Type_Info_Struct); - - ok := false; - if info.type == .STRUCT - { ok = true; } - - if ok for info.members if it.name == "allocator" && it.type == Allocator.(*Type_Info) { - ok = true; - break; - } - - return ok, "can only set allocator on struct with an allocator field or dynamic array"; -} #expand { - if thing.allocator.proc == null { - thing.allocator = allocator; - } -} - -TrySetAllocator :: (array: *[..]$T, allocator := context.allocator) #expand { - if array.allocator.proc == null { - array.allocator = allocator; - } -} - - -#scope_module - -PanicAllocatorProc :: (mode: AllocatorMode, new_size: int, old_size: int, ptr: *void, allocator_data: *void) -> *void { - if mode == { - case .Allocate; - if new_size > 0 { - Panic("jc: allocation using the PanicAllocator"); - } - case .Resize; - if new_size > 0 { - Panic("jc: reallocation using the PanicAllocator"); - } - case .Free; - if ptr != null { - Panic("jc: free using the PanicAllocator"); - } - - case .ThreadStart; #through; - case .ThreadStop; - Panic("jc: start/stop thread using the PanicAllocator"); - - case .CreateHeap; #through; - case .DestroyHeap; - Panic("jc: create/destroy heap using the PanicAllocator"); - } - - return null; -} @jc.nodocs diff --git a/+internal/arenas.jai b/+internal/arenas.jai new file mode 100644 index 0000000..c4b3c2a --- /dev/null +++ b/+internal/arenas.jai @@ -0,0 +1,259 @@ +// @todo(judah): Conditionally added to the context because it causes some weirdness in tests +#if !RunTests { + #add_context arena := Arena.{ proc = PagingArenaProc }; + #add_context temp_arena := Arena.{ proc = TempArenaProc }; +} + +Arena :: struct { + proc: ArenaProc; + data: *void; +} + +ArenaProc :: #type ( + mode: ArenaMode, + arena_data: *void, + + new_size: int, old_size: int, align: int, + new_ptr: *void, old_ptr: *void, + + caller: Source_Code_Location +) -> *void; + +ArenaMode :: enum { + Setup; + Teardown; + + Acquire; + Reset; + + Save; + Restore; +} + + +ArenaPush :: ($T: Type, loc := #caller_location) -> *T { + return ArenaPushSize(size_of(T), loc = loc).(*T); +} + +ArenaPushSize :: (size: int, align := DefaultAlign, loc := #caller_location) -> *void { + if size < 0 + { Panic("jc: size cannot be negative", loc = loc); } + TrySetupArena(*context.arena); + return context.arena.proc(.Acquire, context.arena.data, size, 0, align, null, null, loc); +} + +ArenaSaveResult :: #type,distinct *void; + +ArenaSave :: (loc := #caller_location) -> ArenaSaveResult { + TrySetupArena(*context.arena, loc); + return context.arena.proc(.Save, context.arena.data, 0, 0, 0, null, null, loc).(ArenaSaveResult); +} + +ArenaRestore :: (savepoint: ArenaSaveResult, loc := #caller_location) { + context.arena.proc(.Restore, context.arena.data, 0, 0, 0, null, savepoint.(*void), loc); +} + +ArenaAutoRestore :: () #expand { + wm := ArenaSave(); + `defer ArenaRestore(wm); +} + +ArenaReset :: (loc := #caller_location) { + TrySetupArena(*context.arena); + context.arena.proc(.Reset, context.arena.data, 0, 0, 0, null, null, loc); +} + +ArenaRelease :: (loc := #caller_location) { + TrySetupArena(*context.arena, loc); + context.arena.proc(.Teardown, context.arena.data, 0, 0, 0, null, null, loc); +} + +/// ArenaToAllocator wraps an arena, allowing it to be used with Jai's builtin Allocator system. +ArenaToAllocator :: (arena: *Arena) -> Allocator { + return .{ proc = JaiAllocatorProc, data = arena }; +} + + +PanicArena :: () -> Arena { + return .{ proc = PanicArenaProc }; +} + +BumpData :: struct { + memory: []u8; + offset: int; +} + +BumpArena :: (bump: *BumpData) -> Arena { + return .{ proc = BumpArenaProc, data = bump }; +} + +PagingArena :: () -> Arena { + return .{ proc = PagingArenaProc }; +} + + +#scope_module + +TrySetupArena :: (arena: *Arena, loc := #caller_location) #expand { + if arena.data != null + { return; } + + data := arena.proc(.Setup, null, 0, 0, 0, null, null, loc); + if data != null + { arena.data = data; } +} + +PanicArenaProc :: ( + mode: ArenaMode, + arena_data: *void, + + new_size: int, old_size: int, align: int, + new_ptr: *void, old_ptr: *void, + + caller: Source_Code_Location +) -> *void { + if mode == { + case .Acquire; + if new_size > 0 + { Panic("jc: cannot acquire memory using the PanicArena", loc = caller); } + + case .Save; + Panic("jc: cannot save using the PanicArean", loc = caller); + case .Restore; + Panic("jc: cannot restore using the PanicArean", loc = caller); + } + + return null; +} @jc.nodocs + +BumpArenaProc :: ( + mode: ArenaMode, + arena_data: *void, + + new_size: int, old_size: int, align: int, + new_ptr: *void, old_ptr: *void, + + caller: Source_Code_Location +) -> *void #no_abc { + bump := arena_data.(*BumpData); + if mode == { + case .Setup; + if bump.memory.data == null + { Panic("jc: BumpArena has no memory", loc = caller); } + return arena_data; + + case .Teardown; + // Do nothing, we don't own this memory + + case .Acquire; + end := bump.memory.data + bump.offset; + if new_size == 0 + { return end; } + + ptr := MemAlignForward(end, align.(uint)); + size := new_size + (ptr - end); + if bump.offset + size > bump.memory.count + { Panic("jc: BumpArena out of memory", loc = caller); } + + bump.offset += size; + return ptr; + case .Reset; + bump.offset = 0; + + case .Save; + return bump.offset.(*void); + case .Restore; + wm := old_ptr.(int); + if wm < 0 || wm >= bump.memory.count + { Panic("jc: BumpArena restored invalid savepoint", loc = caller); } + bump.offset = wm; + } + + return null; +} @jc.nodocs + +PagingArenaProc :: ( + mode: ArenaMode, + arena_data: *void, + + new_size: int, old_size: int, align: int, + new_ptr: *void, old_ptr: *void, + + caller: Source_Code_Location +) -> *void { + Panic("not implemented for PagingArena", loc = caller); + return null; +} @jc.nodocs + +// @note(judah): TempArenaProc just wraps BumpArenaProc, but provides its own backing memory. +// This is needed so we can use TempArenaProc without forcing the user to set everything up. +TempArenaProc :: ( + mode: ArenaMode, + arena_data: *void, + + new_size: int, old_size: int, align: int, + new_ptr: *void, old_ptr: *void, + + caller: Source_Code_Location +) -> *void { + // @temp(judah) + libc :: #library,system "libc"; + malloc :: (size: int) -> *void #c_call #foreign libc; + free :: (ptr: *void) #c_call #foreign libc; + + if mode == { + // @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory. + case .Setup; + mcount := AlignForward(size_of(BumpData) + 32768).(int); + memory := malloc(mcount).(*u8); + MemZero(memory, mcount); + + bump := memory.(*BumpData); + bump.memory.data = memory + size_of(BumpData); + bump.memory.count = mcount - size_of(BumpData); + + return bump; + + case .Teardown; + free(arena_data); + } + + return BumpArenaProc(mode, arena_data, new_size, old_size, align, new_ptr, old_ptr, caller); +} @jc.nodocs + + +#scope_file + +JaiAllocatorProc :: (mode: Allocator_Mode, requested_size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void { + CallerFromStackTrace :: () -> Source_Code_Location #expand { + node := context.stack_trace; + if node.next != null { + node = node.next; + } + return node.info.location; + } + + arena := allocator_data.(*Arena); + if mode == { + case .STARTUP; + TrySetupArena(arena); + return null; + + case .ALLOCATE; + loc := CallerFromStackTrace(); + return ArenaPushSize(requested_size, loc = loc,, arena = arena); + + case .RESIZE; + loc := CallerFromStackTrace(); + new := ArenaPushSize(requested_size, loc = loc,, arena = arena); + MemCopy(new, old_memory, old_size); + return new; + + case .FREE; + // Arenas don't free single pointers so give this a value that's obviously incorrect + MemOverwrite(old_memory, old_size, 0xAA); + } + + return null; +} @jc.nodocs + diff --git a/+internal/array.jai b/+internal/array.jai index 441d09b..cb658a2 100644 --- a/+internal/array.jai +++ b/+internal/array.jai @@ -1,28 +1,25 @@ -/// Append pushes value to the end of an array, resizing if necessary. +/// ArrayAppend pushes values to the end of an array, resizing if necessary. +/// A pointer to the first value appended will be returned. /// -/// Note: If no allocator has been set, Append will use the current context allocator. +/// Note: If no allocator has been set, ArrayAppend will use the current context allocator. /// Note: Calls to Append may invalidate pre-existing pointers. -Append :: inline (arr: *[..]$T, value: T) -> *T { +ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T { TrySetAllocator(arr); - ptr := basic.array_add(arr); - ptr.* = value; - return ptr; + + if values.count == 0 { + return basic.array_add(arr); + } + + count := arr.count; + basic.array_add(arr, ..values); + return *arr.data[count]; } -/// Append pushes a default initialized value to the end of an array, -/// returning a pointer to the newly pushed value. +/// ArrayGrow resizes the memory associated with an array to hold new_count values. /// -/// Note: If no allocator has been set, Append will use the current context allocator. -/// Note: Calls to Append may invalidate pre-existing pointers. -Append :: inline (arr: *[..]$T) -> *T { - return Append(arr, default_of(T)); -} - -/// Resize grows the memory associated with an array to hold new_count values. -/// -/// Note: If the array has enough allocated memory to accomodate new_count, Resize does nothing. -/// Note: Resize does not guarantee pointer stability. -Resize :: inline (arr: *[..]$T, new_count: int) { +/// Note: If the array has enough allocated memory to accomodate new_count, ArrayGrow does nothing. +/// Note: ArrayGrow does not guarantee pointer stability. +ArrayGrow :: inline (arr: *[..]$T, new_count: int) { if new_count <= arr.allocated { return; } @@ -30,32 +27,32 @@ Resize :: inline (arr: *[..]$T, new_count: int) { basic.array_reserve(arr, new_count,, allocator = arr.allocator); } -/// Slice returns a subsection of an array. -Slice :: (view: []$T, start_idx: int, count := -1, loc := #caller_location) -> []T { - AssertCallsite(start_idx >= +0 && start_idx < view.count, "jc: incorrect slice bounds"); - AssertCallsite(count >= -1 && count < view.count, "jc: incorrect slice length"); +/// ArraySlice returns a subsection of an array. +ArraySlice :: (arr: []$T, start_idx: int, count := -1, loc := #caller_location) -> []T { + AssertCallsite(start_idx >= +0 && start_idx < arr.count, "jc: incorrect slice bounds"); + AssertCallsite(count >= -1 && count < arr.count, "jc: incorrect slice length"); if count == -1 - { count = view.count - start_idx; } + { count = arr.count - start_idx; } - return .{ data = view.data + start_idx, count = count }; + return .{ data = arr.data + start_idx, count = count }; } -/// Reset sets an array's length to 0, but leaves its memory intact. -/// Note: To reset the associated memory as well, see Clear. -Reset :: (view: *[]$T) { - view.count = 0; +/// ArrayReset sets an array's length to 0, but leaves its memory intact. +/// Note: To reset the associated memory as well, see ArrayClear. +ArrayReset :: (arr: *[]$T) { + arr.count = 0; } -/// Clear zeroes an array's memory and sets its length to 0. -/// Note: To leave the associated memory intact, see Reset. -Clear :: (view: *[]$T) { - MemZero(view.data, view.count * size_of(T)); - view.count = 0; +/// ArrayClear zeroes an array's memory and sets its length to 0. +/// Note: To leave the associated memory intact, see ArrayReset. +ArrayClear :: (arr: *[]$T) { + MemZero(arr.data, arr.count * size_of(T)); + arr.count = 0; } -/// Equal checks the equality of two arrays. -Equal :: (lhs: []$T, rhs: []T) -> bool { +/// ArrayEquals checks equality between two arrays. +ArrayEquals :: (lhs: []$T, rhs: []T) -> bool { if lhs.count != rhs.count { return false; } return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T)); @@ -73,23 +70,21 @@ CheckBounds :: ($$index: $T, $$count: T, loc := #caller_location) #expand { } } - -FindFlags :: enum_flags { - Last; // Return the last matching element. - FromEnd; // Search in reverse. -} - -FindResult :: struct(T: Type) { +ArrayIndex :: struct(T: Type) { value: T = ---; index: int; } -/// Find searches through an array, returning the first element -/// that matches the given value. -Find :: (view: []$T, value: T, $flags: FindFlags = 0) -> (bool, FindResult(T)) { +ArrayFindFlags :: enum_flags { + Last; // Return the last matching element. + FromEnd; // Search in reverse. +} + +/// ArrayFind searches through an array, returning the first element that matches the given value. +ArrayFind :: (view: []$T, value: T, $flags: ArrayFindFlags = 0) -> (bool, ArrayIndex(T)) { found: bool; - result: FindResult(T); + result: ArrayIndex(T); result.index = -1; REVERSE :: #run (flags & .FromEnd).(bool); @@ -103,21 +98,21 @@ Find :: (view: []$T, value: T, $flags: FindFlags = 0) -> (bool, FindResult(T)) { return found, result; } -/// Contains checks if the given value exists in an array. -Contains :: (view: []$T, value: T) -> bool { - return Find(view, value); +/// ArrayContains checks if the given value exists in an array. +ArrayContains :: (view: []$T, value: T) -> bool { + return ArrayFind(view, value); } -TrimFlags :: enum_flags { - FromStart; // Trim the start of the array. - FromEnd; // Trim the end of the array. +ArrayTrimFlags :: enum_flags { + FromStart; // ArrayTrim the start of the array. + FromEnd; // ArrayTrim the end of the array. MatchInFull; // Only trim when the cutset matches exactly. } -/// Trim returns a subsection of an array with all leading/trailing values +/// ArrayTrim returns a subsection of an array with all leading/trailing values /// from cutset removed. -Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T { +ArrayTrim :: (view: []$T, cutset: []T, $flags: ArrayTrimFlags = .FromStart) -> []T { result := view; if cutset.count == 0 || cutset.count > view.count { return result; @@ -125,13 +120,13 @@ Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T { #if flags & .FromStart { #if flags & .MatchInFull { - if Equal(Slice(view, 0, cutset.count), cutset) { - result = Slice(view, cutset.count, -1); + if ArrayEquals(ArraySlice(view, 0, cutset.count), cutset) { + result = ArraySlice(view, cutset.count, -1); } } else { while result.count > 0 { - if !Contains(cutset, result[0]) { + if !ArrayContains(cutset, result[0]) { break; } @@ -143,13 +138,13 @@ Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T { #if flags & .FromEnd { #if flags & .MatchInFull { - if Equal(Slice(view, view.count - cutset.count), cutset) { + if ArrayEquals(ArraySlice(view, view.count - cutset.count), cutset) { result.count -= cutset.count; } } else { while result.count > 0 { - if !Contains(cutset, result[result.count - 1]) { + if !ArrayContains(cutset, result[result.count - 1]) { break; } @@ -169,82 +164,82 @@ basic :: #import "Basic"; // @future #if RunTests #run,stallable { Test("slice", t => { a1 := int.[ 1, 2, 3, 4, 5 ]; - a2 := Slice(a1, 2); + a2 := ArraySlice(a1, 2); Expect(a2.count == 3); - Expect(Equal(a2, int.[ 3, 4, 5 ])); + Expect(ArrayEquals(a2, int.[ 3, 4, 5 ])); b1 := int.[ 1, 2, 3, 4, 5 ]; - b2 := Slice(b1, 2, 0); + b2 := ArraySlice(b1, 2, 0); Expect(b2.count == 0); Expect(b2.data == b1.data + 2); c1 := int.[ 1, 2, 3, 4, 5 ]; - c2 := Slice(c1, 3, 1); + c2 := ArraySlice(c1, 3, 1); Expect(c2.count == 1); - Expect(Equal(c2, int.[ 4 ])); + Expect(ArrayEquals(c2, int.[ 4 ])); d1 := int.[ 1, 2, 3 ]; - d2 := Slice(d1, 2); + d2 := ArraySlice(d1, 2); Expect(d2.count == 1); - Expect(Equal(d2, int.[ 3 ])); + Expect(ArrayEquals(d2, int.[ 3 ])); }); Test("find", t => { a := int.[ 1, 2, 3, 4, 5 ]; - ok, res := Find(a, 3); + ok, res := ArrayFind(a, 3); Expect(ok && res.index == 2); Expect(res.value == 3); - ok, res = Find(a, -1); + ok, res = ArrayFind(a, -1); Expect(!ok && res.index == -1); b := int.[ 1, 2, 2, 3, 4, 5, 2 ]; - ok, res = Find(b, 2); + ok, res = ArrayFind(b, 2); Expect(ok && res.index == 1); - ok, res = Find(b, 2, .FromEnd); + ok, res = ArrayFind(b, 2, .FromEnd); Expect(ok && res.index == 6); c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; - ok, res = Find(c, 0, .Last); + ok, res = ArrayFind(c, 0, .Last); Expect(ok && res.index == 8); - ok, res = Find(c, 0, .FromEnd | .Last); + ok, res = ArrayFind(c, 0, .FromEnd | .Last); Expect(ok && res.index == 0); }); Test("contains", t => { a := int.[ 1, 2, 3, 4, 5 ]; - Expect(Contains(a, 3)); - Expect(!Contains(a, -1)); + Expect(ArrayContains(a, 3)); + Expect(!ArrayContains(a, -1)); }); Test("trim", t => { a1 := int.[ 0, 0, 0, 1, 2, 3 ]; - a2 := Trim(a1, .[ 0 ]); - Expect(Equal(a1, .[ 0, 0, 0, 1, 2, 3 ])); - Expect(Equal(a2, .[ 1, 2, 3 ])); + a2 := ArrayTrim(a1, .[ 0 ]); + Expect(ArrayEquals(a1, .[ 0, 0, 0, 1, 2, 3 ])); + Expect(ArrayEquals(a2, .[ 1, 2, 3 ])); b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; - b2 := Trim(b1, .[ 0 ], .FromEnd); - Expect(Equal(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); - Expect(Equal(b2, .[ 0, 0, 0, 1, 2, 3 ])); + b2 := ArrayTrim(b1, .[ 0 ], .FromEnd); + Expect(ArrayEquals(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); + Expect(ArrayEquals(b2, .[ 0, 0, 0, 1, 2, 3 ])); c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; - c2 := Trim(c1, .[ 0 ], .FromStart | .FromEnd); - Expect(Equal(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); - Expect(Equal(c2, .[ 1, 2, 3 ])); + c2 := ArrayTrim(c1, .[ 0 ], .FromStart | .FromEnd); + Expect(ArrayEquals(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); + Expect(ArrayEquals(c2, .[ 1, 2, 3 ])); d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; - d2 := Trim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull); - d3 := Trim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull); - d4 := Trim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull); - Expect(Equal(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); - Expect(Equal(d2, .[ 1, 2, 3, 0, 0, 0 ])); - Expect(Equal(d3, .[ 0, 0, 0, 1, 2, 3 ])); - Expect(Equal(d4, .[ 1, 2, 3 ])); + d2 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull); + d3 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull); + d4 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull); + Expect(ArrayEquals(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); + Expect(ArrayEquals(d2, .[ 1, 2, 3, 0, 0, 0 ])); + Expect(ArrayEquals(d3, .[ 0, 0, 0, 1, 2, 3 ])); + Expect(ArrayEquals(d4, .[ 1, 2, 3 ])); }); } diff --git a/+internal/builtin.jai b/+internal/builtin.jai index 8dcdcab..4a74dfc 100644 --- a/+internal/builtin.jai +++ b/+internal/builtin.jai @@ -64,6 +64,13 @@ b16 :: enum u16 { false_ :: (0 != 0).(u16); true_ :: (0 == 0).(u16); }; /// b16 b32 :: enum u32 { false_ :: (0 != 0).(u32); true_ :: (0 == 0).(u32); }; /// b32 is a 32-bit boolean. b64 :: enum u64 { false_ :: (0 != 0).(u64); true_ :: (0 == 0).(u64); }; /// b64 is a 64-bit boolean. +#if size_of(int) == { + case 8; uint :: u64; + case 4; uint :: u32; + case 2; uint :: u16; + case 1; uint :: u8; +} + /// Panic displays the given message and crashes the program. /// /// Note: Defers will not run when Panic is called. @@ -74,6 +81,11 @@ Panic :: (message := "jc: runtime panic", loc := #caller_location) #expand #no_d } WriteStderrString(message, "\n"); + + #if DebugBuild { + PrintStackTrace(); + } + Trap(); } @@ -89,7 +101,12 @@ Unreachable :: (message := "jc: unreachable code hit", loc := #caller_location) } WriteStderrString(message, "\n"); - trap(); + + #if DebugBuild { + PrintStackTrace(); + } + + Trap(); } /// CompileError displays the given message and stops compilation. @@ -179,3 +196,22 @@ DebugBuild :: #run -> bool { opts := get_build_options(); return opts.emit_debug_info != .NONE; }; + +PrintStackTrace :: () { + WriteStderrString("\n----- Stack Trace -----\n"); + + node := context.stack_trace.next; + while node != null { + loc := node.info.location; + loc.line_number = node.line_number; + WriteStderrLocation(loc); + + name := node.info.name; + if name.count <= 0 + { name = "(unknown)"; } + WriteStderrString(": in ", name, "\n"); + + if node.next != null + { node = node.next; } + } +} diff --git a/+internal/keywords.jai b/+internal/keywords.jai index df273c7..b731fa5 100644 --- a/+internal/keywords.jai +++ b/+internal/keywords.jai @@ -7,8 +7,7 @@ /// offset_of :: ($T: Type, ident: Code, loc := #caller_location) -> int #expand { #run (loc: Source_Code_Location) { - info := type_info(T); - if info.type != .STRUCT { + if !TypeIsStruct(T) { CompileError("jc: offset_of can only be used on struct types", loc = loc); } }(loc); @@ -29,14 +28,17 @@ offset_of :: ($T: Type, ident: Code, loc := #caller_location) -> int #expand { offset_of :: (#discard value: $T, ident: Code, loc := #caller_location) -> int #expand { type :: #run -> Type { info := T.(*Type_Info); - if info.type == .POINTER { - info = info.(*Type_Info_Pointer).pointer_to; + + ok, pinfo := TypeIsPointer(T); + if ok { // @question(judah): do we want it to traverse all the way up to a non-pointer type? // I opted against because if you have a *T, you only want offset_of to get an offset // from that pointer. What would you do with a field offset from **T? - if info.type == .POINTER { + if TypeIsPointer(pinfo.pointer_to) { CompileError("jc: offset_of only allows one level of pointer indirection.", loc = loc); } + + info = pinfo.pointer_to; } return get_type(info); @@ -50,9 +52,7 @@ align_of :: ($T: Type) -> int #expand { return #run -> int { if size_of(T) == 0 { return 0; } - - info := type_info(struct{ p: u8; t: T; }); - return info.members[1].offset_in_bytes.(int); + return offset_of(struct{ _: u8; t: T; }, #code t); }; } @@ -192,46 +192,9 @@ range_of :: ($T: Type, loc := #caller_location) -> (T, T) #expand { return min_of(T, loc = loc), max_of(T, loc = loc); } -/// sector creates a named block that can exit early via the 'break' keyword. -/// -/// Note: The block created by sector is called 'early' by default. -/// -/// for sector() { -/// break; -/// break early; // automatically created -/// } -/// -/// for sector("render_player") { -/// break render_player; -/// } -/// -sector :: ($name := Sector().Name) -> Sector(name) #expand { return .{}; } - -// @note(judah): there seems to be a weird race condition in the compiler -// that causes this to hit a null reference check error if running at compile-time. -for_expansion :: (v: Sector, code: Code, _: For_Flags) #expand { - // @todo(judah): fix this case? - // 'for this: sector() { break early; break this; }' - // both names valid here! - #insert #run basic.tprint(#string END - for `%1: 0..0 { - `it :: #run zero_of(void); - `it_index :: #run zero_of(void); - #insert,scope(code) code; - } - END, - // @note(judah): guards against calling this_block with - // an empty string which results in weird error messages. - ifx v.Name.count != 0 v.Name else Sector().Name); -} - #scope_file -Sector :: struct(Name: string = "early") {} - -basic :: #import "Basic"; // @future - #if RunTests #run { Test("min_of/max_of:enums", t => { U8Enum :: enum u8 { lo :: -1; hi :: +1; } diff --git a/+internal/kv.jai b/+internal/map.jai similarity index 67% rename from +internal/kv.jai rename to +internal/map.jai index 9a188b7..51e2e50 100644 --- a/+internal/kv.jai +++ b/+internal/map.jai @@ -1,5 +1,5 @@ // Dead simple key-value pair type (aka. hash table or hash map) -Record :: struct(Key: Type, Value: Type) { +Map :: struct(Key: Type, Value: Type) { allocator: Allocator; slots: [..]Slot; free_slots: [..]int; @@ -16,12 +16,7 @@ Record :: struct(Key: Type, Value: Type) { AllocatedItemsAtStart :: 16; } -// @note(judah): Not sure if I like these names, but I want to give them a try -Fetch :: Get; -Update :: Set; -Exists :: Has; - -Get :: (r: *Record, key: r.Key) -> r.Value, bool { +MapGet :: (r: *Map, key: r.Key) -> r.Value, bool { slot, ok := FindSlot(r, GetHash(r, key)); if !ok { return zero_of(r.Value), false; } @@ -29,7 +24,7 @@ Get :: (r: *Record, key: r.Key) -> r.Value, bool { return slot.value, true; } -Set :: (r: *Record, key: r.Key, value: r.Value) { +MapSet :: (r: *Map, key: r.Key, value: r.Value) { hash := GetHash(r, key); slot, exists := FindSlot(r, hash); if !exists { @@ -41,12 +36,12 @@ Set :: (r: *Record, key: r.Key, value: r.Value) { slot.value = value; } -Has :: (r: *Record, key: r.Key) -> bool { +MapHas :: (r: *Map, key: r.Key) -> bool { _, exists := FindSlot(r, GetHash(r, key)); return exists; } -Remove :: (r: *Record, key: r.Key) -> bool, r.Value { +MapRemove :: (r: *Map, key: r.Key) -> bool, r.Value { slot, ok, idx := FindSlot(r, GetHash(r, key)); if !ok { return false, zero_of(r.Value); } @@ -57,13 +52,13 @@ Remove :: (r: *Record, key: r.Key) -> bool, r.Value { return true, last_value; } -Reset :: (t: *Record) { +MapReset :: (t: *Map) { t.count = 0; t.slots.count = 0; t.free_slots.count = 0; } -for_expansion :: (r: *Record, body: Code, flags: For_Flags) #expand { +for_expansion :: (r: *Map, body: Code, flags: For_Flags) #expand { #assert (flags & .POINTER == 0) "cannot iterate by pointer"; for <=(flags & .REVERSE == .REVERSE) slot: r.slots if slot.hash != r.InvalidHash { `it := slot.value; @@ -73,15 +68,15 @@ for_expansion :: (r: *Record, body: Code, flags: For_Flags) #expand { } -#scope_file; +#scope_file -GetHash :: inline (r: *Record, key: r.Key) -> u32 { +GetHash :: inline (r: *Map, key: r.Key) -> u32 { hash := r.HashProc(key); Assert(hash != r.InvalidHash, "key collided with invalid hash"); return hash; } -FindSlot :: (r: *Record, hash: u32) -> *r.Slot, bool, int { +FindSlot :: (r: *Map, hash: u32) -> *r.Slot, bool, int { for * r.slots if it.hash == hash { return it, true, it_index; } @@ -89,7 +84,7 @@ FindSlot :: (r: *Record, hash: u32) -> *r.Slot, bool, int { return null, false, -1; } -CreateOrReuseSlot :: (r: *Record) -> *r.Slot { +CreateOrReuseSlot :: (r: *Map) -> *r.Slot { inline LazyInit(r); if r.free_slots.count > 0 { @@ -99,27 +94,27 @@ CreateOrReuseSlot :: (r: *Record) -> *r.Slot { } if r.slots.allocated == 0 { - Resize(*r.slots, r.AllocatedItemsAtStart); + ArrayGrow(*r.slots, r.AllocatedItemsAtStart); } else if r.slots.count >= r.slots.allocated { - Resize(*r.slots, NextPowerOfTwo(r.slots.allocated)); + ArrayGrow(*r.slots, NextPowerOfTwo(r.slots.allocated)); } - slot := Append(*r.slots); + slot := ArrayAppend(*r.slots); r.count = r.slots.count; return slot; } -MarkSlotForReuse :: (t: *Record, index: int) { +MarkSlotForReuse :: (t: *Map, index: int) { inline LazyInit(t); t.count -= 1; t.slots[index] = .{ hash = t.InvalidHash }; - Append(*t.free_slots, index); + ArrayAppend(*t.free_slots, index); } -LazyInit :: inline (t: *Record) { +LazyInit :: inline (t: *Map) { TrySetAllocator(t); TrySetAllocator(*t.slots); TrySetAllocator(*t.free_slots); @@ -127,52 +122,52 @@ LazyInit :: inline (t: *Record) { #if RunTests #run { - Test("kv:basic operations", t => { + Test("map:basic operations", t => { ITERATIONS :: 64; - values: Record(int, int); + values: Map(int, int); for 0..ITERATIONS { - Set(*values, it, it * it); + MapSet(*values, it, it * it); } for 0..ITERATIONS { - v, ok := Get(*values, it); + v, ok := MapGet(*values, it); Expect(v == it * it); } for 0..ITERATIONS if it % 2 == 0 { - ok := Remove(*values, it); + ok := MapRemove(*values, it); Expect(ok); } for 0..ITERATIONS if it % 2 == 0 { - _, ok := Get(*values, it); + _, ok := MapGet(*values, it); Expect(!ok); } }); - Test("kv:free slots", t => { - values: Record(int, int); + Test("map:free slots", t => { + values: Map(int, int); - Set(*values, 1, 100); - Set(*values, 2, 200); - Set(*values, 3, 300); + MapSet(*values, 1, 100); + MapSet(*values, 2, 200); + MapSet(*values, 3, 300); Expect(values.count == 3); Expect(values.slots.allocated == values.AllocatedItemsAtStart); // deleting something that doesn't exist should do nothing - ok := Remove(*values, 0); + ok := MapRemove(*values, 0); Expect(!ok); Expect(values.count == 3); - Remove(*values, 2); + MapRemove(*values, 2); Expect(values.count == 2); }); - Test("kv:iteration", t => { - values: Record(int, int); + Test("map:iteration", t => { + values: Map(int, int); - for 0..10 Set(*values, it, it * it); + for 0..10 MapSet(*values, it, it * it); Expect(values.count == 11); for v, k: values Expect(v == k * k); diff --git a/+internal/memory.jai b/+internal/memory.jai index 81e6199..e0c8070 100644 --- a/+internal/memory.jai +++ b/+internal/memory.jai @@ -2,7 +2,7 @@ Kilobyte :: 1024; Megabyte :: 1024 * Kilobyte; Gigabyte :: 1024 * Megabyte; -DefaultAlign :: #run 2 * align_of(*void); +DefaultAlign :: size_of(*void); /// MemEqual checks the equality of two pieces of memory. /// @@ -60,22 +60,40 @@ MemReset :: (p: *$T) { } } -AlignUpwards :: (ptr: int, align: int = DefaultAlign) -> int { - Assert(PowerOfTwo(align), "alignment must be a power of two"); - - p := ptr; - mod := p & (align - 1); - if mod != 0 then p += align - mod; - return p; +MemAligned :: (p: *void, align: uint = DefaultAlign) -> bool { + return Aligned(p.(uint), align); } -PowerOfTwo :: (x: int) -> bool { +MemAlignForward :: (p: *void, align: uint = DefaultAlign) -> *void { + return AlignForward(p.(uint), align).(*void); +} + +MemAlignBackward :: (p: *void, align: uint = DefaultAlign) -> *void { + return AlignBackward(p.(uint), align).(*void); +} + +Aligned :: (a: uint, align: uint = DefaultAlign) -> bool { + return (a & (align - 1)) == 0; +} + +AlignForward :: (a: uint, align: uint = DefaultAlign) -> uint { + Assert(PowerOfTwo(align), "jc: must be a power of two"); + return (a + align - 1) & ~(align - 1); +} + +AlignBackward :: (a: uint, align: uint = DefaultAlign) -> uint { + Assert(PowerOfTwo(align), "jc: must be a power of two"); + return a & ~(align - 1); +} + + +PowerOfTwo :: (x: uint) -> bool { if x == 0 return false; return x & (x - 1) == 0; } -NextPowerOfTwo :: (x: int) -> int #no_aoc { - Assert(PowerOfTwo(x), "value must be a power of two"); +NextPowerOfTwo :: (x: uint) -> uint #no_aoc { + Assert(PowerOfTwo(x), "jc: must be a power of two"); // Bit twiddling hacks next power of two x |= x >> 1; @@ -87,3 +105,28 @@ NextPowerOfTwo :: (x: int) -> int #no_aoc { return x + 1; } + +TrySetAllocator :: (thing: *$T, allocator := context.allocator) #modify { + info := T.(*Type_Info_Struct); + + ok := false; + if info.type == .STRUCT + { ok = true; } + + if ok for info.members if it.name == "allocator" && it.type == Allocator.(*Type_Info) { + ok = true; + break; + } + + return ok, "can only set allocator on struct with an allocator field or dynamic array"; +} #expand { + if thing.allocator.proc == null { + thing.allocator = allocator; + } +} + +TrySetAllocator :: (array: *[..]$T, allocator := context.allocator) #expand { + if array.allocator.proc == null { + array.allocator = allocator; + } +} diff --git a/+internal/testing.jai b/+internal/testing.jai index d7f2c39..99907d9 100644 --- a/+internal/testing.jai +++ b/+internal/testing.jai @@ -27,33 +27,7 @@ /// Test :: (name: string, proc: (t: *void) -> (), loc := #caller_location) { // @note(judah): incredibly dumb way to get nicer test runs - path := loc.fully_pathed_filename; - - i := path.count - 1; - found_first_slash := false; - - while i >= 0 { - if path[i] == "/" { - if found_first_slash { - i += 1; - break; - } - - found_first_slash = true; - } - - i -= 1; - } - - if !found_first_slash { - path = strings.path_filename(loc.fully_pathed_filename); - } - else { - path.count -= i; - path.data += i; - } - - WriteString(path, ","); + WriteString(SmallerPath(loc.fully_pathed_filename), ","); WriteNumber(loc.line_number); WriteString(": ", name, "... "); @@ -111,6 +85,35 @@ TestRun :: struct { failed: bool; } +SmallerPath :: (path: string) -> string { + p := path; + i := p.count - 1; + found_first_slash := false; + + while i >= 0 { + if p[i] == "/" { + if found_first_slash { + i += 1; + break; + } + + found_first_slash = true; + } + + i -= 1; + } + + if !found_first_slash { + p = strings.path_filename(path); + } + else { + p.count -= i; + p.data += i; + } + + return p; +} + basic :: #import "Basic"; // @future strings :: #import "String"; // @future compiler :: #import "Compiler"; // @future diff --git a/+internal/type_info.jai b/+internal/type_info.jai index 2db015d..484bb2b 100644 --- a/+internal/type_info.jai +++ b/+internal/type_info.jai @@ -1,6 +1,6 @@ // @todo for jesse these should be PascalCase but I didn't want to give you an annoying merge conflict -check_type_tag :: ($$T: Type, tag: Type_Info_Tag) -> bool, *Type_Info { +TypeTagIs :: ($$T: Type, tag: Type_Info_Tag) -> (bool, *Type_Info) { #if is_constant(T) { info :: type_info(T); if info.type == tag return true, info; @@ -13,31 +13,36 @@ check_type_tag :: ($$T: Type, tag: Type_Info_Tag) -> bool, *Type_Info { return false, null; } -type_is_integer :: ($$T: Type) -> bool, *Type_Info_Integer { - ok, info := check_type_tag(T, .INTEGER); +TypeIsInteger :: ($$T: Type) -> (bool, *Type_Info_Integer) { + ok, info := TypeTagIs(T, .INTEGER); return ok, info.(*Type_Info_Integer); } -type_is_float :: ($$T: Type) -> bool, *Type_Info_Float { - ok, info := check_type_tag(T, .FLOAT); +TypeIsFloat :: ($$T: Type) -> (bool, *Type_Info_Float) { + ok, info := TypeTagIs(T, .FLOAT); return ok, info.(*Type_Info_Float); } -type_is_scalar :: (t: Type) -> bool { - return type_is_integer(t) || type_is_float(t); +TypeIsScalar :: ($$T: Type) -> bool { + return TypeIsInteger(T) || TypeIsFloat(T); } -type_is_array :: ($$T: Type) -> bool, *Type_Info_Array { - ok, info := check_type_tag(T, .ARRAY); +TypeIsArray :: ($$T: Type) -> (bool, *Type_Info_Array) { + ok, info := TypeTagIs(T, .ARRAY); return ok, info.(*Type_Info_Array); } -type_is_struct :: ($$T: Type) -> bool, *Type_Info_Struct { - ok, info := check_type_tag(T, .STRUCT); +TypeIsStruct :: ($$T: Type) -> (bool, *Type_Info_Struct) { + ok, info := TypeTagIs(T, .STRUCT); return ok, info.(*Type_Info_Struct); } -type_is_enum :: ($$T: Type) -> bool, *Type_Info_Enum { - ok, info := check_type_tag(T, .ENUM); +TypeIsEnum :: ($$T: Type) -> (bool, *Type_Info_Enum) { + ok, info := TypeTagIs(T, .ENUM); return ok, info.(*Type_Info_Enum); } + +TypeIsPointer :: ($$T: Type) -> (bool, *Type_Info_Pointer) { + ok, info := TypeTagIs(T, .POINTER); + return ok, info.(*Type_Info_Pointer); +} diff --git a/.gitignore b/.gitignore index ed25003..e434ef8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **.dSYM .DS_Store docs/ +*.scratch.* diff --git a/PLAN.Judah b/PLAN.Judah index c769250..6b6e863 100644 --- a/PLAN.Judah +++ b/PLAN.Judah @@ -7,6 +7,15 @@ This is what I'm doing day-to-day. > completed on a later day < decided against on a later day : notes, todos, etc. + + +/ 09.07.25 + ++ replaced allocators with custom arenas ++ Panic/Unreachable prints stack trace in debug builds ++ type_info names are now pascal case + +: not sure how [..]T should interface with arenas / 09.06.25 @@ -15,9 +24,13 @@ This is what I'm doing day-to-day. + encoding is now fmt + math tests pass after reorg + docs look nicer ++ talked to Jesse about various things ++ name pass to add prefixes ++ Record is Map now ++ removed sector because it's not good (just use 'for 0..0' or 'while true { defer break; ... }') : need to reimplement dynamic arrays -: stable arrays too? +< stable arrays too? / 09.05.25 @@ -35,7 +48,7 @@ This is what I'm doing day-to-day. + error messages now start with 'jc:' + decided to start using John Carmack-styled plan files to keep track of what I'm working on. would be cool to document the repo as it changes over time. -: planning out how I want allocators to work. I want to go big into arenas, but don't want to completely abandon Jai's builtin allocators +> planning out how I want allocators to work. I want to go big into arenas, but don't want to completely abandon Jai's builtin allocators diff --git a/fmt/base64.jai b/fmt/base64.jai index 0f2268b..7ae8224 100644 --- a/fmt/base64.jai +++ b/fmt/base64.jai @@ -1,16 +1,6 @@ #module_parameters(RunTests := false); -Base64Encode :: (str: string, $for_url := false) -> string, bool { - enc, ok := Base64Encode(str.([]u8), for_url); - return enc.(string), ok; -} - -Base64Decode :: (str: string) -> string, bool { - enc, ok := Base64Decode(str.([]u8)); - return enc.(string), ok; -} - -Base64Encode :: ($$data: []u8, $for_url := false) -> []u8, bool { +Base64Encode :: (data: []u8, $for_url := false) -> []u8, bool { if data.count == 0 return .[], false; #if for_url { @@ -144,6 +134,16 @@ Base64Decode :: (data: []u8) -> []u8, bool { return decoded, true; } +Base64Encode :: (str: string, $for_url := false) -> string, bool { + enc, ok := Base64Encode(str.([]u8), for_url); + return enc.(string), ok; +} + +Base64Decode :: (str: string) -> string, bool { + enc, ok := Base64Decode(str.([]u8)); + return enc.(string), ok; +} + #scope_file @@ -162,10 +162,6 @@ basic :: #import "Basic"; // @future strings :: #import "String"; // @future -// ---------------------------------------------------------- -// TESTS -// ---------------------------------------------------------- - #if RunTests #run { Test("encodes", t => { str :: "Hello, World"; diff --git a/math/common.jai b/math/common.jai index 9423642..fd3f28b 100644 --- a/math/common.jai +++ b/math/common.jai @@ -88,7 +88,7 @@ min :: (x: float, y: float) -> float { } abs :: (v: $T) -> T -#modify { return type_is_scalar(T), "Only accepting scalar types, integer and float"; } { +#modify { return TypeIsScalar(T), "Only accepting scalar types, integer and float"; } { return ifx v < 0 then -v else v; } diff --git a/math/ease.jai b/math/ease.jai index c902888..07d0fba 100644 --- a/math/ease.jai +++ b/math/ease.jai @@ -28,7 +28,7 @@ Transition :: enum { @Note: Progress must be between 0.0 and 1.0 */ ease :: (progress: $T, $$ease: Ease = .linear, $$transition: Transition = .in) -> T -#modify { return type_is_float(T); } +#modify { return TypeIsFloat(T); } { p := progress; diff --git a/math/module.jai b/math/module.jai index dd34aed..5b6be85 100644 --- a/math/module.jai +++ b/math/module.jai @@ -5,7 +5,7 @@ RUN_TESTS := false ); -#assert type_is_scalar(RECT_TYPE); +#assert TypeIsScalar(RECT_TYPE); #load "common.jai"; #load "vec.jai"; diff --git a/math/vec.jai b/math/vec.jai index 6ad9442..9d44ca6 100644 --- a/math/vec.jai +++ b/math/vec.jai @@ -187,7 +187,7 @@ operator / :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { } operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric -#modify { return type_is_scalar(R), "type is not integer or float"; } { +#modify { return TypeIsScalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; #if l.N <= 4 { res.x = l.x + r; @@ -202,7 +202,7 @@ operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric } operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc -#modify { return type_is_scalar(R), "type is not integer or float"; } { +#modify { return TypeIsScalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; #if l.N <= 4 { res.x = l.x - r; @@ -216,7 +216,7 @@ operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc return res; } operator - :: inline (l: $R, r: Vec) -> Vec(l.N, l.T) #no_abc -#modify { return type_is_scalar(R), "type is not integer or float"; } { +#modify { return TypeIsScalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; #if l.N <= 4 { res.x = l - r.x; @@ -245,7 +245,7 @@ operator- :: inline(v: Vec) -> Vec(v.N, v.T) #no_abc { } operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric -#modify { return type_is_scalar(R), "type is not integer or float"; } { +#modify { return TypeIsScalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; #if l.N <= 4 { res.x = l.x*r; @@ -260,7 +260,7 @@ operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric } operator / :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc -#modify { return type_is_scalar(R), "type is not integer or float"; } { +#modify { return TypeIsScalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; #if l.N <= 4 { res.x = l.x/r; @@ -486,7 +486,7 @@ reflect :: (v: Vec2, p: Vec2) -> Vec2 #no_abc { } round :: (v: Vec($N, $T)) -> Vec(N, T) #no_abc -#modify { return type_is_float(T), "Used non-float vector on round"; } { +#modify { return TypeIsFloat(T), "Used non-float vector on round"; } { r: Vec(N, T) = ---; n := N - 1; while n >= 0 { @@ -507,31 +507,31 @@ Vec4 :: Vec(4, float); Quat :: #type,distinct Vec4; // Note(Jesse): I Had to make this distinct, otherwise operators stomp on eachother v2f :: (x: $T = 0, y: T = 0) -> Vec2 -#modify { return type_is_float(T), "use v2i for integer arguments"; } +#modify { return TypeIsFloat(T), "use v2i for integer arguments"; } #expand { return .{ x = x, y = y }; } v2i :: (x: $T = 0, y: T = 0) -> Vec(2, T) -#modify { return type_is_integer(T), "use v2f for float arguments"; } +#modify { return TypeIsInteger(T), "use v2f for float arguments"; } #expand { return .{ x = x, y = y }; } v3f :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec3 -#modify { return type_is_float(T), "use v3i for integer arguments"; } +#modify { return TypeIsFloat(T), "use v3i for integer arguments"; } #expand { return .{ x = x, y = y, z = z }; } v3i :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec(3, T) -#modify { return type_is_integer(T), "use v3f for float arguments"; } +#modify { return TypeIsInteger(T), "use v3f for float arguments"; } #expand { return .{ x = x, y = y, z = z }; } v4f :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec4 -#modify { return type_is_float(T), "use v4i for integer arguments"; } +#modify { return TypeIsFloat(T), "use v4i for integer arguments"; } #expand { return .{ x = x, y = y, z = z, w = w }; } @@ -541,7 +541,7 @@ v4f :: (v: Vec3, $$w: float) -> Vec4 #expand { } v4i :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec(4, T) -#modify { return type_is_integer(T), "use v4f for float arguments"; } +#modify { return TypeIsInteger(T), "use v4f for float arguments"; } #expand { return .{ x = x, y = y, z = z, w = w }; } diff --git a/module.jai b/module.jai index 5d3817b..e7cc183 100644 --- a/module.jai +++ b/module.jai @@ -7,15 +7,12 @@ /// system. #module_parameters(RunTests := false); -// @note(judah): this causes some very weird issues in tests so it's ifdef'd out -#if !RunTests #add_context temp_allocator: Allocator; - #load "+internal/builtin.jai"; #load "+internal/array.jai"; -#load "+internal/kv.jai"; +#load "+internal/map.jai"; #load "+internal/hashing.jai"; #load "+internal/memory.jai"; -#load "+internal/allocators.jai"; +#load "+internal/arenas.jai"; #load "+internal/testing.jai"; #load "+internal/keywords.jai"; #load "+internal/type_info.jai";