absolute imports test, normalized all modules

This commit is contained in:
Judah Caruso 2025-07-22 20:39:57 -06:00
parent fd11638b8c
commit 05995a6a06
28 changed files with 301 additions and 132 deletions

22
README
View file

@ -1,16 +1,16 @@
------
jc.jai
------
--
jc
--
# Direct installation
cd [jai install dir]/modules
git clone https://git.brut.systems/judah/jc.jai.git jc
git clone https://git.brut.systems/judah/jc.git
# Indirect installation
git clone https://git.brut.systems/judah/jc.jai.git
git clone https://git.brut.systems/judah/jc.git
ln -s "/path/to/jc.jai" [jai install dir]/modules/jc # POSIX install
mklink /D "C:\path\to\jc.jai" [jai install dir]\jc # Windows install
ln -s "/path/to/jc" [jai install dir]/modules/jc # POSIX install
mklink /D "C:\path\to\jc" [jai install dir]\jc # Windows install
#import "jc";
#import "jc/[module]";
@ -36,10 +36,10 @@ Because Jai is still in closed beta (as of May 15, 2025),
updates to the compiler and "standard library" will break
projects of mine; sometimes in a very annoying way.
jc.jai was made to 1) give myself an escape
hatch/skin-suit to cause fewer breaking changes when
updating the compiler, and 2) put all of my non-project
code in a single place that's easier to manage.
jc was made to 1) give myself an escape hatch/skin-suit to
cause fewer breaking changes when updating the compiler,
and 2) put all of my non-project code in a single place
that's easier to manage.
While I do use many of the modules shipped with the
compiler, my goal is to eventually replace them.

85
_make_module.jai Normal file
View file

@ -0,0 +1,85 @@
#run {
set_build_options_dc(.{ do_output = false });
args := get_build_options().compile_time_command_line;
usage :: () {
print("creates the template for a module or submodule\n\n");
print("usage: jai _make_module.jai - (module|submodule) [path]\n\n");
print("options:\n");
print("\tpath: a simple module path without an extension (example: 'foo' or 'foo/bar')\n");
}
if args.count < 2 {
usage();
return;
}
kind := trim(args[0]);
if kind.count == 0 {
usage();
return;
}
module_path := trim(args[1]);
if module_path.count == 0 {
usage();
return;
}
if kind == {
case "module";
assert(make_directory_if_it_does_not_exist(module_path), "could not create module directory: '%'", module_path);
entry_file := tprint("%/module.jai", module_path);
assert(write_entire_file(entry_file, tprint(MODULE_STRING, module_path)), "could not create %", entry_file);
case "submodule";
entry_file := tprint("%.jai", module_path);
assert(write_entire_file(entry_file, tprint(SUBMODULE_STRING, module_path)), "could not create %", entry_file);
case;
usage();
return;
}
}
MODULE_STRING :: #string END
// %1 is a module that does things.
//
#module_parameters(RUN_TESTS := false, IMPORTED_INTERNALLY := false);
#scope_export;
#if !IMPORTED_INTERNALLY {
// #import "jc/%1/submodule"(RUN_TESTS);
// ...
}
END;
SUBMODULE_STRING :: #string END
// %1 is a module that does stuff.
//
#module_parameters(RUN_TESTS := false);
#scope_export;
// ...
#scope_file;
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("%1:works", t => {
test.expect(t, true);
});
}
END;
#import "File";
#import "Basic";
#import "String";
#import "Compiler";

View file

@ -2,17 +2,14 @@
compiler :: #import "Compiler";
compiler.set_build_options_dc(.{ do_output = false });
// @note(judah): we use relative imports here because that'll
// print cleaner file locations.
#import "jc/array"(RUN_TESTS = true);
#import "jc/encoding"(RUN_TESTS = true);
#import "jc/hash"(RUN_TESTS = true);
#import "jc/memory"(RUN_TESTS = true);
#import "jc/meta"(RUN_TESTS = true);
#import "jc/platform"(RUN_TESTS = true);
#import,file "./array/module.jai"(RUN_TESTS_AT_COMPILE_TIME = true);
#import,file "./encoding/module.jai"(RUN_TESTS_AT_COMPILE_TIME = true);
#import,file "./hash/module.jai"(RUN_TESTS_AT_COMPILE_TIME = true);
#import,file "./memory/module.jai"(RUN_TESTS_AT_COMPILE_TIME = true);
#import,file "./meta/module.jai"(RUN_TESTS_AT_COMPILE_TIME = true);
#import,file "./platform/module.jai"(RUN_TESTS_AT_COMPILE_TIME = true);
rmath :: #import,file "./math/module.jai"(.radians, RUN_TESTS_AT_COMPILE_TIME = true);
dmath :: #import,file "./math/module.jai"(.degrees, RUN_TESTS_AT_COMPILE_TIME = true);
tmath :: #import,file "./math/module.jai"(.turns, RUN_TESTS_AT_COMPILE_TIME = true);
rmath :: #import "jc/math"(.radians, RUN_TESTS = true);
dmath :: #import "jc/math"(.degrees, RUN_TESTS = true);
tmath :: #import "jc/math"(.turns, RUN_TESTS = true);
}

View file

@ -1,44 +1,58 @@
to_string :: (c: *u8, count := 1) -> string #expand {
#module_parameters(RUN_TESTS := false);
to_string :: (c: cstring, count := 1) -> string #expand {
return string.{ data = c, count = count };
}
Index_Mode :: enum {
from_left;
from_right;
Index_Flag :: enum_flags {
from_end;
index_plus_one;
count_on_fail;
}
find_index :: (b: []u8, c: u8, $mode := Index_Mode.from_left) -> bool, int {
#if #complete mode == {
case .from_left;
for b if it == c {
return true, it_index;
find_index :: (b: []byte, c: byte, $mode: Index_Flag) -> bool, int {
count := 0;
#if mode & .from_end {
i := b.count - 1;
while i >= 0 {
if b[i] == c {
return true, #ifx mode & .index_plus_one i + 1 else i;
}
case .from_right;
i := b.count - 1;
while i >= 0 {
if b[i] == c {
return true, i;
}
i -= 1;
i -= 1;
#if mode & .count_on_fail {
count += 1;
}
}
}
else {
for b if it == c {
return true, #ifx mode & .index_plus_one then it_index + 1 else it_index;
}
else #if mode & .count_on_fail {
count += 1;
}
}
return false, -1;
return false, #ifx mode & .count_on_fail then count else -1;
}
find_index :: inline (s: string, c: u8, $mode := Index_Mode.from_left) -> bool, int {
ok, idx := find_index(s.([]u8), c, mode);
find_index :: inline (s: string, c: byte, $mode: Index_Flag) -> bool, int {
ok, idx := find_index(s.([]byte), c, mode);
return ok, idx;
}
#scope_file;
#import "jc";
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
}

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// @todo(judah): replace array_add
append :: inline (arr: *[..]$T, value: T) -> *T {
@ -62,7 +64,12 @@ meta :: #import "jc/meta";
basic :: #import "Basic"; // @future
#if #exists(RUN_TESTS) #run {
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("remove_ordered", t => {

View file

@ -1,13 +1,13 @@
#module_parameters(RUN_TESTS_AT_COMPILE_TIME := false);
#module_parameters(RUN_TESTS := false, WITH_SUBMODULES := true);
#load "bytes.jai";
#load "dynamic.jai";
#load "stable.jai";
#load "static.jai";
#load "xar.jai";
#scope_export;
#if WITH_SUBMODULES {
using #import "jc/array/bytes"(RUN_TESTS);
using #import "jc/array/dynamic"(RUN_TESTS);
using #import "jc/array/stable"(RUN_TESTS);
using #import "jc/array/static"(RUN_TESTS);
using #import "jc/array/xar"(RUN_TESTS);
}
#scope_module;
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
}

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// A dynamic array whose values will never move in memory.
//
// This means it is safe to take a pointer to a value within the array
@ -97,9 +99,6 @@ for_expansion :: (a: Stable_Array, body: Code, flags: For_Flags) #expand {
#scope_file;
mem :: #import "jc/memory";
meta :: #import "jc/meta";
find_or_create_chunk :: (a: *Stable_Array, amount: int) -> *a.Chunk {
if a.chunks.count == 0 {
return create_chunk(a);
@ -122,12 +121,17 @@ create_chunk :: (a: *Stable_Array) -> *a.Chunk {
return chunk;
}
#import "jc/array";
mem :: #import "jc/memory";
meta :: #import "jc/meta";
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("basic operations", t => {

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
Static_Array :: struct(capacity: int, T: Type) {
items: [capacity]T;
count: int;
@ -108,7 +110,7 @@ basic :: #import "Basic"; // @future
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("basic operations", (t) => {

View file

@ -1,9 +1,11 @@
#module_parameters(RUN_TESTS := false);
#scope_file;
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
}

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
base64_encode :: (str: string, $for_url := false) -> string, bool {
enc, ok := base64_encode(str.([]u8), for_url);
return enc.(string), ok;
@ -153,9 +155,6 @@ encoded_length :: (count: int, with_padding := false) -> int {
return (count * 8 + 5) / 6;
}
#scope_file;
basic :: #import "Basic"; // @future
strings :: #import "String"; // @future
@ -164,7 +163,7 @@ strings :: #import "String"; // @future
// TESTS
// ----------------------------------------------------------
#if #exists(TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("encodes", (t) => {

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
Json_Value :: struct {
kind: enum {
none;

View file

@ -1,10 +1,10 @@
#module_parameters(RUN_TESTS_AT_COMPILE_TIME := false);
#module_parameters(RUN_TESTS := false, WITH_SUBMODULES := true);
#load "base64.jai";
#load "json.jai";
#scope_export;
#if WITH_SUBMODULES {
using #import "jc/encoding/base64"(RUN_TESTS);
using #import "jc/encoding/json"(RUN_TESTS);
}
#scope_module;
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
}

View file

@ -1,11 +1,10 @@
#module_parameters(RUN_TESTS_AT_COMPILE_TIME := false);
#module_parameters(RUN_TESTS := false, WITH_SUBMODULES := false);
#load "murmur.jai";
#load "xxhash.jai";
#load "table.jai";
#scope_export;
#scope_module;
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
#if WITH_SUBMODULES {
using #import "jc/hash/murmur"(RUN_TESTS);
using #import "jc/hash/xxhash"(RUN_TESTS);
using #import "jc/hash/table"(RUN_TESTS);
}

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// Implementation: Demetri Spanos (github.com/demetri/scribbles)
// Jai Port: Jesse Coyle (github.com/Zilarrezko)

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// Dead simple key-value pair type (aka. hash table or hash map)
Table :: struct(Key: Type, Value: Type) {
allocator: Allocator;
@ -132,7 +134,7 @@ basic :: #import "Basic"; // @future
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("basic operations", t => {

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// Implementation: Demetri Spanos (github.com/demetri/scribbles)
// Jai Port: Jesse Coyle (github.com/Zilarrezko)

View file

@ -2,7 +2,7 @@
UNITS: enum { radians; degrees; turns; } = .turns,
RECT_TYPE: Type = float,
// RECT_METHOD: enum { dimension; absolute; } = absolute, // Note(Jesse): Maybe at a later point we can do this
RUN_TESTS_AT_COMPILE_TIME := false
RUN_TESTS := false
);
#assert meta.type_is_scalar(RECT_TYPE);
@ -30,14 +30,10 @@ F64_Min, F64_Max :: #run meta.lo_for(float64), #run meta.hi_for(float64);
#scope_module;
meta :: #import "jc/meta";
meta :: #import "jc/meta";
math :: #import "Math"; // @future
basic :: #import "Basic"; // @future
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
}
// @temp(judah): move these to the right files
v2_eq :: (a: Vec2, b: Vec2) -> bool {

View file

@ -651,9 +651,9 @@ cross :: (a: Vec3, b: Vec3) -> Vec3 {
return v;
}
#scope_file
#scope_file;
#if #exists(RUN_TESTS) #run,stallable {
#if RUN_TESTS #run,stallable {
test :: #import "jc/meta/test";
test.run(basic.tprint("%: Vec2", UNITS), t => {

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
make_crash_allocator :: () -> Allocator {
return .{ proc = crash_allocator_proc };
}
@ -87,12 +89,12 @@ arena_allocator_proc :: (mode: Extended_Allocator_Mode, size: s64, old_size: s64
return null;
}
arena_alloc :: (a: *Arena, count: int, alignment := Default_Align, loc := #caller_location) -> *void {
arena_alloc :: (a: *Arena, count: int, alignment := mem.Default_Align, loc := #caller_location) -> *void {
basic.assert(a.memory != null, "arena: not initialized", loc = loc);
basic.assert(power_of_two(alignment));
basic.assert(mem.power_of_two(alignment));
end := a.memory.(*u8) + a.offset;
ptr := align_to(end.(int), alignment);
ptr := mem.align_to(end.(int), alignment);
total_size := (count + ptr.(*u8) - end.(*u8)).(u64);
basic.assert(a.offset + total_size <= a.memory_size, "arena: out of memory", loc = loc);
@ -104,30 +106,32 @@ arena_alloc :: (a: *Arena, count: int, alignment := Default_Align, loc := #calle
#scope_file;
basic :: #import "Basic"; // @future
mem :: #import "jc/memory";
meta :: #import "jc/meta";
basic :: #import "Basic"; // @future
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("arena:basic", t => {
memory := request_memory(1 * Kilobyte);
defer release_memory(memory);
memory := mem.request_memory(1 * mem.Kilobyte);
defer mem.release_memory(memory);
arena: Arena;
init_arena(*arena, memory, 1 * Kilobyte);
init_arena(*arena, memory, 1 * mem.Kilobyte);
context.allocator = make_arena_allocator(*arena);
save_point := allocator_save();
save_point := mem.allocator_save();
i := request_memory(int);
i := mem.request_memory(int);
basic.assert(i != null);
basic.assert(arena.offset == size_of(int));
allocator_restore(save_point);
mem.allocator_restore(save_point);
});
}

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
Buffer :: struct {
allocator: Allocator;
@ -42,10 +44,6 @@ ensure_buffer_has_room :: (buf: *Buffer, count: int) -> *u8 {
return ptr;
}
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("basic operations", t => {
buf: Buffer;
});
}

View file

@ -1,4 +1,6 @@
#module_parameters(RUN_TESTS_AT_COMPILE_TIME := false);
#module_parameters(RUN_TESTS := false, WITH_SUBMODULES := true);
#scope_export;
Kilobyte :: 1024;
Megabyte :: 1024 * Kilobyte;
@ -172,14 +174,11 @@ lazy_set_allocator :: (array: *[..]$T, allocator := context.allocator) #expand {
}
}
#load "allocators.jai";
#scope_module;
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
#if WITH_SUBMODULES {
using #import "jc/memory/allocators"(RUN_TESTS);
}
#scope_file;
meta :: #import "jc/meta";
@ -192,7 +191,7 @@ compiler :: #import "Compiler"; // @future
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("request_memory:dynamic arrays", (t) => {

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
/*
Allows structs to be copied and assigned inline.
@ -215,6 +217,8 @@ Unrolled_Loop :: struct(N: int, T: Type = void) {
#scope_file;
mem :: #import "jc/memory";
basic :: #import "Basic"; // @future
pp :: #import "Program_Print"; // @future
compiler :: #import "Compiler"; // @future
@ -224,7 +228,7 @@ compiler :: #import "Compiler"; // @future
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("this_block", (t) => {

View file

@ -1,4 +1,6 @@
#module_parameters(RUN_TESTS_AT_COMPILE_TIME := false);
#module_parameters(RUN_TESTS := false, WITH_SUBMODULES := true);
#scope_export;
get_stack_trace_caller_location :: (loc := #caller_location) -> Source_Code_Location {
if context.stack_trace == null || context.stack_trace.info == null {
@ -61,16 +63,13 @@ snake_to_pascal :: (name: string) -> string {
return basic.builder_to_string(*b);
}
#load "macros.jai";
#load "type_info.jai";
#if WITH_SUBMODULES {
using #import "jc/meta/macros"(RUN_TESTS);
using #import "jc/meta/type_info"(RUN_TESTS);
}
#scope_module;
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
}
mem :: #import "jc/memory";
basic :: #import "Basic"; // @future
@ -81,7 +80,7 @@ compiler :: #import "Compiler"; // @future
// TESTS
// ----------------------------------------------------------
#if RUN_TESTS_AT_COMPILE_TIME #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("snake_to_pascal", t => {

View file

@ -41,10 +41,33 @@ expect :: (t: *T, cond: bool, message := "", args: ..Any, loc := #caller_locatio
}
run :: (name: string, proc: Proc, loc := #caller_location) {
idx := strings.find_index_from_left(loc.fully_pathed_filename, "./") + 2;
// @note(judah): incredibly dumb way to get nicer test runs
path := loc.fully_pathed_filename;
path.data += idx;
path.count -= idx;
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;
}
basic.print("%,%: %...", path, loc.line_number, name);
t: T;

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// These return the bool first so you can check in a conditional,
// rather than having to do '_, ok := ...'
@ -166,7 +168,7 @@ compiler :: #import "Compiler"; // @future
// TESTS
// ----------------------------------------------------------
#if #exists(RUN_TESTS) #run {
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("lo_for:primitives", t => {

26
module.jai Normal file
View file

@ -0,0 +1,26 @@
#scope_export;
byte :: u8;
f32 :: float32;
f64 :: float64;
cstring :: *byte;
rawptr :: *void;
#if size_of(int) == size_of(s64) {
sint :: s64;
uint :: u64;
}
else {
sint :: s32;
uint :: u32;
}
#if size_of(rawptr) == size_of(u64) {
uptr :: u64;
sptr :: s64;
}
else {
uptr :: u32;
sptr :: s32;
}

View file

@ -1,3 +1,5 @@
#module_parameters(RUN_TESTS := false);
// All of the architecture-specific extensions we care to look for.
// Note: Checking for impossible extensions will always turn into a no-op (ex. NEON support on x64).
ISA_Extension :: enum {

View file

@ -1,10 +1,9 @@
#module_parameters(RUN_TESTS_AT_COMPILE_TIME := false);
#module_parameters(RUN_TESTS := false, WITH_SUBMODULES := true);
#load "arch.jai";
#scope_export;
#if WITH_SUBMODULES {
using #import "jc/platform/arch"(RUN_TESTS);
}
#scope_module;
#if RUN_TESTS_AT_COMPILE_TIME {
RUN_TESTS :: true;
}