diff --git a/base.cpp b/base.cpp index aa81213..94740c5 100644 --- a/base.cpp +++ b/base.cpp @@ -1,117 +1,117 @@ #define CORE_BASE #if defined(__clang__) -# define COMPILER_CLANG 1 -# if defined(_WIN32) -# define OS_WINDOWS 1 -# elif defined(__linux__) -# define OS_LINUX 1 -# else -# error Couldnt figure out the platform automatically -# endif + #define COMPILER_CLANG 1 + #if defined(_WIN32) + #define OS_WINDOWS 1 + #elif defined(__linux__) + #define OS_LINUX 1 + #else + #error Couldnt figure out the platform automatically + #endif #elif defined(_MSC_VER) -# define COMPILER_MSVC 1 -# define OS_WINDOWS 1 + #define COMPILER_MSVC 1 + #define OS_WINDOWS 1 #elif defined(__GNUC__) -# define COMPILER_GCC 1 -# if defined(__linux__) -# define OS_LINUX 1 -# endif + #define COMPILER_GCC 1 + #if defined(__linux__) + #define OS_LINUX 1 + #endif #else -# error Couldnt figure out the compiler + #error Couldnt figure out the compiler #endif #if defined(OS_MAC) -#define OS_UNIX 1 + #define OS_UNIX 1 #endif #if defined(OS_LINUX) -#define OS_UNIX 1 + #define OS_UNIX 1 #endif #if !defined(COMPILER_MSVC) -# define COMPILER_MSVC 0 + #define COMPILER_MSVC 0 #endif #if !defined(COMPILER_GCC) -# define COMPILER_GCC 0 + #define COMPILER_GCC 0 #endif #if !defined(COMPILER_CLANG) -# define COMPILER_CLANG 0 + #define COMPILER_CLANG 0 #endif #if !defined(OS_WINDOWS) -# define OS_WINDOWS 0 + #define OS_WINDOWS 0 #endif #if !defined(OS_LINUX) -# define OS_LINUX 0 + #define OS_LINUX 0 #endif #if !defined(OS_MAC) -# define OS_MAC 0 + #define OS_MAC 0 #endif #if !defined(OS_UNIX) -# define OS_UNIX 0 + #define OS_UNIX 0 #endif #if OS_WINDOWS - #define OS_EXE ".exe" - #define OS_NAME "Win32"_s - #define OS_NAME_LOWER "win32"_s + #define OS_EXE ".exe" + #define OS_NAME "Win32"_s + #define OS_NAME_LOWER "win32"_s #elif OS_LINUX - #define OS_EXE ".out" - #define OS_NAME "Linux"_s - #define OS_NAME_LOWER "linux"_s + #define OS_EXE ".out" + #define OS_NAME "Linux"_s + #define OS_NAME_LOWER "linux"_s #elif OS_MAC - #define OS_EXE ".out" - #define OS_NAME "Mac"_s - #define OS_NAME_LOWER "mac"_s + #define OS_EXE ".out" + #define OS_NAME "Mac"_s + #define OS_NAME_LOWER "mac"_s #else - #error Couldnt figure out the OS with C macros! + #error Couldnt figure out the OS with C macros! #endif #if OS_WINDOWS - #define NOMINMAX - #define _CRT_SECURE_NO_WARNINGS - #include - #define Breakpoint __debugbreak() - #define force_inline __forceinline + #define NOMINMAX + #define _CRT_SECURE_NO_WARNINGS + #include + #define Breakpoint __debugbreak() + #define force_inline __forceinline #else - #define Breakpoint (*(volatile int *)0 = 0) - #define force_inline inline + #define Breakpoint (*(volatile int *)0 = 0) + #define force_inline inline #endif #include #include #include -typedef int8_t S8; -typedef int16_t S16; -typedef int32_t S32; -typedef int64_t S64; -typedef uint8_t U8; +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; +typedef uint8_t U8; typedef uint16_t U16; typedef uint32_t U32; typedef uint64_t U64; -typedef S8 B8; -typedef S16 B16; -typedef S32 B32; -typedef S64 B64; +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; +typedef S64 B64; -typedef float F32; -typedef double F64; +typedef float F32; +typedef double F64; #define U64MAX UINT64_MAX #define U32MAX UINT32_MAX #define U16MAX UINT16_MAX -#define U8MAX UINT8_MAX +#define U8MAX UINT8_MAX #define U64MIN 0 #define U32MIN 0 #define U16MIN 0 -#define U8MIN 0 +#define U8MIN 0 #define S64MAX INT64_MAX #define S64MIN INT64_MIN #define S32MAX INT32_MAX #define S32MIN INT32_MIN #define S16MAX INT16_MAX #define S16MIN INT16_MIN -#define S8MAX INT8_MAX -#define S8MIN INT8_MIN +#define S8MAX INT8_MAX +#define S8MIN INT8_MIN #define F32MAX FLT_MAX #define F32MIN FLT_MIN #define F64MAX DBL_MAX @@ -120,305 +120,323 @@ typedef double F64; #define api #define CORE_Static static #define global static -#define assert(x) do{if(!(x))Breakpoint;}while(0) -#define assert_message(x,...) assert(x) +#define assert(x) \ + do { \ + if (!(x)) Breakpoint; \ + } while (0) +#define assert_message(x, ...) assert(x) #define invalid_codepath assert_message(0, "Invalid codepath") -#define invalid_return do{assert_message(0, "Invalid codepath"); return {};}while(0) -#define invalid_default_case default: invalid_codepath +#define invalid_return \ + do { \ + assert_message(0, "Invalid codepath"); \ + return {}; \ + } while (0) +#define invalid_default_case \ + default: \ + invalid_codepath #define not_implemented assert_message(0, "Not implemented") #define unused(x) ((void)x) -#define buff_cap(x) (sizeof(x)/sizeof((x)[0])) -#define is_flag_set(val,flag) ((val) & (flag)) -#define set_flag(val,flag) ((val) |= (flag)) -#define unset_flag(val,flag) ((val) &= (~(flag))) +#define buff_cap(x) (sizeof(x) / sizeof((x)[0])) +#define is_flag_set(val, flag) ((val) & (flag)) +#define set_flag(val, flag) ((val) |= (flag)) +#define unset_flag(val, flag) ((val) &= (~(flag))) #define bit_flag(x) (1ull << (x)) #define kib(x) ((x)*1024llu) -#define mib(x) (kib(x)*1024llu) -#define gib(x) (mib(x)*1024llu) -#define JOIN1(X,Y) X##Y -#define JOIN(X,Y) JOIN1(X,Y) +#define mib(x) (kib(x) * 1024llu) +#define gib(x) (mib(x) * 1024llu) +#define JOIN1(X, Y) X##Y +#define JOIN(X, Y) JOIN1(X, Y) #define string_expand(x) (int)x.len, x.str -struct String{ - U8 *str; - S64 len; +struct String { + U8 *str; + S64 len; }; global String string_null = {(U8 *)"null", 4}; -union Intern_String{ // Basically just String - String s; - struct{ U8 *str; S64 len; }; +union Intern_String { // Basically just String + String s; + struct { + U8 *str; + S64 len; + }; }; struct Allocator { - typedef void *Allocate(Allocator *, size_t); - typedef void Deallocate(Allocator *, void *p); + typedef void *Allocate(Allocator *, size_t); + typedef void Deallocate(Allocator *, void *p); - Allocate *allocate; - Deallocate *deallocate; + Allocate *allocate; + Deallocate *deallocate; }; CORE_Static void memory_zero(void *p, size_t size); CORE_Static void deallocate_stub(Allocator *, void *) {} -#define allocate_array(a, T, size,...) (T *)allocate_size(a, sizeof(T)*(size),##__VA_ARGS__) -#define allocate_struct(a, T, ...) allocate_array(a, T, 1,##__VA_ARGS__) +#define allocate_array(a, T, size, ...) (T *)allocate_size(a, sizeof(T) * (size), ##__VA_ARGS__) +#define allocate_struct(a, T, ...) allocate_array(a, T, 1, ##__VA_ARGS__) CORE_Static void *allocate_size(Allocator *allocator, size_t size, bool zero_memory = true) { - void *result = allocator->allocate(allocator, size); - if (zero_memory) { - memory_zero(result, size); - } - return result; + void *result = allocator->allocate(allocator, size); + if (zero_memory) { + memory_zero(result, size); + } + return result; } CORE_Static void deallocate(Allocator *allocator, void *p) { - assert(p); - allocator->deallocate(allocator, p); + assert(p); + allocator->deallocate(allocator, p); } - //----------------------------------------------------------------------------- // Utilities //----------------------------------------------------------------------------- CORE_Static size_t -get_align_offset(size_t size, size_t align){ - size_t mask = align - 1; - size_t val = size & mask; - if(val){ - val = align - val; - } - return val; +get_align_offset(size_t size, size_t align) { + size_t mask = align - 1; + size_t val = size & mask; + if (val) { + val = align - val; + } + return val; } CORE_Static size_t -align_up(size_t size, size_t align){ - size_t result = size + get_align_offset(size, align); - return result; +align_up(size_t size, size_t align) { + size_t result = size + get_align_offset(size, align); + return result; } CORE_Static size_t -align_down(size_t size, size_t align){ - size += 1; // Make sure 8 when align is 8 doesn't get rounded down to 0 - size_t result = size - (align - get_align_offset(size, align)); - return result; +align_down(size_t size, size_t align) { + size += 1; // Make sure 8 when align is 8 doesn't get rounded down to 0 + size_t result = size - (align - get_align_offset(size, align)); + return result; } CORE_Static void -memory_copy(void *dst, const void *src, size_t size){ - U8 *d = (U8*)dst; - U8 *s = (U8*)src; - for(size_t i = 0; i < size; i++){ - d[i] = s[i]; - } +memory_copy(void *dst, const void *src, size_t size) { + U8 *d = (U8 *)dst; + U8 *s = (U8 *)src; + for (size_t i = 0; i < size; i++) { + d[i] = s[i]; + } } CORE_Static void -memory_zero(void *p, size_t size){ - U8 *pp = (U8 *)p; - for(size_t i = 0; i < size; i++) - pp[i] = 0; +memory_zero(void *p, size_t size) { + U8 *pp = (U8 *)p; + for (size_t i = 0; i < size; i++) + pp[i] = 0; } -template -void swap(T &a, T &b){ - T temp = a; - a = b; - b = temp; +template +void swap(T &a, T &b) { + T temp = a; + a = b; + b = temp; } -template -T max(T a, T b){ - if(a > b) return a; - return b; +template +T max(T a, T b) { + if (a > b) return a; + return b; } -template -T min(T a, T b){ - if(a > b) return b; - return a; +template +T min(T a, T b) { + if (a > b) return b; + return a; } -template -T clamp_top(T val, T max){ - if(val > max) val = max; - return val; +template +T clamp_top(T val, T max) { + if (val > max) val = max; + return val; } -template -T clamp_bot(T bot, T val){ - if(val < bot) val = bot; - return val; +template +T clamp_bot(T bot, T val) { + if (val < bot) val = bot; + return val; } -template -T clamp(T min, T val, T max){ - if(val > max) val = max; - if(val < min) val = min; - return val; +template +T clamp(T min, T val, T max) { + if (val > max) val = max; + if (val < min) val = min; + return val; } CORE_Static U64 hash_string(String string) { - U64 hash = (U64)14695981039346656037ULL; - for (S64 i = 0; i < string.len; i++) { - hash = hash ^ (U64)(string.str[i]); - hash = hash * (U64)1099511628211ULL; - } - return hash; + U64 hash = (U64)14695981039346656037ULL; + for (S64 i = 0; i < string.len; i++) { + hash = hash ^ (U64)(string.str[i]); + hash = hash * (U64)1099511628211ULL; + } + return hash; } CORE_Static U64 hash_u64(U64 x) { - x *= 0xff51afd7ed558ccd; - x ^= x >> 32; - return x; + x *= 0xff51afd7ed558ccd; + x ^= x >> 32; + return x; } CORE_Static U64 hash_ptr(const void *ptr) { - return hash_u64((uintptr_t)ptr); + return hash_u64((uintptr_t)ptr); } CORE_Static U64 hash_mix(U64 x, U64 y) { - // @note: murmur hash 3 mixer but I add the 'y' - // which means it's probably bad, hopefully better - // then some random scribble I could do - x ^= (y >> 33); - x *= 0xff51afd7ed558ccd; - x ^= (x >> 33); - x *= 0xc4ceb9fe1a85ec53; - x ^= (y >> 33); - return x; + // @note: murmur hash 3 mixer but I add the 'y' + // which means it's probably bad, hopefully better + // then some random scribble I could do + x ^= (y >> 33); + x *= 0xff51afd7ed558ccd; + x ^= (x >> 33); + x *= 0xc4ceb9fe1a85ec53; + x ^= (y >> 33); + return x; } CORE_Static U64 is_pow2(U64 x) { - assert(x != 0); - B32 result = (x & (x - 1llu)) == 0; - return result; + assert(x != 0); + B32 result = (x & (x - 1llu)) == 0; + return result; } CORE_Static U64 wrap_around_pow2(U64 x, U64 power_of_2) { - assert(is_pow2(power_of_2)); - U64 r = (((x)&((power_of_2)-1llu))); - return r; + assert(is_pow2(power_of_2)); + U64 r = (((x) & ((power_of_2)-1llu))); + return r; } force_inline String -operator""_s(const char *str, size_t size){ - return String{(U8 *)str, (S64)size}; +operator""_s(const char *str, size_t size) { + return String{(U8 *)str, (S64)size}; } force_inline B32 -operator==(Intern_String a, Intern_String b){ - return a.str == b.str; +operator==(Intern_String a, Intern_String b) { + return a.str == b.str; } force_inline B32 -operator!=(Intern_String a, Intern_String b){ - B32 result = a.str == b.str; - return !result; +operator!=(Intern_String a, Intern_String b) { + B32 result = a.str == b.str; + return !result; } //----------------------------------------------------------------------------- // Very cool macros. Since these are macros it's recommended to wrap them // in a function and not use directly //----- ----------------------------------------------------------------------- -#define SLL_QUEUE_ADD_MOD(f, l, n, next) \ - do { \ - if ((f) == 0) { \ - (f) = (l) = (n); \ - } else { \ - (l) = (l)->next = (n); \ - } \ +#define SLL_QUEUE_ADD_MOD(f, l, n, next) \ + do { \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } \ + else { \ + (l) = (l)->next = (n); \ + } \ } while (0) #define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next) -#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \ - do { \ - if ((f) == (l)) { \ - (f) = (l) = 0; \ - } else { \ - (f) = (f)->next; \ - } \ +#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \ + do { \ + if ((f) == (l)) { \ + (f) = (l) = 0; \ + } \ + else { \ + (f) = (f)->next; \ + } \ } while (0) #define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next) -#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \ - do { \ - (new_stack_base)->next = (stack_base); \ - (stack_base) = (new_stack_base); \ +#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \ + do { \ + (new_stack_base)->next = (stack_base); \ + (stack_base) = (new_stack_base); \ } while (0) #define SLL_STACK_ADD(stack_base, new_stack_base) SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) -#define SLL_STACK_POP(stack_base) \ - do { \ - if (stack_base) { \ - auto(N) = (stack_base); \ - (stack_base) = (stack_base)->next; \ - (N)->next = 0; \ - } \ +#define SLL_STACK_POP(stack_base) \ + do { \ + if (stack_base) { \ + auto(N) = (stack_base); \ + (stack_base) = (stack_base)->next; \ + (N)->next = 0; \ + } \ } while (0) -#define DLL_QUEUE_ADD_LAST_MOD(f, l, node, next, prev) \ - do { \ - if ((f) == 0) { \ - (f) = (l) = (node); \ - (node)->prev = 0; \ - (node)->next = 0; \ - } else { \ - (l)->next = (node); \ - (node)->prev = (l); \ - (node)->next = 0; \ - (l) = (node); \ - } \ +#define DLL_QUEUE_ADD_LAST_MOD(f, l, node, next, prev) \ + do { \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + else { \ + (l)->next = (node); \ + (node)->prev = (l); \ + (node)->next = 0; \ + (l) = (node); \ + } \ } while (0) #define DLL_QUEUE_ADD_LAST(f, l, node) DLL_QUEUE_ADD_LAST_MOD(f, l, node, next, prev) #define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_LAST(f, l, node) -#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \ - do { \ - if ((first) == (last)) { \ - assert_message((node) == (first), "Macro assert failed"); \ - (first) = (last) = 0; \ - } else if ((last) == (node)) { \ - (last) = (last)->prev; \ - (last)->next = 0; \ - } else if ((first) == (node)) { \ - (first) = (first)->next; \ - (first)->prev = 0; \ - } else { \ - (node)->prev->next = (node)->next; \ - (node)->next->prev = (node)->prev; \ - } \ +#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \ + do { \ + if ((first) == (last)) { \ + assert_message((node) == (first), "Macro assert failed"); \ + (first) = (last) = 0; \ + } \ + else if ((last) == (node)) { \ + (last) = (last)->prev; \ + (last)->next = 0; \ + } \ + else if ((first) == (node)) { \ + (first) = (first)->next; \ + (first)->prev = 0; \ + } \ + else { \ + (node)->prev->next = (node)->next; \ + (node)->next->prev = (node)->prev; \ + } \ } while (0) #define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) -#define DLL_STACK_ADD_MOD(first, node, next, prev) \ - do { \ - (node)->next = (first); \ - if ((first)) \ - (first)->prev = (node); \ - (first) = (node); \ - (node)->prev = 0; \ +#define DLL_STACK_ADD_MOD(first, node, next, prev) \ + do { \ + (node)->next = (first); \ + if ((first)) \ + (first)->prev = (node); \ + (first) = (node); \ + (node)->prev = 0; \ } while (0) #define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev) -#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \ - do { \ - if ((node) == (first)) { \ - (first) = (first)->next; \ - if ((first)) \ - (first)->prev = 0; \ - } else { \ - (node)->prev->next = (node)->next; \ - if ((node)->next) \ - (node)->next->prev = (node)->prev; \ - } \ +#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \ + do { \ + if ((node) == (first)) { \ + (first) = (first)->next; \ + if ((first)) \ + (first)->prev = 0; \ + } \ + else { \ + (node)->prev->next = (node)->next; \ + if ((node)->next) \ + (node)->next->prev = (node)->prev; \ + } \ } while (0) #define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev) -#define For_Linked_List_Named(a,it) for(auto *it = (a); it; it=it->next) // @todo: reference? -#define For_Linked_List(a) For_Linked_List_Named(a,it) -#define For_Named(a,it) for(auto &it : (a)) -#define For(a) For_Named((a),it) +#define For_Linked_List_Named(a, it) for (auto *it = (a); it; it = it->next) // @todo: reference? +#define For_Linked_List(a) For_Linked_List_Named(a, it) +#define For_Named(a, it) for (auto &it : (a)) +#define For(a) For_Named((a), it) diff --git a/base_arena.cpp b/base_arena.cpp index 942881f..dd92a90 100644 --- a/base_arena.cpp +++ b/base_arena.cpp @@ -5,18 +5,18 @@ struct CRT_Heap : Allocator {}; static void *crt_allocate(Allocator *allocator, size_t size) { return malloc(size); } static void crt_deallocate(Allocator *allocator, void *p) { return free(p); } static CRT_Heap make_crt_heap() { - CRT_Heap result = {}; - result.allocate = crt_allocate; - result.deallocate = crt_deallocate; - return result; + CRT_Heap result = {}; + result.allocate = crt_allocate; + result.deallocate = crt_deallocate; + return result; } //----------------------------------------------------------------------------- // Memory OS //----------------------------------------------------------------------------- -struct OS_Memory{ - size_t commit, reserve; - U8 *data; +struct OS_Memory { + size_t commit, reserve; + U8 *data; }; CORE_Static OS_Memory os_reserve(size_t size); CORE_Static B32 os_commit(OS_Memory *m, size_t size); @@ -26,104 +26,107 @@ CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos); //----------------------------------------------------------------------------- // Memory arenas //----------------------------------------------------------------------------- -global const size_t default_reserve_size = gib(4); -global const size_t default_alignment = 8; +global const size_t default_reserve_size = gib(4); +global const size_t default_alignment = 8; global const size_t additional_commit_size = mib(1); struct Arena : Allocator { - OS_Memory memory; - size_t alignment; - size_t len; - String debug_string; + OS_Memory memory; + size_t alignment; + size_t len; + String debug_string; }; CORE_Static void - arena_pop_pos(Arena *arena, size_t pos){ - pos = clamp_top(pos, arena->len); - arena->len = pos; +arena_pop_pos(Arena *arena, size_t pos) { + pos = clamp_top(pos, arena->len); + arena->len = pos; } CORE_Static void * - arena_pop(Arena *arena, size_t size){ - size = clamp_top(size, arena->len); - arena->len -= size; - return arena->memory.data + arena->len; +arena_pop(Arena *arena, size_t size) { + size = clamp_top(size, arena->len); + arena->len -= size; + return arena->memory.data + arena->len; } CORE_Static void - arena_release(Arena *arena){ - os_release(&arena->memory); +arena_release(Arena *arena) { + os_release(&arena->memory); } CORE_Static void - arena_clear(Arena *arena){ - arena_pop_pos(arena, 0); +arena_clear(Arena *arena) { + arena_pop_pos(arena, 0); } CORE_Static void * - arena_push_size(Arena *a, size_t size){ - size_t generous_size = size + a->alignment; - if(a->len+generous_size>a->memory.commit){ - assert(a->memory.reserve > 0); - B32 result = os_commit(&a->memory, generous_size+additional_commit_size); - assert(result); - } +arena_push_size(Arena *a, size_t size) { + size_t generous_size = size + a->alignment; + if (a->len + generous_size > a->memory.commit) { + assert(a->memory.reserve > 0); + B32 result = os_commit(&a->memory, generous_size + additional_commit_size); + assert(result); + } - a->len = align_up(a->len, a->alignment); - assert(a->memory.reserve > a->len + size); - void *result = (U8*)a->memory.data + a->len; - a->len += size; + a->len = align_up(a->len, a->alignment); + assert(a->memory.reserve > a->len + size); + void *result = (U8 *)a->memory.data + a->len; + a->len += size; - return result; + return result; } CORE_Static Arena - push_arena(Allocator *allocator, size_t size, String debug_name) { - Arena result = {}; - result.memory.data = (U8 *)allocate_size(allocator, size); - result.memory.reserve = size; - result.alignment = default_alignment; - result.debug_string = debug_name; - result.allocate = (Allocator::Allocate *)arena_push_size; - result.deallocate = (Allocator::Deallocate *)deallocate_stub; - return result; +push_arena(Allocator *allocator, size_t size, String debug_name) { + Arena result = {}; + result.memory.data = (U8 *)allocate_size(allocator, size); + result.memory.reserve = size; + result.alignment = default_alignment; + result.debug_string = debug_name; + result.allocate = (Allocator::Allocate *)arena_push_size; + result.deallocate = (Allocator::Deallocate *)deallocate_stub; + return result; } CORE_Static void - arena_init(Arena *a, String debug_name){ - a->memory = os_reserve(default_reserve_size); - a->alignment = default_alignment; - a->debug_string = debug_name; - a->allocate = (Allocator::Allocate *)arena_push_size; - a->deallocate = (Allocator::Deallocate *)deallocate_stub; +arena_init(Arena *a, String debug_name) { + a->memory = os_reserve(default_reserve_size); + a->alignment = default_alignment; + a->debug_string = debug_name; + a->allocate = (Allocator::Allocate *)arena_push_size; + a->deallocate = (Allocator::Deallocate *)deallocate_stub; } CORE_Static Arena - arena_sub(Arena *base, size_t size, String debug_name) { - Arena result = {}; - result.memory.data = (U8 *)arena_push_size(base, size); - result.memory.commit = size; - result.memory.reserve = size; - result.alignment = default_alignment; - result.allocate = (Allocator::Allocate *)arena_push_size; - result.deallocate= (Allocator::Deallocate *)deallocate_stub; - return result; +arena_sub(Arena *base, size_t size, String debug_name) { + Arena result = {}; + result.memory.data = (U8 *)arena_push_size(base, size); + result.memory.commit = size; + result.memory.reserve = size; + result.alignment = default_alignment; + result.allocate = (Allocator::Allocate *)arena_push_size; + result.deallocate = (Allocator::Deallocate *)deallocate_stub; + return result; } CORE_Static Arena arena_from_buffer(void *buffer, size_t size) { - Arena result = {}; - result.memory.data = (U8 *)buffer; - result.memory.commit = size; - result.memory.reserve = size; - result.alignment = default_alignment; - result.allocate = (Allocator::Allocate *)arena_push_size; - result.deallocate= (Allocator::Deallocate *)deallocate_stub; - return result; + Arena result = {}; + result.memory.data = (U8 *)buffer; + result.memory.commit = size; + result.memory.reserve = size; + result.alignment = default_alignment; + result.allocate = (Allocator::Allocate *)arena_push_size; + result.deallocate = (Allocator::Deallocate *)deallocate_stub; + return result; } struct Scratch_Scope { - Arena *arena; - int pos; - Scratch_Scope(Arena *arena) { this->arena = arena; this->pos = arena->len; } - ~Scratch_Scope() { this->arena->len = this->pos; } + Arena *arena; + int pos; + Scratch_Scope(Arena *arena) { + this->arena = arena; + this->pos = arena->len; + } + ~Scratch_Scope() { this->arena->len = this->pos; } }; diff --git a/base_data_structures.cpp b/base_data_structures.cpp index 58f5f8f..b7ae3c6 100644 --- a/base_data_structures.cpp +++ b/base_data_structures.cpp @@ -2,295 +2,301 @@ //----------------------------------------------------------------------------- // Array //----------------------------------------------------------------------------- -template -struct Array{ - Allocator *allocator; - T *data; - S64 cap; - S64 len; +template +struct Array { + Allocator *allocator; + T *data; + S64 cap; + S64 len; - T *push_empty(S64 count = 1){ - grow(count); - T *result = data + len; - len += count; - return result; - } - - T *push_empty_zero(S64 count = 1){ - T *result = push_empty(count); - memory_zero(result, count*sizeof(T)); - return result; - } - - void grow(S64 required_size){ - if(cap == 0){ - S64 new_cap = max(required_size*2, (S64)16); - data = allocate_array(allocator, T, new_cap); - cap = new_cap; + T *push_empty(S64 count = 1) { + grow(count); + T *result = data + len; + len += count; + return result; } - else if(len + required_size > cap){ - U64 new_cap = max(cap * 2, len+required_size+1); - T *new_data = allocate_array(allocator, T, new_cap); - memory_copy(new_data, data, cap*sizeof(T)); - deallocate(allocator, data); - data = new_data; - cap = new_cap; + + T *push_empty_zero(S64 count = 1) { + T *result = push_empty(count); + memory_zero(result, count * sizeof(T)); + return result; } - } - S64 get_index(T *item){ - assert((data <= item) && ((data + len) > item)); - size_t offset = item - data; - return (S64)offset; - } - - void add(Array items){ - For(items){ - add(it); + void grow(S64 required_size) { + if (cap == 0) { + S64 new_cap = max(required_size * 2, (S64)16); + data = allocate_array(allocator, T, new_cap); + cap = new_cap; + } + else if (len + required_size > cap) { + U64 new_cap = max(cap * 2, len + required_size + 1); + T *new_data = allocate_array(allocator, T, new_cap); + memory_copy(new_data, data, cap * sizeof(T)); + deallocate(allocator, data); + data = new_data; + cap = new_cap; + } } - } - void add(T item){ - grow(1); - data[len++] = item; - } + S64 get_index(T *item) { + assert((data <= item) && ((data + len) > item)); + size_t offset = item - data; + return (S64)offset; + } - S64 addi(T item){ - S64 result = len; - grow(1); - data[len++] = item; - return result; - } + void add(Array items) { + For(items) { + add(it); + } + } - void unordered_remove(T *item){ - assert(len > 0); - assert((data <= item) && ((data + len) > item)); - *item = data[--len]; - } + void add(T item) { + grow(1); + data[len++] = item; + } - void init(Allocator *a, S64 size = 16){ - allocator = a; - data = allocate_array(a, T, size); - cap = size; - } + S64 addi(T item) { + S64 result = len; + grow(1); + data[len++] = item; + return result; + } - Array copy(Allocator *a){ - Array result = {}; - result.len = len; - result.cap = len*2; - result.allocator = a; - result.data = allocate_array(a, T, result.cap); - memory_copy(result.data, data, sizeof(T)*result.len); - return result; - } + void unordered_remove(T *item) { + assert(len > 0); + assert((data <= item) && ((data + len) > item)); + *item = data[--len]; + } - Array tight_copy(Allocator *a){ - Array result = {}; - result.len = len; - result.cap = len; - result.allocator = 0; - result.data = allocate_array(a, T, len); - memory_copy(result.data, data, sizeof(T)*len); - return result; - } + void init(Allocator *a, S64 size = 16) { + allocator = a; + data = allocate_array(a, T, size); + cap = size; + } - force_inline B32 is_last(T *item){ return item == last(); } - force_inline B32 is_first(T *item){ return item == begin(); } - force_inline void clear(){ len = 0; } - force_inline T pop() { return data[--len]; } - force_inline T *last() { return data + len - 1; } - force_inline T *begin() { return data; } - force_inline T *end () { return data + len; } - force_inline T &operator[](S64 i){ assert(i >= 0 && i < cap); return data[i]; } + Array copy(Allocator *a) { + Array result = {}; + result.len = len; + result.cap = len * 2; + result.allocator = a; + result.data = allocate_array(a, T, result.cap); + memory_copy(result.data, data, sizeof(T) * result.len); + return result; + } + + Array tight_copy(Allocator *a) { + Array result = {}; + result.len = len; + result.cap = len; + result.allocator = 0; + result.data = allocate_array(a, T, len); + memory_copy(result.data, data, sizeof(T) * len); + return result; + } + + force_inline B32 is_last(T *item) { return item == last(); } + force_inline B32 is_first(T *item) { return item == begin(); } + force_inline void clear() { len = 0; } + force_inline T pop() { return data[--len]; } + force_inline T *last() { return data + len - 1; } + force_inline T *begin() { return data; } + force_inline T *end() { return data + len; } + force_inline T &operator[](S64 i) { + assert(i >= 0 && i < cap); + return data[i]; + } }; - -template +template CORE_Static Array -array_make(Allocator *a, S64 size = 16){ - Array result = {}; - result.init(a, size); - return result; +array_make(Allocator *a, S64 size = 16) { + Array result = {}; + result.init(a, size); + return result; } //----------------------------------------------------------------------------- // Map //----------------------------------------------------------------------------- -struct Map_Key_Value{ - int occupied; - U64 key; - void *value; +struct Map_Key_Value { + int occupied; + U64 key; + void *value; }; -struct Map{ - Allocator *allocator; - Map_Key_Value *data; - S64 len; - S64 cap; +struct Map { + Allocator *allocator; + Map_Key_Value *data; + S64 len; + S64 cap; }; CORE_Static void map_insert(Map *map, U64 key, void *val); CORE_Static void - map_grow(Map *map, S64 new_size){ - new_size = max((S64)16, new_size); - assert(new_size > map->cap); - assert(is_pow2(new_size)); - assert(map->allocator); +map_grow(Map *map, S64 new_size) { + new_size = max((S64)16, new_size); + assert(new_size > map->cap); + assert(is_pow2(new_size)); + assert(map->allocator); - Map new_map = {}; - new_map.data = allocate_array(map->allocator, Map_Key_Value, new_size); - new_map.cap = new_size; - new_map.allocator = map->allocator; + Map new_map = {}; + new_map.data = allocate_array(map->allocator, Map_Key_Value, new_size); + new_map.cap = new_size; + new_map.allocator = map->allocator; - for(S64 i = 0; i < map->cap; i++){ - if(map->data[i].occupied){ - map_insert(&new_map, map->data[i].key, map->data[i].value); + for (S64 i = 0; i < map->cap; i++) { + if (map->data[i].occupied) { + map_insert(&new_map, map->data[i].key, map->data[i].value); + } } - } - if(map->data) deallocate(map->allocator, map->data); - *map = new_map; + if (map->data) deallocate(map->allocator, map->data); + *map = new_map; } CORE_Static Map - map_make(Allocator *a, S64 size){ - Map result = {a}; - map_grow(&result, size); - return result; +map_make(Allocator *a, S64 size) { + Map result = {a}; + map_grow(&result, size); + return result; } CORE_Static void - map_insert(Map *map, U64 key, void *val){ - assert(val); - assert(key); - // if(key == 0) key+=1; +map_insert(Map *map, U64 key, void *val) { + assert(val); + assert(key); + // if(key == 0) key+=1; - if((2*map->len) + 1 > map->cap){ - map_grow(map, 2*map->cap); - } - - U64 hash = hash_u64(key); - U64 index = wrap_around_pow2(hash, map->cap); - U64 i = index; - for(;;){ - if(map->data[i].occupied == false){ - map->len++; - map->data[i].occupied = true; - map->data[i].key = key; - map->data[i].value = val; - return; - } - else if(map->data[i].key == key){ - map->data[i].value = val; - return; + if ((2 * map->len) + 1 > map->cap) { + map_grow(map, 2 * map->cap); } - i = wrap_around_pow2(i+1, map->cap); - if(i == map->cap){ - return; + U64 hash = hash_u64(key); + U64 index = wrap_around_pow2(hash, map->cap); + U64 i = index; + for (;;) { + if (map->data[i].occupied == false) { + map->len++; + map->data[i].occupied = true; + map->data[i].key = key; + map->data[i].value = val; + return; + } + else if (map->data[i].key == key) { + map->data[i].value = val; + return; + } + + i = wrap_around_pow2(i + 1, map->cap); + if (i == map->cap) { + return; + } } - } } CORE_Static Map_Key_Value * - map_base_get(Map *map, U64 key){ - if(map->len == 0) return 0; - assert(key); +map_base_get(Map *map, U64 key) { + if (map->len == 0) return 0; + assert(key); - U64 hash = hash_u64(key); - U64 index = wrap_around_pow2(hash, map->cap); - U64 i = index; - for(;;){ - if(map->data[i].key == key){ - return map->data + i; + U64 hash = hash_u64(key); + U64 index = wrap_around_pow2(hash, map->cap); + U64 i = index; + for (;;) { + if (map->data[i].key == key) { + return map->data + i; + } + else if (map->data[i].key == 0) { + return 0; + } + + i = wrap_around_pow2(i + 1, map->cap); + if (i == map->cap) { + return 0; + } } - else if(map->data[i].key == 0){ - return 0; +} + +CORE_Static void * +map_get(Map *map, U64 key) { + Map_Key_Value *result = map_base_get(map, key); + if (result && result->occupied) return result->value; + return 0; +} + +CORE_Static void * +map_remove(Map *map, U64 key) { + Map_Key_Value *kv = map_base_get(map, key); + if (kv) { + kv->occupied = false; + return kv->value; } - - i = wrap_around_pow2(i+1, map->cap); - if(i == map->cap){ - return 0; - } - } + return 0; } CORE_Static void * - map_get(Map *map, U64 key){ - Map_Key_Value *result = map_base_get(map, key); - if(result && result->occupied) return result->value; - return 0; +map_get(Map *map, void *pointer) { + return map_get(map, (U64)pointer); } CORE_Static void * - map_remove(Map *map, U64 key){ - Map_Key_Value *kv = map_base_get(map, key); - if(kv){ - kv->occupied = false; - return kv->value; - } - return 0; -} - -CORE_Static void * - map_get(Map *map, void *pointer){ - return map_get(map, (U64)pointer); -} - -CORE_Static void * - map_get(Map *map, Intern_String string){ - return map_get(map, hash_string(string.s)); +map_get(Map *map, Intern_String string) { + return map_get(map, hash_string(string.s)); } CORE_Static void - map_insert(Map *map, void *key, void *value){ - map_insert(map, (U64)key, value); +map_insert(Map *map, void *key, void *value) { + map_insert(map, (U64)key, value); } CORE_Static void - map_insert(Map *map, Intern_String key, void *value){ - map_insert(map, hash_string(key.s), value); +map_insert(Map *map, Intern_String key, void *value) { + map_insert(map, hash_string(key.s), value); } //----------------------------------------------------------------------------- // String intern //----------------------------------------------------------------------------- -struct Intern_Table{ - Allocator *string_allocator; - Map map; - U8 *first_keyword; - U8 *last_keyword; +struct Intern_Table { + Allocator *string_allocator; + Map map; + U8 *first_keyword; + U8 *last_keyword; }; CORE_Static Intern_Table - intern_table_make(Allocator *string_allocator, Allocator *map_allocator, S64 initial_size = 32){ - Intern_Table result = {}; - result.map = map_make(map_allocator, initial_size); - result.string_allocator = string_allocator; - return result; +intern_table_make(Allocator *string_allocator, Allocator *map_allocator, S64 initial_size = 32) { + Intern_Table result = {}; + result.map = map_make(map_allocator, initial_size); + result.string_allocator = string_allocator; + return result; } CORE_Static Intern_String - intern_string(Intern_Table *t, String string){ - assert(t->string_allocator); - U64 hash = hash_string(string); - U8 *slot = (U8 *)map_get(&t->map, hash); - if(slot){ - // @todo: Is this a cast bug: *(slot-sizeof(S64))? slot is u8 so truncates? - Intern_String result = {{slot, *(slot-sizeof(S64))}}; +intern_string(Intern_Table *t, String string) { + assert(t->string_allocator); + U64 hash = hash_string(string); + U8 *slot = (U8 *)map_get(&t->map, hash); + if (slot) { + // @todo: Is this a cast bug: *(slot-sizeof(S64))? slot is u8 so truncates? + Intern_String result = { + {slot, *(slot - sizeof(S64))} + }; + return result; + } + + S64 *len_address = (S64 *)allocate_size(t->string_allocator, string.len + 1 + sizeof(S64), false); + *len_address = string.len; + + U8 *string_address = (U8 *)(len_address + 1); + memory_copy(string_address, string.str, string.len); + string_address[string.len] = 0; + + map_insert(&t->map, hash, string_address); + Intern_String result = { + {string_address, *len_address} + }; + return result; - } - - S64 *len_address = (S64 *)allocate_size(t->string_allocator, string.len+1+sizeof(S64), false); - *len_address = string.len; - - U8 *string_address = (U8 *)(len_address + 1); - memory_copy(string_address, string.str, string.len); - string_address[string.len] = 0; - - map_insert(&t->map, hash, string_address); - Intern_String result = {{string_address, *len_address}}; - - return result; } //----------------------------------------------------------------------------- @@ -302,262 +308,263 @@ CORE_Static Intern_String const int LIST_DEFAULT_BLOCK_SIZE = 32; const int LIST_DEFAULT_ALLOCATION_MUL = 2; -template -struct List_Node{ - List_Node *next; - List_Node *prev; - int cap; - int len; - T data[]; +template +struct List_Node { + List_Node *next; + List_Node *prev; + int cap; + int len; + T data[]; }; -template -struct List{ - int block_size = 0; - int allocation_multiplier = 0; - List_Node *first = 0; - List_Node *last = 0; - List_Node *first_free = 0; +template +struct List { + int block_size = 0; + int allocation_multiplier = 0; + List_Node *first = 0; + List_Node *last = 0; + List_Node *first_free = 0; - struct Iter{ - T *item; - List_Node *node; - int node_index; + struct Iter { + T *item; + List_Node *node; + int node_index; - T &operator*() { return *item; } + T &operator*() { return *item; } - Iter &operator++() { - if (node) { - if (node_index + 1 >= node->len) { - node = node->next; - node_index = -1; - item = 0; + Iter &operator++() { + if (node) { + if (node_index + 1 >= node->len) { + node = node->next; + node_index = -1; + item = 0; + } + + if (node) { + node_index += 1; + item = node->data + node_index; + } + } + return *this; } + }; - if (node) { - node_index += 1; - item = node->data + node_index; - } - } - return *this; + Iter begin() { + Iter result = {}; + result.node = first; + result.node_index = -1; + return ++result; } - }; - - Iter begin() { - Iter result = {}; - result.node = first; - result.node_index = -1; - return ++result; - } - Iter end() { return{}; } - friend bool operator!=(Iter &a, Iter &b) { return a.item != b.item; } + Iter end() { return {}; } + friend bool operator!=(Iter &a, Iter &b) { return a.item != b.item; } }; -template -List_Node *list_allocate_node(Allocator *arena, int size){ - auto node = (List_Node *)allocate_size(arena, sizeof(List_Node) + size*sizeof(T), false); - node->cap = size; - node->len = 0; - node->next = 0; - node->prev = 0; - return node; +template +List_Node *list_allocate_node(Allocator *arena, int size) { + auto node = (List_Node *)allocate_size(arena, sizeof(List_Node) + size * sizeof(T), false); + node->cap = size; + node->len = 0; + node->next = 0; + node->prev = 0; + return node; } -template -void list_allocate_free_node(Allocator *arena, List *list, int size){ - List_Node *node = list_allocate_node(arena, size); - DLL_STACK_ADD(list->first_free, node); +template +void list_allocate_free_node(Allocator *arena, List *list, int size) { + List_Node *node = list_allocate_node(arena, size); + DLL_STACK_ADD(list->first_free, node); } -template -void list_make_sure_there_is_room_for_item_count(Allocator *arena, List *list, int item_count){ - if(list->last == 0 || list->last->len + item_count > list->last->cap){ - // Not enough space we need to get a new block - List_Node *node = 0; +template +void list_make_sure_there_is_room_for_item_count(Allocator *arena, List *list, int item_count) { + if (list->last == 0 || list->last->len + item_count > list->last->cap) { + // Not enough space we need to get a new block + List_Node *node = 0; - // Iterate the free list to check if we have a block of required size there - For_Linked_List(list->first_free){ - if(it->cap >= item_count){ - DLL_STACK_REMOVE(list->first_free, it); - node = it; - node->len = 0; - break; - } + // Iterate the free list to check if we have a block of required size there + For_Linked_List(list->first_free) { + if (it->cap >= item_count) { + DLL_STACK_REMOVE(list->first_free, it); + node = it; + node->len = 0; + break; + } + } + + // We don't have a block on the free list need to allocate + if (!node) { + // Set default values if not initialized + if (!list->allocation_multiplier) list->allocation_multiplier = LIST_DEFAULT_ALLOCATION_MUL; + if (!list->block_size) list->block_size = LIST_DEFAULT_BLOCK_SIZE; + + if (item_count > list->block_size) + list->block_size = item_count * 2; + node = list_allocate_node(arena, list->block_size); + list->block_size *= list->allocation_multiplier; + } + + assert(node); + DLL_QUEUE_ADD_LAST(list->first, list->last, node); + } +} + +template +T *list_get(List *list, int index, List_Node **node = 0, int *in_block_index = 0) { + if (list) { + int i = 0; + For_Linked_List(list->first) { + int lookup_i = index - i; + if (lookup_i < it->len) { + if (node) *node = it; + if (in_block_index) *in_block_index = lookup_i; + return it->data + lookup_i; + } + i += it->len; + } + } + return 0; +} + +template +T *getp(List *list, int index) { + return list_get(list, index); +} + +template +T get(List *list, int index) { + return *list_get(list, index); +} + +template +void add(Allocator *arena, List *list, T item) { + list_make_sure_there_is_room_for_item_count(arena, list, 1); + list->last->data[list->last->len++] = item; +} + +template +T *add_size(Allocator *arena, List *list, int count = 1, int zero_memory = 0) { + list_make_sure_there_is_room_for_item_count(arena, list, count); + T *result = list->last->data + list->last->len; + list->last->len += count; + + if (zero_memory) { + memory_zero(result, sizeof(T) * count); } - // We don't have a block on the free list need to allocate - if(!node){ - // Set default values if not initialized - if(!list->allocation_multiplier) list->allocation_multiplier = LIST_DEFAULT_ALLOCATION_MUL; - if(!list->block_size) list->block_size = LIST_DEFAULT_BLOCK_SIZE; - - if(item_count > list->block_size) - list->block_size = item_count*2; - node = list_allocate_node(arena, list->block_size); - list->block_size *= list->allocation_multiplier; - } - - assert(node); - DLL_QUEUE_ADD_LAST(list->first, list->last, node); - } + return result; } -template -T *list_get(List *list, int index, List_Node **node = 0, int *in_block_index = 0){ - if(list){ - int i = 0; - For_Linked_List(list->first){ - int lookup_i = index - i; - if(lookup_i < it->len) { - if(node) *node = it; - if(in_block_index) *in_block_index = lookup_i; - return it->data + lookup_i; - } - i += it->len; - } - } - return 0; -} - -template -T *getp(List *list, int index){ - return list_get(list, index); -} - -template -T get(List *list, int index){ - return *list_get(list, index); -} - -template -void add(Allocator *arena, List *list, T item){ - list_make_sure_there_is_room_for_item_count(arena, list, 1); - list->last->data[list->last->len++] = item; -} - -template -T *add_size(Allocator *arena, List *list, int count = 1, int zero_memory = 0){ - list_make_sure_there_is_room_for_item_count(arena, list, count); - T *result = list->last->data + list->last->len; - list->last->len += count; - - if(zero_memory){ - memory_zero(result, sizeof(T)*count); - } - - return result; -} - -template -void list_free_node(List *list, List_Node *node){ +template +void list_free_node(List *list, List_Node *node) { #if 1 - // Make sure it's actually in list list - bool found = false; - For_Linked_List(list->first){ - if(it == node){ - found = true; - break; + // Make sure it's actually in list list + bool found = false; + For_Linked_List(list->first) { + if (it == node) { + found = true; + break; + } } - } - assert(found); + assert(found); #endif - DLL_QUEUE_REMOVE(list->first, list->last, node); - DLL_STACK_ADD(list->first_free, node); + DLL_QUEUE_REMOVE(list->first, list->last, node); + DLL_STACK_ADD(list->first_free, node); } -template -void clear(List *list){ - memory_zero(list, sizeof(List)); +template +void clear(List *list) { + memory_zero(list, sizeof(List)); } -template -int length(List *list){ - int result = 0; - For_Linked_List(list->first){ - result += it->len; - } - return result; -} - -template -void free_all_nodes(List *list){ - assert(!list->last->next); - assert(!list->first->prev); - list->last->next = list->first_free; - if(list->first_free) list->first_free->prev = list->last; - list->first_free = list->first; - list->last = list->first = 0; -} - -template -T ordered_remove(List *list, int index){ - List_Node *node; int in_block_index; - T *data = list_get(list, index, &node, &in_block_index); - - assert_message(data, "Trying to unordered_remove element that's outside of the List"); - if(!data) - return {}; - - T result = *data; - - // Check if we need to deallocate the block - if(node->len == 1) { - list_free_node(list, node); +template +int length(List *list) { + int result = 0; + For_Linked_List(list->first) { + result += it->len; + } return result; - } - - // We need to move part of the block to fill the new empty spot - int right_count = (--node->len) - in_block_index; - memory_copy(data, data+1, sizeof(T)*right_count); - return result; } -template -T unordered_remove(List *list, int index){ - List_Node *node; - T *data = list_get(list, index, &node); - - assert_message(data, "Trying to unordered_remove element that's outside of the List"); - if(!data) - return {}; - - assert(node->len); - assert(node->cap); - - // Swap - T result = *data; - *data = node->data[node->len - 1]; - - node->len -= 1; - if(node->len == 0){ - list_free_node(list, node); - } - - return result; +template +void free_all_nodes(List *list) { + assert(!list->last->next); + assert(!list->first->prev); + list->last->next = list->first_free; + if (list->first_free) list->first_free->prev = list->last; + list->first_free = list->first; + list->last = list->first = 0; } -template -T pop(List *list){ - assert(list->last != 0); - assert(list->last->len > 0); - T result = list->last->data[--list->last->len]; - if(list->last->len == 0){ - list_free_node(list, list->last); - } - return result; +template +T ordered_remove(List *list, int index) { + List_Node *node; + int in_block_index; + T *data = list_get(list, index, &node, &in_block_index); + + assert_message(data, "Trying to unordered_remove element that's outside of the List"); + if (!data) + return {}; + + T result = *data; + + // Check if we need to deallocate the block + if (node->len == 1) { + list_free_node(list, node); + return result; + } + + // We need to move part of the block to fill the new empty spot + int right_count = (--node->len) - in_block_index; + memory_copy(data, data + 1, sizeof(T) * right_count); + return result; } -template -T *merge(Allocator *arena, List *list){ - int len = length(list); - T *result = allocate_size(arena, T, len, false); +template +T unordered_remove(List *list, int index) { + List_Node *node; + T *data = list_get(list, index, &node); - int i = 0; - For_Linked_List(list->first){ - memory_copy(result + i, it->data, it->len*sizeof(T)); - i += it->len; - } + assert_message(data, "Trying to unordered_remove element that's outside of the List"); + if (!data) + return {}; - return result; + assert(node->len); + assert(node->cap); + + // Swap + T result = *data; + *data = node->data[node->len - 1]; + + node->len -= 1; + if (node->len == 0) { + list_free_node(list, node); + } + + return result; +} + +template +T pop(List *list) { + assert(list->last != 0); + assert(list->last->len > 0); + T result = list->last->data[--list->last->len]; + if (list->last->len == 0) { + list_free_node(list, list->last); + } + return result; +} + +template +T *merge(Allocator *arena, List *list) { + int len = length(list); + T *result = allocate_size(arena, T, len, false); + + int i = 0; + For_Linked_List(list->first) { + memory_copy(result + i, it->data, it->len * sizeof(T)); + i += it->len; + } + + return result; } diff --git a/base_string.cpp b/base_string.cpp index cd242ce..2acf37d 100644 --- a/base_string.cpp +++ b/base_string.cpp @@ -1,233 +1,234 @@ CORE_Static U8 to_lower_case(U8 a) { - if (a >= 'A' && a <= 'Z') - a += 32; - return a; + if (a >= 'A' && a <= 'Z') + a += 32; + return a; } CORE_Static U8 to_upper_case(U8 a) { - if (a >= 'a' && a <= 'z') - a -= 32; - return a; + if (a >= 'a' && a <= 'z') + a -= 32; + return a; } CORE_Static U8 -char_to_lower(U8 c){ - if(c >= 'A' && c <= 'Z') - c += 32; - return c; +char_to_lower(U8 c) { + if (c >= 'A' && c <= 'Z') + c += 32; + return c; } CORE_Static U8 -char_to_upper(U8 c){ - if(c >= 'a' && c <= 'z') - c -= 32; - return c; +char_to_upper(U8 c) { + if (c >= 'a' && c <= 'z') + c -= 32; + return c; } CORE_Static B32 is_whitespace(U8 w) { - bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; - return result; + bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; } CORE_Static B32 is_alphabetic(U8 a) { - if ((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) { - return true; - } - return false; + if ((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) { + return true; + } + return false; } CORE_Static B32 is_number(U8 a) { - B32 result = a >= '0' && a <= '9'; - return result; + B32 result = a >= '0' && a <= '9'; + return result; } CORE_Static B32 is_alphanumeric(U8 a) { - B32 result = is_number(a) || is_alphabetic(a); - return result; + B32 result = is_number(a) || is_alphabetic(a); + return result; } CORE_Static B32 string_compare(String a, String b, B32 ignore_case = false) { - if (a.len != b.len) - return false; - for (S64 i = 0; i < a.len; i++) { - U8 A = a.str[i]; - U8 B = b.str[i]; - if (ignore_case) { - A = to_lower_case(A); - B = to_lower_case(B); + if (a.len != b.len) + return false; + for (S64 i = 0; i < a.len; i++) { + U8 A = a.str[i]; + U8 B = b.str[i]; + if (ignore_case) { + A = to_lower_case(A); + B = to_lower_case(B); + } + if (A != B) + return false; } - if (A != B) - return false; - } - return true; + return true; } CORE_Static B32 -operator==(String a, String b){ - return string_compare(a,b); +operator==(String a, String b) { + return string_compare(a, b); } CORE_Static String -string_copy(Allocator *a, String string){ - U8 *copy = allocate_array(a, U8, string.len+1); - memory_copy(copy, string.str, string.len); - copy[string.len] = 0; - return String{copy, string.len}; +string_copy(Allocator *a, String string) { + U8 *copy = allocate_array(a, U8, string.len + 1); + memory_copy(copy, string.str, string.len); + copy[string.len] = 0; + return String{copy, string.len}; } CORE_Static String string_fmtv(Allocator *a, const char *str, va_list args1) { - va_list args2; - va_copy(args2, args1); - S64 len = stbsp_vsnprintf(0, 0, str, args2); - va_end(args2); + va_list args2; + va_copy(args2, args1); + S64 len = stbsp_vsnprintf(0, 0, str, args2); + va_end(args2); - char *result = allocate_array(a, char, len + 1); - stbsp_vsnprintf(result, (int)(len + 1), str, args1); + char *result = allocate_array(a, char, len + 1); + stbsp_vsnprintf(result, (int)(len + 1), str, args1); - String res = {(U8 *)result, len}; - return res; + String res = {(U8 *)result, len}; + return res; } #define STRING_FMT(alloc, str, result) \ -va_list args1; \ -va_start(args1, str); \ -String result = string_fmtv(alloc, str, args1); \ -va_end(args1) + va_list args1; \ + va_start(args1, str); \ + String result = string_fmtv(alloc, str, args1); \ + va_end(args1) CORE_Static String string_fmt(Allocator *a, const char *str, ...) { - STRING_FMT(a, str, result); - return result; + STRING_FMT(a, str, result); + return result; } //----------------------------------------------------------------------------- // String builder //----------------------------------------------------------------------------- -struct String_Builder_Block{ - String_Builder_Block *next; - S64 cap; - S64 len; - U8 data[0]; +struct String_Builder_Block { + String_Builder_Block *next; + S64 cap; + S64 len; + U8 data[0]; }; -struct String_Builder{ - Allocator *allocator; - String_Builder_Block *first_free; - String_Builder_Block *first; - String_Builder_Block *last; - U64 di; +struct String_Builder { + Allocator *allocator; + String_Builder_Block *first_free; + String_Builder_Block *first; + String_Builder_Block *last; + U64 di; - void reset(){ - for(;;){ - auto *block = first; - first = first->next; + void reset() { + for (;;) { + auto *block = first; + first = first->next; - block->next = first_free; - first_free = block; + block->next = first_free; + first_free = block; - if(!first) break; + if (!first) break; + } + + last = 0; + assert(!last && !first); } - last = 0; - assert(!last && !first); - } - - void push_block(size_t size){ - String_Builder_Block *block = 0; - if(first_free){ - block = first_free; - first_free = first_free->next; - } else{ - block = (String_Builder_Block *)allocate_size(allocator, sizeof(String_Builder_Block) + size, false); - } - memory_zero(block, sizeof(String_Builder_Block)+1); // Also clear first byte of character data - block->cap = size; - SLL_QUEUE_ADD(first, last, block); - } - - void init(S64 size = 4096){ - assert(allocator); - push_block(size); - } - - void append_data(void *data, S64 size){ - if(first == 0){ - init(); + void push_block(size_t size) { + String_Builder_Block *block = 0; + if (first_free) { + block = first_free; + first_free = first_free->next; + } + else { + block = (String_Builder_Block *)allocate_size(allocator, sizeof(String_Builder_Block) + size, false); + } + memory_zero(block, sizeof(String_Builder_Block) + 1); // Also clear first byte of character data + block->cap = size; + SLL_QUEUE_ADD(first, last, block); } - S64 remaining_cap = last->cap - last->len; - if(size > remaining_cap){ - S64 new_block_size = max(last->cap*2, size*2); - push_block(new_block_size); + void init(S64 size = 4096) { + assert(allocator); + push_block(size); } - U8 *write_address = last->data + last->len; - last->len += size; - memory_copy(write_address, data, size); - } + void append_data(void *data, S64 size) { + if (first == 0) { + init(); + } - void addf(const char *str, ...){ - if(first == 0){ - init(); + S64 remaining_cap = last->cap - last->len; + if (size > remaining_cap) { + S64 new_block_size = max(last->cap * 2, size * 2); + push_block(new_block_size); + } + + U8 *write_address = last->data + last->len; + last->len += size; + memory_copy(write_address, data, size); } - va_list args, args2; - va_start(args, str); - retry:{ - String_Builder_Block *block = last; - S64 block_size = block->cap - block->len; - char *write_address = (char *)block->data + block->len; - va_copy(args2, args); - int written = stbsp_vsnprintf(write_address, (int)block_size, str, args2); - va_end(args2); + void addf(const char *str, ...) { + if (first == 0) { + init(); + } + va_list args, args2; + va_start(args, str); + retry : { + String_Builder_Block *block = last; + S64 block_size = block->cap - block->len; + char *write_address = (char *)block->data + block->len; - if(written > (block_size-1)){ - S64 new_block_size = max(4096, (written+1)*2); - push_block(new_block_size); - goto retry; - } - block->len += written; + va_copy(args2, args); + int written = stbsp_vsnprintf(write_address, (int)block_size, str, args2); + va_end(args2); + + if (written > (block_size - 1)) { + S64 new_block_size = max(4096, (written + 1) * 2); + push_block(new_block_size); + goto retry; + } + block->len += written; + } + va_end(args); + di++; } - va_end(args); - di++; - } }; CORE_Static String_Builder -string_builder_make(Allocator *a, S64 first_block_size = 4096){ - String_Builder sb = {a}; - sb.init(first_block_size); - return sb; +string_builder_make(Allocator *a, S64 first_block_size = 4096) { + String_Builder sb = {a}; + sb.init(first_block_size); + return sb; } // @! Make string_flatten a method -static String string_flatten(Allocator *a, String_Builder *b){ - // @Note(Krzosa): Compute size to allocate - S64 size = 1; - For_Linked_List(b->first){ - size += it->len; - } +static String string_flatten(Allocator *a, String_Builder *b) { + // @Note(Krzosa): Compute size to allocate + S64 size = 1; + For_Linked_List(b->first) { + size += it->len; + } - String result = {}; - result.str = (U8 *)allocate_size(a, size, false); + String result = {}; + result.str = (U8 *)allocate_size(a, size, false); - // @Note(Krzosa): Copy the content of each block into the string - For_Linked_List(b->first){ - memory_copy(result.str + result.len, it->data, it->len); - result.len += it->len; - } + // @Note(Krzosa): Copy the content of each block into the string + For_Linked_List(b->first) { + memory_copy(result.str + result.len, it->data, it->len); + result.len += it->len; + } - result.str[result.len] = 0; - return result; + result.str[result.len] = 0; + return result; } //----------------------------------------------------------------------------- @@ -235,241 +236,240 @@ static String string_flatten(Allocator *a, String_Builder *b){ //----------------------------------------------------------------------------- CORE_Static void string_path_normalize(String s) { - for (S64 i = 0; i < s.len; i++) { - if (s.str[i] == '\\') - s.str[i] = '/'; - } + for (S64 i = 0; i < s.len; i++) { + if (s.str[i] == '\\') + s.str[i] = '/'; + } } CORE_Static String string_make(char *str, S64 len) { - String result; - result.str = (U8 *)str; - result.len = len; - return result; + String result; + result.str = (U8 *)str; + result.len = len; + return result; } CORE_Static String string_make(U8 *str, S64 len) { - return string_make((char*)str, len); + return string_make((char *)str, len); } CORE_Static String string_chop(String string, S64 len) { - len = clamp_top(len, string.len); - String result = string_make(string.str, string.len - len); - return result; + len = clamp_top(len, string.len); + String result = string_make(string.str, string.len - len); + return result; } CORE_Static String string_skip(String string, S64 len) { - len = clamp_top(len, string.len); - S64 remain = string.len - len; - String result = string_make(string.str + len, remain); - return result; + len = clamp_top(len, string.len); + S64 remain = string.len - len; + String result = string_make(string.str + len, remain); + return result; } CORE_Static String string_get_postfix(String string, S64 len) { - len = clamp_top(len, string.len); - S64 remain_len = string.len - len; - String result = string_make(string.str + remain_len, len); - return result; + len = clamp_top(len, string.len); + S64 remain_len = string.len - len; + String result = string_make(string.str + remain_len, len); + return result; } CORE_Static String string_get_prefix(String string, S64 len) { - len = clamp_top(len, string.len); - String result = string_make(string.str, len); - return result; + len = clamp_top(len, string.len); + String result = string_make(string.str, len); + return result; } CORE_Static String string_slice(String string, S64 first_index, S64 one_past_last_index) { - assert_message(first_index < one_past_last_index, "string_slice, first_index is bigger then one_past_last_index"); - assert_message(string.len > 0, "Slicing string of length 0! Might be an error!"); - String result = string; - if (string.len > 0) { - if (one_past_last_index > first_index) { - first_index = clamp_top(first_index, string.len - 1); - one_past_last_index = clamp_top(one_past_last_index, string.len); - result.str += first_index; - result.len = one_past_last_index - first_index; + assert_message(first_index < one_past_last_index, "string_slice, first_index is bigger then one_past_last_index"); + assert_message(string.len > 0, "Slicing string of length 0! Might be an error!"); + String result = string; + if (string.len > 0) { + if (one_past_last_index > first_index) { + first_index = clamp_top(first_index, string.len - 1); + one_past_last_index = clamp_top(one_past_last_index, string.len); + result.str += first_index; + result.len = one_past_last_index - first_index; + } + else { + result.len = 0; + } } - else { - result.len = 0; - } - } - return result; + return result; } CORE_Static String string_trim(String string) { - if (string.len == 0) return string; + if (string.len == 0) return string; - - S64 whitespace_begin = 0; - for (; whitespace_begin < string.len; whitespace_begin++) { - if (!is_whitespace(string.str[whitespace_begin])) { - break; + S64 whitespace_begin = 0; + for (; whitespace_begin < string.len; whitespace_begin++) { + if (!is_whitespace(string.str[whitespace_begin])) { + break; + } } - } - S64 whitespace_end = string.len; - for (; whitespace_end != whitespace_begin; whitespace_end--) { - if (!is_whitespace(string.str[whitespace_end - 1])) { - break; + S64 whitespace_end = string.len; + for (; whitespace_end != whitespace_begin; whitespace_end--) { + if (!is_whitespace(string.str[whitespace_end - 1])) { + break; + } } - } - if (whitespace_begin == whitespace_end) { - string.len = 0; - } - else { - string = string_slice(string, whitespace_begin, whitespace_end); - } + if (whitespace_begin == whitespace_end) { + string.len = 0; + } + else { + string = string_slice(string, whitespace_begin, whitespace_end); + } - return string; + return string; } CORE_Static String string_trim_end(String string) { - S64 whitespace_end = string.len; - for (; whitespace_end != 0; whitespace_end--) { - if (!is_whitespace(string.str[whitespace_end - 1])) { - break; + S64 whitespace_end = string.len; + for (; whitespace_end != 0; whitespace_end--) { + if (!is_whitespace(string.str[whitespace_end - 1])) { + break; + } } - } - String result = string_get_prefix(string, whitespace_end); - return result; + String result = string_get_prefix(string, whitespace_end); + return result; } CORE_Static String string_to_lower_case(Allocator *arena, String s) { - String copy = string_copy(arena, s); - for (S64 i = 0; i < copy.len; i++) { - copy.str[i] = to_lower_case(copy.str[i]); - } - return copy; + String copy = string_copy(arena, s); + for (S64 i = 0; i < copy.len; i++) { + copy.str[i] = to_lower_case(copy.str[i]); + } + return copy; } CORE_Static String string_to_upper_case(Allocator *arena, String s) { - String copy = string_copy(arena, s); - for (S64 i = 0; i < copy.len; i++) { - copy.str[i] = to_upper_case(copy.str[i]); - } - return copy; + String copy = string_copy(arena, s); + for (S64 i = 0; i < copy.len; i++) { + copy.str[i] = to_upper_case(copy.str[i]); + } + return copy; } typedef U32 MatchFlag; enum { - MatchFlag_None=0, - MatchFlag_FindLast=1, - MatchFlag_IgnoreCase=2, + MatchFlag_None = 0, + MatchFlag_FindLast = 1, + MatchFlag_IgnoreCase = 2, }; CORE_Static B32 string_find(String string, String find, MatchFlag flags, S64 *index_out) { - B32 result = false; - if (flags & MatchFlag_FindLast) { - for (S64 i = string.len; i != 0; i--) { - S64 index = i - 1; - String substring = string_slice(string, index, index + find.len); - if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { - if (index_out) - *index_out = index; - result = true; - break; - } + B32 result = false; + if (flags & MatchFlag_FindLast) { + for (S64 i = string.len; i != 0; i--) { + S64 index = i - 1; + String substring = string_slice(string, index, index + find.len); + if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { + if (index_out) + *index_out = index; + result = true; + break; + } + } } - } - else { - for (S64 i = 0; i < string.len; i++) { - String substring = string_slice(string, i, i + find.len); - if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { - if (index_out) - *index_out = i; - result = true; - break; - } + else { + for (S64 i = 0; i < string.len; i++) { + String substring = string_slice(string, i, i + find.len); + if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { + if (index_out) + *index_out = i; + result = true; + break; + } + } } - } - return result; + return result; } CORE_Static String string_chop_last_slash(String s) { - String result = s; - string_find(s, "/"_s, MatchFlag_FindLast, &result.len); - return result; + String result = s; + string_find(s, "/"_s, MatchFlag_FindLast, &result.len); + return result; } CORE_Static String string_chop_last_period(String s) { - String result = s; - string_find(s, "."_s, MatchFlag_FindLast, &result.len); - return result; + String result = s; + string_find(s, "."_s, MatchFlag_FindLast, &result.len); + return result; } CORE_Static String string_skip_to_last_slash(String s) { - S64 pos; - String result = s; - if (string_find(s, "/"_s, MatchFlag_FindLast, &pos)) { - result = string_skip(result, pos + 1); - } - return result; + S64 pos; + String result = s; + if (string_find(s, "/"_s, MatchFlag_FindLast, &pos)) { + result = string_skip(result, pos + 1); + } + return result; } CORE_Static String string_skip_to_last_period(String s) { - S64 pos; - String result = s; - if (string_find(s, "."_s, MatchFlag_FindLast, &pos)) { - result = string_skip(result, pos + 1); - } - return result; + S64 pos; + String result = s; + if (string_find(s, "."_s, MatchFlag_FindLast, &pos)) { + result = string_skip(result, pos + 1); + } + return result; } CORE_Static S64 -string_len(char *string){ - S64 len = 0; - while(*string++!=0)len++; - return len; +string_len(char *string) { + S64 len = 0; + while (*string++ != 0) len++; + return len; } CORE_Static String -string_from_cstring(char *string){ - String result; - result.str = (U8 *)string; - result.len = string_len(string); - return result; +string_from_cstring(char *string) { + String result; + result.str = (U8 *)string; + result.len = string_len(string); + return result; } struct String_Replace { - String find; - String replace; + String find; + String replace; }; CORE_Static String -string_replace(Arena *scratch, Allocator *allocator, String string, Array pairs){ - Scratch_Scope _scope(scratch); - String_Builder builder = {scratch}; - for(S64 i = 0; i < string.len; i++){ - For(pairs){ - String current = string_skip(string, i); - current = string_get_prefix(current, it.find.len); - if(current == it.find){ - builder.append_data(it.replace.str, it.replace.len); - i += it.find.len; - continue; - } - } +string_replace(Arena *scratch, Allocator *allocator, String string, Array pairs) { + Scratch_Scope _scope(scratch); + String_Builder builder = {scratch}; + for (S64 i = 0; i < string.len; i++) { + For(pairs) { + String current = string_skip(string, i); + current = string_get_prefix(current, it.find.len); + if (current == it.find) { + builder.append_data(it.replace.str, it.replace.len); + i += it.find.len; + continue; + } + } - builder.append_data(string.str + i, 1); - } - return string_flatten(allocator, &builder); + builder.append_data(string.str + i, 1); + } + return string_flatten(allocator, &builder); } \ No newline at end of file diff --git a/base_unicode.cpp b/base_unicode.cpp index 13111fa..41a17d7 100644 --- a/base_unicode.cpp +++ b/base_unicode.cpp @@ -1,274 +1,274 @@ global U32 question_mark32 = '?'; global U16 question_mark16 = 0x003f; -global U8 question_mark8 = '?'; +global U8 question_mark8 = '?'; -struct String32{ - U32 *str; - S64 len; +struct String32 { + U32 *str; + S64 len; }; -struct UTF32_Result{ - U32 out_str; - S64 advance; - B32 error; +struct UTF32_Result { + U32 out_str; + S64 advance; + B32 error; }; CORE_Static UTF32_Result utf8_to_utf32(U8 *c, S64 max_advance) { - UTF32_Result result = {}; + UTF32_Result result = {}; - if ((c[0] & 0b10000000) == 0) { // Check if leftmost zero of first byte is unset - if(max_advance >= 1){ - result.out_str = c[0]; - result.advance = 1; + if ((c[0] & 0b10000000) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; } - else result.error = 1; - } - else if ((c[0] & 0b11100000) == 0b11000000) { - if ((c[1] & 0b11000000) == 0b10000000) { // Continuation byte required - if(max_advance >= 2){ - result.out_str = (U32)(c[0] & 0b00011111) << 6u | (c[1] & 0b00111111); - result.advance = 2; - } - else result.error = 2; + else if ((c[0] & 0b11100000) == 0b11000000) { + if ((c[1] & 0b11000000) == 0b10000000) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (U32)(c[0] & 0b00011111) << 6u | (c[1] & 0b00111111); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; } - else result.error = 2; - } - else if ((c[0] & 0b11110000) == 0b11100000) { - if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000) { // Two continuation bytes required - if(max_advance >= 3){ - result.out_str = (U32)(c[0] & 0b00001111) << 12u | (U32)(c[1] & 0b00111111) << 6u | (c[2] & 0b00111111); - result.advance = 3; - } - else result.error = 3; + else if ((c[0] & 0b11110000) == 0b11100000) { + if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (U32)(c[0] & 0b00001111) << 12u | (U32)(c[1] & 0b00111111) << 6u | (c[2] & 0b00111111); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; } - else result.error = 3; - } - else if ((c[0] & 0b11111000) == 0b11110000) { - if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 && (c[3] & 0b11000000) == 0b10000000) { // Three continuation bytes required - if(max_advance >= 4){ - result.out_str = (U32)(c[0] & 0b00001111) << 18u | (U32)(c[1] & 0b00111111) << 12u | (U32)(c[2] & 0b00111111) << 6u | (U32)(c[3] & 0b00111111); - result.advance = 4; - } - else result.error = 4; + else if ((c[0] & 0b11111000) == 0b11110000) { + if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 && (c[3] & 0b11000000) == 0b10000000) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (U32)(c[0] & 0b00001111) << 18u | (U32)(c[1] & 0b00111111) << 12u | (U32)(c[2] & 0b00111111) << 6u | (U32)(c[3] & 0b00111111); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; } else result.error = 4; - } - else result.error = 4; - return result; + return result; } -struct String16{ - U16 *str; - S64 len; +struct String16 { + U16 *str; + S64 len; }; -struct UTF16_Result{ - U16 out_str[2]; - S32 len; - B32 error; +struct UTF16_Result { + U16 out_str[2]; + S32 len; + B32 error; }; CORE_Static UTF16_Result -utf32_to_utf16(U32 codepoint){ - UTF16_Result result = {}; - if (codepoint < 0x10000) { - result.out_str[0] = (U16)codepoint; - result.out_str[1] = 0; - result.len = 1; - } - else if (codepoint <= 0x10FFFF) { - U32 code = (codepoint - 0x10000); - result.out_str[0] = (U16)(0xD800 | (code >> 10)); - result.out_str[1] = (U16)(0xDC00 | (code & 0x3FF)); - result.len = 2; - } - else{ - result.error = 1; - } +utf32_to_utf16(U32 codepoint) { + UTF16_Result result = {}; + if (codepoint < 0x10000) { + result.out_str[0] = (U16)codepoint; + result.out_str[1] = 0; + result.len = 1; + } + else if (codepoint <= 0x10FFFF) { + U32 code = (codepoint - 0x10000); + result.out_str[0] = (U16)(0xD800 | (code >> 10)); + result.out_str[1] = (U16)(0xDC00 | (code & 0x3FF)); + result.len = 2; + } + else { + result.error = 1; + } - return result; + return result; } -struct UTF8_Result{ - U8 out_str[4]; - S32 len; - B32 error; +struct UTF8_Result { + U8 out_str[4]; + S32 len; + B32 error; }; CORE_Static UTF8_Result utf32_to_utf8(U32 codepoint) { - UTF8_Result result = {}; - if (codepoint <= 0x7F) { - result.len = 1; - result.out_str[0] = (U8)codepoint; - } - else if (codepoint <= 0x7FF) { - result.len= 2; - result.out_str[0] = 0b11000000 | (0b00011111 & (codepoint >> 6)); - result.out_str[1] = 0b10000000 | (0b00111111 & codepoint); - } - else if (codepoint <= 0xFFFF) { // 16 bit word - result.len= 3; - result.out_str[0] = 0b11100000 | (0b00001111 & (codepoint >> 12)); // 4 bits - result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits - result.out_str[2] = 0b10000000 | (0b00111111 & codepoint); // 6 bits - } - else if (codepoint <= 0x10FFFF) { // 21 bit word - result.len= 4; - result.out_str[0] = 0b11110000 | (0b00000111 & (codepoint >> 18)); // 3 bits - result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 12)); // 6 bits - result.out_str[2] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits - result.out_str[3] = 0b10000000 | (0b00111111 & codepoint); // 6 bits - } - else{ - result.error = true; - } + UTF8_Result result = {}; + if (codepoint <= 0x7F) { + result.len = 1; + result.out_str[0] = (U8)codepoint; + } + else if (codepoint <= 0x7FF) { + result.len = 2; + result.out_str[0] = 0b11000000 | (0b00011111 & (codepoint >> 6)); + result.out_str[1] = 0b10000000 | (0b00111111 & codepoint); + } + else if (codepoint <= 0xFFFF) { // 16 bit word + result.len = 3; + result.out_str[0] = 0b11100000 | (0b00001111 & (codepoint >> 12)); // 4 bits + result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits + result.out_str[2] = 0b10000000 | (0b00111111 & codepoint); // 6 bits + } + else if (codepoint <= 0x10FFFF) { // 21 bit word + result.len = 4; + result.out_str[0] = 0b11110000 | (0b00000111 & (codepoint >> 18)); // 3 bits + result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 12)); // 6 bits + result.out_str[2] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits + result.out_str[3] = 0b10000000 | (0b00111111 & codepoint); // 6 bits + } + else { + result.error = true; + } - return result; + return result; } CORE_Static UTF32_Result utf16_to_utf32(U16 *c, S32 max_advance) { - UTF32_Result result = {}; - if(max_advance >= 1){ - result.advance = 1; - result.out_str = c[0]; - if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { - if(max_advance >= 2){ - result.out_str = 0x10000; - result.out_str += (U32)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); - result.advance = 2; - } - else result.error = 2; + UTF32_Result result = {}; + if (max_advance >= 1) { + result.advance = 1; + result.out_str = c[0]; + if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { + if (max_advance >= 2) { + result.out_str = 0x10000; + result.out_str += (U32)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); + result.advance = 2; + } + else result.error = 2; + } } - } - else result.error = 1; + else result.error = 1; - return result; + return result; } -#define unicode_error(question_mark) \ - { \ - result.str[result.len++] = question_mark; \ - break; \ - } +#define unicode_error(question_mark) \ + { \ + result.str[result.len++] = question_mark; \ + break; \ + } CORE_Static String32 -string16_to_string32(Allocator *allocator, String16 string){ - String32 result = {allocate_array(allocator, U32, string.len+1)}; - for(S64 i = 0; i < string.len;){ - UTF32_Result decode = utf16_to_utf32(string.str + i, (S32)(string.len - i)); - if(!decode.error){ - i += decode.advance; - result.str[result.len++] = decode.out_str; +string16_to_string32(Allocator *allocator, String16 string) { + String32 result = {allocate_array(allocator, U32, string.len + 1)}; + for (S64 i = 0; i < string.len;) { + UTF32_Result decode = utf16_to_utf32(string.str + i, (S32)(string.len - i)); + if (!decode.error) { + i += decode.advance; + result.str[result.len++] = decode.out_str; + } + else unicode_error(question_mark32); } - else unicode_error(question_mark32); - } - result.str[result.len] = 0; - return result; + result.str[result.len] = 0; + return result; } CORE_Static String32 -string8_to_string32(Allocator *allocator, String string){ - String32 result = {allocate_array(allocator, U32, string.len+1)}; - for(S64 i = 0; i < string.len;){ - UTF32_Result decode = utf8_to_utf32(string.str + i, string.len - i); - if(!decode.error){ - i += decode.advance; - result.str[result.len++] = decode.out_str; +string8_to_string32(Allocator *allocator, String string) { + String32 result = {allocate_array(allocator, U32, string.len + 1)}; + for (S64 i = 0; i < string.len;) { + UTF32_Result decode = utf8_to_utf32(string.str + i, string.len - i); + if (!decode.error) { + i += decode.advance; + result.str[result.len++] = decode.out_str; + } + else unicode_error(question_mark32); } - else unicode_error(question_mark32); - } - result.str[result.len] = 0; - return result; + result.str[result.len] = 0; + return result; } CORE_Static String16 -string8_to_string16(Allocator *allocator, String in){ - String16 result = {allocate_array(allocator, U16, (in.len*2)+1)}; // @Note(Krzosa): Should be more then enough space - for(S64 i = 0; i < in.len;){ - UTF32_Result decode = utf8_to_utf32(in.str + i, in.len - i); - if(!decode.error){ - i += decode.advance; - UTF16_Result encode = utf32_to_utf16(decode.out_str); - if(!encode.error){ - for(S32 j = 0; j < encode.len; j++){ - result.str[result.len++] = encode.out_str[j]; +string8_to_string16(Allocator *allocator, String in) { + String16 result = {allocate_array(allocator, U16, (in.len * 2) + 1)}; // @Note(Krzosa): Should be more then enough space + for (S64 i = 0; i < in.len;) { + UTF32_Result decode = utf8_to_utf32(in.str + i, in.len - i); + if (!decode.error) { + i += decode.advance; + UTF16_Result encode = utf32_to_utf16(decode.out_str); + if (!encode.error) { + for (S32 j = 0; j < encode.len; j++) { + result.str[result.len++] = encode.out_str[j]; + } + } + else unicode_error(question_mark16); } - } - else unicode_error(question_mark16); + else unicode_error(question_mark16); } - else unicode_error(question_mark16); - } - result.str[result.len] = 0; - return result; + result.str[result.len] = 0; + return result; } CORE_Static String -string16_to_string8(Allocator *allocator, String16 in){ - String result = {allocate_array(allocator, U8, in.len*4+1)}; - for(S64 i = 0; i < in.len;){ - UTF32_Result decode = utf16_to_utf32(in.str + i, (S32)(in.len - i)); - if(!decode.error){ - i += decode.advance; - UTF8_Result encode = utf32_to_utf8(decode.out_str); - if(!encode.error){ - for(S32 j = 0; j < encode.len; j++) - result.str[result.len++] = encode.out_str[j]; - } - else unicode_error(question_mark8); +string16_to_string8(Allocator *allocator, String16 in) { + String result = {allocate_array(allocator, U8, in.len * 4 + 1)}; + for (S64 i = 0; i < in.len;) { + UTF32_Result decode = utf16_to_utf32(in.str + i, (S32)(in.len - i)); + if (!decode.error) { + i += decode.advance; + UTF8_Result encode = utf32_to_utf8(decode.out_str); + if (!encode.error) { + for (S32 j = 0; j < encode.len; j++) + result.str[result.len++] = encode.out_str[j]; + } + else unicode_error(question_mark8); + } + else unicode_error(question_mark8); } - else unicode_error(question_mark8); - } - result.str[result.len] = 0; - return result; + result.str[result.len] = 0; + return result; } CORE_Static B32 -string_compare(String16 a, String16 b){ - if(a.len != b.len) return false; - for(S64 i = 0; i < a.len; i++){ - if(a.str[i] != b.str[i]) return false; - } - return true; +string_compare(String16 a, String16 b) { + if (a.len != b.len) return false; + for (S64 i = 0; i < a.len; i++) { + if (a.str[i] != b.str[i]) return false; + } + return true; } CORE_Static B32 -string_compare(String32 a, String32 b){ - if(a.len != b.len) return false; - for(S64 i = 0; i < a.len; i++){ - if(a.str[i] != b.str[i]) return false; - } - return true; +string_compare(String32 a, String32 b) { + if (a.len != b.len) return false; + for (S64 i = 0; i < a.len; i++) { + if (a.str[i] != b.str[i]) return false; + } + return true; } CORE_Static S64 -widechar_len(wchar_t *string){ - S64 len = 0; - while(*string++!=0)len++; - return len; +widechar_len(wchar_t *string) { + S64 len = 0; + while (*string++ != 0) len++; + return len; } CORE_Static String16 -string16_from_widechar(wchar_t *string){ - String16 result; - result.str = (U16 *)string; - result.len = widechar_len(string); - return result; +string16_from_widechar(wchar_t *string) { + String16 result; + result.str = (U16 *)string; + result.len = widechar_len(string); + return result; } CORE_Static String -string16_copy(Allocator *a, String string){ - U8 *copy = allocate_array(a, U8, string.len+1); - memory_copy(copy, string.str, string.len); - copy[string.len] = 0; - return String{copy, string.len}; +string16_copy(Allocator *a, String string) { + U8 *copy = allocate_array(a, U8, string.len + 1); + memory_copy(copy, string.str, string.len); + copy[string.len] = 0; + return String{copy, string.len}; } diff --git a/c3_big_int.cpp b/c3_big_int.cpp index 07de8b6..f4c0700 100644 --- a/c3_big_int.cpp +++ b/c3_big_int.cpp @@ -3,334 +3,292 @@ // a copy of which can be found in the LICENSE file. #define Set_BigInt_Arena(x) BigInt_Arena bigint_allocator(x) -struct BigInt_Arena{ - Allocator *old; - BigInt_Arena(Allocator *allocator){old = bigint_allocator; bigint_allocator = allocator;} - ~BigInt_Arena(){bigint_allocator = old;} +struct BigInt_Arena { + Allocator *old; + BigInt_Arena(Allocator *allocator) { + old = bigint_allocator; + bigint_allocator = allocator; + } + ~BigInt_Arena() { bigint_allocator = old; } }; CORE_Static BigInt -bigint_u64(U64 value){ - BigInt result; - bigint_init_unsigned(&result, value); - return result; +bigint_u64(U64 value) { + BigInt result; + bigint_init_unsigned(&result, value); + return result; } CORE_Static BigInt -bigint_s64(S64 value){ - BigInt result; - bigint_init_signed(&result, value); - return result; +bigint_s64(S64 value) { + BigInt result; + bigint_init_signed(&result, value); + return result; } CORE_Static BigInt -bigint_mul(const BigInt *a, const BigInt *b){ - BigInt result; - bigint_mul(&result, a, b); - return result; +bigint_mul(const BigInt *a, const BigInt *b) { + BigInt result; + bigint_mul(&result, a, b); + return result; } CORE_Static BigInt -bigint_add(const BigInt *a, const BigInt *b){ - BigInt result; - bigint_add(&result, a, b); - return result; +bigint_add(const BigInt *a, const BigInt *b) { + BigInt result; + bigint_add(&result, a, b); + return result; } CORE_Static BigInt -bigint_copy(Allocator *allocator, BigInt *src){ - BigInt dest = {}; - if (src->digit_count == 0){ - bigint_init_unsigned(&dest, 0); - return dest; - } - if(src->digit_count == 1){ - dest.digit_count = 1; - dest.digit = src->digit; +bigint_copy(Allocator *allocator, BigInt *src) { + BigInt dest = {}; + if (src->digit_count == 0) { + bigint_init_unsigned(&dest, 0); + return dest; + } + if (src->digit_count == 1) { + dest.digit_count = 1; + dest.digit = src->digit; + dest.is_negative = src->is_negative; + return dest; + } dest.is_negative = src->is_negative; - return dest; - } - dest.is_negative = src->is_negative; - dest.digit_count = src->digit_count; + dest.digit_count = src->digit_count; - dest.digits = allocate_array(allocator, uint64_t, dest.digit_count); - memory_copy(dest.digits, src->digits, sizeof(uint64_t) * dest.digit_count); - return dest; + dest.digits = allocate_array(allocator, uint64_t, dest.digit_count); + memory_copy(dest.digits, src->digits, sizeof(uint64_t) * dest.digit_count); + return dest; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -static inline uint32_t u32_min(uint32_t a, uint32_t b) -{ - return a < b ? a : b; +static inline uint32_t u32_min(uint32_t a, uint32_t b) { + return a < b ? a : b; } -static inline size_t size_max(size_t a, size_t b) -{ - return a > b ? a : b; +static inline size_t size_max(size_t a, size_t b) { + return a > b ? a : b; } -static inline unsigned unsigned_max(unsigned a, unsigned b) -{ - return a > b ? a : b; +static inline unsigned unsigned_max(unsigned a, unsigned b) { + return a > b ? a : b; } -static inline const uint64_t *bigint_ptr(const BigInt *big_int) -{ - return big_int->digit_count == 1 ? &big_int->digit : big_int->digits; +static inline const uint64_t *bigint_ptr(const BigInt *big_int) { + return big_int->digit_count == 1 ? &big_int->digit : big_int->digits; } -static void normalize(BigInt *big_int) -{ - const uint64_t *digits = bigint_ptr(big_int); - unsigned last_non_zero = UINT32_MAX; - for (unsigned i = 0; i < big_int->digit_count; i++) - { - if (digits[i] != 0) - { - last_non_zero = i; +static void normalize(BigInt *big_int) { + const uint64_t *digits = bigint_ptr(big_int); + unsigned last_non_zero = UINT32_MAX; + for (unsigned i = 0; i < big_int->digit_count; i++) { + if (digits[i] != 0) { + last_non_zero = i; + } } - } - if (last_non_zero == UINT32_MAX) - { + if (last_non_zero == UINT32_MAX) { + big_int->is_negative = false; + big_int->digit_count = 0; + return; + } + big_int->digit_count = last_non_zero + 1; + if (!last_non_zero) { + big_int->digit = digits[0]; + } +} + +static char digit_to_char(uint8_t digit, bool upper) { + if (digit <= 9) { + return (char)(digit + '0'); + } + if (digit <= 35) { + return (char)(digit + (upper ? 'A' : 'a') - 10); + } + invalid_return; +} + +static bool bit_at_index(const BigInt *big_int, size_t index) { + size_t digit_index = index / 64; + if (digit_index >= big_int->digit_count) { + return false; + } + size_t digit_bit_index = index % 64; + const uint64_t *digits = bigint_ptr(big_int); + uint64_t digit = digits[digit_index]; + return ((digit >> digit_bit_index) & 0x1U) == 0x1U; +} + +uint32_t bigint_hash(BigInt x) { + if (x.digit_count == 0) return 0; + return (uint32_t)bigint_ptr(&x)[0]; +} + +static size_t bigint_bits_needed(const BigInt *big_int) { + size_t full_bits = big_int->digit_count * 64; + size_t leading_zero_count = bigint_clz(big_int, full_bits); + size_t bits_needed = full_bits - leading_zero_count; + return bits_needed + big_int->is_negative; +} + +void bigint_init_unsigned(BigInt *big_int, uint64_t value) { + if (value == 0) { + big_int->digit_count = 0; + big_int->is_negative = false; + return; + } + big_int->digit_count = 1; + big_int->digit = value; big_int->is_negative = false; - big_int->digit_count = 0; - return; - } - big_int->digit_count = last_non_zero + 1; - if (!last_non_zero) - { - big_int->digit = digits[0]; - } } -static char digit_to_char(uint8_t digit, bool upper) -{ - if (digit <= 9) - { - return (char) (digit + '0'); - } - if (digit <= 35) - { - return (char) (digit + (upper ? 'A' : 'a') - 10); - } - invalid_return; -} - -static bool bit_at_index(const BigInt *big_int, size_t index) -{ - size_t digit_index = index / 64; - if (digit_index >= big_int->digit_count) - { - return false; - } - size_t digit_bit_index = index % 64; - const uint64_t *digits = bigint_ptr(big_int); - uint64_t digit = digits[digit_index]; - return ((digit >> digit_bit_index) & 0x1U) == 0x1U; -} - -uint32_t bigint_hash(BigInt x) -{ - if (x.digit_count == 0) return 0; - return (uint32_t) bigint_ptr(&x)[0]; -} - -static size_t bigint_bits_needed(const BigInt *big_int) -{ - size_t full_bits = big_int->digit_count * 64; - size_t leading_zero_count = bigint_clz(big_int, full_bits); - size_t bits_needed = full_bits - leading_zero_count; - return bits_needed + big_int->is_negative; -} - -void bigint_init_unsigned(BigInt *big_int, uint64_t value) -{ - if (value == 0) - { - big_int->digit_count = 0; - big_int->is_negative = false; - return; - } - big_int->digit_count = 1; - big_int->digit = value; - big_int->is_negative = false; -} - -void bigint_init_signed(BigInt *dest, int64_t value) -{ - if (value >= 0) - { - return bigint_init_unsigned(dest, (uint64_t)value); - } - dest->is_negative = true; - dest->digit_count = 1; - dest->digit = ((uint64_t) (-(value + 1))) + 1; -} - -void bigint_init_bigint(BigInt *dest, const BigInt *src) -{ - if (src->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - if (src->digit_count == 1) - { +void bigint_init_signed(BigInt *dest, int64_t value) { + if (value >= 0) { + return bigint_init_unsigned(dest, (uint64_t)value); + } + dest->is_negative = true; dest->digit_count = 1; - dest->digit = src->digit; + dest->digit = ((uint64_t)(-(value + 1))) + 1; +} + +void bigint_init_bigint(BigInt *dest, const BigInt *src) { + if (src->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + if (src->digit_count == 1) { + dest->digit_count = 1; + dest->digit = src->digit; + dest->is_negative = src->is_negative; + return; + } dest->is_negative = src->is_negative; - return; - } - dest->is_negative = src->is_negative; - dest->digit_count = src->digit_count; - dest->digits = ALLOC_DIGITS(dest->digit_count); - memory_copy(dest->digits, src->digits, sizeof(uint64_t) * dest->digit_count); + dest->digit_count = src->digit_count; + dest->digits = ALLOC_DIGITS(dest->digit_count); + memory_copy(dest->digits, src->digits, sizeof(uint64_t) * dest->digit_count); } -void bigint_negate(BigInt *dest, const BigInt *source) -{ - bigint_init_bigint(dest, source); - dest->is_negative = !dest->is_negative; - normalize(dest); -} - -static void to_twos_complement(BigInt *dest, const BigInt *source, size_t bit_count) -{ - if (bit_count == 0 || source->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - if (source->is_negative) - { - BigInt negated = { 0 }; - bigint_negate(&negated, source); - - BigInt inverted = { 0 }; - bigint_not(&inverted, &negated, bit_count, false); - - BigInt one = { 0 }; - bigint_init_unsigned(&one, 1); - - bigint_add(dest, &inverted, &one); - return; - } - - dest->is_negative = false; - const uint64_t *source_digits = bigint_ptr(source); - if (source->digit_count == 1) - { - dest->digit = source_digits[0]; - if (bit_count < 64) - { - dest->digit &= (1ULL << bit_count) - 1; - } - dest->digit_count = 1; +void bigint_negate(BigInt *dest, const BigInt *source) { + bigint_init_bigint(dest, source); + dest->is_negative = !dest->is_negative; normalize(dest); - return; - } - unsigned digits_to_copy = (unsigned int) (bit_count / 64); - unsigned leftover_bits = (unsigned int) (bit_count % 64); - dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); - if (dest->digit_count == 1 && leftover_bits == 0) - { - dest->digit = source_digits[0]; - if (dest->digit == 0) dest->digit_count = 0; - return; - } - dest->digits = (uint64_t *)malloc_arena(dest->digit_count * sizeof(uint64_t)); - for (size_t i = 0; i < digits_to_copy; i += 1) - { - uint64_t digit = (i < source->digit_count) ? source_digits[i] : 0; - dest->digits[i] = digit; - } - if (leftover_bits != 0) - { - uint64_t digit = (digits_to_copy < source->digit_count) ? source_digits[digits_to_copy] : 0; - dest->digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); - } - normalize(dest); } -size_t bigint_clz(const BigInt *big_int, size_t bit_count) -{ - if (big_int->is_negative || bit_count == 0) - { - return 0; - } - if (big_int->digit_count == 0) - { - return bit_count; - } - size_t count = 0; - for (size_t i = bit_count - 1;;) - { - if (bit_at_index(big_int, i)) - { - return count; +static void to_twos_complement(BigInt *dest, const BigInt *source, size_t bit_count) { + if (bit_count == 0 || source->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; } - count++; - if (i == 0) break; - i--; - } - return count; + if (source->is_negative) { + BigInt negated = {0}; + bigint_negate(&negated, source); + + BigInt inverted = {0}; + bigint_not(&inverted, &negated, bit_count, false); + + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + bigint_add(dest, &inverted, &one); + return; + } + + dest->is_negative = false; + const uint64_t *source_digits = bigint_ptr(source); + if (source->digit_count == 1) { + dest->digit = source_digits[0]; + if (bit_count < 64) { + dest->digit &= (1ULL << bit_count) - 1; + } + dest->digit_count = 1; + normalize(dest); + return; + } + unsigned digits_to_copy = (unsigned int)(bit_count / 64); + unsigned leftover_bits = (unsigned int)(bit_count % 64); + dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); + if (dest->digit_count == 1 && leftover_bits == 0) { + dest->digit = source_digits[0]; + if (dest->digit == 0) dest->digit_count = 0; + return; + } + dest->digits = (uint64_t *)malloc_arena(dest->digit_count * sizeof(uint64_t)); + for (size_t i = 0; i < digits_to_copy; i += 1) { + uint64_t digit = (i < source->digit_count) ? source_digits[i] : 0; + dest->digits[i] = digit; + } + if (leftover_bits != 0) { + uint64_t digit = (digits_to_copy < source->digit_count) ? source_digits[digits_to_copy] : 0; + dest->digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); + } + normalize(dest); } -bool bigint_eql(BigInt a, BigInt b) -{ - return bigint_cmp(&a, &b) == CMP_EQ; +size_t bigint_clz(const BigInt *big_int, size_t bit_count) { + if (big_int->is_negative || bit_count == 0) { + return 0; + } + if (big_int->digit_count == 0) { + return bit_count; + } + size_t count = 0; + for (size_t i = bit_count - 1;;) { + if (bit_at_index(big_int, i)) { + return count; + } + count++; + if (i == 0) break; + i--; + } + return count; } -static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) -{ - assert(!src->is_negative); - - if (bit_count == 0 || src->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - - if (is_signed && bit_at_index(src, bit_count - 1)) - { - BigInt negative_one = { 0 }; - bigint_init_signed(&negative_one, -1); - - BigInt minus_one = { 0 }; - bigint_add(&minus_one, src, &negative_one); - - BigInt inverted = { 0 }; - bigint_not(&inverted, &minus_one, bit_count, false); - - bigint_negate(dest, &inverted); - return; - - } - - bigint_init_bigint(dest, src); +bool bigint_eql(BigInt a, BigInt b) { + return bigint_cmp(&a, &b) == CMP_EQ; } +static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) { + assert(!src->is_negative); -void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative) -{ - if (digit_count == 0) return bigint_init_unsigned(dest, 0); + if (bit_count == 0 || src->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } - if (digit_count == 1) - { - dest->digit_count = 1; - dest->digit = digits[0]; + if (is_signed && bit_at_index(src, bit_count - 1)) { + BigInt negative_one = {0}; + bigint_init_signed(&negative_one, -1); + + BigInt minus_one = {0}; + bigint_add(&minus_one, src, &negative_one); + + BigInt inverted = {0}; + bigint_not(&inverted, &minus_one, bit_count, false); + + bigint_negate(dest, &inverted); + return; + } + + bigint_init_bigint(dest, src); +} + +void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative) { + if (digit_count == 0) return bigint_init_unsigned(dest, 0); + + if (digit_count == 1) { + dest->digit_count = 1; + dest->digit = digits[0]; + dest->is_negative = is_negative; + normalize(dest); + return; + } + + dest->digit_count = digit_count; dest->is_negative = is_negative; + dest->digits = ALLOC_DIGITS(digit_count); + memory_copy(dest->digits, digits, sizeof(uint64_t) * digit_count); + normalize(dest); - return; - } - - dest->digit_count = digit_count; - dest->is_negative = is_negative; - dest->digits = ALLOC_DIGITS(digit_count); - memory_copy(dest->digits, digits, sizeof(uint64_t) * digit_count); - - normalize(dest); } /* TODO @@ -368,240 +326,203 @@ void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { } */ -bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed) -{ - assert(big_int->digit_count != 1 || big_int->digit != 0); - if (bit_count == 0) - { - return bigint_cmp_zero(big_int) == CMP_EQ; - } - if (big_int->digit_count == 0) - { - return true; - } +bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed) { + assert(big_int->digit_count != 1 || big_int->digit != 0); + if (bit_count == 0) { + return bigint_cmp_zero(big_int) == CMP_EQ; + } + if (big_int->digit_count == 0) { + return true; + } - if (!is_signed) - { - size_t full_bits = big_int->digit_count * 64; - size_t leading_zero_count = bigint_clz(big_int, full_bits); - return bit_count >= full_bits - leading_zero_count; - } + if (!is_signed) { + size_t full_bits = big_int->digit_count * 64; + size_t leading_zero_count = bigint_clz(big_int, full_bits); + return bit_count >= full_bits - leading_zero_count; + } - BigInt one = { 0 }; - bigint_init_unsigned(&one, 1); + BigInt one = {0}; + bigint_init_unsigned(&one, 1); - BigInt shl_amt = { 0 }; - bigint_init_unsigned(&shl_amt, bit_count - 1); + BigInt shl_amt = {0}; + bigint_init_unsigned(&shl_amt, bit_count - 1); - BigInt max_value_plus_one = { 0 }; - bigint_shl(&max_value_plus_one, &one, &shl_amt); + BigInt max_value_plus_one = {0}; + bigint_shl(&max_value_plus_one, &one, &shl_amt); - BigInt max_value = { 0 }; - bigint_sub(&max_value, &max_value_plus_one, &one); + BigInt max_value = {0}; + bigint_sub(&max_value, &max_value_plus_one, &one); - BigInt min_value = { 0 }; - bigint_negate(&min_value, &max_value_plus_one); + BigInt min_value = {0}; + bigint_negate(&min_value, &max_value_plus_one); - CmpRes min_cmp = bigint_cmp(big_int, &min_value); - CmpRes max_cmp = bigint_cmp(big_int, &max_value); + CmpRes min_cmp = bigint_cmp(big_int, &min_value); + CmpRes max_cmp = bigint_cmp(big_int, &max_value); - - return (min_cmp == CMP_GT || min_cmp == CMP_EQ) && (max_cmp == CMP_LT || max_cmp == CMP_EQ); + return (min_cmp == CMP_GT || min_cmp == CMP_EQ) && (max_cmp == CMP_LT || max_cmp == CMP_EQ); } -void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) -{ - if (bit_count == 0) - { - return; - } - - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, big_int, bit_count); - - const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); - - size_t bits_in_last_digit = bit_count % 64; - if (bits_in_last_digit == 0) bits_in_last_digit = 64; - size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; - size_t unwritten_byte_count = 8 - bytes_in_last_digit; - - if (is_big_endian) - { - size_t last_digit_index = (bit_count - 1) / 64; - size_t digit_index = last_digit_index; - size_t buf_index = 0; - for (;;) - { - uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; - - for (size_t byte_index = 7;;) - { - uint8_t byte = (uint8_t) (x & 0xffU); - if (digit_index == last_digit_index) - { - buf[buf_index + byte_index - unwritten_byte_count] = byte; - if (byte_index == unwritten_byte_count) break; - } - else - { - buf[buf_index + byte_index] = byte; - } - - if (byte_index == 0) break; - byte_index -= 1; - x >>= 8U; - } - - if (digit_index == 0) break; - digit_index -= 1; - if (digit_index == last_digit_index) - { - buf_index += bytes_in_last_digit; - } - else - { - buf_index += 8; - } +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) { + if (bit_count == 0) { + return; } - } - else - { - size_t digit_count = (bit_count + 63) / 64; - size_t buf_index = 0; - for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) - { - uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; - for (size_t byte_index = 0; - byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit); - byte_index += 1) - { - uint8_t byte = (uint8_t) (x & 0xffU); - buf[buf_index] = byte; - buf_index += 1; - x >>= 8U; - } + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, big_int, bit_count); + + const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); + + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) bits_in_last_digit = 64; + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unwritten_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) { + size_t last_digit_index = (bit_count - 1) / 64; + size_t digit_index = last_digit_index; + size_t buf_index = 0; + for (;;) { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 7;;) { + uint8_t byte = (uint8_t)(x & 0xffU); + if (digit_index == last_digit_index) { + buf[buf_index + byte_index - unwritten_byte_count] = byte; + if (byte_index == unwritten_byte_count) break; + } + else { + buf[buf_index + byte_index] = byte; + } + + if (byte_index == 0) break; + byte_index -= 1; + x >>= 8U; + } + + if (digit_index == 0) break; + digit_index -= 1; + if (digit_index == last_digit_index) { + buf_index += bytes_in_last_digit; + } + else { + buf_index += 8; + } + } + } + else { + size_t digit_count = (bit_count + 63) / 64; + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 0; + byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit); + byte_index += 1) { + uint8_t byte = (uint8_t)(x & 0xffU); + buf[buf_index] = byte; + buf_index += 1; + x >>= 8U; + } + } } - } } -uint64_t bigint_as_unsigned(const BigInt *bigint) -{ - assert(!bigint->is_negative); - if (bigint->digit_count == 0) - { - return 0; - } - if (bigint->digit_count != 1) - { - FATAL_ERROR("Bigint exceeds u64"); - } - return bigint->digit; +uint64_t bigint_as_unsigned(const BigInt *bigint) { + assert(!bigint->is_negative); + if (bigint->digit_count == 0) { + return 0; + } + if (bigint->digit_count != 1) { + FATAL_ERROR("Bigint exceeds u64"); + } + return bigint->digit; } void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, - bool is_signed) -{ - if (bit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - - dest->digit_count = (unsigned int) ((bit_count + 63) / 64); - uint64_t *digits; - if (dest->digit_count == 1) - { - digits = &dest->digit; - } - else - { - digits = ALLOC_DIGITS(dest->digit_count); - dest->digits = digits; - } - - size_t bits_in_last_digit = bit_count % 64; - if (bits_in_last_digit == 0) - { - bits_in_last_digit = 64; - } - size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; - size_t unread_byte_count = 8 - bytes_in_last_digit; - - if (is_big_endian) - { - size_t buf_index = 0; - uint64_t digit = 0; - for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) - { - uint8_t byte = buf[buf_index]; - buf_index += 1; - digit <<= 8U; - digit |= byte; + bool is_signed) { + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; } - digits[dest->digit_count - 1] = digit; - for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) - { - digit = 0; - for (size_t byte_index = 0; byte_index < 8; byte_index += 1) - { - uint8_t byte = buf[buf_index]; - buf_index += 1; - digit <<= 8; - digit |= byte; - } - digits[dest->digit_count - 1 - digit_index] = digit; - } - } - else - { - size_t buf_index = 0; - for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) - { - uint64_t digit = 0; - size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; - for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) - { - uint64_t byte = buf[buf_index]; - buf_index += 1; - digit |= byte << (8 * byte_index); - } - digits[digit_index] = digit; + dest->digit_count = (unsigned int)((bit_count + 63) / 64); + uint64_t *digits; + if (dest->digit_count == 1) { + digits = &dest->digit; + } + else { + digits = ALLOC_DIGITS(dest->digit_count); + dest->digits = digits; } - } - if (is_signed) - { - normalize(dest); - BigInt tmp = { 0 }; - bigint_init_bigint(&tmp, dest); - from_twos_complement(dest, &tmp, bit_count, true); - } - else - { - dest->is_negative = false; - normalize(dest); - } + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) { + bits_in_last_digit = 64; + } + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unread_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) { + size_t buf_index = 0; + uint64_t digit = 0; + for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8U; + digit |= byte; + } + digits[dest->digit_count - 1] = digit; + for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) { + digit = 0; + for (size_t byte_index = 0; byte_index < 8; byte_index += 1) { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8; + digit |= byte; + } + digits[dest->digit_count - 1 - digit_index] = digit; + } + } + else { + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) { + uint64_t digit = 0; + size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; + for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) { + uint64_t byte = buf[buf_index]; + buf_index += 1; + + digit |= byte << (8 * byte_index); + } + digits[digit_index] = digit; + } + } + + if (is_signed) { + normalize(dest); + BigInt tmp = {0}; + bigint_init_bigint(&tmp, dest); + from_twos_complement(dest, &tmp, bit_count, true); + } + else { + dest->is_negative = false; + normalize(dest); + } } #if defined(_MSC_VER) -static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { *result = op1 + op2; return *result < op1 || *result < op2; } -static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { *result = op1 - op2; return *result > op1; } -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { *result = op1 * op2; if (op1 == 0 || op2 == 0) return false; @@ -612,1603 +533,1373 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) #else -static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - return __builtin_uaddll_overflow((unsigned long long) op1, (unsigned long long) op2, - (unsigned long long *) result); +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); } -static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - return __builtin_usubll_overflow((unsigned long long) op1, (unsigned long long) op2, - (unsigned long long *) result); +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); } -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) -{ - return __builtin_umulll_overflow((unsigned long long) op1, (unsigned long long) op2, - (unsigned long long *) result); +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); } #endif // @! Big int: Maybe look into those bool operations? -#pragma warning( push ) -#pragma warning( disable : 4804 ) +#pragma warning(push) +#pragma warning(disable : 4804) -void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(dest != op1); - assert(dest != op2); - if (op1->digit_count == 0) - { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative == op2->is_negative) - { - dest->is_negative = op1->is_negative; - - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->digit); - if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - normalize(dest); - return; +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(dest != op1); + assert(dest != op2); + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); } - unsigned i = 1; - uint64_t first_digit = dest->digit; - dest->digits = ALLOC_DIGITS(unsigned_max(op1->digit_count, op2->digit_count) + 1); - dest->digits[0] = first_digit; + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative == op2->is_negative) { + dest->is_negative = op1->is_negative; - for (;;) - { - bool found_digit = false; - uint64_t x = (uint64_t) overflow; - overflow = 0; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->digit); + if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + normalize(dest); + return; + } + unsigned i = 1; + uint64_t first_digit = dest->digit; + dest->digits = ALLOC_DIGITS(unsigned_max(op1->digit_count, op2->digit_count) + 1); + dest->digits[0] = first_digit; - if (i < op1->digit_count) - { - found_digit = true; - uint64_t digit = op1_digits[i]; - overflow += add_u64_overflow(x, digit, &x); - } + for (;;) { + bool found_digit = false; + uint64_t x = (uint64_t)overflow; + overflow = 0; - if (i < op2->digit_count) - { - found_digit = true; - uint64_t digit = op2_digits[i]; - overflow += add_u64_overflow(x, digit, &x); - } + if (i < op1->digit_count) { + found_digit = true; + uint64_t digit = op1_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } - dest->digits[i] = x; - i += 1; + if (i < op2->digit_count) { + found_digit = true; + uint64_t digit = op2_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } - if (!found_digit) - { - dest->digit_count = i; + dest->digits[i] = x; + i += 1; + + if (!found_digit) { + dest->digit_count = i; + normalize(dest); + return; + } + } + } + const BigInt *op_pos; + const BigInt *op_neg; + if (op1->is_negative) { + op_neg = op1; + op_pos = op2; + } + else { + op_pos = op1; + op_neg = op2; + } + + BigInt op_neg_abs = {0}; + bigint_negate(&op_neg_abs, op_neg); + const BigInt *bigger_op; + const BigInt *smaller_op; + switch (bigint_cmp(op_pos, &op_neg_abs)) { + case CMP_EQ: + bigint_init_unsigned(dest, 0); + return; + case CMP_LT: + bigger_op = &op_neg_abs; + smaller_op = op_pos; + dest->is_negative = true; + break; + case CMP_GT: + bigger_op = op_pos; + smaller_op = &op_neg_abs; + dest->is_negative = false; + break; + default: + FATAL_ERROR("UNREACHABLE"); + } + const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); + const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); + uint64_t overflow = (uint64_t)sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->digit); + if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) { + dest->digit_count = 1; normalize(dest); return; - } } - } - const BigInt *op_pos; - const BigInt *op_neg; - if (op1->is_negative) - { - op_neg = op1; - op_pos = op2; - } - else - { - op_pos = op1; - op_neg = op2; - } + uint64_t first_digit = dest->digit; + dest->digits = ALLOC_DIGITS(bigger_op->digit_count); + dest->digits[0] = first_digit; + unsigned i = 1; - BigInt op_neg_abs = { 0 }; - bigint_negate(&op_neg_abs, op_neg); - const BigInt *bigger_op; - const BigInt *smaller_op; - switch (bigint_cmp(op_pos, &op_neg_abs)) - { - case CMP_EQ: - bigint_init_unsigned(dest, 0); - return; - case CMP_LT: - bigger_op = &op_neg_abs; - smaller_op = op_pos; - dest->is_negative = true; - break; - case CMP_GT: - bigger_op = op_pos; - smaller_op = &op_neg_abs; - dest->is_negative = false; - break; - default: - FATAL_ERROR("UNREACHABLE"); - } - const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); - const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); - uint64_t overflow = (uint64_t)sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->digit); - if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) - { - dest->digit_count = 1; + for (;;) { + bool found_digit = false; + uint64_t x = bigger_op_digits[i]; + uint64_t prev_overflow = overflow; + overflow = 0; + + if (i < smaller_op->digit_count) { + found_digit = true; + uint64_t digit = smaller_op_digits[i]; + overflow += sub_u64_overflow(x, digit, &x); + } + if (sub_u64_overflow(x, prev_overflow, &x)) { + found_digit = true; + overflow += 1; + } + dest->digits[i] = x; + i += 1; + + if (!found_digit || i >= bigger_op->digit_count) { + break; + } + } + assert(overflow == 0); + dest->digit_count = i; normalize(dest); - return; - } - uint64_t first_digit = dest->digit; - dest->digits = ALLOC_DIGITS(bigger_op->digit_count); - dest->digits[0] = first_digit; - unsigned i = 1; +} +#pragma warning(pop) - for (;;) - { - bool found_digit = false; - uint64_t x = bigger_op_digits[i]; - uint64_t prev_overflow = overflow; - overflow = 0; +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_add(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} - if (i < smaller_op->digit_count) - { - found_digit = true; - uint64_t digit = smaller_op_digits[i]; - overflow += sub_u64_overflow(x, digit, &x); +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) { + BigInt op2_negated = {0}; + bigint_negate(&op2_negated, op2); + return bigint_add(dest, op1, &op2_negated); +} + +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt op2_negated = {0}; + bigint_negate(&op2_negated, op2); + return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); +} + +static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) { + uint64_t u1 = (op1 & 0xffffffff); + uint64_t v1 = (op2 & 0xffffffff); + uint64_t t = (u1 * v1); + uint64_t w3 = (t & 0xffffffff); + uint64_t k = (t >> 32); + + op1 >>= 32; + t = (op1 * v1) + k; + k = (t & 0xffffffff); + uint64_t w1 = (t >> 32); + + op2 >>= 32; + t = (u1 * op2) + k; + k = (t >> 32); + + *hi = (op1 * op2) + w1 + k; + *lo = (t << 32) + w3; +} + +static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) { + bigint_init_unsigned(dest, 0); + + BigInt bi_64; + bigint_init_unsigned(&bi_64, 64); + + const uint64_t *op_digits = bigint_ptr(op); + size_t i = op->digit_count - 1; + + while (1) { + BigInt shifted; + bigint_shl(&shifted, dest, &bi_64); + + uint64_t result_scalar; + uint64_t carry_scalar; + mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar); + + BigInt result; + bigint_init_unsigned(&result, result_scalar); + + BigInt carry; + bigint_init_unsigned(&carry, carry_scalar); + + BigInt carry_shifted; + bigint_shl(&carry_shifted, &carry, &bi_64); + + BigInt tmp; + bigint_add(&tmp, &shifted, &carry_shifted); + + bigint_add(dest, &tmp, &result); + + if (i == 0) { + break; + } + i -= 1; } - if (sub_u64_overflow(x, prev_overflow, &x)) - { - found_digit = true; - overflow += 1; +} + +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(dest != op1); + assert(dest != op2); + if (op1->digit_count == 0 || op2->digit_count == 0) { + return bigint_init_unsigned(dest, 0); } - dest->digits[i] = x; - i += 1; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); - if (!found_digit || i >= bigger_op->digit_count) - { - break; + uint64_t carry; + mul_overflow(op1_digits[0], op2_digits[0], &dest->digit, &carry); + if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) { + dest->is_negative = (op1->is_negative != op2->is_negative); + dest->digit_count = 1; + normalize(dest); + return; } - } - assert(overflow == 0); - dest->digit_count = i; - normalize(dest); -} -#pragma warning( pop ) + bigint_init_unsigned(dest, 0); -void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt unwrapped = { 0 }; - bigint_add(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} + BigInt bi_64; + bigint_init_unsigned(&bi_64, 64); -void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - BigInt op2_negated = { 0 }; - bigint_negate(&op2_negated, op2); - return bigint_add(dest, op1, &op2_negated); -} + size_t i = op2->digit_count - 1; + for (;;) { + BigInt shifted; + bigint_shl(&shifted, dest, &bi_64); -void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt op2_negated = { 0 }; - bigint_negate(&op2_negated, op2); - return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); -} + BigInt scalar_result; + mul_scalar(&scalar_result, op1, op2_digits[i]); -static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) -{ - uint64_t u1 = (op1 & 0xffffffff); - uint64_t v1 = (op2 & 0xffffffff); - uint64_t t = (u1 * v1); - uint64_t w3 = (t & 0xffffffff); - uint64_t k = (t >> 32); + bigint_add(dest, &scalar_result, &shifted); - op1 >>= 32; - t = (op1 * v1) + k; - k = (t & 0xffffffff); - uint64_t w1 = (t >> 32); - - op2 >>= 32; - t = (u1 * op2) + k; - k = (t >> 32); - - *hi = (op1 * op2) + w1 + k; - *lo = (t << 32) + w3; -} - -static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) -{ - bigint_init_unsigned(dest, 0); - - BigInt bi_64; - bigint_init_unsigned(&bi_64, 64); - - const uint64_t *op_digits = bigint_ptr(op); - size_t i = op->digit_count - 1; - - while (1) - { - BigInt shifted; - bigint_shl(&shifted, dest, &bi_64); - - uint64_t result_scalar; - uint64_t carry_scalar; - mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar); - - BigInt result; - bigint_init_unsigned(&result, result_scalar); - - BigInt carry; - bigint_init_unsigned(&carry, carry_scalar); - - BigInt carry_shifted; - bigint_shl(&carry_shifted, &carry, &bi_64); - - BigInt tmp; - bigint_add(&tmp, &shifted, &carry_shifted); - - bigint_add(dest, &tmp, &result); - - if (i == 0) - { - break; + if (i == 0) { + break; + } + i -= 1; } - i -= 1; - } -} -void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(dest != op1); - assert(dest != op2); - if (op1->digit_count == 0 || op2->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - uint64_t carry; - mul_overflow(op1_digits[0], op2_digits[0], &dest->digit, &carry); - if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) - { dest->is_negative = (op1->is_negative != op2->is_negative); - dest->digit_count = 1; normalize(dest); - return; - } - - bigint_init_unsigned(dest, 0); - - BigInt bi_64; - bigint_init_unsigned(&bi_64, 64); - - size_t i = op2->digit_count - 1; - for (;;) - { - BigInt shifted; - bigint_shl(&shifted, dest, &bi_64); - - BigInt scalar_result; - mul_scalar(&scalar_result, op1, op2_digits[i]); - - bigint_add(dest, &scalar_result, &shifted); - - if (i == 0) - { - break; - } - i -= 1; - } - - dest->is_negative = (op1->is_negative != op2->is_negative); - normalize(dest); } -void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt unwrapped = { 0 }; - bigint_mul(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_mul(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); } -unsigned count_leading_zeros(uint32_t val) -{ - if (val == 0) return 32; +unsigned count_leading_zeros(uint32_t val) { + if (val == 0) return 32; #if defined(_MSC_VER) - unsigned long Index; + unsigned long Index; _BitScanReverse(&Index, val); return Index ^ 31; #else - return __builtin_clz(val); + return __builtin_clz(val); #endif } /// Make a 64-bit integer from a high / low pair of 32-bit integers. -static inline uint64_t make_64(uint32_t hi, uint32_t lo) -{ - return (((uint64_t) hi) << 32) | ((uint64_t) lo); +static inline uint64_t make_64(uint32_t hi, uint32_t lo) { + return (((uint64_t)hi) << 32) | ((uint64_t)lo); } /// Return the high 32 bits of a 64 bit value. -static inline uint32_t hi_32(uint64_t value) -{ - return (uint32_t) (value >> 32); +static inline uint32_t hi_32(uint64_t value) { + return (uint32_t)(value >> 32); } /// Return the low 32 bits of a 64 bit value. -static inline uint32_t lo_32(uint64_t val) -{ - return (uint32_t) val; +static inline uint32_t lo_32(uint64_t val) { + return (uint32_t)val; } /// Implementation of Knuth's Algorithm D (Division of nonnegative integers) /// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The /// variables here have the same names as in the algorithm. Comments explain /// the algorithm and any deviation from it. -static void knuth_div(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t *r, unsigned m, unsigned n) -{ - assert(u && "Must provide dividend"); - assert(v && "Must provide divisor"); - assert(q && "Must provide quotient"); - assert(u != v && u != q && v != q && "Must use different memory"); - assert(n > 1 && "n must be > 1"); +static void knuth_div(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t *r, unsigned m, unsigned n) { + assert(u && "Must provide dividend"); + assert(v && "Must provide divisor"); + assert(q && "Must provide quotient"); + assert(u != v && u != q && v != q && "Must use different memory"); + assert(n > 1 && "n must be > 1"); - // b denotes the base of the number system. In our case b is 2^32. - const uint64_t b = ((uint64_t) 1) << 32; + // b denotes the base of the number system. In our case b is 2^32. + const uint64_t b = ((uint64_t)1) << 32; - // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of - // u and v by d. Note that we have taken Knuth's advice here to use a power - // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of - // 2 allows us to shift instead of multiply and it is easy to determine the - // shift amount from the leading zeros. We are basically normalizing the u - // and v so that its high bits are shifted to the top of v's range without - // overflow. Note that this can require an extra word in u so that u must - // be of length m+n+1. - unsigned shift = count_leading_zeros(v[n - 1]); - uint32_t v_carry = 0; - uint32_t u_carry = 0; - if (shift) - { - for (unsigned i = 0; i < m + n; ++i) - { - uint32_t u_tmp = u[i] >> (32 - shift); - u[i] = (u[i] << shift) | u_carry; - u_carry = u_tmp; + // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of + // u and v by d. Note that we have taken Knuth's advice here to use a power + // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of + // 2 allows us to shift instead of multiply and it is easy to determine the + // shift amount from the leading zeros. We are basically normalizing the u + // and v so that its high bits are shifted to the top of v's range without + // overflow. Note that this can require an extra word in u so that u must + // be of length m+n+1. + unsigned shift = count_leading_zeros(v[n - 1]); + uint32_t v_carry = 0; + uint32_t u_carry = 0; + if (shift) { + for (unsigned i = 0; i < m + n; ++i) { + uint32_t u_tmp = u[i] >> (32 - shift); + u[i] = (u[i] << shift) | u_carry; + u_carry = u_tmp; + } + for (unsigned i = 0; i < n; ++i) { + uint32_t v_tmp = v[i] >> (32 - shift); + v[i] = (v[i] << shift) | v_carry; + v_carry = v_tmp; + } } - for (unsigned i = 0; i < n; ++i) - { - uint32_t v_tmp = v[i] >> (32 - shift); - v[i] = (v[i] << shift) | v_carry; - v_carry = v_tmp; - } - } - u[m + n] = u_carry; + u[m + n] = u_carry; - // D2. [Initialize j.] Set j to m. This is the loop counter over the places. - int j = (int)m; - do - { - // D3. [Calculate q'.]. - // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') - // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') - // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease - // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test - // on v[n-2] determines at high speed most of the cases in which the trial - // value qp is one too large, and it eliminates all cases where qp is two - // too large. - uint64_t dividend = make_64(u[j + n], u[j + n - 1]); - uint64_t qp = dividend / v[n - 1]; - uint64_t rp = dividend % v[n - 1]; - if (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2]) - { - qp--; - rp += v[n - 1]; - if (rp < b && (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2])) - { - qp--; - } - } + // D2. [Initialize j.] Set j to m. This is the loop counter over the places. + int j = (int)m; + do { + // D3. [Calculate q'.]. + // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') + // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') + // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease + // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test + // on v[n-2] determines at high speed most of the cases in which the trial + // value qp is one too large, and it eliminates all cases where qp is two + // too large. + uint64_t dividend = make_64(u[j + n], u[j + n - 1]); + uint64_t qp = dividend / v[n - 1]; + uint64_t rp = dividend % v[n - 1]; + if (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2]) { + qp--; + rp += v[n - 1]; + if (rp < b && (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2])) { + qp--; + } + } - // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with - // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation - // consists of a simple multiplication by a one-place number, combined with - // a subtraction. - // The digits (u[j+n]...u[j]) should be kept positive; if the result of - // this step is actually negative, (u[j+n]...u[j]) should be left as the - // true value plus b**(n+1), namely as the b's complement of - // the true value, and a "borrow" to the left should be remembered. - int64_t borrow = 0; - for (unsigned i = 0; i < n; ++i) - { - uint64_t p = ((uint64_t) qp) * ((uint64_t) (v[i])); - int64_t subres = ((int64_t) (u[j + i])) - borrow - lo_32(p); - u[j + i] = lo_32((uint64_t) subres); - borrow = hi_32(p) - hi_32((uint64_t) subres); - } - bool is_neg = u[j + n] < borrow; - u[j + n] -= lo_32((uint64_t) borrow); + // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with + // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation + // consists of a simple multiplication by a one-place number, combined with + // a subtraction. + // The digits (u[j+n]...u[j]) should be kept positive; if the result of + // this step is actually negative, (u[j+n]...u[j]) should be left as the + // true value plus b**(n+1), namely as the b's complement of + // the true value, and a "borrow" to the left should be remembered. + int64_t borrow = 0; + for (unsigned i = 0; i < n; ++i) { + uint64_t p = ((uint64_t)qp) * ((uint64_t)(v[i])); + int64_t subres = ((int64_t)(u[j + i])) - borrow - lo_32(p); + u[j + i] = lo_32((uint64_t)subres); + borrow = hi_32(p) - hi_32((uint64_t)subres); + } + bool is_neg = u[j + n] < borrow; + u[j + n] -= lo_32((uint64_t)borrow); - // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was - // negative, go to step D6; otherwise go on to step D7. - q[j] = lo_32(qp); - if (is_neg) - { - // D6. [Add back]. The probability that this step is necessary is very - // small, on the order of only 2/b. Make sure that test data accounts for - // this possibility. Decrease q[j] by 1 - q[j]--; - // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). - // A carry will occur to the left of u[j+n], and it should be ignored - // since it cancels with the borrow that occurred in D4. - bool carry = false; - for (unsigned i = 0; i < n; i++) - { - uint32_t limit = u32_min(u[j + i], v[i]); - u[j + i] += v[i] + carry; - carry = u[j + i] < limit || (carry && u[j + i] == limit); - } - u[j + n] += carry; - } + // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was + // negative, go to step D6; otherwise go on to step D7. + q[j] = lo_32(qp); + if (is_neg) { + // D6. [Add back]. The probability that this step is necessary is very + // small, on the order of only 2/b. Make sure that test data accounts for + // this possibility. Decrease q[j] by 1 + q[j]--; + // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). + // A carry will occur to the left of u[j+n], and it should be ignored + // since it cancels with the borrow that occurred in D4. + bool carry = false; + for (unsigned i = 0; i < n; i++) { + uint32_t limit = u32_min(u[j + i], v[i]); + u[j + i] += v[i] + carry; + carry = u[j + i] < limit || (carry && u[j + i] == limit); + } + u[j + n] += carry; + } - // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. - } while (--j >= 0); + // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. + } while (--j >= 0); - // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired - // remainder may be obtained by dividing u[...] by d. If r is non-null we - // compute the remainder (urem uses this). - if (r) - { - // The value d is expressed by the "shift" value above since we avoided - // multiplication by d by using a shift left. So, all we have to do is - // shift right here. - if (shift) - { - uint32_t carry = 0; - for (int i = (int)n - 1; i >= 0; i--) - { - r[i] = (u[i] >> shift) | carry; - carry = u[i] << (32 - shift); - } + // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired + // remainder may be obtained by dividing u[...] by d. If r is non-null we + // compute the remainder (urem uses this). + if (r) { + // The value d is expressed by the "shift" value above since we avoided + // multiplication by d by using a shift left. So, all we have to do is + // shift right here. + if (shift) { + uint32_t carry = 0; + for (int i = (int)n - 1; i >= 0; i--) { + r[i] = (u[i] >> shift) | carry; + carry = u[i] << (32 - shift); + } + } + else { + for (int i = (int)n - 1; i >= 0; i--) { + r[i] = u[i]; + } + } } - else - { - for (int i = (int)n - 1; i >= 0; i--) - { - r[i] = u[i]; - } - } - } } -static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) -{ - CmpRes cmp = bigint_cmp(op1, op2); - if (cmp == CMP_LT) - { - if (!Quotient) - { - bigint_init_unsigned(Quotient, 0); +static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) { + CmpRes cmp = bigint_cmp(op1, op2); + if (cmp == CMP_LT) { + if (!Quotient) { + bigint_init_unsigned(Quotient, 0); + } + if (!Remainder) { + bigint_init_bigint(Remainder, op1); + } + return; } - if (!Remainder) - { - bigint_init_bigint(Remainder, op1); + if (cmp == CMP_EQ) { + if (!Quotient) { + bigint_init_unsigned(Quotient, 1); + } + if (!Remainder) { + bigint_init_unsigned(Remainder, 0); + } + return; } - return; - } - if (cmp == CMP_EQ) - { - if (!Quotient) - { - bigint_init_unsigned(Quotient, 1); - } - if (!Remainder) - { - bigint_init_unsigned(Remainder, 0); - } - return; - } - const uint64_t *lhs = bigint_ptr(op1); - const uint64_t *rhs = bigint_ptr(op2); - unsigned lhsWords = op1->digit_count; - unsigned rhsWords = op2->digit_count; + const uint64_t *lhs = bigint_ptr(op1); + const uint64_t *rhs = bigint_ptr(op2); + unsigned lhsWords = op1->digit_count; + unsigned rhsWords = op2->digit_count; - // First, compose the values into an array of 32-bit words instead of - // 64-bit words. This is a necessity of both the "short division" algorithm - // and the Knuth "classical algorithm" which requires there to be native - // operations for +, -, and * on an m bit value with an m*2 bit result. We - // can't use 64-bit operands here because we don't have native results of - // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't - // work on large-endian machines. - unsigned n = rhsWords * 2; - unsigned m = (lhsWords * 2) - n; + // First, compose the values into an array of 32-bit words instead of + // 64-bit words. This is a necessity of both the "short division" algorithm + // and the Knuth "classical algorithm" which requires there to be native + // operations for +, -, and * on an m bit value with an m*2 bit result. We + // can't use 64-bit operands here because we don't have native results of + // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't + // work on large-endian machines. + unsigned n = rhsWords * 2; + unsigned m = (lhsWords * 2) - n; - // Allocate space for the temporary values we need either on the stack, if - // it will fit, or on the heap if it won't. - uint32_t space[128]; - uint32_t *U = NULL; - uint32_t *V = NULL; - uint32_t *Q = NULL; - uint32_t *R = NULL; - if ((Remainder ? 4 : 3) * n + 2 * m + 1 <= 128) - { - U = &space[0]; - V = &space[m + n + 1]; - Q = &space[(m + n + 1) + n]; - if (Remainder) - { - R = &space[(m + n + 1) + n + (m + n)]; + // Allocate space for the temporary values we need either on the stack, if + // it will fit, or on the heap if it won't. + uint32_t space[128]; + uint32_t *U = NULL; + uint32_t *V = NULL; + uint32_t *Q = NULL; + uint32_t *R = NULL; + if ((Remainder ? 4 : 3) * n + 2 * m + 1 <= 128) { + U = &space[0]; + V = &space[m + n + 1]; + Q = &space[(m + n + 1) + n]; + if (Remainder) { + R = &space[(m + n + 1) + n + (m + n)]; + } } - } - else - { - U = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n + 1)); - V = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); - Q = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n)); - if (Remainder) - { - R = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + else { + U = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n + 1)); + V = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + Q = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n)); + if (Remainder) { + R = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + } } - } - // Initialize the dividend - memset(U, 0, (m + n + 1) * sizeof(uint32_t)); - for (unsigned i = 0; i < lhsWords; ++i) - { - uint64_t tmp = lhs[i]; - U[i * 2] = lo_32(tmp); - U[i * 2 + 1] = hi_32(tmp); - } - U[m + n] = 0; // this extra word is for "spill" in the Knuth algorithm. - - // Initialize the divisor - memset(V, 0, (n) * sizeof(uint32_t)); - for (unsigned i = 0; i < rhsWords; ++i) - { - uint64_t tmp = rhs[i]; - V[i * 2] = lo_32(tmp); - V[i * 2 + 1] = hi_32(tmp); - } - - // initialize the quotient and remainder - memset(Q, 0, (m + n) * sizeof(uint32_t)); - if (Remainder) memset(R, 0, n * sizeof(uint32_t)); - - // Now, adjust m and n for the Knuth division. n is the number of words in - // the divisor. m is the number of words by which the dividend exceeds the - // divisor (i.e. m+n is the length of the dividend). These sizes must not - // contain any zero words or the Knuth algorithm fails. - for (unsigned i = n; i > 0 && V[i - 1] == 0; i--) - { - n--; - m++; - } - for (unsigned i = m + n; i > 0 && U[i - 1] == 0; i--) - { - m--; - } - - // If we're left with only a single word for the divisor, Knuth doesn't work - // so we implement the short division algorithm here. This is much simpler - // and faster because we are certain that we can divide a 64-bit quantity - // by a 32-bit quantity at hardware speed and short division is simply a - // series of such operations. This is just like doing short division but we - // are using base 2^32 instead of base 10. - assert(n != 0 && "Divide by zero?"); - if (n == 1) - { - uint32_t divisor = V[0]; - uint32_t rem = 0; - for (int i = (int)m; i >= 0; i--) - { - uint64_t partial_dividend = make_64(rem, U[i]); - if (partial_dividend == 0) - { - Q[i] = 0; - rem = 0; - } - else if (partial_dividend < divisor) - { - Q[i] = 0; - rem = lo_32(partial_dividend); - } - else if (partial_dividend == divisor) - { - Q[i] = 1; - rem = 0; - } - else - { - Q[i] = lo_32(partial_dividend / divisor); - rem = lo_32(partial_dividend - (Q[i] * divisor)); - } + // Initialize the dividend + memset(U, 0, (m + n + 1) * sizeof(uint32_t)); + for (unsigned i = 0; i < lhsWords; ++i) { + uint64_t tmp = lhs[i]; + U[i * 2] = lo_32(tmp); + U[i * 2 + 1] = hi_32(tmp); } - if (R) - { - R[0] = rem; - } - } - else - { - // Now we're ready to invoke the Knuth classical divide algorithm. In this - // case n > 1. - knuth_div(U, V, Q, R, m, n); - } + U[m + n] = 0; // this extra word is for "spill" in the Knuth algorithm. - // If the caller wants the quotient - if (Quotient) - { - Quotient->is_negative = false; - Quotient->digit_count = lhsWords; - if (lhsWords == 1) - { - Quotient->digit = make_64(Q[1], Q[0]); + // Initialize the divisor + memset(V, 0, (n) * sizeof(uint32_t)); + for (unsigned i = 0; i < rhsWords; ++i) { + uint64_t tmp = rhs[i]; + V[i * 2] = lo_32(tmp); + V[i * 2 + 1] = hi_32(tmp); } - else - { - Quotient->digits = ALLOC_DIGITS(lhsWords); - for (size_t i = 0; i < lhsWords; i += 1) - { - Quotient->digits[i] = make_64(Q[i * 2 + 1], Q[i * 2]); - } - } - } - // If the caller wants the remainder - if (Remainder) - { - Remainder->is_negative = false; - Remainder->digit_count = rhsWords; - if (rhsWords == 1) - { - Remainder->digit = make_64(R[1], R[0]); + // initialize the quotient and remainder + memset(Q, 0, (m + n) * sizeof(uint32_t)); + if (Remainder) memset(R, 0, n * sizeof(uint32_t)); + + // Now, adjust m and n for the Knuth division. n is the number of words in + // the divisor. m is the number of words by which the dividend exceeds the + // divisor (i.e. m+n is the length of the dividend). These sizes must not + // contain any zero words or the Knuth algorithm fails. + for (unsigned i = n; i > 0 && V[i - 1] == 0; i--) { + n--; + m++; } - else - { - Remainder->digits = ALLOC_DIGITS(rhsWords); - for (size_t i = 0; i < rhsWords; i += 1) - { - Remainder->digits[i] = make_64(R[i * 2 + 1], R[i * 2]); - } + for (unsigned i = m + n; i > 0 && U[i - 1] == 0; i--) { + m--; + } + + // If we're left with only a single word for the divisor, Knuth doesn't work + // so we implement the short division algorithm here. This is much simpler + // and faster because we are certain that we can divide a 64-bit quantity + // by a 32-bit quantity at hardware speed and short division is simply a + // series of such operations. This is just like doing short division but we + // are using base 2^32 instead of base 10. + assert(n != 0 && "Divide by zero?"); + if (n == 1) { + uint32_t divisor = V[0]; + uint32_t rem = 0; + for (int i = (int)m; i >= 0; i--) { + uint64_t partial_dividend = make_64(rem, U[i]); + if (partial_dividend == 0) { + Q[i] = 0; + rem = 0; + } + else if (partial_dividend < divisor) { + Q[i] = 0; + rem = lo_32(partial_dividend); + } + else if (partial_dividend == divisor) { + Q[i] = 1; + rem = 0; + } + else { + Q[i] = lo_32(partial_dividend / divisor); + rem = lo_32(partial_dividend - (Q[i] * divisor)); + } + } + if (R) { + R[0] = rem; + } + } + else { + // Now we're ready to invoke the Knuth classical divide algorithm. In this + // case n > 1. + knuth_div(U, V, Q, R, m, n); + } + + // If the caller wants the quotient + if (Quotient) { + Quotient->is_negative = false; + Quotient->digit_count = lhsWords; + if (lhsWords == 1) { + Quotient->digit = make_64(Q[1], Q[0]); + } + else { + Quotient->digits = ALLOC_DIGITS(lhsWords); + for (size_t i = 0; i < lhsWords; i += 1) { + Quotient->digits[i] = make_64(Q[i * 2 + 1], Q[i * 2]); + } + } + } + + // If the caller wants the remainder + if (Remainder) { + Remainder->is_negative = false; + Remainder->digit_count = rhsWords; + if (rhsWords == 1) { + Remainder->digit = make_64(R[1], R[0]); + } + else { + Remainder->digits = ALLOC_DIGITS(rhsWords); + for (size_t i = 0; i < rhsWords; i += 1) { + Remainder->digits[i] = make_64(R[i * 2 + 1], R[i * 2]); + } + } } - } } +void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit = op1_digits[0] / op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); + return; + } + if (op2->digit_count == 1 && op2_digits[0] == 1) { + // X / 1 == X + bigint_init_bigint(dest, op1); + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); + return; + } -void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(op2->digit_count != 0); // division by zero - if (op1->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit = op1_digits[0] / op2_digits[0]; - dest->digit_count = 1; + const BigInt *op1_positive; + BigInt op1_positive_data; + if (op1->is_negative) { + bigint_negate(&op1_positive_data, op1); + op1_positive = &op1_positive_data; + } + else { + op1_positive = op1; + } + + const BigInt *op2_positive; + BigInt op2_positive_data; + if (op2->is_negative) { + bigint_negate(&op2_positive_data, op2); + op2_positive = &op2_positive_data; + } + else { + op2_positive = op2; + } + + bigint_unsigned_division(op1_positive, op2_positive, dest, NULL); dest->is_negative = op1->is_negative != op2->is_negative; normalize(dest); - return; - } - if (op2->digit_count == 1 && op2_digits[0] == 1) - { - // X / 1 == X - bigint_init_bigint(dest, op1); - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); - return; - } - - const BigInt *op1_positive; - BigInt op1_positive_data; - if (op1->is_negative) - { - bigint_negate(&op1_positive_data, op1); - op1_positive = &op1_positive_data; - } - else - { - op1_positive = op1; - } - - const BigInt *op2_positive; - BigInt op2_positive_data; - if (op2->is_negative) - { - bigint_negate(&op2_positive_data, op2); - op2_positive = &op2_positive_data; - } - else - { - op2_positive = op2; - } - - bigint_unsigned_division(op1_positive, op2_positive, dest, NULL); - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); } -void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->is_negative != op2->is_negative) - { - bigint_div_trunc(dest, op1, op2); - BigInt mult_again = { 0 }; - bigint_mul(&mult_again, dest, op2); - mult_again.is_negative = op1->is_negative; - if (bigint_cmp(&mult_again, op1) != CMP_EQ) - { - BigInt tmp = { 0 }; - bigint_init_bigint(&tmp, dest); - BigInt neg_one = { 0 }; - bigint_init_signed(&neg_one, -1); - bigint_add(dest, &tmp, &neg_one); +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative != op2->is_negative) { + bigint_div_trunc(dest, op1, op2); + BigInt mult_again = {0}; + bigint_mul(&mult_again, dest, op2); + mult_again.is_negative = op1->is_negative; + if (bigint_cmp(&mult_again, op1) != CMP_EQ) { + BigInt tmp = {0}; + bigint_init_bigint(&tmp, dest); + BigInt neg_one = {0}; + bigint_init_signed(&neg_one, -1); + bigint_add(dest, &tmp, &neg_one); + } + normalize(dest); + } + else { + bigint_div_trunc(dest, op1, op2); } - normalize(dest); - } - else - { - bigint_div_trunc(dest, op1, op2); - } } -void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(op2->digit_count != 0); // division by zero - if (op1->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit = op1_digits[0] % op2_digits[0]; - dest->digit_count = 1; + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit = op1_digits[0] % op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) { + // special case this divisor + bigint_init_unsigned(dest, op1_digits[0]); + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + + if (op2->digit_count == 1 && op2_digits[0] == 1) { + // X % 1 == 0 + bigint_init_unsigned(dest, 0); + return; + } + + const BigInt *op1_positive; + BigInt op1_positive_data; + if (op1->is_negative) { + bigint_negate(&op1_positive_data, op1); + op1_positive = &op1_positive_data; + } + else { + op1_positive = op1; + } + + const BigInt *op2_positive; + BigInt op2_positive_data; + if (op2->is_negative) { + bigint_negate(&op2_positive_data, op2); + op2_positive = &op2_positive_data; + } + else { + op2_positive = op2; + } + + bigint_unsigned_division(op1_positive, op2_positive, NULL, dest); dest->is_negative = op1->is_negative; normalize(dest); - return; - } - if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) - { - // special case this divisor - bigint_init_unsigned(dest, op1_digits[0]); - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - - if (op2->digit_count == 1 && op2_digits[0] == 1) - { - // X % 1 == 0 - bigint_init_unsigned(dest, 0); - return; - } - - const BigInt *op1_positive; - BigInt op1_positive_data; - if (op1->is_negative) - { - bigint_negate(&op1_positive_data, op1); - op1_positive = &op1_positive_data; - } - else - { - op1_positive = op1; - } - - const BigInt *op2_positive; - BigInt op2_positive_data; - if (op2->is_negative) - { - bigint_negate(&op2_positive_data, op2); - op2_positive = &op2_positive_data; - } - else - { - op2_positive = op2; - } - - bigint_unsigned_division(op1_positive, op2_positive, NULL, dest); - dest->is_negative = op1->is_negative; - normalize(dest); } -void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->is_negative) - { - BigInt first_rem; - bigint_rem(&first_rem, op1, op2); - first_rem.is_negative = !op2->is_negative; - BigInt op2_minus_rem; - bigint_add(&op2_minus_rem, op2, &first_rem); - bigint_rem(dest, &op2_minus_rem, op2); - dest->is_negative = false; - } - else - { - bigint_rem(dest, op1, op2); - dest->is_negative = false; - } +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative) { + BigInt first_rem; + bigint_rem(&first_rem, op1, op2); + first_rem.is_negative = !op2->is_negative; + BigInt op2_minus_rem; + bigint_add(&op2_minus_rem, op2, &first_rem); + bigint_rem(dest, &op2_minus_rem, op2); + dest->is_negative = false; + } + else { + bigint_rem(dest, op1, op2); + dest->is_negative = false; + } } -void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0) - { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative || op2->is_negative) - { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = { 0 }; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = { 0 }; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = { 0 }; - bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else - { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = op1_digits[0] | op2_digits[0]; - normalize(dest); - return; +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); } - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - for (size_t i = 0; i < dest->digit_count; i += 1) - { - uint64_t digit = 0; - if (i < op1->digit_count) - { - digit |= op1_digits[i]; - } - if (i < op2->digit_count) - { - digit |= op2_digits[i]; - } - dest->digits[i] = digit; + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative || op2->is_negative) { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->digit = op1_digits[0] | op2_digits[0]; + normalize(dest); + return; + } + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + for (size_t i = 0; i < dest->digit_count; i += 1) { + uint64_t digit = 0; + if (i < op1->digit_count) { + digit |= op1_digits[i]; + } + if (i < op2->digit_count) { + digit |= op2_digits[i]; + } + dest->digits[i] = digit; + } + normalize(dest); } - normalize(dest); - } } -void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0 || op2->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - if (op1->is_negative || op2->is_negative) - { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = { 0 }; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = { 0 }; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = { 0 }; - bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else - { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = op1_digits[0] & op2_digits[0]; - normalize(dest); - return; +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0 || op2->digit_count == 0) { + return bigint_init_unsigned(dest, 0); } + if (op1->is_negative || op2->is_negative) { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) - { - dest->digits[i] = op1_digits[i] & op2_digits[i]; + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); } - for (; i < dest->digit_count; i += 1) - { - dest->digits[i] = 0; + else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->digit = op1_digits[0] & op2_digits[0]; + normalize(dest); + return; + } + + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->digits[i] = op1_digits[i] & op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->digits[i] = 0; + } + normalize(dest); } - normalize(dest); - } } -void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - if (op1->digit_count == 0) - { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative || op2->is_negative) - { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = { 0 }; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = { 0 }; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = { 0 }; - bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else - { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - assert(op1->digit_count > 0 && op2->digit_count > 0); - if (op1->digit_count == 1 && op2->digit_count == 1) - { - dest->digit_count = 1; - dest->digit = op1_digits[0] ^ op2_digits[0]; - normalize(dest); - return; +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); } - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) - { - dest->digits[i] = op1_digits[i] ^ op2_digits[i]; + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); } - for (; i < dest->digit_count; i += 1) - { - if (i < op1->digit_count) - { - dest->digits[i] = op1_digits[i]; - } - else if (i < op2->digit_count) - { - dest->digits[i] = op2_digits[i]; - } - else - { - FATAL_ERROR("Unreachable"); - } + if (op1->is_negative || op2->is_negative) { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + assert(op1->digit_count > 0 && op2->digit_count > 0); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->digit = op1_digits[0] ^ op2_digits[0]; + normalize(dest); + return; + } + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->digits[i] = op1_digits[i] ^ op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + if (i < op1->digit_count) { + dest->digits[i] = op1_digits[i]; + } + else if (i < op2->digit_count) { + dest->digits[i] = op2_digits[i]; + } + else { + FATAL_ERROR("Unreachable"); + } + } + normalize(dest); } - normalize(dest); - } } void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(!op2->is_negative); +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); - if (op2->digit_count != 1) - { - FATAL_ERROR("Unsupported: shift left by amount greater than 64 bit integer"); - } - bigint_shl_int(dest, op1, bigint_as_unsigned(op2)); + if (op2->digit_count != 1) { + FATAL_ERROR("Unsupported: shift left by amount greater than 64 bit integer"); + } + bigint_shl_int(dest, op1, bigint_as_unsigned(op2)); } -void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) -{ - if (shift == 0) - { - bigint_init_bigint(dest, op1); - return; - } - - if (op1->digit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } - - const uint64_t *op1_digits = bigint_ptr(op1); - - if (op1->digit_count == 1 && shift < 64) - { - dest->digit = op1_digits[0] << shift; - if (dest->digit > op1_digits[0]) - { - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - return; +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) { + if (shift == 0) { + bigint_init_bigint(dest, op1); + return; } - } - uint64_t digit_shift_count = shift / 64; - uint64_t leftover_shift_count = shift % 64; - - dest->digits = ALLOC_DIGITS(op1->digit_count + digit_shift_count + 1); - dest->digit_count = digit_shift_count; - uint64_t carry = 0; - for (size_t i = 0; i < op1->digit_count; i += 1) - { - uint64_t digit = op1_digits[i]; - dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); - dest->digit_count++; - if (leftover_shift_count > 0) - { - carry = digit >> (64 - leftover_shift_count); + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; } - else - { - carry = 0; + + const uint64_t *op1_digits = bigint_ptr(op1); + + if (op1->digit_count == 1 && shift < 64) { + dest->digit = op1_digits[0] << shift; + if (dest->digit > op1_digits[0]) { + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + return; + } } - } - dest->digits[dest->digit_count] = carry; - dest->digit_count += 1; - dest->is_negative = op1->is_negative; - normalize(dest); -} -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) -{ - BigInt unwrapped = { 0 }; - bigint_shl(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} + uint64_t digit_shift_count = shift / 64; + uint64_t leftover_shift_count = shift % 64; -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) -{ - assert(!op2->is_negative); - - if (op1->digit_count == 0) - { - return bigint_init_unsigned(dest, 0); - } - - if (op2->digit_count == 0) - { - return bigint_init_bigint(dest, op1); - } - - if (op2->digit_count != 1) - { - FATAL_ERROR("Unsupported: shift right by amount greater than 64 bit integer"); - } - - const uint64_t *op1_digits = bigint_ptr(op1); - uint64_t shift_amt = bigint_as_unsigned(op2); - - if (op1->digit_count == 1) - { - dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; - dest->digit_count = 1; + dest->digits = ALLOC_DIGITS(op1->digit_count + digit_shift_count + 1); + dest->digit_count = digit_shift_count; + uint64_t carry = 0; + for (size_t i = 0; i < op1->digit_count; i += 1) { + uint64_t digit = op1_digits[i]; + dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); + dest->digit_count++; + if (leftover_shift_count > 0) { + carry = digit >> (64 - leftover_shift_count); + } + else { + carry = 0; + } + } + dest->digits[dest->digit_count] = carry; + dest->digit_count += 1; dest->is_negative = op1->is_negative; normalize(dest); - return; - } - - uint64_t digit_shift_count = shift_amt / 64; - uint64_t leftover_shift_count = shift_amt % 64; - - if (digit_shift_count >= op1->digit_count) - { - return bigint_init_unsigned(dest, 0); - } - - dest->digit_count = op1->digit_count - digit_shift_count; - uint64_t *digits; - if (dest->digit_count == 1) - { - digits = &dest->digit; - } - else - { - digits = ALLOC_DIGITS(dest->digit_count); - dest->digits = digits; - } - - uint64_t carry = 0; - for (size_t op_digit_index = op1->digit_count - 1;;) - { - uint64_t digit = op1_digits[op_digit_index]; - size_t dest_digit_index = op_digit_index - digit_shift_count; - digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = digit << (64 - leftover_shift_count); - - if (dest_digit_index == 0) break; - op_digit_index -= 1; - } - dest->is_negative = op1->is_negative; - normalize(dest); } - -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) -{ - BigInt zero; - bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_shl(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); } -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) -{ - if (bit_count == 0) - { - bigint_init_unsigned(dest, 0); - return; - } +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); - if (is_signed) - { - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, op, bit_count); - - BigInt inverted = { 0 }; - bigint_not(&inverted, &twos_comp, bit_count, false); - - from_twos_complement(dest, &inverted, bit_count, true); - return; - } - - assert(!op->is_negative); - - dest->is_negative = false; - const uint64_t *op_digits = bigint_ptr(op); - if (bit_count <= 64) - { - dest->digit_count = 1; - if (op->digit_count == 0) - { - if (bit_count == 64) - { - dest->digit = UINT64_MAX; - } - else - { - dest->digit = (1ULL << bit_count) - 1; - } + if (op1->digit_count == 0) { + return bigint_init_unsigned(dest, 0); } - else if (op->digit_count == 1) - { - dest->digit = ~op_digits[0]; - if (bit_count != 64) - { + + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + + if (op2->digit_count != 1) { + FATAL_ERROR("Unsupported: shift right by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1) { + dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + + uint64_t digit_shift_count = shift_amt / 64; + uint64_t leftover_shift_count = shift_amt % 64; + + if (digit_shift_count >= op1->digit_count) { + return bigint_init_unsigned(dest, 0); + } + + dest->digit_count = op1->digit_count - digit_shift_count; + uint64_t *digits; + if (dest->digit_count == 1) { + digits = &dest->digit; + } + else { + digits = ALLOC_DIGITS(dest->digit_count); + dest->digits = digits; + } + + uint64_t carry = 0; + for (size_t op_digit_index = op1->digit_count - 1;;) { + uint64_t digit = op1_digits[op_digit_index]; + size_t dest_digit_index = op_digit_index - digit_shift_count; + digits[dest_digit_index] = carry | (digit >> leftover_shift_count); + carry = digit << (64 - leftover_shift_count); + + if (dest_digit_index == 0) break; + op_digit_index -= 1; + } + dest->is_negative = op1->is_negative; + normalize(dest); +} + +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { + BigInt zero; + bigint_init_unsigned(&zero, 0); + bigint_sub_wrap(dest, &zero, op, bit_count, true); +} + +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed) { + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, op, bit_count); + + BigInt inverted = {0}; + bigint_not(&inverted, &twos_comp, bit_count, false); + + from_twos_complement(dest, &inverted, bit_count, true); + return; + } + + assert(!op->is_negative); + + dest->is_negative = false; + const uint64_t *op_digits = bigint_ptr(op); + if (bit_count <= 64) { + dest->digit_count = 1; + if (op->digit_count == 0) { + if (bit_count == 64) { + dest->digit = UINT64_MAX; + } + else { + dest->digit = (1ULL << bit_count) - 1; + } + } + else if (op->digit_count == 1) { + dest->digit = ~op_digits[0]; + if (bit_count != 64) { + uint64_t + mask = (1ULL << bit_count) - 1; + dest->digit &= mask; + } + } + normalize(dest); + return; + } + dest->digit_count = (unsigned int)((bit_count + 63) / 64); + assert(dest->digit_count >= op->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + size_t i = 0; + for (; i < op->digit_count; i += 1) { + dest->digits[i] = ~op_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->digits[i] = 0xffffffffffffffffULL; + } + size_t digit_index = dest->digit_count - 1; + size_t digit_bit_index = bit_count % 64; + if (digit_bit_index != 0) { uint64_t - mask = (1ULL << bit_count) - 1; - dest->digit &= mask; - } + mask = (1ULL << digit_bit_index) - 1; + dest->digits[digit_index] &= mask; } normalize(dest); - return; - } - dest->digit_count = (unsigned int) ((bit_count + 63) / 64); - assert(dest->digit_count >= op->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - size_t i = 0; - for (; i < op->digit_count; i += 1) - { - dest->digits[i] = ~op_digits[i]; - } - for (; i < dest->digit_count; i += 1) - { - dest->digits[i] = 0xffffffffffffffffULL; - } - size_t digit_index = dest->digit_count - 1; - size_t digit_bit_index = bit_count % 64; - if (digit_bit_index != 0) - { - uint64_t - mask = (1ULL << digit_bit_index) - 1; - dest->digits[digit_index] &= mask; - } - normalize(dest); } -void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed) -{ - BigInt twos_comp; - to_twos_complement(&twos_comp, op, bit_count); - from_twos_complement(dst, &twos_comp, bit_count, is_signed); +void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed) { + BigInt twos_comp; + to_twos_complement(&twos_comp, op, bit_count); + from_twos_complement(dst, &twos_comp, bit_count, is_signed); } -CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2) -{ - if (op1->is_negative && !op2->is_negative) return CMP_LT; - if (!op1->is_negative && op2->is_negative) return CMP_GT; - if (op1->digit_count > op2->digit_count) return op1->is_negative ? CMP_LT : CMP_GT; - if (op2->digit_count > op1->digit_count) return op1->is_negative ? CMP_GT : CMP_LT; - if (op1->digit_count == 0) return CMP_EQ; +CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2) { + if (op1->is_negative && !op2->is_negative) return CMP_LT; + if (!op1->is_negative && op2->is_negative) return CMP_GT; + if (op1->digit_count > op2->digit_count) return op1->is_negative ? CMP_LT : CMP_GT; + if (op2->digit_count > op1->digit_count) return op1->is_negative ? CMP_GT : CMP_LT; + if (op1->digit_count == 0) return CMP_EQ; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - for (unsigned i = op1->digit_count - 1;; i--) - { - uint64_t op1_digit = op1_digits[i]; - uint64_t op2_digit = op2_digits[i]; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + for (unsigned i = op1->digit_count - 1;; i--) { + uint64_t op1_digit = op1_digits[i]; + uint64_t op2_digit = op2_digits[i]; - if (op1_digit > op2_digit) - { - return op1->is_negative ? CMP_LT : CMP_GT; + if (op1_digit > op2_digit) { + return op1->is_negative ? CMP_LT : CMP_GT; + } + if (op1_digit < op2_digit) { + return op1->is_negative ? CMP_GT : CMP_LT; + } + if (i == 0) { + return CMP_EQ; + } } - if (op1_digit < op2_digit) - { - return op1->is_negative ? CMP_GT : CMP_LT; - } - if (i == 0) - { - return CMP_EQ; - } - } } -void bigint_print(BigInt *bigint, uint64_t base) -{ - if (bigint->digit_count == 0) - { - printf("0"); - return; - } - if (bigint->is_negative) - { - printf("-"); - } - if (bigint->digit_count == 1 && base == 10) - { - printf("%" PRIu64, bigint->digit); - return; - } - size_t len = bigint->digit_count * 64; - char *start = (char *)malloc_arena(len); - char *buf = start; - - BigInt digit_bi = { 0 }; - BigInt a1 = { 0 }; - BigInt a2 = { 0 }; - - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, bigint); - - BigInt base_bi = { 0 }; - bigint_init_unsigned(&base_bi, base); - - for (;;) - { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); - *(buf++) = digit_to_char(digit, false); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; +void bigint_print(BigInt *bigint, uint64_t base) { + if (bigint->digit_count == 0) { + printf("0"); + return; } - if (bigint_cmp_zero(a) == CMP_EQ) - { - break; + if (bigint->is_negative) { + printf("-"); } - } + if (bigint->digit_count == 1 && base == 10) { + printf("%" PRIu64, bigint->digit); + return; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)malloc_arena(len); + char *buf = start; - // reverse + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; - for (char *ptr = buf - 1; ptr >= start; ptr--) - { - printf("%c", *ptr); - } + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) { + break; + } + } + + // reverse + + for (char *ptr = buf - 1; ptr >= start; ptr--) { + printf("%c", *ptr); + } } -const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base) -{ - Set_BigInt_Arena(allocator); - if (bigint->digit_count == 0) - { - return "0"; - } - if (bigint->digit_count == 1 && base == 10) - { - char *res = NULL; - if (bigint->is_negative) - { - String string = string_fmt(allocator, "-%" PRIu64, bigint->digit); - return (const char *)string.str; +const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base) { + Set_BigInt_Arena(allocator); + if (bigint->digit_count == 0) { + return "0"; } - else - { - String string = string_fmt(allocator, "%" PRIu64, bigint->digit); - return (const char *)string.str; + if (bigint->digit_count == 1 && base == 10) { + char *res = NULL; + if (bigint->is_negative) { + String string = string_fmt(allocator, "-%" PRIu64, bigint->digit); + return (const char *)string.str; + } + else { + String string = string_fmt(allocator, "%" PRIu64, bigint->digit); + return (const char *)string.str; + } + return res; } - return res; - } - size_t len = bigint->digit_count * 64; - char *start = (char *)allocate_size(allocator, len); - char *buf = start; + size_t len = bigint->digit_count * 64; + char *start = (char *)allocate_size(allocator, len); + char *buf = start; - BigInt digit_bi = { 0 }; - BigInt a1 = { 0 }; - BigInt a2 = { 0 }; + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, bigint); + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); - BigInt base_bi = { 0 }; - bigint_init_unsigned(&base_bi, base); + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); - for (;;) - { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); - *(buf++) = digit_to_char(digit, false); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) { + break; + } } - if (bigint_cmp_zero(a) == CMP_EQ) - { - break; + + // reverse + char *out = (char *)allocate_size(allocator, buf - start + 2); + char *current = out; + if (bigint->is_negative) { + *(current++) = '-'; } - } + for (char *ptr = buf - 1; ptr >= start; ptr--) { + *(current++) = *ptr; + } + *(current++) = '\0'; - // reverse - char *out = (char *)allocate_size(allocator, buf - start + 2); - char *current = out; - if (bigint->is_negative) - { - *(current++) = '-'; - } - for (char *ptr = buf - 1; ptr >= start; ptr--) - { - *(current++) = *ptr; - } - *(current++) = '\0'; - - return out; + return out; } -void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) -{ - if (bigint->digit_count == 0) - { - fprintf(file, "0"); - return; - } - if (bigint->is_negative) - { - fprintf(file, "-"); - } - if (bigint->digit_count == 1 && base == 10) - { - fprintf(file, "%" PRIu64, bigint->digit); - return; - } - size_t len = bigint->digit_count * 64; - char *start = (char *)malloc_arena(len); - char *buf = start; - - BigInt digit_bi = { 0 }; - BigInt a1 = { 0 }; - BigInt a2 = { 0 }; - - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, bigint); - - BigInt base_bi = { 0 }; - bigint_init_unsigned(&base_bi, base); - - for (;;) - { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); - *(buf++) = digit_to_char(digit, false); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; +void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) { + if (bigint->digit_count == 0) { + fprintf(file, "0"); + return; } - if (bigint_cmp_zero(a) == CMP_EQ) - { - break; + if (bigint->is_negative) { + fprintf(file, "-"); } - } + if (bigint->digit_count == 1 && base == 10) { + fprintf(file, "%" PRIu64, bigint->digit); + return; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)malloc_arena(len); + char *buf = start; - // reverse + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; - for (char *ptr = buf - 1; ptr >= start; ptr--) - { - fprintf(file, "%c", *ptr); - } + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) { + break; + } + } + + // reverse + + for (char *ptr = buf - 1; ptr >= start; ptr--) { + fprintf(file, "%c", *ptr); + } } -size_t bigint_popcount_unsigned(const BigInt *big_int) -{ - assert(!big_int->is_negative); - if (big_int->digit_count == 0) - { - return 0; - } - - unsigned count = 0; - size_t bit_count = big_int->digit_count * 64; - for (size_t i = 0; i < bit_count; i++) - { - if (bit_at_index(big_int, i)) - { - count += 1; +size_t bigint_popcount_unsigned(const BigInt *big_int) { + assert(!big_int->is_negative); + if (big_int->digit_count == 0) { + return 0; } - } - return count; + + unsigned count = 0; + size_t bit_count = big_int->digit_count * 64; + for (size_t i = 0; i < bit_count; i++) { + if (bit_at_index(big_int, i)) { + count += 1; + } + } + return count; } -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) -{ - if (bit_count == 0) - { - return 0; - } - if (bi->digit_count == 0) - { - return 0; - } - - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) - { - if (bit_at_index(&twos_comp, i)) - { - count += 1; +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) { + return 0; } - } - return count; -} - -size_t bigint_ctz(const BigInt *bi, size_t bit_count) -{ - if (bit_count == 0) - { - return 0; - } - if (bi->digit_count == 0) - { - return bit_count; - } - - BigInt twos_comp = { 0 }; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) - { - if (bit_at_index(&twos_comp, i)) - { - return count; + if (bi->digit_count == 0) { + return 0; } - count += 1; - } - return count; -} + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); -int64_t bigint_as_signed(const BigInt *bigint) -{ - if (bigint->digit_count == 0) return 0; - if (bigint->digit_count != 1) - { - FATAL_ERROR("BigInt larger than i64"); - } - - if (bigint->is_negative) - { - // TODO this code path is untested - if (bigint->digit <= 9223372036854775808ULL) - { - return (-((int64_t) (bigint->digit - 1))) - 1; + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) { + count += 1; + } } - FATAL_ERROR("BigInt does not fit in i64"); - } - return (int64_t)bigint->digit; + return count; } -CmpRes bigint_cmp_zero(const BigInt *op) -{ - if (op->digit_count == 0) - { - return CMP_EQ; - } - return op->is_negative ? CMP_LT : CMP_GT; -} - - -void bigint_incr(BigInt *x) -{ - if (!x->digit_count) - { - bigint_init_unsigned(x, 1); - return; - } - - if (x->digit_count == 1) - { - if (x->is_negative && x->digit != 0) - { - x->digit -= 1; - return; +size_t bigint_ctz(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) { + return 0; } - if (!x->is_negative && x->digit != UINT64_MAX) - { - x->digit += 1; - return; + if (bi->digit_count == 0) { + return bit_count; } - } - BigInt copy; - bigint_init_bigint(©, x); + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); - BigInt one; - bigint_init_unsigned(&one, 1); - - bigint_add(x, ©, &one); + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) { + return count; + } + count += 1; + } + return count; } -double bigint_as_float(const BigInt *bigint) -{ - if (bigint_fits_in_bits(bigint, 64, bigint->is_negative)) - { - return bigint->is_negative ? (double)bigint_as_signed(bigint) : (double)bigint_as_unsigned(bigint); - } - BigInt div; - uint64_t mult = 0x100000000000ULL; - double mul = 1; - bigint_init_unsigned(&div, mult); - BigInt current; - bigint_init_bigint(¤t, bigint); - double f = 0; - do - { - BigInt temp; - bigint_mod(&temp, ¤t, &div); - f += bigint_as_signed(&temp) * mul; - mul *= mult; - bigint_div_trunc(&temp, ¤t, &div); - current = temp; - } - while (current.digit_count > 0); - return f; +int64_t bigint_as_signed(const BigInt *bigint) { + if (bigint->digit_count == 0) return 0; + if (bigint->digit_count != 1) { + FATAL_ERROR("BigInt larger than i64"); + } + + if (bigint->is_negative) { + // TODO this code path is untested + if (bigint->digit <= 9223372036854775808ULL) { + return (-((int64_t)(bigint->digit - 1))) - 1; + } + FATAL_ERROR("BigInt does not fit in i64"); + } + return (int64_t)bigint->digit; +} + +CmpRes bigint_cmp_zero(const BigInt *op) { + if (op->digit_count == 0) { + return CMP_EQ; + } + return op->is_negative ? CMP_LT : CMP_GT; +} + +void bigint_incr(BigInt *x) { + if (!x->digit_count) { + bigint_init_unsigned(x, 1); + return; + } + + if (x->digit_count == 1) { + if (x->is_negative && x->digit != 0) { + x->digit -= 1; + return; + } + if (!x->is_negative && x->digit != UINT64_MAX) { + x->digit += 1; + return; + } + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt one; + bigint_init_unsigned(&one, 1); + + bigint_add(x, ©, &one); +} + +double bigint_as_float(const BigInt *bigint) { + if (bigint_fits_in_bits(bigint, 64, bigint->is_negative)) { + return bigint->is_negative ? (double)bigint_as_signed(bigint) : (double)bigint_as_unsigned(bigint); + } + BigInt div; + uint64_t mult = 0x100000000000ULL; + double mul = 1; + bigint_init_unsigned(&div, mult); + BigInt current; + bigint_init_bigint(¤t, bigint); + double f = 0; + do { + BigInt temp; + bigint_mod(&temp, ¤t, &div); + f += bigint_as_signed(&temp) * mul; + mul *= mult; + bigint_div_trunc(&temp, ¤t, &div); + current = temp; + } while (current.digit_count > 0); + return f; } diff --git a/c3_big_int.h b/c3_big_int.h index 8f894ea..ac141d6 100644 --- a/c3_big_int.h +++ b/c3_big_int.h @@ -1,11 +1,10 @@ struct Token; #include -enum CmpRes -{ - CMP_LT, - CMP_GT, - CMP_EQ, +enum CmpRes { + CMP_LT, + CMP_GT, + CMP_EQ, }; #define malloc_arena(x) allocate_size(bigint_allocator, x) diff --git a/core_ast.cpp b/core_ast.cpp index 504050b..259239e 100644 --- a/core_ast.cpp +++ b/core_ast.cpp @@ -1,387 +1,387 @@ -#define AST_NEW(T,ikind,ipos,iflags) \ - Ast_##T *result = allocate_struct(pctx->perm, Ast_##T); \ - result->flags = iflags; \ - result->kind = AST_##ikind; \ - result->parent_scope = pctx->currently_parsed_scope; \ - result->pos = ipos; \ - result->di = ++pctx->unique_ids +#define AST_NEW(T, ikind, ipos, iflags) \ + Ast_##T *result = allocate_struct(pctx->perm, Ast_##T); \ + result->flags = iflags; \ + result->kind = AST_##ikind; \ + result->parent_scope = pctx->currently_parsed_scope; \ + result->pos = ipos; \ + result->di = ++pctx->unique_ids -#define ast_new(T,kind,pos,flags) (T *)_ast_new(sizeof(T), kind, pos, flags) +#define ast_new(T, kind, pos, flags) (T *)_ast_new(sizeof(T), kind, pos, flags) CORE_Static Ast * -_ast_new(size_t size, Ast_Kind kind, Token *pos, Ast_Flag flags = 0){ - Ast *result = (Ast *)allocate_size(pctx->perm, size); - result->flags = flags; - result->kind = kind; - result->parent_scope = pctx->currently_parsed_scope; - result->pos = pos; - result->di = ++pctx->unique_ids; - return result; +_ast_new(size_t size, Ast_Kind kind, Token *pos, Ast_Flag flags = 0) { + Ast *result = (Ast *)allocate_size(pctx->perm, size); + result->flags = flags; + result->kind = kind; + result->parent_scope = pctx->currently_parsed_scope; + result->pos = pos; + result->di = ++pctx->unique_ids; + return result; } CORE_Static Ast_Atom * -ast_str(Token *pos, Intern_String string){ - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->type = pctx->untyped_string; - result->intern_val = string; - return result; +ast_str(Token *pos, Intern_String string) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->type = pctx->untyped_string; + result->intern_val = string; + return result; } CORE_Static Ast_Atom * -ast_ident(Token *pos, Intern_String string){ - AST_NEW(Atom, IDENT, pos, AST_EXPR | AST_ATOM); - result->intern_val = string; - return result; +ast_ident(Token *pos, Intern_String string) { + AST_NEW(Atom, IDENT, pos, AST_EXPR | AST_ATOM); + result->intern_val = string; + return result; } CORE_Static Ast_Atom * -ast_bool(Token *pos, B32 bool_val){ - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->bool_val = bool_val; - result->type = pctx->untyped_bool; - return result; +ast_bool(Token *pos, B32 bool_val) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->bool_val = bool_val; + result->type = pctx->untyped_bool; + return result; } CORE_Static Ast_Atom * -ast_float(Token *pos, F64 value){ - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->type = pctx->untyped_float; - result->f64_val = value; - return result; +ast_float(Token *pos, F64 value) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->type = pctx->untyped_float; + result->f64_val = value; + return result; } CORE_Static Ast_Atom * -ast_int(Token *pos, BigInt val){ - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->type = pctx->untyped_int; - result->big_int_val = bigint_copy(pctx->perm, &val); - return result; +ast_int(Token *pos, BigInt val) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->type = pctx->untyped_int; + result->big_int_val = bigint_copy(pctx->perm, &val); + return result; } CORE_Static Ast_Atom * -ast_int(Token *pos, U64 value){ - return ast_int(pos, bigint_u64(value)); +ast_int(Token *pos, U64 value) { + return ast_int(pos, bigint_u64(value)); } CORE_Static Ast_Expr * -ast_expr_binary(Ast_Expr *left, Ast_Expr *right, Token *op){ - AST_NEW(Binary, BINARY, op, AST_EXPR); - result->op = op->kind; - result->left = left; - result->right = right; - return result; +ast_expr_binary(Ast_Expr *left, Ast_Expr *right, Token *op) { + AST_NEW(Binary, BINARY, op, AST_EXPR); + result->op = op->kind; + result->left = left; + result->right = right; + return result; } CORE_Static Ast_Call * -ast_call(Token *pos, Ast_Expr *name, Array exprs){ - // name here specifies also typespec for compound expressions ! - AST_NEW(Call, CALL, pos, AST_EXPR); - result->name = name; - result->exprs = exprs.tight_copy(pctx->perm); - return result; +ast_call(Token *pos, Ast_Expr *name, Array exprs) { + // name here specifies also typespec for compound expressions ! + AST_NEW(Call, CALL, pos, AST_EXPR); + result->name = name; + result->exprs = exprs.tight_copy(pctx->perm); + return result; } CORE_Static Ast_Call_Item * -ast_call_item(Token *pos, Ast_Atom *name, Ast_Expr *index, Ast_Expr *item){ - AST_NEW(Call_Item, CALL_ITEM, pos, AST_EXPR); - result->name = name; - result->item = item; - result->index = index; - return result; +ast_call_item(Token *pos, Ast_Atom *name, Ast_Expr *index, Ast_Expr *item) { + AST_NEW(Call_Item, CALL_ITEM, pos, AST_EXPR); + result->name = name; + result->item = item; + result->index = index; + return result; } CORE_Static Ast_Expr * -ast_expr_unary(Token *pos, Token_Kind op, Ast_Expr *expr){ - AST_NEW(Unary, UNARY, pos, AST_EXPR); - result->flags = AST_EXPR; - result->expr = expr; - result->op = op; - return result; +ast_expr_unary(Token *pos, Token_Kind op, Ast_Expr *expr) { + AST_NEW(Unary, UNARY, pos, AST_EXPR); + result->flags = AST_EXPR; + result->expr = expr; + result->op = op; + return result; } CORE_Static Ast_Expr * -ast_expr_index(Token *pos, Ast_Expr *expr, Ast_Expr *index){ - AST_NEW(Index, INDEX, pos, AST_EXPR); - result->flags = AST_EXPR; - result->expr = expr; - result->index = index; - return result; +ast_expr_index(Token *pos, Ast_Expr *expr, Ast_Expr *index) { + AST_NEW(Index, INDEX, pos, AST_EXPR); + result->flags = AST_EXPR; + result->expr = expr; + result->index = index; + return result; } CORE_Static Ast_Lambda * -ast_lambda(Token *pos, Array params, Array ret, Ast_Scope *scope){ - AST_NEW(Lambda, LAMBDA_EXPR, pos, AST_EXPR); - result->flags = AST_EXPR; - result->args = params.tight_copy(pctx->perm); - result->ret = ret.tight_copy(pctx->perm); - result->scope = scope; - return result; +ast_lambda(Token *pos, Array params, Array ret, Ast_Scope *scope) { + AST_NEW(Lambda, LAMBDA_EXPR, pos, AST_EXPR); + result->flags = AST_EXPR; + result->args = params.tight_copy(pctx->perm); + result->ret = ret.tight_copy(pctx->perm); + result->scope = scope; + return result; } CORE_Static Ast_If * -ast_if(Token *pos, Array ifs){ - AST_NEW(If, IF, pos, AST_STMT); - result->ifs = ifs.tight_copy(pctx->perm); - return result; +ast_if(Token *pos, Array ifs) { + AST_NEW(If, IF, pos, AST_STMT); + result->ifs = ifs.tight_copy(pctx->perm); + return result; } CORE_Static Ast_For * -ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Scope *scope){ - AST_NEW(For, FOR, pos, AST_STMT); - result->init = init; - result->cond = cond; - result->iter = iter; - result->scope = scope; - return result; +ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Scope *scope) { + AST_NEW(For, FOR, pos, AST_STMT); + result->init = init; + result->cond = cond; + result->iter = iter; + result->scope = scope; + return result; } CORE_Static Ast_Pass * -ast_pass(Token *pos){ - AST_NEW(Pass, PASS, pos, AST_STMT); - return result; +ast_pass(Token *pos) { + AST_NEW(Pass, PASS, pos, AST_STMT); + return result; } CORE_Static Ast_Break * -ast_break(Token *pos){ - AST_NEW(Break, BREAK, pos, AST_STMT); - return result; +ast_break(Token *pos) { + AST_NEW(Break, BREAK, pos, AST_STMT); + return result; } CORE_Static Ast_Return * -ast_return(Token *pos, Array expr){ - AST_NEW(Return, RETURN, pos, AST_STMT); - if(expr.len){ - For(expr) assert(is_flag_set(it->flags, AST_EXPR)); - result->expr = expr.tight_copy(pctx->perm); - } - return result; +ast_return(Token *pos, Array expr) { + AST_NEW(Return, RETURN, pos, AST_STMT); + if (expr.len) { + For(expr) assert(is_flag_set(it->flags, AST_EXPR)); + result->expr = expr.tight_copy(pctx->perm); + } + return result; } CORE_Static Ast_If_Node * -ast_if_node(Token *pos, Ast_Expr *init, Ast_Expr *expr, Ast_Scope *scope){ - AST_NEW(If_Node, IF_NODE, pos, AST_STMT); - result->scope = scope; - result->expr = expr; - result->init = (Ast_Binary *)init; - if(result->init) { - assert(init->kind == AST_VAR); - } - return result; +ast_if_node(Token *pos, Ast_Expr *init, Ast_Expr *expr, Ast_Scope *scope) { + AST_NEW(If_Node, IF_NODE, pos, AST_STMT); + result->scope = scope; + result->expr = expr; + result->init = (Ast_Binary *)init; + if (result->init) { + assert(init->kind == AST_VAR); + } + return result; } CORE_Static Ast_Array * -ast_array(Token *pos, Ast_Expr *expr){ - AST_NEW(Array, ARRAY, pos, AST_EXPR); - result->expr = expr; - return result; +ast_array(Token *pos, Ast_Expr *expr) { + AST_NEW(Array, ARRAY, pos, AST_EXPR); + result->expr = expr; + return result; } CORE_Static Ast_Scope * -begin_decl_scope(Allocator *scratch, Token *pos){ - AST_NEW(Scope, SCOPE, pos, AST_DECL); - result->file = pctx->currently_parsed_file; - result->module = pctx->currently_parsed_file->module; - result->scope_id = pctx->scope_ids++; - result->debug_name = pos->string; - assert(result->file); - pctx->currently_parsed_scope = result; - return result; +begin_decl_scope(Allocator *scratch, Token *pos) { + AST_NEW(Scope, SCOPE, pos, AST_DECL); + result->file = pctx->currently_parsed_file; + result->module = pctx->currently_parsed_file->module; + result->scope_id = pctx->scope_ids++; + result->debug_name = pos->string; + assert(result->file); + pctx->currently_parsed_scope = result; + return result; } CORE_Static void -finalize_decl_scope(Ast_Scope *scope){ - pctx->currently_parsed_scope = scope->parent_scope; +finalize_decl_scope(Ast_Scope *scope) { + pctx->currently_parsed_scope = scope->parent_scope; } CORE_Static Ast_Scope * -begin_stmt_scope(Allocator *scratch, Token *pos){ - AST_NEW(Scope, SCOPE, pos, AST_STMT); - result->stmts = {scratch}; - result->file = pctx->currently_parsed_file; - result->module = pctx->currently_parsed_file->module; - result->scope_id = pctx->scope_ids++; - result->debug_name = pos->string; - assert(result->file); - pctx->currently_parsed_scope = result; - return result; +begin_stmt_scope(Allocator *scratch, Token *pos) { + AST_NEW(Scope, SCOPE, pos, AST_STMT); + result->stmts = {scratch}; + result->file = pctx->currently_parsed_file; + result->module = pctx->currently_parsed_file->module; + result->scope_id = pctx->scope_ids++; + result->debug_name = pos->string; + assert(result->file); + pctx->currently_parsed_scope = result; + return result; } CORE_Static void -finalize_stmt_scope(Ast_Scope *scope){ - scope->stmts = scope->stmts.tight_copy(pctx->perm); - pctx->currently_parsed_scope = scope->parent_scope; +finalize_stmt_scope(Ast_Scope *scope) { + scope->stmts = scope->stmts.tight_copy(pctx->perm); + pctx->currently_parsed_scope = scope->parent_scope; } CORE_Static Ast_Decl * -ast_struct(Token *pos, Ast_Scope *scope){ - AST_NEW(Decl, STRUCT, pos, AST_DECL | AST_AGGREGATE); - result->scope = scope; - return result; +ast_struct(Token *pos, Ast_Scope *scope) { + AST_NEW(Decl, STRUCT, pos, AST_DECL | AST_AGGREGATE); + result->scope = scope; + return result; } CORE_Static Ast_Decl * -ast_enum(Token *pos, Ast_Expr *typespec, Ast_Scope *scope){ - AST_NEW(Decl, ENUM, pos, AST_DECL | AST_AGGREGATE); - result->scope = scope; - result->typespec = typespec; - return result; +ast_enum(Token *pos, Ast_Expr *typespec, Ast_Scope *scope) { + AST_NEW(Decl, ENUM, pos, AST_DECL | AST_AGGREGATE); + result->scope = scope; + result->typespec = typespec; + return result; } CORE_Static Ast_Decl * -ast_var(Token *pos, Ast_Expr *typespec, Intern_String name, Ast_Expr *expr){ - AST_NEW(Decl, VAR, pos, AST_DECL); - result->name = name; - result->typespec = typespec; - result->expr = expr; - return result; +ast_var(Token *pos, Ast_Expr *typespec, Intern_String name, Ast_Expr *expr) { + AST_NEW(Decl, VAR, pos, AST_DECL); + result->name = name; + result->typespec = typespec; + result->expr = expr; + return result; } CORE_Static Ast_Decl * -ast_const(Token *pos, Intern_String name, Value value){ - AST_NEW(Decl, CONST, pos, AST_DECL); - result->value = value; - result->name = name; - return result; +ast_const(Token *pos, Intern_String name, Value value) { + AST_NEW(Decl, CONST, pos, AST_DECL); + result->value = value; + result->name = name; + return result; } CORE_Static Ast_Decl * -ast_const(Token *pos, Intern_String name, Ast_Expr *expr){ - AST_NEW(Decl, CONST, pos, AST_DECL); - result->expr = expr; - result->name = name; - return result; +ast_const(Token *pos, Intern_String name, Ast_Expr *expr) { + AST_NEW(Decl, CONST, pos, AST_DECL); + result->expr = expr; + result->name = name; + return result; } CORE_Static Ast_Decl * -ast_type(Token *pos, Intern_String name, Ast_Type *type){ - AST_NEW(Decl, TYPE, pos, AST_DECL); - result->type = pctx->type_type; - result->type_val = type; - result->name = name; - return result; +ast_type(Token *pos, Intern_String name, Ast_Type *type) { + AST_NEW(Decl, TYPE, pos, AST_DECL); + result->type = pctx->type_type; + result->type_val = type; + result->name = name; + return result; } CORE_Static Ast_Scope * -ast_decl_scope(Token *pos, Allocator *allocator, Ast_File *file){ - AST_NEW(Scope, SCOPE, pos, AST_DECL); - result->file = file; +ast_decl_scope(Token *pos, Allocator *allocator, Ast_File *file) { + AST_NEW(Scope, SCOPE, pos, AST_DECL); + result->file = file; - result->scope_id = pctx->scope_ids++; - assert(result->file); - return result; + result->scope_id = pctx->scope_ids++; + assert(result->file); + return result; } CORE_Static Ast_Decl * -ast_namespace(Token *pos, Ast_Scope *module, Intern_String name){ - AST_NEW(Decl, NAMESPACE, pos, AST_DECL); - result->scope = module; - result->name = name; - return result; +ast_namespace(Token *pos, Ast_Scope *module, Intern_String name) { + AST_NEW(Decl, NAMESPACE, pos, AST_DECL); + result->scope = module; + result->name = name; + return result; } CORE_Static Ast_Builtin * -ast_runtime_assert(Token *pos, Ast_Expr *expr, Intern_String message){ - AST_NEW(Builtin, RUNTIME_ASSERT, pos, AST_EXPR); - result->expr = expr; - result->assert_message = message; - return result; +ast_runtime_assert(Token *pos, Ast_Expr *expr, Intern_String message) { + AST_NEW(Builtin, RUNTIME_ASSERT, pos, AST_EXPR); + result->expr = expr; + result->assert_message = message; + return result; } CORE_Static Ast_Builtin * -ast_constant_assert(Token *pos, Ast_Expr *expr, Intern_String message){ - AST_NEW(Builtin, CONSTANT_ASSERT, pos, AST_EXPR); - result->expr = expr; - result->assert_message = message; - return result; +ast_constant_assert(Token *pos, Ast_Expr *expr, Intern_String message) { + AST_NEW(Builtin, CONSTANT_ASSERT, pos, AST_EXPR); + result->expr = expr; + result->assert_message = message; + return result; } CORE_Static Ast_Builtin * -ast_sizeof(Token *pos, Ast_Expr *expr){ - AST_NEW(Builtin, SIZE_OF, pos, AST_EXPR); - result->expr = expr; - return result; +ast_sizeof(Token *pos, Ast_Expr *expr) { + AST_NEW(Builtin, SIZE_OF, pos, AST_EXPR); + result->expr = expr; + return result; } CORE_Static Ast_Builtin * -ast_len(Token *pos, Ast_Expr *expr){ - AST_NEW(Builtin, LENGTH_OF, pos, AST_EXPR); - result->expr = expr; - return result; +ast_len(Token *pos, Ast_Expr *expr) { + AST_NEW(Builtin, LENGTH_OF, pos, AST_EXPR); + result->expr = expr; + return result; } CORE_Static Ast_Builtin * -ast_alignof(Token *pos, Ast_Expr *expr){ - AST_NEW(Builtin, ALIGN_OF, pos, AST_EXPR); - result->expr = expr; - return result; +ast_alignof(Token *pos, Ast_Expr *expr) { + AST_NEW(Builtin, ALIGN_OF, pos, AST_EXPR); + result->expr = expr; + return result; } CORE_Static Ast_Var_Unpack * -ast_var_unpack(Token *pos, Array vars, Ast_Expr *expr){ - AST_NEW(Var_Unpack, VAR_UNPACK, pos, AST_STMT); - result->vars = vars.tight_copy(pctx->perm); - result->expr = expr; - return result; +ast_var_unpack(Token *pos, Array vars, Ast_Expr *expr) { + AST_NEW(Var_Unpack, VAR_UNPACK, pos, AST_STMT); + result->vars = vars.tight_copy(pctx->perm); + result->expr = expr; + return result; } //----------------------------------------------------------------------------- // Value //----------------------------------------------------------------------------- CORE_Static Value -value_bool(B32 v){ - Value value; - value.bool_val = v; - value.type = pctx->untyped_bool; - return value; +value_bool(B32 v) { + Value value; + value.bool_val = v; + value.type = pctx->untyped_bool; + return value; } CORE_Static Value -value_int(BigInt b){ - Value value; - value.big_int_val = b; - value.type = pctx->untyped_int; - return value; +value_int(BigInt b) { + Value value; + value.big_int_val = b; + value.type = pctx->untyped_int; + return value; } CORE_Static Value -value_int(S64 s64){ - Value value; - value.type = pctx->untyped_int; - bigint_init_signed(&value.big_int_val, s64); - return value; +value_int(S64 s64) { + Value value; + value.type = pctx->untyped_int; + bigint_init_signed(&value.big_int_val, s64); + return value; } CORE_Static Value -value_float(F64 b){ - Value value; - value.f64_val = b; - value.type = pctx->untyped_float; - return value; +value_float(F64 b) { + Value value; + value.f64_val = b; + value.type = pctx->untyped_float; + return value; } CORE_Static Value -value_float(BigInt a){ - Value value; - value.f64_val = bigint_as_float(&a); - value.type = pctx->untyped_float; - return value; +value_float(BigInt a) { + Value value; + value.f64_val = bigint_as_float(&a); + value.type = pctx->untyped_float; + return value; } CORE_Static B32 -is_ident(Ast *ast){ - B32 result = ast->kind == AST_IDENT; - return result; +is_ident(Ast *ast) { + B32 result = ast->kind == AST_IDENT; + return result; } CORE_Static B32 -is_binary(Ast *ast){ - B32 result = ast->kind == AST_BINARY; - return result; +is_binary(Ast *ast) { + B32 result = ast->kind == AST_BINARY; + return result; } CORE_Static B32 -is_atom(Ast *ast){ - B32 result = is_flag_set(ast->flags, AST_ATOM); - return result; +is_atom(Ast *ast) { + B32 result = is_flag_set(ast->flags, AST_ATOM); + return result; } diff --git a/core_codegen_c_language.cpp b/core_codegen_c_language.cpp index 4a2d484..778ce5c 100644 --- a/core_codegen_c_language.cpp +++ b/core_codegen_c_language.cpp @@ -1,6 +1,11 @@ #define gen(...) pctx->gen.addf(__VA_ARGS__) -#define genln(...) do{gen("\n"); gen_indent(); gen(__VA_ARGS__); }while(0) +#define genln(...) \ + do { \ + gen("\n"); \ + gen_indent(); \ + gen(__VA_ARGS__); \ + } while (0) global S32 global_indent; global S32 is_inside_struct; @@ -8,520 +13,537 @@ CORE_Static void gen_ast(Ast *ast); CORE_Static bool gen_expr(Ast_Expr *ast); CORE_Static void -gen_indent(){ - for(S32 i = 0; i < global_indent; i++) gen(" "); +gen_indent() { + for (S32 i = 0; i < global_indent; i++) gen(" "); } global Intern_String last_filename; global int last_line; CORE_Static void -gen_line(Ast *node){ - if(pctx->emit_line_directives){ - last_line = node->pos->line+1; - genln("#line %d", last_line); - if(node->pos->file != last_filename){ - last_filename = node->pos->file; - gen(" \"%Q\"", last_filename); +gen_line(Ast *node) { + if (pctx->emit_line_directives) { + last_line = node->pos->line + 1; + genln("#line %d", last_line); + if (node->pos->file != last_filename) { + last_filename = node->pos->file; + gen(" \"%Q\"", last_filename); + } } - } } CORE_Static void -gen_last_line(){ - if(pctx->emit_line_directives){ - genln("#line %d", last_line); - } +gen_last_line() { + if (pctx->emit_line_directives) { + genln("#line %d", last_line); + } } CORE_Static String -string_scope_name(Allocator *a, Ast_Scope *scope){ - String string = {}; - if(scope->parent_scope) string = string_scope_name(a, scope->parent_scope); - assert_message(scope->scope_id != 0, "Scope id is equal to 0 which is invalid, scope didn't initialize id"); - string = string_fmt(a, "%QS%u_", string, scope->scope_id); - return string; +string_scope_name(Allocator *a, Ast_Scope *scope) { + String string = {}; + if (scope->parent_scope) string = string_scope_name(a, scope->parent_scope); + assert_message(scope->scope_id != 0, "Scope id is equal to 0 which is invalid, scope didn't initialize id"); + string = string_fmt(a, "%QS%u_", string, scope->scope_id); + return string; } CORE_Static void -gen_scope_name(Ast_Scope *scope){ - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - String string = string_scope_name(scratch, scope); - gen("%.*s", (int)string.len, string.str); +gen_scope_name(Ast_Scope *scope) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + String string = string_scope_name(scratch, scope); + gen("%.*s", (int)string.len, string.str); } CORE_Static Intern_String -get_unique_name(Ast *ast){ - Scratch_Scope _scope(pctx->scratch); - String result = string_scope_name(pctx->scratch, ast->parent_scope); - assert(result.len); - result = string_fmt(pctx->scratch, "%Q%d", result, ast->pos->line); - Intern_String result2 = pctx->intern(result); - return result2; +get_unique_name(Ast *ast) { + Scratch_Scope _scope(pctx->scratch); + String result = string_scope_name(pctx->scratch, ast->parent_scope); + assert(result.len); + result = string_fmt(pctx->scratch, "%Q%d", result, ast->pos->line); + Intern_String result2 = pctx->intern(result); + return result2; } global String prefixed_string_type; CORE_Static const char * -get_ctype_name_for_type(Ast_Type *type){ - switch(type->kind){ - case TYPE_VOID: return "void"; - case TYPE_BOOL: return "bool"; - case TYPE_STRING: return (char *)prefixed_string_type.str; - case TYPE_CHAR: return "char"; - case TYPE_F32: return "float"; - case TYPE_F64: return "double"; - case TYPE_INT: return "int"; - case TYPE_S8: return "int8_t"; - case TYPE_S16: return "int16_t"; - case TYPE_S32: return "int32_t"; - case TYPE_S64: return "int64_t"; - case TYPE_U8: return "uint8_t"; - case TYPE_U16: return "uint16_t"; - case TYPE_U32: return "uint32_t"; - case TYPE_U64: return "uint64_t"; - case TYPE_TUPLE: return "Tuple"; - case TYPE_TYPE: return "int64_t"; +get_ctype_name_for_type(Ast_Type *type) { + switch (type->kind) { + case TYPE_VOID: return "void"; + case TYPE_BOOL: return "bool"; + case TYPE_STRING: return (char *)prefixed_string_type.str; + case TYPE_CHAR: return "char"; + case TYPE_F32: return "float"; + case TYPE_F64: return "double"; + case TYPE_INT: return "int"; + case TYPE_S8: return "int8_t"; + case TYPE_S16: return "int16_t"; + case TYPE_S32: return "int32_t"; + case TYPE_S64: return "int64_t"; + case TYPE_U8: return "uint8_t"; + case TYPE_U16: return "uint16_t"; + case TYPE_U32: return "uint32_t"; + case TYPE_U64: return "uint64_t"; + case TYPE_TUPLE: return "Tuple"; + case TYPE_TYPE: return "int64_t"; - case TYPE_INCOMPLETE: { - // This happens in case where we have a procedure in an away module. - // And that procedure takes a pointer from a struct that is never - // use elsewhere. In that case this seems fine. - return "void"; + case TYPE_INCOMPLETE: { + // This happens in case where we have a procedure in an away module. + // And that procedure takes a pointer from a struct that is never + // use elsewhere. In that case this seems fine. + return "void"; + } + invalid_default_case; } - invalid_default_case; - } - return ""; + return ""; } CORE_Static String -string_simple_decl_prefix(Allocator *a, Ast_Type *ast){ - switch(ast->kind){ - case TYPE_POINTER:{ - String string = string_simple_decl_prefix(a, ast->base); - string = string_fmt(a, "%Q*", string); - return string; - }break; - case TYPE_LAMBDA: return {}; break; - case TYPE_ENUM: - case TYPE_ARRAY: { - return string_simple_decl_prefix(a, ast->base); - }break; - case TYPE_SLICE:{ - String string = string_simple_decl_prefix(a, ast->base); - string = string_fmt(a, "Slice%llu ", ast->type_id); - return string; - }break; - case TYPE_TUPLE:{ - String string = string_fmt(a, "Tuple%llu ", ast->type_id); - return string; - }break; - case TYPE_STRUCT: { - auto constant = (Ast_Decl *)ast->ast; - auto name = constant->unique_name; - String string = string_fmt(a, "%Q ", name); - String sc = {}; - return string_fmt(a, "%Q%Q", sc, string); - }break; - default: { - String string = string_fmt(a, "%s ", get_ctype_name_for_type(ast)); - return string; +string_simple_decl_prefix(Allocator *a, Ast_Type *ast) { + switch (ast->kind) { + case TYPE_POINTER: { + String string = string_simple_decl_prefix(a, ast->base); + string = string_fmt(a, "%Q*", string); + return string; + } break; + case TYPE_LAMBDA: return {}; break; + case TYPE_ENUM: + case TYPE_ARRAY: { + return string_simple_decl_prefix(a, ast->base); + } break; + case TYPE_SLICE: { + String string = string_simple_decl_prefix(a, ast->base); + string = string_fmt(a, "Slice%llu ", ast->type_id); + return string; + } break; + case TYPE_TUPLE: { + String string = string_fmt(a, "Tuple%llu ", ast->type_id); + return string; + } break; + case TYPE_STRUCT: { + auto constant = (Ast_Decl *)ast->ast; + auto name = constant->unique_name; + String string = string_fmt(a, "%Q ", name); + String sc = {}; + return string_fmt(a, "%Q%Q", sc, string); + } break; + default: { + String string = string_fmt(a, "%s ", get_ctype_name_for_type(ast)); + return string; + } } - } - return {}; + return {}; } CORE_Static String -string_simple_decl_postfix(Allocator *a, Ast_Type *ast){ - switch(ast->kind){ - case TYPE_POINTER: - return string_simple_decl_postfix(a, ast->base); - break; - case TYPE_ARRAY:{ - String result = string_simple_decl_postfix(a, ast->arr.base); - String string = string_fmt(a, "[%d]%Q", ast->arr.size, result); - return string; - }break; - case TYPE_LAMBDA:break; - case TYPE_SLICE: case TYPE_ENUM: case TYPE_STRUCT:break; - default:break; - // default: return string_from_cstring((char *)name(ast)); - } - return {}; +string_simple_decl_postfix(Allocator *a, Ast_Type *ast) { + switch (ast->kind) { + case TYPE_POINTER: + return string_simple_decl_postfix(a, ast->base); + break; + case TYPE_ARRAY: { + String result = string_simple_decl_postfix(a, ast->arr.base); + String string = string_fmt(a, "[%d]%Q", ast->arr.size, result); + return string; + } break; + case TYPE_LAMBDA: break; + case TYPE_SLICE: + case TYPE_ENUM: + case TYPE_STRUCT: break; + default: + break; + // default: return string_from_cstring((char *)name(ast)); + } + return {}; } CORE_Static String -string_simple_decl(Allocator *a, Ast_Type *ast, Intern_String name = {}){ - if(ast->kind == TYPE_LAMBDA) { - String prefix = string_simple_decl_prefix(a, ast->func.ret); - String string = string_fmt(a, "%Q(*%Q)(", prefix, name); - For(ast->func.args){ - String prefix_arg = string_simple_decl_prefix(a, it); - string = string_fmt(a, "%Q%Q", string, prefix_arg); - if(&it != ast->func.args.end() - 1) - string = string_fmt(a, "%Q, ", string); +string_simple_decl(Allocator *a, Ast_Type *ast, Intern_String name = {}) { + if (ast->kind == TYPE_LAMBDA) { + String prefix = string_simple_decl_prefix(a, ast->func.ret); + String string = string_fmt(a, "%Q(*%Q)(", prefix, name); + For(ast->func.args) { + String prefix_arg = string_simple_decl_prefix(a, it); + string = string_fmt(a, "%Q%Q", string, prefix_arg); + if (&it != ast->func.args.end() - 1) + string = string_fmt(a, "%Q, ", string); + } + string = string_fmt(a, "%Q)", string); + return string; } - string = string_fmt(a, "%Q)", string); - return string; - } - else{ - String string = string_simple_decl_prefix(a, ast); - if(name.len) { - string = string_fmt(a, "%Q%Q", string, name); + else { + String string = string_simple_decl_prefix(a, ast); + if (name.len) { + string = string_fmt(a, "%Q%Q", string, name); + } + String postfix = string_simple_decl_postfix(a, ast); + string = string_fmt(a, "%Q%Q", string, postfix); + return string; } - String postfix = string_simple_decl_postfix(a, ast); - string = string_fmt(a, "%Q%Q", string, postfix); - return string; - } } CORE_Static void -gen_simple_decl(Ast_Type *ast, Intern_String name = {}){ - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); +gen_simple_decl(Ast_Type *ast, Intern_String name = {}) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); - String string = string_simple_decl(scratch, ast, name); - gen("%.*s", (int)string.len, string.str); + String string = string_simple_decl(scratch, ast, name); + gen("%.*s", (int)string.len, string.str); } CORE_Static String -get_type_postfix(Ast_Type *type){ - switch(type->kind) { - case TYPE_F32: return "f"_s; break; - case TYPE_U64: return "ULL"_s; break; - case TYPE_S64: return "LL"_s; break; - case TYPE_F64:case TYPE_S8:case TYPE_S16: - case TYPE_S32:case TYPE_U8:case TYPE_U16: - case TYPE_INT:case TYPE_U32:case TYPE_CHAR: - return ""_s; - break; - invalid_default_case; - } - assert(!"Unhandled case or error"); - return ""_s; +get_type_postfix(Ast_Type *type) { + switch (type->kind) { + case TYPE_F32: return "f"_s; break; + case TYPE_U64: return "ULL"_s; break; + case TYPE_S64: return "LL"_s; break; + case TYPE_F64: + case TYPE_S8: + case TYPE_S16: + case TYPE_S32: + case TYPE_U8: + case TYPE_U16: + case TYPE_INT: + case TYPE_U32: + case TYPE_CHAR: + return ""_s; + break; + invalid_default_case; + } + assert(!"Unhandled case or error"); + return ""_s; } CORE_Static B32 -gen_value(Token *pos, Value a){ - if(is_untyped(a.type)) compiler_error(pos, "Internal compiler error: Untyped got propagated to the codegen stage"); +gen_value(Token *pos, Value a) { + if (is_untyped(a.type)) compiler_error(pos, "Internal compiler error: Untyped got propagated to the codegen stage"); - B32 result = true; - Ast_Type *type = a.type; - if(is_enum(a.type)){ - type = a.type->base; - } - - switch(type->kind){ - CASE_INT: { - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - - String postfix = get_type_postfix(type); - const char *string = bigint_to_error_string(scratch, &a.big_int_val, 10); - gen("%s%Q", string, postfix); - }break; - case TYPE_POINTER:{ - if(a.type == pctx->type_pointer_to_char){ - gen("\"%Q\"", a.intern_val); - } - else{ - U64 pointer_value = bigint_as_unsigned(&a.big_int_val); - gen("0x%llx", pointer_value); - } - }break; - CASE_BOOL: { - a.bool_val ? gen("true"):gen("false"); - }break; - CASE_FLOAT: { - String postfix = get_type_postfix(type); - gen("%f%Q", a.f64_val, postfix); - }break; - case TYPE_TYPE: { - gen("%d", a.type_val->type_id); - }break; - default: { - if (is_string(type)) { - int length = 0; - gen("(%QString){(uint8_t *)\"", pctx->symbol_prefix); - for(int i = 0; i < a.intern_val.len; i++){ - if(a.intern_val.str[i] == '\n'){length += 2; gen("\\n");} - else if(a.intern_val.str[i] == '\r'){length += 2; gen("\\r");} - else{length += 1; gen("%c", a.intern_val.str[i]);} - - } - gen("\", %d}", length); - } - else result = false; + B32 result = true; + Ast_Type *type = a.type; + if (is_enum(a.type)) { + type = a.type->base; } - } - return result; + + switch (type->kind) { + CASE_INT : { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + + String postfix = get_type_postfix(type); + const char *string = bigint_to_error_string(scratch, &a.big_int_val, 10); + gen("%s%Q", string, postfix); + } break; + case TYPE_POINTER: { + if (a.type == pctx->type_pointer_to_char) { + gen("\"%Q\"", a.intern_val); + } + else { + U64 pointer_value = bigint_as_unsigned(&a.big_int_val); + gen("0x%llx", pointer_value); + } + } break; + CASE_BOOL : { + a.bool_val ? gen("true") : gen("false"); + } break; + CASE_FLOAT : { + String postfix = get_type_postfix(type); + gen("%f%Q", a.f64_val, postfix); + } break; + case TYPE_TYPE: { + gen("%d", a.type_val->type_id); + } break; + default: { + if (is_string(type)) { + int length = 0; + gen("(%QString){(uint8_t *)\"", pctx->symbol_prefix); + for (int i = 0; i < a.intern_val.len; i++) { + if (a.intern_val.str[i] == '\n') { + length += 2; + gen("\\n"); + } + else if (a.intern_val.str[i] == '\r') { + length += 2; + gen("\\r"); + } + else { + length += 1; + gen("%c", a.intern_val.str[i]); + } + } + gen("\", %d}", length); + } + else result = false; + } + } + return result; } CORE_Static void -gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0){ - gen("{"); - global_indent++; - For(scope->stmts) { - gen_line(it); - genln(""); - gen_ast(it); - } - if(switch_case_gen_break == 1) genln("break;"); - global_indent--; - gen_last_line(); - genln("}"); +gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0) { + gen("{"); + global_indent++; + For(scope->stmts) { + gen_line(it); + genln(""); + gen_ast(it); + } + if (switch_case_gen_break == 1) genln("break;"); + global_indent--; + gen_last_line(); + genln("}"); } enum { - ALWAYS_EMIT_VALUE = 0, - DONT_EMIT_VALUE = 1, + ALWAYS_EMIT_VALUE = 0, + DONT_EMIT_VALUE = 1, }; CORE_Static void -gen_pointer(Ast_Expr *expr){ - gen("&"); - if(is_flag_set(expr->flags, AST_IS_LVALUE)){ - gen_expr(expr); - } - else{ - gen("("); - gen_simple_decl(expr->resolved_type); - gen(")"); - gen("{"); - gen_expr(expr); - gen("}"); - } -} - -CORE_Static void -gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type){ - - // We want normal values to get boxed as Any pointers - // but other then that we shouldn't box other Any values - if(expr->kind != AST_COMPOUND && is_any(decl_type) && !is_any(expr->resolved_type)){ - gen("(Any){"); - gen_pointer(expr); - gen(", %d}", expr->resolved_type->type_id); - } - else if(expr->kind == AST_IDENT && is_slice(decl_type)){ - Ast_Atom *atom = (Ast_Atom *)expr; - gen("("); - gen_simple_decl(decl_type); - gen(")"); - gen("{%d, ", atom->resolved_type->arr.size); - gen_expr(expr); - gen("}"); - } - else gen_expr(expr); -} - -CORE_Static void -gen_var(Ast_Decl *decl, B32 emit_value, B32 scope_names){ - if(is_flag_set(decl->flags, AST_FOREIGN)) gen("extern "); - gen_simple_decl(decl->type, decl->name); - if(is_flag_set(decl->flags, AST_FOREIGN)) return; - - if(emit_value == DONT_EMIT_VALUE){ - return; - } - - if(decl->expr){ - gen(" = "); - gen_try_any_or_slice(decl->expr, decl->type); - } else { // Default zero - if(is_numeric(decl->type)){ - gen(" = 0"); - } else { - gen(" = {}"); +gen_pointer(Ast_Expr *expr) { + gen("&"); + if (is_flag_set(expr->flags, AST_IS_LVALUE)) { + gen_expr(expr); + } + else { + gen("("); + gen_simple_decl(expr->resolved_type); + gen(")"); + gen("{"); + gen_expr(expr); + gen("}"); } - } } CORE_Static void -gen_lambda(Intern_String name, Ast_Lambda *lambda, B32 generate_block = true){ - gen_simple_decl(lambda->resolved_type->func.ret, name); - gen("("); - For(lambda->args){ - gen_var(it, DONT_EMIT_VALUE, true); - if(&it != (lambda->args.end() - 1)) - gen(", "); - } - gen(")"); +gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type) { - if(generate_block && lambda->scope){ - gen_stmt_scope(lambda->scope); - } - else gen(";"); + // We want normal values to get boxed as Any pointers + // but other then that we shouldn't box other Any values + if (expr->kind != AST_COMPOUND && is_any(decl_type) && !is_any(expr->resolved_type)) { + gen("(Any){"); + gen_pointer(expr); + gen(", %d}", expr->resolved_type->type_id); + } + else if (expr->kind == AST_IDENT && is_slice(decl_type)) { + Ast_Atom *atom = (Ast_Atom *)expr; + gen("("); + gen_simple_decl(decl_type); + gen(")"); + gen("{%d, ", atom->resolved_type->arr.size); + gen_expr(expr); + gen("}"); + } + else gen_expr(expr); +} + +CORE_Static void +gen_var(Ast_Decl *decl, B32 emit_value, B32 scope_names) { + if (is_flag_set(decl->flags, AST_FOREIGN)) gen("extern "); + gen_simple_decl(decl->type, decl->name); + if (is_flag_set(decl->flags, AST_FOREIGN)) return; + + if (emit_value == DONT_EMIT_VALUE) { + return; + } + + if (decl->expr) { + gen(" = "); + gen_try_any_or_slice(decl->expr, decl->type); + } + else { // Default zero + if (is_numeric(decl->type)) { + gen(" = 0"); + } + else { + gen(" = {}"); + } + } +} + +CORE_Static void +gen_lambda(Intern_String name, Ast_Lambda *lambda, B32 generate_block = true) { + gen_simple_decl(lambda->resolved_type->func.ret, name); + gen("("); + For(lambda->args) { + gen_var(it, DONT_EMIT_VALUE, true); + if (&it != (lambda->args.end() - 1)) + gen(", "); + } + gen(")"); + + if (generate_block && lambda->scope) { + gen_stmt_scope(lambda->scope); + } + else gen(";"); } CORE_Static bool -gen_expr(Ast_Expr *ast){ - switch(ast->kind){ - CASE(IDENT, Atom){ - if(node->resolved_decl->kind == AST_NAMESPACE) - return false; +gen_expr(Ast_Expr *ast) { + switch (ast->kind) { + CASE(IDENT, Atom) { + if (node->resolved_decl->kind == AST_NAMESPACE) + return false; - if(node->resolved_decl->kind == AST_LAMBDA){ - gen("%Q", node->resolved_decl->unique_name); - } + if (node->resolved_decl->kind == AST_LAMBDA) { + gen("%Q", node->resolved_decl->unique_name); + } - else { - gen("%Q", node->intern_val); - } + else { + gen("%Q", node->intern_val); + } - - BREAK(); - } - - CASE(VALUE, Atom){ - B32 written = gen_value(node->pos, node->value); - if(!written) { - gen("%Q", node->value.intern_val); - } - BREAK(); - } - - CASE(ARRAY, Array){ - gen("%d", node->resolved_type->type_id); - BREAK(); - } - - CASE(INDEX, Index){ - gen("("); - gen_expr(node->expr); - if(node->index_original_type == pctx->type_string || node->index_original_type == pctx->untyped_string){ - gen(".data"); - } - else if(is_slice(node->index_original_type)){ - gen(".data"); - } - gen("["); - gen_expr(node->index); - gen("]"); - gen(")"); - BREAK(); - } - - CASE(LAMBDA_EXPR, Lambda){ - gen_lambda({}, node); - BREAK(); - } - - CASE(BINARY, Binary){ - if(node->op == TK_Dot){ - if(gen_expr(node->left)){ - if(node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->"); - else gen("."); + BREAK(); } - gen_expr(node->right); - return true; - } - else if(node->op == TK_Arrow){ - gen("("); - gen("("); - gen_simple_decl(node->resolved_type); - gen(")"); - gen_expr(node->left); - gen(")"); - return true; - } + CASE(VALUE, Atom) { + B32 written = gen_value(node->pos, node->value); + if (!written) { + gen("%Q", node->value.intern_val); + } + BREAK(); + } - else if(node->resolved_operator_overload){ - gen("%Q(", node->resolved_operator_overload->unique_name); - gen_expr(node->left); - gen(", "); - gen_expr(node->right); - gen(")"); - } + CASE(ARRAY, Array) { + gen("%d", node->resolved_type->type_id); + BREAK(); + } - else { - if(!token_is_assign(node->op)) gen("("); - gen_expr(node->left); - gen("%s", name(node->op)); - gen_expr(node->right); - if(!token_is_assign(node->op)) gen(")"); - } + CASE(INDEX, Index) { + gen("("); + gen_expr(node->expr); + if (node->index_original_type == pctx->type_string || node->index_original_type == pctx->untyped_string) { + gen(".data"); + } + else if (is_slice(node->index_original_type)) { + gen(".data"); + } + gen("["); + gen_expr(node->index); + gen("]"); + gen(")"); + BREAK(); + } + CASE(LAMBDA_EXPR, Lambda) { + gen_lambda({}, node); + BREAK(); + } - BREAK(); + CASE(BINARY, Binary) { + if (node->op == TK_Dot) { + if (gen_expr(node->left)) { + if (node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->"); + else gen("."); + } + gen_expr(node->right); + return true; + } + + else if (node->op == TK_Arrow) { + gen("("); + gen("("); + gen_simple_decl(node->resolved_type); + gen(")"); + gen_expr(node->left); + gen(")"); + return true; + } + + else if (node->resolved_operator_overload) { + gen("%Q(", node->resolved_operator_overload->unique_name); + gen_expr(node->left); + gen(", "); + gen_expr(node->right); + gen(")"); + } + + else { + if (!token_is_assign(node->op)) gen("("); + gen_expr(node->left); + gen("%s", name(node->op)); + gen_expr(node->right); + if (!token_is_assign(node->op)) gen(")"); + } + + BREAK(); + } + + CASE(UNARY, Unary) { + if (node->resolved_operator_overload) { + gen("%Q(", node->resolved_operator_overload->unique_name); + gen_expr(node->expr); + gen(")"); + } + + else { + gen("("); + if (node->op != TK_PostIncrement && node->op != TK_PostDecrement) gen("%s", name(node->op)); + gen_expr(node->expr); + if (node->op == TK_PostIncrement || node->op == TK_PostDecrement) gen("%s", name(node->op)); + gen(")"); + } + BREAK(); + } + + CASE(VAR, Decl) { + gen_ast(node); + BREAK(); + } + + CASE(LENGTH_OF, Call) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + gen_expr(expr); + if (is_pointer(expr->resolved_type)) + gen("->len"); + else gen(".len"); + BREAK(); + } + + CASE(CALL, Call) { + gen("%Q(", node->resolved_decl->unique_name); + For(node->exprs) { + gen_try_any_or_slice(it->item, it->resolved_type); + if (!node->exprs.is_last(&it)) gen(", "); + } + gen(")"); + BREAK(); + } + + CASE(COMPOUND, Call) { + gen("("); + gen_simple_decl(node->resolved_type); + gen(")"); + gen("{"); + + // We need to double wrap it because Any is a pointer + // We need to pass it a compound array + if (is_slice(node->resolved_type)) { + gen(".len = %d, ", node->exprs.len); + gen(".data = ("); + gen_simple_decl(node->resolved_type->base); + gen("[]"); + gen(")"); + gen("{"); + } + + For(node->exprs) { + if (is_struct(node->resolved_type)) + gen(".%Q = ", it->resolved_name); + else if (is_array(node->resolved_type)) + gen("[%d] = ", (int)it->resolved_index); + gen_try_any_or_slice(it->item, it->resolved_type); + + if (!node->exprs.is_last(&it)) gen(", "); + } + + if (is_slice(node->resolved_type)) gen("}"); + gen("}"); + BREAK(); + } + + invalid_default_case; } - - CASE(UNARY, Unary){ - if(node->resolved_operator_overload){ - gen("%Q(", node->resolved_operator_overload->unique_name); - gen_expr(node->expr); - gen(")"); - } - - else { - gen("("); - if(node->op != TK_PostIncrement && node->op != TK_PostDecrement) gen("%s", name(node->op)); - gen_expr(node->expr); - if(node->op == TK_PostIncrement || node->op == TK_PostDecrement) gen("%s", name(node->op)); - gen(")"); - } - BREAK(); - } - - CASE(VAR, Decl){ - gen_ast(node); - BREAK(); - } - - CASE(LENGTH_OF, Call){ - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - gen_expr(expr); - if(is_pointer(expr->resolved_type)) - gen("->len"); - else gen(".len"); - BREAK(); - } - - CASE(CALL, Call){ - gen("%Q(", node->resolved_decl->unique_name); - For(node->exprs){ - gen_try_any_or_slice(it->item, it->resolved_type); - if(!node->exprs.is_last(&it)) gen(", "); - } - gen(")"); - BREAK(); - } - - CASE(COMPOUND, Call){ - gen("("); - gen_simple_decl(node->resolved_type); - gen(")"); - gen("{"); - - // We need to double wrap it because Any is a pointer - // We need to pass it a compound array - if(is_slice(node->resolved_type)) { - gen(".len = %d, ", node->exprs.len); - gen(".data = ("); - gen_simple_decl(node->resolved_type->base); - gen("[]"); - gen(")"); - gen("{"); - } - - For(node->exprs){ - if(is_struct(node->resolved_type)) - gen(".%Q = ", it->resolved_name); - else if(is_array(node->resolved_type)) - gen("[%d] = ", (int)it->resolved_index); - gen_try_any_or_slice(it->item, it->resolved_type); - - if(!node->exprs.is_last(&it)) gen(", "); - } - - if(is_slice(node->resolved_type)) gen("}"); - gen("}"); - BREAK(); - } - - invalid_default_case; - } - return true; + return true; } /* @@ -530,50 +552,50 @@ gen_expr(Ast_Expr *ast){ */ CORE_Static void -gen_ast(Ast *ast){ - switch(ast->kind){ +gen_ast(Ast *ast) { + switch (ast->kind) { - CASE(RUNTIME_ASSERT, Builtin){ - if(node->assert_message.len == 0) gen("%QAssert", pctx->symbol_prefix); - else gen("%QAssertMessage", pctx->symbol_prefix); - gen("("); - gen_expr(node->expr); - if(node->assert_message.len){ - gen(", \"%Q\"", node->assert_message); - } - gen(");"); - BREAK(); - } - - CASE(RETURN, Return){ - if(is_tuple(node->resolved_type)) { - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - - Intern_String tuple_name = get_unique_name(node); - gen_simple_decl(node->resolved_type, tuple_name); - gen(";"); - - int i = 0; - For(node->expr){ - - // We cant assign to array in C so we need a special case - if (is_array(it->resolved_type)) { - genln("%QMemoryCopy(&%Q.m%d, ", pctx->symbol_prefix, tuple_name, i); - gen_expr(it); - gen(", sizeof(%Q.m%d));", tuple_name, i); - } - - else { - genln("%Q.m%d = ", tuple_name, i); - gen_expr(it); - gen(";"); - } - - i += 1; + CASE(RUNTIME_ASSERT, Builtin) { + if (node->assert_message.len == 0) gen("%QAssert", pctx->symbol_prefix); + else gen("%QAssertMessage", pctx->symbol_prefix); + gen("("); + gen_expr(node->expr); + if (node->assert_message.len) { + gen(", \"%Q\"", node->assert_message); + } + gen(");"); + BREAK(); } - gen_line(node); - genln("return %Q;", tuple_name); + + CASE(RETURN, Return) { + if (is_tuple(node->resolved_type)) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + + Intern_String tuple_name = get_unique_name(node); + gen_simple_decl(node->resolved_type, tuple_name); + gen(";"); + + int i = 0; + For(node->expr) { + + // We cant assign to array in C so we need a special case + if (is_array(it->resolved_type)) { + genln("%QMemoryCopy(&%Q.m%d, ", pctx->symbol_prefix, tuple_name, i); + gen_expr(it); + gen(", sizeof(%Q.m%d));", tuple_name, i); + } + + else { + genln("%Q.m%d = ", tuple_name, i); + gen_expr(it); + gen(";"); + } + + i += 1; + } + gen_line(node); + genln("return %Q;", tuple_name); #if 0 gen("return ("); gen_simple_decl(node->resolved_type); @@ -592,233 +614,234 @@ gen_ast(Ast *ast){ genln(""); gen("};"); #endif - return; - } + return; + } - assert(node->expr.len <= 1); - gen("return "); - For(node->expr) gen_expr(it); - gen(";"); - BREAK(); - } - - CASE(VAR, Decl){ - gen_var(node, is_inside_struct ? DONT_EMIT_VALUE : ALWAYS_EMIT_VALUE, true); - if(!is_flag_set(ast->flags, AST_EXPR)) gen(";"); - BREAK(); - } - - CASE(IF, If){ - For(node->ifs){ - gen_line(it); - genln(""); - if(it->init) { - gen_expr(it->init); - gen(";"); - genln(""); - } - if(node->ifs.is_first(&it)){ - gen("if("); - gen_expr(it->expr); - gen(")"); - gen_stmt_scope(it->scope); - } - else{ - gen("else"); - if(it->expr){ - gen(" if("); - gen_expr(it->expr); - gen(")"); - } - gen_stmt_scope(it->scope); - } - } - BREAK(); - } - - - CASE(BREAK, Break){ - unused(node); - gen("break;"); - BREAK(); - } - - - CASE(PASS, Pass){ - unused(node); - gen("//pass"); - BREAK(); - } - - CASE(BINARY,Binary){ - gen_expr(node); - gen(";"); - BREAK(); - } - - CASE(FOR, For){ - - // Array iter - if(node->is_array_traversal){ - gen("for(int64_t _i%d = 0; _i%d < ", node->pos->line, node->pos->line); - if(is_array(node->cond->resolved_type)){ - gen("%QBufferSize(", pctx->symbol_prefix); - gen_expr(node->cond); - gen(")"); - } else{ - assert(is_slice(node->cond->resolved_type)); - gen_expr(node->cond); - gen(".len"); + assert(node->expr.len <= 1); + gen("return "); + For(node->expr) gen_expr(it); + gen(";"); + BREAK(); } - - gen("; _i%d+=1)", node->pos->line); - gen("{"); - global_indent++; - genln(""); - gen_simple_decl(node->array_traversal_var->type, node->array_traversal_var->name); - gen(" = "); - gen_expr(node->cond); - if(node->is_also_slice_traversal) gen(".data"); - gen(" + _i%d;", node->pos->line); - For(node->scope->stmts) { - gen_line(it); - genln(""); - gen_ast(it); - } - global_indent--; - gen_last_line(); - genln("}"); - } - - // Normal for loop - else{ - gen("for("); - if(node->init) gen_expr(node->init); - gen(";"); - if(node->cond) gen_expr(node->cond); - gen(";"); - if(node->iter) gen_expr(node->iter); - gen(")"); - gen_stmt_scope(node->scope); - } - - BREAK(); - } - - CASE(LAMBDA, Decl){ - gen_line(node); - genln(""); - - if(is_flag_set(node->expr->flags, AST_FOREIGN)){ - gen("/*foreign*/"); - } - gen_lambda(node->unique_name, node->lambda); - BREAK(); - } - - CASE(STRUCT, Decl){ - gen("struct "); - gen("%Q{", node->unique_name); - global_indent++; - is_inside_struct++; - For(node->scope->decls){ - genln(""); - gen_ast(it); - } - - is_inside_struct--; - global_indent--; - genln("};"); - BREAK(); - } - - CASE(ENUM, Decl){ - gen("/*enum %Q{", node->name); - // @todo add typespec - global_indent++; - For(node->scope->decls){ - genln("%Q", it->name); - gen(" = "); - gen_value(it->pos, it->value); - gen(","); - } - global_indent--; - genln("};*/"); - BREAK(); - } - - case AST_TYPE: - CASE(CONST, Decl){unused(node);BREAK();} - - CASE(SWITCH, Switch){ - gen("switch("); - gen_expr(node->value); - gen("){"); - global_indent++; - - For(node->cases){ - For_Named(it->labels, label){ - gen_line(it); - genln(""); - gen("case "); - gen_expr(label); - gen(":"); + CASE(VAR, Decl) { + gen_var(node, is_inside_struct ? DONT_EMIT_VALUE : ALWAYS_EMIT_VALUE, true); + if (!is_flag_set(ast->flags, AST_EXPR)) gen(";"); + BREAK(); } - gen_stmt_scope(it->scope, it->fallthrough ? false : true); - } + CASE(IF, If) { + For(node->ifs) { + gen_line(it); + genln(""); + if (it->init) { + gen_expr(it->init); + gen(";"); + genln(""); + } + if (node->ifs.is_first(&it)) { + gen("if("); + gen_expr(it->expr); + gen(")"); + gen_stmt_scope(it->scope); + } + else { + gen("else"); + if (it->expr) { + gen(" if("); + gen_expr(it->expr); + gen(")"); + } + gen_stmt_scope(it->scope); + } + } + BREAK(); + } - if(node->default_scope){ - genln("default: "); - gen_stmt_scope(node->default_scope); - } + CASE(BREAK, Break) { + unused(node); + gen("break;"); + BREAK(); + } - global_indent--; - gen_last_line(); - genln("}"); + CASE(PASS, Pass) { + unused(node); + gen("//pass"); + BREAK(); + } - BREAK(); + CASE(BINARY, Binary) { + gen_expr(node); + gen(";"); + BREAK(); + } + + CASE(FOR, For) { + + // Array iter + if (node->is_array_traversal) { + gen("for(int64_t _i%d = 0; _i%d < ", node->pos->line, node->pos->line); + if (is_array(node->cond->resolved_type)) { + gen("%QBufferSize(", pctx->symbol_prefix); + gen_expr(node->cond); + gen(")"); + } + else { + assert(is_slice(node->cond->resolved_type)); + gen_expr(node->cond); + gen(".len"); + } + + gen("; _i%d+=1)", node->pos->line); + gen("{"); + global_indent++; + genln(""); + gen_simple_decl(node->array_traversal_var->type, node->array_traversal_var->name); + gen(" = "); + gen_expr(node->cond); + if (node->is_also_slice_traversal) gen(".data"); + gen(" + _i%d;", node->pos->line); + For(node->scope->stmts) { + gen_line(it); + genln(""); + gen_ast(it); + } + global_indent--; + gen_last_line(); + genln("}"); + } + + // Normal for loop + else { + gen("for("); + if (node->init) gen_expr(node->init); + gen(";"); + if (node->cond) gen_expr(node->cond); + gen(";"); + if (node->iter) gen_expr(node->iter); + gen(")"); + gen_stmt_scope(node->scope); + } + + BREAK(); + } + + CASE(LAMBDA, Decl) { + gen_line(node); + genln(""); + + if (is_flag_set(node->expr->flags, AST_FOREIGN)) { + gen("/*foreign*/"); + } + gen_lambda(node->unique_name, node->lambda); + BREAK(); + } + + CASE(STRUCT, Decl) { + gen("struct "); + gen("%Q{", node->unique_name); + global_indent++; + is_inside_struct++; + For(node->scope->decls) { + genln(""); + gen_ast(it); + } + + is_inside_struct--; + global_indent--; + genln("};"); + BREAK(); + } + + CASE(ENUM, Decl) { + gen("/*enum %Q{", node->name); + // @todo add typespec + global_indent++; + For(node->scope->decls) { + genln("%Q", it->name); + gen(" = "); + gen_value(it->pos, it->value); + gen(","); + } + global_indent--; + genln("};*/"); + BREAK(); + } + + case AST_TYPE: + CASE(CONST, Decl) { + unused(node); + BREAK(); + } + + CASE(SWITCH, Switch) { + gen("switch("); + gen_expr(node->value); + gen("){"); + global_indent++; + + For(node->cases) { + For_Named(it->labels, label) { + gen_line(it); + genln(""); + gen("case "); + gen_expr(label); + gen(":"); + } + + gen_stmt_scope(it->scope, it->fallthrough ? false : true); + } + + if (node->default_scope) { + genln("default: "); + gen_stmt_scope(node->default_scope); + } + + global_indent--; + gen_last_line(); + genln("}"); + + BREAK(); + } + + CASE(VAR_UNPACK, Var_Unpack) { + For(node->vars) + gen_ast(it); + + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + + Intern_String var_name = get_unique_name(node); + gen_simple_decl(node->resolved_type, var_name); + gen(" = "); + gen_expr(node->expr); + gen(";"); + + int i = 0; + For(node->vars) { + gen("%QMemoryCopy((void *)&%Q, (void *)&%Q.m%d, sizeof(%Q));", pctx->symbol_prefix, it->name, var_name, i++, it->name); + } + BREAK(); + } + + case AST_CONSTANT_ASSERT: + case AST_NAMESPACE: break; + + default: { + assert(is_flag_set(ast->flags, AST_EXPR)); + gen_expr((Ast_Expr *)ast); + gen(";"); + } } - - CASE(VAR_UNPACK, Var_Unpack){ - For(node->vars) - gen_ast(it); - - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - - Intern_String var_name = get_unique_name(node); - gen_simple_decl(node->resolved_type, var_name); - gen(" = "); - gen_expr(node->expr); - gen(";"); - - int i = 0; - For(node->vars){ - gen("%QMemoryCopy((void *)&%Q, (void *)&%Q.m%d, sizeof(%Q));", pctx->symbol_prefix, it->name, var_name, i++, it->name); - } - BREAK(); - } - - case AST_CONSTANT_ASSERT: - case AST_NAMESPACE:break; - - default: { - assert(is_flag_set(ast->flags, AST_EXPR)); - gen_expr((Ast_Expr *)ast); - gen(";"); - } - } } CORE_Static String -compile_to_c_code(){ - pctx->time.code_generation = os_time(); +compile_to_c_code() { + pctx->time.code_generation = os_time(); - prefixed_string_type = string_fmt(pctx->perm, "%QString", pctx->symbol_prefix); - if(pctx->single_header_library_mode){ - gen(R"( + prefixed_string_type = string_fmt(pctx->perm, "%QString", pctx->symbol_prefix); + if (pctx->single_header_library_mode) { + gen(R"( /* Do this: #define %Q_IMPLEMENTATION @@ -835,13 +858,14 @@ compile_to_c_code(){ You can #define %QAssertMessage(x) to get more comprehensive error info You can #define %QMemoryCopy(x) to avoid using default memory copy */ - )", pctx->single_header_library_name, pctx->single_header_library_name, pctx->single_header_library_name, - pctx->symbol_prefix, pctx->symbol_prefix, pctx->symbol_prefix); - genln("#ifndef %Q_LIBRARY_HEADER ", pctx->single_header_library_name); - genln("#define %Q_LIBRARY_HEADER ", pctx->single_header_library_name); - } + )", + pctx->single_header_library_name, pctx->single_header_library_name, pctx->single_header_library_name, + pctx->symbol_prefix, pctx->symbol_prefix, pctx->symbol_prefix); + genln("#ifndef %Q_LIBRARY_HEADER ", pctx->single_header_library_name); + genln("#define %Q_LIBRARY_HEADER ", pctx->single_header_library_name); + } - gen(R"( + gen(R"( #include #include @@ -872,148 +896,144 @@ compile_to_c_code(){ )"); - // Generate struct forward decls - For(pctx->ordered_decls){ - if(it->kind == AST_STRUCT){ - genln("typedef struct %Q %Q;", it->unique_name, it->unique_name); - } - } - - // Generate slice and tuple types - For_Named(pctx->all_types, type){ - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - - if(type->kind == TYPE_SLICE){ - genln("typedef struct Slice%llu{", type->type_id); - global_indent++; - genln("int64_t len;"); - genln(""); - gen_simple_decl(type_pointer(type->base), pctx->intern("data"_s)); - gen(";"); - global_indent--; - genln("} Slice%llu;", type->type_id); + // Generate struct forward decls + For(pctx->ordered_decls) { + if (it->kind == AST_STRUCT) { + genln("typedef struct %Q %Q;", it->unique_name, it->unique_name); + } } - else if(type->kind == TYPE_TUPLE){ - genln("typedef struct Tuple%llu{", type->type_id); - global_indent++; - For(type->agg.members){ - genln(""); - // @todo remove intern from gen - Intern_String name = pctx->intern(string_fmt(scratch, "m%llu", type->agg.members.get_index(&it))); - gen_simple_decl(it.type, name); - gen(";"); - } - global_indent--; - genln("} Tuple%llu;", type->type_id); - } - } + // Generate slice and tuple types + For_Named(pctx->all_types, type) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); - Intern_String intern_main = pctx->intern("main"_s); - Intern_String intern_win_main = pctx->intern("WinMain"_s); + if (type->kind == TYPE_SLICE) { + genln("typedef struct Slice%llu{", type->type_id); + global_indent++; + genln("int64_t len;"); + genln(""); + gen_simple_decl(type_pointer(type->base), pctx->intern("data"_s)); + gen(";"); + global_indent--; + genln("} Slice%llu;", type->type_id); + } - Ast_Decl *main = 0; - Ast_Decl *win_main = 0; - - // Generate lambda forward decls - For(pctx->ordered_decls){ - if(it->kind == AST_LAMBDA){ - if(it->name == intern_main){ - main = it; - it->unique_name = it->name; - } - - if(it->name == intern_win_main){ - win_main = it; - it->unique_name = it->name; - } - - genln(""); - - gen_lambda(it->unique_name, it->lambda, false); - } - } - - if(!main && !win_main){ - compiler_error(0, "Entry point is not defined! Try main or WinMain"); - } - - - if(pctx->emit_type_info){ - // Generate language.core - for(S32 i = 0; i < pctx->base_language_ordered_decl_len; i++){ - Ast_Decl *it = get(&pctx->ordered_decls, i); - genln(""); - gen_ast(it); + else if (type->kind == TYPE_TUPLE) { + genln("typedef struct Tuple%llu{", type->type_id); + global_indent++; + For(type->agg.members) { + genln(""); + // @todo remove intern from gen + Intern_String name = pctx->intern(string_fmt(scratch, "m%llu", type->agg.members.get_index(&it))); + gen_simple_decl(it.type, name); + gen(";"); + } + global_indent--; + genln("} Tuple%llu;", type->type_id); + } } - // Generate type info - genln("int64_t type_infos_len = %d;", length(&pctx->all_types)); - genln("Type_Info *type_infos = (Type_Info[]){"); - global_indent++; - For_Named(pctx->all_types, t){ - genln("{/*%Q*/.kind = %d, .size = %d, .align = %d, .is_unsigned = %s, .type = %d, ", typestring(t), - (S32)t->kind, (S32)t->size, (S32)t->align, t->is_unsigned ? "true" : "false", t->type_id); - switch(t->kind){ - case TYPE_POINTER: - case TYPE_SLICE: { - gen(".base_type = %d", t->base->type_id); - } break; - case TYPE_ARRAY: { - gen(".base_type = %d, ", t->base->type_id); - gen(".array_size = %d", t->arr.size); - }break; - case TYPE_LAMBDA: { - gen(".lambda_return = %d, ", t->func.ret->type_id); - gen(".lambda_argument_count = %d, ", t->func.args.len); - gen(".lambda_arguments = (Type_Info[%d]){", t->func.args.len); - For_Named(t->func.args, arg){ - gen("{.type = %d}, ", arg->type_id); - } - gen("}"); - } break; - case TYPE_STRUCT:{ - gen(".struct_member_count = %d, ", t->agg.members.len); - gen(".struct_members = (Type_Info_Struct_Member[]){"); - For_Named(t->agg.members, m){ - gen("{.name = (%Q){(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", prefixed_string_type, m.name, m.name.len, m.type->type_id, m.offset); + Intern_String intern_main = pctx->intern("main"_s); + Intern_String intern_win_main = pctx->intern("WinMain"_s); - } - gen("}"); - }break; - default: {} - // invalid_default_case; - } - gen("},"); + Ast_Decl *main = 0; + Ast_Decl *win_main = 0; + + // Generate lambda forward decls + For(pctx->ordered_decls) { + if (it->kind == AST_LAMBDA) { + if (it->name == intern_main) { + main = it; + it->unique_name = it->name; + } + + if (it->name == intern_win_main) { + win_main = it; + it->unique_name = it->name; + } + + genln(""); + + gen_lambda(it->unique_name, it->lambda, false); + } } - global_indent--; - gen("};"); - } - - if(pctx->single_header_library_mode){ - genln("#endif"); - genln("#ifdef %Q_IMPLEMENTATION ", pctx->single_header_library_name); - } - - // Generate actual code - int index = 0; - For(pctx->ordered_decls){ - if(index >= pctx->base_language_ordered_decl_len){ - genln(""); - gen_ast(it); + if (!main && !win_main) { + compiler_error(0, "Entry point is not defined! Try main or WinMain"); } - index += 1; - } + if (pctx->emit_type_info) { + // Generate language.core + for (S32 i = 0; i < pctx->base_language_ordered_decl_len; i++) { + Ast_Decl *it = get(&pctx->ordered_decls, i); + genln(""); + gen_ast(it); + } - if(pctx->single_header_library_mode){ - genln("#endif"); - } + // Generate type info + genln("int64_t type_infos_len = %d;", length(&pctx->all_types)); + genln("Type_Info *type_infos = (Type_Info[]){"); + global_indent++; + For_Named(pctx->all_types, t) { + genln("{/*%Q*/.kind = %d, .size = %d, .align = %d, .is_unsigned = %s, .type = %d, ", typestring(t), + (S32)t->kind, (S32)t->size, (S32)t->align, t->is_unsigned ? "true" : "false", t->type_id); + switch (t->kind) { + case TYPE_POINTER: + case TYPE_SLICE: { + gen(".base_type = %d", t->base->type_id); + } break; + case TYPE_ARRAY: { + gen(".base_type = %d, ", t->base->type_id); + gen(".array_size = %d", t->arr.size); + } break; + case TYPE_LAMBDA: { + gen(".lambda_return = %d, ", t->func.ret->type_id); + gen(".lambda_argument_count = %d, ", t->func.args.len); + gen(".lambda_arguments = (Type_Info[%d]){", t->func.args.len); + For_Named(t->func.args, arg) { + gen("{.type = %d}, ", arg->type_id); + } + gen("}"); + } break; + case TYPE_STRUCT: { + gen(".struct_member_count = %d, ", t->agg.members.len); + gen(".struct_members = (Type_Info_Struct_Member[]){"); + For_Named(t->agg.members, m) { + gen("{.name = (%Q){(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", prefixed_string_type, m.name, m.name.len, m.type->type_id, m.offset); + } + gen("}"); + } break; + default: { + } + // invalid_default_case; + } + gen("},"); + } + global_indent--; + gen("};"); + } + if (pctx->single_header_library_mode) { + genln("#endif"); + genln("#ifdef %Q_IMPLEMENTATION ", pctx->single_header_library_name); + } - String string_result = string_flatten(pctx->perm, &pctx->gen); - pctx->time.code_generation = os_time() - pctx->time.code_generation; - return string_result; + // Generate actual code + int index = 0; + For(pctx->ordered_decls) { + if (index >= pctx->base_language_ordered_decl_len) { + genln(""); + gen_ast(it); + } + index += 1; + } + + if (pctx->single_header_library_mode) { + genln("#endif"); + } + + String string_result = string_flatten(pctx->perm, &pctx->gen); + pctx->time.code_generation = os_time() - pctx->time.code_generation; + return string_result; } diff --git a/core_compiler.cpp b/core_compiler.cpp index 345a961..f51412e 100644 --- a/core_compiler.cpp +++ b/core_compiler.cpp @@ -1,312 +1,312 @@ -//apifn +// apifn static void core_free_compiler() { - deallocate(pctx->heap, pctx->type_map.data); - deallocate(pctx->heap, pctx->interns.map.data); - arena_release(pctx->stage_arena); - arena_release(pctx->scratch); - arena_release(pctx->perm); + deallocate(pctx->heap, pctx->type_map.data); + deallocate(pctx->heap, pctx->interns.map.data); + arena_release(pctx->stage_arena); + arena_release(pctx->scratch); + arena_release(pctx->perm); } -//apifn +// apifn static void core_init_compiler(Core_Ctx *ctx, Allocator *allocator) { - ctx->time.init_context = os_time(); - pctx = ctx; + ctx->time.init_context = os_time(); + pctx = ctx; - ctx->emit_type_info = true; - ctx->emit_line_directives = true; - ctx->debugger_break_on_compiler_error = true; - ctx->color_codes_enabled = true; - ctx->same_scope_token = { SAME_SCOPE }; + ctx->emit_type_info = true; + ctx->emit_line_directives = true; + ctx->debugger_break_on_compiler_error = true; + ctx->color_codes_enabled = true; + ctx->same_scope_token = {SAME_SCOPE}; - arena_init(&ctx->perm_push_only, "Perm Push Only"_s); - arena_init(&ctx->scratch_, "Scratch"_s); - arena_init(&ctx->stage_arena_, "Stage Arena"_s); - ctx->scratch = &ctx->scratch_; - ctx->stage_arena = &ctx->stage_arena_; - ctx->perm = &ctx->perm_push_only; - ctx->heap = allocator; - ctx->type_map = map_make(ctx->heap, 2048); - ctx->gen = {ctx->perm}; - ctx->helper_builder = {ctx->perm}; - ctx->scope_ids = 1; - bigint_allocator = ctx->perm; + arena_init(&ctx->perm_push_only, "Perm Push Only"_s); + arena_init(&ctx->scratch_, "Scratch"_s); + arena_init(&ctx->stage_arena_, "Stage Arena"_s); + ctx->scratch = &ctx->scratch_; + ctx->stage_arena = &ctx->stage_arena_; + ctx->perm = &ctx->perm_push_only; + ctx->heap = allocator; + ctx->type_map = map_make(ctx->heap, 2048); + ctx->gen = {ctx->perm}; + ctx->helper_builder = {ctx->perm}; + ctx->scope_ids = 1; + bigint_allocator = ctx->perm; - ctx->interns = intern_table_make(ctx->perm, ctx->heap, 2048); + ctx->interns = intern_table_make(ctx->perm, ctx->heap, 2048); -/*#import meta -for i in meta.keywords: - print(f'pctx->keyword_{i.lower()} = pctx->intern("{i}"_s);') -print(f'pctx->interns.first_keyword = pctx->keyword_{meta.keywords[0].lower()}.str;') -print(f'pctx->interns.last_keyword = pctx->keyword_{meta.keywords[-1].lower()}.str;') + /*#import meta + for i in meta.keywords: + print(f'pctx->keyword_{i.lower()} = pctx->intern("{i}"_s);') + print(f'pctx->interns.first_keyword = pctx->keyword_{meta.keywords[0].lower()}.str;') + print(f'pctx->interns.last_keyword = pctx->keyword_{meta.keywords[-1].lower()}.str;') -for i in meta.interns: - print(f'pctx->intern_{i.lower()} = pctx->intern("{i}"_s);') + for i in meta.interns: + print(f'pctx->intern_{i.lower()} = pctx->intern("{i}"_s);') -index = 0 -for i in meta.token_simple_expr: - if i[1] != "SPECIAL": - print(f'pctx->op_info_table[{index}] = {{pctx->intern("{i[1]}"_s), "{i[0].upper()}"_s, TK_{i[0]}, {int(i[2]&meta.BINARY_EXPR>0)}, {int(i[2]&meta.UNARY_EXPR>0)}}};') - index += 1 + index = 0 + for i in meta.token_simple_expr: + if i[1] != "SPECIAL": + print(f'pctx->op_info_table[{index}] = {{pctx->intern("{i[1]}"_s), "{i[0].upper()}"_s, TK_{i[0]}, {int(i[2]&meta.BINARY_EXPR>0)}, {int(i[2]&meta.UNARY_EXPR>0)}}};') + index += 1 -*/ -pctx->keyword_struct = pctx->intern("struct"_s); -pctx->keyword_union = pctx->intern("union"_s); -pctx->keyword_true = pctx->intern("true"_s); -pctx->keyword_default = pctx->intern("default"_s); -pctx->keyword_break = pctx->intern("break"_s); -pctx->keyword_false = pctx->intern("false"_s); -pctx->keyword_return = pctx->intern("return"_s); -pctx->keyword_switch = pctx->intern("switch"_s); -pctx->keyword_assert = pctx->intern("Assert"_s); -pctx->keyword_if = pctx->intern("if"_s); -pctx->keyword_elif = pctx->intern("elif"_s); -pctx->keyword_pass = pctx->intern("pass"_s); -pctx->keyword_else = pctx->intern("else"_s); -pctx->keyword_for = pctx->intern("for"_s); -pctx->keyword_enum = pctx->intern("enum"_s); -pctx->interns.first_keyword = pctx->keyword_struct.str; -pctx->interns.last_keyword = pctx->keyword_enum.str; -pctx->intern_typeof = pctx->intern("TypeOf"_s); -pctx->intern_sizeof = pctx->intern("SizeOf"_s); -pctx->intern_len = pctx->intern("Len"_s); -pctx->intern_alignof = pctx->intern("AlignOf"_s); -pctx->intern_foreign = pctx->intern("foreign"_s); -pctx->intern_strict = pctx->intern("strict"_s); -pctx->intern_void = pctx->intern("void"_s); -pctx->intern_flag = pctx->intern("flag"_s); -pctx->intern_it = pctx->intern("it"_s); -pctx->intern_load = pctx->intern("load"_s); -pctx->intern_import = pctx->intern("import"_s); -pctx->intern_link = pctx->intern("link"_s); -pctx->op_info_table[0] = {pctx->intern("*"_s), "MUL"_s, TK_Mul, 1, 0}; -pctx->op_info_table[1] = {pctx->intern("/"_s), "DIV"_s, TK_Div, 1, 0}; -pctx->op_info_table[2] = {pctx->intern("%"_s), "MOD"_s, TK_Mod, 1, 0}; -pctx->op_info_table[3] = {pctx->intern("<<"_s), "LEFTSHIFT"_s, TK_LeftShift, 1, 0}; -pctx->op_info_table[4] = {pctx->intern(">>"_s), "RIGHTSHIFT"_s, TK_RightShift, 1, 0}; -pctx->op_info_table[5] = {pctx->intern("+"_s), "ADD"_s, TK_Add, 1, 1}; -pctx->op_info_table[6] = {pctx->intern("-"_s), "SUB"_s, TK_Sub, 1, 1}; -pctx->op_info_table[7] = {pctx->intern("=="_s), "EQUALS"_s, TK_Equals, 1, 0}; -pctx->op_info_table[8] = {pctx->intern("<="_s), "LESSERTHENOREQUAL"_s, TK_LesserThenOrEqual, 1, 0}; -pctx->op_info_table[9] = {pctx->intern(">="_s), "GREATERTHENOREQUAL"_s, TK_GreaterThenOrEqual, 1, 0}; -pctx->op_info_table[10] = {pctx->intern("<"_s), "LESSERTHEN"_s, TK_LesserThen, 1, 0}; -pctx->op_info_table[11] = {pctx->intern(">"_s), "GREATERTHEN"_s, TK_GreaterThen, 1, 0}; -pctx->op_info_table[12] = {pctx->intern("!="_s), "NOTEQUALS"_s, TK_NotEquals, 1, 0}; -pctx->op_info_table[13] = {pctx->intern("&"_s), "BITAND"_s, TK_BitAnd, 1, 0}; -pctx->op_info_table[14] = {pctx->intern("|"_s), "BITOR"_s, TK_BitOr, 1, 0}; -pctx->op_info_table[15] = {pctx->intern("^"_s), "BITXOR"_s, TK_BitXor, 1, 0}; -pctx->op_info_table[16] = {pctx->intern("&&"_s), "AND"_s, TK_And, 1, 0}; -pctx->op_info_table[17] = {pctx->intern("||"_s), "OR"_s, TK_Or, 1, 0}; -pctx->op_info_table[18] = {pctx->intern("~"_s), "NEG"_s, TK_Neg, 0, 1}; -pctx->op_info_table[19] = {pctx->intern("!"_s), "NOT"_s, TK_Not, 0, 1}; -/*END*/ + */ + pctx->keyword_struct = pctx->intern("struct"_s); + pctx->keyword_union = pctx->intern("union"_s); + pctx->keyword_true = pctx->intern("true"_s); + pctx->keyword_default = pctx->intern("default"_s); + pctx->keyword_break = pctx->intern("break"_s); + pctx->keyword_false = pctx->intern("false"_s); + pctx->keyword_return = pctx->intern("return"_s); + pctx->keyword_switch = pctx->intern("switch"_s); + pctx->keyword_assert = pctx->intern("Assert"_s); + pctx->keyword_if = pctx->intern("if"_s); + pctx->keyword_elif = pctx->intern("elif"_s); + pctx->keyword_pass = pctx->intern("pass"_s); + pctx->keyword_else = pctx->intern("else"_s); + pctx->keyword_for = pctx->intern("for"_s); + pctx->keyword_enum = pctx->intern("enum"_s); + pctx->interns.first_keyword = pctx->keyword_struct.str; + pctx->interns.last_keyword = pctx->keyword_enum.str; + pctx->intern_typeof = pctx->intern("TypeOf"_s); + pctx->intern_sizeof = pctx->intern("SizeOf"_s); + pctx->intern_len = pctx->intern("Len"_s); + pctx->intern_alignof = pctx->intern("AlignOf"_s); + pctx->intern_foreign = pctx->intern("foreign"_s); + pctx->intern_strict = pctx->intern("strict"_s); + pctx->intern_void = pctx->intern("void"_s); + pctx->intern_flag = pctx->intern("flag"_s); + pctx->intern_it = pctx->intern("it"_s); + pctx->intern_load = pctx->intern("load"_s); + pctx->intern_import = pctx->intern("import"_s); + pctx->intern_link = pctx->intern("link"_s); + pctx->op_info_table[0] = {pctx->intern("*"_s), "MUL"_s, TK_Mul, 1, 0}; + pctx->op_info_table[1] = {pctx->intern("/"_s), "DIV"_s, TK_Div, 1, 0}; + pctx->op_info_table[2] = {pctx->intern("%"_s), "MOD"_s, TK_Mod, 1, 0}; + pctx->op_info_table[3] = {pctx->intern("<<"_s), "LEFTSHIFT"_s, TK_LeftShift, 1, 0}; + pctx->op_info_table[4] = {pctx->intern(">>"_s), "RIGHTSHIFT"_s, TK_RightShift, 1, 0}; + pctx->op_info_table[5] = {pctx->intern("+"_s), "ADD"_s, TK_Add, 1, 1}; + pctx->op_info_table[6] = {pctx->intern("-"_s), "SUB"_s, TK_Sub, 1, 1}; + pctx->op_info_table[7] = {pctx->intern("=="_s), "EQUALS"_s, TK_Equals, 1, 0}; + pctx->op_info_table[8] = {pctx->intern("<="_s), "LESSERTHENOREQUAL"_s, TK_LesserThenOrEqual, 1, 0}; + pctx->op_info_table[9] = {pctx->intern(">="_s), "GREATERTHENOREQUAL"_s, TK_GreaterThenOrEqual, 1, 0}; + pctx->op_info_table[10] = {pctx->intern("<"_s), "LESSERTHEN"_s, TK_LesserThen, 1, 0}; + pctx->op_info_table[11] = {pctx->intern(">"_s), "GREATERTHEN"_s, TK_GreaterThen, 1, 0}; + pctx->op_info_table[12] = {pctx->intern("!="_s), "NOTEQUALS"_s, TK_NotEquals, 1, 0}; + pctx->op_info_table[13] = {pctx->intern("&"_s), "BITAND"_s, TK_BitAnd, 1, 0}; + pctx->op_info_table[14] = {pctx->intern("|"_s), "BITOR"_s, TK_BitOr, 1, 0}; + pctx->op_info_table[15] = {pctx->intern("^"_s), "BITXOR"_s, TK_BitXor, 1, 0}; + pctx->op_info_table[16] = {pctx->intern("&&"_s), "AND"_s, TK_And, 1, 0}; + pctx->op_info_table[17] = {pctx->intern("||"_s), "OR"_s, TK_Or, 1, 0}; + pctx->op_info_table[18] = {pctx->intern("~"_s), "NEG"_s, TK_Neg, 0, 1}; + pctx->op_info_table[19] = {pctx->intern("!"_s), "NOT"_s, TK_Not, 0, 1}; + /*END*/ - // Init types - pctx->type__void = {TYPE_VOID}; - // pctx->type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; - pctx->type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; - pctx->type__type = {TYPE_TYPE, sizeof(S64), __alignof(S64)}; + // Init types + pctx->type__void = {TYPE_VOID}; + // pctx->type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; + pctx->type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; + pctx->type__type = {TYPE_TYPE, sizeof(S64), __alignof(S64)}; - pctx->type__f32 = {TYPE_F32, sizeof(F32), __alignof(F32)}; - pctx->type__f64 = {TYPE_F64, sizeof(F64), __alignof(F64)}; + pctx->type__f32 = {TYPE_F32, sizeof(F32), __alignof(F32)}; + pctx->type__f64 = {TYPE_F64, sizeof(F64), __alignof(F64)}; - pctx->type__s8 = {TYPE_S8, sizeof(S8), __alignof(S8)}; - pctx->type__s16 = {TYPE_S16, sizeof(S16), __alignof(S16)}; - pctx->type__s32 = {TYPE_S32, sizeof(S32), __alignof(S32)}; - pctx->type__s64 = {TYPE_S64, sizeof(S64), __alignof(S64)}; + pctx->type__s8 = {TYPE_S8, sizeof(S8), __alignof(S8)}; + pctx->type__s16 = {TYPE_S16, sizeof(S16), __alignof(S16)}; + pctx->type__s32 = {TYPE_S32, sizeof(S32), __alignof(S32)}; + pctx->type__s64 = {TYPE_S64, sizeof(S64), __alignof(S64)}; - pctx->type__u8 = {TYPE_U8, sizeof(U8), __alignof(U8), true}; - pctx->type__u16 = {TYPE_U16, sizeof(U16), __alignof(U16), true}; - pctx->type__u32 = {TYPE_U32, sizeof(U32), __alignof(U32), true}; - pctx->type__u64 = {TYPE_U64, sizeof(U64), __alignof(U64), true}; + pctx->type__u8 = {TYPE_U8, sizeof(U8), __alignof(U8), true}; + pctx->type__u16 = {TYPE_U16, sizeof(U16), __alignof(U16), true}; + pctx->type__u32 = {TYPE_U32, sizeof(U32), __alignof(U32), true}; + pctx->type__u64 = {TYPE_U64, sizeof(U64), __alignof(U64), true}; - pctx->type__untyped_bool = {TYPE_UNTYPED_BOOL, sizeof(bool), __alignof(bool)}; - pctx->type__untyped_int = {TYPE_UNTYPED_INT, sizeof(S64), __alignof(S64)}; - pctx->type__untyped_string = {TYPE_UNTYPED_STRING, sizeof(String), __alignof(String)}; - pctx->type__untyped_float = {TYPE_UNTYPED_FLOAT, sizeof(double), __alignof(double)}; + pctx->type__untyped_bool = {TYPE_UNTYPED_BOOL, sizeof(bool), __alignof(bool)}; + pctx->type__untyped_int = {TYPE_UNTYPED_INT, sizeof(S64), __alignof(S64)}; + pctx->type__untyped_string = {TYPE_UNTYPED_STRING, sizeof(String), __alignof(String)}; + pctx->type__untyped_float = {TYPE_UNTYPED_FLOAT, sizeof(double), __alignof(double)}; - pctx->type__char = {TYPE_CHAR, sizeof(char), __alignof(char)}; - pctx->type__int = {TYPE_INT, sizeof(int), __alignof(int)}; + pctx->type__char = {TYPE_CHAR, sizeof(char), __alignof(char)}; + pctx->type__int = {TYPE_INT, sizeof(int), __alignof(int)}; - pctx->type_char = &pctx->type__char; - pctx->type_int = &pctx->type__int; - pctx->type_void = &pctx->type__void; + pctx->type_char = &pctx->type__char; + pctx->type_int = &pctx->type__int; + pctx->type_void = &pctx->type__void; - //pctx->type_any; // Needs to be inited at runtime + // pctx->type_any; // Needs to be inited at runtime - pctx->type_type = &pctx->type__type; - // pctx->type_string = &pctx->type__string; - pctx->type_bool = &pctx->type__bool; + pctx->type_type = &pctx->type__type; + // pctx->type_string = &pctx->type__string; + pctx->type_bool = &pctx->type__bool; - pctx->type_f32 = &pctx->type__f32; - pctx->type_f64 = &pctx->type__f64; + pctx->type_f32 = &pctx->type__f32; + pctx->type_f64 = &pctx->type__f64; - pctx->type_s8 = &pctx->type__s8 ; - pctx->type_s16 = &pctx->type__s16; - pctx->type_s32 = &pctx->type__s32; - pctx->type_s64 = &pctx->type__s64; + pctx->type_s8 = &pctx->type__s8; + pctx->type_s16 = &pctx->type__s16; + pctx->type_s32 = &pctx->type__s32; + pctx->type_s64 = &pctx->type__s64; - pctx->type_u8 = &pctx->type__u8 ; - pctx->type_u16 = &pctx->type__u16; - pctx->type_u32 = &pctx->type__u32; - pctx->type_u64 = &pctx->type__u64; + pctx->type_u8 = &pctx->type__u8; + pctx->type_u16 = &pctx->type__u16; + pctx->type_u32 = &pctx->type__u32; + pctx->type_u64 = &pctx->type__u64; - pctx->untyped_string = &pctx->type__untyped_string; - pctx->untyped_bool = &pctx->type__untyped_bool; - pctx->untyped_int = &pctx->type__untyped_int; - pctx->untyped_float = &pctx->type__untyped_float; + pctx->untyped_string = &pctx->type__untyped_string; + pctx->untyped_bool = &pctx->type__untyped_bool; + pctx->untyped_int = &pctx->type__untyped_int; + pctx->untyped_float = &pctx->type__untyped_float; - pctx->type_pointer_to_char = type_pointer(pctx->type_char); - pctx->type_pointer_to_void = type_pointer(pctx->type_void); + pctx->type_pointer_to_char = type_pointer(pctx->type_char); + pctx->type_pointer_to_void = type_pointer(pctx->type_void); - // Init paths - ctx->exe_folder = os_get_exe_dir(ctx->perm); - ctx->working_folder = os_get_working_dir(ctx->perm); + // Init paths + ctx->exe_folder = os_get_exe_dir(ctx->perm); + ctx->working_folder = os_get_working_dir(ctx->perm); - String main_module = string_fmt(ctx->perm, "%Q/modules", ctx->exe_folder); - add(ctx->perm, &ctx->module_folders, main_module); - ctx->time.init_context = os_time() - ctx->time.init_context; + String main_module = string_fmt(ctx->perm, "%Q/modules", ctx->exe_folder); + add(ctx->perm, &ctx->module_folders, main_module); + ctx->time.init_context = os_time() - ctx->time.init_context; } -//apifn +// apifn CORE_Static void core_bootstrap_compiler(Allocator *allocator) { - Core_Ctx *ctx = allocate_struct(allocator, Core_Ctx); - core_init_compiler(ctx, allocator); + Core_Ctx *ctx = allocate_struct(allocator, Core_Ctx); + core_init_compiler(ctx, allocator); } CORE_Static void insert_builtin_type_into_scope(Ast_Scope *p, String name, Ast_Type *type) { - Intern_String string = pctx->intern(name); - Ast_Decl * decl = ast_type(0, string, type); - type->type_id = pctx->type_ids++; - decl->parent_scope = p; - decl->state = DECL_RESOLVED; - insert_into_scope(p, decl); - add(pctx->perm, &pctx->all_types, type); + Intern_String string = pctx->intern(name); + Ast_Decl *decl = ast_type(0, string, type); + type->type_id = pctx->type_ids++; + decl->parent_scope = p; + decl->state = DECL_RESOLVED; + insert_into_scope(p, decl); + add(pctx->perm, &pctx->all_types, type); } CORE_Static void parse_all_modules() { - pctx->time.parsing = os_time(); + pctx->time.parsing = os_time(); - For_Named(pctx->modules, module) { - if (module->state != MODULE_REGISTERED) - continue; + For_Named(pctx->modules, module) { + if (module->state != MODULE_REGISTERED) + continue; - For(module->all_loaded_files) { - parse_file(it); + For(module->all_loaded_files) { + parse_file(it); + } + + if (module != pctx->language_base_module) { + add(pctx->perm, &module->implicit_imports, (Ast_Scope *)pctx->language_base_module); + } + + module->state = MODULE_PARSED; } - - if (module != pctx->language_base_module) { - add(pctx->perm, &module->implicit_imports, (Ast_Scope *)pctx->language_base_module); - } - - module->state = MODULE_PARSED; - } - pctx->time.parsing = os_time() - pctx->time.parsing; + pctx->time.parsing = os_time() - pctx->time.parsing; } CORE_Static Ast_Module * add_module(Token *pos, Intern_String filename, B32 command_line_module, bool string_only_module) { - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - String absolute_file_path = {}; - String absolute_base_folder = {}; + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + String absolute_file_path = {}; + String absolute_base_folder = {}; - if (string_only_module) { - absolute_file_path = filename.s; - absolute_base_folder = filename.s; - } - else { - // - // Find in working directory - // - if (command_line_module) { - if (os_does_file_exist(filename.s)) { - String path = os_get_absolute_path(scratch, filename.s); - string_path_normalize(path); - absolute_file_path = string_copy(scratch, path); - absolute_base_folder = string_chop_last_slash(path); - } + if (string_only_module) { + absolute_file_path = filename.s; + absolute_base_folder = filename.s; } - - // - // Find in module folder - // else { - For(pctx->module_folders) { - String path = string_fmt(scratch, "%Q/%Q", it, filename); - if (os_does_file_exist(path)) { - absolute_file_path = path; - absolute_base_folder = string_chop_last_slash(path); - break; + // + // Find in working directory + // + if (command_line_module) { + if (os_does_file_exist(filename.s)) { + String path = os_get_absolute_path(scratch, filename.s); + string_path_normalize(path); + absolute_file_path = string_copy(scratch, path); + absolute_base_folder = string_chop_last_slash(path); + } + } + + // + // Find in module folder + // + else { + For(pctx->module_folders) { + String path = string_fmt(scratch, "%Q/%Q", it, filename); + if (os_does_file_exist(path)) { + absolute_file_path = path; + absolute_base_folder = string_chop_last_slash(path); + break; + } + } + } + + if (absolute_file_path.len == 0) { + compiler_error(pos, "Couldn't find the module with name %Q", filename); } - } } - if (absolute_file_path.len == 0) { - compiler_error(pos, "Couldn't find the module with name %Q", filename); + For(pctx->modules) { + if (string_compare(it->absolute_file_path, absolute_file_path)) { + log_trace("Returning registered module: %Q\n", absolute_file_path); + return it; + } } - } - For(pctx->modules) { - if (string_compare(it->absolute_file_path, absolute_file_path)) { - log_trace("Returning registered module: %Q\n", absolute_file_path); - return it; - } - } + log_trace("Adding module: %Q\n", filename); + Ast_Module *result = ast_new(Ast_Module, AST_MODULE, pos, 0); + result->absolute_file_path = string_copy(pctx->perm, absolute_file_path); + result->absolute_base_folder = string_copy(pctx->perm, absolute_base_folder); + result->debug_name = string_skip_to_last_slash(result->absolute_file_path); + result->module = result; // @warning: self referential + result->file = result; // @warning: self referential + result->parent_scope = 0; + result->scope_id = pctx->scope_ids++; - log_trace("Adding module: %Q\n", filename); - Ast_Module *result = ast_new(Ast_Module, AST_MODULE, pos, 0); - result->absolute_file_path = string_copy(pctx->perm, absolute_file_path); - result->absolute_base_folder = string_copy(pctx->perm, absolute_base_folder); - result->debug_name = string_skip_to_last_slash(result->absolute_file_path); - result->module = result; // @warning: self referential - result->file = result; // @warning: self referential - result->parent_scope = 0; - result->scope_id = pctx->scope_ids++; - - register_ast_file(pos, result->absolute_file_path, result, GLOBAL_IMPLICIT_LOAD); - add(pctx->perm, &pctx->modules, result); - return result; + register_ast_file(pos, result->absolute_file_path, result, GLOBAL_IMPLICIT_LOAD); + add(pctx->perm, &pctx->modules, result); + return result; } CORE_Static Ast_File * custom_parse_string(String string) { - Ast_Module *module = pctx->custom_module; - Ast_File *file = get(&module->all_loaded_files, 0); - file->filecontent = string; - parse_file(file); - return file; + Ast_Module *module = pctx->custom_module; + Ast_File *file = get(&module->all_loaded_files, 0); + file->filecontent = string; + parse_file(file); + return file; } CORE_Static void resolve_everything_in_module(Ast_Module *module) { - if (module->state == MODULE_RESOLVED) - return; - pctx->time.typechecking = os_time(); - For_Named(module->all_loaded_files, file) { - For_Named(file->decls, decl) { - resolve_name(file, decl->pos, decl->name); + if (module->state == MODULE_RESOLVED) + return; + pctx->time.typechecking = os_time(); + For_Named(module->all_loaded_files, file) { + For_Named(file->decls, decl) { + resolve_name(file, decl->pos, decl->name); - if (decl->kind == AST_STRUCT) { - type_complete(decl->type_val); - } + if (decl->kind == AST_STRUCT) { + type_complete(decl->type_val); + } + } } - } - module->state = MODULE_RESOLVED; - pctx->time.typechecking = os_time() - pctx->time.typechecking; + module->state = MODULE_RESOLVED; + pctx->time.typechecking = os_time() - pctx->time.typechecking; } CORE_Static void init_language_core() { - pctx->custom_module = add_module(0, pctx->intern("Custom.core"_s), false, true); - pctx->custom_module->state = MODULE_RESOLVED; + pctx->custom_module = add_module(0, pctx->intern("Custom.core"_s), false, true); + pctx->custom_module->state = MODULE_RESOLVED; - Ast_Module *module = add_module(0, pctx->intern("Language.core"_s), false, true); - get(&module->all_loaded_files, 0)->filecontent = - R"( + Ast_Module *module = add_module(0, pctx->intern("Language.core"_s), false, true); + get(&module->all_loaded_files, 0)->filecontent = + R"( Any :: struct data: *void type: Type @@ -371,80 +371,80 @@ GetTypeInfo :: (type: Type): *Type_Info return type_infos + id )"_s; - { - insert_builtin_type_into_scope(module, "S64"_s, pctx->type_s64); - insert_builtin_type_into_scope(module, "S32"_s, pctx->type_s32); - insert_builtin_type_into_scope(module, "S16"_s, pctx->type_s16); - insert_builtin_type_into_scope(module, "S8"_s, pctx->type_s8); - insert_builtin_type_into_scope(module, "int"_s, pctx->type_int); - insert_builtin_type_into_scope(module, "char"_s, pctx->type_char); - insert_builtin_type_into_scope(module, "U64"_s, pctx->type_u64); - insert_builtin_type_into_scope(module, "U32"_s, pctx->type_u32); - insert_builtin_type_into_scope(module, "U16"_s, pctx->type_u16); - insert_builtin_type_into_scope(module, "U8"_s, pctx->type_u8); - insert_builtin_type_into_scope(module, "F64"_s, pctx->type_f64); - insert_builtin_type_into_scope(module, "F32"_s, pctx->type_f32); - insert_builtin_type_into_scope(module, "void"_s, pctx->type_void); - insert_builtin_type_into_scope(module, "Bool"_s, pctx->type_bool); - // insert_builtin_type_into_scope(module, "String"_s, pctx->type_string); - insert_builtin_type_into_scope(module, "Type"_s, pctx->type_type); - } + { + insert_builtin_type_into_scope(module, "S64"_s, pctx->type_s64); + insert_builtin_type_into_scope(module, "S32"_s, pctx->type_s32); + insert_builtin_type_into_scope(module, "S16"_s, pctx->type_s16); + insert_builtin_type_into_scope(module, "S8"_s, pctx->type_s8); + insert_builtin_type_into_scope(module, "int"_s, pctx->type_int); + insert_builtin_type_into_scope(module, "char"_s, pctx->type_char); + insert_builtin_type_into_scope(module, "U64"_s, pctx->type_u64); + insert_builtin_type_into_scope(module, "U32"_s, pctx->type_u32); + insert_builtin_type_into_scope(module, "U16"_s, pctx->type_u16); + insert_builtin_type_into_scope(module, "U8"_s, pctx->type_u8); + insert_builtin_type_into_scope(module, "F64"_s, pctx->type_f64); + insert_builtin_type_into_scope(module, "F32"_s, pctx->type_f32); + insert_builtin_type_into_scope(module, "void"_s, pctx->type_void); + insert_builtin_type_into_scope(module, "Bool"_s, pctx->type_bool); + // insert_builtin_type_into_scope(module, "String"_s, pctx->type_string); + insert_builtin_type_into_scope(module, "Type"_s, pctx->type_type); + } - { - Ast_Scope *scope = ast_decl_scope(&pctx->null_token, pctx->perm, get(&module->all_loaded_files, 0)); - Ast_Decl * decl = ast_namespace(&pctx->null_token, scope, pctx->intern("Const"_s)); - decl->state = DECL_RESOLVED; + { + Ast_Scope *scope = ast_decl_scope(&pctx->null_token, pctx->perm, get(&module->all_loaded_files, 0)); + Ast_Decl *decl = ast_namespace(&pctx->null_token, scope, pctx->intern("Const"_s)); + decl->state = DECL_RESOLVED; - Value v1 = {}; - v1.type = pctx->untyped_string; - v1.intern_val = pctx->intern(OS_NAME); - Ast_Decl *const_os1 = ast_const(&pctx->null_token, pctx->intern("OSName"_s), v1); - const_os1->state = DECL_RESOLVED; - insert_into_scope(scope, const_os1); + Value v1 = {}; + v1.type = pctx->untyped_string; + v1.intern_val = pctx->intern(OS_NAME); + Ast_Decl *const_os1 = ast_const(&pctx->null_token, pctx->intern("OSName"_s), v1); + const_os1->state = DECL_RESOLVED; + insert_into_scope(scope, const_os1); - Value v2 = {}; - v1.type = pctx->untyped_string; - v1.intern_val = pctx->intern(OS_NAME_LOWER); - Ast_Decl *const_os2 = ast_const(&pctx->null_token, pctx->intern("OSNameLower"_s), v2); - const_os2->state = DECL_RESOLVED; - insert_into_scope(scope, const_os2); + Value v2 = {}; + v1.type = pctx->untyped_string; + v1.intern_val = pctx->intern(OS_NAME_LOWER); + Ast_Decl *const_os2 = ast_const(&pctx->null_token, pctx->intern("OSNameLower"_s), v2); + const_os2->state = DECL_RESOLVED; + insert_into_scope(scope, const_os2); - insert_into_scope(module, decl); - } + insert_into_scope(module, decl); + } - pctx->language_base_module = module; + pctx->language_base_module = module; - parse_all_modules(); - resolve_everything_in_module(module); + parse_all_modules(); + resolve_everything_in_module(module); - // @note: language stuff needs to be declared before type_info data - // so we mark where it ends - pctx->base_language_ordered_decl_len = length(&pctx->ordered_decls); - Ast_Decl *any_decl = search_for_single_decl(module, pctx->intern("Any"_s)); - assert(any_decl->type == pctx->type_type); - pctx->type_any = any_decl->type_val; + // @note: language stuff needs to be declared before type_info data + // so we mark where it ends + pctx->base_language_ordered_decl_len = length(&pctx->ordered_decls); + Ast_Decl *any_decl = search_for_single_decl(module, pctx->intern("Any"_s)); + assert(any_decl->type == pctx->type_type); + pctx->type_any = any_decl->type_val; - Ast_Decl *string_decl = search_for_single_decl(module, pctx->intern("String"_s)); - assert(string_decl->type == pctx->type_type); - pctx->type_string = string_decl->type_val; + Ast_Decl *string_decl = search_for_single_decl(module, pctx->intern("String"_s)); + assert(string_decl->type == pctx->type_type); + pctx->type_string = string_decl->type_val; } CORE_Static String compile_file_to_string(Allocator *allocator, String filename) { - F64 total_time = os_time(); - core_bootstrap_compiler(allocator); - pctx->time.total = total_time; - pctx->time.start = total_time; - init_language_core(); + F64 total_time = os_time(); + core_bootstrap_compiler(allocator); + pctx->time.total = total_time; + pctx->time.start = total_time; + init_language_core(); - Ast_Module *module = add_module(0, pctx->intern(filename), true); - parse_all_modules(); - assert(module); + Ast_Module *module = add_module(0, pctx->intern(filename), true); + parse_all_modules(); + assert(module); - resolve_everything_in_module(module); + resolve_everything_in_module(module); - String result = compile_to_c_code(); + String result = compile_to_c_code(); - pctx->time.total = os_time() - pctx->time.total; - return result; + pctx->time.total = os_time() - pctx->time.total; + return result; } diff --git a/core_compiler.h b/core_compiler.h index e6322a8..23cd428 100644 --- a/core_compiler.h +++ b/core_compiler.h @@ -1,195 +1,195 @@ -struct Lex_Stream{ - String stream; - S64 iter; +struct Lex_Stream { + String stream; + S64 iter; - U8 *line_begin; - Intern_String file; - S32 line; - S32 inside_brace_paren; - Array indent_stack; // @scratch_allocated + U8 *line_begin; + Intern_String file; + S32 line; + S32 inside_brace_paren; + Array indent_stack; // @scratch_allocated }; -struct Core_Ctx{ - Allocator *heap; +struct Core_Ctx { + Allocator *heap; - Arena perm_push_only; - Arena *perm; // Stores: AST, tokens, interns + Arena perm_push_only; + Arena *perm; // Stores: AST, tokens, interns - Arena *scratch; - Arena scratch_; - Arena stage_arena_; - Arena *stage_arena; - String_Builder helper_builder; + Arena *scratch; + Arena scratch_; + Arena stage_arena_; + Arena *stage_arena; + String_Builder helper_builder; - int errors_occured; - int warnings_occured; - Core_Message *first_message; - Core_Message *last_message; + int errors_occured; + int warnings_occured; + Core_Message *first_message; + Core_Message *last_message; - // Lexer stuff - Lex_Stream stream; - Array tokens; - Intern_Table interns; - S64 token_iter; - U32 token_debug_ids; + // Lexer stuff + Lex_Stream stream; + Array tokens; + Intern_Table interns; + S64 token_iter; + U32 token_debug_ids; - // Types - List all_types; - S32 type_ids; - int lambda_ids; - U64 unique_ids; // @Debug - Map type_map; + // Types + List all_types; + S32 type_ids; + int lambda_ids; + U64 unique_ids; // @Debug + Map type_map; - Ast_Module *custom_module; - Ast_Module *language_base_module; + Ast_Module *custom_module; + Ast_Module *language_base_module; - List files; - List modules; - List ordered_decls; - S32 base_language_ordered_decl_len; + List files; + List modules; + List ordered_decls; + S32 base_language_ordered_decl_len; - Ast_Scope *currently_parsed_scope; - Ast_File *currently_parsed_file; - U32 scope_ids; - U32 scope_visit_id; + Ast_Scope *currently_parsed_scope; + Ast_File *currently_parsed_file; + U32 scope_ids; + U32 scope_visit_id; - List module_folders; - String module_folder; - String exe_folder; - String working_folder; - List files_to_link; + List module_folders; + String module_folder; + String exe_folder; + String working_folder; + List files_to_link; - struct { - F64 typechecking; - F64 code_generation; - F64 total; - F64 init_context; - F64 parsing; + struct { + F64 typechecking; + F64 code_generation; + F64 total; + F64 init_context; + F64 parsing; - F64 start; - } time; + F64 start; + } time; - bool color_codes_enabled; - bool debugger_break_on_compiler_error; + bool color_codes_enabled; + bool debugger_break_on_compiler_error; - // Codegen stage mostly - S64 indent; - String_Builder gen; + // Codegen stage mostly + S64 indent; + String_Builder gen; - // Codegen stage configurables - bool emit_line_directives; - bool emit_type_info; - String symbol_prefix; - bool single_header_library_mode; - String single_header_library_name; + // Codegen stage configurables + bool emit_line_directives; + bool emit_type_info; + String symbol_prefix; + bool single_header_library_mode; + String single_header_library_name; - Token same_scope_token; - Token null_token; + Token same_scope_token; + Token null_token; -/*#import meta -for i in meta.keywords: print(f'Intern_String keyword_{i.lower()};') -for i in meta.interns: print(f'Intern_String intern_{i.lower()};') -*/ -Intern_String keyword_struct; -Intern_String keyword_union; -Intern_String keyword_true; -Intern_String keyword_default; -Intern_String keyword_break; -Intern_String keyword_false; -Intern_String keyword_return; -Intern_String keyword_switch; -Intern_String keyword_assert; -Intern_String keyword_if; -Intern_String keyword_elif; -Intern_String keyword_pass; -Intern_String keyword_else; -Intern_String keyword_for; -Intern_String keyword_enum; -Intern_String intern_typeof; -Intern_String intern_sizeof; -Intern_String intern_len; -Intern_String intern_alignof; -Intern_String intern_foreign; -Intern_String intern_strict; -Intern_String intern_void; -Intern_String intern_flag; -Intern_String intern_it; -Intern_String intern_load; -Intern_String intern_import; -Intern_String intern_link; -/*END*/ + /*#import meta + for i in meta.keywords: print(f'Intern_String keyword_{i.lower()};') + for i in meta.interns: print(f'Intern_String intern_{i.lower()};') + */ + Intern_String keyword_struct; + Intern_String keyword_union; + Intern_String keyword_true; + Intern_String keyword_default; + Intern_String keyword_break; + Intern_String keyword_false; + Intern_String keyword_return; + Intern_String keyword_switch; + Intern_String keyword_assert; + Intern_String keyword_if; + Intern_String keyword_elif; + Intern_String keyword_pass; + Intern_String keyword_else; + Intern_String keyword_for; + Intern_String keyword_enum; + Intern_String intern_typeof; + Intern_String intern_sizeof; + Intern_String intern_len; + Intern_String intern_alignof; + Intern_String intern_foreign; + Intern_String intern_strict; + Intern_String intern_void; + Intern_String intern_flag; + Intern_String intern_it; + Intern_String intern_load; + Intern_String intern_import; + Intern_String intern_link; + /*END*/ - /*#import meta -size = 0 -for i in meta.token_simple_expr: - if i[1] != "SPECIAL": - size += 1 -print(f"Ast_Operator_Info op_info_table[{size}];") -*/ -Ast_Operator_Info op_info_table[20]; -/*END*/ + /*#import meta + size = 0 + for i in meta.token_simple_expr: + if i[1] != "SPECIAL": + size += 1 + print(f"Ast_Operator_Info op_info_table[{size}];") + */ + Ast_Operator_Info op_info_table[20]; + /*END*/ - Ast_Type type__void; - Ast_Type type__string; - Ast_Type type__bool; - Ast_Type type__type; + Ast_Type type__void; + Ast_Type type__string; + Ast_Type type__bool; + Ast_Type type__type; - Ast_Type type__f32; - Ast_Type type__f64; + Ast_Type type__f32; + Ast_Type type__f64; - Ast_Type type__s8 ; - Ast_Type type__s16; - Ast_Type type__s32; - Ast_Type type__s64; + Ast_Type type__s8; + Ast_Type type__s16; + Ast_Type type__s32; + Ast_Type type__s64; - Ast_Type type__u8 ; - Ast_Type type__u16; - Ast_Type type__u32; - Ast_Type type__u64; + Ast_Type type__u8; + Ast_Type type__u16; + Ast_Type type__u32; + Ast_Type type__u64; - Ast_Type type__untyped_bool; - Ast_Type type__untyped_int; - Ast_Type type__untyped_string; - Ast_Type type__untyped_float; + Ast_Type type__untyped_bool; + Ast_Type type__untyped_int; + Ast_Type type__untyped_string; + Ast_Type type__untyped_float; - Ast_Type type__char; - Ast_Type type__int; + Ast_Type type__char; + Ast_Type type__int; - Ast_Type *type_char = &type__char; - Ast_Type *type_int = &type__int; - Ast_Type *type_void = &type__void; + Ast_Type *type_char = &type__char; + Ast_Type *type_int = &type__int; + Ast_Type *type_void = &type__void; - Ast_Type *type_pointer_to_char; - Ast_Type *type_pointer_to_void; - Ast_Type *type_any; // Needs to be inited at runtime + Ast_Type *type_pointer_to_char; + Ast_Type *type_pointer_to_void; + Ast_Type *type_any; // Needs to be inited at runtime - Ast_Type *type_type = &type__type; - Ast_Type *type_string = &type__string; - Ast_Type *type_bool = &type__bool; + Ast_Type *type_type = &type__type; + Ast_Type *type_string = &type__string; + Ast_Type *type_bool = &type__bool; - Ast_Type *type_f32 = &type__f32; - Ast_Type *type_f64 = &type__f64; + Ast_Type *type_f32 = &type__f32; + Ast_Type *type_f64 = &type__f64; - Ast_Type *type_s8 = &type__s8 ; - Ast_Type *type_s16 = &type__s16; - Ast_Type *type_s32 = &type__s32; - Ast_Type *type_s64 = &type__s64; + Ast_Type *type_s8 = &type__s8; + Ast_Type *type_s16 = &type__s16; + Ast_Type *type_s32 = &type__s32; + Ast_Type *type_s64 = &type__s64; - Ast_Type *type_u8 = &type__u8 ; - Ast_Type *type_u16 = &type__u16; - Ast_Type *type_u32 = &type__u32; - Ast_Type *type_u64 = &type__u64; + Ast_Type *type_u8 = &type__u8; + Ast_Type *type_u16 = &type__u16; + Ast_Type *type_u32 = &type__u32; + Ast_Type *type_u64 = &type__u64; - Ast_Type *untyped_string = &type__untyped_string; - Ast_Type *untyped_bool = &type__untyped_bool; - Ast_Type *untyped_int = &type__untyped_int; - Ast_Type *untyped_float = &type__untyped_float; + Ast_Type *untyped_string = &type__untyped_string; + Ast_Type *untyped_bool = &type__untyped_bool; + Ast_Type *untyped_int = &type__untyped_int; + Ast_Type *untyped_float = &type__untyped_float; - Intern_String intern(String string){ - assert(string.len > 0); - return intern_string(&interns, string); - } + Intern_String intern(String string) { + assert(string.len > 0); + return intern_string(&interns, string); + } }; CORE_Static String compile_to_c_code(); diff --git a/core_compiler_includes.cpp b/core_compiler_includes.cpp index 4a4bca0..838c224 100644 --- a/core_compiler_includes.cpp +++ b/core_compiler_includes.cpp @@ -8,11 +8,11 @@ #include "os.h" #if OS_WINDOWS -#include "os_windows.cpp" + #include "os_windows.cpp" #elif OS_LINUX -#include "os_linux.cpp" + #include "os_linux.cpp" #else -#error Couldnt figure out OS using macros + #error Couldnt figure out OS using macros #endif #include "core_compiler_interface.hpp" diff --git a/core_generated.cpp b/core_generated.cpp index 5a21ce3..f4684c2 100644 --- a/core_generated.cpp +++ b/core_generated.cpp @@ -26,55 +26,57 @@ print(" return 0;\n}") */ CORE_Static Ast_Operator_Info * -get_operator_info(Token_Kind op){ - switch(op){ - case TK_Mul: return pctx->op_info_table + 0; - case TK_Div: return pctx->op_info_table + 1; - case TK_Mod: return pctx->op_info_table + 2; - case TK_LeftShift: return pctx->op_info_table + 3; - case TK_RightShift: return pctx->op_info_table + 4; - case TK_Add: return pctx->op_info_table + 5; - case TK_Sub: return pctx->op_info_table + 6; - case TK_Equals: return pctx->op_info_table + 7; - case TK_LesserThenOrEqual: return pctx->op_info_table + 8; - case TK_GreaterThenOrEqual: return pctx->op_info_table + 9; - case TK_LesserThen: return pctx->op_info_table + 10; - case TK_GreaterThen: return pctx->op_info_table + 11; - case TK_NotEquals: return pctx->op_info_table + 12; - case TK_BitAnd: return pctx->op_info_table + 13; - case TK_BitOr: return pctx->op_info_table + 14; - case TK_BitXor: return pctx->op_info_table + 15; - case TK_And: return pctx->op_info_table + 16; - case TK_Or: return pctx->op_info_table + 17; - case TK_Neg: return pctx->op_info_table + 18; - case TK_Not: return pctx->op_info_table + 19; - default: {} - } - return 0; +get_operator_info(Token_Kind op) { + switch (op) { + case TK_Mul: return pctx->op_info_table + 0; + case TK_Div: return pctx->op_info_table + 1; + case TK_Mod: return pctx->op_info_table + 2; + case TK_LeftShift: return pctx->op_info_table + 3; + case TK_RightShift: return pctx->op_info_table + 4; + case TK_Add: return pctx->op_info_table + 5; + case TK_Sub: return pctx->op_info_table + 6; + case TK_Equals: return pctx->op_info_table + 7; + case TK_LesserThenOrEqual: return pctx->op_info_table + 8; + case TK_GreaterThenOrEqual: return pctx->op_info_table + 9; + case TK_LesserThen: return pctx->op_info_table + 10; + case TK_GreaterThen: return pctx->op_info_table + 11; + case TK_NotEquals: return pctx->op_info_table + 12; + case TK_BitAnd: return pctx->op_info_table + 13; + case TK_BitOr: return pctx->op_info_table + 14; + case TK_BitXor: return pctx->op_info_table + 15; + case TK_And: return pctx->op_info_table + 16; + case TK_Or: return pctx->op_info_table + 17; + case TK_Neg: return pctx->op_info_table + 18; + case TK_Not: return pctx->op_info_table + 19; + default: { + } + } + return 0; } CORE_Static Ast_Operator_Info * -get_operator_info(Intern_String op){ - if(0){} - else if(pctx->op_info_table[0].op.str == op.str) return pctx->op_info_table + 0; - else if(pctx->op_info_table[1].op.str == op.str) return pctx->op_info_table + 1; - else if(pctx->op_info_table[2].op.str == op.str) return pctx->op_info_table + 2; - else if(pctx->op_info_table[3].op.str == op.str) return pctx->op_info_table + 3; - else if(pctx->op_info_table[4].op.str == op.str) return pctx->op_info_table + 4; - else if(pctx->op_info_table[5].op.str == op.str) return pctx->op_info_table + 5; - else if(pctx->op_info_table[6].op.str == op.str) return pctx->op_info_table + 6; - else if(pctx->op_info_table[7].op.str == op.str) return pctx->op_info_table + 7; - else if(pctx->op_info_table[8].op.str == op.str) return pctx->op_info_table + 8; - else if(pctx->op_info_table[9].op.str == op.str) return pctx->op_info_table + 9; - else if(pctx->op_info_table[10].op.str == op.str) return pctx->op_info_table + 10; - else if(pctx->op_info_table[11].op.str == op.str) return pctx->op_info_table + 11; - else if(pctx->op_info_table[12].op.str == op.str) return pctx->op_info_table + 12; - else if(pctx->op_info_table[13].op.str == op.str) return pctx->op_info_table + 13; - else if(pctx->op_info_table[14].op.str == op.str) return pctx->op_info_table + 14; - else if(pctx->op_info_table[15].op.str == op.str) return pctx->op_info_table + 15; - else if(pctx->op_info_table[16].op.str == op.str) return pctx->op_info_table + 16; - else if(pctx->op_info_table[17].op.str == op.str) return pctx->op_info_table + 17; - else if(pctx->op_info_table[18].op.str == op.str) return pctx->op_info_table + 18; - else if(pctx->op_info_table[19].op.str == op.str) return pctx->op_info_table + 19; - return 0; +get_operator_info(Intern_String op) { + if (0) { + } + else if (pctx->op_info_table[0].op.str == op.str) return pctx->op_info_table + 0; + else if (pctx->op_info_table[1].op.str == op.str) return pctx->op_info_table + 1; + else if (pctx->op_info_table[2].op.str == op.str) return pctx->op_info_table + 2; + else if (pctx->op_info_table[3].op.str == op.str) return pctx->op_info_table + 3; + else if (pctx->op_info_table[4].op.str == op.str) return pctx->op_info_table + 4; + else if (pctx->op_info_table[5].op.str == op.str) return pctx->op_info_table + 5; + else if (pctx->op_info_table[6].op.str == op.str) return pctx->op_info_table + 6; + else if (pctx->op_info_table[7].op.str == op.str) return pctx->op_info_table + 7; + else if (pctx->op_info_table[8].op.str == op.str) return pctx->op_info_table + 8; + else if (pctx->op_info_table[9].op.str == op.str) return pctx->op_info_table + 9; + else if (pctx->op_info_table[10].op.str == op.str) return pctx->op_info_table + 10; + else if (pctx->op_info_table[11].op.str == op.str) return pctx->op_info_table + 11; + else if (pctx->op_info_table[12].op.str == op.str) return pctx->op_info_table + 12; + else if (pctx->op_info_table[13].op.str == op.str) return pctx->op_info_table + 13; + else if (pctx->op_info_table[14].op.str == op.str) return pctx->op_info_table + 14; + else if (pctx->op_info_table[15].op.str == op.str) return pctx->op_info_table + 15; + else if (pctx->op_info_table[16].op.str == op.str) return pctx->op_info_table + 16; + else if (pctx->op_info_table[17].op.str == op.str) return pctx->op_info_table + 17; + else if (pctx->op_info_table[18].op.str == op.str) return pctx->op_info_table + 18; + else if (pctx->op_info_table[19].op.str == op.str) return pctx->op_info_table + 19; + return 0; } /*END*/ diff --git a/core_globals.cpp b/core_globals.cpp index fbd64ea..05c69a7 100644 --- a/core_globals.cpp +++ b/core_globals.cpp @@ -3,8 +3,5 @@ thread_local Core_Ctx *pctx; Allocator *bigint_allocator; global S64 bigint_allocation_count; -const uintptr_t pointer_size = sizeof(uintptr_t); +const uintptr_t pointer_size = sizeof(uintptr_t); const uintptr_t pointer_align = __alignof(uintptr_t); - - - diff --git a/core_lexing.cpp b/core_lexing.cpp index f1c1c5c..b44c8fa 100644 --- a/core_lexing.cpp +++ b/core_lexing.cpp @@ -1,609 +1,692 @@ -force_inline B32 token_is_assign(Token_Kind token){return token >= TK_FirstAssign && token <= TK_LastAssign;} -force_inline B32 token_is_assign(Token *token){return token_is_assign(token->kind);} -force_inline B32 token_is_compare(Token_Kind token){return token >= TK_FirstCompare && token <= TK_LastCompare;} -force_inline B32 token_is_compare(Token *token){return token_is_compare(token->kind);} +force_inline B32 token_is_assign(Token_Kind token) { return token >= TK_FirstAssign && token <= TK_LastAssign; } +force_inline B32 token_is_assign(Token *token) { return token_is_assign(token->kind); } +force_inline B32 token_is_compare(Token_Kind token) { return token >= TK_FirstCompare && token <= TK_LastCompare; } +force_inline B32 token_is_compare(Token *token) { return token_is_compare(token->kind); } CORE_Static U8 -lexc(Lex_Stream *s){ - return s->stream.str[s->iter]; +lexc(Lex_Stream *s) { + return s->stream.str[s->iter]; } CORE_Static U8 -lexci(Lex_Stream *s, S32 i){ - return s->stream.str[s->iter+i]; +lexci(Lex_Stream *s, S32 i) { + return s->stream.str[s->iter + i]; } CORE_Static U8 * -lexcp(Lex_Stream *s){ - return s->stream.str + s->iter; +lexcp(Lex_Stream *s) { + return s->stream.str + s->iter; } CORE_Static B32 -lex_is_whitespace(U8 c){ - B32 result = c == ' ' || c == '\r'; - return result; +lex_is_whitespace(U8 c) { + B32 result = c == ' ' || c == '\r'; + return result; } CORE_Static B32 -lex_is_alphabetic(U8 c){ - B32 result = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); - return result; +lex_is_alphabetic(U8 c) { + B32 result = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + return result; } CORE_Static B32 -lex_is_numeric(U8 c){ - B32 result = c >= '0' && c <= '9'; - return result; +lex_is_numeric(U8 c) { + B32 result = c >= '0' && c <= '9'; + return result; } CORE_Static B32 -lex_is_numeric_base16(U8 c){ - B32 result = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || - (c >= 'a' && c <= 'f'); - return result; +lex_is_numeric_base16(U8 c) { + B32 result = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f'); + return result; } CORE_Static B32 -lex_is_alphanumeric(U8 c){ - B32 result = lex_is_numeric(c) || lex_is_alphabetic(c); - return result; +lex_is_alphanumeric(U8 c) { + B32 result = lex_is_numeric(c) || lex_is_alphabetic(c); + return result; } CORE_Static void -lex_set_len(Lex_Stream *s, Token *token){ - assert(lexcp(s) >= token->str); - token->len = lexcp(s) - token->str; +lex_set_len(Lex_Stream *s, Token *token) { + assert(lexcp(s) >= token->str); + token->len = lexcp(s) - token->str; } CORE_Static void -lex_set_keywords(Core_Ctx *lexer, Array keywords){ - Intern_String keyword = {}; - For(keywords){ - keyword = intern_string(&lexer->interns, it); - if(&it == keywords.begin()) - lexer->interns.first_keyword = keyword.str; - } - lexer->interns.last_keyword = keyword.str; -} - -CORE_Static B32 -lex_is_keyword(Intern_Table *lexer, Intern_String keyword){ - B32 result = keyword.str >= lexer->first_keyword && keyword.str <= lexer->last_keyword; - return result; -} - -CORE_Static void -token_error(Token *t, String error_val){ - t->kind = TK_Error; - t->error_val = error_val; -} - -CORE_Static void -lex_parse_u64(Core_Ctx *lexer, Token *t, S64 base){ - Scratch_Scope _scope(lexer->scratch); - Set_BigInt_Arena(lexer->scratch); - - t->kind = TK_Integer; - BigInt m = bigint_u64(1); - BigInt base_mul = bigint_u64(base); - BigInt result = bigint_u64(0); - - for(S64 i = t->len - 1; i >= 0; --i){ - U64 value = t->str[i]; - if(t->str[i] >= 'a') value = value - 'a' + 10; - else if(t->str[i] >= 'A') value = value - 'A' + 10; - else value -= '0'; - - BigInt val = bigint_u64(value); - BigInt new_val = bigint_mul(&val, &m); - result = bigint_add(&result, &new_val); - m = bigint_mul(&m, &base_mul); - } - - t->int_val = bigint_copy(lexer->perm, &result); -} - -CORE_Static void -lex_parse_f64(Token *t){ - t->kind = TK_Float; - char buffer[128]; - S64 len = clamp_top((int)t->len, 126); - memory_copy(buffer, t->str, len); - buffer[len] = 0; - t->f64_val = strtod(buffer, 0); -} - -CORE_Static void -lex_advance(Lex_Stream *s){ - if(s->iter >= s->stream.len){ - return; - } - else if(lexc(s) == '\n'){ - s->iter++; - s->line++; - s->line_begin = lexcp(s); - } - else{ - s->iter++; - } -} - -CORE_Static void -lex_parse_string(Lex_Stream *s, Token *t, U8 c){ - for(;;){ - if(lexc(s) == '\\') lex_advance(s); - else if(lexc(s) == c) break; - else if(lexc(s) == 0){ - token_error(t, "Unterminated string, reached end of file"_s); - break; +lex_set_keywords(Core_Ctx *lexer, Array keywords) { + Intern_String keyword = {}; + For(keywords) { + keyword = intern_string(&lexer->interns, it); + if (&it == keywords.begin()) + lexer->interns.first_keyword = keyword.str; } - lex_advance(s); - } - if(t->kind != TK_Error){ - lex_advance(s); - lex_set_len(s,t); - } + lexer->interns.last_keyword = keyword.str; +} + +CORE_Static B32 +lex_is_keyword(Intern_Table *lexer, Intern_String keyword) { + B32 result = keyword.str >= lexer->first_keyword && keyword.str <= lexer->last_keyword; + return result; } CORE_Static void -lex_parse_ident(Intern_Table *table, Lex_Stream *s, Token *t){ - while(lex_is_alphanumeric(lexc(s)) || lexc(s) == '_') - lex_advance(s); - lex_set_len(s,t); +token_error(Token *t, String error_val) { + t->kind = TK_Error; + t->error_val = error_val; } -#define CASE2(op, OpName, Assign) \ - case op: \ - if (lexc(s) == '=') { \ - lex_advance(s); \ - t.kind = Assign; \ - } else { \ - t.kind = OpName; \ - } \ - break -#define CASE3(op, OpName, Assign, Incr) \ - case op: \ - if (lexc(s) == '=') { \ - lex_advance(s); \ - t.kind = Assign; \ - } else if (lexc(s) == op) { \ - lex_advance(s); \ - t.kind = Incr; \ - } else { \ - t.kind = OpName; \ - } \ - break +CORE_Static void +lex_parse_u64(Core_Ctx *lexer, Token *t, S64 base) { + Scratch_Scope _scope(lexer->scratch); + Set_BigInt_Arena(lexer->scratch); + + t->kind = TK_Integer; + BigInt m = bigint_u64(1); + BigInt base_mul = bigint_u64(base); + BigInt result = bigint_u64(0); + + for (S64 i = t->len - 1; i >= 0; --i) { + U64 value = t->str[i]; + if (t->str[i] >= 'a') value = value - 'a' + 10; + else if (t->str[i] >= 'A') value = value - 'A' + 10; + else value -= '0'; + + BigInt val = bigint_u64(value); + BigInt new_val = bigint_mul(&val, &m); + result = bigint_add(&result, &new_val); + m = bigint_mul(&m, &base_mul); + } + + t->int_val = bigint_copy(lexer->perm, &result); +} + +CORE_Static void +lex_parse_f64(Token *t) { + t->kind = TK_Float; + char buffer[128]; + S64 len = clamp_top((int)t->len, 126); + memory_copy(buffer, t->str, len); + buffer[len] = 0; + t->f64_val = strtod(buffer, 0); +} + +CORE_Static void +lex_advance(Lex_Stream *s) { + if (s->iter >= s->stream.len) { + return; + } + else if (lexc(s) == '\n') { + s->iter++; + s->line++; + s->line_begin = lexcp(s); + } + else { + s->iter++; + } +} + +CORE_Static void +lex_parse_string(Lex_Stream *s, Token *t, U8 c) { + for (;;) { + if (lexc(s) == '\\') lex_advance(s); + else if (lexc(s) == c) break; + else if (lexc(s) == 0) { + token_error(t, "Unterminated string, reached end of file"_s); + break; + } + lex_advance(s); + } + if (t->kind != TK_Error) { + lex_advance(s); + lex_set_len(s, t); + } +} + +CORE_Static void +lex_parse_ident(Intern_Table *table, Lex_Stream *s, Token *t) { + while (lex_is_alphanumeric(lexc(s)) || lexc(s) == '_') + lex_advance(s); + lex_set_len(s, t); +} + +#define CASE2(op, OpName, Assign) \ + case op: \ + if (lexc(s) == '=') { \ + lex_advance(s); \ + t.kind = Assign; \ + } \ + else { \ + t.kind = OpName; \ + } \ + break +#define CASE3(op, OpName, Assign, Incr) \ + case op: \ + if (lexc(s) == '=') { \ + lex_advance(s); \ + t.kind = Assign; \ + } \ + else if (lexc(s) == op) { \ + lex_advance(s); \ + t.kind = Incr; \ + } \ + else { \ + t.kind = OpName; \ + } \ + break CORE_Static Token -token_make(Core_Ctx *lexer, U8 *str, Intern_String file, int line, U8 *line_begin){ - Token t = {}; - t.str = str; - t.file = file; - t.line = line; - t.line_begin = line_begin; - t.di = lexer->token_debug_ids++; - return t; +token_make(Core_Ctx *lexer, U8 *str, Intern_String file, int line, U8 *line_begin) { + Token t = {}; + t.str = str; + t.file = file; + t.line = line; + t.line_begin = line_begin; + t.di = lexer->token_debug_ids++; + return t; } CORE_Static Token -token_make(Core_Ctx *lexer){ - return token_make(lexer, lexcp(&lexer->stream), lexer->stream.file, lexer->stream.line, lexer->stream.line_begin); +token_make(Core_Ctx *lexer) { + return token_make(lexer, lexcp(&lexer->stream), lexer->stream.file, lexer->stream.line, lexer->stream.line_begin); } CORE_Static Token * -lex_last_indent_token(Lex_Stream *s){ - if (s->indent_stack.len > 0) { - return *s->indent_stack.last(); - } - return &pctx->same_scope_token; +lex_last_indent_token(Lex_Stream *s) { + if (s->indent_stack.len > 0) { + return *s->indent_stack.last(); + } + return &pctx->same_scope_token; } CORE_Static B32 -lex_is_scope(Token *t){ - B32 result = t->kind == OPEN_SCOPE || t->kind == CLOSE_SCOPE || t->kind == SAME_SCOPE; - return result; +lex_is_scope(Token *t) { + B32 result = t->kind == OPEN_SCOPE || t->kind == CLOSE_SCOPE || t->kind == SAME_SCOPE; + return result; } CORE_Static void lex_add_token(Core_Ctx *ctx, Token *token) { - Token *top = (Token *)arena_push_size(ctx->stage_arena, sizeof(Token)); - *top = *token; - ctx->tokens.len += 1; - ctx->tokens.cap += 1; - ctx->tokens.data = (Token *)ctx->stage_arena->memory.data; + Token *top = (Token *)arena_push_size(ctx->stage_arena, sizeof(Token)); + *top = *token; + ctx->tokens.len += 1; + ctx->tokens.cap += 1; + ctx->tokens.data = (Token *)ctx->stage_arena->memory.data; } CORE_Static void -lex_unwind_indent_stack(Core_Ctx *ctx, Token *t, Lex_Stream *s){ - for(S64 i = s->indent_stack.len-1; i >= 0; i-=1){ - auto it = s->indent_stack.data[i]; - assert(lex_is_scope(it)); - if(it->indent == t->indent){ - t->kind = SAME_SCOPE; - lex_add_token(ctx, t); - break; +lex_unwind_indent_stack(Core_Ctx *ctx, Token *t, Lex_Stream *s) { + for (S64 i = s->indent_stack.len - 1; i >= 0; i -= 1) { + auto it = s->indent_stack.data[i]; + assert(lex_is_scope(it)); + if (it->indent == t->indent) { + t->kind = SAME_SCOPE; + lex_add_token(ctx, t); + break; + } + else if (it->indent < t->indent) { + token_error(t, "Bad indentation"_s); + lex_add_token(ctx, t); + break; + } + else { + s->indent_stack.pop(); + t->kind = CLOSE_SCOPE; + lex_add_token(ctx, t); + } } - else if(it->indent < t->indent){ - token_error(t, "Bad indentation"_s); - lex_add_token(ctx, t); - break; - } - else{ - s->indent_stack.pop(); - t->kind = CLOSE_SCOPE; - lex_add_token(ctx, t); - } - } } CORE_Static void -lex__stream(Core_Ctx *lexer){ - Intern_Table *table = &lexer->interns; - Lex_Stream *s = &lexer->stream; +lex__stream(Core_Ctx *lexer) { + Intern_Table *table = &lexer->interns; + Lex_Stream *s = &lexer->stream; - B32 beginning = true; - for(;;){ - if(lexc(s) == 0 || s->iter >= s->stream.len){ - end_of_stream: - Token t = token_make(lexer); - lex_unwind_indent_stack(lexer, &t, s); - break; + B32 beginning = true; + for (;;) { + if (lexc(s) == 0 || s->iter >= s->stream.len) { + end_of_stream: + Token t = token_make(lexer); + lex_unwind_indent_stack(lexer, &t, s); + break; + } + + // @note: the lexer is going to be a 2 stage process + // first we tokenize the indentation and then proceed to tokenize + // the good stuff + + // for blocks of stmts we parse till we cant find another new line + // of same scope. + // parse_decl doesn't require preceding new line + // + // in that way new lines act as commas in CORE_Static params + // seeing a comma means that there is a next thing to parse + // and it's easy to parse stuff using a do while loop + + // @note: first handle indentation + // mostly we want to merge multiple new lines + // but for down scopes we want to emit 2 new lines + // that will ease out parsing, one token to break out + // from a block parsing, second to allow continuation of surrounding scope + Token t = token_make(lexer); + B32 should_emit = beginning; + for (;;) { + switch (lexc(s)) { + case 0: goto end_of_stream; break; + case '\t': + case ' ': + lex_advance(s); + t.indent++; + break; + case '\r': lex_advance(s); break; + case '/': { + if (lexci(s, 1) == '/') { + lex_advance(s); + lex_advance(s); + t.kind = TK_Comment; + for (;;) { + if (lexc(s) == '\n' || lexc(s) == 0) break; + lex_advance(s); + } + } + else if (lexci(s, 1) == '*') { + lex_advance(s); + lex_advance(s); + t.kind = TK_Comment; + for (;;) { + if (lexc(s) == '*' && lexci(s, 1) == '/') { + lex_advance(s); + lex_advance(s); + break; + } + else if (lexc(s) == 0) { + token_error(&t, "Unterminated block comment"_s); + break; + } + lex_advance(s); + } + } + else goto indent_loop_break; + } break; + + // @todo: add [;;] operator which adds new scope + // @todo: also need some way to detect indentation so that + // first of all we can check for consistency and second of + // all because we would know by how much to indent + // @todo: after detecting indentation 2 spaces would become 1 indent value + case ';': { + Token semi = token_make(lexer); + Token *last = lex_last_indent_token(s); + semi.indent = last->indent; + lex_advance(s); + if (lexc(s) == ';') { + lex_advance(s); + semi.kind = OPEN_SCOPE; + semi.indent = last->indent + 2; // @todo: proper detection of indentation + lex_add_token(lexer, &semi); + s->indent_stack.add(lexer->tokens.last()); + } + else { + semi.kind = SAME_SCOPE; + lex_add_token(lexer, &semi); + } + } break; + + case '\n': { + lex_advance(s); + should_emit = true; + t = token_make(lexer); + } break; + + default: { + if (s->inside_brace_paren) should_emit = false; + if (should_emit) { + Token *last = lex_last_indent_token(s); + if (t.indent > last->indent) { + t.kind = OPEN_SCOPE; + lex_add_token(lexer, &t); + s->indent_stack.add(lexer->tokens.last()); + } + + else if (t.indent < last->indent) { + lex_unwind_indent_stack(lexer, &t, s); + } + else { + t.kind = SAME_SCOPE; + lex_add_token(lexer, &t); + } + } + + goto indent_loop_break; + } + } + } + indent_loop_break: + beginning = false; + + // @note: handle the indented token + t = token_make(lexer); + lex_advance(s); + switch (*t.str) { + case 0: goto end_of_stream; break; + case '@': t.kind = TK_At; break; + case '(': + s->inside_brace_paren++; + t.kind = TK_OpenParen; + break; + case ')': + s->inside_brace_paren--; + t.kind = TK_CloseParen; + break; + case '{': + s->inside_brace_paren++; + t.kind = TK_OpenBrace; + break; + case '}': + s->inside_brace_paren--; + t.kind = TK_CloseBrace; + break; + case '[': + s->inside_brace_paren++; + t.kind = TK_OpenBracket; + break; + case ']': + s->inside_brace_paren--; + t.kind = TK_CloseBracket; + break; + case ',': t.kind = TK_Comma; break; + case '~': t.kind = TK_Neg; break; + case '?': t.kind = TK_Question; break; + case '^': + t.kind = TK_BitXor; + break; + CASE2('!', TK_Not, TK_NotEquals); + CASE2('=', TK_Assign, TK_Equals); + CASE2('*', TK_Mul, TK_MulAssign); + CASE2('%', TK_Mod, TK_ModAssign); + CASE3('+', TK_Add, TK_AddAssign, TK_Increment); + CASE3('&', TK_BitAnd, TK_AndAssign, TK_And); + CASE3('|', TK_BitOr, TK_OrAssign, TK_Or); + + case '#': { + t.kind = TK_Pound; + lex_parse_ident(table, s, &t); + t.str += 1; + t.len -= 1; + t.intern_val = intern_string(table, t.string); + if (t.len == 0) token_error(&t, "Macro token without content"_s); + } break; + + case '.': { + if (lexc(s) == '.') { + lex_advance(s); + if (lexci(s, 1) == '.') { + lex_advance(s); + t.kind = TK_ThreeDots; + } + else t.kind = TK_TwoDots; + } + else t.kind = TK_Dot; + } break; + + case '\'': { + assert(s->stream.len >= s->iter); + UTF32_Result decode = utf8_to_utf32(lexcp(s), s->stream.len - s->iter); + if (!decode.error) { + for (S32 i = 0; i < decode.advance; i++) lex_advance(s); + t.unicode = decode.out_str; + t.kind = TK_UnicodeLit; + if (lexc(s) == '\'') { + lex_advance(s); + } + else { + token_error(&t, "Unclosed unicode literal"_s); + } + } + else { + token_error(&t, "Invalid UTF8 sequence in unicode literal"_s); + } + } break; + + case '<': { + if (lexc(s) == '<') { + lex_advance(s); + if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_LeftShiftAssign; + } + else { + t.kind = TK_LeftShift; + } + } + else if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_LesserThenOrEqual; + } + else { + t.kind = TK_LesserThen; + } + } break; + + case '>': { + if (lexc(s) == '>') { + lex_advance(s); + if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_RightShiftAssign; + } + else { + t.kind = TK_RightShift; + } + } + else if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_GreaterThenOrEqual; + } + else { + t.kind = TK_GreaterThen; + } + } break; + + case ':': { + if (lexc(s) == ':') { + lex_advance(s); + t.kind = TK_DoubleColon; + } + else if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_ColonAssign; + } + else { + t.kind = TK_Colon; + } + } break; + + case '-': { + if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_SubAssign; + } + else if (lexc(s) == '-') { + lex_advance(s); + t.kind = TK_Decrement; + } + else if (lexc(s) == '>') { + lex_advance(s); + t.kind = TK_Arrow; + } + else { + t.kind = TK_Sub; + } + } break; + + case '"': { + t.kind = TK_StringLit; + lex_parse_string(s, &t, '"'); + if (t.kind != TK_Error) { + t.str += 1; + t.len -= 2; + } + t.intern_val = intern_string(table, t.string); + } break; + + case '/': { + if (lexc(s) == '=') { + t.kind = TK_DivAssign; + lex_advance(s); + } + else { + t.kind = TK_Div; + } + } break; + + case '0': { + if (lexc(s) == 'x') { + lex_advance(s); + while (lex_is_numeric_base16(lexc(s))) + lex_advance(s); + lex_set_len(s, &t); + t.str += 2; + t.len -= 2; + if (t.len == 0) + token_error(&t, "Hex constant doesn't have value"_s); + else + lex_parse_u64(lexer, &t, 16); + break; + } + else if (lexc(s) == 'b') { + lex_advance(s); + while (lexc(s) == '0' || lexc(s) == '1') + lex_advance(s); + lex_set_len(s, &t); + t.str += 2; + t.len -= 2; + if (t.len == 0) + token_error(&t, "Hex constant doesn't have value"_s); + else + lex_parse_u64(lexer, &t, 2); + break; + } + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + B32 found_dot = false; + for (;;) { + if (lex_is_numeric(lexc(s))) + ; + else if (lexc(s) == '.') { + if (found_dot) { + token_error(&t, "Multiple '.' in float literal"_s); + goto end_of_switch; + } + found_dot = true; + } + else break; + + lex_advance(s); + } + lex_set_len(s, &t); + if (found_dot) lex_parse_f64(&t); + else lex_parse_u64(lexer, &t, 10); + + } break; + + case 'A': + case 'a': + case 'M': + case 'm': + case 'B': + case 'b': + case 'N': + case 'n': + case 'C': + case 'c': + case 'O': + case 'o': + case 'D': + case 'd': + case 'P': + case 'p': + case 'E': + case 'e': + case 'Q': + case 'q': + case 'F': + case 'f': + case 'R': + case 'r': + case 'G': + case 'g': + case 'S': + case 's': + case 'H': + case 'h': + case 'T': + case 't': + case 'I': + case 'i': + case 'U': + case 'u': + case 'J': + case 'j': + case 'V': + case 'v': + case 'K': + case 'k': + case 'W': + case 'w': + case 'L': + case 'X': + case 'l': + case 'x': + case 'Z': + case 'z': + case 'Y': + case 'y': + case '_': { + t.kind = TK_Identifier; + lex_parse_ident(table, s, &t); + t.intern_val = intern_string(table, t.string); + if (lex_is_keyword(table, t.intern_val)) { + t.kind = TK_Keyword; + } + } break; + + default: { + token_error(&t, "Unknown token"_s); + } + } + end_of_switch: + + if (t.len == 0) + lex_set_len(s, &t); + + lex_add_token(lexer, &t); } - - // @note: the lexer is going to be a 2 stage process - // first we tokenize the indentation and then proceed to tokenize - // the good stuff - - // for blocks of stmts we parse till we cant find another new line - // of same scope. - // parse_decl doesn't require preceding new line - // - // in that way new lines act as commas in CORE_Static params - // seeing a comma means that there is a next thing to parse - // and it's easy to parse stuff using a do while loop - - // @note: first handle indentation - // mostly we want to merge multiple new lines - // but for down scopes we want to emit 2 new lines - // that will ease out parsing, one token to break out - // from a block parsing, second to allow continuation of surrounding scope - Token t = token_make(lexer); - B32 should_emit = beginning; - for(;;){ - switch(lexc(s)){ - case 0 : goto end_of_stream; break; - case '\t': case ' ': lex_advance(s); t.indent++; break; - case '\r': lex_advance(s); break; - case '/': { - if(lexci(s,1) == '/'){ - lex_advance(s); lex_advance(s); - t.kind = TK_Comment; - for(;;){ - if(lexc(s) == '\n' || lexc(s) == 0) break; - lex_advance(s); - } - } - else if(lexci(s,1) == '*'){ - lex_advance(s); lex_advance(s); - t.kind = TK_Comment; - for(;;){ - if(lexc(s) == '*' && lexci(s,1) == '/'){ - lex_advance(s); lex_advance(s); - break; - } - else if(lexc(s) == 0){ - token_error(&t, "Unterminated block comment"_s); - break; - } - lex_advance(s); - } - } - else goto indent_loop_break; - } break; - - // @todo: add [;;] operator which adds new scope - // @todo: also need some way to detect indentation so that - // first of all we can check for consistency and second of - // all because we would know by how much to indent - // @todo: after detecting indentation 2 spaces would become 1 indent value - case ';' : { - Token semi = token_make(lexer); - Token *last = lex_last_indent_token(s); - semi.indent = last->indent; - lex_advance(s); - if(lexc(s) == ';'){ - lex_advance(s); - semi.kind = OPEN_SCOPE; - semi.indent = last->indent + 2; // @todo: proper detection of indentation - lex_add_token(lexer, &semi); - s->indent_stack.add(lexer->tokens.last()); - } else{ - semi.kind = SAME_SCOPE; - lex_add_token(lexer, &semi); - } - } break; - - case '\n':{ - lex_advance(s); - should_emit = true; - t = token_make(lexer); - } break; - - default:{ - if(s->inside_brace_paren) should_emit = false; - if(should_emit){ - Token *last = lex_last_indent_token(s); - if(t.indent > last->indent){ - t.kind = OPEN_SCOPE; - lex_add_token(lexer, &t); - s->indent_stack.add(lexer->tokens.last()); - } - - else if(t.indent < last->indent){ - lex_unwind_indent_stack(lexer, &t, s); - } - else { - t.kind = SAME_SCOPE; - lex_add_token(lexer, &t); - } - } - - goto indent_loop_break; - } - } - } indent_loop_break: - beginning = false; - - // @note: handle the indented token - t = token_make(lexer); - lex_advance(s); - switch(*t.str){ - case 0 : goto end_of_stream; break; - case '@': t.kind = TK_At; break; - case '(': s->inside_brace_paren++; t.kind = TK_OpenParen; break; - case ')': s->inside_brace_paren--; t.kind = TK_CloseParen; break; - case '{': s->inside_brace_paren++; t.kind = TK_OpenBrace; break; - case '}': s->inside_brace_paren--; t.kind = TK_CloseBrace; break; - case '[': s->inside_brace_paren++; t.kind = TK_OpenBracket; break; - case ']': s->inside_brace_paren--; t.kind = TK_CloseBracket; break; - case ',': t.kind = TK_Comma; break; - case '~': t.kind = TK_Neg; break; - case '?': t.kind = TK_Question; break; - case '^': t.kind = TK_BitXor; break; - CASE2('!', TK_Not, TK_NotEquals); - CASE2('=', TK_Assign, TK_Equals); - CASE2('*', TK_Mul, TK_MulAssign); - CASE2('%', TK_Mod, TK_ModAssign); - CASE3('+', TK_Add, TK_AddAssign, TK_Increment); - CASE3('&', TK_BitAnd, TK_AndAssign, TK_And); - CASE3('|', TK_BitOr, TK_OrAssign, TK_Or); - - case '#': { - t.kind = TK_Pound; - lex_parse_ident(table, s, &t); - t.str += 1; - t.len -= 1; - t.intern_val = intern_string(table, t.string); - if(t.len == 0) token_error(&t, "Macro token without content"_s); - }break; - - case '.': { - if(lexc(s) == '.'){ - lex_advance(s); - if(lexci(s,1) == '.') { - lex_advance(s); - t.kind = TK_ThreeDots; - } - else t.kind = TK_TwoDots; - } - else t.kind = TK_Dot; - } break; - - case '\'':{ - assert(s->stream.len >= s->iter); - UTF32_Result decode = utf8_to_utf32(lexcp(s), s->stream.len - s->iter); - if(!decode.error){ - for(S32 i = 0; i < decode.advance; i++) lex_advance(s); - t.unicode = decode.out_str; - t.kind = TK_UnicodeLit; - if(lexc(s) == '\''){ - lex_advance(s); - } - else{ - token_error(&t, "Unclosed unicode literal"_s); - } - } - else{ - token_error(&t, "Invalid UTF8 sequence in unicode literal"_s); - } - } break; - - case '<': { - if (lexc(s) == '<') { - lex_advance(s); - if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_LeftShiftAssign; - } - else { - t.kind = TK_LeftShift; - } - } - else if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_LesserThenOrEqual; - } - else { - t.kind = TK_LesserThen; - } - } break; - - case '>': { - if (lexc(s) == '>') { - lex_advance(s); - if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_RightShiftAssign; - } - else { - t.kind = TK_RightShift; - } - } - else if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_GreaterThenOrEqual; - } - else { - t.kind = TK_GreaterThen; - } - } break; - - case ':': { - if (lexc(s) == ':') { - lex_advance(s); - t.kind = TK_DoubleColon; - } - else if(lexc(s) == '='){ - lex_advance(s); - t.kind = TK_ColonAssign; - } - else { - t.kind = TK_Colon; - } - } break; - - case '-':{ - if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_SubAssign; - } - else if (lexc(s) == '-') { - lex_advance(s); - t.kind = TK_Decrement; - } - else if (lexc(s) == '>') { - lex_advance(s); - t.kind = TK_Arrow; - } - else { - t.kind = TK_Sub; - } - } break; - - case '"': { - t.kind = TK_StringLit; - lex_parse_string(s,&t,'"'); - if(t.kind != TK_Error){ - t.str += 1; - t.len -= 2; - } - t.intern_val = intern_string(table, t.string); - } break; - - case '/': { - if(lexc(s) == '='){ - t.kind = TK_DivAssign; - lex_advance(s); - } - else { - t.kind = TK_Div; - } - } break; - - case '0':{ - if(lexc(s) == 'x'){ - lex_advance(s); - while(lex_is_numeric_base16(lexc(s))) - lex_advance(s); - lex_set_len(s, &t); - t.str += 2; - t.len -= 2; - if(t.len == 0) - token_error(&t, "Hex constant doesn't have value"_s); - else - lex_parse_u64(lexer, &t, 16); - break; - } - else if(lexc(s) == 'b'){ - lex_advance(s); - while(lexc(s) == '0' || lexc(s) == '1') - lex_advance(s); - lex_set_len(s, &t); - t.str += 2; - t.len -= 2; - if(t.len == 0) - token_error(&t, "Hex constant doesn't have value"_s); - else - lex_parse_u64(lexer, &t, 2); - break; - } - - } - case '1':case '2':case '3':case '4': - case '5':case '6':case '7':case '8':case '9':{ - B32 found_dot = false; - for(;;){ - if(lex_is_numeric(lexc(s))) - ; - else if(lexc(s) == '.'){ - if(found_dot){ - token_error(&t, "Multiple '.' in float literal"_s); - goto end_of_switch; - } - found_dot = true; - } - else break; - - lex_advance(s); - } - lex_set_len(s, &t); - if(found_dot) lex_parse_f64(&t); - else lex_parse_u64(lexer, &t, 10); - - } break; - - case 'A':case 'a':case 'M':case 'm':case 'B': - case 'b':case 'N':case 'n':case 'C':case 'c':case 'O': - case 'o':case 'D':case 'd':case 'P':case 'p':case 'E': - case 'e':case 'Q':case 'q':case 'F':case 'f':case 'R': - case 'r':case 'G':case 'g':case 'S':case 's':case 'H': - case 'h':case 'T':case 't':case 'I':case 'i':case 'U': - case 'u':case 'J':case 'j':case 'V':case 'v':case 'K': - case 'k':case 'W':case 'w':case 'L':case 'X':case 'l': - case 'x':case 'Z':case 'z':case 'Y':case 'y':case '_': { - t.kind = TK_Identifier; - lex_parse_ident(table, s, &t); - t.intern_val = intern_string(table, t.string); - if(lex_is_keyword(table, t.intern_val)){ - t.kind = TK_Keyword; - } - } break; - - default: { - token_error(&t, "Unknown token"_s); - } - }end_of_switch: - - if(t.len==0) - lex_set_len(s,&t); - - lex_add_token(lexer, &t); - } #undef CASE2 #undef CASE3 } CORE_Static void -lex_restream(Core_Ctx *lexer, String istream, String file){ - lexer->stream = {}; - lexer->stream.stream = istream; - lexer->stream.line_begin = istream.str; - lexer->stream.file = lexer->intern(file); +lex_restream(Core_Ctx *lexer, String istream, String file) { + lexer->stream = {}; + lexer->stream.stream = istream; + lexer->stream.line_begin = istream.str; + lexer->stream.file = lexer->intern(file); - Scratch_Scope _scope(lexer->scratch); - lexer->stream.indent_stack.allocator = lexer->scratch; - lexer->stream.indent_stack.add(&lexer->same_scope_token); - lex__stream(lexer); + Scratch_Scope _scope(lexer->scratch); + lexer->stream.indent_stack.allocator = lexer->scratch; + lexer->stream.indent_stack.add(&lexer->same_scope_token); + lex__stream(lexer); } //----------------------------------------------------------------------------- @@ -611,83 +694,84 @@ lex_restream(Core_Ctx *lexer, String istream, String file){ //----------------------------------------------------------------------------- CORE_Static const char * -name(Token_Kind kind){ - switch(kind){ - case TK_End: return "End of stream"; -/*# -import meta -for i in meta.token_kinds: - if i[1] != "SPECIAL": - print("case TK_" + i[0] + f": return \"{i[1]}\";") -*/ -case TK_Mul: return "*"; -case TK_Div: return "/"; -case TK_Mod: return "%"; -case TK_LeftShift: return "<<"; -case TK_RightShift: return ">>"; -case TK_Add: return "+"; -case TK_Sub: return "-"; -case TK_Equals: return "=="; -case TK_LesserThenOrEqual: return "<="; -case TK_GreaterThenOrEqual: return ">="; -case TK_LesserThen: return "<"; -case TK_GreaterThen: return ">"; -case TK_NotEquals: return "!="; -case TK_BitAnd: return "&"; -case TK_BitOr: return "|"; -case TK_BitXor: return "^"; -case TK_And: return "&&"; -case TK_Or: return "||"; -case TK_Neg: return "~"; -case TK_Not: return "!"; -case TK_Decrement: return "--"; -case TK_Increment: return "++"; -case TK_PostDecrement: return "--"; -case TK_PostIncrement: return "++"; -case TK_Assign: return "="; -case TK_ColonAssign: return ":="; -case TK_DivAssign: return "/="; -case TK_MulAssign: return "*="; -case TK_ModAssign: return "%="; -case TK_SubAssign: return "-="; -case TK_AddAssign: return "+="; -case TK_AndAssign: return "&="; -case TK_OrAssign: return "|="; -case TK_XorAssign: return "^="; -case TK_LeftShiftAssign: return "<<="; -case TK_RightShiftAssign: return ">>="; -case TK_OpenParen: return "("; -case TK_CloseParen: return ")"; -case TK_OpenBrace: return "{"; -case TK_CloseBrace: return "}"; -case TK_OpenBracket: return "["; -case TK_CloseBracket: return "]"; -case TK_Comma: return ","; -case TK_Pound: return "#"; -case TK_Question: return "?"; -case TK_ThreeDots: return "..."; -case TK_Semicolon: return ";"; -case TK_Dot: return "."; -case TK_TwoDots: return ".."; -case TK_NewLine: return "[NewLine]"; -case TK_Colon: return ":"; -case TK_DoubleColon: return "::"; -case TK_At: return "@"; -case TK_Arrow: return "->"; -case TK_ExprSizeof: return "[SizeOf]"; -case TK_DocComment: return "[///]"; -case TK_Comment: return "//"; -case TK_Identifier: return "[Ident]"; -case TK_UnicodeLit: return "[Unicode]"; -case TK_StringLit: return "[String]"; -case TK_Error: return "[Error]"; -case TK_Float: return "[Float]"; -case TK_Integer: return "[Int]"; -case TK_Keyword: return "[Keyword]"; -/*END*/ - case CLOSE_SCOPE: return "Close_Scope"; - case OPEN_SCOPE: return "Open_Scope"; - case SAME_SCOPE: return "Same_Scope"; - default: invalid_codepath; return ""; - } +name(Token_Kind kind) { + switch (kind) { + case TK_End: return "End of stream"; + /*# + import meta + for i in meta.token_kinds: + if i[1] != "SPECIAL": + print("case TK_" + i[0] + f": return \"{i[1]}\";") + */ + case TK_Mul: return "*"; + case TK_Div: return "/"; + case TK_Mod: return "%"; + case TK_LeftShift: return "<<"; + case TK_RightShift: return ">>"; + case TK_Add: return "+"; + case TK_Sub: return "-"; + case TK_Equals: return "=="; + case TK_LesserThenOrEqual: return "<="; + case TK_GreaterThenOrEqual: return ">="; + case TK_LesserThen: return "<"; + case TK_GreaterThen: return ">"; + case TK_NotEquals: return "!="; + case TK_BitAnd: return "&"; + case TK_BitOr: return "|"; + case TK_BitXor: return "^"; + case TK_And: return "&&"; + case TK_Or: return "||"; + case TK_Neg: return "~"; + case TK_Not: return "!"; + case TK_Decrement: return "--"; + case TK_Increment: return "++"; + case TK_PostDecrement: return "--"; + case TK_PostIncrement: return "++"; + case TK_Assign: return "="; + case TK_ColonAssign: return ":="; + case TK_DivAssign: return "/="; + case TK_MulAssign: return "*="; + case TK_ModAssign: return "%="; + case TK_SubAssign: return "-="; + case TK_AddAssign: return "+="; + case TK_AndAssign: return "&="; + case TK_OrAssign: return "|="; + case TK_XorAssign: return "^="; + case TK_LeftShiftAssign: return "<<="; + case TK_RightShiftAssign: return ">>="; + case TK_OpenParen: return "("; + case TK_CloseParen: return ")"; + case TK_OpenBrace: return "{"; + case TK_CloseBrace: return "}"; + case TK_OpenBracket: return "["; + case TK_CloseBracket: return "]"; + case TK_Comma: return ","; + case TK_Pound: return "#"; + case TK_Question: return "?"; + case TK_ThreeDots: return "..."; + case TK_Semicolon: return ";"; + case TK_Dot: return "."; + case TK_TwoDots: return ".."; + case TK_NewLine: return "[NewLine]"; + case TK_Colon: return ":"; + case TK_DoubleColon: return "::"; + case TK_At: return "@"; + case TK_Arrow: return "->"; + case TK_ExprSizeof: return "[SizeOf]"; + case TK_DocComment: return "[///]"; + case TK_Comment: return "//"; + case TK_Identifier: return "[Ident]"; + case TK_UnicodeLit: return "[Unicode]"; + case TK_StringLit: return "[String]"; + case TK_Error: return "[Error]"; + case TK_Float: return "[Float]"; + case TK_Integer: return "[Int]"; + case TK_Keyword: + return "[Keyword]"; + /*END*/ + case CLOSE_SCOPE: return "Close_Scope"; + case OPEN_SCOPE: return "Open_Scope"; + case SAME_SCOPE: return "Same_Scope"; + default: invalid_codepath; return ""; + } } diff --git a/core_main.cpp b/core_main.cpp index da3a321..03b1b59 100644 --- a/core_main.cpp +++ b/core_main.cpp @@ -93,138 +93,137 @@ Ideas #include "core_compiler_includes.cpp" -const U32 COMPILE_NULL = 0x0; -const U32 COMPILE_PRINT_STATS = 0x1; +const U32 COMPILE_NULL = 0x0; +const U32 COMPILE_PRINT_STATS = 0x1; const U32 COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY = 0x2; -const U32 COMPILE_AND_RUN = 0x4; -const U32 COMPILE_TESTING = 0x8; +const U32 COMPILE_AND_RUN = 0x4; +const U32 COMPILE_TESTING = 0x8; static void compile_file(Allocator *allocator, String filename, U32 compile_flags = COMPILE_NULL) { - if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { - printf("%s - ", filename.str); - } - String result = compile_file_to_string(allocator, filename); + if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { + printf("%s - ", filename.str); + } + String result = compile_file_to_string(allocator, filename); - B32 r = os_write_file("program.c"_s, result); - assert(r); - F64 total_compiler_time = os_time() - pctx->time.start; - printf("%f - ", total_compiler_time); + B32 r = os_write_file("program.c"_s, result); + assert(r); + F64 total_compiler_time = os_time() - pctx->time.start; + printf("%f - ", total_compiler_time); - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); - F64 begin = os_time(); - String_Builder builder = {scratch}; - builder.addf("clang program.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a" OS_EXE " "); - For(pctx->files_to_link) { - builder.addf("-l%Q ", it->intern_val); - } - String compiler_call = string_flatten(scratch, &builder); + F64 begin = os_time(); + String_Builder builder = {scratch}; + builder.addf("clang program.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a" OS_EXE " "); + For(pctx->files_to_link) { + builder.addf("-l%Q ", it->intern_val); + } + String compiler_call = string_flatten(scratch, &builder); - system((const char *)compiler_call.str); - F64 end = os_time(); + system((const char *)compiler_call.str); + F64 end = os_time(); - if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) { - printf("total = %f\n", os_time() - pctx->time.start); - printf("core_total = %f\n", pctx->time.total); - printf("clang = %f\n", end - begin); - printf("parsing = %f\n", pctx->time.parsing); - printf("typecheck = %f\n", pctx->time.typechecking); - printf("generatin = %f\n", pctx->time.code_generation); - } + if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) { + printf("total = %f\n", os_time() - pctx->time.start); + printf("core_total = %f\n", pctx->time.total); + printf("clang = %f\n", end - begin); + printf("parsing = %f\n", pctx->time.parsing); + printf("typecheck = %f\n", pctx->time.typechecking); + printf("generatin = %f\n", pctx->time.code_generation); + } - if (is_flag_set(compile_flags, COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY)) { - // @! Allocator stats - } + if (is_flag_set(compile_flags, COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY)) { + // @! Allocator stats + } - if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { - String testing = compile_flags & COMPILE_TESTING ? "testing"_s : ""_s; + if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { + String testing = compile_flags & COMPILE_TESTING ? "testing"_s : ""_s; #if OS_WINDOWS - String sys = string_fmt(scratch, "a.exe %Q", testing); + String sys = string_fmt(scratch, "a.exe %Q", testing); #else - String sys = string_fmt(scratch, "./a.out %Q", testing); + String sys = string_fmt(scratch, "./a.out %Q", testing); #endif - int result = system((char *)sys.str); - assert(result != -1); - if (result == 0) { - printf(PRINTF_GREEN "OK!" PRINTF_RESET); + int result = system((char *)sys.str); + assert(result != -1); + if (result == 0) { + printf(PRINTF_GREEN "OK!" PRINTF_RESET); + } + if (result != 0) { + printf(PRINTF_RED "ERROR!" PRINTF_RESET); + } + printf("\n"); } - if (result != 0) { - printf(PRINTF_RED "ERROR!" PRINTF_RESET); - } - printf("\n"); - } } -int main(int argument_count, char **arguments){ - Arena arena = {}; - Arena scratch = {}; - arena_init(&arena, "Pernament arena"_s); - arena_init(&scratch, "Pernament scratch arena"_s); +int main(int argument_count, char **arguments) { + Arena arena = {}; + Arena scratch = {}; + arena_init(&arena, "Pernament arena"_s); + arena_init(&scratch, "Pernament scratch arena"_s); - Array args = {&scratch}; - for(int i = 1; i < argument_count; i+=1){ - String arg = string_from_cstring(arguments[i]); - args.add(arg); - } + Array args = {&scratch}; + for (int i = 1; i < argument_count; i += 1) { + String arg = string_from_cstring(arguments[i]); + args.add(arg); + } - if(!args.len){ - printf("Please specify a file to compile!"); - return 0; - } + if (!args.len) { + printf("Please specify a file to compile!"); + return 0; + } - bool enable_color_codes = false; + bool enable_color_codes = false; #if OS_UNIX - enable_color_codes = true; + enable_color_codes = true; #endif #if OS_WINDOWS - // Set output mode to handle virtual terminal sequences - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (hOut != INVALID_HANDLE_VALUE) { - DWORD dwMode = 0; - if (GetConsoleMode(hOut, &dwMode)) { - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (SetConsoleMode(hOut, dwMode)) { - enable_color_codes = true; - } - else{ - printf("Failed to enable colored terminal output C\n"); - } + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut != INVALID_HANDLE_VALUE) { + DWORD dwMode = 0; + if (GetConsoleMode(hOut, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (SetConsoleMode(hOut, dwMode)) { + enable_color_codes = true; + } + else { + printf("Failed to enable colored terminal output C\n"); + } + } + else { + printf("Failed to enable colored terminal output B\n"); + } } - else{ - printf("Failed to enable colored terminal output B\n"); + else { + printf("Failed to enable colored terminal output A\n"); } - } - else { - printf("Failed to enable colored terminal output A\n"); - } - test_os_memory(); + test_os_memory(); #endif - (void)enable_color_codes; - For(args){ + (void)enable_color_codes; + For(args) { - if(it == "-testing"_s){ - Scratch_Scope _scope(&scratch); - Array examples = os_list_dir(&scratch, &scratch, "examples"_s); - Array tests = os_list_dir(&scratch, &scratch, "tests"_s); - For(examples){ - if(it.is_directory) continue; - compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); - } - For(tests){ - if(it.is_directory) continue; - compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); - } + if (it == "-testing"_s) { + Scratch_Scope _scope(&scratch); + Array examples = os_list_dir(&scratch, &scratch, "examples"_s); + Array tests = os_list_dir(&scratch, &scratch, "tests"_s); + For(examples) { + if (it.is_directory) continue; + compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); + } + For(tests) { + if (it.is_directory) continue; + compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); + } + } + + else { + String program_name = string_from_cstring(arguments[1]); + compile_file(&arena, program_name, COMPILE_PRINT_STATS); + } } - - else { - String program_name = string_from_cstring(arguments[1]); - compile_file(&arena, program_name, COMPILE_PRINT_STATS); - } - - } - printf("End of program\n"); - return 0; + printf("End of program\n"); + return 0; } diff --git a/core_parsing.cpp b/core_parsing.cpp index 60ab9d2..d97b87a 100644 --- a/core_parsing.cpp +++ b/core_parsing.cpp @@ -1,827 +1,835 @@ CORE_Static Ast_Decl *parse_decl(B32 is_global); static Core_Message *core_add_message(Core_Message_Kind kind, String string, Token *pos1, Token *pos2 = 0, int line = -1, const char *file = 0) { - if (kind == CORE_ERROR) pctx->errors_occured += 1; - if (kind == CORE_WARNING) pctx->warnings_occured += 1; - Core_Message *message = allocate_struct(pctx->perm, Core_Message); - message->kind = kind; - message->string = string; - message->tokens[0] = pos1; - message->tokens[1] = pos2; - message->trace_line = line; - message->trace_file = (char *)file; - SLL_QUEUE_ADD(pctx->first_message, pctx->last_message, message); - return message; + if (kind == CORE_ERROR) pctx->errors_occured += 1; + if (kind == CORE_WARNING) pctx->warnings_occured += 1; + Core_Message *message = allocate_struct(pctx->perm, Core_Message); + message->kind = kind; + message->string = string; + message->tokens[0] = pos1; + message->tokens[1] = pos2; + message->trace_line = line; + message->trace_file = (char *)file; + SLL_QUEUE_ADD(pctx->first_message, pctx->last_message, message); + return message; } -#define log_trace(...) core_log_trace(__LINE__, __FILE__,##__VA_ARGS__) -static void core_log_trace(int line, const char *file, const char *str, ...){ - STRING_FMT(pctx->perm, str, string); - core_add_message(CORE_TRACE, string, 0, 0, line, file); +#define log_trace(...) core_log_trace(__LINE__, __FILE__, ##__VA_ARGS__) +static void core_log_trace(int line, const char *file, const char *str, ...) { + STRING_FMT(pctx->perm, str, string); + core_add_message(CORE_TRACE, string, 0, 0, line, file); } #define PRINTF_GREEN "\033[32m" -#define PRINTF_RED "\033[31m" +#define PRINTF_RED "\033[31m" #define PRINTF_RESET "\033[0m" String core_stringify_message(Core_Ctx *pctx, Allocator *allocator, Core_Message *msg, int color_codes_enabled = false) { - String_Builder &b = pctx->helper_builder; + String_Builder &b = pctx->helper_builder; - if (msg->kind == CORE_ERROR) b.addf("Error! "); - else if (msg->kind == CORE_WARNING) b.addf("Warning! "); - else if (msg->kind == CORE_TRACE) b.addf("Trace: "); - else invalid_codepath; + if (msg->kind == CORE_ERROR) b.addf("Error! "); + else if (msg->kind == CORE_WARNING) b.addf("Warning! "); + else if (msg->kind == CORE_TRACE) b.addf("Trace: "); + else invalid_codepath; - for (int i = 0; i < buff_cap(msg->tokens); i += 1) { - Token *it = msg->tokens[i]; - if (it) { - if (it->kind == TK_Error) { - b.addf("%Q | ", it->error_val); - } + for (int i = 0; i < buff_cap(msg->tokens); i += 1) { + Token *it = msg->tokens[i]; + if (it) { + if (it->kind == TK_Error) { + b.addf("%Q | ", it->error_val); + } + } } - } - b.addf("%Q", msg->string); + b.addf("%Q", msg->string); - for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { - Token *token = msg->tokens[i]; - if (token) { - b.addf("\n"); - // Print from line begin to token - S64 i1 = token->str - token->line_begin; - b.addf("%.*s", i1, token->line_begin); + for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { + Token *token = msg->tokens[i]; + if (token) { + b.addf("\n"); + // Print from line begin to token + S64 i1 = token->str - token->line_begin; + b.addf("%.*s", i1, token->line_begin); - // Print token part - if(color_codes_enabled){ - b.addf( PRINTF_RED "%.*s" PRINTF_RESET, (S64)token->len, token->str); - } else { - b.addf("%.*s", (S64)token->len, token->str); - } + // Print token part + if (color_codes_enabled) { + b.addf(PRINTF_RED "%.*s" PRINTF_RESET, (S64)token->len, token->str); + } + else { + b.addf("%.*s", (S64)token->len, token->str); + } - // Print to end of line from token - S64 iend = 0; - U8 *pointer = token->str + token->len; - while(pointer[iend]!='\n' && pointer[iend]!=0) iend++; - b.addf("%.*s", iend, pointer); + // Print to end of line from token + S64 iend = 0; + U8 *pointer = token->str + token->len; + while (pointer[iend] != '\n' && pointer[iend] != 0) iend++; + b.addf("%.*s", iend, pointer); + } } - } - for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { - Token *it = msg->tokens[i]; - if (it) { - b.addf("\n%s:%d", it->file.str, (S64)it->line + 1); + for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { + Token *it = msg->tokens[i]; + if (it) { + b.addf("\n%s:%d", it->file.str, (S64)it->line + 1); + } } - } - String result = string_flatten(allocator, &b); - return result; + String result = string_flatten(allocator, &b); + return result; } static void compiler_error(Token *token1, Token *token2, const char *str, ...) { - STRING_FMT(pctx->perm, str, string); - Core_Message *msg = core_add_message(CORE_ERROR, string, token1, token2); - if (pctx->debugger_break_on_compiler_error) { - String str = core_stringify_message(pctx, pctx->perm, msg, pctx->color_codes_enabled); - printf("%s", str.str); // @! How to get rid of printf ? - fflush(stdout); - Breakpoint; - } + STRING_FMT(pctx->perm, str, string); + Core_Message *msg = core_add_message(CORE_ERROR, string, token1, token2); + if (pctx->debugger_break_on_compiler_error) { + String str = core_stringify_message(pctx, pctx->perm, msg, pctx->color_codes_enabled); + printf("%s", str.str); // @! How to get rid of printf ? + fflush(stdout); + Breakpoint; + } } CORE_Static void -compiler_error(Token *token, const char *str, ...){ - STRING_FMT(pctx->perm, str, string); - Core_Message *msg = core_add_message(CORE_ERROR, string, token); - if (pctx->debugger_break_on_compiler_error) { - String str = core_stringify_message(pctx, pctx->perm, msg, pctx->color_codes_enabled); - printf("%s", str.str); // @! How to get rid of printf ? - fflush(stdout); - Breakpoint; - } +compiler_error(Token *token, const char *str, ...) { + STRING_FMT(pctx->perm, str, string); + Core_Message *msg = core_add_message(CORE_ERROR, string, token); + if (pctx->debugger_break_on_compiler_error) { + String str = core_stringify_message(pctx, pctx->perm, msg, pctx->color_codes_enabled); + printf("%s", str.str); // @! How to get rid of printf ? + fflush(stdout); + Breakpoint; + } } CORE_Static Token * -token_get(S64 i = 0){ - i += pctx->token_iter; - if(i >= pctx->tokens.len){ - return &pctx->null_token; - } - Token *result = &pctx->tokens[i]; - return result; +token_get(S64 i = 0) { + i += pctx->token_iter; + if (i >= pctx->tokens.len) { + return &pctx->null_token; + } + Token *result = &pctx->tokens[i]; + return result; } CORE_Static Token * -token_is_scope(){ - Token *token = token_get(); - if(lex_is_scope(token)) return token; - return 0; +token_is_scope() { + Token *token = token_get(); + if (lex_is_scope(token)) return token; + return 0; } CORE_Static Token * -token_next(){ - Token *token = token_get(); - if(lex_is_scope(token)) pctx->indent = token->indent; - pctx->token_iter++; - return token; -} - -CORE_Static Token * -token_is(Token_Kind kind, S64 lookahead = 0){ - Token *token = token_get(lookahead); - if(token->kind == kind){ +token_next() { + Token *token = token_get(); + if (lex_is_scope(token)) pctx->indent = token->indent; + pctx->token_iter++; return token; - } - return 0; } CORE_Static Token * -token_is_keyword(Intern_String keyword, S64 lookahead = 0){ - Token *token = token_get(lookahead); - if(token->kind == TK_Keyword){ - if(keyword.str == token->intern_val.str){ - return token; +token_is(Token_Kind kind, S64 lookahead = 0) { + Token *token = token_get(lookahead); + if (token->kind == kind) { + return token; } - } - return 0; + return 0; } CORE_Static Token * -token_match_pound(Intern_String string){ - Token *token = token_get(); - if(token->kind == TK_Pound){ - if(token->intern_val == string){ - return token_next(); +token_is_keyword(Intern_String keyword, S64 lookahead = 0) { + Token *token = token_get(lookahead); + if (token->kind == TK_Keyword) { + if (keyword.str == token->intern_val.str) { + return token; + } } - } - return 0; + return 0; } CORE_Static Token * -token_match(Token_Kind kind){ - Token *token = token_get(); - if(token->kind == kind){ - return token_next(); - } - return 0; -} - -CORE_Static Token * -token_match(Token_Kind a, Token_Kind b){ - Token *ta = token_get(); - Token *tb = token_get(1); - if(ta->kind == a && tb->kind == b){ - token_next(); token_next(); - return ta; - } - return 0; -} - -CORE_Static Token * -token_match_keyword(Intern_String string){ - Token *token = token_get(); - if(token->kind == TK_Keyword){ - if(string.str == token->intern_val.str){ - token = token_next(); - return token; +token_match_pound(Intern_String string) { + Token *token = token_get(); + if (token->kind == TK_Pound) { + if (token->intern_val == string) { + return token_next(); + } } - } - return 0; + return 0; } CORE_Static Token * -token_expect(Token_Kind kind){ - Token *token = token_get(); - if(token->kind == kind) return token_next(); - compiler_error(token, "Expected token of kind: [%s], got instead token of kind: [%s]", name(kind), name(token->kind)); - return 0; +token_match(Token_Kind kind) { + Token *token = token_get(); + if (token->kind == kind) { + return token_next(); + } + return 0; +} + +CORE_Static Token * +token_match(Token_Kind a, Token_Kind b) { + Token *ta = token_get(); + Token *tb = token_get(1); + if (ta->kind == a && tb->kind == b) { + token_next(); + token_next(); + return ta; + } + return 0; +} + +CORE_Static Token * +token_match_keyword(Intern_String string) { + Token *token = token_get(); + if (token->kind == TK_Keyword) { + if (string.str == token->intern_val.str) { + token = token_next(); + return token; + } + } + return 0; +} + +CORE_Static Token * +token_expect(Token_Kind kind) { + Token *token = token_get(); + if (token->kind == kind) return token_next(); + compiler_error(token, "Expected token of kind: [%s], got instead token of kind: [%s]", name(kind), name(token->kind)); + return 0; } CORE_Static Ast_Expr * -parse_init_stmt(Ast_Expr *expr){ - Token *token = token_get(); - if(token->kind == TK_ColonAssign && expr->kind != AST_IDENT) - compiler_error(expr->pos, "Binding with [:=] to something that is not an identifier"); +parse_init_stmt(Ast_Expr *expr) { + Token *token = token_get(); + if (token->kind == TK_ColonAssign && expr->kind != AST_IDENT) + compiler_error(expr->pos, "Binding with [:=] to something that is not an identifier"); - else if(token_is_assign(token)){ - token_next(); - Ast_Expr *value = parse_expr(); - Ast_Expr *result = 0; - if(token->kind == TK_ColonAssign){ - Ast_Atom *name = (Ast_Atom *)expr; - result = (Ast_Expr *)ast_var(token, 0, name->intern_val, value); - set_flag(result->flags, AST_EXPR); - } else{ - result = ast_expr_binary((Ast_Atom *)expr, value, token); + else if (token_is_assign(token)) { + token_next(); + Ast_Expr *value = parse_expr(); + Ast_Expr *result = 0; + if (token->kind == TK_ColonAssign) { + Ast_Atom *name = (Ast_Atom *)expr; + result = (Ast_Expr *)ast_var(token, 0, name->intern_val, value); + set_flag(result->flags, AST_EXPR); + } + else { + result = ast_expr_binary((Ast_Atom *)expr, value, token); + } + set_flag(result->flags, AST_STMT); + return result; } - set_flag(result->flags, AST_STMT); - return result; - } - return expr; + return expr; } CORE_Static Ast_Call * -parse_expr_call(Ast_Expr *left, Token_Kind close_kind){ - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); - Token *pos = token_get(); - Array exprs = {scratch}; +parse_expr_call(Ast_Expr *left, Token_Kind close_kind) { + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); + Token *pos = token_get(); + Array exprs = {scratch}; - while(!token_is(close_kind)){ - Ast_Call_Item *item_comp = ast_new(Ast_Call_Item, AST_CALL_ITEM, token_get(), AST_EXPR); - item_comp->item = parse_expr(); - if(token_match(TK_Assign)){ - if(!is_flag_set(item_comp->item->flags, AST_ATOM)){ - compiler_error(item_comp->pos, "Invalid value specifier, it's required to be a simple identifier/index"); - } + while (!token_is(close_kind)) { + Ast_Call_Item *item_comp = ast_new(Ast_Call_Item, AST_CALL_ITEM, token_get(), AST_EXPR); + item_comp->item = parse_expr(); + if (token_match(TK_Assign)) { + if (!is_flag_set(item_comp->item->flags, AST_ATOM)) { + compiler_error(item_comp->pos, "Invalid value specifier, it's required to be a simple identifier/index"); + } - if(item_comp->item->kind != AST_IDENT){ - item_comp->index = item_comp->item; - set_flag(item_comp->call_flags, CALL_INDEX); - } - else{ - item_comp->name = (Ast_Atom *)item_comp->item; - set_flag(item_comp->call_flags, CALL_NAME); - } + if (item_comp->item->kind != AST_IDENT) { + item_comp->index = item_comp->item; + set_flag(item_comp->call_flags, CALL_INDEX); + } + else { + item_comp->name = (Ast_Atom *)item_comp->item; + set_flag(item_comp->call_flags, CALL_NAME); + } - item_comp->item = parse_expr(); + item_comp->item = parse_expr(); + } + + if (close_kind == TK_OpenParen && is_flag_set(item_comp->call_flags, CALL_INDEX)) + compiler_error(item_comp->pos, "Lambda calls can't have indexed arguments"); + + exprs.add(item_comp); + if (!token_match(TK_Comma)) { + break; + } } + token_expect(close_kind); - if(close_kind == TK_OpenParen && is_flag_set(item_comp->call_flags, CALL_INDEX)) - compiler_error(item_comp->pos, "Lambda calls can't have indexed arguments"); - - exprs.add(item_comp); - if(!token_match(TK_Comma)){ - break; - } - } - token_expect(close_kind); - - Ast_Call *result = ast_call(pos, left, exprs); - return result; + Ast_Call *result = ast_call(pos, left, exprs); + return result; } CORE_Static Ast_Expr * -parse_optional_type(){ - Ast_Expr *result = 0; - if(token_match(TK_Colon)) result = parse_expr(); - return result; +parse_optional_type() { + Ast_Expr *result = 0; + if (token_match(TK_Colon)) result = parse_expr(); + return result; } CORE_Static Ast_Scope * -parse_stmt_scope(Ast_Scope *scope_defined_outside = 0){ - Ast_Scope *scope = scope_defined_outside; +parse_stmt_scope(Ast_Scope *scope_defined_outside = 0) { + Ast_Scope *scope = scope_defined_outside; - if(token_expect(OPEN_SCOPE)){ // @todo: Fix error message here, it doesn't show proper token context - Token *token_block = token_get(); + if (token_expect(OPEN_SCOPE)) { // @todo: Fix error message here, it doesn't show proper token context + Token *token_block = token_get(); - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); - if(!scope_defined_outside) scope = begin_stmt_scope(scratch, token_block); - do{ - Token *token = token_get(); + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); + if (!scope_defined_outside) scope = begin_stmt_scope(scratch, token_block); + do { + Token *token = token_get(); - if(token_match_keyword(pctx->keyword_return)){ - Array expr = {scratch}; - if(!token_is_scope()) { - do{ - Ast_Expr *subexpr = parse_expr(); - expr.add(subexpr); - } while(token_match(TK_Comma)); - } - scope->stmts.add(ast_return(token, expr)); - } - - else if(token_match_keyword(pctx->keyword_break)){ - scope->stmts.add(ast_break(token)); - } - - else if(token_match_keyword(pctx->keyword_pass)){ - scope->stmts.add(ast_pass(token)); - } - - else if(token_match_keyword(pctx->keyword_switch)){ - Ast_Switch *result = ast_new(Ast_Switch, AST_SWITCH, token, AST_STMT); - result->value = parse_expr(); - result->cases = {scratch}; - - token_expect(OPEN_SCOPE); - do{ - if(token_match_keyword(pctx->keyword_default)){ - result->default_scope = parse_stmt_scope(); - continue; - } - - Ast_Switch_Case *switch_case = ast_new(Ast_Switch_Case, AST_SWITCH_CASE, token_get(), AST_STMT); - if(token_match_pound(pctx->intern("fallthrough"_s))) - switch_case->fallthrough = true; - - switch_case->labels = {scratch}; - do{ - switch_case->labels.add(parse_expr()); - }while(token_match(TK_Comma)); - switch_case->labels = switch_case->labels.tight_copy(pctx->perm); - - switch_case->scope = parse_stmt_scope(); - result->cases.add(switch_case); - }while(token_match(SAME_SCOPE)); - token_expect(CLOSE_SCOPE); - result->cases = result->cases.tight_copy(pctx->perm); - - scope->stmts.add(result); - } - - else if(token_match_keyword(pctx->keyword_assert)){ - token_expect(TK_OpenParen); - Ast_Expr *expr = parse_expr(); - Intern_String message = {}; - if(token_match(TK_Comma)){ - Token *t = token_expect(TK_StringLit); - message = t->intern_val; - } - token_expect(TK_CloseParen); - scope->stmts.add(ast_runtime_assert(token, expr, message)); - } - - else if(token_match_pound(pctx->keyword_assert)){ - token_expect(TK_OpenParen); - Ast_Expr *expr = parse_expr(); - Intern_String message = {}; - if(token_match(TK_Comma)){ - Token *t = token_expect(TK_StringLit); - message = t->intern_val; - } - token_expect(TK_CloseParen); - scope->stmts.add(ast_constant_assert(token, expr, message)); - } - - else if(token_match_keyword(pctx->keyword_for)){ - Ast_Scope *for_scope = begin_stmt_scope(scratch, token_get()); - Ast_Expr *init = 0; - Ast_Expr *cond = 0; - Ast_Expr *iter = 0; - - if(!token_is(OPEN_SCOPE)){ - if(!token_is(TK_Comma)){ - Ast_Expr *expr_first = parse_expr(); - init = parse_init_stmt(expr_first); - } - - if(token_match(TK_Comma)){ - if(!token_is(TK_Comma)) cond = parse_expr(); - if(token_match(TK_Comma)){ - iter = parse_expr(); - iter = parse_init_stmt(iter); + if (token_match_keyword(pctx->keyword_return)) { + Array expr = {scratch}; + if (!token_is_scope()) { + do { + Ast_Expr *subexpr = parse_expr(); + expr.add(subexpr); + } while (token_match(TK_Comma)); + } + scope->stmts.add(ast_return(token, expr)); } - } - } - parse_stmt_scope(for_scope); - finalize_stmt_scope(for_scope); - scope->stmts.add(ast_for(token, init, cond, iter, for_scope)); - } + else if (token_match_keyword(pctx->keyword_break)) { + scope->stmts.add(ast_break(token)); + } - else if(token_match_keyword(pctx->keyword_if)){ - Array if_nodes = {scratch}; - Ast_Expr *expr = parse_expr(); - Ast_Expr *init_val = parse_init_stmt(expr); - if(init_val != expr){ - if(token_match(TK_Comma)) expr = parse_expr(); - else expr = 0; - } - if(init_val == expr) init_val = 0; + else if (token_match_keyword(pctx->keyword_pass)) { + scope->stmts.add(ast_pass(token)); + } - Ast_Scope *if_block = parse_stmt_scope(); - Ast_If_Node *if_node = ast_if_node(token, init_val, expr, if_block); - if_nodes.add(if_node); + else if (token_match_keyword(pctx->keyword_switch)) { + Ast_Switch *result = ast_new(Ast_Switch, AST_SWITCH, token, AST_STMT); + result->value = parse_expr(); + result->cases = {scratch}; - while(token_is(SAME_SCOPE) && (token_is_keyword(pctx->keyword_elif, 1) || (token_is_keyword(pctx->keyword_else, 1)))){ - token_next(); - token = token_get(); - if(token_match_keyword(pctx->keyword_elif)){ - assert(token->intern_val == pctx->keyword_elif); - Ast_Expr *expr = parse_expr(); - Ast_Scope *else_if_block = parse_stmt_scope(); - Ast_If_Node *if_node = ast_if_node(token, 0, expr, else_if_block); - if_nodes.add(if_node); - } - else{ - token_match_keyword(pctx->keyword_else); - assert(token->intern_val == pctx->keyword_else); - Ast_Scope *else_block = parse_stmt_scope(); - Ast_If_Node *if_node = ast_if_node(token, 0, 0, else_block); - if_nodes.add(if_node); - break; - } - } - Ast_If *result_if = ast_if(token, if_nodes); - scope->stmts.add(result_if); + token_expect(OPEN_SCOPE); + do { + if (token_match_keyword(pctx->keyword_default)) { + result->default_scope = parse_stmt_scope(); + continue; + } - } - else if(token_is(TK_Identifier) && token_is(TK_Comma, 1)){ - Array decls = {scratch}; - do{ - Token *name = token_match(TK_Identifier); - Ast_Decl *decl = ast_var(name, 0, name->intern_val, 0); - decls.add(decl); - }while(token_match(TK_Comma)); + Ast_Switch_Case *switch_case = ast_new(Ast_Switch_Case, AST_SWITCH_CASE, token_get(), AST_STMT); + if (token_match_pound(pctx->intern("fallthrough"_s))) + switch_case->fallthrough = true; - token_expect(TK_ColonAssign); - Ast_Expr *expr = parse_expr(); - Ast_Var_Unpack *vars = ast_var_unpack(token, decls, expr); - scope->stmts.add(vars); - } - else{ - Ast *result = parse_decl(false); - if(result && result->kind != AST_VAR && result->kind != AST_CONST){ - compiler_error(token, "Invalid statement construct"); - } - if(!result){ - result = parse_expr(); - result = parse_init_stmt((Ast_Expr *)result); - } + switch_case->labels = {scratch}; + do { + switch_case->labels.add(parse_expr()); + } while (token_match(TK_Comma)); + switch_case->labels = switch_case->labels.tight_copy(pctx->perm); - if(result) { - result->flags = set_flag(result->flags, AST_STMT); - scope->stmts.add(result); - } - else { - compiler_error(token, "Unexpected token [%s] while parsing statement", name(token->kind)); - } + switch_case->scope = parse_stmt_scope(); + result->cases.add(switch_case); + } while (token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); + result->cases = result->cases.tight_copy(pctx->perm); - } - } while(token_match(SAME_SCOPE)); - token_expect(CLOSE_SCOPE); + scope->stmts.add(result); + } - if(!scope_defined_outside) finalize_stmt_scope(scope); - } - return scope; + else if (token_match_keyword(pctx->keyword_assert)) { + token_expect(TK_OpenParen); + Ast_Expr *expr = parse_expr(); + Intern_String message = {}; + if (token_match(TK_Comma)) { + Token *t = token_expect(TK_StringLit); + message = t->intern_val; + } + token_expect(TK_CloseParen); + scope->stmts.add(ast_runtime_assert(token, expr, message)); + } + + else if (token_match_pound(pctx->keyword_assert)) { + token_expect(TK_OpenParen); + Ast_Expr *expr = parse_expr(); + Intern_String message = {}; + if (token_match(TK_Comma)) { + Token *t = token_expect(TK_StringLit); + message = t->intern_val; + } + token_expect(TK_CloseParen); + scope->stmts.add(ast_constant_assert(token, expr, message)); + } + + else if (token_match_keyword(pctx->keyword_for)) { + Ast_Scope *for_scope = begin_stmt_scope(scratch, token_get()); + Ast_Expr *init = 0; + Ast_Expr *cond = 0; + Ast_Expr *iter = 0; + + if (!token_is(OPEN_SCOPE)) { + if (!token_is(TK_Comma)) { + Ast_Expr *expr_first = parse_expr(); + init = parse_init_stmt(expr_first); + } + + if (token_match(TK_Comma)) { + if (!token_is(TK_Comma)) cond = parse_expr(); + if (token_match(TK_Comma)) { + iter = parse_expr(); + iter = parse_init_stmt(iter); + } + } + } + + parse_stmt_scope(for_scope); + finalize_stmt_scope(for_scope); + scope->stmts.add(ast_for(token, init, cond, iter, for_scope)); + } + + else if (token_match_keyword(pctx->keyword_if)) { + Array if_nodes = {scratch}; + Ast_Expr *expr = parse_expr(); + Ast_Expr *init_val = parse_init_stmt(expr); + if (init_val != expr) { + if (token_match(TK_Comma)) expr = parse_expr(); + else expr = 0; + } + if (init_val == expr) init_val = 0; + + Ast_Scope *if_block = parse_stmt_scope(); + Ast_If_Node *if_node = ast_if_node(token, init_val, expr, if_block); + if_nodes.add(if_node); + + while (token_is(SAME_SCOPE) && (token_is_keyword(pctx->keyword_elif, 1) || (token_is_keyword(pctx->keyword_else, 1)))) { + token_next(); + token = token_get(); + if (token_match_keyword(pctx->keyword_elif)) { + assert(token->intern_val == pctx->keyword_elif); + Ast_Expr *expr = parse_expr(); + Ast_Scope *else_if_block = parse_stmt_scope(); + Ast_If_Node *if_node = ast_if_node(token, 0, expr, else_if_block); + if_nodes.add(if_node); + } + else { + token_match_keyword(pctx->keyword_else); + assert(token->intern_val == pctx->keyword_else); + Ast_Scope *else_block = parse_stmt_scope(); + Ast_If_Node *if_node = ast_if_node(token, 0, 0, else_block); + if_nodes.add(if_node); + break; + } + } + Ast_If *result_if = ast_if(token, if_nodes); + scope->stmts.add(result_if); + } + else if (token_is(TK_Identifier) && token_is(TK_Comma, 1)) { + Array decls = {scratch}; + do { + Token *name = token_match(TK_Identifier); + Ast_Decl *decl = ast_var(name, 0, name->intern_val, 0); + decls.add(decl); + } while (token_match(TK_Comma)); + + token_expect(TK_ColonAssign); + Ast_Expr *expr = parse_expr(); + Ast_Var_Unpack *vars = ast_var_unpack(token, decls, expr); + scope->stmts.add(vars); + } + else { + Ast *result = parse_decl(false); + if (result && result->kind != AST_VAR && result->kind != AST_CONST) { + compiler_error(token, "Invalid statement construct"); + } + if (!result) { + result = parse_expr(); + result = parse_init_stmt((Ast_Expr *)result); + } + + if (result) { + result->flags = set_flag(result->flags, AST_STMT); + scope->stmts.add(result); + } + else { + compiler_error(token, "Unexpected token [%s] while parsing statement", name(token->kind)); + } + } + } while (token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); + + if (!scope_defined_outside) finalize_stmt_scope(scope); + } + return scope; } CORE_Static Ast_Lambda * -parse_lambda(Token *token){ - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); +parse_lambda(Token *token) { + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); - Array params = {scratch}; - if(!token_is(TK_CloseParen)){ - for(;;){ - Token *name = token_get(); - if(token_match(TK_Identifier)){ - token_expect(TK_Colon); - Ast_Decl *param = ast_new(Ast_Decl, AST_VAR, name, AST_DECL); - param->name = name->intern_val; + Array params = {scratch}; + if (!token_is(TK_CloseParen)) { + for (;;) { + Token *name = token_get(); + if (token_match(TK_Identifier)) { + token_expect(TK_Colon); + Ast_Decl *param = ast_new(Ast_Decl, AST_VAR, name, AST_DECL); + param->name = name->intern_val; - param->typespec = parse_expr(); - if(token_match(TK_Assign)) - param->expr = parse_expr(); + param->typespec = parse_expr(); + if (token_match(TK_Assign)) + param->expr = parse_expr(); - params.add(param); - } - else compiler_error(name, "Expected [Identifier] or [..] when parsing lambda arguments"); + params.add(param); + } + else compiler_error(name, "Expected [Identifier] or [..] when parsing lambda arguments"); - if(!token_match(TK_Comma)) - break; + if (!token_match(TK_Comma)) + break; + } } - } - token_expect(TK_CloseParen); + token_expect(TK_CloseParen); - Array ret = {scratch}; - if(token_match(TK_Colon)){ - do{ - Ast_Expr *typespec = parse_expr(); - ret.add(typespec); - }while(token_match(TK_Comma)); - } - else ret.add(ast_ident(token, pctx->intern_void)); + Array ret = {scratch}; + if (token_match(TK_Colon)) { + do { + Ast_Expr *typespec = parse_expr(); + ret.add(typespec); + } while (token_match(TK_Comma)); + } + else ret.add(ast_ident(token, pctx->intern_void)); - Ast_Scope *scope = token_is(OPEN_SCOPE) ? parse_stmt_scope() : 0; - Ast_Lambda *result = ast_lambda(token, params, ret, scope); - return result; + Ast_Scope *scope = token_is(OPEN_SCOPE) ? parse_stmt_scope() : 0; + Ast_Lambda *result = ast_lambda(token, params, ret, scope); + return result; } //----------------------------------------------------------------------------- // Pratt expression parser // Based on this really good article: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html //----------------------------------------------------------------------------- -struct Binding_Power{S64 left;S64 right;}; -enum Binding{Binding_Prefix,Binding_Infix,Binding_Postfix}; +struct Binding_Power { + S64 left; + S64 right; +}; +enum Binding { Binding_Prefix, + Binding_Infix, + Binding_Postfix }; CORE_Static Binding_Power -binding_power(Binding binding, Token_Kind kind){ - if(binding == Binding_Prefix) goto Prefix; - if(binding == Binding_Infix) goto Infix; - if(binding == Binding_Postfix) goto Postfix; - else invalid_codepath; +binding_power(Binding binding, Token_Kind kind) { + if (binding == Binding_Prefix) goto Prefix; + if (binding == Binding_Infix) goto Infix; + if (binding == Binding_Postfix) goto Postfix; + else invalid_codepath; - Prefix: switch(kind){ - case TK_OpenBracket: - return {-2, 22}; - case TK_Increment: - case TK_Decrement: - case TK_Pointer: - case TK_Dereference: - case TK_Keyword: - case TK_OpenParen: - case TK_OpenBrace: - case TK_Sub: - case TK_Add: - case TK_Neg: - case TK_Not: - return{-2, 20}; - default: return {-1, -1}; - } - Infix: switch(kind){ - case TK_Or: - return {9,10}; - case TK_And: - return {11,12}; - case TK_Equals: - case TK_NotEquals: - case TK_GreaterThen: - case TK_GreaterThenOrEqual: - case TK_LesserThen: - case TK_LesserThenOrEqual: - return {13,14}; - case TK_Sub: - case TK_Add: - case TK_BitOr: - case TK_BitXor: - return {15,16}; - case TK_RightShift: - case TK_LeftShift: - case TK_BitAnd: - case TK_Mul: - case TK_Div: - case TK_Mod: - return {17,18}; - case TK_Dot: - return {31,30}; - case TK_Arrow: - return {20,19}; - default: return {}; - } - Postfix: switch(kind){ - case TK_Increment: - case TK_Decrement: - case TK_OpenBracket: - case TK_OpenParen: - case TK_OpenBrace: - return {21, -2}; - default: return{-1,-1}; - } +Prefix: + switch (kind) { + case TK_OpenBracket: + return {-2, 22}; + case TK_Increment: + case TK_Decrement: + case TK_Pointer: + case TK_Dereference: + case TK_Keyword: + case TK_OpenParen: + case TK_OpenBrace: + case TK_Sub: + case TK_Add: + case TK_Neg: + case TK_Not: + return {-2, 20}; + default: return {-1, -1}; + } +Infix: + switch (kind) { + case TK_Or: + return {9, 10}; + case TK_And: + return {11, 12}; + case TK_Equals: + case TK_NotEquals: + case TK_GreaterThen: + case TK_GreaterThenOrEqual: + case TK_LesserThen: + case TK_LesserThenOrEqual: + return {13, 14}; + case TK_Sub: + case TK_Add: + case TK_BitOr: + case TK_BitXor: + return {15, 16}; + case TK_RightShift: + case TK_LeftShift: + case TK_BitAnd: + case TK_Mul: + case TK_Div: + case TK_Mod: + return {17, 18}; + case TK_Dot: + return {31, 30}; + case TK_Arrow: + return {20, 19}; + default: return {}; + } +Postfix: + switch (kind) { + case TK_Increment: + case TK_Decrement: + case TK_OpenBracket: + case TK_OpenParen: + case TK_OpenBrace: + return {21, -2}; + default: return {-1, -1}; + } } CORE_Static Ast_Expr * -parse_expr(S64 min_bp){ - Ast_Expr *left = 0; - Token *token = token_next(); - Binding_Power prefix_bp = binding_power(Binding_Prefix, token->kind); +parse_expr(S64 min_bp) { + Ast_Expr *left = 0; + Token *token = token_next(); + Binding_Power prefix_bp = binding_power(Binding_Prefix, token->kind); - // @note: parse prefix expression - switch(token->kind){ - case TK_StringLit : left = ast_str(token, token->intern_val); break; - case TK_Identifier : left = ast_ident(token, token->intern_val); break; - case TK_Integer : left = ast_int(token, token->int_val); break; - case TK_UnicodeLit : left = ast_int(token, token->unicode); break; - case TK_Float : left = ast_float(token, token->f64_val); break; - case TK_Pointer : left = ast_expr_unary(token, TK_Pointer, parse_expr(prefix_bp.right)); break; - case TK_Dereference: left = ast_expr_unary(token, TK_Dereference, parse_expr(prefix_bp.right)); break; - case TK_Sub : left = ast_expr_unary(token, TK_Sub, parse_expr(prefix_bp.right)); break; - case TK_Add : left = ast_expr_unary(token, TK_Add, parse_expr(prefix_bp.right)); break; - case TK_Not : left = ast_expr_unary(token, TK_Not, parse_expr(prefix_bp.right)); break; - case TK_Neg : left = ast_expr_unary(token, TK_Neg, parse_expr(prefix_bp.right)); break; - case TK_Increment : left = ast_expr_unary(token, TK_Increment, parse_expr(prefix_bp.right)); break; - case TK_Decrement : left = ast_expr_unary(token, TK_Decrement, parse_expr(prefix_bp.right)); break; + // @note: parse prefix expression + switch (token->kind) { + case TK_StringLit: left = ast_str(token, token->intern_val); break; + case TK_Identifier: left = ast_ident(token, token->intern_val); break; + case TK_Integer: left = ast_int(token, token->int_val); break; + case TK_UnicodeLit: left = ast_int(token, token->unicode); break; + case TK_Float: left = ast_float(token, token->f64_val); break; + case TK_Pointer: left = ast_expr_unary(token, TK_Pointer, parse_expr(prefix_bp.right)); break; + case TK_Dereference: left = ast_expr_unary(token, TK_Dereference, parse_expr(prefix_bp.right)); break; + case TK_Sub: left = ast_expr_unary(token, TK_Sub, parse_expr(prefix_bp.right)); break; + case TK_Add: left = ast_expr_unary(token, TK_Add, parse_expr(prefix_bp.right)); break; + case TK_Not: left = ast_expr_unary(token, TK_Not, parse_expr(prefix_bp.right)); break; + case TK_Neg: left = ast_expr_unary(token, TK_Neg, parse_expr(prefix_bp.right)); break; + case TK_Increment: left = ast_expr_unary(token, TK_Increment, parse_expr(prefix_bp.right)); break; + case TK_Decrement: left = ast_expr_unary(token, TK_Decrement, parse_expr(prefix_bp.right)); break; - case TK_OpenBracket: { - Ast_Expr *expr = 0; - if(!token_is(TK_CloseBracket)) - expr = parse_expr(0); + case TK_OpenBracket: { + Ast_Expr *expr = 0; + if (!token_is(TK_CloseBracket)) + expr = parse_expr(0); - Ast_Array *result = ast_array(token, expr); - token_expect(TK_CloseBracket); - result->base = parse_expr(prefix_bp.right); - left = result; - }break; + Ast_Array *result = ast_array(token, expr); + token_expect(TK_CloseBracket); + result->base = parse_expr(prefix_bp.right); + left = result; + } break; - case TK_OpenBrace: { - left = parse_expr_call(0, TK_CloseBrace); - left->kind = AST_COMPOUND; - }break; - - case TK_Keyword: { - if(token->intern_val == pctx->keyword_true) - left = ast_bool(token, 1); - else if(token->intern_val == pctx->keyword_false) - left = ast_bool(token, 0); - else compiler_error(token, "Unexpected keyword: [%s]", token->intern_val.str); - }break; - - case TK_OpenParen: { - if(token_is(TK_CloseParen) || (token_is(TK_Identifier) && token_is(TK_Colon, 1)) || token_is(TK_ThreeDots)) - left = parse_lambda(token); - else{ - left = parse_expr(0); - token_expect(TK_CloseParen); - } - }break; - default: compiler_error(token, "Unexpected token of kind: [%s] in expression", name(token->kind)); return 0; - } - - for(;;){ - token = token_get(); - - // lets say [+] is left:1, right:2 and we parse 2+3+4 - // We pass min_bp of 2 to the next recursion - // in recursion we check if left(1) > min_bp(2) - // it's not so we don't recurse - we break - // We do standard do the for loop instead - - Binding_Power postfix_bp = binding_power(Binding_Postfix, token->kind); - Binding_Power infix_bp = binding_power(Binding_Infix, token->kind); - - // @note: parse postfix expression - if(postfix_bp.left > min_bp){ - token_next(); - switch(token->kind){ - case TK_OpenBracket:{ - Ast_Expr *index = parse_expr(0); - token_expect(TK_CloseBracket); - left = ast_expr_index(token, left, index); - }break; case TK_OpenBrace: { - left = parse_expr_call(left, TK_CloseBrace); - left->kind = AST_COMPOUND; - }break; - case TK_OpenParen:{ - left = parse_expr_call(left, TK_CloseParen); - }break; - default:{ - assert(token->kind == TK_Increment || token->kind == TK_Decrement); - if(token->kind == TK_Increment) token->kind = TK_PostIncrement; - else if(token->kind == TK_Decrement) token->kind = TK_PostDecrement; - left = ast_expr_unary(token, token->kind, left); + left = parse_expr_call(0, TK_CloseBrace); + left->kind = AST_COMPOUND; + } break; + + case TK_Keyword: { + if (token->intern_val == pctx->keyword_true) + left = ast_bool(token, 1); + else if (token->intern_val == pctx->keyword_false) + left = ast_bool(token, 0); + else compiler_error(token, "Unexpected keyword: [%s]", token->intern_val.str); + } break; + + case TK_OpenParen: { + if (token_is(TK_CloseParen) || (token_is(TK_Identifier) && token_is(TK_Colon, 1)) || token_is(TK_ThreeDots)) + left = parse_lambda(token); + else { + left = parse_expr(0); + token_expect(TK_CloseParen); + } + } break; + default: compiler_error(token, "Unexpected token of kind: [%s] in expression", name(token->kind)); return 0; + } + + for (;;) { + token = token_get(); + + // lets say [+] is left:1, right:2 and we parse 2+3+4 + // We pass min_bp of 2 to the next recursion + // in recursion we check if left(1) > min_bp(2) + // it's not so we don't recurse - we break + // We do standard do the for loop instead + + Binding_Power postfix_bp = binding_power(Binding_Postfix, token->kind); + Binding_Power infix_bp = binding_power(Binding_Infix, token->kind); + + // @note: parse postfix expression + if (postfix_bp.left > min_bp) { + token_next(); + switch (token->kind) { + case TK_OpenBracket: { + Ast_Expr *index = parse_expr(0); + token_expect(TK_CloseBracket); + left = ast_expr_index(token, left, index); + } break; + case TK_OpenBrace: { + left = parse_expr_call(left, TK_CloseBrace); + left->kind = AST_COMPOUND; + } break; + case TK_OpenParen: { + left = parse_expr_call(left, TK_CloseParen); + } break; + default: { + assert(token->kind == TK_Increment || token->kind == TK_Decrement); + if (token->kind == TK_Increment) token->kind = TK_PostIncrement; + else if (token->kind == TK_Decrement) token->kind = TK_PostDecrement; + left = ast_expr_unary(token, token->kind, left); + } + } } - } + + // @note: parse infix expression + else if (infix_bp.left > min_bp) { + token = token_next(); + Ast_Expr *right = parse_expr(infix_bp.right); + left = ast_expr_binary(left, right, token); + } + + else break; } - - // @note: parse infix expression - else if(infix_bp.left > min_bp){ - token = token_next(); - Ast_Expr *right = parse_expr(infix_bp.right); - left = ast_expr_binary(left, right, token); - } - - else break; - - } - return left; + return left; } CORE_Static Ast_Expr * -parse_assign_expr(){ - Ast_Expr *result = 0; - if(token_match(TK_Assign)) result = parse_expr(); - return result; +parse_assign_expr() { + Ast_Expr *result = 0; + if (token_match(TK_Assign)) result = parse_expr(); + return result; } CORE_Static Ast_Decl * -parse_struct(Token *pos){ - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); +parse_struct(Token *pos) { + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); - token_match(OPEN_SCOPE); - Ast_Scope *scope = begin_decl_scope(scratch, token_get()); - do{ - Token *token = token_expect(TK_Identifier); - token_expect(TK_Colon); + token_match(OPEN_SCOPE); + Ast_Scope *scope = begin_decl_scope(scratch, token_get()); + do { + Token *token = token_expect(TK_Identifier); + token_expect(TK_Colon); - Ast_Expr *typespec = parse_expr(); - Ast_Decl *decl = ast_var(token, typespec, token->intern_val, 0); - decl->flags = set_flag(decl->flags, AST_AGGREGATE_CHILD); + Ast_Expr *typespec = parse_expr(); + Ast_Decl *decl = ast_var(token, typespec, token->intern_val, 0); + decl->flags = set_flag(decl->flags, AST_AGGREGATE_CHILD); - add(pctx->perm, &scope->decls, decl); + add(pctx->perm, &scope->decls, decl); - }while(token_match(SAME_SCOPE)); - token_expect(CLOSE_SCOPE); + } while (token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); - finalize_decl_scope(scope); - Ast_Decl *result = ast_struct(pos, scope); - return result; + finalize_decl_scope(scope); + Ast_Decl *result = ast_struct(pos, scope); + return result; } CORE_Static Ast_Decl * -parse_enum(Token *pos){ - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); - Ast_Expr *typespec = parse_optional_type(); - Token *flag = token_match_pound(pctx->intern_flag); +parse_enum(Token *pos) { + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); + Ast_Expr *typespec = parse_optional_type(); + Token *flag = token_match_pound(pctx->intern_flag); - token_match(OPEN_SCOPE); - Ast_Scope *scope = begin_decl_scope(scratch, token_get()); - do{ - Token *name = token_expect(TK_Identifier); - Ast_Expr *value = 0; - if(token_match(TK_DoubleColon)) value = parse_expr(); - Ast_Decl *member = ast_const(name, name->intern_val, value); - member->flags = set_flag(member->flags, AST_AGGREGATE_CHILD); - add(pctx->perm, &scope->decls, member); - }while(token_match(SAME_SCOPE)); - finalize_decl_scope(scope); - token_expect(CLOSE_SCOPE); + token_match(OPEN_SCOPE); + Ast_Scope *scope = begin_decl_scope(scratch, token_get()); + do { + Token *name = token_expect(TK_Identifier); + Ast_Expr *value = 0; + if (token_match(TK_DoubleColon)) value = parse_expr(); + Ast_Decl *member = ast_const(name, name->intern_val, value); + member->flags = set_flag(member->flags, AST_AGGREGATE_CHILD); + add(pctx->perm, &scope->decls, member); + } while (token_match(SAME_SCOPE)); + finalize_decl_scope(scope); + token_expect(CLOSE_SCOPE); - Ast_Decl *result = ast_enum(pos, typespec, scope); - if(flag) set_flag(result->flags, AST_FLAG); - return result; + Ast_Decl *result = ast_enum(pos, typespec, scope); + if (flag) set_flag(result->flags, AST_FLAG); + return result; } CORE_Static void -add_implicit_import(Ast_Scope *scope, Ast_Scope *to_add){ - B32 found = false; - For(scope->implicit_imports){ - if(it == to_add){ - found = true; - break; +add_implicit_import(Ast_Scope *scope, Ast_Scope *to_add) { + B32 found = false; + For(scope->implicit_imports) { + if (it == to_add) { + found = true; + break; + } + } + if (!found) { + add(pctx->perm, &scope->implicit_imports, to_add); } - } - if(!found){ - add(pctx->perm, &scope->implicit_imports, to_add); - } } -enum{ GLOBAL_IMPLICIT_LOAD = 1 }; +enum { GLOBAL_IMPLICIT_LOAD = 1 }; CORE_Static Ast_File * -register_ast_file(Token *pos, String absolute_file_path, Ast_Module *module, B32 global_implicit_load){ - Ast_File *file = 0; +register_ast_file(Token *pos, String absolute_file_path, Ast_Module *module, B32 global_implicit_load) { + Ast_File *file = 0; - For(pctx->files){ - if(string_compare(it->absolute_file_path, absolute_file_path)){ - if(module == it->module){ - log_trace("%Q :: Returning registered file: %Q\n", module->absolute_file_path, absolute_file_path); - file = it; - break; - } + For(pctx->files) { + if (string_compare(it->absolute_file_path, absolute_file_path)) { + if (module == it->module) { + log_trace("%Q :: Returning registered file: %Q\n", module->absolute_file_path, absolute_file_path); + file = it; + break; + } - compiler_error(it->pos, pos, "This file is already loaded by module: %Q, try importing that module to get access to it", module->absolute_file_path); + compiler_error(it->pos, pos, "This file is already loaded by module: %Q, try importing that module to get access to it", module->absolute_file_path); + } } - } - if(!file){ - log_trace("%Q :: Registering file: %Q\n", module->absolute_file_path, absolute_file_path); - AST_NEW(File, FILE, 0, 0); - file = result; - file->absolute_file_path = absolute_file_path; - file->absolute_base_folder = string_copy(pctx->perm, string_chop_last_slash(file->absolute_file_path)); - file->module = module; - file->parent_scope = module; - file->file = file; // @warning: self referential! - file->pos = pos; - file->debug_name = string_skip_to_last_slash(absolute_file_path); - add(pctx->perm, &file->module->all_loaded_files, file); - file->scope_id = pctx->scope_ids++; - add(pctx->perm, &pctx->files, file); - } + if (!file) { + log_trace("%Q :: Registering file: %Q\n", module->absolute_file_path, absolute_file_path); + AST_NEW(File, FILE, 0, 0); + file = result; + file->absolute_file_path = absolute_file_path; + file->absolute_base_folder = string_copy(pctx->perm, string_chop_last_slash(file->absolute_file_path)); + file->module = module; + file->parent_scope = module; + file->file = file; // @warning: self referential! + file->pos = pos; + file->debug_name = string_skip_to_last_slash(absolute_file_path); + add(pctx->perm, &file->module->all_loaded_files, file); + file->scope_id = pctx->scope_ids++; + add(pctx->perm, &pctx->files, file); + } - if(global_implicit_load) { - add_implicit_import(module, file); - } + if (global_implicit_load) { + add_implicit_import(module, file); + } - return file; + return file; } CORE_Static Intern_String -preprocess_filename(Token *token_filename){ - Scratch_Scope _scope(pctx->scratch); - String filename = token_filename->intern_val.s; - Array replace = {pctx->scratch}; - replace.add({"$OS"_s, OS_NAME}); - replace.add({"$os"_s, OS_NAME_LOWER}); - String result0 = string_replace(pctx->scratch, pctx->scratch, filename, replace); - Intern_String result = pctx->intern(result0); - return result; +preprocess_filename(Token *token_filename) { + Scratch_Scope _scope(pctx->scratch); + String filename = token_filename->intern_val.s; + Array replace = {pctx->scratch}; + replace.add({"$OS"_s, OS_NAME}); + replace.add({"$os"_s, OS_NAME_LOWER}); + String result0 = string_replace(pctx->scratch, pctx->scratch, filename, replace); + Intern_String result = pctx->intern(result0); + return result; } CORE_Static Ast_File * -parse_load(B32 global_implicit_load){ - Token *file = token_expect(TK_StringLit); - Intern_String filename = preprocess_filename(file); - String absolute_path = string_fmt(pctx->perm, "%Q/%Q", pctx->currently_parsed_file->absolute_base_folder, filename); - Ast_File *result = register_ast_file(file, absolute_path, pctx->currently_parsed_file->module, global_implicit_load); - return result; +parse_load(B32 global_implicit_load) { + Token *file = token_expect(TK_StringLit); + Intern_String filename = preprocess_filename(file); + String absolute_path = string_fmt(pctx->perm, "%Q/%Q", pctx->currently_parsed_file->absolute_base_folder, filename); + Ast_File *result = register_ast_file(file, absolute_path, pctx->currently_parsed_file->module, global_implicit_load); + return result; } CORE_Static Ast_Module *add_module(Token *pos, Intern_String filename, B32 command_line_module = false, bool string_only_module = false); CORE_Static Ast_Module * -parse_import(B32 global_implicit_import){ - Token *file = token_expect(TK_StringLit); - Intern_String filename = preprocess_filename(file); - Ast_Module *result = add_module(file, filename); - if(global_implicit_import){ - add_implicit_import(pctx->currently_parsed_file->module, result); - } - return result; +parse_import(B32 global_implicit_import) { + Token *file = token_expect(TK_StringLit); + Intern_String filename = preprocess_filename(file); + Ast_Module *result = add_module(file, filename); + if (global_implicit_import) { + add_implicit_import(pctx->currently_parsed_file->module, result); + } + return result; } /* @@ -830,156 +838,159 @@ for parsing statements and it makes code nicer. Statements can have named syntax i := */ CORE_Static Ast_Decl * -parse_decl(B32 is_global){ - Ast_Decl *result = 0; - if(is_global) { - token_match(SAME_SCOPE); - if(pctx->indent != 0){ - compiler_error(token_get(), "Top level declarations shouldn't be indented"); - } - } - - Ast_Flag flags = 0; - Token *tname = token_get(); - if(token_match(TK_Identifier, TK_DoubleColon)){ - - if(token_match_pound(pctx->intern_foreign)){ - set_flag(flags, AST_FOREIGN); - } else if(token_match_pound(pctx->intern_strict)){ - set_flag(flags, AST_STRICT); - } - - // @note parse struct binding - if(token_match_keyword(pctx->keyword_struct)){ - result = parse_struct(tname); - } - - else if(token_match_keyword(pctx->keyword_enum)){ - result = parse_enum(tname); - } - - else if(token_match_pound(pctx->intern("import"_s))){ - Ast_Module *module = parse_import(false); - result = ast_namespace(tname, module, tname->intern_val); - } - - else{ - Ast_Expr *expr = parse_expr(); - result = ast_const(tname, tname->intern_val, expr); - - if(expr->kind == AST_LAMBDA_EXPR){ - auto a = (Ast_Lambda *)expr; - if(a->scope || is_flag_set(flags, AST_FOREIGN)){ - result->kind = AST_LAMBDA; - if(is_flag_set(flags, AST_FOREIGN)) - set_flag(expr->flags, flags); +parse_decl(B32 is_global) { + Ast_Decl *result = 0; + if (is_global) { + token_match(SAME_SCOPE); + if (pctx->indent != 0) { + compiler_error(token_get(), "Top level declarations shouldn't be indented"); } - } - } - } - else if(token_match(TK_StringLit, TK_DoubleColon)){ - Ast_Lambda *expr = (Ast_Lambda *)parse_expr(); - if(expr->kind != AST_LAMBDA_EXPR){ - compiler_error(tname, "Operator overload is required to be a lambda function"); - } - if(!expr->scope){ - compiler_error(tname, "Operator overload doesn't have body"); - } - Ast_Operator_Info *op_info = get_operator_info(tname->intern_val); - if(!op_info){ - compiler_error(tname, "This operator cannot be overloaded"); } - if(expr->args.len == 1){ - if(!op_info->valid_unary_expr){ - compiler_error(tname, "This operator cannot have a unary expression"); - } + Ast_Flag flags = 0; + Token *tname = token_get(); + if (token_match(TK_Identifier, TK_DoubleColon)) { + + if (token_match_pound(pctx->intern_foreign)) { + set_flag(flags, AST_FOREIGN); + } + else if (token_match_pound(pctx->intern_strict)) { + set_flag(flags, AST_STRICT); + } + + // @note parse struct binding + if (token_match_keyword(pctx->keyword_struct)) { + result = parse_struct(tname); + } + + else if (token_match_keyword(pctx->keyword_enum)) { + result = parse_enum(tname); + } + + else if (token_match_pound(pctx->intern("import"_s))) { + Ast_Module *module = parse_import(false); + result = ast_namespace(tname, module, tname->intern_val); + } + + else { + Ast_Expr *expr = parse_expr(); + result = ast_const(tname, tname->intern_val, expr); + + if (expr->kind == AST_LAMBDA_EXPR) { + auto a = (Ast_Lambda *)expr; + if (a->scope || is_flag_set(flags, AST_FOREIGN)) { + result->kind = AST_LAMBDA; + if (is_flag_set(flags, AST_FOREIGN)) + set_flag(expr->flags, flags); + } + } + } } - else if(expr->args.len == 2){ - if(!op_info->valid_binary_expr){ - compiler_error(tname, "This operator cannot have a binary expression"); - } + else if (token_match(TK_StringLit, TK_DoubleColon)) { + Ast_Lambda *expr = (Ast_Lambda *)parse_expr(); + if (expr->kind != AST_LAMBDA_EXPR) { + compiler_error(tname, "Operator overload is required to be a lambda function"); + } + if (!expr->scope) { + compiler_error(tname, "Operator overload doesn't have body"); + } + Ast_Operator_Info *op_info = get_operator_info(tname->intern_val); + if (!op_info) { + compiler_error(tname, "This operator cannot be overloaded"); + } + + if (expr->args.len == 1) { + if (!op_info->valid_unary_expr) { + compiler_error(tname, "This operator cannot have a unary expression"); + } + } + else if (expr->args.len == 2) { + if (!op_info->valid_binary_expr) { + compiler_error(tname, "This operator cannot have a binary expression"); + } + } + else { + compiler_error(tname, "Invalid argument count for operator overload, unhandled operator"); + } + + result = ast_const(tname, tname->intern_val, expr); + result->overload_op_info = op_info; + result->kind = AST_LAMBDA; + result->flags = set_flag(result->flags, AST_OPERATOR_OVERLOAD); } - else { - compiler_error(tname, "Invalid argument count for operator overload, unhandled operator"); + else if (token_match(TK_Identifier, TK_Colon)) { + Ast_Expr *typespec = parse_expr(); + Ast_Expr *expr = parse_assign_expr(); + if (token_match_pound(pctx->intern_foreign)) + set_flag(flags, AST_FOREIGN); + + result = ast_var(tname, typespec, tname->intern_val, expr); } - result = ast_const(tname, tname->intern_val, expr); - result->overload_op_info = op_info; - result->kind = AST_LAMBDA; - result->flags = set_flag(result->flags, AST_OPERATOR_OVERLOAD); - } - else if(token_match(TK_Identifier, TK_Colon)){ - Ast_Expr *typespec = parse_expr(); - Ast_Expr *expr = parse_assign_expr(); - if(token_match_pound(pctx->intern_foreign)) - set_flag(flags, AST_FOREIGN); + else if (token_match(TK_Identifier, TK_ColonAssign)) { + Ast_Expr *expr = parse_expr(); + result = ast_var(tname, 0, tname->intern_val, expr); + } - result = ast_var(tname, typespec, tname->intern_val, expr); - } + else if (is_global && tname->kind != TK_End) { + compiler_error(tname, "Unexpected token: [%s] when parsing a declaration", name(tname->kind)); + } - else if(token_match(TK_Identifier, TK_ColonAssign)){ - Ast_Expr *expr = parse_expr(); - result = ast_var(tname, 0, tname->intern_val, expr); - } + if (result) { + set_flag(result->flags, flags); + result->name = tname->intern_val; + } - else if(is_global && tname->kind != TK_End){ - compiler_error(tname, "Unexpected token: [%s] when parsing a declaration", name(tname->kind)); - } - - if(result){ - set_flag(result->flags, flags); - result->name = tname->intern_val; - } - - return result; + return result; } CORE_Static void -parse_file(Ast_File *file){ - assert(file); - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); +parse_file(Ast_File *file) { + assert(file); + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); - if (!file->filecontent.len) { - file->filecontent = os_read_file(pctx->perm, file->absolute_file_path); - if(file->filecontent.len == 0){ - compiler_error(file->pos, "Failed to open file \"%Q\"", file->absolute_file_path); - } - } - - pctx->currently_parsed_file = file; - pctx->currently_parsed_scope = file; - lex_restream(pctx, file->filecontent, file->absolute_file_path); - while (token_expect(SAME_SCOPE)) { - if (token_match_pound(pctx->intern_load)) { - parse_load(true); - continue; - } else if (token_match_pound(pctx->intern_import)) { - parse_import(true); - continue; - } else if (token_match_pound(pctx->intern_link)) { - Token *file = token_expect(TK_StringLit); - add(pctx->perm, &pctx->files_to_link, file); - continue; + if (!file->filecontent.len) { + file->filecontent = os_read_file(pctx->perm, file->absolute_file_path); + if (file->filecontent.len == 0) { + compiler_error(file->pos, "Failed to open file \"%Q\"", file->absolute_file_path); + } } - if(!file->pos){ - file->pos = token_get(); + pctx->currently_parsed_file = file; + pctx->currently_parsed_scope = file; + lex_restream(pctx, file->filecontent, file->absolute_file_path); + while (token_expect(SAME_SCOPE)) { + if (token_match_pound(pctx->intern_load)) { + parse_load(true); + continue; + } + else if (token_match_pound(pctx->intern_import)) { + parse_import(true); + continue; + } + else if (token_match_pound(pctx->intern_link)) { + Token *file = token_expect(TK_StringLit); + add(pctx->perm, &pctx->files_to_link, file); + continue; + } + + if (!file->pos) { + file->pos = token_get(); + } + + Ast_Decl *decl = parse_decl(true); + if (!decl) break; + + set_flag(decl->flags, AST_GLOBAL); + if (decl->kind == AST_STRUCT) { + decl->type = pctx->type_type; + decl->type_val = type_incomplete(decl); + decl->state = DECL_RESOLVED; + } + + insert_into_scope(file, decl); } - - Ast_Decl *decl = parse_decl(true); - if(!decl) break; - - set_flag(decl->flags, AST_GLOBAL); - if(decl->kind == AST_STRUCT){ - decl->type = pctx->type_type; - decl->type_val = type_incomplete(decl); - decl->state = DECL_RESOLVED; - } - - insert_into_scope(file, decl); - } - pctx->currently_parsed_scope = 0; - pctx->currently_parsed_file = 0; + pctx->currently_parsed_scope = 0; + pctx->currently_parsed_file = 0; } diff --git a/core_typechecking.cpp b/core_typechecking.cpp index 8bab5dc..093eebe 100644 --- a/core_typechecking.cpp +++ b/core_typechecking.cpp @@ -1,607 +1,624 @@ CORE_Static Resolve_Flag -inherit_flag(Resolve_Flag flag, B32 ast_can_be_null){ - unset_flag(flag, AST_CAN_BE_NULL); - set_flag(flag, ast_can_be_null); - return flag; +inherit_flag(Resolve_Flag flag, B32 ast_can_be_null) { + unset_flag(flag, AST_CAN_BE_NULL); + set_flag(flag, ast_can_be_null); + return flag; } //----------------------------------------------------------------------------- // Operands //----------------------------------------------------------------------------- CORE_Static Operand -operand(Ast_Decl *decl){ - Operand result = {}; - result.type = decl->type; - result.is_const = decl->kind != AST_VAR ? true : false; - result.is_lvalue= decl->kind == AST_CONST ? false : true; // Cant assign to const values - result.value = decl->value; - result.resolved_decl = decl; - return result; +operand(Ast_Decl *decl) { + Operand result = {}; + result.type = decl->type; + result.is_const = decl->kind != AST_VAR ? true : false; + result.is_lvalue = decl->kind == AST_CONST ? false : true; // Cant assign to const values + result.value = decl->value; + result.resolved_decl = decl; + return result; } static Operand operand_type(Ast_Type *type) { - Operand result = {}; - result.type = pctx->type_type; - result.is_const = true; - result.is_lvalue = false; - result.type_val = type; - return result; + Operand result = {}; + result.type = pctx->type_type; + result.is_const = true; + result.is_lvalue = false; + result.type_val = type; + return result; } CORE_Static Operand -operand_int(BigInt big_int){ - Operand result = {}; - result.type = pctx->untyped_int; - result.big_int_val = bigint_copy(pctx->perm, &big_int); - result.is_const = true; - result.is_lvalue = false; - return result; +operand_int(BigInt big_int) { + Operand result = {}; + result.type = pctx->untyped_int; + result.big_int_val = bigint_copy(pctx->perm, &big_int); + result.is_const = true; + result.is_lvalue = false; + return result; } CORE_Static Operand -operand_str(Intern_String intern_val){ - Operand result = {}; - result.type = pctx->type_string; - result.intern_val = intern_val; - result.is_const = true; - result.is_lvalue = false; - return result; +operand_str(Intern_String intern_val) { + Operand result = {}; + result.type = pctx->type_string; + result.intern_val = intern_val; + result.is_const = true; + result.is_lvalue = false; + return result; } CORE_Static Operand -operand_lambda(Ast_Type *type){ - Operand result = {}; - result.type = type; - result.is_const = true; - result.is_lvalue = false; - return result; +operand_lambda(Ast_Type *type) { + Operand result = {}; + result.type = type; + result.is_const = true; + result.is_lvalue = false; + return result; } CORE_Static Operand -operand_const_rvalue(Value value){ - Operand result = {}; - result.is_const = true; - result.value = value; - return result; +operand_const_rvalue(Value value) { + Operand result = {}; + result.is_const = true; + result.value = value; + return result; } CORE_Static Operand -operand_lvalue_set_flag_on_node(Ast_Type *type, Ast *ast){ - Operand result = {}; - result.type = type; - result.is_const = false; - result.is_lvalue = true; - set_flag(ast->flags, AST_IS_LVALUE); - return result; +operand_lvalue_set_flag_on_node(Ast_Type *type, Ast *ast) { + Operand result = {}; + result.type = type; + result.is_const = false; + result.is_lvalue = true; + set_flag(ast->flags, AST_IS_LVALUE); + return result; } CORE_Static Operand -operand_rvalue(Ast_Type *type){ - Operand result = {}; - result.type = type; - result.is_const = false; - result.is_lvalue = false; - return result; +operand_rvalue(Ast_Type *type) { + Operand result = {}; + result.type = type; + result.is_const = false; + result.is_lvalue = false; + return result; } CORE_Static Ast_Type * -get_default_type_from_untyped(Ast_Type *type){ - switch(type->kind){ - case TYPE_UNTYPED_INT: return pctx->type_s64; break; - case TYPE_UNTYPED_BOOL: return pctx->type_bool; break; - case TYPE_UNTYPED_STRING: return pctx->type_string; break; - case TYPE_UNTYPED_FLOAT: return pctx->type_f32; break; - default: invalid_codepath; - } - return 0; +get_default_type_from_untyped(Ast_Type *type) { + switch (type->kind) { + case TYPE_UNTYPED_INT: return pctx->type_s64; break; + case TYPE_UNTYPED_BOOL: return pctx->type_bool; break; + case TYPE_UNTYPED_STRING: return pctx->type_string; break; + case TYPE_UNTYPED_FLOAT: return pctx->type_f32; break; + default: invalid_codepath; + } + return 0; } CORE_Static void -try_converting_untyped_to_default_type(Value *op){ - if(is_untyped(op->type)){ - op->type = get_default_type_from_untyped(op->type); - } +try_converting_untyped_to_default_type(Value *op) { + if (is_untyped(op->type)) { + op->type = get_default_type_from_untyped(op->type); + } } CORE_Static void -try_converting_untyped_to_default_type(Operand *op){ - try_converting_untyped_to_default_type(&op->value); +try_converting_untyped_to_default_type(Operand *op) { + try_converting_untyped_to_default_type(&op->value); } CORE_Static void -check_value_bounds(Token *pos, Value *a){ - if(!is_int(a->type)) return; +check_value_bounds(Token *pos, Value *a) { + if (!is_int(a->type)) return; - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); - if(!bigint_fits_in_bits(&a->big_int_val, a->type->size*8, is_signed_int(a->type))){ - const char *string = bigint_to_error_string(scratch, &a->big_int_val, 10); - compiler_error(pos, "Value %s doesn't fit in type %Q", string, typestring(a->type)); - } + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); + if (!bigint_fits_in_bits(&a->big_int_val, a->type->size * 8, is_signed_int(a->type))) { + const char *string = bigint_to_error_string(scratch, &a->big_int_val, 10); + compiler_error(pos, "Value %s doesn't fit in type %Q", string, typestring(a->type)); + } } CORE_Static void -convert_untyped_to_typed(Token *pos, Value *a, Ast_Type *new_type){ - assert(new_type); - if(a->type == 0) return; - if(is_typed(a->type)) return; +convert_untyped_to_typed(Token *pos, Value *a, Ast_Type *new_type) { + assert(new_type); + if (a->type == 0) return; + if (is_typed(a->type)) return; - if(is_any(new_type)) - new_type = get_default_type_from_untyped(a->type); - else if(is_int(a->type) && is_int(new_type)) - assert(a->type == pctx->untyped_int); - else if(is_int(a->type) && is_enum(new_type)) - ; - else if(is_int(a->type) && is_float(new_type)) - a->f64_val = bigint_as_float(&a->big_int_val); // @leak bigint - else if(is_int(a->type) && is_pointer(new_type)) - ; - else if(is_float(a->type) && is_float(new_type)) - ; // nothing to do - else if(is_bool(a->type) && is_bool(new_type)) - ; // nothing to do - else if(is_string(a->type) && is_string(new_type)) - ; // nothing to do - else compiler_error(pos, "Type mismatch when converting from %Q to %Q", typestring(a->type), typestring(new_type)); + if (is_any(new_type)) + new_type = get_default_type_from_untyped(a->type); + else if (is_int(a->type) && is_int(new_type)) + assert(a->type == pctx->untyped_int); + else if (is_int(a->type) && is_enum(new_type)) + ; + else if (is_int(a->type) && is_float(new_type)) + a->f64_val = bigint_as_float(&a->big_int_val); // @leak bigint + else if (is_int(a->type) && is_pointer(new_type)) + ; + else if (is_float(a->type) && is_float(new_type)) + ; // nothing to do + else if (is_bool(a->type) && is_bool(new_type)) + ; // nothing to do + else if (is_string(a->type) && is_string(new_type)) + ; // nothing to do + else compiler_error(pos, "Type mismatch when converting from %Q to %Q", typestring(a->type), typestring(new_type)); - a->type = new_type; - check_value_bounds(pos, a); + a->type = new_type; + check_value_bounds(pos, a); } CORE_Static Ast_Type * -convert_untyped_to_typed(Token *pos, Ast_Type *type, Ast_Type *new_type){ - Value value = {}; - value.type = type; - convert_untyped_to_typed(pos, &value, new_type); - return value.type; +convert_untyped_to_typed(Token *pos, Ast_Type *type, Ast_Type *new_type) { + Value value = {}; + value.type = type; + convert_untyped_to_typed(pos, &value, new_type); + return value.type; } CORE_Static void -make_sure_types_are_compatible_for_constant_evaluation(Token *pos, Value *a, Value *b){ - if((is_pointer(a->type) && is_int(b->type)) || (is_pointer(b->type) && is_int(a->type))){ - return; - } - else if(is_pointer(a->type) && is_pointer(b->type)){ - return; - } - else if(is_typed(a->type) && is_typed(b->type)){ - if(a->type != b->type){ - compiler_error(pos, "Type mismatch in make_sure_types_are_compatible_for_constant_evaluation - left: %Q right: %Q", typestring(a->type), typestring(b->type)); +make_sure_types_are_compatible_for_constant_evaluation(Token *pos, Value *a, Value *b) { + if ((is_pointer(a->type) && is_int(b->type)) || (is_pointer(b->type) && is_int(a->type))) { + return; + } + else if (is_pointer(a->type) && is_pointer(b->type)) { + return; + } + else if (is_typed(a->type) && is_typed(b->type)) { + if (a->type != b->type) { + compiler_error(pos, "Type mismatch in make_sure_types_are_compatible_for_constant_evaluation - left: %Q right: %Q", typestring(a->type), typestring(b->type)); + } } - } - if(is_untyped(a->type) && is_typed(b->type)){ - assert(is_typed(b->type)); - convert_untyped_to_typed(pos, a, b->type); - } - else if(is_typed(a->type) && is_untyped(b->type)){ - assert(is_typed(a->type)); - convert_untyped_to_typed(pos, b, a->type); - } - else if(is_int(a->type) && is_float(b->type)){ - *a = value_float(a->big_int_val); - } - else if(is_float(a->type) && is_int(b->type)){ - *b = value_float(b->big_int_val); - } + if (is_untyped(a->type) && is_typed(b->type)) { + assert(is_typed(b->type)); + convert_untyped_to_typed(pos, a, b->type); + } + else if (is_typed(a->type) && is_untyped(b->type)) { + assert(is_typed(a->type)); + convert_untyped_to_typed(pos, b, a->type); + } + else if (is_int(a->type) && is_float(b->type)) { + *a = value_float(a->big_int_val); + } + else if (is_float(a->type) && is_int(b->type)) { + *b = value_float(b->big_int_val); + } } CORE_Static Value -compare_values(Token *pos, Token_Kind op, Value a, Value b, bool is_const){ - if(is_enum(a.type) && (a.type == b.type)) - goto skip_eval; - if(a.type->kind == TYPE_TYPE && b.type->kind == TYPE_TYPE) - goto skip_eval; - if(!(is_numeric(a.type) && is_numeric(b.type))) - compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); +compare_values(Token *pos, Token_Kind op, Value a, Value b, bool is_const) { + if (is_enum(a.type) && (a.type == b.type)) + goto skip_eval; + if (a.type->kind == TYPE_TYPE && b.type->kind == TYPE_TYPE) + goto skip_eval; + if (!(is_numeric(a.type) && is_numeric(b.type))) + compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); - make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); + make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); - skip_eval: B32 result = 0; - if(is_const){ - switch(a.type->kind){ - case TYPE_TYPE:{ - switch(op){ - case TK_Equals: result = a.type_val->type_id == b.type_val->type_id; break; - case TK_NotEquals: result = a.type_val->type_id != b.type_val->type_id; break; - default: goto failure; - } - }break; - CASE_INT:{ - CmpRes cmp = bigint_cmp(&a.big_int_val, &b.big_int_val); - switch(op){ - case TK_LesserThenOrEqual: result = (cmp == CMP_LT) || (cmp == CMP_EQ); break; - case TK_GreaterThenOrEqual: result = (cmp == CMP_GT) || (cmp == CMP_EQ); break; - case TK_GreaterThen: result = cmp == CMP_GT; break; - case TK_LesserThen: result = cmp == CMP_LT; break; - case TK_Equals: result = cmp == CMP_EQ; break; - case TK_NotEquals: result = cmp != CMP_EQ; break; - default: goto failure; - } - }break; - CASE_BOOL:{ - switch(op){ - case TK_Equals: result = a.bool_val == b.bool_val; break; - case TK_NotEquals: result = a.bool_val != b.bool_val; break; - default: goto failure; - } - }break; - CASE_FLOAT:{ - switch(op){ - case TK_LesserThenOrEqual: result = a.f64_val <= b.f64_val; break; - case TK_GreaterThenOrEqual: result = a.f64_val >= b.f64_val; break; - case TK_GreaterThen: result = a.f64_val > b.f64_val; break; - case TK_LesserThen: result = a.f64_val < b.f64_val; break; - case TK_Equals: result = a.f64_val == b.f64_val; break; - case TK_NotEquals: result = a.f64_val != b.f64_val; break; - default: goto failure; - } - }break; +skip_eval: + B32 result = 0; + if (is_const) { + switch (a.type->kind) { + case TYPE_TYPE: { + switch (op) { + case TK_Equals: result = a.type_val->type_id == b.type_val->type_id; break; + case TK_NotEquals: result = a.type_val->type_id != b.type_val->type_id; break; + default: goto failure; + } + } break; + CASE_INT : { + CmpRes cmp = bigint_cmp(&a.big_int_val, &b.big_int_val); + switch (op) { + case TK_LesserThenOrEqual: result = (cmp == CMP_LT) || (cmp == CMP_EQ); break; + case TK_GreaterThenOrEqual: result = (cmp == CMP_GT) || (cmp == CMP_EQ); break; + case TK_GreaterThen: result = cmp == CMP_GT; break; + case TK_LesserThen: result = cmp == CMP_LT; break; + case TK_Equals: result = cmp == CMP_EQ; break; + case TK_NotEquals: result = cmp != CMP_EQ; break; + default: goto failure; + } + } break; + CASE_BOOL : { + switch (op) { + case TK_Equals: result = a.bool_val == b.bool_val; break; + case TK_NotEquals: result = a.bool_val != b.bool_val; break; + default: goto failure; + } + } break; + CASE_FLOAT : { + switch (op) { + case TK_LesserThenOrEqual: result = a.f64_val <= b.f64_val; break; + case TK_GreaterThenOrEqual: result = a.f64_val >= b.f64_val; break; + case TK_GreaterThen: result = a.f64_val > b.f64_val; break; + case TK_LesserThen: result = a.f64_val < b.f64_val; break; + case TK_Equals: result = a.f64_val == b.f64_val; break; + case TK_NotEquals: result = a.f64_val != b.f64_val; break; + default: goto failure; + } + } break; - CASE_STRING:{ - goto failure; - }break; + CASE_STRING : { + goto failure; + } break; - default: { - failure: compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); - } + default: { + failure: + compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); + } + } } - } - - return value_bool(result); + return value_bool(result); } CORE_Static Value -eval_binary(Token *pos, Token_Kind op, Value a, Value b, bool is_const){ - if(token_is_compare(op)) - return compare_values(pos, op, a, b, is_const); +eval_binary(Token *pos, Token_Kind op, Value a, Value b, bool is_const) { + if (token_is_compare(op)) + return compare_values(pos, op, a, b, is_const); - if(!(is_numeric(a.type) && is_numeric(b.type))) - compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); + if (!(is_numeric(a.type) && is_numeric(b.type))) + compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); - make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); + make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); - // @todo: Add compiler error to the invalid switch cases !!! - Value result = {}; - result.type = a.type; - if(is_const){ - switch(a.type->kind){ - CASE_INT:{ - switch(op){ - case TK_BitXor: bigint_xor(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_BitAnd: bigint_and(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_BitOr: bigint_or(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Add: bigint_add(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Sub: bigint_sub(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Mul: bigint_mul(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Div: bigint_div_trunc(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Mod: bigint_mod(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_LeftShift: bigint_shl(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_RightShift: bigint_shr(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - invalid_default_case; + // @todo: Add compiler error to the invalid switch cases !!! + Value result = {}; + result.type = a.type; + if (is_const) { + switch (a.type->kind) { + CASE_INT : { + switch (op) { + case TK_BitXor: bigint_xor(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_BitAnd: bigint_and(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_BitOr: bigint_or(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Add: bigint_add(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Sub: bigint_sub(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Mul: bigint_mul(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Div: bigint_div_trunc(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Mod: bigint_mod(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_LeftShift: bigint_shl(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_RightShift: + bigint_shr(&result.big_int_val, &a.big_int_val, &b.big_int_val); + break; + invalid_default_case; + } + } break; + CASE_BOOL : { + switch (op) { + case TK_And: result.bool_val = a.bool_val && b.bool_val; break; + case TK_Or: + result.bool_val = a.bool_val || b.bool_val; + break; + invalid_default_case; + } + } break; + CASE_FLOAT : { + switch (op) { + case TK_Add: result.f64_val = a.f64_val + b.f64_val; break; + case TK_Sub: result.f64_val = a.f64_val - b.f64_val; break; + case TK_Mul: result.f64_val = a.f64_val * b.f64_val; break; + case TK_Div: + result.f64_val = a.f64_val / b.f64_val; + break; + invalid_default_case; + } + } break; + CASE_STRING : { + invalid_codepath; + } break; + invalid_default_case; } - }break; - CASE_BOOL:{ - switch(op){ - case TK_And: result.bool_val = a.bool_val && b.bool_val; break; - case TK_Or: result.bool_val = a.bool_val || b.bool_val; break; - invalid_default_case; - } - }break; - CASE_FLOAT:{ - switch(op){ - case TK_Add: result.f64_val = a.f64_val + b.f64_val; break; - case TK_Sub: result.f64_val = a.f64_val - b.f64_val; break; - case TK_Mul: result.f64_val = a.f64_val * b.f64_val; break; - case TK_Div: result.f64_val = a.f64_val / b.f64_val; break; - invalid_default_case; - } - }break; - CASE_STRING:{ - invalid_codepath; - }break; - invalid_default_case; } - } - return result; + return result; } CORE_Static S64 -digit_count(const Value *a){ - S64 digit_count = a->big_int_val.digit_count*64; - if(is_typed(a->type)){ - digit_count = a->type->size*8; - } - return digit_count; +digit_count(const Value *a) { + S64 digit_count = a->big_int_val.digit_count * 64; + if (is_typed(a->type)) { + digit_count = a->type->size * 8; + } + return digit_count; } CORE_Static void -eval_unary(Token *pos, Token_Kind op, Operand *operand){ - Value *a = &operand->value; - Ast_Type *type = a->type; - if(!is_numeric(type)) - compiler_error(pos, "Unary [%s] cant be applied to value of type %Q", name(op), typestring(type)); +eval_unary(Token *pos, Token_Kind op, Operand *operand) { + Value *a = &operand->value; + Ast_Type *type = a->type; + if (!is_numeric(type)) + compiler_error(pos, "Unary [%s] cant be applied to value of type %Q", name(op), typestring(type)); - if(op == TK_Not) - a->type = pctx->untyped_bool; + if (op == TK_Not) + a->type = pctx->untyped_bool; - if(op == TK_Increment || op == TK_Decrement || op == TK_PostIncrement || op == TK_PostDecrement) - if(!operand->is_lvalue) - compiler_error(pos, "Unary [%s] requires an assignable value(lvalue)", name(op)); + if (op == TK_Increment || op == TK_Decrement || op == TK_PostIncrement || op == TK_PostDecrement) + if (!operand->is_lvalue) + compiler_error(pos, "Unary [%s] requires an assignable value(lvalue)", name(op)); - if(!operand->is_const) - return; + if (!operand->is_const) + return; - BigInt result = {}; - switch(op){ - case TK_Add:{} break; - case TK_Sub:{ - switch(type->kind){ - CASE_INT:{ - bigint_negate(&result, &a->big_int_val); - a->big_int_val = result; - }break; - CASE_FLOAT:{ - a->f64_val = -a->f64_val; - }break; - default:goto failure; - } - } break; - case TK_Neg:{ - switch(type->kind){ - CASE_SINT: case TYPE_UNTYPED_INT: - bigint_not(&result, &a->big_int_val, digit_count(a), 1); break; - CASE_UINT: - bigint_not(&result, &a->big_int_val, digit_count(a), 0); break; - default:goto failure; - } - a->big_int_val = result; - } break; - case TK_Not:{ - switch(type->kind){ - CASE_INT: { - if(CMP_EQ == bigint_cmp_zero(&a->big_int_val)) - a->bool_val = 1; - a->bool_val = 0; - }break; - CASE_FLOAT: a->bool_val = !a->f64_val; break; - CASE_BOOL: a->bool_val = !a->bool_val; break; - default:goto failure; - } - } break; - default: failure: compiler_error(pos, "Constant application of unary %s on values of type %Q is not allowed", name(op), typestring(a->type)); - } + BigInt result = {}; + switch (op) { + case TK_Add: { + } break; + case TK_Sub: { + switch (type->kind) { + CASE_INT : { + bigint_negate(&result, &a->big_int_val); + a->big_int_val = result; + } break; + CASE_FLOAT : { + a->f64_val = -a->f64_val; + } break; + default: goto failure; + } + } break; + case TK_Neg: { + switch (type->kind) { + CASE_SINT: + case TYPE_UNTYPED_INT: + bigint_not(&result, &a->big_int_val, digit_count(a), 1); + break; + CASE_UINT: + bigint_not(&result, &a->big_int_val, digit_count(a), 0); + break; + default: goto failure; + } + a->big_int_val = result; + } break; + case TK_Not: { + switch (type->kind) { + CASE_INT : { + if (CMP_EQ == bigint_cmp_zero(&a->big_int_val)) + a->bool_val = 1; + a->bool_val = 0; + } break; + CASE_FLOAT: + a->bool_val = !a->f64_val; + break; + CASE_BOOL: + a->bool_val = !a->bool_val; + break; + default: goto failure; + } + } break; + default: + failure: + compiler_error(pos, "Constant application of unary %s on values of type %Q is not allowed", name(op), typestring(a->type)); + } } CORE_Static void -make_sure_value_is_compatible_with_type(Token *pos, Operand *expr, Ast_Type *type, Typecheck_Flag debug_flag){ - if(type == expr->type){ - assert(type); +make_sure_value_is_compatible_with_type(Token *pos, Operand *expr, Ast_Type *type, Typecheck_Flag debug_flag) { + if (type == expr->type) { + assert(type); + assert(expr->type); + return; + } + + if (!type) { + assert(is_flag_set(debug_flag, TYPE_CAN_BE_NULL)); + assert(expr->type); + try_converting_untyped_to_default_type(expr); + } + else if (!expr->type) { + assert(is_flag_set(debug_flag, EXPR_CAN_BE_NULL)); + assert(type); + expr->type = type; + } + else if (is_any(type)) { + expr->type = type; + } + else if (is_void_pointer(type) && is_pointer(expr->type)) { + expr->type = type; + } + else if (is_pointer(type) && is_void_pointer(expr->type)) { + expr->type = type; + } + else if (is_untyped(expr->type)) { + convert_untyped_to_typed(pos, &expr->value, type); + } + else if (is_slice(type) && is_array(expr->type)) { + if (type->arr.slice_hash == expr->type->arr.slice_hash) + expr->type = type; + } + + if (type && expr->type != type) { + String expr_type_string = typestring(expr->type); + String type_string = typestring(type); + compiler_error(pos, "Assigning but incompatible types, expression: %Q expected var type: %Q", expr_type_string, type_string); + } + + type_complete(expr->type); + check_value_bounds(pos, &expr->value); assert(expr->type); - return; - } - - if(!type){ - assert(is_flag_set(debug_flag, TYPE_CAN_BE_NULL)); - assert(expr->type); - try_converting_untyped_to_default_type(expr); - } - else if(!expr->type){ - assert(is_flag_set(debug_flag, EXPR_CAN_BE_NULL)); - assert(type); - expr->type = type; - } - else if(is_any(type)){ - expr->type = type; - } - else if(is_void_pointer(type) && is_pointer(expr->type)){ - expr->type = type; - } - else if(is_pointer(type) && is_void_pointer(expr->type)){ - expr->type = type; - } - else if(is_untyped(expr->type)){ - convert_untyped_to_typed(pos, &expr->value, type); - } - else if(is_slice(type) && is_array(expr->type)){ - if(type->arr.slice_hash == expr->type->arr.slice_hash) - expr->type = type; - } - - if(type && expr->type != type){ - String expr_type_string = typestring(expr->type); - String type_string = typestring(type); - compiler_error(pos, "Assigning but incompatible types, expression: %Q expected var type: %Q", expr_type_string, type_string); - } - - type_complete(expr->type); - check_value_bounds(pos, &expr->value); - assert(expr->type); } -#define rewrite_into_const(ast,T,s) _rewrite_into_const(ast,sizeof(T),s) -CORE_Static void _rewrite_into_const(Ast *node, U64 ast_size, Value value){ - auto ast = (Ast_Atom *)node; - assert(ast_size >= sizeof(Ast_Atom)); - set_flag(ast->flags, AST_ATOM); - if(value.type && is_lambda(value.type)){ - ast->kind = AST_IDENT; - ast->value = value; - } - else { - ast->kind = AST_VALUE; - ast->value = value; - ast->resolved_type = value.type; - } +#define rewrite_into_const(ast, T, s) _rewrite_into_const(ast, sizeof(T), s) +CORE_Static void _rewrite_into_const(Ast *node, U64 ast_size, Value value) { + auto ast = (Ast_Atom *)node; + assert(ast_size >= sizeof(Ast_Atom)); + set_flag(ast->flags, AST_ATOM); + if (value.type && is_lambda(value.type)) { + ast->kind = AST_IDENT; + ast->value = value; + } + else { + ast->kind = AST_VALUE; + ast->value = value; + ast->resolved_type = value.type; + } } CORE_Static void -inside_scope_search(Scope_Search *search, Ast_Scope *scope, int level){ - if(scope->visit_id == search->scope_visit_id) return; - scope->visit_id = search->scope_visit_id; +inside_scope_search(Scope_Search *search, Ast_Scope *scope, int level) { + if (scope->visit_id == search->scope_visit_id) return; + scope->visit_id = search->scope_visit_id; - // Search for declarations in current scope - For_Named(scope->decls, decl){ - if(decl->name == search->name){ - search->results.add(decl); - if(search->exit_on_find){ - return; - } + // Search for declarations in current scope + For_Named(scope->decls, decl) { + if (decl->name == search->name) { + search->results.add(decl); + if (search->exit_on_find) { + return; + } + } } - } - // Search for declarations in imported implicitly scopes - // We dont want to descent into foreign implicit imports - // so we cap it with level and then we need to make sure that we - // descent into module 1 level away and it's files but no more then that - For(scope->implicit_imports){ - if(level > 0 && it->kind == AST_MODULE) continue; - inside_scope_search(search, it, level + 1); + // Search for declarations in imported implicitly scopes + // We dont want to descent into foreign implicit imports + // so we cap it with level and then we need to make sure that we + // descent into module 1 level away and it's files but no more then that + For(scope->implicit_imports) { + if (level > 0 && it->kind == AST_MODULE) continue; + inside_scope_search(search, it, level + 1); - if(search->exit_on_find && search->results.len){ - return; + if (search->exit_on_find && search->results.len) { + return; + } } - } } CORE_Static void -scope_search(Scope_Search *search){ - // Climb the scope tree and search each scope in module and also - // search in implicitly imported scopes - For_Named(search->scopes, scope){ - for(Ast_Scope *it = scope; it; it=it->parent_scope){ - inside_scope_search(search, it, 0); +scope_search(Scope_Search *search) { + // Climb the scope tree and search each scope in module and also + // search in implicitly imported scopes + For_Named(search->scopes, scope) { + for (Ast_Scope *it = scope; it; it = it->parent_scope) { + inside_scope_search(search, it, 0); - if(search->exit_on_find && search->results.len){ - return; - } + if (search->exit_on_find && search->results.len) { + return; + } - if(search->search_only_current_scope){ - return; - } + if (search->search_only_current_scope) { + return; + } + } + inside_scope_search(search, scope->module, 0); } - inside_scope_search(search, scope->module, 0); - } } CORE_Static Scope_Search -make_scope_search(Allocator *arena, Ast_Scope *scope, Intern_String name){ - Scope_Search result = {}; - result.results.allocator = arena; - result.name = name; - result.scope_visit_id = ++pctx->scope_visit_id; - result.scopes = {arena}; - result.scopes.add(scope); - result.exit_on_find = false; - return result; +make_scope_search(Allocator *arena, Ast_Scope *scope, Intern_String name) { + Scope_Search result = {}; + result.results.allocator = arena; + result.name = name; + result.scope_visit_id = ++pctx->scope_visit_id; + result.scopes = {arena}; + result.scopes.add(scope); + result.exit_on_find = false; + return result; } CORE_Static Ast_Decl * -search_for_single_decl(Ast_Scope *scope, Intern_String name, bool error_if_no_matches = true){ - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); +search_for_single_decl(Ast_Scope *scope, Intern_String name, bool error_if_no_matches = true) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); - Scope_Search search = make_scope_search(scratch, scope, name); - scope_search(&search); + Scope_Search search = make_scope_search(scratch, scope, name); + scope_search(&search); - if(search.results.len == 0){ - if (error_if_no_matches) compiler_error(0, "Unidentified name [%s]", name.str); - return 0; - } - if(search.results.len > 1){ - compiler_error(search.results.data[0]->pos, search.results.data[1]->pos, "Found multiple definitions of name [%s]", name.str); - } + if (search.results.len == 0) { + if (error_if_no_matches) compiler_error(0, "Unidentified name [%s]", name.str); + return 0; + } + if (search.results.len > 1) { + compiler_error(search.results.data[0]->pos, search.results.data[1]->pos, "Found multiple definitions of name [%s]", name.str); + } - return search.results.data[0]; + return search.results.data[0]; } CORE_Static Ast_Decl * -resolve_name(Ast_Scope *scope, Token *pos, Intern_String name, Search_Flag search_flags){ - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Scope_Search search = make_scope_search(scratch, scope, name); - search.search_only_current_scope = search_flags & SEARCH_ONLY_CURRENT_SCOPE; - scope_search(&search); +resolve_name(Ast_Scope *scope, Token *pos, Intern_String name, Search_Flag search_flags) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Scope_Search search = make_scope_search(scratch, scope, name); + search.search_only_current_scope = search_flags & SEARCH_ONLY_CURRENT_SCOPE; + scope_search(&search); - if(search.results.len == 0){ - compiler_error(pos, "Unidentified name [%s]", name.str); - } - if(search.results.len > 1){ - if(is_flag_set(search.results[0]->flags, AST_OPERATOR_OVERLOAD)){ - if(is_flag_set(search_flags, RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED)){ - compiler_error(pos, "Internal compiler error: somehow we found %Q which is an operator to be also an identifier and we started resolving it using resolve_name", name); - } - For(search.results){ + if (search.results.len == 0) { + compiler_error(pos, "Unidentified name [%s]", name.str); + } + if (search.results.len > 1) { + if (is_flag_set(search.results[0]->flags, AST_OPERATOR_OVERLOAD)) { + if (is_flag_set(search_flags, RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED)) { + compiler_error(pos, "Internal compiler error: somehow we found %Q which is an operator to be also an identifier and we started resolving it using resolve_name", name); + } + For(search.results) { + resolve_decl(it); + } + return 0; + } + compiler_error(pos, "Found multiple definitions of name [%s]", name.str); + } + + Ast_Decl *decl = *search.results.data; + resolve_decl(decl); + return decl; +} + +CORE_Static Ast_Decl * +resolve_operator_overload(Ast_Scope *scope, Ast_Type *left, Ast_Type *right, Token *pos, Token_Kind op, U64 argument_hash) { + Ast_Operator_Info *op_info = get_operator_info(op); + if (op_info == 0) return 0; + + // Search for all possible candidates in three scopes + // The current module, left type definition module, right type definition module + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Scope_Search search = make_scope_search(scratch, scope, op_info->op); + if (left->ast && left->ast->parent_scope) search.scopes.add(left->ast->parent_scope); + if (right && right->ast && right->ast->parent_scope) search.scopes.add(right->ast->parent_scope); + search.exit_on_find = false; + scope_search(&search); + + // Resolve them until we hit a match + Array matching_ops = {scratch}; + For(search.results) { resolve_decl(it); - } - return 0; + if (it->type->func.hash_without_ret == argument_hash) { + matching_ops.add(it); + } } - compiler_error(pos, "Found multiple definitions of name [%s]", name.str); - } - Ast_Decl *decl = *search.results.data; - resolve_decl(decl); - return decl; -} - -CORE_Static Ast_Decl * -resolve_operator_overload(Ast_Scope *scope, Ast_Type *left, Ast_Type *right, Token *pos, Token_Kind op, U64 argument_hash){ - Ast_Operator_Info *op_info = get_operator_info(op); - if(op_info == 0) return 0; - - // Search for all possible candidates in three scopes - // The current module, left type definition module, right type definition module - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Scope_Search search = make_scope_search(scratch, scope, op_info->op); - if( left->ast && left->ast->parent_scope) search.scopes.add(left->ast->parent_scope); - if(right && right->ast && right->ast->parent_scope) search.scopes.add(right->ast->parent_scope); - search.exit_on_find = false; - scope_search(&search); - - // Resolve them until we hit a match - Array matching_ops = {scratch}; - For(search.results){ - resolve_decl(it); - if(it->type->func.hash_without_ret == argument_hash){ - matching_ops.add(it); + if (matching_ops.len > 1) { + compiler_error(pos, "Found multiple matching operator overloads for [%Q]", op_info->op); } - } - if(matching_ops.len > 1){ - compiler_error(pos, "Found multiple matching operator overloads for [%Q]", op_info->op); - } + if (matching_ops.len == 1) { + return matching_ops.data[0]; + } - if(matching_ops.len == 1){ - return matching_ops.data[0]; - } - - return 0; + return 0; } CORE_Static void -insert_into_scope(Ast_Scope *scope, Ast_Decl *decl){ - // This function is called when resolving statements - // inside code blocks, inserting lambda arguments into scope - // - // It's also called when scanning top level declarations of a module - // as such we probably don't want to call any resolve stuff here - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Scope_Search search = make_scope_search(scratch, scope, decl->name); - search.search_only_current_scope = true; - scope_search(&search); - if(search.results.len != 0){ - if(!is_flag_set(decl->flags, AST_OPERATOR_OVERLOAD)){ - compiler_error(search.results.data[0]->pos, decl->pos, "[%s] is already defined", decl->name.str); +insert_into_scope(Ast_Scope *scope, Ast_Decl *decl) { + // This function is called when resolving statements + // inside code blocks, inserting lambda arguments into scope + // + // It's also called when scanning top level declarations of a module + // as such we probably don't want to call any resolve stuff here + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Scope_Search search = make_scope_search(scratch, scope, decl->name); + search.search_only_current_scope = true; + scope_search(&search); + if (search.results.len != 0) { + if (!is_flag_set(decl->flags, AST_OPERATOR_OVERLOAD)) { + compiler_error(search.results.data[0]->pos, decl->pos, "[%s] is already defined", decl->name.str); + } } - } - add(pctx->perm, &scope->decls, decl); + add(pctx->perm, &scope->decls, decl); } // @@ -613,1150 +630,1164 @@ insert_into_scope(Ast_Scope *scope, Ast_Decl *decl){ // need to propagate int to untyped_int // CORE_Static void -try_propagating_resolved_type_to_untyped_literals(Ast *ast, Ast_Type *type, Ast_Type *additional_not_bool_type = 0){ - if(!type) compiler_error(ast->pos, "Internal compiler error: Type passed to try_propagating_resolved_type_to_untyped_literals is null"); - if(!ast) return; - if(is_untyped(type)) return; +try_propagating_resolved_type_to_untyped_literals(Ast *ast, Ast_Type *type, Ast_Type *additional_not_bool_type = 0) { + if (!type) compiler_error(ast->pos, "Internal compiler error: Type passed to try_propagating_resolved_type_to_untyped_literals is null"); + if (!ast) return; + if (is_untyped(type)) return; - // @note: - // Normal untyped should only be 1 deep cause we make sure to colapse all constants - // and only constants can be untyped - if(!is_bool(type)){ - if(ast->kind != AST_VALUE) return; + // @note: + // Normal untyped should only be 1 deep cause we make sure to colapse all constants + // and only constants can be untyped + if (!is_bool(type)) { + if (ast->kind != AST_VALUE) return; - auto node = (Ast_Atom *)ast; - convert_untyped_to_typed(ast->pos, &node->value, type); + auto node = (Ast_Atom *)ast; + convert_untyped_to_typed(ast->pos, &node->value, type); - // This only is required when we have arrays of Any type. - // That is because we are folding all the constants and there - // the resolved_type is set - node->resolved_type = node->value.type; - return; - } - - // Boolean special case - switch(ast->kind){ - CASE(IDENT, Atom){ - type = additional_not_bool_type ? additional_not_bool_type : type; - convert_untyped_to_typed(ast->pos, &node->value, type); - BREAK(); + // This only is required when we have arrays of Any type. + // That is because we are folding all the constants and there + // the resolved_type is set + node->resolved_type = node->value.type; + return; } - CASE(VALUE, Atom){ - type = additional_not_bool_type ? additional_not_bool_type : type; - convert_untyped_to_typed(ast->pos, &node->value, type); - BREAK(); - } + // Boolean special case + switch (ast->kind) { + CASE(IDENT, Atom) { + type = additional_not_bool_type ? additional_not_bool_type : type; + convert_untyped_to_typed(ast->pos, &node->value, type); + BREAK(); + } - CASE(BINARY, Binary){ - node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); - try_propagating_resolved_type_to_untyped_literals(node->left, type, node->right->resolved_type); - try_propagating_resolved_type_to_untyped_literals(node->right, type, node->left->resolved_type); - BREAK(); - } + CASE(VALUE, Atom) { + type = additional_not_bool_type ? additional_not_bool_type : type; + convert_untyped_to_typed(ast->pos, &node->value, type); + BREAK(); + } - CASE(CALL, Call){ - if(is_untyped(node->resolved_type)) - compiler_error(node->pos, "Internal compiler error: Resolved type of a call is marked as untyped"); - BREAK(); - } + CASE(BINARY, Binary) { + node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); + try_propagating_resolved_type_to_untyped_literals(node->left, type, node->right->resolved_type); + try_propagating_resolved_type_to_untyped_literals(node->right, type, node->left->resolved_type); + BREAK(); + } - CASE(UNARY, Unary){ - node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); - try_propagating_resolved_type_to_untyped_literals(node->expr, type); - BREAK(); - } + CASE(CALL, Call) { + if (is_untyped(node->resolved_type)) + compiler_error(node->pos, "Internal compiler error: Resolved type of a call is marked as untyped"); + BREAK(); + } - default:{} - } + CASE(UNARY, Unary) { + node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); + try_propagating_resolved_type_to_untyped_literals(node->expr, type); + BREAK(); + } + + default: { + } + } } CORE_Static Ast_Type * -resolve_typespec(Ast_Expr *ast, Resolve_Flag flags){ - if(!ast && is_flag_set(flags, AST_CAN_BE_NULL)) - return 0; +resolve_typespec(Ast_Expr *ast, Resolve_Flag flags) { + if (!ast && is_flag_set(flags, AST_CAN_BE_NULL)) + return 0; - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Operand resolved = resolve_expr(ast, flags | RESOLVE_TYPESPEC, 0, 0); - if(is_flag_set(flags, RESOLVE_TYPESPEC_COMPLETE)) - type_complete(resolved.type_val); - if(resolved.type != pctx->type_type) - compiler_error(ast->pos, "Expected [Type] got instead %Q", typestring(resolved.type)); - return resolved.type_val; + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Operand resolved = resolve_expr(ast, flags | RESOLVE_TYPESPEC, 0, 0); + if (is_flag_set(flags, RESOLVE_TYPESPEC_COMPLETE)) + type_complete(resolved.type_val); + if (resolved.type != pctx->type_type) + compiler_error(ast->pos, "Expected [Type] got instead %Q", typestring(resolved.type)); + return resolved.type_val; } CORE_Static Operand -resolve_and_require_bool(const char *error, Ast_Expr *expr, Resolve_Flag flags){ - if(!expr){ - if(flags == AST_CAN_BE_NULL) - return {}; - else compiler_error(0, "Compiler error: Null expression"); - } +resolve_and_require_bool(const char *error, Ast_Expr *expr, Resolve_Flag flags) { + if (!expr) { + if (flags == AST_CAN_BE_NULL) + return {}; + else compiler_error(0, "Compiler error: Null expression"); + } - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Operand op = resolve_expr(expr, flags, 0, 0); - if(!is_bool(op.type)){ - compiler_error(expr->pos, "Expected type [Bool] got instead type %Q :: %s", typestring(op.type), error); - } + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Operand op = resolve_expr(expr, flags, 0, 0); + if (!is_bool(op.type)) { + compiler_error(expr->pos, "Expected type [Bool] got instead type %Q :: %s", typestring(op.type), error); + } - return op; -} - -CORE_Static Operand -require_const_int(Ast_Expr *expr, Resolve_Flag flags){ - Operand op = resolve_expr(expr, flags, 0, 0); - - if(expr == 0 && flags) return op; - else if(expr == 0) - compiler_error(expr->pos, "This field cannot be null"); +} - if(!op.is_const) - compiler_error(expr->pos, "Expected a const value"); - if(!(is_int(op.type) || is_enum(op.type))) - compiler_error(expr->pos, "Expected a constant integer got instead %Q", typestring(op.type)); +CORE_Static Operand +require_const_int(Ast_Expr *expr, Resolve_Flag flags) { + Operand op = resolve_expr(expr, flags, 0, 0); - return op; + if (expr == 0 && flags) + return op; + else if (expr == 0) + compiler_error(expr->pos, "This field cannot be null"); + + if (!op.is_const) + compiler_error(expr->pos, "Expected a const value"); + if (!(is_int(op.type) || is_enum(op.type))) + compiler_error(expr->pos, "Expected a constant integer got instead %Q", typestring(op.type)); + + return op; } // @note: Ret is return value of CORE_Static passed down the stack // to check if type matches CORE_Static void -resolve_stmt(Ast *ast, Ast_Type *ret){ - if(!ast) return; - assert(ast->parent_scope->kind == AST_SCOPE); +resolve_stmt(Ast *ast, Ast_Type *ret) { + if (!ast) return; + assert(ast->parent_scope->kind == AST_SCOPE); + switch (ast->kind) { + CASE(RETURN, Return) { // @todo: need to check if all paths return a value + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Array types = {scratch}; - switch(ast->kind){ - CASE(RETURN, Return){ // @todo: need to check if all paths return a value - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Array types = {scratch}; + int i = 0; + For(node->expr) { + Operand op; + if (is_tuple(ret)) { + Ast_Type *sub_type = ret->agg.members[i].type; + op = resolve_expr(it, AST_CAN_BE_NULL, sub_type, 0); + } + else { + op = resolve_expr(it, AST_CAN_BE_NULL, ret, 0); + convert_untyped_to_typed(node->pos, &op.value, ret); + } - int i = 0; - For(node->expr){ - Operand op; - if(is_tuple(ret)){ - Ast_Type *sub_type = ret->agg.members[i].type; - op = resolve_expr(it, AST_CAN_BE_NULL, sub_type, 0); - } - else{ - op = resolve_expr(it, AST_CAN_BE_NULL, ret, 0); - convert_untyped_to_typed(node->pos, &op.value, ret); + types.add(op.type); + i += 1; + } + + Ast_Type *type = type_try_tupling(types, node); + if (type && type != ret) + compiler_error(node->pos, "Return statement has different type then returned value, expecting: %Q got instead %Q", typestring(ret), typestring(type)); + node->resolved_type = type; + + i = 0; + For(node->expr) { + Ast_Type *sub_type = type; + if (is_tuple(type)) sub_type = type->agg.members[i].type; + + try_propagating_resolved_type_to_untyped_literals(it, sub_type); + i += 1; + } + + BREAK(); } - types.add(op.type); - i += 1; - } + case AST_LAMBDA: + case AST_VAR: + CASE(CONST, Decl) { + resolve_decl(node); + insert_into_scope(node->parent_scope, node); + BREAK(); + } - Ast_Type *type = type_try_tupling(types, node); - if(type && type != ret) - compiler_error(node->pos, "Return statement has different type then returned value, expecting: %Q got instead %Q", typestring(ret), typestring(type)); - node->resolved_type = type; + case AST_BREAK: + CASE(PASS, Pass) { + unused(node); + BREAK(); + } - i = 0; - For(node->expr){ - Ast_Type *sub_type = type; - if(is_tuple(type)) sub_type = type->agg.members[i].type; + CASE(RUNTIME_ASSERT, Builtin) { + resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); + try_propagating_resolved_type_to_untyped_literals(node->expr, pctx->type_bool); + BREAK(); + } - try_propagating_resolved_type_to_untyped_literals(it, sub_type); - i += 1; - } + CASE(CONSTANT_ASSERT, Builtin) { + Operand op = resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); + if (!op.is_const) compiler_error(node->pos, "#Assert expression required to be constant"); + if (op.bool_val == false) { + compiler_error(node->pos, "#Assert condition not met :: %.*s", (int)node->assert_message.len, node->assert_message.str); + } + BREAK(); + } - BREAK(); - } + CASE(FOR, For) { + if (node->init && node->cond == 0 && node->iter == 0) { + if (!is_flag_set(node->init->flags, AST_STMT)) { + node->cond = node->init; + node->init = 0; + } + } - case AST_LAMBDA: - case AST_VAR: - CASE(CONST, Decl){ - resolve_decl(node); - insert_into_scope(node->parent_scope, node); - BREAK(); - } + resolve_expr(node->init, AST_CAN_BE_NULL, 0, 0); + Operand op = resolve_expr(node->cond, AST_CAN_BE_NULL, 0, 0); + if (node->cond) { + if ((!node->init && !node->iter) && (is_array(op.type) || is_slice(op.type))) { + node->is_array_traversal = true; + if (is_slice(op.type)) node->is_also_slice_traversal = true; + Ast_Decl *var = ast_new(Ast_Decl, AST_VAR, node->cond->pos, AST_DECL); + var->state = DECL_RESOLVED; + var->type = type_pointer(op.type->base); + var->name = pctx->intern_it; + insert_into_scope(node->scope, var); + node->array_traversal_var = var; + } + else if (!is_bool(op.type)) { + compiler_error(node->pos, "Invalid type of for loop condition %Q, required [Bool]", typestring(op.type)); + } + else { + try_propagating_resolved_type_to_untyped_literals(node->cond, pctx->type_bool); + } + } - case AST_BREAK: - CASE(PASS, Pass){ - unused(node); - BREAK(); - } + resolve_expr(node->iter, AST_CAN_BE_NULL, 0, 0); + For(node->scope->stmts) resolve_stmt(it, ret); + BREAK(); + } - CASE(RUNTIME_ASSERT, Builtin){ - resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); - try_propagating_resolved_type_to_untyped_literals(node->expr, pctx->type_bool); - BREAK(); - } + // @todo: maybe add else kind ?? and then make sure other then else are AST_CANT_BE_NULL + CASE(IF, If) { + For(node->ifs) { + resolve_stmt(it->init, ret); + resolve_and_require_bool("Conditional in a if condition", it->expr, AST_CAN_BE_NULL); + try_propagating_resolved_type_to_untyped_literals(it->expr, pctx->type_bool); + For_Named(it->scope->stmts, jt) + resolve_stmt(jt, ret); + } + BREAK(); + } - CASE(CONSTANT_ASSERT, Builtin){ - Operand op = resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); - if(!op.is_const) compiler_error(node->pos, "#Assert expression required to be constant"); - if(op.bool_val == false){ - compiler_error(node->pos, "#Assert condition not met :: %.*s", (int)node->assert_message.len, node->assert_message.str); - } - BREAK(); - } + CASE(SWITCH, Switch) { + Operand base = resolve_expr(node->value, AST_CANT_BE_NULL, 0, 0); + if (!is_int(base.type) && !is_enum(base.type)) + compiler_error(node->pos, "You can only switch on integer values(enums included), the type of expression in switch statement is instead %Q", typestring(base.type)); - CASE(FOR, For){ - if(node->init && node->cond == 0 && node->iter == 0){ - if(!is_flag_set(node->init->flags, AST_STMT)){ - node->cond = node->init; - node->init = 0; + try_converting_untyped_to_default_type(&base.value); + try_propagating_resolved_type_to_untyped_literals(node->value, base.type); + For(node->cases) { + For_Named(it->labels, label) { + Operand op = resolve_expr(label, AST_CANT_BE_NULL, 0, 0); + if (!op.is_const) compiler_error(label->pos, "Switch label required to be constant"); + make_sure_value_is_compatible_with_type(label->pos, &op, base.type, TYPE_AND_EXPR_REQUIRED); + try_propagating_resolved_type_to_untyped_literals(label, base.type); + } + + For_Named(it->scope->stmts, stmt) + resolve_stmt(stmt, ret); + } + if (node->default_scope) + For_Named(node->default_scope->stmts, stmt) + resolve_stmt(stmt, ret); + + BREAK(); + } + + CASE(VAR_UNPACK, Var_Unpack) { + Operand expr_op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); + if (!is_tuple(expr_op.type)) + compiler_error(node->pos, "Expected expression to be of type [Tuple]"); + if (expr_op.type->agg.members.len != node->vars.len) + compiler_error(node->pos, "Different count of return values and assigning values"); + node->resolved_type = expr_op.type; + + For(node->vars) { + S64 index = node->vars.get_index(&it); + Ast_Resolved_Member *type = expr_op.type->agg.members.data + index; + it->type = type->type; + resolve_decl(it); + insert_into_scope(node->parent_scope, it); + } + BREAK(); + } + + default: { + if (is_flag_set(ast->flags, AST_EXPR)) { + assert(is_flag_set(ast->flags, AST_STMT)); + resolve_expr((Ast_Expr *)ast, AST_CANT_BE_NULL, 0, 0); + } + else invalid_codepath; } - } - - resolve_expr(node->init, AST_CAN_BE_NULL, 0, 0); - Operand op = resolve_expr(node->cond, AST_CAN_BE_NULL, 0, 0); - if(node->cond){ - if((!node->init && !node->iter) && (is_array(op.type) || is_slice(op.type))){ - node->is_array_traversal = true; - if(is_slice(op.type)) node->is_also_slice_traversal = true; - Ast_Decl *var = ast_new(Ast_Decl, AST_VAR, node->cond->pos, AST_DECL); - var->state = DECL_RESOLVED; - var->type = type_pointer(op.type->base); - var->name = pctx->intern_it; - insert_into_scope(node->scope, var); - node->array_traversal_var = var; - } - else if(!is_bool(op.type)){ - compiler_error(node->pos, "Invalid type of for loop condition %Q, required [Bool]", typestring(op.type)); - } - else{ - try_propagating_resolved_type_to_untyped_literals(node->cond, pctx->type_bool); - } - } - - resolve_expr(node->iter, AST_CAN_BE_NULL, 0, 0); - For(node->scope->stmts) resolve_stmt(it, ret); - BREAK(); } - - // @todo: maybe add else kind ?? and then make sure other then else are AST_CANT_BE_NULL - CASE(IF, If){ - For(node->ifs){ - resolve_stmt(it->init, ret); - resolve_and_require_bool("Conditional in a if condition", it->expr, AST_CAN_BE_NULL); - try_propagating_resolved_type_to_untyped_literals(it->expr, pctx->type_bool); - For_Named(it->scope->stmts, jt) - resolve_stmt(jt, ret); - } - BREAK(); - } - - CASE(SWITCH, Switch){ - Operand base = resolve_expr(node->value, AST_CANT_BE_NULL, 0, 0); - if(!is_int(base.type) && !is_enum(base.type)) - compiler_error(node->pos, "You can only switch on integer values(enums included), the type of expression in switch statement is instead %Q", typestring(base.type)); - - try_converting_untyped_to_default_type(&base.value); - try_propagating_resolved_type_to_untyped_literals(node->value, base.type); - For(node->cases){ - For_Named(it->labels, label){ - Operand op = resolve_expr(label, AST_CANT_BE_NULL, 0, 0); - if(!op.is_const) compiler_error(label->pos, "Switch label required to be constant"); - make_sure_value_is_compatible_with_type(label->pos, &op, base.type, TYPE_AND_EXPR_REQUIRED); - try_propagating_resolved_type_to_untyped_literals(label, base.type); - } - - For_Named(it->scope->stmts, stmt) - resolve_stmt(stmt, ret); - } - if(node->default_scope) - For_Named(node->default_scope->stmts, stmt) - resolve_stmt(stmt, ret); - - BREAK(); - } - - CASE(VAR_UNPACK, Var_Unpack){ - Operand expr_op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); - if(!is_tuple(expr_op.type)) - compiler_error(node->pos, "Expected expression to be of type [Tuple]"); - if(expr_op.type->agg.members.len != node->vars.len) - compiler_error(node->pos, "Different count of return values and assigning values"); - node->resolved_type = expr_op.type; - - For(node->vars){ - S64 index = node->vars.get_index(&it); - Ast_Resolved_Member *type = expr_op.type->agg.members.data + index; - it->type = type->type; - resolve_decl(it); - insert_into_scope(node->parent_scope, it); - } - BREAK(); - } - - default:{ - if(is_flag_set(ast->flags, AST_EXPR)){ - assert(is_flag_set(ast->flags, AST_STMT)); - resolve_expr((Ast_Expr *)ast, AST_CANT_BE_NULL, 0, 0); - } - else invalid_codepath; - } - } } CORE_Static Ast_Type * -resolve_lambda_type(Ast_Lambda *lambda){ - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Array args = {scratch}; - Array ret = {scratch}; - For(lambda->ret) { - Ast_Type *type = resolve_typespec(it, AST_CANT_BE_NULL); - ret.add(type); - } +resolve_lambda_type(Ast_Lambda *lambda) { + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Array args = {scratch}; + Array ret = {scratch}; + For(lambda->ret) { + Ast_Type *type = resolve_typespec(it, AST_CANT_BE_NULL); + ret.add(type); + } - For(lambda->args){ - Ast_Type *type = resolve_typespec(it->typespec, AST_CANT_BE_NULL); - Operand default_value = resolve_expr(it->expr, AST_CAN_BE_NULL, type, 0); - make_sure_value_is_compatible_with_type(it->pos, &default_value, type, EXPR_CAN_BE_NULL); + For(lambda->args) { + Ast_Type *type = resolve_typespec(it->typespec, AST_CANT_BE_NULL); + Operand default_value = resolve_expr(it->expr, AST_CAN_BE_NULL, type, 0); + make_sure_value_is_compatible_with_type(it->pos, &default_value, type, EXPR_CAN_BE_NULL); - it->type = type; - try_propagating_resolved_type_to_untyped_literals(it->expr, it->type); + it->type = type; + try_propagating_resolved_type_to_untyped_literals(it->expr, it->type); - args.add(it->type); - } + args.add(it->type); + } - return type_lambda(lambda, ret, args); + return type_lambda(lambda, ret, args); } CORE_Static void -try_resolving_lambda_scope(Operand *op, Ast_Lambda *lambda, Ast_Type *lambda_type){ - if(lambda->scope){ - For(lambda->args){ - insert_into_scope(lambda->scope, it); - } - For(lambda->scope->stmts){ - resolve_stmt(it, lambda_type->func.ret); - } +try_resolving_lambda_scope(Operand *op, Ast_Lambda *lambda, Ast_Type *lambda_type) { + if (lambda->scope) { + For(lambda->args) { + insert_into_scope(lambda->scope, it); + } + For(lambda->scope->stmts) { + resolve_stmt(it, lambda_type->func.ret); + } - *op = operand_lambda(lambda_type); - } - else if(is_flag_set(lambda->flags, AST_FOREIGN)){ - *op = operand_lambda(lambda_type); - } + *op = operand_lambda(lambda_type); + } + else if (is_flag_set(lambda->flags, AST_FOREIGN)) { + *op = operand_lambda(lambda_type); + } } CORE_Static Operand -resolve_cast(Ast_Binary *node){ - Operand expr = resolve_expr(node->left, AST_CANT_BE_NULL, 0, 0); - Ast_Type *type = resolve_typespec(node->right, AST_CANT_BE_NULL); - Ast_Type *original_type = expr.type; - node->before_type = expr.type; +resolve_cast(Ast_Binary *node) { + Operand expr = resolve_expr(node->left, AST_CANT_BE_NULL, 0, 0); + Ast_Type *type = resolve_typespec(node->right, AST_CANT_BE_NULL); + Ast_Type *original_type = expr.type; + node->before_type = expr.type; - switch(expr.type->kind){ - case TYPE_TYPE:{ - if(is_int(type)){ - expr.type = type; - } else goto failure; - } break; - case TYPE_ENUM:{ - if(is_int(type)) - expr.type = type; - else goto failure; - }break; - case TYPE_POINTER:{ - if(is_pointer(type)) - expr = operand_rvalue(type); - else goto failure; - } break; - CASE_UNTYPED: { - convert_untyped_to_typed(node->pos, &expr.value, type); - } break; - CASE_UINT: - CASE_SINT:{ - if(is_int(type)) - expr.type = type; - else if(is_float(type)){ - expr.value.type = type; - if(expr.is_const) expr.value.f64_val = bigint_as_float(&expr.big_int_val); // @leak - } else goto failure; - } break; - case TYPE_F32: case TYPE_F64: { - if(is_float(type)){ - expr.type = type; - } - else if(is_int(type)){ - if(expr.is_const) expr.value.big_int_val = bigint_s64((S64)expr.value.f64_val); // @todo: What to do here??? - expr.type = type; - } else goto failure; - } break; - default: failure: compiler_error(node->pos, "Failed to cast from %Q to %Q", typestring(expr.type), typestring(type));; - } + switch (expr.type->kind) { + case TYPE_TYPE: { + if (is_int(type)) { + expr.type = type; + } + else goto failure; + } break; + case TYPE_ENUM: { + if (is_int(type)) + expr.type = type; + else goto failure; + } break; + case TYPE_POINTER: { + if (is_pointer(type)) + expr = operand_rvalue(type); + else goto failure; + } break; + CASE_UNTYPED : { + convert_untyped_to_typed(node->pos, &expr.value, type); + } break; + CASE_UINT: + CASE_SINT : { + if (is_int(type)) + expr.type = type; + else if (is_float(type)) { + expr.value.type = type; + if (expr.is_const) expr.value.f64_val = bigint_as_float(&expr.big_int_val); // @leak + } + else goto failure; + } break; + case TYPE_F32: + case TYPE_F64: { + if (is_float(type)) { + expr.type = type; + } + else if (is_int(type)) { + if (expr.is_const) expr.value.big_int_val = bigint_s64((S64)expr.value.f64_val); // @todo: What to do here??? + expr.type = type; + } + else goto failure; + } break; + default: + failure: + compiler_error(node->pos, "Failed to cast from %Q to %Q", typestring(expr.type), typestring(type)); + ; + } - assert(original_type != type ? expr.type == type : 1); - if(expr.is_const) { - check_value_bounds(node->pos, &expr.value); - rewrite_into_const(node, Ast_Binary, expr.value); - } + assert(original_type != type ? expr.type == type : 1); + if (expr.is_const) { + check_value_bounds(node->pos, &expr.value); + rewrite_into_const(node, Ast_Binary, expr.value); + } - node->resolved_type = expr.type; - if(!expr.is_const) - try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); - return expr; + node->resolved_type = expr.type; + if (!expr.is_const) + try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); + return expr; } CORE_Static void -resolve_compound_array(Ast_Call *node, Ast_Type *type){ - Ast_Type *item_type = type->arr.base; - S64 size = type->arr.size; +resolve_compound_array(Ast_Call *node, Ast_Type *type) { + Ast_Type *item_type = type->arr.base; + S64 size = type->arr.size; - if(is_array(type) && node->exprs.len > size) - compiler_error(node->pos, "Too many items in compound expression, expected: %lld got: %lld", size, node->exprs.len); + if (is_array(type) && node->exprs.len > size) + compiler_error(node->pos, "Too many items in compound expression, expected: %lld got: %lld", size, node->exprs.len); - S64 default_counter = 0; - For(node->exprs){ - if(is_flag_set(it->call_flags, CALL_NAME)) - compiler_error(it->pos, "Arrays can't have named compound expression arguments"); + S64 default_counter = 0; + For(node->exprs) { + if (is_flag_set(it->call_flags, CALL_NAME)) + compiler_error(it->pos, "Arrays can't have named compound expression arguments"); - if(is_flag_set(it->call_flags, CALL_INDEX)){ - default_counter = -1; - Operand op = require_const_int(it->index, AST_CANT_BE_NULL); - S64 i = bigint_as_signed(&op.value.big_int_val); // @todo: what happens when big num is here??? + if (is_flag_set(it->call_flags, CALL_INDEX)) { + default_counter = -1; + Operand op = require_const_int(it->index, AST_CANT_BE_NULL); + S64 i = bigint_as_signed(&op.value.big_int_val); // @todo: what happens when big num is here??? - if(i >= size) - compiler_error(it->pos, "Index %lld is out of range of array of size %lld", i, size); - it->resolved_index = (S32)i; - } - else{ - if(default_counter == -1) - compiler_error(it->pos, "Mixing expicit indexes and default is illegal"); - it->resolved_index = default_counter; - } - - Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); - make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); - it->resolved_type = item_type; - try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); - } -} - -CORE_Static void -resolve_compound_struct(Ast_Call *node, Ast_Type *type){ - S64 default_counter = 0; - For(node->exprs){ - if(is_flag_set(it->call_flags, CALL_INDEX)) - compiler_error(it->pos, "Index specifier in a struct compound expression is not legal"); - - Ast_Type *item_type = 0; - if(is_flag_set(it->call_flags, CALL_NAME)){ - For_Named(type->agg.members, m){ - if(it->name->intern_val == m.name){ - it->resolved_name = m.name; - it->resolved_index = type->agg.members.get_index(&m); - // @copy_paste - if(m.type == pctx->type_type) - item_type = m.type_val; - else item_type = m.type; - - if(m.visited){ - compiler_error(it->pos, "Field already initialized"); - } else m.visited = true; - break; + if (i >= size) + compiler_error(it->pos, "Index %lld is out of range of array of size %lld", i, size); + it->resolved_index = (S32)i; } - } - if(!item_type) - compiler_error(it->pos, "No member with that name"); + else { + if (default_counter == -1) + compiler_error(it->pos, "Mixing expicit indexes and default is illegal"); + it->resolved_index = default_counter; + } + + Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); + make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); + it->resolved_type = item_type; + try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); } - else{ - if(default_counter == -1) - compiler_error(it->pos, "Cant mix named fields and normal fields"); - if(default_counter >= type->agg.members.len) - compiler_error(it->pos, "Too many struct initializers"); +} - Ast_Resolved_Member *m = &type->agg.members[default_counter]; - it->resolved_name = m->name; - it->resolved_index = default_counter; - m->visited = true; +CORE_Static void +resolve_compound_struct(Ast_Call *node, Ast_Type *type) { + S64 default_counter = 0; + For(node->exprs) { + if (is_flag_set(it->call_flags, CALL_INDEX)) + compiler_error(it->pos, "Index specifier in a struct compound expression is not legal"); - // @copy_paste - if(m->type == pctx->type_type && m->type_val){ - item_type = m->type_val; - } - else item_type = m->type; + Ast_Type *item_type = 0; + if (is_flag_set(it->call_flags, CALL_NAME)) { + For_Named(type->agg.members, m) { + if (it->name->intern_val == m.name) { + it->resolved_name = m.name; + it->resolved_index = type->agg.members.get_index(&m); + // @copy_paste + if (m.type == pctx->type_type) + item_type = m.type_val; + else item_type = m.type; - default_counter+=1; + if (m.visited) { + compiler_error(it->pos, "Field already initialized"); + } + else m.visited = true; + break; + } + } + if (!item_type) + compiler_error(it->pos, "No member with that name"); + } + else { + if (default_counter == -1) + compiler_error(it->pos, "Cant mix named fields and normal fields"); + if (default_counter >= type->agg.members.len) + compiler_error(it->pos, "Too many struct initializers"); + + Ast_Resolved_Member *m = &type->agg.members[default_counter]; + it->resolved_name = m->name; + it->resolved_index = default_counter; + m->visited = true; + + // @copy_paste + if (m->type == pctx->type_type && m->type_val) { + item_type = m->type_val; + } + else item_type = m->type; + + default_counter += 1; + } + + assert(item_type); + Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); + make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); + + it->resolved_type = item_type; + try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); } - assert(item_type); - Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); - make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); - - it->resolved_type = item_type; - try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); - } - - For(type->agg.members) it.visited = false; + For(type->agg.members) it.visited = false; } CORE_Static Ast_Expr * -unpack_ast_call_expr_for_builtin(Ast_Call *call){ - if(call->exprs.len != 1) { - compiler_error(call->pos, "Expected exactly 1 argument inside a builtin function call got instead %d", (int)call->exprs.len); - } - return call->exprs[0]->item; +unpack_ast_call_expr_for_builtin(Ast_Call *call) { + if (call->exprs.len != 1) { + compiler_error(call->pos, "Expected exactly 1 argument inside a builtin function call got instead %d", (int)call->exprs.len); + } + return call->exprs[0]->item; } CORE_Static bool -expr_atom_is_equal_intern(Ast_Expr *expr, Intern_String intern){ - assert(expr->kind == AST_IDENT || expr->kind == AST_BINARY); - if(expr->kind == AST_IDENT){ - Ast_Atom *atom = (Ast_Atom *)expr; - if(atom->intern_val == intern) { - return true; +expr_atom_is_equal_intern(Ast_Expr *expr, Intern_String intern) { + assert(expr->kind == AST_IDENT || expr->kind == AST_BINARY); + if (expr->kind == AST_IDENT) { + Ast_Atom *atom = (Ast_Atom *)expr; + if (atom->intern_val == intern) { + return true; + } } - } - return false; + return false; } CORE_Static Operand -resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_string_context, Ast_Scope *field_access_scope){ - if(!ast && is_flag_set(flags, AST_CAN_BE_NULL)) return {}; - assert(is_flag_set(ast->flags, AST_EXPR)); - assert(ast->parent_scope->kind == AST_SCOPE || ast->parent_scope->kind == AST_FILE); +resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_string_context, Ast_Scope *field_access_scope) { + if (!ast && is_flag_set(flags, AST_CAN_BE_NULL)) return {}; + assert(is_flag_set(ast->flags, AST_EXPR)); + assert(ast->parent_scope->kind == AST_SCOPE || ast->parent_scope->kind == AST_FILE); - switch(ast->kind){ + switch (ast->kind) { - CASE(IDENT, Atom){ - Ast_Scope *scope = field_access_scope ? field_access_scope : node->parent_scope; - Search_Flag flag = field_access_scope ? SEARCH_ONLY_CURRENT_SCOPE : 0; + CASE(IDENT, Atom) { + Ast_Scope *scope = field_access_scope ? field_access_scope : node->parent_scope; + Search_Flag flag = field_access_scope ? SEARCH_ONLY_CURRENT_SCOPE : 0; - Ast_Decl *decl = resolve_name(scope, node->pos, node->intern_val, flag | RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED); + Ast_Decl *decl = resolve_name(scope, node->pos, node->intern_val, flag | RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED); - // Substitute lambda alias - if(decl->kind == AST_CONST && decl->resolved_decl && decl->resolved_decl->kind == AST_LAMBDA){ - decl = decl->resolved_decl; - } - - node->resolved_decl = decl; - node->resolved_type = node->resolved_decl->type; - - Operand result = operand(node->resolved_decl); - if(result.is_const){ - rewrite_into_const(node, Ast_Atom, node->resolved_decl->value); - node->resolved_decl = decl; - } - - return result; - BREAK(); - } - - CASE(VALUE, Atom){ - return operand_const_rvalue(node->value); - BREAK(); - } - - // Typespec array [32]int - CASE(ARRAY, Array){ - Operand type = resolve_expr(node->base, AST_CANT_BE_NULL, 0, 0); - Operand expr = require_const_int(node->expr, AST_CAN_BE_NULL); - if(type.type != pctx->type_type) compiler_error(node->pos, "Prefix array operator is only allowed on types"); - type_complete(type.type_val); - - if(node->expr){ - type.type_val = type_array(type.type_val, bigint_as_unsigned(&expr.big_int_val)); - } else{ - type.type_val = type_slice(type.type_val, node); - } - - // If this is a type we want to rewrite that type - // int o an integer constant which should be an id - // of that type - if(!is_flag_set(flags, RESOLVE_TYPESPEC)){ - rewrite_into_const(node, Ast_Array, type.value); - } - - node->resolved_type = type.type_val; - return operand_type(node->resolved_type); - BREAK(); - } - - CASE(LAMBDA_EXPR, Lambda){ - node->resolved_type = resolve_lambda_type(node); - Operand result = operand_type(node->resolved_type); - try_resolving_lambda_scope(&result, node, node->resolved_type); - return result; - BREAK(); - } - - CASE(INDEX, Index){ - Operand left = resolve_expr(node->expr, AST_CANT_BE_NULL , 0, field_access_scope); - Operand index = resolve_expr(node->index, AST_CANT_BE_NULL, 0, 0); - if(!(is_int(index.type) || is_enum(index.type))) - compiler_error(node->pos, "Trying to index the array with invalid type, expected [Int] got instead %Q", typestring(index.type)); - - node->index_original_type = left.type; - node->resolved_type = left.type->arr.base; - if(left.type == pctx->type_pointer_to_char){ - node->resolved_type = pctx->type_char; - } - if (left.type == pctx->type_pointer_to_void){ - compiler_error(node->pos, "Trying to index a void pointer is invalid"); - } - else if(is_string(left.type)) { - - // @warning: not sure about this context thing here. - // I didn't test it much so might need to delete later. - if(is_untyped(left.type)){ - if(compound_and_const_string_context){ - if(!is_string(compound_and_const_string_context)){ - compiler_error(node->pos, "Type mismatch, it's %Q but expected %Q", typestring(left.type), typestring(compound_and_const_string_context)); - } - left.type = compound_and_const_string_context; - } - else{ - left.type = pctx->type_string; - } - } - node->resolved_type = pctx->type_u8; - } - - // @todo: type_architecture? - // we only try to convert the index cause array can't be const - // right now - try_propagating_resolved_type_to_untyped_literals(node->index, pctx->type_s64); - try_propagating_resolved_type_to_untyped_literals(node->expr, left.type); - - if(!left.type) - compiler_error(node->expr->pos, "Internal compiler error: type of array is null somehow"); - if(is_untyped(left.type)) - compiler_error(node->expr->pos, "Internal compiler error: type of array is marked as untyped somehow"); - - if(is_string(left.type)){ - return operand_lvalue_set_flag_on_node(pctx->type_u8, node); - } - - if(!is_array(left.type) && !is_pointer(left.type) && !is_slice(left.type)){ - compiler_error(node->pos, "Indexing variable that is not an [Array] or [Pointer], it's of type %Q instead", typestring(left.type)); - } - - return operand_lvalue_set_flag_on_node(node->resolved_type, node); - BREAK(); - } - - CASE(BINARY, Binary){ - if(token_is_assign(node->op)){ - assert(is_flag_set(node->flags, AST_STMT)); - Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); - if(!left.is_lvalue) compiler_error(node->pos, "Assigning to rvalue"); - Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, field_access_scope); - - // @warning: Could be buggy, previously we strictly matched if types are exact - // need to test this with slices and stuff - make_sure_value_is_compatible_with_type(node->pos, &right, left.type, TYPE_AND_EXPR_REQUIRED); - - node->resolved_type = right.type; - try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type, node->right->resolved_type); - try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type, node->left->resolved_type); - return {}; - } - else if(node->op == TK_Arrow){ - return resolve_cast(node); - } - else if(node->op == TK_Dot){ - Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); - Ast_Scope *scope = 0; - - Ast_Decl *decl = left.resolved_decl; - if(decl && decl->kind == AST_NAMESPACE){ - scope = decl->scope; - } - - else { - // Make sure it's a type but also not a type id - Ast_Type *type = left.type; - if(type == pctx->type_type && left.type_val){ - if(is_enum(left.type_val) || is_struct(left.type_val)){ - type = left.type_val; - } - } - - // We need 2 resolved types, for left and right - // Then we can compare if we got a pointer we need to - // figure that out to replace '.' with '->' for pointer structs - node->dot_access_step_resolution = type; - - if(is_pointer(type)) type = type->base; - type_complete(type); - - if(!type->ast){ - compiler_error(node->pos, "Builtin type %Q doesn't have anything to access using '.' operator", typestring(type)); - } - scope = ((Ast_Decl *)type->ast)->scope; - if(!scope){ - compiler_error(node->pos, "Internal compiler error? Type %Q doesn't have scope, you cannot use '.' on this variable", typestring(type)); - } - } - - Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, scope); - node->resolved_type = right.type; - - if(right.is_const){ - rewrite_into_const(node, Ast_Binary, right.value); - } - return right; - } - else{ - Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); - Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, field_access_scope); - B32 is_const = left.is_const && right.is_const; - B32 proceed_to_default_operator_handler = true; - - // Try finding a operator overload - if(!is_const){ - Value left_copy = left.value; - Value right_copy = right.value; - try_converting_untyped_to_default_type(&left_copy); - try_converting_untyped_to_default_type(&right_copy); - U64 hash = calculate_hash_for_arguments(left_copy.type, right_copy.type); - Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, left_copy.type, right_copy.type, node->pos, node->op, hash); - - if(operator_overload){ - proceed_to_default_operator_handler = false; - if(operator_overload->lambda->ret.len != 1){ - compiler_error(operator_overload->pos, "Operator overload is required to have exactly 1 return value"); + // Substitute lambda alias + if (decl->kind == AST_CONST && decl->resolved_decl && decl->resolved_decl->kind == AST_LAMBDA) { + decl = decl->resolved_decl; } - left.value = left_copy; - right.value = right_copy; - // @warning: might be buggy, added after a long break - // Propagate const - if (left.is_const) { - Ast_Atom *atom_left = (Ast_Atom *)node->left; - atom_left->value = left.value; - } - if (right.is_const) { - Ast_Atom *atom_right = (Ast_Atom *)node->right; - atom_right->value = right.value; + node->resolved_decl = decl; + node->resolved_type = node->resolved_decl->type; + + Operand result = operand(node->resolved_decl); + if (result.is_const) { + rewrite_into_const(node, Ast_Atom, node->resolved_decl->value); + node->resolved_decl = decl; } - node->resolved_type = operator_overload->type->func.ret; - node->resolved_operator_overload = operator_overload; - - // We opt out early because we convert all literals to default type - // We don't need to propagate resolved type - return operand_rvalue(node->resolved_type); - } + return result; + BREAK(); } - // Not found or constant then go for default option - Value value = {}; - if(proceed_to_default_operator_handler){ - value = eval_binary(node->pos, node->op, left.value, right.value, is_const); - node->resolved_type = value.type; + CASE(VALUE, Atom) { + return operand_const_rvalue(node->value); + BREAK(); } - if(is_const){ - // We don't need to propagte types for const values cause we are rewritting them - rewrite_into_const(node, Ast_Binary, value); - return operand_const_rvalue(value); + // Typespec array [32]int + CASE(ARRAY, Array) { + Operand type = resolve_expr(node->base, AST_CANT_BE_NULL, 0, 0); + Operand expr = require_const_int(node->expr, AST_CAN_BE_NULL); + if (type.type != pctx->type_type) compiler_error(node->pos, "Prefix array operator is only allowed on types"); + type_complete(type.type_val); + + if (node->expr) { + type.type_val = type_array(type.type_val, bigint_as_unsigned(&expr.big_int_val)); + } + else { + type.type_val = type_slice(type.type_val, node); + } + + // If this is a type we want to rewrite that type + // int o an integer constant which should be an id + // of that type + if (!is_flag_set(flags, RESOLVE_TYPESPEC)) { + rewrite_into_const(node, Ast_Array, type.value); + } + + node->resolved_type = type.type_val; + return operand_type(node->resolved_type); + BREAK(); } - else { - try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); - try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type); - return operand_rvalue(node->resolved_type); + + CASE(LAMBDA_EXPR, Lambda) { + node->resolved_type = resolve_lambda_type(node); + Operand result = operand_type(node->resolved_type); + try_resolving_lambda_scope(&result, node, node->resolved_type); + return result; + BREAK(); } - } - BREAK(); + + CASE(INDEX, Index) { + Operand left = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, field_access_scope); + Operand index = resolve_expr(node->index, AST_CANT_BE_NULL, 0, 0); + if (!(is_int(index.type) || is_enum(index.type))) + compiler_error(node->pos, "Trying to index the array with invalid type, expected [Int] got instead %Q", typestring(index.type)); + + node->index_original_type = left.type; + node->resolved_type = left.type->arr.base; + if (left.type == pctx->type_pointer_to_char) { + node->resolved_type = pctx->type_char; + } + if (left.type == pctx->type_pointer_to_void) { + compiler_error(node->pos, "Trying to index a void pointer is invalid"); + } + else if (is_string(left.type)) { + + // @warning: not sure about this context thing here. + // I didn't test it much so might need to delete later. + if (is_untyped(left.type)) { + if (compound_and_const_string_context) { + if (!is_string(compound_and_const_string_context)) { + compiler_error(node->pos, "Type mismatch, it's %Q but expected %Q", typestring(left.type), typestring(compound_and_const_string_context)); + } + left.type = compound_and_const_string_context; + } + else { + left.type = pctx->type_string; + } + } + node->resolved_type = pctx->type_u8; + } + + // @todo: type_architecture? + // we only try to convert the index cause array can't be const + // right now + try_propagating_resolved_type_to_untyped_literals(node->index, pctx->type_s64); + try_propagating_resolved_type_to_untyped_literals(node->expr, left.type); + + if (!left.type) + compiler_error(node->expr->pos, "Internal compiler error: type of array is null somehow"); + if (is_untyped(left.type)) + compiler_error(node->expr->pos, "Internal compiler error: type of array is marked as untyped somehow"); + + if (is_string(left.type)) { + return operand_lvalue_set_flag_on_node(pctx->type_u8, node); + } + + if (!is_array(left.type) && !is_pointer(left.type) && !is_slice(left.type)) { + compiler_error(node->pos, "Indexing variable that is not an [Array] or [Pointer], it's of type %Q instead", typestring(left.type)); + } + + return operand_lvalue_set_flag_on_node(node->resolved_type, node); + BREAK(); + } + + CASE(BINARY, Binary) { + if (token_is_assign(node->op)) { + assert(is_flag_set(node->flags, AST_STMT)); + Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); + if (!left.is_lvalue) compiler_error(node->pos, "Assigning to rvalue"); + Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, field_access_scope); + + // @warning: Could be buggy, previously we strictly matched if types are exact + // need to test this with slices and stuff + make_sure_value_is_compatible_with_type(node->pos, &right, left.type, TYPE_AND_EXPR_REQUIRED); + + node->resolved_type = right.type; + try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type, node->right->resolved_type); + try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type, node->left->resolved_type); + return {}; + } + else if (node->op == TK_Arrow) { + return resolve_cast(node); + } + else if (node->op == TK_Dot) { + Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); + Ast_Scope *scope = 0; + + Ast_Decl *decl = left.resolved_decl; + if (decl && decl->kind == AST_NAMESPACE) { + scope = decl->scope; + } + + else { + // Make sure it's a type but also not a type id + Ast_Type *type = left.type; + if (type == pctx->type_type && left.type_val) { + if (is_enum(left.type_val) || is_struct(left.type_val)) { + type = left.type_val; + } + } + + // We need 2 resolved types, for left and right + // Then we can compare if we got a pointer we need to + // figure that out to replace '.' with '->' for pointer structs + node->dot_access_step_resolution = type; + + if (is_pointer(type)) type = type->base; + type_complete(type); + + if (!type->ast) { + compiler_error(node->pos, "Builtin type %Q doesn't have anything to access using '.' operator", typestring(type)); + } + scope = ((Ast_Decl *)type->ast)->scope; + if (!scope) { + compiler_error(node->pos, "Internal compiler error? Type %Q doesn't have scope, you cannot use '.' on this variable", typestring(type)); + } + } + + Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, scope); + node->resolved_type = right.type; + + if (right.is_const) { + rewrite_into_const(node, Ast_Binary, right.value); + } + return right; + } + else { + Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); + Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, field_access_scope); + B32 is_const = left.is_const && right.is_const; + B32 proceed_to_default_operator_handler = true; + + // Try finding a operator overload + if (!is_const) { + Value left_copy = left.value; + Value right_copy = right.value; + try_converting_untyped_to_default_type(&left_copy); + try_converting_untyped_to_default_type(&right_copy); + U64 hash = calculate_hash_for_arguments(left_copy.type, right_copy.type); + Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, left_copy.type, right_copy.type, node->pos, node->op, hash); + + if (operator_overload) { + proceed_to_default_operator_handler = false; + if (operator_overload->lambda->ret.len != 1) { + compiler_error(operator_overload->pos, "Operator overload is required to have exactly 1 return value"); + } + + left.value = left_copy; + right.value = right_copy; + // @warning: might be buggy, added after a long break + // Propagate const + if (left.is_const) { + Ast_Atom *atom_left = (Ast_Atom *)node->left; + atom_left->value = left.value; + } + if (right.is_const) { + Ast_Atom *atom_right = (Ast_Atom *)node->right; + atom_right->value = right.value; + } + + node->resolved_type = operator_overload->type->func.ret; + node->resolved_operator_overload = operator_overload; + + // We opt out early because we convert all literals to default type + // We don't need to propagate resolved type + return operand_rvalue(node->resolved_type); + } + } + + // Not found or constant then go for default option + Value value = {}; + if (proceed_to_default_operator_handler) { + value = eval_binary(node->pos, node->op, left.value, right.value, is_const); + node->resolved_type = value.type; + } + + if (is_const) { + // We don't need to propagte types for const values cause we are rewritting them + rewrite_into_const(node, Ast_Binary, value); + return operand_const_rvalue(value); + } + else { + try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); + try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type); + return operand_rvalue(node->resolved_type); + } + } + BREAK(); + } + + CASE(VAR, Decl) { + resolve_stmt(node, 0); + return {}; + BREAK(); + } + + CASE(UNARY, Unary) { + Operand value = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + if (node->op == TK_Pointer) { + if (value.type->kind == TYPE_POINTER) { + node->resolved_type = value.type->base; + return operand_lvalue_set_flag_on_node(node->resolved_type, node); + } + else if (value.type->kind == TYPE_TYPE) { + if (!is_flag_set(flags, RESOLVE_TYPESPEC)) { + rewrite_into_const(node, Ast_Array, value.value); + } + + node->resolved_type = type_pointer(value.type_val); + return operand_type(node->resolved_type); + } + else { + compiler_error(node->pos, "Dereferencing expression %Q that is not a [Pointer] or [Type]", typestring(value.type)); + return {}; + } + } + else if (node->op == TK_Dereference) { + if (!value.is_lvalue) { + compiler_error(node->pos, "Can't take a pointer of this expression. It's not bound to anything so taking a pointer of it would be pretty silly."); + } + + node->resolved_type = type_pointer(value.type); + return operand_lvalue_set_flag_on_node(node->resolved_type, node); + } + else { + + // Try finding a operator overload + B32 proceed_to_default_operator_handler = true; + if (!value.is_const) { + U64 hash = calculate_hash_for_arguments(value.type); + Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, value.type, 0, node->pos, node->op, hash); + if (operator_overload) { + proceed_to_default_operator_handler = false; + if (operator_overload->lambda->ret.len != 1) { + compiler_error(operator_overload->pos, "Operator overload is required to have exactly 1 return value"); + } + + node->resolved_type = operator_overload->type->func.ret; + node->resolved_operator_overload = operator_overload; + } + } + + if (proceed_to_default_operator_handler) { + eval_unary(node->pos, node->op, &value); + node->resolved_type = value.type; + } + + if (value.is_const) { + rewrite_into_const(node, Ast_Unary, value.value); + return operand_const_rvalue(value.value); + } + + return operand_rvalue(node->resolved_type); + } + + BREAK(); + } + + CASE(COMPOUND, Call) { + Operand op = resolve_expr(node->typespec, inherit_flag(flags, AST_CAN_BE_NULL), 0, field_access_scope); + + Ast_Type *type = op.type; + if (type) { + if (type != pctx->type_type) + compiler_error(node->pos, "Expected type [Type] got instead %Q", typestring(type)); + type = op.type_val; + } + if (!type) + type = compound_and_const_string_context; + if (!type) + compiler_error(node->pos, "Couldn't infer type of compound expression"); + + type_complete(type); + node->resolved_type = type; + if (is_array(type) || is_slice(type)) { + resolve_compound_array(node, type); + } + else if (is_struct(type)) { + resolve_compound_struct(node, type); + } + else { + compiler_error(node->pos, "Internal compiler error: Invalid type was passed to the compound expression, should have been an array, struct or slice"); + } + + return operand_lvalue_set_flag_on_node(type, node); + + BREAK(); + } + + CASE(CALL, Call) { + if (expr_atom_is_equal_intern(node->name, pctx->intern_sizeof)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_SIZE_OF; + + Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; + type_complete(type); + + if (type->size == 0) { + compiler_error(node->pos, "Internal compiler error: calling SizeOf but the resulting size of type is obviously invalid suggesting that type was not completed properly"); + } + + Value v = value_int(type->size); + rewrite_into_const(node, Ast_Builtin, v); + return operand_const_rvalue(v); + } + + else if (expr_atom_is_equal_intern(node->name, pctx->intern_typeof)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_TYPE_OF; + Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; + + Operand result = operand_type(type); + rewrite_into_const(node, Ast_Builtin, result.value); + return result; + } + + else if (expr_atom_is_equal_intern(node->name, pctx->intern_alignof)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_ALIGN_OF; + + Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; + type_complete(type); + if (type->size == 0) { + compiler_error(node->pos, "Internal compiler error: calling SizeOf but the resulting size of type is obviously invalid suggesting that type was not completed properly"); + } + + Value v = value_int(type->align); + rewrite_into_const(node, Ast_Builtin, v); + return operand_const_rvalue(v); + } + + else if (expr_atom_is_equal_intern(node->name, pctx->intern_len)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_LENGTH_OF; + node->resolved_type = pctx->type_s64; + if (is_array(name.type)) { + Value value = value_int(name.type->arr.size); + rewrite_into_const(node, Ast_Builtin, value); + return operand_const_rvalue(value); + } + else if (name.type->kind == TYPE_UNTYPED_STRING) { + Value value = value_int(name.intern_val.len); + rewrite_into_const(node, Ast_Builtin, value); + return operand_const_rvalue(value); + } + else if (is_array(name.type) || is_slice(name.type) || is_string(name.type)) { + return operand_rvalue(pctx->type_s64); + } + else compiler_error(node->pos, "Can't get length of type %Q", typestring(name.type)); + } + + else { + // if (node->name->pos->string == "CreateFileW"_s) { + // __debugbreak(); + // } + Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + if (name.type->kind != TYPE_LAMBDA) { + compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type)); + } + if (!name.resolved_decl) { + compiler_error(node->pos, "Internal compiler error: Failed to propagate a resolved lambda declaration from atom resolution"); + } + node->resolved_decl = name.resolved_decl; + + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + Array items = {scratch}; + S64 default_iter = 0; + + /* + @warning + We only have one instance of a given Lambda type for example (a: Vec3): Vec3. + Even though we might have multiple functions with this signature. + This creates a problem cause sometimes we might refer to the wrong Ast. + Need to be careful!!! + */ + Ast_Lambda *lambda = (Ast_Lambda *)name.type->ast; + For_Named(lambda->args, lambda_arg) { + assert(lambda_arg->type); + + Ast_Call_Item *item = 0; + For(node->exprs) { + assert(!is_flag_set(it->call_flags, CALL_INDEX)); + if (is_flag_set(it->call_flags, CALL_NAME)) { + Ast_Atom *name = it->name; + assert(name->kind == AST_IDENT); + if (name->intern_val.str == lambda_arg->name.str) + item = it; + } + else if (node->exprs.get_index(&it) == default_iter) { + default_iter++; + item = it; + } + else if (node->exprs.get_index(&it) > default_iter) { + compiler_error(it->pos, "Positional argument after named argument"); + } + + if (item) break; + } + + if (item) { + set_flag(item->call_flags, CALL_INCLUDED); + Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); + + make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED); + item->resolved_type = lambda_arg->type; + + try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type); + item->resolved_index = lambda->args.get_index(&lambda_arg); + items.add(item); + } + + else { + if (!lambda_arg->expr) { + compiler_error(node->pos, node->resolved_decl->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name); + } + + // @note: default values are typechecked when they get resolved + Ast_Call_Item *call_item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr); + call_item->resolved_type = lambda_arg->expr->resolved_type; + call_item->resolved_index = lambda->args.get_index(&lambda_arg); + set_flag(call_item->call_flags, CALL_INCLUDED); + items.add(call_item); + } + } + + // @note: check if all arguments are included and cleanup + For(node->exprs) { + if (!is_flag_set(it->call_flags, CALL_INCLUDED)) + compiler_error(it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments"); + else unset_flag(it->call_flags, CALL_INCLUDED); + } + + node->exprs = items.tight_copy(pctx->perm); + node->resolved_type = name.type->func.ret; + + return operand_rvalue(name.type->func.ret); + // + // CALL End + // + } + BREAK(); + } + + invalid_default_case; } - CASE(VAR, Decl){ - resolve_stmt(node, 0); - return {}; - BREAK(); - } - - CASE(UNARY, Unary){ - Operand value = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - if(node->op == TK_Pointer){ - if(value.type->kind == TYPE_POINTER){ - node->resolved_type = value.type->base; - return operand_lvalue_set_flag_on_node(node->resolved_type, node); - } - else if(value.type->kind == TYPE_TYPE){ - if(!is_flag_set(flags, RESOLVE_TYPESPEC)){ - rewrite_into_const(node, Ast_Array, value.value); - } - - node->resolved_type = type_pointer(value.type_val); - return operand_type(node->resolved_type); - } - else{ compiler_error(node->pos, "Dereferencing expression %Q that is not a [Pointer] or [Type]", typestring(value.type)); return {}; } - } - else if(node->op == TK_Dereference){ - if(!value.is_lvalue){ - compiler_error(node->pos, "Can't take a pointer of this expression. It's not bound to anything so taking a pointer of it would be pretty silly."); - } - - node->resolved_type = type_pointer(value.type); - return operand_lvalue_set_flag_on_node(node->resolved_type, node); - } - else{ - - // Try finding a operator overload - B32 proceed_to_default_operator_handler = true; - if(!value.is_const){ - U64 hash = calculate_hash_for_arguments(value.type); - Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, value.type, 0, node->pos, node->op, hash); - if(operator_overload){ - proceed_to_default_operator_handler = false; - if(operator_overload->lambda->ret.len != 1){ - compiler_error(operator_overload->pos, "Operator overload is required to have exactly 1 return value"); - } - - node->resolved_type = operator_overload->type->func.ret; - node->resolved_operator_overload = operator_overload; - } - } - - if(proceed_to_default_operator_handler){ - eval_unary(node->pos, node->op, &value); - node->resolved_type = value.type; - } - - if(value.is_const){ - rewrite_into_const(node, Ast_Unary, value.value); - return operand_const_rvalue(value.value); - } - - return operand_rvalue(node->resolved_type); - } - - BREAK(); - } - - CASE(COMPOUND, Call){ - Operand op = resolve_expr(node->typespec, inherit_flag(flags, AST_CAN_BE_NULL), 0, field_access_scope); - - Ast_Type *type = op.type; - if(type){ - if(type != pctx->type_type) - compiler_error(node->pos, "Expected type [Type] got instead %Q", typestring(type)); - type = op.type_val; - } - if(!type) - type = compound_and_const_string_context; - if(!type) - compiler_error(node->pos, "Couldn't infer type of compound expression"); - - type_complete(type); - node->resolved_type = type; - if(is_array(type) || is_slice(type)){ - resolve_compound_array(node, type); - } - else if(is_struct(type)){ - resolve_compound_struct(node, type); - } - else { - compiler_error(node->pos, "Internal compiler error: Invalid type was passed to the compound expression, should have been an array, struct or slice"); - } - - return operand_lvalue_set_flag_on_node(type, node); - - BREAK(); - } - - CASE(CALL, Call){ - if(expr_atom_is_equal_intern(node->name, pctx->intern_sizeof)){ - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_SIZE_OF; - - Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; - type_complete(type); - - if(type->size == 0){ - compiler_error(node->pos, "Internal compiler error: calling SizeOf but the resulting size of type is obviously invalid suggesting that type was not completed properly"); - } - - Value v = value_int(type->size); - rewrite_into_const(node, Ast_Builtin, v); - return operand_const_rvalue(v); - } - - else if(expr_atom_is_equal_intern(node->name, pctx->intern_typeof)){ - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_TYPE_OF; - Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; - - Operand result = operand_type(type); - rewrite_into_const(node, Ast_Builtin, result.value); - return result; - } - - else if(expr_atom_is_equal_intern(node->name, pctx->intern_alignof)){ - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_ALIGN_OF; - - Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; - type_complete(type); - if(type->size == 0){ - compiler_error(node->pos, "Internal compiler error: calling SizeOf but the resulting size of type is obviously invalid suggesting that type was not completed properly"); - } - - Value v = value_int(type->align); - rewrite_into_const(node, Ast_Builtin, v); - return operand_const_rvalue(v); - } - - else if(expr_atom_is_equal_intern(node->name, pctx->intern_len)){ - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_LENGTH_OF; - node->resolved_type = pctx->type_s64; - if(is_array(name.type)){ - Value value = value_int(name.type->arr.size); - rewrite_into_const(node, Ast_Builtin, value); - return operand_const_rvalue(value); - } - else if(name.type->kind == TYPE_UNTYPED_STRING){ - Value value = value_int(name.intern_val.len); - rewrite_into_const(node, Ast_Builtin, value); - return operand_const_rvalue(value); - } - else if(is_array(name.type) || is_slice(name.type) || is_string(name.type)){ - return operand_rvalue(pctx->type_s64); - } - else compiler_error(node->pos, "Can't get length of type %Q", typestring(name.type)); - } - - else { - // if (node->name->pos->string == "CreateFileW"_s) { - // __debugbreak(); - // } - Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - if(name.type->kind != TYPE_LAMBDA){ - compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type)); - } - if(!name.resolved_decl){ - compiler_error(node->pos, "Internal compiler error: Failed to propagate a resolved lambda declaration from atom resolution"); - } - node->resolved_decl = name.resolved_decl; - - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - Array items = {scratch}; - S64 default_iter = 0; - - /* - @warning - We only have one instance of a given Lambda type for example (a: Vec3): Vec3. - Even though we might have multiple functions with this signature. - This creates a problem cause sometimes we might refer to the wrong Ast. - Need to be careful!!! - */ - Ast_Lambda *lambda = (Ast_Lambda *)name.type->ast; - For_Named(lambda->args, lambda_arg){ - assert(lambda_arg->type); - - Ast_Call_Item *item = 0; - For(node->exprs){ - assert(!is_flag_set(it->call_flags, CALL_INDEX)); - if(is_flag_set(it->call_flags, CALL_NAME)){ - Ast_Atom *name = it->name; - assert(name->kind == AST_IDENT); - if(name->intern_val.str == lambda_arg->name.str) - item = it; - } - else if(node->exprs.get_index(&it) == default_iter){ - default_iter++; - item = it; - } - else if(node->exprs.get_index(&it) > default_iter){ - compiler_error(it->pos, "Positional argument after named argument"); - } - - if(item) break; - } - - if(item){ - set_flag(item->call_flags, CALL_INCLUDED); - Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); - - make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED); - item->resolved_type = lambda_arg->type; - - try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type); - item->resolved_index = lambda->args.get_index(&lambda_arg); - items.add(item); - } - - else{ - if(!lambda_arg->expr){ - compiler_error(node->pos, node->resolved_decl->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name); - } - - // @note: default values are typechecked when they get resolved - Ast_Call_Item *call_item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr); - call_item->resolved_type = lambda_arg->expr->resolved_type; - call_item->resolved_index = lambda->args.get_index(&lambda_arg); - set_flag(call_item->call_flags, CALL_INCLUDED); - items.add(call_item); - } - - } - - // @note: check if all arguments are included and cleanup - For(node->exprs){ - if(!is_flag_set(it->call_flags, CALL_INCLUDED)) - compiler_error(it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments"); - else unset_flag(it->call_flags, CALL_INCLUDED); - } - - node->exprs = items.tight_copy(pctx->perm); - node->resolved_type = name.type->func.ret; - - - return operand_rvalue(name.type->func.ret); - // - // CALL End - // - } - BREAK(); - } - - invalid_default_case; - } - - invalid_return; + invalid_return; } CORE_Static Ast_Type * -get_type_base(Ast_Type *type){ - switch(type->kind){ - case TYPE_POINTER: case TYPE_SLICE: case TYPE_ARRAY: return get_type_base(type->base); - default: return type; - } +get_type_base(Ast_Type *type) { + switch (type->kind) { + case TYPE_POINTER: + case TYPE_SLICE: + case TYPE_ARRAY: return get_type_base(type->base); + default: return type; + } } CORE_Static void -resolve_decl(Ast_Decl *ast){ - if(ast->state == DECL_RESOLVED) - return; - else if(ast->state == DECL_RESOLVING){ - compiler_error(ast->pos, "Cyclic dependency of %s", ast->name.str); - return; - } - assert(ast->state == DECL_NOT_RESOLVED); - - ast->state = DECL_RESOLVING; - { - - switch(ast->kind){ - CASE(LAMBDA, Decl){ - Ast_Lambda *lambda = node->lambda; - lambda->resolved_type = resolve_lambda_type(lambda); - Operand result = operand_type(lambda->resolved_type); - - // @note: top level lambda needs to get marked as resolved - // so that the cyclic dependency wont trigger - node->type = lambda->resolved_type; - node->state = DECL_RESOLVED; - - // @todo: We also need to make sure there is a return value when ret type is not void - try_resolving_lambda_scope(&result, lambda, node->type); - node->value = result.value; - - Arena *scratch = pctx->scratch; - Scratch_Scope _scope(scratch); - - node->unique_name = node->name; - if(!is_flag_set(node->expr->flags, AST_FOREIGN)){ - node->unique_name = pctx->intern(string_fmt(scratch, "%Q%Q%d", pctx->symbol_prefix, node->name, pctx->lambda_ids++)); - } - - if(is_flag_set(node->flags, AST_OPERATOR_OVERLOAD)){ - node->unique_name = pctx->intern(string_fmt(scratch, "%QOPERATOR_%Q%d", pctx->symbol_prefix, node->overload_op_info->name, pctx->lambda_ids++)); - } - - BREAK(); - } - - CASE(CONST, Decl){ - // @warning: if in the future we add type here then pass it to resolve expr for - // compound - Operand op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); - if(!op.is_const){ - compiler_error(node->pos, "Assigning a value that is not constant to a constant declaration"); - } - - node->value = op.value; - if(op.value.type == pctx->type_type){ - node->kind = AST_TYPE; - if(is_flag_set(node->flags, AST_STRICT)){ - node->type_val = type_copy(pctx->perm, node->type_val); - } - } - - // if(is_lambda(op.type)){ - // node->unique_name = node->resolved_decl->unique_name; - // } - BREAK(); - } - - CASE(VAR, Decl){ - Ast_Type *type = node->type; - if(!type) type = resolve_typespec(node->typespec, AST_CAN_BE_NULL | RESOLVE_TYPESPEC_COMPLETE); - Operand op = resolve_expr(node->expr, AST_CAN_BE_NULL, type, 0); - assert(op.type != 0 || type != 0); - - make_sure_value_is_compatible_with_type(node->pos, &op, type, EXPR_CAN_BE_NULL|TYPE_CAN_BE_NULL); - node->value = op.value; - - if(is_flag_set(node->flags, AST_FOREIGN)){ - if(node->expr) compiler_error(node->pos, "#foreign variable shouldn't have value"); - } - - try_propagating_resolved_type_to_untyped_literals(node->expr, node->type); - - if(op.is_const){ - set_flag(node->flags, AST_VAR_IS_CONST); - } - BREAK(); - } - - case AST_NAMESPACE: break; - - CASE(ENUM, Decl){ - Ast_Type *type_of_enum = resolve_typespec(node->typespec, AST_CAN_BE_NULL); - - node->type = pctx->type_type; - node->type_val = type_enum(node, type_of_enum); - - S64 value = 1; - For_Named(node->scope->decls, decl){ - Operand op = {}; - if(decl->expr){ - op = require_const_int(decl->expr, AST_CANT_BE_NULL); - value = bigint_as_signed(&op.big_int_val) + 1; - } else{ - decl->state = DECL_RESOLVED; - op.type = node->type_val; - bigint_init_signed(&op.big_int_val, value); - if(is_flag_set(node->flags, AST_FLAG)){ - value = value << 1; - } else { - value += 1; - } - } - - decl->value = op.value; - } - BREAK(); - } - - invalid_default_case; +resolve_decl(Ast_Decl *ast) { + if (ast->state == DECL_RESOLVED) + return; + else if (ast->state == DECL_RESOLVING) { + compiler_error(ast->pos, "Cyclic dependency of %s", ast->name.str); + return; } + assert(ast->state == DECL_NOT_RESOLVED); - } - ast->state = DECL_RESOLVED; + ast->state = DECL_RESOLVING; + { - if(is_flag_set(ast->flags, AST_GLOBAL)) - add(pctx->perm, &pctx->ordered_decls, ast); + switch (ast->kind) { + CASE(LAMBDA, Decl) { + Ast_Lambda *lambda = node->lambda; + lambda->resolved_type = resolve_lambda_type(lambda); + Operand result = operand_type(lambda->resolved_type); + + // @note: top level lambda needs to get marked as resolved + // so that the cyclic dependency wont trigger + node->type = lambda->resolved_type; + node->state = DECL_RESOLVED; + + // @todo: We also need to make sure there is a return value when ret type is not void + try_resolving_lambda_scope(&result, lambda, node->type); + node->value = result.value; + + Arena *scratch = pctx->scratch; + Scratch_Scope _scope(scratch); + + node->unique_name = node->name; + if (!is_flag_set(node->expr->flags, AST_FOREIGN)) { + node->unique_name = pctx->intern(string_fmt(scratch, "%Q%Q%d", pctx->symbol_prefix, node->name, pctx->lambda_ids++)); + } + + if (is_flag_set(node->flags, AST_OPERATOR_OVERLOAD)) { + node->unique_name = pctx->intern(string_fmt(scratch, "%QOPERATOR_%Q%d", pctx->symbol_prefix, node->overload_op_info->name, pctx->lambda_ids++)); + } + + BREAK(); + } + + CASE(CONST, Decl) { + // @warning: if in the future we add type here then pass it to resolve expr for + // compound + Operand op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); + if (!op.is_const) { + compiler_error(node->pos, "Assigning a value that is not constant to a constant declaration"); + } + + node->value = op.value; + if (op.value.type == pctx->type_type) { + node->kind = AST_TYPE; + if (is_flag_set(node->flags, AST_STRICT)) { + node->type_val = type_copy(pctx->perm, node->type_val); + } + } + + // if(is_lambda(op.type)){ + // node->unique_name = node->resolved_decl->unique_name; + // } + BREAK(); + } + + CASE(VAR, Decl) { + Ast_Type *type = node->type; + if (!type) type = resolve_typespec(node->typespec, AST_CAN_BE_NULL | RESOLVE_TYPESPEC_COMPLETE); + Operand op = resolve_expr(node->expr, AST_CAN_BE_NULL, type, 0); + assert(op.type != 0 || type != 0); + + make_sure_value_is_compatible_with_type(node->pos, &op, type, EXPR_CAN_BE_NULL | TYPE_CAN_BE_NULL); + node->value = op.value; + + if (is_flag_set(node->flags, AST_FOREIGN)) { + if (node->expr) compiler_error(node->pos, "#foreign variable shouldn't have value"); + } + + try_propagating_resolved_type_to_untyped_literals(node->expr, node->type); + + if (op.is_const) { + set_flag(node->flags, AST_VAR_IS_CONST); + } + BREAK(); + } + + case AST_NAMESPACE: + break; + + CASE(ENUM, Decl) { + Ast_Type *type_of_enum = resolve_typespec(node->typespec, AST_CAN_BE_NULL); + + node->type = pctx->type_type; + node->type_val = type_enum(node, type_of_enum); + + S64 value = 1; + For_Named(node->scope->decls, decl) { + Operand op = {}; + if (decl->expr) { + op = require_const_int(decl->expr, AST_CANT_BE_NULL); + value = bigint_as_signed(&op.big_int_val) + 1; + } + else { + decl->state = DECL_RESOLVED; + op.type = node->type_val; + bigint_init_signed(&op.big_int_val, value); + if (is_flag_set(node->flags, AST_FLAG)) { + value = value << 1; + } + else { + value += 1; + } + } + + decl->value = op.value; + } + BREAK(); + } + + invalid_default_case; + } + } + ast->state = DECL_RESOLVED; + + if (is_flag_set(ast->flags, AST_GLOBAL)) + add(pctx->perm, &pctx->ordered_decls, ast); } diff --git a/core_typechecking.h b/core_typechecking.h index f028f6c..dae64b3 100644 --- a/core_typechecking.h +++ b/core_typechecking.h @@ -1,64 +1,68 @@ -struct Operand{ -/*#import meta -meta.inline_value_fields() -*/ -union { - Value value; - struct { - Ast_Type *type; -Ast_Decl *resolved_decl; -union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; -}; +struct Operand { + /*#import meta + meta.inline_value_fields() + */ + union { + Value value; + struct { + Ast_Type *type; + Ast_Decl *resolved_decl; + union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; + }; + }; }; -}; -/*END*/ - // is_const is used to rewrite the tree and bound - // something to the const name at the end - U8 is_const : 1; - U8 is_lvalue : 1; - U8 pound_strict: 1; + /*END*/ + // is_const is used to rewrite the tree and bound + // something to the const name at the end + U8 is_const : 1; + U8 is_lvalue : 1; + U8 pound_strict : 1; }; struct Scope_Search { - Array results; - Intern_String name; - Array scopes; + Array results; + Intern_String name; + Array scopes; - bool exit_on_find; - bool search_only_current_scope; - U32 scope_visit_id; + bool exit_on_find; + bool search_only_current_scope; + U32 scope_visit_id; }; typedef U32 Typecheck_Flag; enum { - TYPE_AND_EXPR_REQUIRED = 0, - TYPE_CAN_BE_NULL = 1, - EXPR_CAN_BE_NULL = 2 + TYPE_AND_EXPR_REQUIRED = 0, + TYPE_CAN_BE_NULL = 1, + EXPR_CAN_BE_NULL = 2 }; typedef U32 Resolve_Flag; -enum{ - AST_CANT_BE_NULL = bit_flag(0), - AST_CAN_BE_NULL = bit_flag(1), - RESOLVE_TYPESPEC_COMPLETE = bit_flag(2), - RESOLVE_TYPESPEC = bit_flag(3), +enum { + AST_CANT_BE_NULL = bit_flag(0), + AST_CAN_BE_NULL = bit_flag(1), + RESOLVE_TYPESPEC_COMPLETE = bit_flag(2), + RESOLVE_TYPESPEC = bit_flag(3), }; typedef U32 Search_Flag; -enum{ - SEARCH_ONLY_CURRENT_SCOPE = bit_flag(1), - RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED = bit_flag(2), +enum { + SEARCH_ONLY_CURRENT_SCOPE = bit_flag(1), + RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED = bit_flag(2), }; CORE_Static Operand resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_context, Ast_Scope *field_access_scope); CORE_Static void resolve_decl(Ast_Decl *ast); CORE_Static Ast_Decl *resolve_name(Ast_Scope *parent_scope, Token *pos, Intern_String name, Search_Flag search_flags = 0); -#define CASE(kind,type) case AST_##kind: { Ast_##type *node = (Ast_##type *)ast; -#define BREAK() } break +#define CASE(kind, type) \ + case AST_##kind: { \ + Ast_##type *node = (Ast_##type *)ast; +#define BREAK() \ + } \ + break diff --git a/core_types.cpp b/core_types.cpp index dc5b370..a4d16aa 100644 --- a/core_types.cpp +++ b/core_types.cpp @@ -1,331 +1,335 @@ CORE_Static const char * -get_name_of_type(Ast_Type *type){ - switch(type->kind){ - case TYPE_VOID: return "void"; - case TYPE_BOOL: return "Bool"; - case TYPE_CHAR: return "char"; - case TYPE_F32: return "F32"; - case TYPE_F64: return "F64"; - case TYPE_S8: return "S8"; - case TYPE_INT: return "int"; - case TYPE_S16: return "S16"; - case TYPE_S32: return "S32"; - case TYPE_S64: return "S64"; - case TYPE_U8: return "U8"; - case TYPE_U16: return "U16"; - case TYPE_U32: return "U32"; - case TYPE_U64: return "U64"; - case TYPE_TUPLE: return "Tuple"; - case TYPE_TYPE: return "Type"; - invalid_default_case; - } - return ""; +get_name_of_type(Ast_Type *type) { + switch (type->kind) { + case TYPE_VOID: return "void"; + case TYPE_BOOL: return "Bool"; + case TYPE_CHAR: return "char"; + case TYPE_F32: return "F32"; + case TYPE_F64: return "F64"; + case TYPE_S8: return "S8"; + case TYPE_INT: return "int"; + case TYPE_S16: return "S16"; + case TYPE_S32: return "S32"; + case TYPE_S64: return "S64"; + case TYPE_U8: return "U8"; + case TYPE_U16: return "U16"; + case TYPE_U32: return "U32"; + case TYPE_U64: return "U64"; + case TYPE_TUPLE: return "Tuple"; + case TYPE_TYPE: + return "Type"; + invalid_default_case; + } + return ""; } //----------------------------------------------------------------------------- // Type constructors and utillities //----------------------------------------------------------------------------- -force_inline B32 is_any(Ast_Type *a){return a == pctx->type_any;} -force_inline B32 is_struct(Ast_Type *a){return a->kind == TYPE_STRUCT;} -force_inline B32 is_lambda(Ast_Type *a){return a->kind == TYPE_LAMBDA;} -force_inline B32 is_array(Ast_Type *a){return a->kind == TYPE_ARRAY;} -force_inline B32 is_slice(Ast_Type *a){return a->kind == TYPE_SLICE;} -force_inline B32 is_tuple(Ast_Type *a){return a->kind == TYPE_TUPLE;} -force_inline B32 is_enum(Ast_Type *a){return a->kind == TYPE_ENUM;} -force_inline B32 is_pointer(Ast_Type *a){return a->kind == TYPE_POINTER;} -force_inline B32 is_void(Ast_Type *a){return a->kind == TYPE_VOID;} -force_inline B32 is_void_pointer(Ast_Type *a){return a == pctx->type_pointer_to_void;} -force_inline B32 is_string(Ast_Type *a){return a == pctx->type_string || a->kind == TYPE_UNTYPED_STRING || a == pctx->type_pointer_to_char;} -force_inline B32 is_untyped_int(Ast_Type *a){return a->kind == TYPE_UNTYPED_INT;} -force_inline B32 is_typed_int(Ast_Type *a){return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8);} -force_inline B32 is_int(Ast_Type *a){return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8) || a->kind == TYPE_UNTYPED_INT;} -force_inline B32 is_signed_int(Ast_Type *a){return !a->is_unsigned;} -force_inline B32 is_unsigned_int(Ast_Type *a){return a->is_unsigned;} -force_inline B32 is_float(Ast_Type *a){return a->kind == TYPE_F32 || a->kind == TYPE_F64 || a->kind == TYPE_UNTYPED_FLOAT;} -force_inline B32 is_f32(Ast_Type *a){return a->kind == TYPE_F32;} -force_inline B32 is_f64(Ast_Type *a){return a->kind == TYPE_F64;} -force_inline B32 is_bool(Ast_Type *a){return a->kind == TYPE_BOOL || a->kind == TYPE_UNTYPED_BOOL;} -force_inline B32 is_untyped(Ast_Type *a){return a->kind >= TYPE_UNTYPED_FIRST && a->kind <= TYPE_UNTYPED_LAST;} -force_inline B32 is_typed(Ast_Type *a){return !is_untyped(a);} -force_inline B32 is_numeric(Ast_Type *type){ - return (type->kind >= TYPE_UNTYPED_FIRST_NUMERIC && type->kind <= TYPE_UNTYPED_LAST_NUMERIC) || - (type->kind >= TYPE_FIRST_NUMERIC && type->kind <= TYPE_LAST_NUMERIC); +force_inline B32 is_any(Ast_Type *a) { return a == pctx->type_any; } +force_inline B32 is_struct(Ast_Type *a) { return a->kind == TYPE_STRUCT; } +force_inline B32 is_lambda(Ast_Type *a) { return a->kind == TYPE_LAMBDA; } +force_inline B32 is_array(Ast_Type *a) { return a->kind == TYPE_ARRAY; } +force_inline B32 is_slice(Ast_Type *a) { return a->kind == TYPE_SLICE; } +force_inline B32 is_tuple(Ast_Type *a) { return a->kind == TYPE_TUPLE; } +force_inline B32 is_enum(Ast_Type *a) { return a->kind == TYPE_ENUM; } +force_inline B32 is_pointer(Ast_Type *a) { return a->kind == TYPE_POINTER; } +force_inline B32 is_void(Ast_Type *a) { return a->kind == TYPE_VOID; } +force_inline B32 is_void_pointer(Ast_Type *a) { return a == pctx->type_pointer_to_void; } +force_inline B32 is_string(Ast_Type *a) { return a == pctx->type_string || a->kind == TYPE_UNTYPED_STRING || a == pctx->type_pointer_to_char; } +force_inline B32 is_untyped_int(Ast_Type *a) { return a->kind == TYPE_UNTYPED_INT; } +force_inline B32 is_typed_int(Ast_Type *a) { return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8); } +force_inline B32 is_int(Ast_Type *a) { return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8) || a->kind == TYPE_UNTYPED_INT; } +force_inline B32 is_signed_int(Ast_Type *a) { return !a->is_unsigned; } +force_inline B32 is_unsigned_int(Ast_Type *a) { return a->is_unsigned; } +force_inline B32 is_float(Ast_Type *a) { return a->kind == TYPE_F32 || a->kind == TYPE_F64 || a->kind == TYPE_UNTYPED_FLOAT; } +force_inline B32 is_f32(Ast_Type *a) { return a->kind == TYPE_F32; } +force_inline B32 is_f64(Ast_Type *a) { return a->kind == TYPE_F64; } +force_inline B32 is_bool(Ast_Type *a) { return a->kind == TYPE_BOOL || a->kind == TYPE_UNTYPED_BOOL; } +force_inline B32 is_untyped(Ast_Type *a) { return a->kind >= TYPE_UNTYPED_FIRST && a->kind <= TYPE_UNTYPED_LAST; } +force_inline B32 is_typed(Ast_Type *a) { return !is_untyped(a); } +force_inline B32 is_numeric(Ast_Type *type) { + return (type->kind >= TYPE_UNTYPED_FIRST_NUMERIC && type->kind <= TYPE_UNTYPED_LAST_NUMERIC) || + (type->kind >= TYPE_FIRST_NUMERIC && type->kind <= TYPE_LAST_NUMERIC); } //----------------------------------------------------------------------------- // Hash consed types //----------------------------------------------------------------------------- CORE_Static Ast_Type * -type_new(Allocator *allocator, Ast_Type_Kind kind, size_t size, size_t align){ - Ast_Type *result = allocate_struct(allocator, Ast_Type, true); - result->kind = kind; - result->size = size; - result->align = align; - result->type_id = pctx->type_ids++; - add(pctx->perm, &pctx->all_types, result); - return result; -} - -CORE_Static Ast_Type * -type_copy(Allocator *a, Ast_Type *type){ - // @warning: This changes type id !!!! - Ast_Type *result = allocate_struct(a, Ast_Type); - memory_copy(result, type, sizeof(Ast_Type)); - result->type_id = pctx->type_ids++; - add(pctx->perm, &pctx->all_types, result); - return result; -} - -CORE_Static Ast_Type * -type_pointer(Ast_Type *base){ - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, (void *)base); - if(!result){ - result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align); - result->base = base; - result->is_unsigned = true; - map_insert(&pctx->type_map, base, result); - } - assert(result->kind == TYPE_POINTER); - return result; -} - -CORE_Static Ast_Type * -type_slice(Ast_Type *base, Ast *ast){ - U64 hash_base = hash_ptr(base); - U64 hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - if(result){ - assert(result->kind == TYPE_SLICE); - assert(result->arr.base == base); +type_new(Allocator *allocator, Ast_Type_Kind kind, size_t size, size_t align) { + Ast_Type *result = allocate_struct(allocator, Ast_Type, true); + result->kind = kind; + result->size = size; + result->align = align; + result->type_id = pctx->type_ids++; + add(pctx->perm, &pctx->all_types, result); return result; - } - - struct Slice{void *p; S64 len;}; - result = type_new(pctx->perm, TYPE_SLICE, sizeof(Slice), alignof(Slice)); - result->arr.base = base; - result->arr.slice_hash = hash; - result->ast = ast; - map_insert(&pctx->type_map, hash, result); - return result; } CORE_Static Ast_Type * -type_try_tupling(Array types, Ast *ast){ - if(types.len == 0) return pctx->type_void; - if(types.len == 1) return types[0]; +type_copy(Allocator *a, Ast_Type *type) { + // @warning: This changes type id !!!! + Ast_Type *result = allocate_struct(a, Ast_Type); + memory_copy(result, type, sizeof(Ast_Type)); + result->type_id = pctx->type_ids++; + add(pctx->perm, &pctx->all_types, result); + return result; +} - U64 hash = 13; - For(types) hash = hash_mix(hash, hash_ptr(it)); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - if(result){ - assert(result->kind == TYPE_TUPLE); - assert(result->agg.members.len == types.len); +CORE_Static Ast_Type * +type_pointer(Ast_Type *base) { + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, (void *)base); + if (!result) { + result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align); + result->base = base; + result->is_unsigned = true; + map_insert(&pctx->type_map, base, result); + } + assert(result->kind == TYPE_POINTER); + return result; +} + +CORE_Static Ast_Type * +type_slice(Ast_Type *base, Ast *ast) { + U64 hash_base = hash_ptr(base); + U64 hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); + if (result) { + assert(result->kind == TYPE_SLICE); + assert(result->arr.base == base); + return result; + } + + struct Slice { + void *p; + S64 len; + }; + result = type_new(pctx->perm, TYPE_SLICE, sizeof(Slice), alignof(Slice)); + result->arr.base = base; + result->arr.slice_hash = hash; + result->ast = ast; + map_insert(&pctx->type_map, hash, result); + return result; +} + +CORE_Static Ast_Type * +type_try_tupling(Array types, Ast *ast) { + if (types.len == 0) return pctx->type_void; + if (types.len == 1) return types[0]; + + U64 hash = 13; + For(types) hash = hash_mix(hash, hash_ptr(it)); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); + if (result) { + assert(result->kind == TYPE_TUPLE); + assert(result->agg.members.len == types.len); + assert(result->agg.members.len > 1); + return result; + } + + // @todo alignment, offsets + result = type_new(pctx->perm, TYPE_TUPLE, 0, pointer_align); + result->agg.members = array_make(pctx->perm, types.len); + For(types) { + Ast_Resolved_Member m = {}; + m.type = it; + m.offset = 0; // @todo + result->size += it->size; + result->agg.members.add(m); + } + map_insert(&pctx->type_map, hash, result); assert(result->agg.members.len > 1); + return result; - } - - // @todo alignment, offsets - result = type_new(pctx->perm, TYPE_TUPLE, 0, pointer_align); - result->agg.members = array_make(pctx->perm, types.len); - For(types){ - Ast_Resolved_Member m = {}; - m.type = it; - m.offset = 0; // @todo - result->size += it->size; - result->agg.members.add(m); - } - map_insert(&pctx->type_map, hash, result); - assert(result->agg.members.len > 1); - - return result; } CORE_Static Ast_Type * -type_array(Ast_Type *base, S64 size){ - U64 hash_base = hash_ptr(base); - U64 hash = hash_mix(hash_base, hash_u64(size)); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - if(result){ - assert(result->kind == TYPE_ARRAY); - assert(result->arr.size == size); - assert(result->arr.base == base); - return result; - } +type_array(Ast_Type *base, S64 size) { + U64 hash_base = hash_ptr(base); + U64 hash = hash_mix(hash_base, hash_u64(size)); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); + if (result) { + assert(result->kind == TYPE_ARRAY); + assert(result->arr.size == size); + assert(result->arr.base == base); + return result; + } - result = type_new(pctx->perm, TYPE_ARRAY, size*base->size, pointer_align); - result->arr.base = base; - result->arr.size = size; - result->arr.slice_hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); - map_insert(&pctx->type_map, hash, result); - return result; + result = type_new(pctx->perm, TYPE_ARRAY, size * base->size, pointer_align); + result->arr.base = base; + result->arr.size = size; + result->arr.slice_hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); + map_insert(&pctx->type_map, hash, result); + return result; } inline U64 -calculate_hash_for_arguments(Ast_Type *a, Ast_Type *b){ - U64 result = 13; - result = hash_mix(result, hash_ptr(a)); - result = hash_mix(result, hash_ptr(b)); - return result; +calculate_hash_for_arguments(Ast_Type *a, Ast_Type *b) { + U64 result = 13; + result = hash_mix(result, hash_ptr(a)); + result = hash_mix(result, hash_ptr(b)); + return result; } inline U64 -calculate_hash_for_arguments(Ast_Type *a){ - U64 result = 13; - result = hash_mix(result, hash_ptr(a)); - return result; -} - -CORE_Static Ast_Type * -type_lambda(Ast *ast, Array return_vals, Array args){ - Ast_Type *ret = type_try_tupling(return_vals, ast); - U64 hash_without_ret = 13; - For(args) hash_without_ret = hash_mix(hash_without_ret, hash_ptr(it)); - U64 hash = hash_mix(hash_ptr(ret), hash_without_ret); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - - if(result){ - assert(result->kind == TYPE_LAMBDA); - assert(result->func.args.len == args.len); +calculate_hash_for_arguments(Ast_Type *a) { + U64 result = 13; + result = hash_mix(result, hash_ptr(a)); return result; - } - - result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align); - result->ast = ast; - result->func.ret = ret; - result->func.args = args.tight_copy(pctx->perm); - result->func.hash_without_ret = hash_without_ret; - map_insert(&pctx->type_map, hash, result); - - return result; } CORE_Static Ast_Type * -type_enum(Ast_Decl *ast, Ast_Type *type){ - if(!type){ - type = pctx->type_s64; - } +type_lambda(Ast *ast, Array return_vals, Array args) { + Ast_Type *ret = type_try_tupling(return_vals, ast); + U64 hash_without_ret = 13; + For(args) hash_without_ret = hash_mix(hash_without_ret, hash_ptr(it)); + U64 hash = hash_mix(hash_ptr(ret), hash_without_ret); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - Ast_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align); - result->base = type; - result->ast = ast; - return result; + if (result) { + assert(result->kind == TYPE_LAMBDA); + assert(result->func.args.len == args.len); + return result; + } + + result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align); + result->ast = ast; + result->func.ret = ret; + result->func.args = args.tight_copy(pctx->perm); + result->func.hash_without_ret = hash_without_ret; + map_insert(&pctx->type_map, hash, result); + + return result; } CORE_Static Ast_Type * -type_incomplete(Ast *ast){ - Ast_Type *result = type_new(pctx->perm, TYPE_INCOMPLETE, 0, 0); - result->ast = ast; - return result; +type_enum(Ast_Decl *ast, Ast_Type *type) { + if (!type) { + type = pctx->type_s64; + } + + Ast_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align); + result->base = type; + result->ast = ast; + return result; +} + +CORE_Static Ast_Type * +type_incomplete(Ast *ast) { + Ast_Type *result = type_new(pctx->perm, TYPE_INCOMPLETE, 0, 0); + result->ast = ast; + return result; } CORE_Static void type_complete(Ast_Type *type); CORE_Static void -type_struct_complete(Ast_Type *type, Ast_Decl *node){ - assert(node->kind == AST_STRUCT); - // @todo: compute size, alignement, offset !!! - // @note: resolve all the struct members first - Arena *scratch = pctx->scratch; - Scratch_Scope __scope(scratch); +type_struct_complete(Ast_Type *type, Ast_Decl *node) { + assert(node->kind == AST_STRUCT); + // @todo: compute size, alignement, offset !!! + // @note: resolve all the struct members first + Arena *scratch = pctx->scratch; + Scratch_Scope __scope(scratch); - Array members = {scratch}; - type->kind = TYPE_COMPLETING; - size_t members_size = 0; - For_Named(node->scope->decls, decl){ - resolve_decl(decl); - assert(decl->type->kind != TYPE_INCOMPLETE); - assert(is_pow2(decl->type->align)); + Array members = {scratch}; + type->kind = TYPE_COMPLETING; + size_t members_size = 0; + For_Named(node->scope->decls, decl) { + resolve_decl(decl); + assert(decl->type->kind != TYPE_INCOMPLETE); + assert(is_pow2(decl->type->align)); - Ast_Resolved_Member m = {}; - m.offset = type->size; - members_size += decl->type->size; - type->align = max(type->align, decl->type->align); - type->size = decl->type->size + align_up(type->size, decl->type->align); + Ast_Resolved_Member m = {}; + m.offset = type->size; + members_size += decl->type->size; + type->align = max(type->align, decl->type->align); + type->size = decl->type->size + align_up(type->size, decl->type->align); - m.name = decl->name; - m.value = decl->value; - members.add(m); - } - type->size = align_up(type->size, type->align); - type->padding = type->size - members_size; - type->agg.members = members.tight_copy(pctx->perm); - type->kind = TYPE_STRUCT; - node->unique_name = pctx->intern(string_fmt(scratch, "%Q%Q", pctx->symbol_prefix, node->name)); + m.name = decl->name; + m.value = decl->value; + members.add(m); + } + type->size = align_up(type->size, type->align); + type->padding = type->size - members_size; + type->agg.members = members.tight_copy(pctx->perm); + type->kind = TYPE_STRUCT; + node->unique_name = pctx->intern(string_fmt(scratch, "%Q%Q", pctx->symbol_prefix, node->name)); } CORE_Static void -type_complete(Ast_Type *type){ - if(!type) { - return; - } - if(type->kind == TYPE_COMPLETING){ - compiler_error(type->ast->pos, "Cyclic type dependency"); - } - else if(type->kind != TYPE_INCOMPLETE){ - return; - } +type_complete(Ast_Type *type) { + if (!type) { + return; + } + if (type->kind == TYPE_COMPLETING) { + compiler_error(type->ast->pos, "Cyclic type dependency"); + } + else if (type->kind != TYPE_INCOMPLETE) { + return; + } - type_struct_complete(type, (Ast_Decl *)type->ast); - add(pctx->perm, &pctx->ordered_decls, (Ast_Decl *)type->ast); + type_struct_complete(type, (Ast_Decl *)type->ast); + add(pctx->perm, &pctx->ordered_decls, (Ast_Decl *)type->ast); } CORE_Static void -typename_base(String_Builder *sb, Ast_Type *type){ - switch(type->kind){ - case TYPE_INCOMPLETE: sb->addf("INCOMPLETE"); break; - case TYPE_COMPLETING: sb->addf("COMPLETING"); break; - case TYPE_TYPE: sb->addf("TYPE"); break; - case TYPE_POINTER: - sb->addf("*"); - typename_base(sb, type->base); - break; - case TYPE_LAMBDA: - sb->addf("("); - For(type->func.args) { - typename_base(sb, it); - if(!type->func.args.is_last(&it)) sb->addf(", "); - } +typename_base(String_Builder *sb, Ast_Type *type) { + switch (type->kind) { + case TYPE_INCOMPLETE: sb->addf("INCOMPLETE"); break; + case TYPE_COMPLETING: sb->addf("COMPLETING"); break; + case TYPE_TYPE: sb->addf("TYPE"); break; + case TYPE_POINTER: + sb->addf("*"); + typename_base(sb, type->base); + break; + case TYPE_LAMBDA: + sb->addf("("); + For(type->func.args) { + typename_base(sb, it); + if (!type->func.args.is_last(&it)) sb->addf(", "); + } - sb->addf("):"); - typename_base(sb, type->func.ret); - break; - case TYPE_ARRAY: - sb->addf("[%d]", (int)type->arr.size); - typename_base(sb, type->arr.base); - break; - case TYPE_SLICE: - sb->addf("[]"); - typename_base(sb, type->base); - break; - case TYPE_STRUCT: - case TYPE_ENUM:{ - // @todo direct access - auto constant = (Ast_Decl *)type->ast; - auto name = constant->name; - sb->addf("%Q", name); - break; + sb->addf("):"); + typename_base(sb, type->func.ret); + break; + case TYPE_ARRAY: + sb->addf("[%d]", (int)type->arr.size); + typename_base(sb, type->arr.base); + break; + case TYPE_SLICE: + sb->addf("[]"); + typename_base(sb, type->base); + break; + case TYPE_STRUCT: + case TYPE_ENUM: { + // @todo direct access + auto constant = (Ast_Decl *)type->ast; + auto name = constant->name; + sb->addf("%Q", name); + break; + } + case TYPE_UNTYPED_BOOL: sb->addf("Untyped_Bool"); break; + case TYPE_UNTYPED_INT: sb->addf("Untyped_Int"); break; + case TYPE_UNTYPED_FLOAT: sb->addf("Untyped_Float"); break; + case TYPE_UNTYPED_STRING: sb->addf("Untyped_String"); break; + default: { + sb->addf("%s", get_name_of_type(type)); + } } - case TYPE_UNTYPED_BOOL: sb->addf("Untyped_Bool"); break; - case TYPE_UNTYPED_INT: sb->addf("Untyped_Int"); break; - case TYPE_UNTYPED_FLOAT: sb->addf("Untyped_Float"); break; - case TYPE_UNTYPED_STRING: sb->addf("Untyped_String"); break; - default: { - sb->addf("%s", get_name_of_type(type)); - } - } } CORE_Static String -get_typename(Allocator *a, Ast_Type *type){ - pctx->helper_builder.addf("["); - typename_base(&pctx->helper_builder, type); - pctx->helper_builder.addf("]"); - String result = string_flatten(a, &pctx->helper_builder); - pctx->helper_builder.reset(); - return result; +get_typename(Allocator *a, Ast_Type *type) { + pctx->helper_builder.addf("["); + typename_base(&pctx->helper_builder, type); + pctx->helper_builder.addf("]"); + String result = string_flatten(a, &pctx->helper_builder); + pctx->helper_builder.reset(); + return result; } CORE_Static String -typestring(Ast_Type *type){ - return get_typename(pctx->stage_arena, type); +typestring(Ast_Type *type) { + return get_typename(pctx->stage_arena, type); } diff --git a/core_types.h b/core_types.h index 4f561fa..60ba992 100644 --- a/core_types.h +++ b/core_types.h @@ -2,12 +2,36 @@ // Resolved Types //----------------------------------------------------------------------------- - -#define CASE_SINT case TYPE_S8:case TYPE_S16:case TYPE_S32:case TYPE_S64: case TYPE_CHAR: case TYPE_INT -#define CASE_UINT case TYPE_U8:case TYPE_U16:case TYPE_U32:case TYPE_U64 -#define CASE_INT case TYPE_UNTYPED_INT: CASE_SINT: CASE_UINT -#define CASE_BOOL case TYPE_UNTYPED_BOOL: case TYPE_BOOL -#define CASE_FLOAT case TYPE_UNTYPED_FLOAT: case TYPE_F32: case TYPE_F64 -#define CASE_STRING case TYPE_UNTYPED_STRING: case TYPE_STRUCT: case TYPE_POINTER -#define CASE_UNTYPED case TYPE_UNTYPED_INT: case TYPE_UNTYPED_BOOL: case TYPE_UNTYPED_FLOAT: case TYPE_UNTYPED_STRING +#define CASE_SINT \ + case TYPE_S8: \ + case TYPE_S16: \ + case TYPE_S32: \ + case TYPE_S64: \ + case TYPE_CHAR: \ + case TYPE_INT +#define CASE_UINT \ + case TYPE_U8: \ + case TYPE_U16: \ + case TYPE_U32: \ + case TYPE_U64 +#define CASE_INT \ + case TYPE_UNTYPED_INT: \ + CASE_SINT: \ + CASE_UINT +#define CASE_BOOL \ + case TYPE_UNTYPED_BOOL: \ + case TYPE_BOOL +#define CASE_FLOAT \ + case TYPE_UNTYPED_FLOAT: \ + case TYPE_F32: \ + case TYPE_F64 +#define CASE_STRING \ + case TYPE_UNTYPED_STRING: \ + case TYPE_STRUCT: \ + case TYPE_POINTER +#define CASE_UNTYPED \ + case TYPE_UNTYPED_INT: \ + case TYPE_UNTYPED_BOOL: \ + case TYPE_UNTYPED_FLOAT: \ + case TYPE_UNTYPED_STRING #define ARRAY_SIZE_SLICE (-1) diff --git a/os.h b/os.h index adaa426..42c288e 100644 --- a/os.h +++ b/os.h @@ -1,9 +1,9 @@ -const U32 LIST_NO_FLAGS = 0; -const U32 LIST_RECURSE_INTO_DIRS = 1; +const U32 LIST_NO_FLAGS = 0; +const U32 LIST_RECURSE_INTO_DIRS = 1; -struct OS_File_Info{ - String relative_path; - String absolute_path; - B32 is_directory; +struct OS_File_Info { + String relative_path; + String absolute_path; + B32 is_directory; }; diff --git a/os_linux.cpp b/os_linux.cpp index c4b8ed2..44e1017 100644 --- a/os_linux.cpp +++ b/os_linux.cpp @@ -11,184 +11,184 @@ #define POSIX_PAGE_SIZE 4096 CORE_Static B32 -os_write_file(String filename, String filecontent){ - FILE *f = fopen((const char *)filename.str, "w"); - if(f){ - fwrite(filecontent.str, 1, filecontent.len, f); - fclose(f); - return true; - } - return false; +os_write_file(String filename, String filecontent) { + FILE *f = fopen((const char *)filename.str, "w"); + if (f) { + fwrite(filecontent.str, 1, filecontent.len, f); + fclose(f); + return true; + } + return false; } CORE_Static String -os_read_file(Alloator *a, String name){ - String result = {0}; - FILE *f = fopen((char *)name.str, "rb"); - if(f){ - fseek(f, 0, SEEK_END); - result.len = ftell(f); - fseek(f, 0, SEEK_SET); - result.str = (U8 *)allocate_size(a, result.len + 1, false); - fread(result.str, result.len, 1, f); - fclose(f); - result.str[result.len] = 0; - } +os_read_file(Alloator *a, String name) { + String result = {0}; + FILE *f = fopen((char *)name.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + result.str = (U8 *)allocate_size(a, result.len + 1, false); + fread(result.str, result.len, 1, f); + fclose(f); + result.str[result.len] = 0; + } - return result; + return result; } CORE_Static String -os_get_exe_dir(Allocator *a){ - char buffer[PATH_MAX] = {}; - if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { - log_info("Failed to retrieve the path of the executable, the method used is fetching /proc/self/exe, very likely you are using an OS that doesn't follow this convention and as such it is currently not supported"); - } - String exe = string_from_cstring(buffer); - exe = string_chop_last_slash(exe); - String result = string_copy(a, exe); +os_get_exe_dir(Allocator *a) { + char buffer[PATH_MAX] = {}; + if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { + log_info("Failed to retrieve the path of the executable, the method used is fetching /proc/self/exe, very likely you are using an OS that doesn't follow this convention and as such it is currently not supported"); + } + String exe = string_from_cstring(buffer); + exe = string_chop_last_slash(exe); + String result = string_copy(a, exe); - log_trace("Retrieved executable path %Q", result); - return result; + log_trace("Retrieved executable path %Q", result); + return result; } CORE_Static String -os_get_absolute_path(Allocator *a, String path){ - assert(path.str[path.len] == 0); - char buffer[PATH_MAX] = {}; - realpath((char *)path.str, buffer); - String abs = string_from_cstring(buffer); - String result = string_copy(a, abs); - return result; +os_get_absolute_path(Allocator *a, String path) { + assert(path.str[path.len] == 0); + char buffer[PATH_MAX] = {}; + realpath((char *)path.str, buffer); + String abs = string_from_cstring(buffer); + String result = string_copy(a, abs); + return result; } CORE_Static B32 -os_does_file_exist(String path){ - B32 result = false; - assert(path.str[path.len] == 0); - if (access((char *)path.str, F_OK) == 0) { - result = true; - } - log_trace("Does file exist? %Q %d", path, result); - return result; +os_does_file_exist(String path) { + B32 result = false; + assert(path.str[path.len] == 0); + if (access((char *)path.str, F_OK) == 0) { + result = true; + } + log_trace("Does file exist? %Q %d", path, result); + return result; } CORE_Static String -os_get_working_dir(Allocator *allocator){ - char *buffer = allocate_array(allocator, char, PATH_MAX, false); - char *result = getcwd(buffer, PATH_MAX); - return string_from_cstring(result); +os_get_working_dir(Allocator *allocator) { + char *buffer = allocate_array(allocator, char, PATH_MAX, false); + char *result = getcwd(buffer, PATH_MAX); + return string_from_cstring(result); } CORE_Static Array -os_list_dir(Scratch_Arena *scratch, Allocator *a, String dir, U32 flags = LIST_RECURSE_INTO_DIRS){ - Scratch_Scope _scope(scratch); - Array dirs_to_read = {scratch}; - dirs_to_read.add(dir); +os_list_dir(Scratch_Arena *scratch, Allocator *a, String dir, U32 flags = LIST_RECURSE_INTO_DIRS) { + Scratch_Scope _scope(scratch); + Array dirs_to_read = {scratch}; + dirs_to_read.add(dir); - Array result = {a}; - for(auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++){ - assert(it->str[it->len] == 0); - dirent *dir; - DIR *d = opendir((char *)it->str); - if(d){ - while ((dir = readdir(d)) != NULL) { - if(dir->d_name[0] == '.'){ - if(dir->d_name[1] == '.'){ - if(dir->d_name[2] == 0) - continue; - } + Array result = {a}; + for (auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++) { + assert(it->str[it->len] == 0); + dirent *dir; + DIR *d = opendir((char *)it->str); + if (d) { + while ((dir = readdir(d)) != NULL) { + if (dir->d_name[0] == '.') { + if (dir->d_name[1] == '.') { + if (dir->d_name[2] == 0) + continue; + } - if(dir->d_name[1] == 0) - continue; + if (dir->d_name[1] == 0) + continue; + } + + OS_File_Info entry = {}; + entry.relative_path = string_fmt(a, "%Q/%s", *it, dir->d_name); + entry.absolute_path = os_get_absolute_path(a, entry.relative_path); + + if (dir->d_type == DT_DIR) { + entry.is_directory = true; + + if (flags & LIST_RECURSE_INTO_DIRS) { + dirs_to_read.add(entry.absolute_path); + } + } + + result.add(entry); + log_trace("%Q %d", entry.absolute_path, entry.is_directory); + } + closedir(d); } - - OS_File_Info entry = {}; - entry.relative_path = string_fmt(a, "%Q/%s", *it, dir->d_name); - entry.absolute_path = os_get_absolute_path(a, entry.relative_path); - - if(dir->d_type == DT_DIR){ - entry.is_directory = true; - - if(flags & LIST_RECURSE_INTO_DIRS){ - dirs_to_read.add(entry.absolute_path); - } + else { + log_info("ERROR Failed to read dir: %Q, errno: %s", *it, strerror(errno)); } - - result.add(entry); - log_trace("%Q %d", entry.absolute_path, entry.is_directory); - } - closedir(d); } - else{ - log_info("ERROR Failed to read dir: %Q, errno: %s", *it, strerror(errno)); - } - } - return result; + return result; } CORE_Static U8 * os_advance_commit(OS_Memory *m, size_t *commit_size, size_t page_size) { - size_t aligned_up_commit = align_up(*commit_size, page_size); - size_t to_be_total_commited_size = aligned_up_commit + m->commit; - size_t to_be_total_commited_size_clamped_to_reserve = clamp_top(to_be_total_commited_size, m->reserve); - size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit; - assert_message(adjusted_to_boundary_commit, "Debug WIN32 Error: Reached the virtual memory reserved boundary"); - U8 *result = m->data + m->commit; - if (adjusted_to_boundary_commit == 0) - result = 0; - *commit_size = adjusted_to_boundary_commit; - return result; + size_t aligned_up_commit = align_up(*commit_size, page_size); + size_t to_be_total_commited_size = aligned_up_commit + m->commit; + size_t to_be_total_commited_size_clamped_to_reserve = clamp_top(to_be_total_commited_size, m->reserve); + size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit; + assert_message(adjusted_to_boundary_commit, "Debug WIN32 Error: Reached the virtual memory reserved boundary"); + U8 *result = m->data + m->commit; + if (adjusted_to_boundary_commit == 0) + result = 0; + *commit_size = adjusted_to_boundary_commit; + return result; } CORE_Static OS_Memory os_reserve(size_t size) { - OS_Memory result = {}; - size_t size_aligned = align_up(size, POSIX_PAGE_SIZE); - result.data = (U8 *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - assert_message(result.data, POSIX_ERROR "Failed to reserve memory using mmap!!"); - if (result.data) { - result.reserve = size_aligned; - } - return result; + OS_Memory result = {}; + size_t size_aligned = align_up(size, POSIX_PAGE_SIZE); + result.data = (U8 *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert_message(result.data, POSIX_ERROR "Failed to reserve memory using mmap!!"); + if (result.data) { + result.reserve = size_aligned; + } + return result; } CORE_Static B32 os_commit(OS_Memory *m, size_t commit) { - B32 result = false; - U8 * pointer = os_advance_commit(m, &commit, POSIX_PAGE_SIZE); - if (pointer) { - int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE); - assert_message(mprotect_result == 0, "OS1 POSIX Debug Error: Failed to commit more memory using mmap"); - if (mprotect_result == 0) { - m->commit += commit; - result = true; + B32 result = false; + U8 *pointer = os_advance_commit(m, &commit, POSIX_PAGE_SIZE); + if (pointer) { + int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE); + assert_message(mprotect_result == 0, "OS1 POSIX Debug Error: Failed to commit more memory using mmap"); + if (mprotect_result == 0) { + m->commit += commit; + result = true; + } } - } - return result; + return result; } CORE_Static void os_release(OS_Memory *m) { - int result = munmap(m->data, m->reserve); - assert_message(result == 0, "OS1 POSIX Debug Error: Failed to release virtual memory using munmap"); - if (result == 0) { - memory_zero(m, sizeof(*m)); - } + int result = munmap(m->data, m->reserve); + assert_message(result == 0, "OS1 POSIX Debug Error: Failed to release virtual memory using munmap"); + if (result == 0) { + memory_zero(m, sizeof(*m)); + } } CORE_Static U64 os_get_microseconds() { - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - U64 result = (((U64)ts.tv_sec) * 1000000ull) + ((U64)ts.tv_nsec) / 1000ull; - return result; + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + U64 result = (((U64)ts.tv_sec) * 1000000ull) + ((U64)ts.tv_nsec) / 1000ull; + return result; } CORE_Static F64 -os_time(){ - F64 time = (F64)os_get_microseconds(); - F64 result = time / 1000000.0; // Microseconds to seconds - return result; +os_time() { + F64 time = (F64)os_get_microseconds(); + F64 result = time / 1000000.0; // Microseconds to seconds + return result; } diff --git a/os_windows.cpp b/os_windows.cpp index ca17757..efccf63 100644 --- a/os_windows.cpp +++ b/os_windows.cpp @@ -6,82 +6,82 @@ const size_t os_page_size = 4096; CORE_Static OS_Memory -os_reserve(size_t size){ - OS_Memory result = {}; - size_t adjusted_size = align_up(size, os_page_size); - result.data = (U8*)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE); - assert_message(result.data, "Failed to reserve virtual memory"); - result.reserve = adjusted_size; - return result; +os_reserve(size_t size) { + OS_Memory result = {}; + size_t adjusted_size = align_up(size, os_page_size); + result.data = (U8 *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE); + assert_message(result.data, "Failed to reserve virtual memory"); + result.reserve = adjusted_size; + return result; } CORE_Static B32 -os_commit(OS_Memory *m, size_t size){ - size_t commit = align_up(size, os_page_size); - size_t total_commit = m->commit + commit; - total_commit = clamp_top(total_commit, m->reserve); - size_t adjusted_commit = total_commit - m->commit; - if(adjusted_commit != 0){ - void *result = VirtualAlloc((U8*)m->data + m->commit, adjusted_commit, MEM_COMMIT, PAGE_READWRITE); - assert_message(result, "Failed to commit more memory"); - m->commit += adjusted_commit; - return true; - } - return false; -} - -CORE_Static void -os_release(OS_Memory *m){ - BOOL result = VirtualFree(m->data, 0, MEM_RELEASE); - assert_message(result != 0, "Failed to release OS_Memory"); - if(result){ - m->data = 0; - m->commit = 0; - m->reserve = 0; - } -} - -CORE_Static B32 -os_decommit_pos(OS_Memory *m, size_t pos){ - size_t aligned = align_down(pos, os_page_size); - size_t adjusted_pos = clamp_top(aligned, m->commit); - size_t size_to_decommit = m->commit - adjusted_pos; - if(size_to_decommit){ - U8 *base_address = m->data + adjusted_pos; - BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT); - if(result){ - m->commit -= size_to_decommit; - return true; +os_commit(OS_Memory *m, size_t size) { + size_t commit = align_up(size, os_page_size); + size_t total_commit = m->commit + commit; + total_commit = clamp_top(total_commit, m->reserve); + size_t adjusted_commit = total_commit - m->commit; + if (adjusted_commit != 0) { + void *result = VirtualAlloc((U8 *)m->data + m->commit, adjusted_commit, MEM_COMMIT, PAGE_READWRITE); + assert_message(result, "Failed to commit more memory"); + m->commit += adjusted_commit; + return true; } - } - return false; + return false; } CORE_Static void -test_os_memory(){ - assert(align_down(4096, 4096) == 4096); - assert(align_down(4095, 4096) == 0); +os_release(OS_Memory *m) { + BOOL result = VirtualFree(m->data, 0, MEM_RELEASE); + assert_message(result != 0, "Failed to release OS_Memory"); + if (result) { + m->data = 0; + m->commit = 0; + m->reserve = 0; + } +} - OS_Memory memory = os_reserve(9000); - assert(memory.reserve == 4096*3 && memory.data && memory.commit == 0); - os_commit(&memory, 100); - assert(memory.commit == 4096); - os_commit(&memory, 100); - assert(memory.commit == 4096*2); - os_commit(&memory, 9000); - assert(memory.commit == 4096*3); - os_commit(&memory, 9000); - assert(memory.commit == 4096*3); +CORE_Static B32 +os_decommit_pos(OS_Memory *m, size_t pos) { + size_t aligned = align_down(pos, os_page_size); + size_t adjusted_pos = clamp_top(aligned, m->commit); + size_t size_to_decommit = m->commit - adjusted_pos; + if (size_to_decommit) { + U8 *base_address = m->data + adjusted_pos; + BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT); + if (result) { + m->commit -= size_to_decommit; + return true; + } + } + return false; +} - os_decommit_pos(&memory, 4096); - assert(memory.commit == 4096); - os_decommit_pos(&memory, 4096); - assert(memory.commit == 4096); - os_decommit_pos(&memory, 0); - assert(memory.commit == 0); +CORE_Static void +test_os_memory() { + assert(align_down(4096, 4096) == 4096); + assert(align_down(4095, 4096) == 0); - os_release(&memory); - assert(memory.data == 0); + OS_Memory memory = os_reserve(9000); + assert(memory.reserve == 4096 * 3 && memory.data && memory.commit == 0); + os_commit(&memory, 100); + assert(memory.commit == 4096); + os_commit(&memory, 100); + assert(memory.commit == 4096 * 2); + os_commit(&memory, 9000); + assert(memory.commit == 4096 * 3); + os_commit(&memory, 9000); + assert(memory.commit == 4096 * 3); + + os_decommit_pos(&memory, 4096); + assert(memory.commit == 4096); + os_decommit_pos(&memory, 4096); + assert(memory.commit == 4096); + os_decommit_pos(&memory, 0); + assert(memory.commit == 0); + + os_release(&memory); + assert(memory.data == 0); } //----------------------------------------------------------------------------- @@ -89,156 +89,155 @@ test_os_memory(){ //----------------------------------------------------------------------------- global S64 Global_counts_per_second; api F64 os_time() { - if (Global_counts_per_second == 0) { - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - Global_counts_per_second = freq.QuadPart; - } + if (Global_counts_per_second == 0) { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + Global_counts_per_second = freq.QuadPart; + } - LARGE_INTEGER time; - QueryPerformanceCounter(&time); - F64 result = (F64)time.QuadPart / (F64)Global_counts_per_second; - return result; + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + F64 result = (F64)time.QuadPart / (F64)Global_counts_per_second; + return result; } //----------------------------------------------------------------------------- // Filesystem //----------------------------------------------------------------------------- CORE_Static B32 -os_write_file(String filename, String filecontent){ - FILE *f = fopen((const char *)filename.str, "w"); - if(f){ - fwrite(filecontent.str, 1, filecontent.len, f); - fclose(f); - return true; - } - return false; +os_write_file(String filename, String filecontent) { + FILE *f = fopen((const char *)filename.str, "w"); + if (f) { + fwrite(filecontent.str, 1, filecontent.len, f); + fclose(f); + return true; + } + return false; } CORE_Static String -os_read_file(Allocator *a, String name){ - String result = {0}; - FILE *f = fopen((char *)name.str, "rb"); - if(f){ - fseek(f, 0, SEEK_END); - result.len = ftell(f); - fseek(f, 0, SEEK_SET); - result.str = (U8 *)allocate_size(a, result.len + 1, false); - fread(result.str, result.len, 1, f); - fclose(f); - result.str[result.len] = 0; - } +os_read_file(Allocator *a, String name) { + String result = {0}; + FILE *f = fopen((char *)name.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + result.str = (U8 *)allocate_size(a, result.len + 1, false); + fread(result.str, result.len, 1, f); + fclose(f); + result.str[result.len] = 0; + } - return result; + return result; } CORE_Static String -os_get_working_dir(Allocator *a){ - wchar_t buffer[2048]; - DWORD written = GetCurrentDirectoryW(2048, buffer); - assert(written != 0); - String16 string16 = string16_from_widechar(buffer); - String result = string16_to_string8(a, string16); - string_path_normalize(result); - return result; +os_get_working_dir(Allocator *a) { + wchar_t buffer[2048]; + DWORD written = GetCurrentDirectoryW(2048, buffer); + assert(written != 0); + String16 string16 = string16_from_widechar(buffer); + String result = string16_to_string8(a, string16); + string_path_normalize(result); + return result; } CORE_Static String -os_get_exe_dir(Allocator *a){ - wchar_t buffer[2048]; - DWORD written = GetModuleFileNameW(0, buffer, 2048); - assert(written != 0); - String16 string16 = string16_from_widechar(buffer); - String result = string16_to_string8(a, string16); - string_path_normalize(result); - result = string_chop_last_slash(result); +os_get_exe_dir(Allocator *a) { + wchar_t buffer[2048]; + DWORD written = GetModuleFileNameW(0, buffer, 2048); + assert(written != 0); + String16 string16 = string16_from_widechar(buffer); + String result = string16_to_string8(a, string16); + string_path_normalize(result); + result = string_chop_last_slash(result); - if(string16.len > result.len) result.str[result.len] = 0; - string_path_normalize(result); - return result; + if (string16.len > result.len) result.str[result.len] = 0; + string_path_normalize(result); + return result; } CORE_Static String -os_get_absolute_path(Allocator *a, String path){ - char buff[2048]; - Arena scratch = arena_from_buffer(buff, 2048); - String16 path16 = string8_to_string16(&scratch, path); +os_get_absolute_path(Allocator *a, String path) { + char buff[2048]; + Arena scratch = arena_from_buffer(buff, 2048); + String16 path16 = string8_to_string16(&scratch, path); - wchar_t *buffer = allocate_array(&scratch, wchar_t, 512); - DWORD written = GetFullPathNameW((wchar_t *)path16.str, 512, buffer, 0); - if(written == 0) return {}; + wchar_t *buffer = allocate_array(&scratch, wchar_t, 512); + DWORD written = GetFullPathNameW((wchar_t *)path16.str, 512, buffer, 0); + if (written == 0) return {}; - String16 absolute16 = string16_from_widechar(buffer); - String absolute = string16_to_string8(a, absolute16); - string_path_normalize(absolute); - return absolute; + String16 absolute16 = string16_from_widechar(buffer); + String absolute = string16_to_string8(a, absolute16); + string_path_normalize(absolute); + return absolute; } CORE_Static B32 -os_does_file_exist(String path){ - char buff[2048]; - Arena scratch = arena_from_buffer(buff, buff_cap(buff)); - String16 path16 = string8_to_string16(&scratch, path); - DWORD attribs = GetFileAttributesW((wchar_t *)path16.str); - B32 result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; - return result; +os_does_file_exist(String path) { + char buff[2048]; + Arena scratch = arena_from_buffer(buff, buff_cap(buff)); + String16 path16 = string8_to_string16(&scratch, path); + DWORD attribs = GetFileAttributesW((wchar_t *)path16.str); + B32 result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; + return result; } CORE_Static Array -os_list_dir(Arena *scratch, Allocator *a, String dir, U32 flags = LIST_NO_FLAGS){ - Scratch_Scope _scope(scratch); - Array dirs_to_read = {scratch}; - dirs_to_read.add(dir); +os_list_dir(Arena *scratch, Allocator *a, String dir, U32 flags = LIST_NO_FLAGS) { + Scratch_Scope _scope(scratch); + Array dirs_to_read = {scratch}; + dirs_to_read.add(dir); - Array result = {a}; - for(auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++){ - String modified_path = string_fmt(scratch, "%Q\\*", it); - String16 path16 = string8_to_string16(scratch, modified_path); + Array result = {a}; + for (auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++) { + String modified_path = string_fmt(scratch, "%Q\\*", it); + String16 path16 = string8_to_string16(scratch, modified_path); - WIN32_FIND_DATAW ffd; - HANDLE handle = FindFirstFileW((wchar_t *)path16.str, &ffd); - if(handle == INVALID_HANDLE_VALUE) continue; + WIN32_FIND_DATAW ffd; + HANDLE handle = FindFirstFileW((wchar_t *)path16.str, &ffd); + if (handle == INVALID_HANDLE_VALUE) continue; - do{ + do { - // - // Skip '.' and '..' - // - if(ffd.cFileName[0] == '.'){ - if(ffd.cFileName[1] == '.'){ - if(ffd.cFileName[2] == 0) - continue; + // + // Skip '.' and '..' + // + if (ffd.cFileName[0] == '.') { + if (ffd.cFileName[1] == '.') { + if (ffd.cFileName[2] == 0) + continue; + } + + if (ffd.cFileName[1] == 0) + continue; + } + + String16 filename16 = string16_from_widechar(ffd.cFileName); + String filename = string16_to_string8(scratch, filename16); + + String full_file_path = string_fmt(a, "%Q/%Q", dir, filename); + OS_File_Info listing = {full_file_path, os_get_absolute_path(a, full_file_path)}; + + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + listing.is_directory = true; + + if (flags & LIST_RECURSE_INTO_DIRS) { + dirs_to_read.add(full_file_path); + } + } + + result.add(listing); + + } while (FindNextFileW(handle, &ffd) != 0); + + DWORD error = GetLastError(); + if (error != ERROR_NO_MORE_FILES) { + // Not sure what to do here hmmm } - - if(ffd.cFileName[1] == 0) - continue; - } - - String16 filename16 = string16_from_widechar(ffd.cFileName); - String filename = string16_to_string8(scratch, filename16); - - String full_file_path = string_fmt(a, "%Q/%Q", dir, filename); - OS_File_Info listing = {full_file_path, os_get_absolute_path(a, full_file_path)}; - - - if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ - listing.is_directory = true; - - if(flags & LIST_RECURSE_INTO_DIRS){ - dirs_to_read.add(full_file_path); - } - } - - result.add(listing); - - }while(FindNextFileW(handle, &ffd) != 0); - - DWORD error = GetLastError(); - if(error != ERROR_NO_MORE_FILES){ - // Not sure what to do here hmmm + FindClose(handle); } - FindClose(handle); - } - return result; + return result; } diff --git a/stb_sprintf.h b/stb_sprintf.h index 63a8447..160c28a 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -27,7 +27,7 @@ // See end of file for license information. #ifndef STB_SPRINTF_H_INCLUDE -#define STB_SPRINTF_H_INCLUDE + #define STB_SPRINTF_H_INCLUDE /* Single file sprintf replacement. @@ -142,73 +142,73 @@ PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): "...512 char string..." ( 35.0x/32.5x faster!) */ -#if defined(__clang__) -#if defined(__has_feature) && defined(__has_attribute) -#if __has_feature(address_sanitizer) -#if __has_attribute(__no_sanitize__) -#define STBSP__ASAN __attribute__((__no_sanitize__("address"))) -#elif __has_attribute(__no_sanitize_address__) -#define STBSP__ASAN __attribute__((__no_sanitize_address__)) -#elif __has_attribute(__no_address_safety_analysis__) -#define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) -#endif -#endif -#endif -#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) -#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ -#define STBSP__ASAN __attribute__((__no_sanitize_address__)) -#endif -#endif + #if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) + #endif + #endif + #endif + #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif + #endif -#ifndef STBSP__ASAN -#define STBSP__ASAN -#endif + #ifndef STBSP__ASAN + #define STBSP__ASAN + #endif -#ifdef STB_SPRINTF_STATIC -#define STBSP__PUBLICDEC static -#define STBSP__PUBLICDEF static STBSP__ASAN -#else -#ifdef __cplusplus -#define STBSP__PUBLICDEC extern "C" -#define STBSP__PUBLICDEF extern "C" STBSP__ASAN -#else -#define STBSP__PUBLICDEC extern -#define STBSP__PUBLICDEF STBSP__ASAN -#endif -#endif + #ifdef STB_SPRINTF_STATIC + #define STBSP__PUBLICDEC static + #define STBSP__PUBLICDEF static STBSP__ASAN + #else + #ifdef __cplusplus + #define STBSP__PUBLICDEC extern "C" + #define STBSP__PUBLICDEF extern "C" STBSP__ASAN + #else + #define STBSP__PUBLICDEC extern + #define STBSP__PUBLICDEF STBSP__ASAN + #endif + #endif -#if defined(__has_attribute) -#if __has_attribute(format) -#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) -#endif -#endif + #if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) + #endif + #endif -#ifndef STBSP__ATTRIBUTE_FORMAT -#define STBSP__ATTRIBUTE_FORMAT(fmt,va) -#endif + #ifndef STBSP__ATTRIBUTE_FORMAT + #define STBSP__ATTRIBUTE_FORMAT(fmt, va) + #endif -#ifdef _MSC_VER -#define STBSP__NOTUSED(v) (void)(v) -#else -#define STBSP__NOTUSED(v) (void)sizeof(v) -#endif + #ifdef _MSC_VER + #define STBSP__NOTUSED(v) (void)(v) + #else + #define STBSP__NOTUSED(v) (void)sizeof(v) + #endif -#include // for va_arg(), va_list() -#include // size_t, ptrdiff_t + #include // for va_arg(), va_list() + #include // size_t, ptrdiff_t -#ifndef STB_SPRINTF_MIN -#define STB_SPRINTF_MIN 512 // how many characters per callback -#endif + #ifndef STB_SPRINTF_MIN + #define STB_SPRINTF_MIN 512 // how many characters per callback + #endif typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); -#ifndef STB_SPRINTF_DECORATE -#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names -#endif + #ifndef STB_SPRINTF_DECORATE + #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names + #endif STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2, 3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3, 4); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); @@ -217,1664 +217,1668 @@ STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri #ifdef STB_SPRINTF_IMPLEMENTATION -#define stbsp__uint32 unsigned int -#define stbsp__int32 signed int + #define stbsp__uint32 unsigned int + #define stbsp__int32 signed int -#ifdef _MSC_VER -#define stbsp__uint64 unsigned __int64 -#define stbsp__int64 signed __int64 -#else -#define stbsp__uint64 unsigned long long -#define stbsp__int64 signed long long -#endif -#define stbsp__uint16 unsigned short + #ifdef _MSC_VER + #define stbsp__uint64 unsigned __int64 + #define stbsp__int64 signed __int64 + #else + #define stbsp__uint64 unsigned long long + #define stbsp__int64 signed long long + #endif + #define stbsp__uint16 unsigned short -#ifndef stbsp__uintptr -#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) -#define stbsp__uintptr stbsp__uint64 -#else -#define stbsp__uintptr stbsp__uint32 -#endif -#endif + #ifndef stbsp__uintptr + #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) + #define stbsp__uintptr stbsp__uint64 + #else + #define stbsp__uintptr stbsp__uint32 + #endif + #endif -#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#define STB_SPRINTF_MSVC_MODE -#endif -#endif + #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) + #if defined(_MSC_VER) && (_MSC_VER < 1900) + #define STB_SPRINTF_MSVC_MODE + #endif + #endif -#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses -#define STBSP__UNALIGNED(code) -#else -#define STBSP__UNALIGNED(code) code -#endif + #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses + #define STBSP__UNALIGNED(code) + #else + #define STBSP__UNALIGNED(code) code + #endif -#ifndef STB_SPRINTF_NOFLOAT + #ifndef STB_SPRINTF_NOFLOAT // internal float utility functions static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); -#define STBSP__SPECIAL 0x7000 -#endif + #define STBSP__SPECIAL 0x7000 + #endif static char stbsp__period = '.'; static char stbsp__comma = ','; static struct { - short temp; // force next field to be 2-byte aligned - char pair[201]; + short temp; // force next field to be 2-byte aligned + char pair[201]; } stbsp__digitpair = -{ - 0, - "00010203040506070809101112131415161718192021222324" - "25262728293031323334353637383940414243444546474849" - "50515253545556575859606162636465666768697071727374" - "75767778798081828384858687888990919293949596979899" -}; + { + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899"}; -STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) -{ - stbsp__period = pperiod; - stbsp__comma = pcomma; +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) { + stbsp__period = pperiod; + stbsp__comma = pcomma; } -#define STBSP__LEFTJUST 1 -#define STBSP__LEADINGPLUS 2 -#define STBSP__LEADINGSPACE 4 -#define STBSP__LEADING_0X 8 -#define STBSP__LEADINGZERO 16 -#define STBSP__INTMAX 32 -#define STBSP__TRIPLET_COMMA 64 -#define STBSP__NEGATIVE 128 -#define STBSP__METRIC_SUFFIX 256 -#define STBSP__HALFWIDTH 512 -#define STBSP__METRIC_NOSPACE 1024 -#define STBSP__METRIC_1024 2048 -#define STBSP__METRIC_JEDEC 4096 + #define STBSP__LEFTJUST 1 + #define STBSP__LEADINGPLUS 2 + #define STBSP__LEADINGSPACE 4 + #define STBSP__LEADING_0X 8 + #define STBSP__LEADINGZERO 16 + #define STBSP__INTMAX 32 + #define STBSP__TRIPLET_COMMA 64 + #define STBSP__NEGATIVE 128 + #define STBSP__METRIC_SUFFIX 256 + #define STBSP__HALFWIDTH 512 + #define STBSP__METRIC_NOSPACE 1024 + #define STBSP__METRIC_1024 2048 + #define STBSP__METRIC_JEDEC 4096 -static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) -{ - sign[0] = 0; - if (fl & STBSP__NEGATIVE) { - sign[0] = 1; - sign[1] = '-'; - } else if (fl & STBSP__LEADINGSPACE) { - sign[0] = 1; - sign[1] = ' '; - } else if (fl & STBSP__LEADINGPLUS) { - sign[0] = 1; - sign[1] = '+'; - } -} - -static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) -{ - char const * sn = s; - - // get up to 4-byte alignment - for (;;) { - if (((stbsp__uintptr)sn & 3) == 0) - break; - - if (!limit || *sn == 0) - return (stbsp__uint32)(sn - s); - - ++sn; - --limit; - } - - // scan over 4 bytes at a time to find terminating 0 - // this will intentionally scan up to 3 bytes past the end of buffers, - // but becase it works 4B aligned, it will never cross page boundaries - // (hence the STBSP__ASAN markup; the over-read here is intentional - // and harmless) - while (limit >= 4) { - stbsp__uint32 v = *(stbsp__uint32 *)sn; - // bit hack to find if there's a 0 byte in there - if ((v - 0x01010101) & (~v) & 0x80808080UL) - break; - - sn += 4; - limit -= 4; - } - - // handle the last few characters to find actual size - while (limit && *sn) { - ++sn; - --limit; - } - - return (stbsp__uint32)(sn - s); -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) -{ - static char hex[] = "0123456789abcdefxp"; - static char hexu[] = "0123456789ABCDEFXP"; - char *bf; - char const *f; - int tlen = 0; - - bf = buf; - f = fmt; - for (;;) { - stbsp__int32 fw, pr, tz; - stbsp__uint32 fl; - - // macros for the callback buffer stuff -#define stbsp__chk_cb_bufL(bytes) \ -{ \ -int len = (int)(bf - buf); \ -if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ -tlen += len; \ -if (0 == (bf = buf = callback(buf, user, len))) \ -goto done; \ -} \ -} -#define stbsp__chk_cb_buf(bytes) \ -{ \ -if (callback) { \ -stbsp__chk_cb_bufL(bytes); \ -} \ -} -#define stbsp__flush_cb() \ -{ \ -stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ -} // flush if there is even one byte in the buffer -#define stbsp__cb_buf_clamp(cl, v) \ -cl = v; \ -if (callback) { \ -int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ -if (cl > lg) \ -cl = lg; \ -} - - // fast copy everything up to the next % (or end of string) - for (;;) { - while (((stbsp__uintptr)f) & 3) { - schk1: - if (f[0] == '%') - goto scandd; - schk2: - if (f[0] == 0) - goto endfmt; - stbsp__chk_cb_buf(1); - *bf++ = f[0]; - ++f; - } - for (;;) { - // Check if the next 4 bytes contain %(0x25) or end of string. - // Using the 'hasless' trick: - // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord - stbsp__uint32 v, c; - v = *(stbsp__uint32 *)f; - c = (~v) & 0x80808080; - if (((v ^ 0x25252525) - 0x01010101) & c) - goto schk1; - if ((v - 0x01010101) & c) - goto schk2; - if (callback) - if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) - goto schk1; -#ifdef STB_SPRINTF_NOUNALIGNED - if(((stbsp__uintptr)bf) & 3) { - bf[0] = f[0]; - bf[1] = f[1]; - bf[2] = f[2]; - bf[3] = f[3]; - } else -#endif - { - *(stbsp__uint32 *)bf = v; - } - bf += 4; - f += 4; - } +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; } + else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } + else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) { + char const *sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + #ifdef STB_SPRINTF_NOUNALIGNED + if (((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } + else + #endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } scandd: - ++f; + ++f; - // ok, we have a percent, read the modifiers first - fw = 0; - pr = -1; - fl = 0; - tz = 0; + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; - // flags - for (;;) { - switch (f[0]) { - // if we have left justify - case '-': - fl |= STBSP__LEFTJUST; - ++f; - continue; - // if we have leading plus - case '+': - fl |= STBSP__LEADINGPLUS; - ++f; - continue; - // if we have leading space - case ' ': - fl |= STBSP__LEADINGSPACE; - ++f; - continue; - // if we have leading 0x - case '#': - fl |= STBSP__LEADING_0X; - ++f; - continue; - // if we have thousand commas - case '\'': - fl |= STBSP__TRIPLET_COMMA; - ++f; - continue; - // if we have kilo marker (none->kilo->kibi->jedec) - case '$': - if (fl & STBSP__METRIC_SUFFIX) { - if (fl & STBSP__METRIC_1024) { - fl |= STBSP__METRIC_JEDEC; - } else { - fl |= STBSP__METRIC_1024; - } - } else { - fl |= STBSP__METRIC_SUFFIX; + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } + else { + fl |= STBSP__METRIC_1024; + } + } + else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } } - ++f; - continue; - // if we don't want space between metric suffix and number - case '_': - fl |= STBSP__METRIC_NOSPACE; - ++f; - continue; - // if we have leading zero - case '0': - fl |= STBSP__LEADINGZERO; - ++f; - goto flags_done; - default: goto flags_done; - } - } flags_done: - // get the field width - if (f[0] == '*') { - fw = va_arg(va, stbsp__uint32); - ++f; - } else { - while ((f[0] >= '0') && (f[0] <= '9')) { - fw = fw * 10 + f[0] - '0'; - f++; - } - } - // get the precision - if (f[0] == '.') { - ++f; - if (f[0] == '*') { - pr = va_arg(va, stbsp__uint32); - ++f; - } else { - pr = 0; - while ((f[0] >= '0') && (f[0] <= '9')) { - pr = pr * 10 + f[0] - '0'; - f++; + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; } - } - } - - // handle integer size overrides - switch (f[0]) { - // are we halfwidth? - case 'h': - fl |= STBSP__HALFWIDTH; - ++f; - if (f[0] == 'h') - ++f; // QUARTERWIDTH - break; - // are we 64-bit (unix style) - case 'l': - fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); - ++f; - if (f[0] == 'l') { - fl |= STBSP__INTMAX; - ++f; - } - break; - // are we 64-bit on intmax? (c99) - case 'j': - fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - // are we 64-bit on size_t or ptrdiff_t? (c99) - case 'z': - fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - case 't': - fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - // are we 64-bit (msft style) - case 'I': - if ((f[1] == '6') && (f[2] == '4')) { - fl |= STBSP__INTMAX; - f += 3; - } else if ((f[1] == '3') && (f[2] == '2')) { - f += 3; - } else { - fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); - ++f; - } - break; - default: break; - } - - // handle each replacement - switch (f[0]) { -#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 - char num[STBSP__NUMSZ]; - char lead[8]; - char tail[8]; - char *s; - String str; - char const *h; - stbsp__uint32 l, n, cs; - stbsp__uint64 n64; -#ifndef STB_SPRINTF_NOFLOAT - double fv; -#endif - stbsp__int32 dp; - char const *sn; - - case 'Q': - str = va_arg(va, String); - if (str.str == 0 && str.len != 0) - str = string_null; - pr = str.len; - s = (char *)str.str; - l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - - case 's': - // get the string - s = va_arg(va, char *); - if (s == 0) - s = (char *)"null"; - // get the length, limited to desired precision - // always limit to ~0u chars since our counts are 32b - l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - // copy the string in - goto scopy; - - case 'c': // char - // get the character - s = num + STBSP__NUMSZ - 1; - *s = (char)va_arg(va, int); - l = 1; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - - case 'n': // weird write-bytes specifier - { - int *d = va_arg(va, int *); - *d = tlen + (int)(bf - buf); - } break; - -#ifdef STB_SPRINTF_NOFLOAT - case 'A': // float - case 'a': // hex float - case 'G': // float - case 'g': // float - case 'E': // float - case 'e': // float - case 'f': // float - va_arg(va, double); // eat it - s = (char *)"No float"; - l = 8; - lead[0] = 0; - tail[0] = 0; - pr = 0; - cs = 0; - STBSP__NOTUSED(dp); - goto scopy; -#else - case 'A': // hex float - case 'a': // hex float - h = (f[0] == 'A') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) - fl |= STBSP__NEGATIVE; - - s = num + 64; - - stbsp__lead_sign(fl, lead); - - if (dp == -1023) - dp = (n64) ? -1022 : 0; - else - n64 |= (((stbsp__uint64)1) << 52); - n64 <<= (64 - 56); - if (pr < 15) - n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); - // add leading chars - -#ifdef STB_SPRINTF_MSVC_MODE - *s++ = '0'; - *s++ = 'x'; -#else - lead[1 + lead[0]] = '0'; - lead[2 + lead[0]] = 'x'; - lead[0] += 2; -#endif - *s++ = h[(n64 >> 60) & 15]; - n64 <<= 4; - if (pr) - *s++ = stbsp__period; - sn = s; - - // print the bits - n = pr; - if (n > 13) - n = 13; - if (pr > (stbsp__int32)n) - tz = pr - n; - pr = 0; - while (n--) { - *s++ = h[(n64 >> 60) & 15]; - n64 <<= 4; - } - - // print the expo - tail[1] = h[17]; - if (dp < 0) { - tail[2] = '-'; - dp = -dp; - } else - tail[2] = '+'; - n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); - tail[0] = (char)n; - for (;;) { - tail[n] = '0' + dp % 10; - if (n <= 3) - break; - --n; - dp /= 10; - } - - dp = (int)(s - sn); - l = (int)(s - (num + 64)); - s = num + 64; - cs = 1 + (3 << 24); - goto scopy; - - case 'G': // float - case 'g': // float - h = (f[0] == 'G') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; - else if (pr == 0) - pr = 1; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) - fl |= STBSP__NEGATIVE; - - // clamp the precision and delete extra zeros after clamp - n = pr; - if (l > (stbsp__uint32)pr) - l = pr; - while ((l > 1) && (pr) && (sn[l - 1] == '0')) { - --pr; - --l; - } - - // should we use %e - if ((dp <= -4) || (dp > (stbsp__int32)n)) { - if (pr > (stbsp__int32)l) - pr = l - 1; - else if (pr) - --pr; // when using %e, there is one digit before the decimal - goto doexpfromg; - } - // this is the insane action to get the pr to match %g semantics for %f - if (dp > 0) { - pr = (dp < (stbsp__int32)l) ? l - dp : 0; - } else { - pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); - } - goto dofloatfromg; - - case 'E': // float - case 'e': // float - h = (f[0] == 'E') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) - fl |= STBSP__NEGATIVE; - doexpfromg: - tail[0] = 0; - stbsp__lead_sign(fl, lead); - if (dp == STBSP__SPECIAL) { - s = (char *)sn; - cs = 0; - pr = 0; - goto scopy; - } - s = num + 64; - // handle leading chars - *s++ = sn[0]; - - if (pr) - *s++ = stbsp__period; - - // handle after decimal - if ((l - 1) > (stbsp__uint32)pr) - l = pr + 1; - for (n = 1; n < l; n++) - *s++ = sn[n]; - // trailing zeros - tz = pr - (l - 1); - pr = 0; - // dump expo - tail[1] = h[0xe]; - dp -= 1; - if (dp < 0) { - tail[2] = '-'; - dp = -dp; - } else - tail[2] = '+'; -#ifdef STB_SPRINTF_MSVC_MODE - n = 5; -#else - n = (dp >= 100) ? 5 : 4; -#endif - tail[0] = (char)n; - for (;;) { - tail[n] = '0' + dp % 10; - if (n <= 3) - break; - --n; - dp /= 10; - } - cs = 1 + (3 << 24); // how many tens - goto flt_lead; - - case 'f': // float - fv = va_arg(va, double); - doafloat: - // do kilos - if (fl & STBSP__METRIC_SUFFIX) { - double divisor; - divisor = 1000.0f; - if (fl & STBSP__METRIC_1024) - divisor = 1024.0; - while (fl < 0x4000000) { - if ((fv < divisor) && (fv > -divisor)) - break; - fv /= divisor; - fl += 0x1000000; + else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } + else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } } - } - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) - fl |= STBSP__NEGATIVE; - dofloatfromg: - tail[0] = 0; - stbsp__lead_sign(fl, lead); - if (dp == STBSP__SPECIAL) { - s = (char *)sn; - cs = 0; - pr = 0; - goto scopy; - } - s = num + 64; - // handle the three decimal varieties - if (dp <= 0) { - stbsp__int32 i; - // handle 0.000*000xxxx - *s++ = '0'; - if (pr) - *s++ = stbsp__period; - n = -dp; - if ((stbsp__int32)n > pr) - n = pr; - i = n; - while (i) { - if ((((stbsp__uintptr)s) & 3) == 0) - break; - *s++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)s = 0x30303030; - s += 4; - i -= 4; - } - while (i) { - *s++ = '0'; - --i; - } - if ((stbsp__int32)(l + n) > pr) - l = pr - n; - i = l; - while (i) { - *s++ = *sn++; - --i; - } - tz = pr - (n + l); - cs = 1 + (3 << 24); // how many tens did we write (for commas below) - } else { - cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; - if ((stbsp__uint32)dp >= l) { - // handle xxxx000*000.0 - n = 0; - for (;;) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } else { - *s++ = sn[n]; - ++n; - if (n >= l) + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH break; - } - } - if (n < (stbsp__uint32)dp) { - n = dp - n; - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (n) { - if ((((stbsp__uintptr)s) & 3) == 0) - break; - *s++ = '0'; - --n; - } - while (n >= 4) { - *(stbsp__uint32 *)s = 0x30303030; - s += 4; - n -= 4; - } - } - while (n) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } + else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } + else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + String str; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; + #ifndef STB_SPRINTF_NOFLOAT + double fv; + #endif + stbsp__int32 dp; + char const *sn; + + case 'Q': + str = va_arg(va, String); + if (str.str == 0 && str.len != 0) + str = string_null; + pr = str.len; + s = (char *)str.str; + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; cs = 0; - *s++ = stbsp__comma; - } else { + goto scopy; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + + #ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; + #else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + + #ifdef STB_SPRINTF_MSVC_MODE *s++ = '0'; - --n; - } - } - } - cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens - if (pr) { - *s++ = stbsp__period; - tz = pr; - } - } else { - // handle xxxxx.xxxx000*000 - n = 0; - for (;;) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } else { - *s++ = sn[n]; - ++n; - if (n >= (stbsp__uint32)dp) + *s++ = 'x'; + #else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; + #endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } + else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } + else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32)l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } + else + tail[2] = '+'; + #ifdef STB_SPRINTF_MSVC_MODE + n = 5; + #else + n = (dp >= 100) ? 5 : 4; + #endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } + else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } + else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } + else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } + else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } + else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; + #endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } + else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + + #ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } + #endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } + else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } + else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } + else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } + else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } + else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } break; - } - } - cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens - if (pr) - *s++ = stbsp__period; - if ((l - dp) > (stbsp__uint32)pr) - l = pr + dp; - while (n < l) { - *s++ = sn[n]; - ++n; - } - tz = pr - (l - dp); + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; } - } - pr = 0; - - // handle k,m,g,t - if (fl & STBSP__METRIC_SUFFIX) { - char idx; - idx = 1; - if (fl & STBSP__METRIC_NOSPACE) - idx = 0; - tail[0] = idx; - tail[1] = ' '; - { - if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. - if (fl & STBSP__METRIC_1024) - tail[idx + 1] = "_KMGT"[fl >> 24]; - else - tail[idx + 1] = "_kMGT"[fl >> 24]; - idx++; - // If printing kibits and not in jedec, add the 'i'. - if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { - tail[idx + 1] = 'i'; - idx++; - } - tail[0] = idx; - } - } - }; - - flt_lead: - // get the length that we copied - l = (stbsp__uint32)(s - (num + 64)); - s = num + 64; - goto scopy; -#endif - - case 'B': // upper binary - case 'b': // lower binary - h = (f[0] == 'B') ? hexu : hex; - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 2; - lead[1] = '0'; - lead[2] = h[0xb]; - } - l = (8 << 4) | (1 << 8); - goto radixnum; - - case 'o': // octal - h = hexu; - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 1; - lead[1] = '0'; - } - l = (3 << 4) | (3 << 8); - goto radixnum; - - case 'p': // pointer - fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; - pr = sizeof(void *) * 2; - fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros - // fall through - to X - - case 'X': // upper hex - case 'x': // lower hex - h = (f[0] == 'X') ? hexu : hex; - l = (4 << 4) | (4 << 8); - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 2; - lead[1] = '0'; - lead[2] = h[16]; - } - radixnum: - // get the number - if (fl & STBSP__INTMAX) - n64 = va_arg(va, stbsp__uint64); - else - n64 = va_arg(va, stbsp__uint32); - - s = num + STBSP__NUMSZ; - dp = 0; - // clear tail, and clear leading if value is zero - tail[0] = 0; - if (n64 == 0) { - lead[0] = 0; - if (pr == 0) { - l = 0; - cs = 0; - goto scopy; - } - } - // convert to string - for (;;) { - *--s = h[n64 & ((1 << (l >> 8)) - 1)]; - n64 >>= (l >> 8); - if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) - break; - if (fl & STBSP__TRIPLET_COMMA) { - ++l; - if ((l & 15) == ((l >> 4) & 15)) { - l &= ~15; - *--s = stbsp__comma; - } - } - }; - // get the tens and the comma pos - cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); - // get the length that we copied - l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); - // copy it - goto scopy; - - case 'u': // unsigned - case 'i': - case 'd': // integer - // get the integer and abs it - if (fl & STBSP__INTMAX) { - stbsp__int64 i64 = va_arg(va, stbsp__int64); - n64 = (stbsp__uint64)i64; - if ((f[0] != 'u') && (i64 < 0)) { - n64 = (stbsp__uint64)-i64; - fl |= STBSP__NEGATIVE; - } - } else { - stbsp__int32 i = va_arg(va, stbsp__int32); - n64 = (stbsp__uint32)i; - if ((f[0] != 'u') && (i < 0)) { - n64 = (stbsp__uint32)-i; - fl |= STBSP__NEGATIVE; - } - } - -#ifndef STB_SPRINTF_NOFLOAT - if (fl & STBSP__METRIC_SUFFIX) { - if (n64 < 1024) - pr = 0; - else if (pr == -1) - pr = 1; - fv = (double)(stbsp__int64)n64; - goto doafloat; - } -#endif - - // convert to string - s = num + STBSP__NUMSZ; - l = 0; - - for (;;) { - // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) - char *o = s - 8; - if (n64 >= 100000000) { - n = (stbsp__uint32)(n64 % 100000000); - n64 /= 100000000; - } else { - n = (stbsp__uint32)n64; - n64 = 0; - } - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - do { - s -= 2; - *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; - n /= 100; - } while (n); - } - while (n) { - if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { - l = 0; - *--s = stbsp__comma; - --o; - } else { - *--s = (char)(n % 10) + '0'; - n /= 10; - } - } - if (n64 == 0) { - if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) - ++s; - break; - } - while (s != o) - if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { - l = 0; - *--s = stbsp__comma; - --o; - } else { - *--s = '0'; - } - } - - tail[0] = 0; - stbsp__lead_sign(fl, lead); - - // get the length that we copied - l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); - if (l == 0) { - *--s = '0'; - l = 1; - } - cs = l + (3 << 24); - if (pr < 0) - pr = 0; - - scopy: - // get fw=leading/trailing space, pr=leading zeros - if (pr < (stbsp__int32)l) - pr = l; - n = pr + lead[0] + tail[0] + tz; - if (fw < (stbsp__int32)n) - fw = n; - fw -= n; - pr -= l; - - // handle right justify and leading zeros - if ((fl & STBSP__LEFTJUST) == 0) { - if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr - { - pr = (fw > pr) ? fw : pr; - fw = 0; - } else { - fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas - } - } - - // copy the spaces and/or zeros - if (fw + pr) { - stbsp__int32 i; - stbsp__uint32 c; - - // copy leading spaces (or when doing %8.4d stuff) - if ((fl & STBSP__LEFTJUST) == 0) - while (fw > 0) { - stbsp__cb_buf_clamp(i, fw); - fw -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = ' '; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x20202020; - bf += 4; - i -= 4; - } - while (i) { - *bf++ = ' '; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy leader - sn = lead + 1; - while (lead[0]) { - stbsp__cb_buf_clamp(i, lead[0]); - lead[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy leading zeros - c = cs >> 24; - cs &= 0xffffff; - cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; - while (pr > 0) { - stbsp__cb_buf_clamp(i, pr); - pr -= i; - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x30303030; - bf += 4; - i -= 4; - } - } - while (i) { - if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { - cs = 0; - *bf++ = stbsp__comma; - } else - *bf++ = '0'; - --i; - } - stbsp__chk_cb_buf(1); - } - } - - // copy leader if there is still one - sn = lead + 1; - while (lead[0]) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, lead[0]); - lead[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy the string - n = l; - while (n) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, n); - n -= i; - STBSP__UNALIGNED(while (i >= 4) { - *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; - bf += 4; - s += 4; - i -= 4; - }) - while (i) { - *bf++ = *s++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy trailing zeros - while (tz) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, tz); - tz -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x30303030; - bf += 4; - i -= 4; - } - while (i) { - *bf++ = '0'; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy tail if there is one - sn = tail + 1; - while (tail[0]) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, tail[0]); - tail[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // handle the left justify - if (fl & STBSP__LEFTJUST) - if (fw > 0) { - while (fw) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, fw); - fw -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = ' '; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x20202020; - bf += 4; - i -= 4; - } - while (i--) - *bf++ = ' '; - stbsp__chk_cb_buf(1); - } - } - break; - - default: // unknown, just copy code - s = num + STBSP__NUMSZ - 1; - *s = f[0]; - l = 1; - fw = fl = 0; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; + ++f; } - ++f; - } - endfmt: +endfmt: - if (!callback) - *bf = 0; - else - stbsp__flush_cb(); + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); - done: - return tlen + (int)(bf - buf); +done: + return tlen + (int)(bf - buf); } -// cleanup -#undef STBSP__LEFTJUST -#undef STBSP__LEADINGPLUS -#undef STBSP__LEADINGSPACE -#undef STBSP__LEADING_0X -#undef STBSP__LEADINGZERO -#undef STBSP__INTMAX -#undef STBSP__TRIPLET_COMMA -#undef STBSP__NEGATIVE -#undef STBSP__METRIC_SUFFIX -#undef STBSP__NUMSZ -#undef stbsp__chk_cb_bufL -#undef stbsp__chk_cb_buf -#undef stbsp__flush_cb -#undef stbsp__cb_buf_clamp + // cleanup + #undef STBSP__LEFTJUST + #undef STBSP__LEADINGPLUS + #undef STBSP__LEADINGSPACE + #undef STBSP__LEADING_0X + #undef STBSP__LEADINGZERO + #undef STBSP__INTMAX + #undef STBSP__TRIPLET_COMMA + #undef STBSP__NEGATIVE + #undef STBSP__METRIC_SUFFIX + #undef STBSP__NUMSZ + #undef stbsp__chk_cb_bufL + #undef stbsp__chk_cb_buf + #undef stbsp__flush_cb + #undef stbsp__cb_buf_clamp // ============================================================================ // wrapper functions -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) -{ - int result; - va_list va; - va_start(va, fmt); - result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); - va_end(va); - return result; +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) { + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; } typedef struct stbsp__context { - char *buf; - int count; - int length; - char tmp[STB_SPRINTF_MIN]; + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; } stbsp__context; -static char *stbsp__clamp_callback(const char *buf, void *user, int len) -{ - stbsp__context *c = (stbsp__context *)user; - c->length += len; +static char *stbsp__clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + c->length += len; - if (len > c->count) - len = c->count; + if (len > c->count) + len = c->count; - if (len) { - if (buf != c->buf) { - const char *s, *se; - char *d; - d = c->buf; - s = buf; - se = buf + len; - do { - *d++ = *s++; - } while (s < se); + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; } - c->buf += len; - c->count -= len; - } - if (c->count <= 0) - return c->tmp; - return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can } -static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) -{ - stbsp__context * c = (stbsp__context*)user; - (void) sizeof(buf); +static char *stbsp__count_clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + (void)sizeof(buf); - c->length += len; - return c->tmp; // go direct into buffer if you can + c->length += len; + return c->tmp; // go direct into buffer if you can } -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) -{ - stbsp__context c; +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va) { + stbsp__context c; - if ( (count == 0) && !buf ) - { - c.length = 0; + if ((count == 0) && !buf) { + c.length = 0; - STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); - } - else - { - int l; + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__count_clamp_callback, &c, c.tmp, fmt, va); + } + else { + int l; - c.buf = buf; - c.count = count; - c.length = 0; + c.buf = buf; + c.count = count; + c.length = 0; - STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__clamp_callback, &c, stbsp__clamp_callback(0, &c, 0), fmt, va); - // zero-terminate - l = (int)( c.buf - buf ); - if ( l >= count ) // should never be greater, only equal (or less) than count - l = count - 1; - buf[l] = 0; - } + // zero-terminate + l = (int)(c.buf - buf); + if (l >= count) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } - return c.length; + return c.length; } -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) -{ - int result; - va_list va; - va_start(va, fmt); +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) { + int result; + va_list va; + va_start(va, fmt); - result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); - va_end(va); + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); - return result; + return result; } -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) -{ - return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) { + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); } // ======================================================================= // low level float utility functions -#ifndef STB_SPRINTF_NOFLOAT + #ifndef STB_SPRINTF_NOFLOAT -// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) -#define STBSP__COPYFP(dest, src) \ -{ \ -int cn; \ -for (cn = 0; cn < 8; cn++) \ -((char *)&dest)[cn] = ((char *)&src)[cn]; \ -} + // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) + #define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } // get float info -static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) -{ - double d; - stbsp__int64 b = 0; +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) { + double d; + stbsp__int64 b = 0; - // load value and round at the frac_digits - d = value; + // load value and round at the frac_digits + d = value; - STBSP__COPYFP(b, d); + STBSP__COPYFP(b, d); - *bits = b & ((((stbsp__uint64)1) << 52) - 1); - *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); - return (stbsp__int32)((stbsp__uint64) b >> 63); + return (stbsp__int32)((stbsp__uint64)b >> 63); } static double const stbsp__bot[23] = { - 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, - 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 -}; + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022}; static double const stbsp__negbot[22] = { - 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, - 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 -}; + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022}; static double const stbsp__negboterr[22] = { - -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, - 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, - -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, - 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 -}; + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039}; static double const stbsp__top[13] = { - 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 -}; + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299}; static double const stbsp__negtop[13] = { - 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 -}; + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299}; static double const stbsp__toperr[13] = { - 8388608, - 6.8601809640529717e+028, - -7.253143638152921e+052, - -4.3377296974619174e+075, - -1.5559416129466825e+098, - -3.2841562489204913e+121, - -3.7745893248228135e+144, - -1.7356668416969134e+167, - -3.8893577551088374e+190, - -9.9566444326005119e+213, - 6.3641293062232429e+236, - -5.2069140800249813e+259, - -5.2504760255204387e+282 -}; + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282}; static double const stbsp__negtoperr[13] = { - 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, - -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, - 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, - 8.0970921678014997e-317 -}; + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317}; -#if defined(_MSC_VER) && (_MSC_VER <= 1200) + #if defined(_MSC_VER) && (_MSC_VER <= 1200) static stbsp__uint64 const stbsp__powten[20] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000U -}; -#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) -#else + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U}; + #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) + #else static stbsp__uint64 const stbsp__powten[20] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000ULL, - 100000000000ULL, - 1000000000000ULL, - 10000000000000ULL, - 100000000000000ULL, - 1000000000000000ULL, - 10000000000000000ULL, - 100000000000000000ULL, - 1000000000000000000ULL, - 10000000000000000000ULL -}; -#define stbsp__tento19th (1000000000000000000ULL) -#endif + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL}; + #define stbsp__tento19th (1000000000000000000ULL) + #endif -#define stbsp__ddmulthi(oh, ol, xh, yh) \ -{ \ -double ahi = 0, alo, bhi = 0, blo; \ -stbsp__int64 bt; \ -oh = xh * yh; \ -STBSP__COPYFP(bt, xh); \ -bt &= ((~(stbsp__uint64)0) << 27); \ -STBSP__COPYFP(ahi, bt); \ -alo = xh - ahi; \ -STBSP__COPYFP(bt, yh); \ -bt &= ((~(stbsp__uint64)0) << 27); \ -STBSP__COPYFP(bhi, bt); \ -blo = yh - bhi; \ -ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ -} + #define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } -#define stbsp__ddtoS64(ob, xh, xl) \ -{ \ -double ahi = 0, alo, vh, t; \ -ob = (stbsp__int64)xh; \ -vh = (double)ob; \ -ahi = (xh - vh); \ -t = (ahi - xh); \ -alo = (xh - (ahi - t)) - (vh + t); \ -ob += (stbsp__int64)(ahi + alo + xl); \ -} + #define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } -#define stbsp__ddrenorm(oh, ol) \ -{ \ -double s; \ -s = oh + ol; \ -ol = ol - (s - oh); \ -oh = s; \ -} + #define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } -#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); -#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 { - double ph, pl; - if ((power >= 0) && (power <= 22)) { - stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); - } else { - stbsp__int32 e, et, eb; - double p2h, p2l; - - e = power; - if (power < 0) - e = -e; - et = (e * 0x2c9) >> 14; /* %23 */ - if (et > 13) - et = 13; - eb = e - (et * 23); - - ph = d; - pl = 0.0; - if (power < 0) { - if (eb) { - --eb; - stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); - stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); - } - if (et) { - stbsp__ddrenorm(ph, pl); - --et; - stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); - stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); - ph = p2h; - pl = p2l; - } - } else { - if (eb) { - e = eb; - if (eb > 22) - eb = 22; - e -= eb; - stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); - if (e) { - stbsp__ddrenorm(ph, pl); - stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); - stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); - ph = p2h; - pl = p2l; - } - } - if (et) { - stbsp__ddrenorm(ph, pl); - --et; - stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); - stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); - ph = p2h; - pl = p2l; - } + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); } - } - stbsp__ddrenorm(ph, pl); - *ohi = ph; - *olo = pl; + else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } + else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; } // given a float value, returns the significant bits in bits, and the position of the // decimal point in decimal_pos. +/-INF and NAN are specified by special values // returned in the decimal_pos parameter. // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 -static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) -{ - double d; - stbsp__int64 bits = 0; - stbsp__int32 expo, e, ng, tens; +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) { + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; - d = value; - STBSP__COPYFP(bits, d); - expo = (stbsp__int32)((bits >> 52) & 2047); - ng = (stbsp__int32)((stbsp__uint64) bits >> 63); - if (ng) - d = -d; + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64)bits >> 63); + if (ng) + d = -d; - if (expo == 2047) // is nan or inf? - { - *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; - *decimal_pos = STBSP__SPECIAL; - *len = 3; - return ng; - } - - if (expo == 0) // is zero or denormal - { - if (((stbsp__uint64) bits << 1) == 0) // do zero + if (expo == 2047) // is nan or inf? { - *decimal_pos = 1; - *start = out; - out[0] = '0'; - *len = 1; - return ng; + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; } - // find the right expo for denormals + + if (expo == 0) // is zero or denormal { - stbsp__int64 v = ((stbsp__uint64)1) << 51; - while ((bits & v) == 0) { - --expo; - v >>= 1; - } + if (((stbsp__uint64)bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } } - } - // find the decimal exponent as well as the decimal bits of the value - { - double ph, pl; + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; - // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 - tens = expo - 1023; - tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); - // move the significant bits into position and stick them into an int - stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); - // get full as much precision from double-double as possible - stbsp__ddtoS64(bits, ph, pl); + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); - // check if we undershot - if (((stbsp__uint64)bits) >= stbsp__tento19th) - ++tens; - } - - // now do the rounding in integer land - frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); - if ((frac_digits < 24)) { - stbsp__uint32 dg = 1; - if ((stbsp__uint64)bits >= stbsp__powten[9]) - dg = 10; - while ((stbsp__uint64)bits >= stbsp__powten[dg]) { - ++dg; - if (dg == 20) - goto noround; - } - if (frac_digits < dg) { - stbsp__uint64 r; - // add 0.5 at the right position and round - e = dg - frac_digits; - if ((stbsp__uint32)e >= 24) - goto noround; - r = stbsp__powten[e]; - bits = bits + (r / 2); - if ((stbsp__uint64)bits >= stbsp__powten[dg]) - ++tens; - bits /= r; + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } noround:; - } - - // kill long trailing runs of zeros - if (bits) { - stbsp__uint32 n; - for (;;) { - if (bits <= 0xffffffff) - break; - if (bits % 1000) - goto donez; - bits /= 1000; } - n = (stbsp__uint32)bits; - while ((n % 1000) == 0) - n /= 1000; - bits = n; + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; donez:; - } + } - // convert to string - out += 64; - e = 0; - for (;;) { - stbsp__uint32 n; - char *o = out - 8; - // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) - if (bits >= 100000000) { - n = (stbsp__uint32)(bits % 100000000); - bits /= 100000000; - } else { - n = (stbsp__uint32)bits; - bits = 0; + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } + else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } } - while (n) { - out -= 2; - *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; - n /= 100; - e += 2; - } - if (bits == 0) { - if ((e) && (out[0] == '0')) { - ++out; - --e; - } - break; - } - while (out != o) { - *--out = '0'; - ++e; - } - } - *decimal_pos = tens; - *start = out; - *len = e; - return ng; + *decimal_pos = tens; + *start = out; + *len = e; + return ng; } -#undef stbsp__ddmulthi -#undef stbsp__ddrenorm -#undef stbsp__ddmultlo -#undef stbsp__ddmultlos -#undef STBSP__SPECIAL -#undef STBSP__COPYFP + #undef stbsp__ddmulthi + #undef stbsp__ddrenorm + #undef stbsp__ddmultlo + #undef stbsp__ddmultlos + #undef STBSP__SPECIAL + #undef STBSP__COPYFP -#endif // STB_SPRINTF_NOFLOAT + #endif // STB_SPRINTF_NOFLOAT -// clean up -#undef stbsp__uint16 -#undef stbsp__uint32 -#undef stbsp__int32 -#undef stbsp__uint64 -#undef stbsp__int64 -#undef STBSP__UNALIGNED + // clean up + #undef stbsp__uint16 + #undef stbsp__uint32 + #undef stbsp__int32 + #undef stbsp__uint64 + #undef stbsp__int64 + #undef STBSP__UNALIGNED #endif // STB_SPRINTF_IMPLEMENTATION