-#ifndef BH_H\r
-#define BH_H\r
-\r
-#include <sys/stat.h>\r
-#include <unistd.h>\r
-#include <fcntl.h>\r
-#include <errno.h>\r
-\r
-#include <stdlib.h>\r
-#include <stdarg.h>\r
-#include <string.h> // TODO: Replace with needed functions\r
-#include <assert.h>\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better types\r
-//-------------------------------------------------------------------------------------\r
-typedef unsigned char u8;\r
-typedef unsigned short u16;\r
-typedef unsigned int u32;\r
-typedef unsigned long u64;\r
-typedef unsigned long long u128;\r
-typedef signed char i8;\r
-typedef signed short i16;\r
-typedef signed int i32;\r
-typedef signed long i64;\r
-typedef signed long long i128;\r
-typedef unsigned long isize;\r
-typedef i32 b32;\r
-typedef void* ptr;\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better character functions\r
-//-------------------------------------------------------------------------------------\r
-b32 char_is_alpha(const char a);\r
-b32 char_is_num(const char a);\r
-b32 char_is_alphanum(const char a);\r
-b32 char_is_whitespace(const char a);\r
-b32 char_in_range(const char lo, const char hi, const char a);\r
-char charset_contains(const char* charset, char ch);\r
-i64 chars_match(char* ptr1, char* ptr2);\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better math functions\r
-//-------------------------------------------------------------------------------------\r
-#define bh_max(a, b) ((a) > (b) ? (a) : (b))\r
-#define bh_min(a, b) ((a) < (b) ? (a) : (b))\r
-#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))\r
-#define bh_abs(x) ((x) < 0 ? -(x) : (x))\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Helpful macros\r
-//-------------------------------------------------------------------------------------\r
-#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem)\r
-#define bh_aligh_of(Type) bh_offset_of(struct { char c; Type member; }, member)\r
-#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)\r
-\r
-#define bh_pointer_add(ptr, amm) ((void *)((u8 *) ptr + amm))\r
-#define BH_BIT(x) (1 << (x))\r
-#define BH_MASK_SET(var, set, mask) ((set) ? (var) |= (mask) : (var) &= ~(mask))\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Custom allocators\r
-//-------------------------------------------------------------------------------------\r
-\r
-typedef enum bh_allocator_actions {\r
- bh_allocator_action_alloc,\r
- bh_allocator_action_free,\r
- bh_allocator_action_resize,\r
-} bh_allocator_actions;\r
-\r
-#define BH_ALLOCATOR_PROC(name) \\r
-ptr name(ptr data, bh_allocator_actions action, \\r
- isize size, isize alignment, \\r
- void* prev_memory, \\r
- u64 flags)\r
-\r
-typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type\r
-\r
-typedef struct bh_allocator {\r
- bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions\r
- ptr data; // Pointer to the other data for the allocator\r
-} bh_allocator;\r
-\r
-typedef enum bh_allocator_flags {\r
- bh_allocator_flag_clear = 1 // Sets all memory to be 0\r
-} bh_allocator_flags;\r
-\r
-ptr bh_alloc(bh_allocator a, isize size);\r
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);\r
-ptr bh_resize(bh_allocator a, ptr data, isize new_size);\r
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);\r
-void bh_free(bh_allocator a, ptr data);\r
-\r
-#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T))\r
-#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n))\r
-\r
-// NOTE: This should get optimized out since alignment should be a power of two\r
-#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment)\r
-\r
-\r
-\r
-\r
-// HEAP ALLOCATOR\r
-// Essentially a wrapper for malloc, free and realloc\r
-bh_allocator bh_heap_allocator(void);\r
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc);\r
-\r
-\r
-\r
-\r
-\r
-// ARENA ALLOCATOR\r
-typedef struct bh_arena {\r
- bh_allocator backing;\r
- ptr first_arena, current_arena;\r
- isize size, arena_size; // in bytes\r
-} bh_arena;\r
-\r
-typedef struct bh__arena_internal {\r
- ptr next_arena;\r
- void* data; // Not actually a pointer, just used for the offset\r
-} bh__arena_internal;\r
-\r
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);\r
-void bh_arena_free(bh_arena* alloc);\r
-bh_allocator bh_arena_allocator(bh_arena* alloc);\r
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc);\r
-\r
-\r
-\r
-// SCRATCH ALLOCATOR\r
-typedef struct bh_scratch {\r
- bh_allocator backing;\r
- ptr memory, end, curr;\r
-} bh_scratch;\r
-\r
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);\r
-void bh_scratch_free(bh_scratch* scratch);\r
-bh_allocator bh_scratch_allocator(bh_scratch* scratch);\r
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better strings\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_STRING\r
-\r
-typedef struct bh__string {\r
- u64 length;\r
- u64 capacity;\r
-} bh__string;\r
-\r
-typedef char bh_string;\r
-\r
-#define bh__stringhead(x) (((bh__string *)(x)) - 1)\r
-\r
-#define bh_string_new(x) _Generic((x), \\r
- unsigned long: bh_string_new_cap, \\r
- unsigned int: bh_string_new_cap, \\r
- int: bh_string_new_cap, \\r
- long: bh_string_new_cap, \\r
- const char*: bh_string_new_str, \\r
- char*: bh_string_new_str)(x)\r
-\r
-#define bh_string_append(str1, str2) _Generic((str2), \\r
- bh_string*: bh_string_append_bh_string, \\r
- char*: bh_string_append_cstr, \\r
- const char*: bh_string_append_cstr)(str1, str2)\r
-\r
-#define bh_string_replace_at(dest, src, offset) _Generic((src), \\r
- bh_string*: bh_string_replace_at_bh_string, \\r
- char*: bh_string_replace_at_cstr, \\r
- const char*: bh_string_replace_at_cstr)(dest, src, offset)\r
-\r
-#define bh_string_insert_at(dest, src, offset) _Generic((src), \\r
- bh_string*: bh_string_insert_at_bh_string, \\r
- char*: bh_string_insert_at_cstr, \\r
- const char*: bh_string_insert_at_cstr)(dest, src, offset)\r
-\r
-bh_string bh_string_new_cap(unsigned long cap);\r
-bh_string bh_string_new_str(const char* cstr);\r
-b32 bh_string_delete(bh_string* str);\r
-b32 bh_string_ensure_capacity(bh_string* str, u64 cap);\r
-void bh_string_append_bh_string(bh_string* str1, bh_string* str2);\r
-void bh_string_append_cstr(bh_string* str1, const char* str2);\r
-void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset);\r
-void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset);\r
-void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset);\r
-void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset);\r
-void bh_string_trim_end(bh_string* str, const char* charset);\r
-void bh_string_trim_begin(bh_string* str, const char* charset);\r
-void bh_string_trim_end_space(bh_string* str);\r
-// TEMP\r
-void bh_string_print(bh_string* str);\r
-\r
-#endif\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better files\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_FILE\r
-\r
-typedef enum bh_file_error {\r
- BH_FILE_ERROR_NONE,\r
- BH_FILE_ERROR_INVALID,\r
- BH_FILE_ERROR_BAD_FD,\r
-} bh_file_error;\r
-\r
-typedef enum bh_file_mode {\r
- BH_FILE_MODE_READ = 1 << 0,\r
- BH_FILE_MODE_WRITE = 1 << 1,\r
- BH_FILE_MODE_APPEND = 1 << 2,\r
- BH_FILE_MODE_RW = 1 << 3,\r
-\r
- BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW\r
-} bh_file_mode;\r
-\r
-typedef enum bh_file_whence {\r
- BH_FILE_WHENCE_BEGIN = SEEK_SET,\r
- BH_FILE_WHENCE_CURRENT = SEEK_CUR,\r
- BH_FILE_WHENCE_END = SEEK_END,\r
-} bh_file_whence;\r
-\r
-typedef int bh_file_descriptor;\r
-\r
-typedef struct bh_file {\r
- bh_file_descriptor fd;\r
- char const* filename;\r
-} bh_file;\r
-\r
-typedef enum bh_file_standard {\r
- BH_FILE_STANDARD_INPUT,\r
- BH_FILE_STANDARD_OUTPUT,\r
- BH_FILE_STANDARD_ERROR\r
-} bh_file_standard;\r
-\r
-typedef struct bh_file_contents {\r
- bh_allocator allocator;\r
- const char *filename;\r
- isize length;\r
- void* data;\r
-} bh_file_contents;\r
-\r
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);\r
-\r
-bh_file_error bh_file_create(bh_file* file, char const* filename);\r
-bh_file_error bh_file_open(bh_file* file, char const* filename);\r
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);\r
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);\r
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);\r
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);\r
-static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset);\r
-i64 bh_file_seek_to_end(bh_file* file);\r
-i64 bh_file_skip(bh_file* file, i64 bytes);\r
-i64 bh_file_tell(bh_file* file);\r
-bh_file_error bh_file_close(bh_file* file);\r
-i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);\r
-i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);\r
-i64 bh_file_size(bh_file* file);\r
-\r
-#define bh_file_read_contents(allocator_, x) _Generic((x), \\r
- bh_file*: bh_file_read_contents_bh_file, \\r
- const char*: bh_file_read_contents_direct, \\r
- char*: bh_file_read_contents_direct)((allocator_), x)\r
-\r
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);\r
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);\r
-i32 bh_file_contents_delete(bh_file_contents* contents);\r
-\r
-#endif\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Alternate printing\r
-//-------------------------------------------------------------------------------------\r
-// Barebones implementation of printf. Does not support all format options\r
-// Currently supports:\r
-// %c - chars\r
-// %_(u)d - ints where _ is:\r
-// nothing - decimal\r
-// o - octal\r
-// x - hexadecimal\r
-// %_(u)l - longs where _ is:\r
-// nothing - decimal\r
-// o - octal\r
-// x - hexadecimal\r
-// %f - floating points\r
-// %s - null terminated strings\r
-// %p - pointers\r
-// %% - literal %\r
-\r
-typedef struct bh__print_format {\r
- u32 base;\r
-} bh__print_format;\r
-\r
-isize bh_printf(char const *fmt, ...);\r
-isize bh_printf_va(char const *fmt, va_list va);\r
-isize bh_printf_err(char const *fmt, ...);\r
-isize bh_printf_err_va(char const *fmt, va_list va);\r
-isize bh_fprintf(bh_file* f, char const *fmt, ...);\r
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);\r
-char* bh_bprintf(char const *fmt, ...);\r
-char* bh_bprintf_va(char const *fmt, va_list va);\r
-isize bh_snprintf(char *str, isize n, char const *fmt, ...);\r
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better debug functions\r
-//-------------------------------------------------------------------------------------\r
-#ifdef BH_DEBUG\r
-\r
-void* bh__debug_malloc(size_t size, const char* file, u64 line);\r
-void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line);\r
-void bh__debug_free(void* ptr, const char* file, u64 line);\r
-void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line);\r
-\r
-#ifdef BH_DEFINE\r
-\r
-void* bh__debug_malloc(size_t size, const char* file, u64 line) {\r
- void* p = malloc(size);\r
- bh_printf("[DEBUG] %p = malloc(%d) at %s:%d\n", p, size, file, line);\r
- return p;\r
-}\r
-\r
-void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line) {\r
- void* p = aligned_alloc(size, alignment);\r
- bh_printf("[DEBUG] %p = aligned_alloc(%d, %d) at %s:%d\n", p, alignment, size, file, line);\r
- return p;\r
-}\r
-\r
-void bh__debug_free(void* ptr, const char* file, u64 line) {\r
- bh_printf("[DEBUG] free(%p) at %s:%d\n", ptr, file, line);\r
- free(ptr);\r
-}\r
-\r
-void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line) {\r
- void* p = realloc(ptr, size);\r
- bh_printf("[DEBUG] %p = realloc(%p, %d) at %s:%d\n", p, ptr, size, file, line);\r
- return p;\r
-}\r
-\r
-#endif\r
-\r
-#define malloc(size) (bh__debug_malloc(size, __FILE__, __LINE__))\r
-#define aligned_alloc(size, alignment) (bh__debug_aligned_alloc(size, alignment, __FILE__, __LINE__))\r
-#define free(ptr) (bh__debug_free(ptr, __FILE__, __LINE__))\r
-#define realloc(ptr, size) (bh__debug_realloc(ptr, size, __FILE__, __LINE__))\r
-\r
-#endif\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// Better dynamically-sized arrays\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_ARRAY\r
-\r
-typedef struct bh__arr {\r
- bh_allocator allocator;\r
- i32 length, capacity;\r
-} bh__arr;\r
-\r
-#ifndef BH_ARR_GROW_FORMULA\r
-#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4)\r
-#endif\r
-\r
-#define bh_arr(T) T*\r
-#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1)\r
-\r
-#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())\r
-#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0)\r
-#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0)\r
-#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)\r
-#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)\r
-\r
-#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length])\r
-#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1])\r
-#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)])\r
-\r
-#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))\r
-#define bh_arr_free(arr) (bh__arr_free((void**) &(arr)))\r
-#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))\r
-\r
-#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))\r
-#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))\r
-#define bh_arr_set_length(arr, n) ( \\r
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), n), \\r
- bh__arrhead(arr)->length = n)\r
-\r
-#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))\r
-\r
-#define bh_arr_insert_end(arr, n) ( \\r
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \\r
- bh__arrhead(arr)->length += n)\r
-\r
-#define bh_arr_push(arr, value) ( \\r
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1), \\r
- arr[bh__arrhead(arr)->length++] = value)\r
-\r
-#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1)\r
-#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0)\r
-\r
-#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))\r
-#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length])\r
-\r
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);\r
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);\r
-b32 bh__arr_free(void **arr);\r
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);\r
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);\r
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);\r
-\r
-#endif\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// HASH TABLE FUNCTIONS\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_HASHTABLE\r
-\r
-#define BH__HASH_STORED_KEY_SIZE 64\r
-typedef struct bh__hash_entry {\r
- char key[BH__HASH_STORED_KEY_SIZE];\r
- i32 value; // NOTE: Not actually an i32, just used as a placeholder for offset\r
-} bh__hash_entry;\r
-\r
-#define BH__HASH_MODULUS 1021\r
-#define BH__HASH_KEYSIZE 64\r
-#ifdef BH_DEFINE\r
-u64 bh__hash_function(const char* str, i32 len) {\r
- u64 hash = 5381;\r
- i32 c, l = 0;\r
- if (len == 0) len = BH__HASH_KEYSIZE;\r
-\r
- while ((c = *str++) && l++ < len) {\r
- hash = (hash << 5) + hash + c;\r
- }\r
-\r
- return hash % BH__HASH_MODULUS;\r
-}\r
-#endif\r
-\r
-typedef struct bh_hash_iterator {\r
- ptr *tab, *endtab;\r
- i32 elemsize, arrlen;\r
- bh__hash_entry* entry;\r
-} bh_hash_iterator;\r
-\r
-typedef struct bh__hash {\r
- bh_allocator allocator;\r
- ptr arrs[BH__HASH_MODULUS];\r
-} bh__hash;\r
-\r
-#define bh_hash(T) T*\r
-\r
-#ifdef BH_HASH_SIZE_SAFE\r
- #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))\r
- #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))\r
- #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = (T) value))\r
- #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((bh__hash *) tab, sizeof(T), key)))\r
- #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key))))\r
- #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((bh__hash *) tab, sizeof(T), key))\r
-\r
- #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))\r
- #define bh_hash_iter_key(it) (it.entry->key)\r
- #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value))\r
-#else\r
- #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))\r
- #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))\r
- #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = value)\r
- #define bh_hash_has(T, tab, key) (bh__hash_has((bh__hash *) tab, sizeof(T), key))\r
- #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key)))\r
- #define bh_hash_delete(T, tab, key) (bh__hash_delete((bh__hash *) tab, sizeof(T), key))\r
-\r
- #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))\r
- #define bh_hash_iter_key(it) (it.entry->key)\r
- #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value))\r
-#endif\r
-\r
-b32 bh__hash_init(bh_allocator allocator, bh__hash **table);\r
-b32 bh__hash_free(bh__hash **table);\r
-ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key);\r
-b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key);\r
-ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key);\r
-void bh__hash_delete(bh__hash *table, i32 elemsize, char *key);\r
-bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize);\r
-b32 bh_hash_iter_next(bh_hash_iterator* it);\r
-\r
-#endif\r
-\r
-#ifdef BH_DEFINE\r
-#undef BH_DEFINE\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// IMPLEMENTATIONS\r
-//-------------------------------------------------------------------------------------\r
-\r
-//-------------------------------------------------------------------------------------\r
-// CHAR FUNCTIONS\r
-//-------------------------------------------------------------------------------------\r
-b32 char_is_alpha(const char a) {\r
- return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');\r
-}\r
-\r
-b32 char_is_num(const char a) {\r
- return ('0' <= a && a <= '9');\r
-}\r
-\r
-b32 char_is_alphanum(const char a) {\r
- return char_is_alpha(a) || char_is_num(a);\r
-}\r
-\r
-b32 char_is_whitespace(const char a) {\r
- return charset_contains(" \t\r\n", a);\r
-}\r
-\r
-b32 char_in_range(const char lo, const char hi, const char a) {\r
- return lo <= a <= hi;\r
-}\r
-\r
-char charset_contains(const char* charset, char ch) {\r
- while (*charset) {\r
- if (*charset == ch) return ch;\r
- charset++;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-i64 chars_match(char* ptr1, char* ptr2) {\r
- i64 len = 0;\r
- while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;\r
- return *ptr2 == '\0' ? len : 0;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// CUSTOM ALLOCATORS IMPLEMENTATION\r
-//-------------------------------------------------------------------------------------\r
-\r
-\r
-ptr bh_alloc(bh_allocator a, isize size) {\r
- return bh_alloc_aligned(a, size, 16);\r
-}\r
-\r
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {\r
- return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0);\r
-}\r
-\r
-ptr bh_resize(bh_allocator a, ptr data, isize new_size) {\r
- return bh_resize_aligned(a, data, new_size, 16);\r
-}\r
-\r
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {\r
- return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);\r
-}\r
-\r
-void bh_free(bh_allocator a, ptr data) {\r
- if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);\r
-}\r
-\r
-\r
-\r
-// HEAP ALLOCATOR IMPLEMENTATION\r
-\r
-bh_allocator bh_heap_allocator(void) {\r
- return (bh_allocator) {\r
- .proc = bh_heap_allocator_proc,\r
- .data = NULL\r
- };\r
-}\r
-\r
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {\r
- ptr retval = NULL;\r
-\r
- switch (action) {\r
- case bh_allocator_action_alloc: {\r
- retval = aligned_alloc(alignment, size);\r
-\r
- if (flags & bh_allocator_flag_clear && retval != NULL) {\r
- memset(retval, 0, size);\r
- }\r
- } break;\r
-\r
- case bh_allocator_action_resize: {\r
- // TODO: Maybe replace with better custom function\r
- retval = realloc(prev_memory, size);\r
- } break;\r
-\r
- case bh_allocator_action_free: {\r
- free(prev_memory);\r
- } break;\r
- }\r
-\r
- return retval;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-// ARENA ALLOCATOR IMPLEMENTATION\r
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {\r
- arena_size = bh_max(arena_size, sizeof(ptr));\r
- ptr data = bh_alloc(backing, arena_size);\r
-\r
- alloc->backing = backing;\r
- alloc->arena_size = arena_size;\r
- alloc->size = sizeof(ptr);\r
- alloc->first_arena = data;\r
- alloc->current_arena = data;\r
-\r
- ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;\r
-}\r
-\r
-void bh_arena_free(bh_arena* alloc) {\r
- bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;\r
- bh__arena_internal *trailer = walker;\r
- while (walker != NULL) {\r
- walker = walker->next_arena;\r
- bh_free(alloc->backing, trailer);\r
- trailer = walker;\r
- }\r
-\r
- alloc->first_arena = NULL;\r
- alloc->current_arena = NULL;\r
- alloc->arena_size = 0;\r
- alloc->size = 0;\r
-}\r
-\r
-bh_allocator bh_arena_allocator(bh_arena* alloc) {\r
- return (bh_allocator) {\r
- .proc = bh_arena_allocator_proc,\r
- .data = alloc,\r
- };\r
-}\r
-\r
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {\r
- bh_arena* alloc_arena = (bh_arena*) data;\r
-\r
- ptr retval = NULL;\r
-\r
- switch (action) {\r
- case bh_allocator_action_alloc: {\r
-\r
- // TODO: Do this better because right now bh__align is bad\r
- // size = bh__align(size, alignment);\r
- if (size > alloc_arena->arena_size - sizeof(ptr)) {\r
- // Size too large for the arena\r
- return NULL;\r
- }\r
-\r
- if (alloc_arena->size + size >= alloc_arena->arena_size) {\r
- alloc_arena->size = sizeof(ptr);\r
- bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);\r
-\r
- if (new_arena == NULL) {\r
- bh_printf_err("Arena Allocator: couldn't allocate new arena");\r
- return NULL;\r
- }\r
-\r
- new_arena->next_arena = NULL;\r
- ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;\r
- alloc_arena->current_arena = new_arena;\r
- }\r
-\r
- retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);\r
- alloc_arena->size += size;\r
- } break;\r
-\r
- case bh_allocator_action_resize: {\r
- // Do nothing since this is a fixed allocator\r
- } break;\r
-\r
- case bh_allocator_action_free: {\r
- // Do nothing since this allocator isn't made for freeing memory\r
- } break;\r
- }\r
-\r
- return retval;\r
-}\r
-\r
-\r
-\r
-\r
-// SCRATCH ALLOCATOR IMPLEMENTATION\r
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {\r
- ptr memory = bh_alloc(backing, scratch_size);\r
-\r
- scratch->backing = backing;\r
- scratch->memory = memory;\r
- scratch->curr = memory;\r
- scratch->end = memory + scratch_size;\r
-}\r
-\r
-void bh_scratch_free(bh_scratch* scratch) {\r
- bh_free(scratch->backing, scratch->memory);\r
-\r
- scratch->memory = NULL;\r
- scratch->curr = NULL;\r
- scratch->end = NULL;\r
-}\r
-\r
-bh_allocator bh_scratch_allocator(bh_scratch* scratch) {\r
- return (bh_allocator) {\r
- .proc = bh_scratch_allocator_proc,\r
- .data = scratch,\r
- };\r
-}\r
-\r
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {\r
- bh_scratch* scratch = (bh_scratch*) data;\r
- ptr retval = NULL;\r
-\r
- switch (action) {\r
- case bh_allocator_action_alloc: {\r
- if (size > scratch->end - scratch->memory) {\r
- return NULL;\r
- }\r
-\r
- retval = scratch->curr;\r
- scratch->curr += size;\r
-\r
- if (scratch->curr >= scratch->end) {\r
- scratch->curr = scratch->memory;\r
- retval = scratch->curr;\r
- }\r
- } break;\r
-\r
- case bh_allocator_action_free:\r
- case bh_allocator_action_resize:\r
- // Do nothing\r
- break;\r
- }\r
-\r
- return retval;\r
-}\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// STRING IMPLEMENTATION (BROKEN)\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_STRING\r
-\r
-bh_string* bh_string_new_cap(unsigned long cap) {\r
- bh__string* str;\r
- str = (bh__string*) malloc(sizeof(*str) + sizeof(char) * cap + 1);\r
- str[0] = 0;\r
- return str + 1;\r
-}\r
-\r
-bh_string* bh_string_new_str(const char* cstr) {\r
- const i32 len = strlen(cstr);\r
- bh__string* str;\r
- i32 i;\r
-\r
- str = malloc(sizeof(*str) + sizeof(char) * len + 1);\r
- char* data = (char*) (str + 1);\r
- for (i = 0; i < len; i++) {\r
- data[i] = cstr[i];\r
- }\r
-\r
- data[len] = 0; // Always null terminate the string\r
-\r
- str->length = len;\r
- str->capacity = len;\r
- return str + 1;\r
-}\r
-\r
-b32 bh_string_delete(bh_string** str) {\r
- bh__string* strptr = bh__stringhead(*str);\r
- free(strptr);\r
- str->length = 0;\r
- str->capacity = 0;\r
- return 1;\r
-}\r
-\r
-b32 bh_string_grow(bh_string** str, u64 cap) {\r
- bh__string* strptr = bh__stringhead(*str);\r
- if (strptr->capacity >= cap) return 1;\r
-\r
- void* p;\r
- p = realloc(strptr, sizeof(*strptr) + sizeof(char) * cap + 1);\r
-\r
- strptr->capacity = cap;\r
-\r
- return 1;\r
-}\r
-\r
-void bh_string_append_bh_string(bh_string** str1, bh_string** str2) {\r
- if (!bh_string_ensure_capacity(str1, str1->length + str2->length)) return;\r
-\r
- //TODO: Replace with custom memory management\r
- memcpy(str1->data + str1->length, str2->data, str2->length);\r
- str1->length += str2->length;\r
-}\r
-\r
-void bh_string_append_cstr(bh_string* str1, const char* str2) {\r
- const i32 str2len = strlen(str2);\r
- if (!bh_string_ensure_capacity(str1, str1->length + str2len)) return;\r
-\r
- //TODO: Replace with custom memory management\r
- memcpy(str1->data + str1->length, str2, str2len);\r
- str1->length += str2len;\r
-}\r
-\r
-void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {\r
- if (offset > dest->length) return;\r
- if (!bh_string_ensure_capacity(dest, offset + src->length)) return;\r
-\r
- memcpy(dest->data + offset, src->data, src->length);\r
- if (offset + src->length > dest->length)\r
- dest->length = offset + src->length;\r
-}\r
-\r
-void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset) {\r
- if (offset > dest->length) return;\r
- const i32 srclen = strlen(src);\r
- if (!bh_string_ensure_capacity(dest, offset + srclen)) return;\r
-\r
- memcpy(dest->data + offset, src, srclen);\r
- if (offset + srclen > dest->length)\r
- dest->length = offset + srclen;\r
-}\r
-\r
-void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {\r
- if (!bh_string_ensure_capacity(dest, dest->length + src->length)) return;\r
-\r
- memmove(dest->data + offset + src->length, dest->data + offset, dest->length + src->length - offset);\r
- memcpy(dest->data + offset, src->data, src->length);\r
- dest->length += src->length;\r
-}\r
-\r
-void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset) {\r
- const i32 srclen = strlen(src);\r
- if (!bh_string_ensure_capacity(dest, dest->length + srclen)) return;\r
-\r
- // TODO: Use something better. This copies to a seperate buffer first\r
- memmove(dest->data + offset + srclen, dest->data + offset, dest->length + srclen - offset);\r
- memcpy(dest->data + offset, src, srclen);\r
- dest->length += srclen;\r
-}\r
-\r
-\r
-void bh_string_trim_end(bh_string* str, const char* charset) {\r
- while (charset_contains(charset, str->data[str->length - 1]))\r
- str->length--;\r
-}\r
-\r
-void bh_string_trim_begin(bh_string* str, const char* charset) {\r
- u32 off = 0, i;\r
- while (charset_contains(charset, str->data[off])) off++;\r
-\r
- if (off == 0) return;\r
-\r
- for (i = 0; i < str->length - off; i++) {\r
- str->data[i] = str->data[i + off];\r
- }\r
-\r
- str->length -= off;\r
-}\r
-\r
-void bh_string_trim_end_space(bh_string* str) {\r
- bh_string_trim_end(str, " \t\n\r");\r
-}\r
-\r
-// TEMP\r
-void bh_string_print(bh_string* str) {\r
- write(STDOUT_FILENO, str->data, str->length);\r
-}\r
-\r
-#endif // ifndef BH_NO_STRING\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// FILE IMPLEMENTATION\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_FILE\r
-\r
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {\r
- i32 sd_fd = -1;\r
- const char* filename = NULL;\r
-\r
- switch (stand) {\r
- case BH_FILE_STANDARD_INPUT:\r
- sd_fd = STDIN_FILENO;\r
- filename = "stdin"; // These are constants in the data section so everything should be okay\r
- break;\r
- case BH_FILE_STANDARD_OUTPUT:\r
- sd_fd = STDOUT_FILENO;\r
- filename = "stdout";\r
- break;\r
- case BH_FILE_STANDARD_ERROR:\r
- sd_fd = STDERR_FILENO;\r
- filename = "stderr";\r
- break;\r
- default:\r
- return BH_FILE_ERROR_BAD_FD;\r
- }\r
-\r
- file->fd = sd_fd;\r
- file->filename = filename;\r
-\r
- return BH_FILE_ERROR_NONE;\r
-}\r
-\r
-bh_file_error bh_file_create(bh_file* file, const char* filename) {\r
- // Need to do this to avoid compiler complaining about types\r
- bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);\r
- return bh_file_open_mode(file, write_rw, filename);\r
-}\r
-\r
-bh_file_error bh_file_open(bh_file* file, const char* filename) {\r
- return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);\r
-}\r
-\r
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {\r
-\r
- i32 os_mode = 0;\r
-\r
- switch (mode & BH_FILE_MODE_MODES) {\r
- case BH_FILE_MODE_READ: os_mode = O_RDONLY; break;\r
- case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;\r
- case BH_FILE_MODE_APPEND: os_mode = O_RDONLY | O_APPEND | O_CREAT; break;\r
- case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break;\r
- case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break;\r
- case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break;\r
- //default: // TODO Handle errors\r
- }\r
-\r
- file->fd = open(filename, os_mode,\r
- S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-\r
- );\r
- if (file->fd < 0) {\r
- return BH_FILE_ERROR_INVALID;\r
- }\r
-\r
- // TODO: Set this using some allocator\r
- file->filename = filename;\r
-\r
- return BH_FILE_ERROR_NONE;\r
-}\r
-\r
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {\r
- file->filename = filename; // This may be unsafe\r
- file->fd = fd;\r
- return BH_FILE_ERROR_NONE;\r
-}\r
-\r
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {\r
- isize res = pread(file->fd, buffer, buff_size, offset);\r
- if (res < 0) return 0;\r
- if (bytes_read) *bytes_read = res;\r
- return 1;\r
-}\r
-\r
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {\r
- isize res;\r
- i64 current_offset = 0;\r
- bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_CURRENT, ¤t_offset);\r
- if (current_offset == offset) {\r
- // Standard in and out do like pwrite()\r
- res = write(file->fd, buffer, buff_size);\r
- } else {\r
- res = pwrite(file->fd, buffer, buff_size, offset);\r
- }\r
- if (res < 0) return 0;\r
- if (bytes_wrote) *bytes_wrote = res;\r
-\r
- return 1;\r
-}\r
-\r
-static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset) {\r
- i64 res = lseek(fd, offset, whence);\r
- if (res < 0) return 0;\r
- if (new_offset) *new_offset = res;\r
- return 1;\r
-}\r
-\r
-// Returns new offset\r
-i64 bh_file_seek_to(bh_file* file, i64 offset) {\r
- i64 new_offset = -1;\r
- bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);\r
- return new_offset;\r
-}\r
-\r
-i64 bh_file_seek_to_end(bh_file* file) {\r
- i64 new_offset = -1;\r
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);\r
- return new_offset;\r
-}\r
-\r
-i64 bh_file_skip(bh_file* file, i64 bytes) {\r
- i64 new_offset = 0;\r
- bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);\r
- return new_offset;\r
-}\r
-\r
-i64 bh_file_tell(bh_file* file) {\r
- i64 new_offset = 0;\r
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);\r
- return new_offset;\r
-}\r
-\r
-bh_file_error bh_file_close(bh_file* file) {\r
- bh_file_error err = BH_FILE_ERROR_NONE;\r
- i32 res = close(file->fd);\r
- if (res < 0)\r
- err = BH_FILE_ERROR_INVALID;\r
-\r
- return err;\r
-}\r
-\r
-b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {\r
- return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);\r
-}\r
-\r
-b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {\r
- return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);\r
-}\r
-\r
-i64 bh_file_size(bh_file* file) {\r
- i64 size = 0;\r
- i64 prev = bh_file_tell(file);\r
- bh_file_seek_to_end(file);\r
- size = bh_file_tell(file);\r
- bh_file_seek_to(file, prev);\r
- return size;\r
-}\r
-\r
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {\r
- bh_file_contents fc = {\r
- .allocator = alloc,\r
- .filename = file->filename,\r
- .length = 0, .data = NULL\r
- };\r
-\r
- isize size = bh_file_size(file);\r
- if (size <= 0) return fc;\r
-\r
- fc.data = bh_alloc(alloc, size + 1);\r
- fc.length = size;\r
- bh_file_read_at(file, 0, fc.data, fc.length, NULL);\r
- ((u8*) fc.data)[fc.length] = '\0';\r
-\r
- return fc;\r
-}\r
-\r
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {\r
- bh_file file;\r
- bh_file_open(&file, filename);\r
- bh_file_contents fc = bh_file_read_contents(alloc, &file);\r
- bh_file_close(&file);\r
- return fc;\r
-}\r
-\r
-b32 bh_file_contents_delete(bh_file_contents* contents) {\r
- bh_free(contents->allocator, contents->data);\r
- contents->length = 0;\r
- return 1;\r
-}\r
-\r
-#endif // ifndef BH_NO_FILE\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// ALTERNATE PRINTF IMPLEMENTATION\r
-//-------------------------------------------------------------------------------------\r
-isize bh_printf(char const *fmt, ...) {\r
- isize res;\r
- va_list va;\r
- va_start(va, fmt);\r
- res = bh_printf_va(fmt, va);\r
- va_end(va);\r
- return res;\r
-}\r
-\r
-isize bh_printf_va(char const *fmt, va_list va) {\r
- bh_file file;\r
- bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);\r
- return bh_fprintf_va(&file, fmt, va);\r
-}\r
-\r
-isize bh_printf_err(char const *fmt, ...) {\r
- isize res;\r
- va_list va;\r
- va_start(va, fmt);\r
- res = bh_printf_err_va(fmt, va);\r
- va_end(va);\r
- return res;\r
-}\r
-\r
-isize bh_printf_err_va(char const *fmt, va_list va) {\r
- bh_file file;\r
- bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);\r
- return bh_fprintf_va(&file, fmt, va);\r
-}\r
-\r
-isize bh_fprintf(bh_file* f, char const *fmt, ...) {\r
- isize res;\r
- va_list va;\r
- va_start(va, fmt);\r
- res = bh_fprintf_va(f, fmt, va);\r
- va_end(va);\r
- return res;\r
-}\r
-\r
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {\r
- static char buf[4096];\r
- isize len = bh_snprintf_va(buf, sizeof(buf), fmt, va);\r
- bh_file_write(f, buf, len - 1);\r
- return len;\r
-}\r
-\r
-char* bh_bprintf(char const *fmt, ...) {\r
- char* res;\r
- va_list va;\r
- va_start(va, fmt);\r
- res = bh_bprintf_va(fmt, va);\r
- va_end(va);\r
- return res;\r
-}\r
-\r
-char* bh_bprintf_va(char const *fmt, va_list va) {\r
- static char buffer[4096];\r
- bh_snprintf_va(buffer, sizeof(buffer), fmt, va);\r
- return buffer;\r
-}\r
-\r
-isize bh_snprintf(char *str, isize n, char const *fmt, ...) {\r
- isize res;\r
- va_list va;\r
- va_start(va, fmt);\r
- res = bh_snprintf_va(str, n, fmt, va);\r
- va_end(va);\r
- return res;\r
-}\r
-\r
-isize bh__print_string(char* dest, isize n, char* src) {\r
- isize len = 0;\r
- while (n-- && (*dest++ = *src++)) len++;\r
- return len;\r
-}\r
-\r
-isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {\r
- char buf[128];\r
- buf[127] = 0;\r
- char* walker = buf + 127;\r
- u32 base = format.base ? format.base : 10, tmp;\r
-\r
- while (value > 0) {\r
- tmp = value % base;\r
- if (tmp > 9) {\r
- switch (tmp) {\r
- case 10: tmp = 'a'; break;\r
- case 11: tmp = 'b'; break;\r
- case 12: tmp = 'c'; break;\r
- case 13: tmp = 'd'; break;\r
- case 14: tmp = 'e'; break;\r
- case 15: tmp = 'f'; break;\r
- }\r
- } else {\r
- tmp += '0';\r
- }\r
-\r
- *--walker = tmp;\r
- value /= base;\r
- }\r
-\r
- if (format.base == 16) {\r
- *--walker = 'x';\r
- *--walker = '0';\r
- }\r
-\r
- return bh__print_string(str, n, walker);\r
-}\r
-\r
-isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {\r
- char buf[128];\r
- buf[127] = 0;\r
- char* walker = buf + 127;\r
- u32 base = format.base ? format.base : 10, tmp;\r
-\r
- b32 negative = value < 0;\r
- if (negative) value = -value;\r
-\r
- if (value == 0) {\r
- *--walker = '0';\r
- } else {\r
- while (value > 0) {\r
- tmp = value % base;\r
- if (tmp > 9) {\r
- switch (tmp) {\r
- case 10: tmp = 'a'; break;\r
- case 11: tmp = 'b'; break;\r
- case 12: tmp = 'c'; break;\r
- case 13: tmp = 'd'; break;\r
- case 14: tmp = 'e'; break;\r
- case 15: tmp = 'f'; break;\r
- }\r
- } else {\r
- tmp += '0';\r
- }\r
-\r
- *--walker = tmp;\r
- value /= base;\r
- }\r
- }\r
-\r
- if (negative) {\r
- *--walker = '-';\r
- }\r
-\r
- if (format.base == 16) {\r
- *--walker = 'x';\r
- *--walker = '0';\r
- }\r
-\r
- return bh__print_string(str, n, walker);\r
-}\r
-\r
-// TODO: This is very hacked together but for now it will work.\r
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {\r
- char const *text_start = str;\r
- isize res;\r
-\r
- while (*fmt) {\r
- bh__print_format format = { 0 };\r
- isize len = 0;\r
-\r
- while (*fmt && *fmt != '%') {\r
- *(str++) = *(fmt++);\r
- }\r
-\r
- if (!*fmt) goto end_of_format;\r
-\r
- fmt++;\r
-\r
- switch (*fmt++) {\r
- case 'o': format.base = 8; break;\r
- case 'x': format.base = 16; break;\r
- default: fmt--;\r
- }\r
-\r
- switch (*fmt) {\r
- case 'c': {\r
- char c = (char) va_arg(va, int);\r
- *(str++) = c;\r
- } break;\r
-\r
- case 'd': {\r
- i64 value = (i64) va_arg(va, int);\r
- len = bh__printi64(str, n, format, value);\r
- } break;\r
-\r
- case 'l': {\r
- i64 value = (i64) va_arg(va, long);\r
- len = bh__printi64(str, n, format, value);\r
- } break;\r
-\r
- case 'p': {\r
- u64 value = (u64) va_arg(va, ptr);\r
- format.base = 16;\r
- len = bh__printu64(str, n, format, value);\r
- } break;\r
-\r
- case 's': {\r
- char* s = va_arg(va, char *);\r
- len = bh__print_string(str, n, s);\r
- } break;\r
-\r
- case 'b': { // String with a length (not null terminated)\r
- char* s = va_arg(va, char *);\r
- i32 l = va_arg(va, int);\r
- len = bh__print_string(str, bh_min(l, n), s);\r
- } break;\r
-\r
- default: fmt--;\r
- }\r
-\r
- fmt++;\r
-\r
-end_of_format:\r
- str += len;\r
- n -= len;\r
- }\r
-\r
- return str - text_start + 1;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// ARRAY IMPLEMENTATION\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_ARRAY\r
-\r
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {\r
- bh__arr* arrptr;\r
-\r
- if (*arr == NULL) {\r
- if (cap == 0 && elemsize == 0) return 1;\r
-\r
- arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);\r
- if (arrptr == NULL) return 0;\r
-\r
- arrptr->allocator = alloc;\r
- arrptr->capacity = cap;\r
- arrptr->length = 0;\r
-\r
- } else {\r
- arrptr = bh__arrhead(*arr);\r
-\r
- if (arrptr->capacity < cap) {\r
- void* p;\r
- i32 newcap = arrptr->capacity;\r
- while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);\r
-\r
- p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);\r
-\r
- if (p) {\r
- arrptr = (bh__arr *) p;\r
- arrptr->capacity = newcap;\r
- } else {\r
- return 0;\r
- }\r
- }\r
- }\r
-\r
- *arr = arrptr + 1;\r
- return 1;\r
-}\r
-\r
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {\r
- if (*arr == NULL) return 0;\r
-\r
- bh__arr* arrptr = bh__arrhead(*arr);\r
- cap = bh_max(cap, arrptr->length);\r
-\r
- if (arrptr->capacity > cap) {\r
- void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);\r
-\r
- if (p) {\r
- arrptr = (bh__arr *) p;\r
- arrptr->capacity = cap;\r
- } else {\r
- return 0;\r
- }\r
- }\r
-\r
- *arr = arrptr + 1;\r
- return 1;\r
-}\r
-\r
-b32 bh__arr_free(void **arr) {\r
- bh__arr* arrptr = bh__arrhead(*arr);\r
- bh_free(arrptr->allocator, arrptr);\r
- *arr = NULL;\r
-}\r
-\r
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {\r
- bh__arr* arrptr = bh__arrhead(arr);\r
-\r
- const i32 cap = arrptr->length;\r
-\r
- void* newarr = NULL;\r
- bh__arr_grow(alloc, &newarr, elemsize, cap);\r
- bh__arrhead(newarr)->length = cap;\r
- bh__arrhead(newarr)->capacity = cap;\r
- memcpy(newarr, arr, elemsize * arrptr->length);\r
-\r
- return newarr;\r
-}\r
-\r
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {\r
- bh__arr* arrptr = bh__arrhead(*arr);\r
-\r
- if (index >= arrptr->length) return; // Can't delete past the end of the array\r
- if (numelems <= 0) return; // Can't delete nothing\r
-\r
- memmove(\r
- (char *)(*arr) + elemsize * index, // Target\r
- (char *)(*arr) + elemsize * (index + numelems), // Source\r
- elemsize * (arrptr->length - (index + numelems))); // Length\r
- arrptr->length -= numelems;\r
-}\r
-\r
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {\r
- if (numelems) {\r
- if (*arr == NULL) {\r
- bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array\r
- return;\r
- }\r
-\r
- bh__arr* arrptr = bh__arrhead(*arr);\r
- if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case\r
- memmove(\r
- (char *)(*arr) + elemsize * (index + numelems),\r
- (char *)(*arr) + elemsize * index,\r
- elemsize * (arrptr->length - index));\r
- arrptr->length += numelems;\r
- }\r
-}\r
-\r
-#endif // ifndef BH_NO_ARRAY\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//-------------------------------------------------------------------------------------\r
-// HASHTABLE IMPLEMENTATION\r
-//-------------------------------------------------------------------------------------\r
-#ifndef BH_NO_HASHTABLE\r
-\r
-b32 bh__hash_init(bh_allocator allocator, bh__hash **table) {\r
- *table = bh_alloc(allocator, sizeof(bh__hash));\r
- if (*table == NULL) return 0;\r
-\r
- (*table)->allocator = allocator;\r
-\r
- for (i32 i = 0; i < BH__HASH_MODULUS; i++) {\r
- (*table)->arrs[i] = NULL;\r
- }\r
-\r
- return 1;\r
-}\r
-\r
-b32 bh__hash_free(bh__hash **table) {\r
- for (i32 i = 0; i < BH__HASH_MODULUS; i++) {\r
- if ((*table)->arrs[i] != NULL) {\r
- bh_arr_free((*table)->arrs[i]);\r
- }\r
- }\r
-\r
- bh_free((*table)->allocator, *table);\r
- *table = NULL;\r
-}\r
-\r
-// Assumes NULL terminated string for key\r
-ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key) {\r
- u64 index = bh__hash_function(key, 0);\r
-\r
- elemsize += BH__HASH_STORED_KEY_SIZE;\r
-\r
- ptr arrptr = table->arrs[index];\r
- i32 len = bh_arr_length(arrptr);\r
-\r
- while (len--) {\r
- if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) goto found_matching;\r
- arrptr = bh_pointer_add(arrptr, elemsize);\r
- }\r
-\r
- // Didn't find it in the array, make a new one\r
- arrptr = table->arrs[index];\r
- len = bh_arr_length(arrptr);\r
- bh__arr_grow(table->allocator, &arrptr, elemsize, len + 1);\r
- bh__arrhead(arrptr)->length++;\r
- table->arrs[index] = arrptr;\r
-\r
- arrptr = bh_pointer_add(arrptr, elemsize * len);\r
- strncpy(arrptr, key, BH__HASH_STORED_KEY_SIZE);\r
-\r
-found_matching:\r
- return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);\r
-}\r
-\r
-b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) {\r
- u64 index = bh__hash_function(key, 0);\r
-\r
- ptr arrptr = table->arrs[index];\r
- if (arrptr == NULL) return 0;\r
-\r
- i32 len = bh_arr_length(arrptr);\r
- i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;\r
-\r
- while (len--) {\r
- if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1;\r
- arrptr = bh_pointer_add(arrptr, stride);\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) {\r
- u64 index = bh__hash_function(key, 0);\r
-\r
- ptr arrptr = table->arrs[index];\r
- if (arrptr == NULL) return NULL;\r
-\r
- i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;\r
-\r
- i32 len = bh_arr_length(arrptr);\r
- while (len--) {\r
- if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) {\r
- return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);\r
- }\r
-\r
- arrptr = bh_pointer_add(arrptr, stride);\r
- }\r
-\r
- return NULL;\r
-}\r
-\r
-void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) {\r
- u64 index = bh__hash_function(key, 0);\r
-\r
- ptr arrptr = table->arrs[index], walker;\r
- if (arrptr == NULL) return; // Didn't exist\r
- walker = arrptr;\r
-\r
- i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;\r
- i32 i = 0;\r
-\r
- i32 len = bh_arr_length(arrptr);\r
- while (len && strncmp(key, (char *) walker, BH__HASH_STORED_KEY_SIZE) != 0) {\r
- walker = bh_pointer_add(walker, stride);\r
- i++, len--;\r
- }\r
-\r
- if (len == 0) return; // Didn't exist\r
-\r
- bh__arr_deleten((void **) &arrptr, stride, i, 1);\r
- table->arrs[index] = arrptr;\r
-}\r
-\r
-bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize) {\r
- bh_hash_iterator it = {\r
- .tab = table->arrs,\r
- .endtab = table->arrs + BH__HASH_MODULUS,\r
- .elemsize = elemsize,\r
- .entry = NULL\r
- };\r
- return it;\r
-}\r
-\r
-b32 bh_hash_iter_next(bh_hash_iterator* it) {\r
- if (it->tab == NULL) return 0;\r
-\r
- if (it->entry != NULL) {\r
- it->arrlen--;\r
- if (it->arrlen <= 0) {\r
- it->tab++;\r
- goto step_to_next;\r
- }\r
-\r
- it->entry = (bh__hash_entry *)bh_pointer_add(it->entry, BH__HASH_STORED_KEY_SIZE + it->elemsize);\r
- return 1;\r
- }\r
-\r
-step_to_next:\r
- // Step forward to find next valid\r
- while (*it->tab == NULL && it->tab != it->endtab) {\r
- it->tab++;\r
- }\r
-\r
- if (it->tab == it->endtab) return 0;\r
-\r
- it->entry = *it->tab;\r
- it->arrlen = bh_arr_length(it->entry);\r
- if (it->arrlen <= 0) {\r
- it->tab++;\r
- goto step_to_next;\r
- }\r
- return 1;\r
-}\r
-\r
-#endif // ifndef BH_NO_HASHTABLE\r
-\r
-#endif // ifdef BH_DEFINE\r
-\r
-#endif // ifndef BH_H\r
+#ifndef BH_H
+#define BH_H
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h> // TODO: Replace with needed functions
+#include <assert.h>
+
+//-------------------------------------------------------------------------------------
+// Better types
+//-------------------------------------------------------------------------------------
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long u64;
+typedef unsigned long long u128;
+typedef signed char i8;
+typedef signed short i16;
+typedef signed int i32;
+typedef signed long i64;
+typedef signed long long i128;
+typedef unsigned long isize;
+typedef i32 b32;
+typedef void* ptr;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better character functions
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a);
+b32 char_is_num(const char a);
+b32 char_is_alphanum(const char a);
+b32 char_is_whitespace(const char a);
+b32 char_in_range(const char lo, const char hi, const char a);
+char charset_contains(const char* charset, char ch);
+i64 chars_match(char* ptr1, char* ptr2);
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better math functions
+//-------------------------------------------------------------------------------------
+#define bh_max(a, b) ((a) > (b) ? (a) : (b))
+#define bh_min(a, b) ((a) < (b) ? (a) : (b))
+#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))
+#define bh_abs(x) ((x) < 0 ? -(x) : (x))
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Helpful macros
+//-------------------------------------------------------------------------------------
+#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem)
+#define bh_aligh_of(Type) bh_offset_of(struct { char c; Type member; }, member)
+#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
+
+#define bh_pointer_add(ptr, amm) ((void *)((u8 *) ptr + amm))
+#define BH_BIT(x) (1 << (x))
+#define BH_MASK_SET(var, set, mask) ((set) ? (var) |= (mask) : (var) &= ~(mask))
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Custom allocators
+//-------------------------------------------------------------------------------------
+
+typedef enum bh_allocator_actions {
+ bh_allocator_action_alloc,
+ bh_allocator_action_free,
+ bh_allocator_action_resize,
+} bh_allocator_actions;
+
+#define BH_ALLOCATOR_PROC(name) \
+ptr name(ptr data, bh_allocator_actions action, \
+ isize size, isize alignment, \
+ void* prev_memory, \
+ u64 flags)
+
+typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
+
+typedef struct bh_allocator {
+ bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
+ ptr data; // Pointer to the other data for the allocator
+} bh_allocator;
+
+typedef enum bh_allocator_flags {
+ bh_allocator_flag_clear = 1 // Sets all memory to be 0
+} bh_allocator_flags;
+
+ptr bh_alloc(bh_allocator a, isize size);
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
+ptr bh_resize(bh_allocator a, ptr data, isize new_size);
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
+void bh_free(bh_allocator a, ptr data);
+
+#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T))
+#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n))
+
+// NOTE: This should get optimized out since alignment should be a power of two
+#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment)
+
+
+
+
+// HEAP ALLOCATOR
+// Essentially a wrapper for malloc, free and realloc
+bh_allocator bh_heap_allocator(void);
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
+
+
+
+
+
+// ARENA ALLOCATOR
+typedef struct bh_arena {
+ bh_allocator backing;
+ ptr first_arena, current_arena;
+ isize size, arena_size; // in bytes
+} bh_arena;
+
+typedef struct bh__arena_internal {
+ ptr next_arena;
+ void* data; // Not actually a pointer, just used for the offset
+} bh__arena_internal;
+
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
+void bh_arena_free(bh_arena* alloc);
+bh_allocator bh_arena_allocator(bh_arena* alloc);
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+
+
+
+// SCRATCH ALLOCATOR
+typedef struct bh_scratch {
+ bh_allocator backing;
+ ptr memory, end, curr;
+} bh_scratch;
+
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
+void bh_scratch_free(bh_scratch* scratch);
+bh_allocator bh_scratch_allocator(bh_scratch* scratch);
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better strings
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_STRING
+
+typedef struct bh__string {
+ u64 length;
+ u64 capacity;
+} bh__string;
+
+typedef char bh_string;
+
+#define bh__stringhead(x) (((bh__string *)(x)) - 1)
+
+#define bh_string_new(x) _Generic((x), \
+ unsigned long: bh_string_new_cap, \
+ unsigned int: bh_string_new_cap, \
+ int: bh_string_new_cap, \
+ long: bh_string_new_cap, \
+ const char*: bh_string_new_str, \
+ char*: bh_string_new_str)(x)
+
+#define bh_string_append(str1, str2) _Generic((str2), \
+ bh_string*: bh_string_append_bh_string, \
+ char*: bh_string_append_cstr, \
+ const char*: bh_string_append_cstr)(str1, str2)
+
+#define bh_string_replace_at(dest, src, offset) _Generic((src), \
+ bh_string*: bh_string_replace_at_bh_string, \
+ char*: bh_string_replace_at_cstr, \
+ const char*: bh_string_replace_at_cstr)(dest, src, offset)
+
+#define bh_string_insert_at(dest, src, offset) _Generic((src), \
+ bh_string*: bh_string_insert_at_bh_string, \
+ char*: bh_string_insert_at_cstr, \
+ const char*: bh_string_insert_at_cstr)(dest, src, offset)
+
+bh_string bh_string_new_cap(unsigned long cap);
+bh_string bh_string_new_str(const char* cstr);
+b32 bh_string_delete(bh_string* str);
+b32 bh_string_ensure_capacity(bh_string* str, u64 cap);
+void bh_string_append_bh_string(bh_string* str1, bh_string* str2);
+void bh_string_append_cstr(bh_string* str1, const char* str2);
+void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset);
+void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset);
+void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset);
+void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset);
+void bh_string_trim_end(bh_string* str, const char* charset);
+void bh_string_trim_begin(bh_string* str, const char* charset);
+void bh_string_trim_end_space(bh_string* str);
+// TEMP
+void bh_string_print(bh_string* str);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better files
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+typedef enum bh_file_error {
+ BH_FILE_ERROR_NONE,
+ BH_FILE_ERROR_INVALID,
+ BH_FILE_ERROR_BAD_FD,
+} bh_file_error;
+
+typedef enum bh_file_mode {
+ BH_FILE_MODE_READ = 1 << 0,
+ BH_FILE_MODE_WRITE = 1 << 1,
+ BH_FILE_MODE_APPEND = 1 << 2,
+ BH_FILE_MODE_RW = 1 << 3,
+
+ BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
+} bh_file_mode;
+
+typedef enum bh_file_whence {
+ BH_FILE_WHENCE_BEGIN = SEEK_SET,
+ BH_FILE_WHENCE_CURRENT = SEEK_CUR,
+ BH_FILE_WHENCE_END = SEEK_END,
+} bh_file_whence;
+
+typedef int bh_file_descriptor;
+
+typedef struct bh_file {
+ bh_file_descriptor fd;
+ char const* filename;
+} bh_file;
+
+typedef enum bh_file_standard {
+ BH_FILE_STANDARD_INPUT,
+ BH_FILE_STANDARD_OUTPUT,
+ BH_FILE_STANDARD_ERROR
+} bh_file_standard;
+
+typedef struct bh_file_contents {
+ bh_allocator allocator;
+ const char *filename;
+ isize length;
+ void* data;
+} bh_file_contents;
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
+
+bh_file_error bh_file_create(bh_file* file, char const* filename);
+bh_file_error bh_file_open(bh_file* file, char const* filename);
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
+static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset);
+i64 bh_file_seek_to_end(bh_file* file);
+i64 bh_file_skip(bh_file* file, i64 bytes);
+i64 bh_file_tell(bh_file* file);
+bh_file_error bh_file_close(bh_file* file);
+i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
+i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
+i64 bh_file_size(bh_file* file);
+
+#define bh_file_read_contents(allocator_, x) _Generic((x), \
+ bh_file*: bh_file_read_contents_bh_file, \
+ const char*: bh_file_read_contents_direct, \
+ char*: bh_file_read_contents_direct)((allocator_), x)
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
+i32 bh_file_contents_delete(bh_file_contents* contents);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Alternate printing
+//-------------------------------------------------------------------------------------
+// Barebones implementation of printf. Does not support all format options
+// Currently supports:
+// %c - chars
+// %_(u)d - ints where _ is:
+// nothing - decimal
+// o - octal
+// x - hexadecimal
+// %_(u)l - longs where _ is:
+// nothing - decimal
+// o - octal
+// x - hexadecimal
+// %f - floating points
+// %s - null terminated strings
+// %p - pointers
+// %% - literal %
+
+typedef struct bh__print_format {
+ u32 base;
+} bh__print_format;
+
+isize bh_printf(char const *fmt, ...);
+isize bh_printf_va(char const *fmt, va_list va);
+isize bh_printf_err(char const *fmt, ...);
+isize bh_printf_err_va(char const *fmt, va_list va);
+isize bh_fprintf(bh_file* f, char const *fmt, ...);
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
+char* bh_bprintf(char const *fmt, ...);
+char* bh_bprintf_va(char const *fmt, va_list va);
+isize bh_snprintf(char *str, isize n, char const *fmt, ...);
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better debug functions
+//-------------------------------------------------------------------------------------
+#ifdef BH_DEBUG
+
+void* bh__debug_malloc(size_t size, const char* file, u64 line);
+void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line);
+void bh__debug_free(void* ptr, const char* file, u64 line);
+void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line);
+
+#ifdef BH_DEFINE
+
+void* bh__debug_malloc(size_t size, const char* file, u64 line) {
+ void* p = malloc(size);
+ bh_printf("[DEBUG] %p = malloc(%d) at %s:%d\n", p, size, file, line);
+ return p;
+}
+
+void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line) {
+ void* p = aligned_alloc(size, alignment);
+ bh_printf("[DEBUG] %p = aligned_alloc(%d, %d) at %s:%d\n", p, alignment, size, file, line);
+ return p;
+}
+
+void bh__debug_free(void* ptr, const char* file, u64 line) {
+ bh_printf("[DEBUG] free(%p) at %s:%d\n", ptr, file, line);
+ free(ptr);
+}
+
+void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line) {
+ void* p = realloc(ptr, size);
+ bh_printf("[DEBUG] %p = realloc(%p, %d) at %s:%d\n", p, ptr, size, file, line);
+ return p;
+}
+
+#endif
+
+#define malloc(size) (bh__debug_malloc(size, __FILE__, __LINE__))
+#define aligned_alloc(size, alignment) (bh__debug_aligned_alloc(size, alignment, __FILE__, __LINE__))
+#define free(ptr) (bh__debug_free(ptr, __FILE__, __LINE__))
+#define realloc(ptr, size) (bh__debug_realloc(ptr, size, __FILE__, __LINE__))
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better dynamically-sized arrays
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+typedef struct bh__arr {
+ bh_allocator allocator;
+ i32 length, capacity;
+} bh__arr;
+
+#ifndef BH_ARR_GROW_FORMULA
+#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4)
+#endif
+
+#define bh_arr(T) T*
+#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1)
+
+#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
+#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0)
+#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0)
+#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
+#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
+
+#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length])
+#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1])
+#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)])
+
+#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_free(arr) (bh__arr_free((void**) &(arr)))
+#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
+
+#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_set_length(arr, n) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), n), \
+ bh__arrhead(arr)->length = n)
+
+#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
+
+#define bh_arr_insert_end(arr, n) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
+ bh__arrhead(arr)->length += n)
+
+#define bh_arr_push(arr, value) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1), \
+ arr[bh__arrhead(arr)->length++] = value)
+
+#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1)
+#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0)
+
+#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
+#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length])
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_free(void **arr);
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// HASH TABLE FUNCTIONS
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_HASHTABLE
+
+#define BH__HASH_STORED_KEY_SIZE 64
+typedef struct bh__hash_entry {
+ char key[BH__HASH_STORED_KEY_SIZE];
+ i32 value; // NOTE: Not actually an i32, just used as a placeholder for offset
+} bh__hash_entry;
+
+#define BH__HASH_MODULUS 1021
+#define BH__HASH_KEYSIZE 64
+#ifdef BH_DEFINE
+u64 bh__hash_function(const char* str, i32 len) {
+ u64 hash = 5381;
+ i32 c, l = 0;
+ if (len == 0) len = BH__HASH_KEYSIZE;
+
+ while ((c = *str++) && l++ < len) {
+ hash = (hash << 5) + hash + c;
+ }
+
+ return hash % BH__HASH_MODULUS;
+}
+#endif
+
+typedef struct bh_hash_iterator {
+ ptr *tab, *endtab;
+ i32 elemsize, arrlen;
+ bh__hash_entry* entry;
+} bh_hash_iterator;
+
+typedef struct bh__hash {
+ bh_allocator allocator;
+ ptr arrs[BH__HASH_MODULUS];
+} bh__hash;
+
+#define bh_hash(T) T*
+
+#ifdef BH_HASH_SIZE_SAFE
+ #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))
+ #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))
+ #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = (T) value))
+ #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((bh__hash *) tab, sizeof(T), key)))
+ #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key))))
+ #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((bh__hash *) tab, sizeof(T), key))
+
+ #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))
+ #define bh_hash_iter_key(it) (it.entry->key)
+ #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value))
+#else
+ #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))
+ #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))
+ #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = value)
+ #define bh_hash_has(T, tab, key) (bh__hash_has((bh__hash *) tab, sizeof(T), key))
+ #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key)))
+ #define bh_hash_delete(T, tab, key) (bh__hash_delete((bh__hash *) tab, sizeof(T), key))
+
+ #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))
+ #define bh_hash_iter_key(it) (it.entry->key)
+ #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value))
+#endif
+
+b32 bh__hash_init(bh_allocator allocator, bh__hash **table);
+b32 bh__hash_free(bh__hash **table);
+ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key);
+b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key);
+ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key);
+void bh__hash_delete(bh__hash *table, i32 elemsize, char *key);
+bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize);
+b32 bh_hash_iter_next(bh_hash_iterator* it);
+
+#endif
+
+#ifdef BH_DEFINE
+#undef BH_DEFINE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// IMPLEMENTATIONS
+//-------------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------
+// CHAR FUNCTIONS
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a) {
+ return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
+}
+
+b32 char_is_num(const char a) {
+ return ('0' <= a && a <= '9');
+}
+
+b32 char_is_alphanum(const char a) {
+ return char_is_alpha(a) || char_is_num(a);
+}
+
+b32 char_is_whitespace(const char a) {
+ return charset_contains(" \t\r\n", a);
+}
+
+b32 char_in_range(const char lo, const char hi, const char a) {
+ return lo <= a <= hi;
+}
+
+char charset_contains(const char* charset, char ch) {
+ while (*charset) {
+ if (*charset == ch) return ch;
+ charset++;
+ }
+
+ return 0;
+}
+
+i64 chars_match(char* ptr1, char* ptr2) {
+ i64 len = 0;
+ while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+ return *ptr2 == '\0' ? len : 0;
+}
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CUSTOM ALLOCATORS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+
+
+ptr bh_alloc(bh_allocator a, isize size) {
+ return bh_alloc_aligned(a, size, 16);
+}
+
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
+ return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0);
+}
+
+ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
+ return bh_resize_aligned(a, data, new_size, 16);
+}
+
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
+ return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
+}
+
+void bh_free(bh_allocator a, ptr data) {
+ if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
+}
+
+
+
+// HEAP ALLOCATOR IMPLEMENTATION
+
+bh_allocator bh_heap_allocator(void) {
+ return (bh_allocator) {
+ .proc = bh_heap_allocator_proc,
+ .data = NULL
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+ retval = aligned_alloc(alignment, size);
+
+ if (flags & bh_allocator_flag_clear && retval != NULL) {
+ memset(retval, 0, size);
+ }
+ } break;
+
+ case bh_allocator_action_resize: {
+ // TODO: Maybe replace with better custom function
+ retval = realloc(prev_memory, size);
+ } break;
+
+ case bh_allocator_action_free: {
+ free(prev_memory);
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+
+
+
+// ARENA ALLOCATOR IMPLEMENTATION
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
+ arena_size = bh_max(arena_size, sizeof(ptr));
+ ptr data = bh_alloc(backing, arena_size);
+
+ alloc->backing = backing;
+ alloc->arena_size = arena_size;
+ alloc->size = sizeof(ptr);
+ alloc->first_arena = data;
+ alloc->current_arena = data;
+
+ ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
+}
+
+void bh_arena_free(bh_arena* alloc) {
+ bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
+ bh__arena_internal *trailer = walker;
+ while (walker != NULL) {
+ walker = walker->next_arena;
+ bh_free(alloc->backing, trailer);
+ trailer = walker;
+ }
+
+ alloc->first_arena = NULL;
+ alloc->current_arena = NULL;
+ alloc->arena_size = 0;
+ alloc->size = 0;
+}
+
+bh_allocator bh_arena_allocator(bh_arena* alloc) {
+ return (bh_allocator) {
+ .proc = bh_arena_allocator_proc,
+ .data = alloc,
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
+ bh_arena* alloc_arena = (bh_arena*) data;
+
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+
+ // TODO: Do this better because right now bh__align is bad
+ // size = bh__align(size, alignment);
+ if (size > alloc_arena->arena_size - sizeof(ptr)) {
+ // Size too large for the arena
+ return NULL;
+ }
+
+ if (alloc_arena->size + size >= alloc_arena->arena_size) {
+ alloc_arena->size = sizeof(ptr);
+ bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
+
+ if (new_arena == NULL) {
+ bh_printf_err("Arena Allocator: couldn't allocate new arena");
+ return NULL;
+ }
+
+ new_arena->next_arena = NULL;
+ ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
+ alloc_arena->current_arena = new_arena;
+ }
+
+ retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
+ alloc_arena->size += size;
+ } break;
+
+ case bh_allocator_action_resize: {
+ // Do nothing since this is a fixed allocator
+ } break;
+
+ case bh_allocator_action_free: {
+ // Do nothing since this allocator isn't made for freeing memory
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+// SCRATCH ALLOCATOR IMPLEMENTATION
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
+ ptr memory = bh_alloc(backing, scratch_size);
+
+ scratch->backing = backing;
+ scratch->memory = memory;
+ scratch->curr = memory;
+ scratch->end = memory + scratch_size;
+}
+
+void bh_scratch_free(bh_scratch* scratch) {
+ bh_free(scratch->backing, scratch->memory);
+
+ scratch->memory = NULL;
+ scratch->curr = NULL;
+ scratch->end = NULL;
+}
+
+bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
+ return (bh_allocator) {
+ .proc = bh_scratch_allocator_proc,
+ .data = scratch,
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
+ bh_scratch* scratch = (bh_scratch*) data;
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+ if (size > scratch->end - scratch->memory) {
+ return NULL;
+ }
+
+ retval = scratch->curr;
+ scratch->curr += size;
+
+ if (scratch->curr >= scratch->end) {
+ scratch->curr = scratch->memory;
+ retval = scratch->curr;
+ }
+ } break;
+
+ case bh_allocator_action_free:
+ case bh_allocator_action_resize:
+ // Do nothing
+ break;
+ }
+
+ return retval;
+}
+
+
+//-------------------------------------------------------------------------------------
+// STRING IMPLEMENTATION (BROKEN)
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_STRING
+
+bh_string* bh_string_new_cap(unsigned long cap) {
+ bh__string* str;
+ str = (bh__string*) malloc(sizeof(*str) + sizeof(char) * cap + 1);
+ str[0] = 0;
+ return str + 1;
+}
+
+bh_string* bh_string_new_str(const char* cstr) {
+ const i32 len = strlen(cstr);
+ bh__string* str;
+ i32 i;
+
+ str = malloc(sizeof(*str) + sizeof(char) * len + 1);
+ char* data = (char*) (str + 1);
+ for (i = 0; i < len; i++) {
+ data[i] = cstr[i];
+ }
+
+ data[len] = 0; // Always null terminate the string
+
+ str->length = len;
+ str->capacity = len;
+ return str + 1;
+}
+
+b32 bh_string_delete(bh_string** str) {
+ bh__string* strptr = bh__stringhead(*str);
+ free(strptr);
+ str->length = 0;
+ str->capacity = 0;
+ return 1;
+}
+
+b32 bh_string_grow(bh_string** str, u64 cap) {
+ bh__string* strptr = bh__stringhead(*str);
+ if (strptr->capacity >= cap) return 1;
+
+ void* p;
+ p = realloc(strptr, sizeof(*strptr) + sizeof(char) * cap + 1);
+
+ strptr->capacity = cap;
+
+ return 1;
+}
+
+void bh_string_append_bh_string(bh_string** str1, bh_string** str2) {
+ if (!bh_string_ensure_capacity(str1, str1->length + str2->length)) return;
+
+ //TODO: Replace with custom memory management
+ memcpy(str1->data + str1->length, str2->data, str2->length);
+ str1->length += str2->length;
+}
+
+void bh_string_append_cstr(bh_string* str1, const char* str2) {
+ const i32 str2len = strlen(str2);
+ if (!bh_string_ensure_capacity(str1, str1->length + str2len)) return;
+
+ //TODO: Replace with custom memory management
+ memcpy(str1->data + str1->length, str2, str2len);
+ str1->length += str2len;
+}
+
+void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {
+ if (offset > dest->length) return;
+ if (!bh_string_ensure_capacity(dest, offset + src->length)) return;
+
+ memcpy(dest->data + offset, src->data, src->length);
+ if (offset + src->length > dest->length)
+ dest->length = offset + src->length;
+}
+
+void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset) {
+ if (offset > dest->length) return;
+ const i32 srclen = strlen(src);
+ if (!bh_string_ensure_capacity(dest, offset + srclen)) return;
+
+ memcpy(dest->data + offset, src, srclen);
+ if (offset + srclen > dest->length)
+ dest->length = offset + srclen;
+}
+
+void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {
+ if (!bh_string_ensure_capacity(dest, dest->length + src->length)) return;
+
+ memmove(dest->data + offset + src->length, dest->data + offset, dest->length + src->length - offset);
+ memcpy(dest->data + offset, src->data, src->length);
+ dest->length += src->length;
+}
+
+void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset) {
+ const i32 srclen = strlen(src);
+ if (!bh_string_ensure_capacity(dest, dest->length + srclen)) return;
+
+ // TODO: Use something better. This copies to a seperate buffer first
+ memmove(dest->data + offset + srclen, dest->data + offset, dest->length + srclen - offset);
+ memcpy(dest->data + offset, src, srclen);
+ dest->length += srclen;
+}
+
+
+void bh_string_trim_end(bh_string* str, const char* charset) {
+ while (charset_contains(charset, str->data[str->length - 1]))
+ str->length--;
+}
+
+void bh_string_trim_begin(bh_string* str, const char* charset) {
+ u32 off = 0, i;
+ while (charset_contains(charset, str->data[off])) off++;
+
+ if (off == 0) return;
+
+ for (i = 0; i < str->length - off; i++) {
+ str->data[i] = str->data[i + off];
+ }
+
+ str->length -= off;
+}
+
+void bh_string_trim_end_space(bh_string* str) {
+ bh_string_trim_end(str, " \t\n\r");
+}
+
+// TEMP
+void bh_string_print(bh_string* str) {
+ write(STDOUT_FILENO, str->data, str->length);
+}
+
+#endif // ifndef BH_NO_STRING
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FILE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
+ i32 sd_fd = -1;
+ const char* filename = NULL;
+
+ switch (stand) {
+ case BH_FILE_STANDARD_INPUT:
+ sd_fd = STDIN_FILENO;
+ filename = "stdin"; // These are constants in the data section so everything should be okay
+ break;
+ case BH_FILE_STANDARD_OUTPUT:
+ sd_fd = STDOUT_FILENO;
+ filename = "stdout";
+ break;
+ case BH_FILE_STANDARD_ERROR:
+ sd_fd = STDERR_FILENO;
+ filename = "stderr";
+ break;
+ default:
+ return BH_FILE_ERROR_BAD_FD;
+ }
+
+ file->fd = sd_fd;
+ file->filename = filename;
+
+ return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_create(bh_file* file, const char* filename) {
+ // Need to do this to avoid compiler complaining about types
+ bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
+ return bh_file_open_mode(file, write_rw, filename);
+}
+
+bh_file_error bh_file_open(bh_file* file, const char* filename) {
+ return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
+}
+
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
+
+ i32 os_mode = 0;
+
+ switch (mode & BH_FILE_MODE_MODES) {
+ case BH_FILE_MODE_READ: os_mode = O_RDONLY; break;
+ case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
+ case BH_FILE_MODE_APPEND: os_mode = O_RDONLY | O_APPEND | O_CREAT; break;
+ case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break;
+ case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
+ case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break;
+ //default: // TODO Handle errors
+ }
+
+ file->fd = open(filename, os_mode,
+ S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
+ );
+ if (file->fd < 0) {
+ return BH_FILE_ERROR_INVALID;
+ }
+
+ // TODO: Set this using some allocator
+ file->filename = filename;
+
+ return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
+ file->filename = filename; // This may be unsafe
+ file->fd = fd;
+ return BH_FILE_ERROR_NONE;
+}
+
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
+ isize res = pread(file->fd, buffer, buff_size, offset);
+ if (res < 0) return 0;
+ if (bytes_read) *bytes_read = res;
+ return 1;
+}
+
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
+ isize res;
+ i64 current_offset = 0;
+ bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_CURRENT, ¤t_offset);
+ if (current_offset == offset) {
+ // Standard in and out do like pwrite()
+ res = write(file->fd, buffer, buff_size);
+ } else {
+ res = pwrite(file->fd, buffer, buff_size, offset);
+ }
+ if (res < 0) return 0;
+ if (bytes_wrote) *bytes_wrote = res;
+
+ return 1;
+}
+
+static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset) {
+ i64 res = lseek(fd, offset, whence);
+ if (res < 0) return 0;
+ if (new_offset) *new_offset = res;
+ return 1;
+}
+
+// Returns new offset
+i64 bh_file_seek_to(bh_file* file, i64 offset) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_seek_to_end(bh_file* file) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_skip(bh_file* file, i64 bytes) {
+ i64 new_offset = 0;
+ bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_tell(bh_file* file) {
+ i64 new_offset = 0;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
+ return new_offset;
+}
+
+bh_file_error bh_file_close(bh_file* file) {
+ bh_file_error err = BH_FILE_ERROR_NONE;
+ i32 res = close(file->fd);
+ if (res < 0)
+ err = BH_FILE_ERROR_INVALID;
+
+ return err;
+}
+
+b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
+ return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
+ return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+i64 bh_file_size(bh_file* file) {
+ i64 size = 0;
+ i64 prev = bh_file_tell(file);
+ bh_file_seek_to_end(file);
+ size = bh_file_tell(file);
+ bh_file_seek_to(file, prev);
+ return size;
+}
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
+ bh_file_contents fc = {
+ .allocator = alloc,
+ .filename = file->filename,
+ .length = 0, .data = NULL
+ };
+
+ isize size = bh_file_size(file);
+ if (size <= 0) return fc;
+
+ fc.data = bh_alloc(alloc, size + 1);
+ fc.length = size;
+ bh_file_read_at(file, 0, fc.data, fc.length, NULL);
+ ((u8*) fc.data)[fc.length] = '\0';
+
+ return fc;
+}
+
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
+ bh_file file;
+ bh_file_open(&file, filename);
+ bh_file_contents fc = bh_file_read_contents(alloc, &file);
+ bh_file_close(&file);
+ return fc;
+}
+
+b32 bh_file_contents_delete(bh_file_contents* contents) {
+ bh_free(contents->allocator, contents->data);
+ contents->length = 0;
+ return 1;
+}
+
+#endif // ifndef BH_NO_FILE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ALTERNATE PRINTF IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+isize bh_printf(char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_printf_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_printf_va(char const *fmt, va_list va) {
+ bh_file file;
+ bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
+ return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_printf_err(char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_printf_err_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_printf_err_va(char const *fmt, va_list va) {
+ bh_file file;
+ bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
+ return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_fprintf(bh_file* f, char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_fprintf_va(f, fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
+ static char buf[4096];
+ isize len = bh_snprintf_va(buf, sizeof(buf), fmt, va);
+ bh_file_write(f, buf, len - 1);
+ return len;
+}
+
+char* bh_bprintf(char const *fmt, ...) {
+ char* res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_bprintf_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+char* bh_bprintf_va(char const *fmt, va_list va) {
+ static char buffer[4096];
+ bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+ return buffer;
+}
+
+isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_snprintf_va(str, n, fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh__print_string(char* dest, isize n, char* src) {
+ isize len = 0;
+ while (n-- && (*dest++ = *src++)) len++;
+ return len;
+}
+
+isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
+ char buf[128];
+ buf[127] = 0;
+ char* walker = buf + 127;
+ u32 base = format.base ? format.base : 10, tmp;
+
+ while (value > 0) {
+ tmp = value % base;
+ if (tmp > 9) {
+ switch (tmp) {
+ case 10: tmp = 'a'; break;
+ case 11: tmp = 'b'; break;
+ case 12: tmp = 'c'; break;
+ case 13: tmp = 'd'; break;
+ case 14: tmp = 'e'; break;
+ case 15: tmp = 'f'; break;
+ }
+ } else {
+ tmp += '0';
+ }
+
+ *--walker = tmp;
+ value /= base;
+ }
+
+ if (format.base == 16) {
+ *--walker = 'x';
+ *--walker = '0';
+ }
+
+ return bh__print_string(str, n, walker);
+}
+
+isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
+ char buf[128];
+ buf[127] = 0;
+ char* walker = buf + 127;
+ u32 base = format.base ? format.base : 10, tmp;
+
+ b32 negative = value < 0;
+ if (negative) value = -value;
+
+ if (value == 0) {
+ *--walker = '0';
+ } else {
+ while (value > 0) {
+ tmp = value % base;
+ if (tmp > 9) {
+ switch (tmp) {
+ case 10: tmp = 'a'; break;
+ case 11: tmp = 'b'; break;
+ case 12: tmp = 'c'; break;
+ case 13: tmp = 'd'; break;
+ case 14: tmp = 'e'; break;
+ case 15: tmp = 'f'; break;
+ }
+ } else {
+ tmp += '0';
+ }
+
+ *--walker = tmp;
+ value /= base;
+ }
+ }
+
+ if (negative) {
+ *--walker = '-';
+ }
+
+ if (format.base == 16) {
+ *--walker = 'x';
+ *--walker = '0';
+ }
+
+ return bh__print_string(str, n, walker);
+}
+
+// TODO: This is very hacked together but for now it will work.
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
+ char const *text_start = str;
+ isize res;
+
+ while (*fmt) {
+ bh__print_format format = { 0 };
+ isize len = 0;
+
+ while (*fmt && *fmt != '%') {
+ *(str++) = *(fmt++);
+ }
+
+ if (!*fmt) goto end_of_format;
+
+ fmt++;
+
+ switch (*fmt++) {
+ case 'o': format.base = 8; break;
+ case 'x': format.base = 16; break;
+ default: fmt--;
+ }
+
+ switch (*fmt) {
+ case 'c': {
+ char c = (char) va_arg(va, int);
+ *(str++) = c;
+ } break;
+
+ case 'd': {
+ i64 value = (i64) va_arg(va, int);
+ len = bh__printi64(str, n, format, value);
+ } break;
+
+ case 'l': {
+ i64 value = (i64) va_arg(va, long);
+ len = bh__printi64(str, n, format, value);
+ } break;
+
+ case 'p': {
+ u64 value = (u64) va_arg(va, ptr);
+ format.base = 16;
+ len = bh__printu64(str, n, format, value);
+ } break;
+
+ case 's': {
+ char* s = va_arg(va, char *);
+ len = bh__print_string(str, n, s);
+ } break;
+
+ case 'b': { // String with a length (not null terminated)
+ char* s = va_arg(va, char *);
+ i32 l = va_arg(va, int);
+ len = bh__print_string(str, bh_min(l, n), s);
+ } break;
+
+ default: fmt--;
+ }
+
+ fmt++;
+
+end_of_format:
+ str += len;
+ n -= len;
+ }
+
+ return str - text_start + 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ARRAY IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
+ bh__arr* arrptr;
+
+ if (*arr == NULL) {
+ if (cap == 0 && elemsize == 0) return 1;
+
+ arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
+ if (arrptr == NULL) return 0;
+
+ arrptr->allocator = alloc;
+ arrptr->capacity = cap;
+ arrptr->length = 0;
+
+ } else {
+ arrptr = bh__arrhead(*arr);
+
+ if (arrptr->capacity < cap) {
+ void* p;
+ i32 newcap = arrptr->capacity;
+ while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
+
+ p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
+
+ if (p) {
+ arrptr = (bh__arr *) p;
+ arrptr->capacity = newcap;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ *arr = arrptr + 1;
+ return 1;
+}
+
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
+ if (*arr == NULL) return 0;
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ cap = bh_max(cap, arrptr->length);
+
+ if (arrptr->capacity > cap) {
+ void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
+
+ if (p) {
+ arrptr = (bh__arr *) p;
+ arrptr->capacity = cap;
+ } else {
+ return 0;
+ }
+ }
+
+ *arr = arrptr + 1;
+ return 1;
+}
+
+b32 bh__arr_free(void **arr) {
+ bh__arr* arrptr = bh__arrhead(*arr);
+ bh_free(arrptr->allocator, arrptr);
+ *arr = NULL;
+}
+
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
+ bh__arr* arrptr = bh__arrhead(arr);
+
+ const i32 cap = arrptr->length;
+
+ void* newarr = NULL;
+ bh__arr_grow(alloc, &newarr, elemsize, cap);
+ bh__arrhead(newarr)->length = cap;
+ bh__arrhead(newarr)->capacity = cap;
+ memcpy(newarr, arr, elemsize * arrptr->length);
+
+ return newarr;
+}
+
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
+ bh__arr* arrptr = bh__arrhead(*arr);
+
+ if (index >= arrptr->length) return; // Can't delete past the end of the array
+ if (numelems <= 0) return; // Can't delete nothing
+
+ memmove(
+ (char *)(*arr) + elemsize * index, // Target
+ (char *)(*arr) + elemsize * (index + numelems), // Source
+ elemsize * (arrptr->length - (index + numelems))); // Length
+ arrptr->length -= numelems;
+}
+
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
+ if (numelems) {
+ if (*arr == NULL) {
+ bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
+ return;
+ }
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
+ memmove(
+ (char *)(*arr) + elemsize * (index + numelems),
+ (char *)(*arr) + elemsize * index,
+ elemsize * (arrptr->length - index));
+ arrptr->length += numelems;
+ }
+}
+
+#endif // ifndef BH_NO_ARRAY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// HASHTABLE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_HASHTABLE
+
+b32 bh__hash_init(bh_allocator allocator, bh__hash **table) {
+ *table = bh_alloc(allocator, sizeof(bh__hash));
+ if (*table == NULL) return 0;
+
+ (*table)->allocator = allocator;
+
+ for (i32 i = 0; i < BH__HASH_MODULUS; i++) {
+ (*table)->arrs[i] = NULL;
+ }
+
+ return 1;
+}
+
+b32 bh__hash_free(bh__hash **table) {
+ for (i32 i = 0; i < BH__HASH_MODULUS; i++) {
+ if ((*table)->arrs[i] != NULL) {
+ bh_arr_free((*table)->arrs[i]);
+ }
+ }
+
+ bh_free((*table)->allocator, *table);
+ *table = NULL;
+}
+
+// Assumes NULL terminated string for key
+ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ elemsize += BH__HASH_STORED_KEY_SIZE;
+
+ ptr arrptr = table->arrs[index];
+ i32 len = bh_arr_length(arrptr);
+
+ while (len--) {
+ if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) goto found_matching;
+ arrptr = bh_pointer_add(arrptr, elemsize);
+ }
+
+ // Didn't find it in the array, make a new one
+ arrptr = table->arrs[index];
+ len = bh_arr_length(arrptr);
+ bh__arr_grow(table->allocator, &arrptr, elemsize, len + 1);
+ bh__arrhead(arrptr)->length++;
+ table->arrs[index] = arrptr;
+
+ arrptr = bh_pointer_add(arrptr, elemsize * len);
+ strncpy(arrptr, key, BH__HASH_STORED_KEY_SIZE);
+
+found_matching:
+ return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);
+}
+
+b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) return 0;
+
+ i32 len = bh_arr_length(arrptr);
+ i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
+
+ while (len--) {
+ if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1;
+ arrptr = bh_pointer_add(arrptr, stride);
+ }
+
+ return 0;
+}
+
+ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) return NULL;
+
+ i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
+
+ i32 len = bh_arr_length(arrptr);
+ while (len--) {
+ if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) {
+ return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);
+ }
+
+ arrptr = bh_pointer_add(arrptr, stride);
+ }
+
+ return NULL;
+}
+
+void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ ptr arrptr = table->arrs[index], walker;
+ if (arrptr == NULL) return; // Didn't exist
+ walker = arrptr;
+
+ i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
+ i32 i = 0;
+
+ i32 len = bh_arr_length(arrptr);
+ while (len && strncmp(key, (char *) walker, BH__HASH_STORED_KEY_SIZE) != 0) {
+ walker = bh_pointer_add(walker, stride);
+ i++, len--;
+ }
+
+ if (len == 0) return; // Didn't exist
+
+ bh__arr_deleten((void **) &arrptr, stride, i, 1);
+ table->arrs[index] = arrptr;
+}
+
+bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize) {
+ bh_hash_iterator it = {
+ .tab = table->arrs,
+ .endtab = table->arrs + BH__HASH_MODULUS,
+ .elemsize = elemsize,
+ .entry = NULL
+ };
+ return it;
+}
+
+b32 bh_hash_iter_next(bh_hash_iterator* it) {
+ if (it->tab == NULL) return 0;
+
+ if (it->entry != NULL) {
+ it->arrlen--;
+ if (it->arrlen <= 0) {
+ it->tab++;
+ goto step_to_next;
+ }
+
+ it->entry = (bh__hash_entry *)bh_pointer_add(it->entry, BH__HASH_STORED_KEY_SIZE + it->elemsize);
+ return 1;
+ }
+
+step_to_next:
+ // Step forward to find next valid
+ while (*it->tab == NULL && it->tab != it->endtab) {
+ it->tab++;
+ }
+
+ if (it->tab == it->endtab) return 0;
+
+ it->entry = *it->tab;
+ it->arrlen = bh_arr_length(it->entry);
+ if (it->arrlen <= 0) {
+ it->tab++;
+ goto step_to_next;
+ }
+ return 1;
+}
+
+#endif // ifndef BH_NO_HASHTABLE
+
+#endif // ifdef BH_DEFINE
+
+#endif // ifndef BH_H
-\r
-#include "onyxlex.h"\r
-#include "onyxparser.h"\r
-\r
-static const char* ast_node_names[] = {\r
- "ERROR",\r
- "PROGRAM",\r
-\r
- "FUNCDEF",\r
- "BLOCK",\r
- "SCOPE",\r
- "LOCAL",\r
-\r
- "ADD",\r
- "MINUS",\r
- "MULTIPLY",\r
- "DIVIDE",\r
- "MODULUS",\r
- "NEGATE",\r
-\r
- "TYPE",\r
- "LITERAL",\r
- "CAST",\r
- "PARAM",\r
- "CALL",\r
- "ASSIGN",\r
- "RETURN",\r
-\r
- "EQUAL",\r
- "NOT_EQUAL",\r
- "GREATER",\r
- "GREATER_EQUAL",\r
- "LESS",\r
- "LESS_EQUAL",\r
- "NOT",\r
-\r
- "IF",\r
- "LOOP",\r
-\r
- "ONYX_AST_NODE_KIND_COUNT",\r
-};\r
-\r
-struct OnyxTypeInfo builtin_types[] = {\r
- { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" },\r
- { ONYX_TYPE_INFO_KIND_VOID, 0, "void" },\r
-\r
- { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1 },\r
-\r
- { ONYX_TYPE_INFO_KIND_UINT8, 1, "u8", 1, 1, 0, 0 },\r
- { ONYX_TYPE_INFO_KIND_UINT16, 2, "u16", 1, 1, 0, 0 },\r
- { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0 },\r
- { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0 },\r
-\r
- { ONYX_TYPE_INFO_KIND_INT8, 1, "i8", 1, 0, 0, 0 },\r
- { ONYX_TYPE_INFO_KIND_INT16, 2, "i16", 1, 0, 0, 0 },\r
- { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0 },\r
- { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0 },\r
-\r
- { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0 },\r
- { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0 },\r
- { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0 },\r
-\r
- { 0xffffffff } // Sentinel\r
-};\r
-\r
-static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } };\r
-\r
-// NOTE: Forward declarations\r
-static void parser_next_token(OnyxParser* parser);\r
-static void parser_prev_token(OnyxParser* parser);\r
-static b32 is_terminating_token(OnyxTokenType token_type);\r
-static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type);\r
-static OnyxAstNodeScope* enter_scope(OnyxParser* parser);\r
-static OnyxAstNodeScope* leave_scope(OnyxParser* parser);\r
-static void insert_identifier(OnyxParser* parser, OnyxAstNodeLocal* local);\r
-static OnyxAstNode* parse_factor(OnyxParser* parser);\r
-static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left);\r
-static OnyxAstNode* parse_expression(OnyxParser* parser);\r
-static OnyxAstNode* parse_if_stmt(OnyxParser* parser);\r
-static OnyxAstNode* parse_expression_statement(OnyxParser* parser);\r
-static OnyxAstNode* parse_return_statement(OnyxParser* parser);\r
-static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function);\r
-static OnyxAstNode* parse_statement(OnyxParser* parser);\r
-static OnyxTypeInfo* parse_type(OnyxParser* parser);\r
-static OnyxAstNodeParam* parse_function_params(OnyxParser* parser);\r
-static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser);\r
-static OnyxAstNode* parse_top_level_statement(OnyxParser* parser);\r
-\r
-static void parser_next_token(OnyxParser* parser) {\r
- parser->prev_token = parser->curr_token;\r
- parser->curr_token++;\r
- while (parser->curr_token->type == TOKEN_TYPE_COMMENT) parser->curr_token++;\r
-}\r
-\r
-static void parser_prev_token(OnyxParser* parser) {\r
- // TODO: This is probably wrong\r
- parser->prev_token--;\r
- while (parser->prev_token->type == TOKEN_TYPE_COMMENT) parser->prev_token--;\r
- parser->curr_token = parser->prev_token;\r
- parser->prev_token--;\r
-}\r
-\r
-static b32 is_terminating_token(OnyxTokenType token_type) {\r
- switch (token_type) {\r
- case TOKEN_TYPE_SYM_SEMICOLON:\r
- case TOKEN_TYPE_CLOSE_BRACE:\r
- case TOKEN_TYPE_OPEN_BRACE:\r
- case TOKEN_TYPE_END_STREAM:\r
- return 1;\r
- default:\r
- return 0;\r
- }\r
-}\r
-\r
-// Advances to next token no matter what\r
-static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) {\r
- OnyxToken* token = parser->curr_token;\r
- parser_next_token(parser);\r
-\r
- if (token->type != token_type) {\r
- onyx_message_add(parser->msgs,\r
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,\r
- token->pos,\r
- onyx_get_token_type_name(token_type), onyx_get_token_type_name(token->type));\r
- return NULL;\r
- }\r
-\r
- return token;\r
-}\r
-\r
-static OnyxAstNodeScope* enter_scope(OnyxParser* parser) {\r
- OnyxAstNodeScope* scope = (OnyxAstNodeScope*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE);\r
- scope->prev_scope = parser->curr_scope;\r
- parser->curr_scope = scope;\r
- return scope;\r
-}\r
-\r
-static OnyxAstNodeScope* leave_scope(OnyxParser* parser) {\r
- // NOTE: Can't leave a scope if there is no scope\r
- assert(parser->curr_scope != NULL);\r
-\r
- for (OnyxAstNodeLocal *walker = parser->curr_scope->last_local; walker != NULL; walker = walker->prev_local) {\r
- onyx_token_null_toggle(*walker->token);\r
- bh_hash_delete(OnyxAstNode*, parser->identifiers, walker->token->token);\r
- onyx_token_null_toggle(*walker->token);\r
- }\r
-\r
- parser->curr_scope = parser->curr_scope->prev_scope;\r
- return parser->curr_scope;\r
-}\r
-\r
-static OnyxAstNode* lookup_identifier(OnyxParser* parser, OnyxToken* token) {\r
- OnyxAstNode* ident = NULL;\r
-\r
- onyx_token_null_toggle(*token);\r
- if (bh_hash_has(OnyxAstNode*, parser->identifiers, token->token)) {\r
- ident = bh_hash_get(OnyxAstNode*, parser->identifiers, token->token);\r
- }\r
- onyx_token_null_toggle(*token);\r
-\r
- return ident;\r
-}\r
-\r
-static void insert_identifier(OnyxParser* parser, OnyxAstNodeLocal* local) {\r
- OnyxAstNodeScope* scope = parser->curr_scope;\r
- local->prev_local = scope->last_local;\r
- scope->last_local = local;\r
-\r
- onyx_token_null_toggle(*local->token);\r
- bh_hash_put(OnyxAstNodeLocal*, parser->identifiers, local->token->token, local);\r
- onyx_token_null_toggle(*local->token);\r
-}\r
-\r
-static OnyxAstNode* parse_factor(OnyxParser* parser) {\r
- switch (parser->curr_token->type) {\r
- case TOKEN_TYPE_OPEN_PAREN: {\r
- parser_next_token(parser);\r
- OnyxAstNode* expr = parse_expression(parser);\r
- expect(parser, TOKEN_TYPE_CLOSE_PAREN);\r
- return expr;\r
- }\r
-\r
- case TOKEN_TYPE_SYMBOL: {\r
- OnyxToken* sym_token = expect(parser, TOKEN_TYPE_SYMBOL);\r
- OnyxAstNode* sym_node = lookup_identifier(parser, sym_token);\r
-\r
- // TODO: Handle calling a function\r
- return sym_node;\r
- }\r
-\r
- case TOKEN_TYPE_LITERAL_NUMERIC: {\r
- OnyxAstNode* lit_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL);\r
- lit_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_INT64];\r
- lit_node->token = expect(parser, TOKEN_TYPE_LITERAL_NUMERIC);\r
- return lit_node;\r
- }\r
-\r
- default:\r
- onyx_message_add(parser->msgs,\r
- ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,\r
- parser->curr_token->pos,\r
- onyx_get_token_type_name(parser->curr_token->type));\r
- }\r
-\r
- return NULL;\r
-}\r
-\r
-static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left) {\r
- OnyxAstNodeKind kind = -1;\r
-\r
- switch (parser->curr_token->type) {\r
- case TOKEN_TYPE_SYM_PLUS: kind = ONYX_AST_NODE_KIND_ADD; break;\r
- case TOKEN_TYPE_SYM_MINUS: kind = ONYX_AST_NODE_KIND_MINUS; break;\r
- case TOKEN_TYPE_SYM_STAR: kind = ONYX_AST_NODE_KIND_MULTIPLY; break;\r
- case TOKEN_TYPE_SYM_FSLASH: kind = ONYX_AST_NODE_KIND_DIVIDE; break;\r
- case TOKEN_TYPE_SYM_PERCENT: kind = ONYX_AST_NODE_KIND_MODULUS; break;\r
- }\r
-\r
- if (kind != -1) {\r
- parser_next_token(parser);\r
- OnyxAstNode* right = parse_factor(parser);\r
-\r
- OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind);\r
- bin_op->left = left;\r
- bin_op->right = right;\r
- return bin_op;\r
- }\r
-\r
- return &error_node;\r
-}\r
-\r
-static OnyxAstNode* parse_expression(OnyxParser* parser) {\r
- OnyxAstNode* left = parse_factor(parser);\r
-\r
- switch (parser->curr_token->type) {\r
- case TOKEN_TYPE_SYM_PLUS:\r
- case TOKEN_TYPE_SYM_MINUS:\r
- case TOKEN_TYPE_SYM_STAR:\r
- case TOKEN_TYPE_SYM_FSLASH:\r
- case TOKEN_TYPE_SYM_PERCENT: {\r
- OnyxAstNode* expr = parse_bin_op(parser, left);\r
- return expr;\r
- }\r
- }\r
-\r
- return left;\r
-}\r
-\r
-static OnyxAstNode* parse_if_stmt(OnyxParser* parser) {\r
- return &error_node;\r
-}\r
-\r
-static OnyxAstNode* parse_expression_statement(OnyxParser* parser) {\r
- if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) return NULL;\r
- OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);\r
-\r
- switch (parser->curr_token->type) {\r
- // NOTE: Declaration\r
- case TOKEN_TYPE_SYM_COLON: {\r
- parser_next_token(parser);\r
- OnyxTypeInfo* type = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];\r
-\r
- // NOTE: var: type\r
- if (parser->curr_token->type == TOKEN_TYPE_SYMBOL) {\r
- type = parse_type(parser);\r
- }\r
-\r
- OnyxAstNodeLocal* local = (OnyxAstNodeLocal*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LOCAL);\r
- local->token = symbol;\r
- local->type = type;\r
-\r
- insert_identifier(parser, local);\r
-\r
- if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) {\r
- parser_next_token(parser);\r
-\r
- OnyxAstNode* expr = parse_expression(parser);\r
- OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);\r
- assignment->right = expr;\r
- assignment->left = (OnyxAstNode*) local;\r
- return assignment;\r
- }\r
- }\r
-\r
- // NOTE: Assignment\r
- case TOKEN_TYPE_SYM_EQUALS: {\r
- parser_next_token(parser);\r
-\r
- OnyxAstNode* lval = lookup_identifier(parser, symbol);\r
-\r
- if (lval == NULL) {\r
- // TODO: error handling\r
- }\r
-\r
- OnyxAstNode* rval = parse_expression(parser);\r
- OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);\r
- assignment->right = rval;\r
- assignment->left = lval;\r
- return assignment;\r
- }\r
-\r
- default:\r
- parser_prev_token(parser);\r
- }\r
-\r
- return NULL;\r
-}\r
-\r
-static OnyxAstNode* parse_return_statement(OnyxParser* parser) {\r
- expect(parser, TOKEN_TYPE_KEYWORD_RETURN);\r
-\r
- OnyxAstNode* return_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_RETURN);\r
- return_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_VOID];\r
- OnyxAstNode* expr = NULL;\r
-\r
- if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {\r
- expr = parse_expression(parser);\r
-\r
- if (expr == NULL || expr == &error_node) {\r
- return &error_node;\r
- } else {\r
- return_node->next = expr;\r
- }\r
- }\r
-\r
- return return_node;\r
-}\r
-\r
-static OnyxAstNode* parse_statement(OnyxParser* parser) {\r
- switch (parser->curr_token->type) {\r
- case TOKEN_TYPE_KEYWORD_RETURN:\r
- return parse_return_statement(parser);\r
-\r
- case TOKEN_TYPE_OPEN_BRACE:\r
- return (OnyxAstNode *) parse_block(parser, 0);\r
-\r
- case TOKEN_TYPE_SYMBOL: {\r
- OnyxAstNode* ret = parse_expression_statement(parser);\r
- if (ret != NULL) return ret;\r
- }\r
-\r
- case TOKEN_TYPE_OPEN_PAREN:\r
- case TOKEN_TYPE_SYM_PLUS:\r
- case TOKEN_TYPE_SYM_MINUS:\r
- case TOKEN_TYPE_SYM_BANG:\r
- case TOKEN_TYPE_LITERAL_NUMERIC:\r
- case TOKEN_TYPE_LITERAL_STRING:\r
- return parse_expression(parser);\r
-\r
- case TOKEN_TYPE_KEYWORD_IF:\r
- return parse_if_stmt(parser);\r
-\r
- default:\r
- return NULL;\r
- }\r
-}\r
-\r
-static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function) {\r
- // --- is for an empty block\r
- if (parser->curr_token->type == TOKEN_TYPE_SYM_MINUS) {\r
- expect(parser, TOKEN_TYPE_SYM_MINUS);\r
- expect(parser, TOKEN_TYPE_SYM_MINUS);\r
- expect(parser, TOKEN_TYPE_SYM_MINUS);\r
- return NULL;\r
- }\r
-\r
- expect(parser, TOKEN_TYPE_OPEN_BRACE);\r
-\r
- OnyxAstNodeBlock* block = (OnyxAstNodeBlock *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_BLOCK);\r
- if (belongs_to_function) {\r
- OnyxAstNodeScope* scope = enter_scope(parser);\r
- block->scope = scope;\r
- }\r
-\r
- OnyxAstNode** next = &block->body;\r
- OnyxAstNode* stmt = NULL;\r
- while (parser->curr_token->type != TOKEN_TYPE_CLOSE_BRACE) {\r
- stmt = parse_statement(parser);\r
-\r
- if (stmt != NULL && stmt->kind != ONYX_AST_NODE_KIND_ERROR) {\r
- *next = stmt;\r
- next = &stmt->next;\r
- }\r
-\r
- if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {\r
- onyx_message_add(parser->msgs,\r
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,\r
- parser->curr_token->pos,\r
- onyx_get_token_type_name(TOKEN_TYPE_SYM_SEMICOLON),\r
- onyx_get_token_type_name(parser->curr_token->type));\r
- }\r
- parser_next_token(parser);\r
- }\r
-\r
- expect(parser, TOKEN_TYPE_CLOSE_BRACE);\r
-\r
- if (belongs_to_function) {\r
- leave_scope(parser);\r
- }\r
-\r
- return block;\r
-}\r
-\r
-static OnyxTypeInfo* parse_type(OnyxParser* parser) {\r
- OnyxTypeInfo* type_info = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];\r
-\r
- OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);\r
- if (symbol == NULL) return type_info;\r
-\r
- onyx_token_null_toggle(*symbol);\r
-\r
- if (!bh_hash_has(OnyxAstNode*, parser->identifiers, symbol->token)) {\r
- onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->token);\r
- } else {\r
- OnyxAstNode* type_info_node = bh_hash_get(OnyxAstNode*, parser->identifiers, symbol->token);\r
-\r
- if (type_info_node->kind == ONYX_AST_NODE_KIND_TYPE) {\r
- type_info = type_info_node->type;\r
- }\r
- }\r
-\r
- onyx_token_null_toggle(*symbol);\r
- return type_info;\r
-}\r
-\r
-static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) {\r
- expect(parser, TOKEN_TYPE_OPEN_PAREN);\r
-\r
- if (parser->curr_token->type == TOKEN_TYPE_CLOSE_PAREN) {\r
- parser_next_token(parser);\r
- return NULL;\r
- }\r
-\r
- OnyxAstNodeParam* first_param = NULL;\r
- u64 param_count = 0;\r
-\r
- OnyxAstNodeParam* curr_param = NULL;\r
- OnyxAstNodeParam* trailer = NULL;\r
-\r
- OnyxToken* symbol;\r
- while (parser->curr_token->type != TOKEN_TYPE_CLOSE_PAREN) {\r
- if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser);\r
- param_count++;\r
-\r
- symbol = expect(parser, TOKEN_TYPE_SYMBOL);\r
-\r
- curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM);\r
- curr_param->token = symbol;\r
- curr_param->type = parse_type(parser);\r
-\r
- if (first_param == NULL) first_param = curr_param;\r
-\r
- curr_param->next = NULL;\r
- if (trailer) trailer->next = curr_param;\r
-\r
- trailer = curr_param;\r
- }\r
-\r
- first_param->param_count = param_count;\r
-\r
- parser_next_token(parser); // Skip the )\r
- return first_param;\r
-}\r
-\r
-static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) {\r
- expect(parser, TOKEN_TYPE_KEYWORD_PROC);\r
-\r
- OnyxAstNodeFuncDef* func_def = (OnyxAstNodeFuncDef *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_FUNCDEF);\r
-\r
- OnyxAstNodeParam* params = parse_function_params(parser);\r
- func_def->params = params;\r
-\r
- expect(parser, TOKEN_TYPE_RIGHT_ARROW);\r
-\r
- OnyxTypeInfo* return_type = parse_type(parser);\r
- func_def->return_type = return_type;\r
-\r
- // TODO: Add params to parser.identifiers\r
- for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {\r
- onyx_token_null_toggle(*p->token);\r
- bh_hash_put(OnyxAstNode*, parser->identifiers, p->token->token, (OnyxAstNode*) p);\r
- onyx_token_null_toggle(*p->token);\r
- }\r
-\r
- func_def->body = parse_block(parser, 1);\r
-\r
- // TODO: Remove params from parser.identifiers\r
- for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {\r
- onyx_token_null_toggle(*p->token);\r
- bh_hash_delete(OnyxAstNode*, parser->identifiers, p->token->token);\r
- onyx_token_null_toggle(*p->token);\r
- }\r
- return func_def;\r
-}\r
-\r
-static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) {\r
- switch (parser->curr_token->type) {\r
- case TOKEN_TYPE_KEYWORD_USE:\r
- assert(0);\r
- break;\r
-\r
- case TOKEN_TYPE_KEYWORD_EXPORT: {\r
- expect(parser, TOKEN_TYPE_KEYWORD_EXPORT);\r
- if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) {\r
- onyx_message_add(parser->msgs,\r
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,\r
- parser->curr_token->pos,\r
- onyx_get_token_type_name(TOKEN_TYPE_SYMBOL),\r
- onyx_get_token_type_name(parser->curr_token->type));\r
- break;\r
- }\r
-\r
- OnyxAstNode* top_level_decl = parse_top_level_statement(parser);\r
- top_level_decl->flags |= ONYX_AST_FLAG_EXPORTED;\r
- return top_level_decl;\r
- } break;\r
-\r
- case TOKEN_TYPE_SYMBOL: {\r
- OnyxToken* symbol = parser->curr_token;\r
- parser_next_token(parser);\r
-\r
- expect(parser, TOKEN_TYPE_SYM_COLON);\r
- expect(parser, TOKEN_TYPE_SYM_COLON);\r
-\r
- if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) {\r
- OnyxAstNodeFuncDef* func_def = parse_function_definition(parser);\r
- func_def->token = symbol;\r
- return (OnyxAstNode *) func_def;\r
-\r
- } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) {\r
- // Handle struct case\r
- assert(0);\r
- } else {\r
- onyx_message_add(parser->msgs,\r
- ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,\r
- parser->curr_token->pos,\r
- onyx_get_token_type_name(parser->curr_token->type));\r
- }\r
- } break;\r
- }\r
-\r
- parser_next_token(parser);\r
- return NULL;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind) {\r
- return ast_node_names[kind];\r
-}\r
-\r
-OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\\r
- OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode));\r
- node->kind = kind;\r
- node->flags = 0;\r
- node->token = NULL;\r
- node->type = NULL;\r
- node->next = NULL;\r
- node->left = NULL;\r
- node->right = NULL;\r
-\r
- return node;\r
-}\r
-\r
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) {\r
- OnyxParser parser;\r
-\r
- bh_hash_init(bh_heap_allocator(), parser.identifiers);\r
-\r
- OnyxTypeInfo* it = &builtin_types[0];\r
- while (it->kind != 0xffffffff) {\r
- OnyxAstNode* tmp = onyx_ast_node_new(alloc, ONYX_AST_NODE_KIND_TYPE);\r
- tmp->type = it;\r
- bh_hash_put(OnyxAstNode*, parser.identifiers, (char *)it->name, tmp);\r
- it++;\r
- }\r
-\r
- parser.allocator = alloc;\r
- parser.tokenizer = tokenizer;\r
- parser.curr_token = tokenizer->tokens;\r
- parser.prev_token = NULL;\r
- parser.msgs = msgs;\r
- parser.curr_scope = NULL;\r
-\r
- return parser;\r
-}\r
-\r
-void onyx_parser_free(OnyxParser* parser) {\r
- bh_hash_free(parser->identifiers);\r
-}\r
-\r
-OnyxAstNode* onyx_parse(OnyxParser *parser) {\r
- OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM);\r
-\r
- OnyxAstNode** prev_stmt = &program->next;\r
- OnyxAstNode* curr_stmt = NULL;\r
- while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) {\r
- curr_stmt = parse_top_level_statement(parser);\r
-\r
- // Building a linked list of statements down the "next" chain\r
- if (curr_stmt != NULL && curr_stmt != &error_node) {\r
- *prev_stmt = curr_stmt;\r
- prev_stmt = &curr_stmt->next;\r
- }\r
- }\r
-\r
- return program;\r
-}\r
+
+#include "onyxlex.h"
+#include "onyxparser.h"
+
+static const char* ast_node_names[] = {
+ "ERROR",
+ "PROGRAM",
+
+ "FUNCDEF",
+ "BLOCK",
+ "SCOPE",
+ "LOCAL",
+
+ "ADD",
+ "MINUS",
+ "MULTIPLY",
+ "DIVIDE",
+ "MODULUS",
+ "NEGATE",
+
+ "TYPE",
+ "LITERAL",
+ "CAST",
+ "PARAM",
+ "CALL",
+ "ASSIGN",
+ "RETURN",
+
+ "EQUAL",
+ "NOT_EQUAL",
+ "GREATER",
+ "GREATER_EQUAL",
+ "LESS",
+ "LESS_EQUAL",
+ "NOT",
+
+ "IF",
+ "LOOP",
+
+ "ONYX_AST_NODE_KIND_COUNT",
+};
+
+struct OnyxTypeInfo builtin_types[] = {
+ { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" },
+ { ONYX_TYPE_INFO_KIND_VOID, 0, "void" },
+
+ { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1 },
+
+ { ONYX_TYPE_INFO_KIND_UINT8, 1, "u8", 1, 1, 0, 0 },
+ { ONYX_TYPE_INFO_KIND_UINT16, 2, "u16", 1, 1, 0, 0 },
+ { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0 },
+ { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0 },
+
+ { ONYX_TYPE_INFO_KIND_INT8, 1, "i8", 1, 0, 0, 0 },
+ { ONYX_TYPE_INFO_KIND_INT16, 2, "i16", 1, 0, 0, 0 },
+ { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0 },
+ { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0 },
+
+ { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0 },
+ { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0 },
+ { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0 },
+
+ { 0xffffffff } // Sentinel
+};
+
+static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } };
+
+// NOTE: Forward declarations
+static void parser_next_token(OnyxParser* parser);
+static void parser_prev_token(OnyxParser* parser);
+static b32 is_terminating_token(OnyxTokenType token_type);
+static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type);
+static OnyxAstNodeScope* enter_scope(OnyxParser* parser);
+static OnyxAstNodeScope* leave_scope(OnyxParser* parser);
+static void insert_identifier(OnyxParser* parser, OnyxAstNodeLocal* local);
+static OnyxAstNode* parse_factor(OnyxParser* parser);
+static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left);
+static OnyxAstNode* parse_expression(OnyxParser* parser);
+static OnyxAstNode* parse_if_stmt(OnyxParser* parser);
+static b32 parse_expression_statement(OnyxParser* parser, OnyxAstNode** ret);
+static OnyxAstNode* parse_return_statement(OnyxParser* parser);
+static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function);
+static OnyxAstNode* parse_statement(OnyxParser* parser);
+static OnyxTypeInfo* parse_type(OnyxParser* parser);
+static OnyxAstNodeParam* parse_function_params(OnyxParser* parser);
+static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser);
+static OnyxAstNode* parse_top_level_statement(OnyxParser* parser);
+
+static void parser_next_token(OnyxParser* parser) {
+ parser->prev_token = parser->curr_token;
+ parser->curr_token++;
+ while (parser->curr_token->type == TOKEN_TYPE_COMMENT) parser->curr_token++;
+}
+
+static void parser_prev_token(OnyxParser* parser) {
+ // TODO: This is probably wrong
+ parser->prev_token--;
+ while (parser->prev_token->type == TOKEN_TYPE_COMMENT) parser->prev_token--;
+ parser->curr_token = parser->prev_token;
+ parser->prev_token--;
+}
+
+static b32 is_terminating_token(OnyxTokenType token_type) {
+ switch (token_type) {
+ case TOKEN_TYPE_SYM_SEMICOLON:
+ case TOKEN_TYPE_CLOSE_BRACE:
+ case TOKEN_TYPE_OPEN_BRACE:
+ case TOKEN_TYPE_END_STREAM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// Advances to next token no matter what
+static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) {
+ OnyxToken* token = parser->curr_token;
+ parser_next_token(parser);
+
+ if (token->type != token_type) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ token->pos,
+ onyx_get_token_type_name(token_type), onyx_get_token_type_name(token->type));
+ return NULL;
+ }
+
+ return token;
+}
+
+static OnyxAstNodeScope* enter_scope(OnyxParser* parser) {
+ OnyxAstNodeScope* scope = (OnyxAstNodeScope*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE);
+ scope->prev_scope = parser->curr_scope;
+ parser->curr_scope = scope;
+ return scope;
+}
+
+static OnyxAstNodeScope* leave_scope(OnyxParser* parser) {
+ // NOTE: Can't leave a scope if there is no scope
+ assert(parser->curr_scope != NULL);
+
+ for (OnyxAstNodeLocal *walker = parser->curr_scope->last_local; walker != NULL; walker = walker->prev_local) {
+ onyx_token_null_toggle(*walker->token);
+
+ if (walker->shadowed) {
+ // NOTE: Restore shadowed variable
+ bh_hash_put(OnyxAstNode*, parser->identifiers, walker->token->token, walker->shadowed);
+ } else {
+ bh_hash_delete(OnyxAstNode*, parser->identifiers, walker->token->token);
+ }
+
+ onyx_token_null_toggle(*walker->token);
+ }
+
+ parser->curr_scope = parser->curr_scope->prev_scope;
+ return parser->curr_scope;
+}
+
+static OnyxAstNode* lookup_identifier(OnyxParser* parser, OnyxToken* token) {
+ OnyxAstNode* ident = NULL;
+
+ onyx_token_null_toggle(*token);
+ if (bh_hash_has(OnyxAstNode*, parser->identifiers, token->token)) {
+ ident = bh_hash_get(OnyxAstNode*, parser->identifiers, token->token);
+ }
+ onyx_token_null_toggle(*token);
+
+ return ident;
+}
+
+static void insert_identifier(OnyxParser* parser, OnyxAstNodeLocal* local) {
+ OnyxAstNodeScope* scope = parser->curr_scope;
+ local->prev_local = scope->last_local;
+ scope->last_local = local;
+
+ onyx_token_null_toggle(*local->token);
+ if (bh_hash_has(OnyxAstNodeLocal*, parser->identifiers, local->token->token)) {
+ local->shadowed = bh_hash_get(OnyxAstNode*, parser->identifiers, local->token->token);
+ }
+
+ bh_hash_put(OnyxAstNodeLocal*, parser->identifiers, local->token->token, local);
+ onyx_token_null_toggle(*local->token);
+}
+
+static OnyxAstNode* parse_factor(OnyxParser* parser) {
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_OPEN_PAREN: {
+ parser_next_token(parser);
+ OnyxAstNode* expr = parse_expression(parser);
+ expect(parser, TOKEN_TYPE_CLOSE_PAREN);
+ return expr;
+ }
+
+ case TOKEN_TYPE_SYMBOL: {
+ OnyxToken* sym_token = expect(parser, TOKEN_TYPE_SYMBOL);
+ OnyxAstNode* sym_node = lookup_identifier(parser, sym_token);
+
+ // TODO: Handle calling a function
+ return sym_node;
+ }
+
+ case TOKEN_TYPE_LITERAL_NUMERIC: {
+ OnyxAstNode* lit_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL);
+ lit_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_INT64];
+ lit_node->token = expect(parser, TOKEN_TYPE_LITERAL_NUMERIC);
+ return lit_node;
+ }
+
+ default:
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(parser->curr_token->type));
+ }
+
+ return NULL;
+}
+
+static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left) {
+ OnyxAstNodeKind kind = -1;
+
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_SYM_PLUS: kind = ONYX_AST_NODE_KIND_ADD; break;
+ case TOKEN_TYPE_SYM_MINUS: kind = ONYX_AST_NODE_KIND_MINUS; break;
+ case TOKEN_TYPE_SYM_STAR: kind = ONYX_AST_NODE_KIND_MULTIPLY; break;
+ case TOKEN_TYPE_SYM_FSLASH: kind = ONYX_AST_NODE_KIND_DIVIDE; break;
+ case TOKEN_TYPE_SYM_PERCENT: kind = ONYX_AST_NODE_KIND_MODULUS; break;
+ }
+
+ if (kind != -1) {
+ parser_next_token(parser);
+ OnyxAstNode* right = parse_factor(parser);
+
+ OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind);
+ bin_op->left = left;
+ bin_op->right = right;
+ return bin_op;
+ }
+
+ return &error_node;
+}
+
+static OnyxAstNode* parse_expression(OnyxParser* parser) {
+ OnyxAstNode* left = parse_factor(parser);
+
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_SYM_PLUS:
+ case TOKEN_TYPE_SYM_MINUS:
+ case TOKEN_TYPE_SYM_STAR:
+ case TOKEN_TYPE_SYM_FSLASH:
+ case TOKEN_TYPE_SYM_PERCENT: {
+ OnyxAstNode* expr = parse_bin_op(parser, left);
+ return expr;
+ }
+ }
+
+ return left;
+}
+
+static OnyxAstNode* parse_if_stmt(OnyxParser* parser) {
+ return &error_node;
+}
+
+// Returns 1 if the symbol was consumed. Returns 0 otherwise
+// ret is set to the statement to insert
+static b32 parse_expression_statement(OnyxParser* parser, OnyxAstNode** ret) {
+ if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) return 0;
+ OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+
+ switch (parser->curr_token->type) {
+ // NOTE: Declaration
+ case TOKEN_TYPE_SYM_COLON: {
+ parser_next_token(parser);
+ OnyxTypeInfo* type = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];
+
+ // NOTE: var: type
+ if (parser->curr_token->type == TOKEN_TYPE_SYMBOL) {
+ type = parse_type(parser);
+ }
+
+ OnyxAstNodeLocal* local = (OnyxAstNodeLocal*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LOCAL);
+ local->token = symbol;
+ local->type = type;
+
+ insert_identifier(parser, local);
+
+ if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) {
+ parser_next_token(parser);
+
+ OnyxAstNode* expr = parse_expression(parser);
+ OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);
+ assignment->right = expr;
+ assignment->left = (OnyxAstNode*) local;
+ *ret = assignment;
+ }
+ return 1;
+ }
+
+ // NOTE: Assignment
+ case TOKEN_TYPE_SYM_EQUALS: {
+ parser_next_token(parser);
+
+ OnyxAstNode* lval = lookup_identifier(parser, symbol);
+
+ if (lval == NULL) {
+ // TODO: error handling
+ }
+
+ OnyxAstNode* rval = parse_expression(parser);
+ OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);
+ assignment->right = rval;
+ assignment->left = lval;
+ *ret = assignment;
+ return 1;
+ }
+
+ default:
+ parser_prev_token(parser);
+ }
+
+ return 0;
+}
+
+static OnyxAstNode* parse_return_statement(OnyxParser* parser) {
+ expect(parser, TOKEN_TYPE_KEYWORD_RETURN);
+
+ OnyxAstNode* return_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_RETURN);
+ return_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_VOID];
+ OnyxAstNode* expr = NULL;
+
+ if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {
+ expr = parse_expression(parser);
+
+ if (expr == NULL || expr == &error_node) {
+ return &error_node;
+ } else {
+ return_node->next = expr;
+ }
+ }
+
+ return return_node;
+}
+
+static OnyxAstNode* parse_statement(OnyxParser* parser) {
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_KEYWORD_RETURN:
+ return parse_return_statement(parser);
+
+ case TOKEN_TYPE_OPEN_BRACE:
+ return (OnyxAstNode *) parse_block(parser, 0);
+
+ case TOKEN_TYPE_SYMBOL: {
+ OnyxAstNode* ret = NULL;
+ if (parse_expression_statement(parser, &ret)) return ret;
+ // fallthrough
+ }
+
+ case TOKEN_TYPE_OPEN_PAREN:
+ case TOKEN_TYPE_SYM_PLUS:
+ case TOKEN_TYPE_SYM_MINUS:
+ case TOKEN_TYPE_SYM_BANG:
+ case TOKEN_TYPE_LITERAL_NUMERIC:
+ case TOKEN_TYPE_LITERAL_STRING:
+ return parse_expression(parser);
+
+ case TOKEN_TYPE_KEYWORD_IF:
+ return parse_if_stmt(parser);
+
+ default:
+ return NULL;
+ }
+}
+
+static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function) {
+ // --- is for an empty block
+ if (parser->curr_token->type == TOKEN_TYPE_SYM_MINUS) {
+ expect(parser, TOKEN_TYPE_SYM_MINUS);
+ expect(parser, TOKEN_TYPE_SYM_MINUS);
+ expect(parser, TOKEN_TYPE_SYM_MINUS);
+ return NULL;
+ }
+
+ expect(parser, TOKEN_TYPE_OPEN_BRACE);
+
+ OnyxAstNodeBlock* block = (OnyxAstNodeBlock *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_BLOCK);
+ if (belongs_to_function) {
+ OnyxAstNodeScope* scope = enter_scope(parser);
+ block->scope = scope;
+ }
+
+ OnyxAstNode** next = &block->body;
+ OnyxAstNode* stmt = NULL;
+ while (parser->curr_token->type != TOKEN_TYPE_CLOSE_BRACE) {
+ stmt = parse_statement(parser);
+
+ if (stmt != NULL && stmt->kind != ONYX_AST_NODE_KIND_ERROR) {
+ *next = stmt;
+ next = &stmt->next;
+ }
+
+ if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(TOKEN_TYPE_SYM_SEMICOLON),
+ onyx_get_token_type_name(parser->curr_token->type));
+ }
+ parser_next_token(parser);
+ }
+
+ expect(parser, TOKEN_TYPE_CLOSE_BRACE);
+
+ if (belongs_to_function) {
+ leave_scope(parser);
+ }
+
+ return block;
+}
+
+static OnyxTypeInfo* parse_type(OnyxParser* parser) {
+ OnyxTypeInfo* type_info = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];
+
+ OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+ if (symbol == NULL) return type_info;
+
+ onyx_token_null_toggle(*symbol);
+
+ if (!bh_hash_has(OnyxAstNode*, parser->identifiers, symbol->token)) {
+ onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->token);
+ } else {
+ OnyxAstNode* type_info_node = bh_hash_get(OnyxAstNode*, parser->identifiers, symbol->token);
+
+ if (type_info_node->kind == ONYX_AST_NODE_KIND_TYPE) {
+ type_info = type_info_node->type;
+ }
+ }
+
+ onyx_token_null_toggle(*symbol);
+ return type_info;
+}
+
+static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) {
+ expect(parser, TOKEN_TYPE_OPEN_PAREN);
+
+ if (parser->curr_token->type == TOKEN_TYPE_CLOSE_PAREN) {
+ parser_next_token(parser);
+ return NULL;
+ }
+
+ OnyxAstNodeParam* first_param = NULL;
+ u64 param_count = 0;
+
+ OnyxAstNodeParam* curr_param = NULL;
+ OnyxAstNodeParam* trailer = NULL;
+
+ OnyxToken* symbol;
+ while (parser->curr_token->type != TOKEN_TYPE_CLOSE_PAREN) {
+ if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser);
+ param_count++;
+
+ symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+
+ curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM);
+ curr_param->token = symbol;
+ curr_param->type = parse_type(parser);
+
+ if (first_param == NULL) first_param = curr_param;
+
+ curr_param->next = NULL;
+ if (trailer) trailer->next = curr_param;
+
+ trailer = curr_param;
+ }
+
+ first_param->param_count = param_count;
+
+ parser_next_token(parser); // Skip the )
+ return first_param;
+}
+
+static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) {
+ expect(parser, TOKEN_TYPE_KEYWORD_PROC);
+
+ OnyxAstNodeFuncDef* func_def = (OnyxAstNodeFuncDef *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_FUNCDEF);
+
+ OnyxAstNodeParam* params = parse_function_params(parser);
+ func_def->params = params;
+
+ expect(parser, TOKEN_TYPE_RIGHT_ARROW);
+
+ OnyxTypeInfo* return_type = parse_type(parser);
+ func_def->return_type = return_type;
+
+ // TODO: Add params to parser.identifiers
+ for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {
+ onyx_token_null_toggle(*p->token);
+ bh_hash_put(OnyxAstNode*, parser->identifiers, p->token->token, (OnyxAstNode*) p);
+ onyx_token_null_toggle(*p->token);
+ }
+
+ func_def->body = parse_block(parser, 1);
+
+ // TODO: Remove params from parser.identifiers
+ for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {
+ onyx_token_null_toggle(*p->token);
+ bh_hash_delete(OnyxAstNode*, parser->identifiers, p->token->token);
+ onyx_token_null_toggle(*p->token);
+ }
+ return func_def;
+}
+
+static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) {
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_KEYWORD_USE:
+ assert(0);
+ break;
+
+ case TOKEN_TYPE_KEYWORD_EXPORT: {
+ expect(parser, TOKEN_TYPE_KEYWORD_EXPORT);
+ if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(TOKEN_TYPE_SYMBOL),
+ onyx_get_token_type_name(parser->curr_token->type));
+ break;
+ }
+
+ OnyxAstNode* top_level_decl = parse_top_level_statement(parser);
+ top_level_decl->flags |= ONYX_AST_FLAG_EXPORTED;
+ return top_level_decl;
+ } break;
+
+ case TOKEN_TYPE_SYMBOL: {
+ OnyxToken* symbol = parser->curr_token;
+ parser_next_token(parser);
+
+ expect(parser, TOKEN_TYPE_SYM_COLON);
+ expect(parser, TOKEN_TYPE_SYM_COLON);
+
+ if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) {
+ OnyxAstNodeFuncDef* func_def = parse_function_definition(parser);
+ func_def->token = symbol;
+ return (OnyxAstNode *) func_def;
+
+ } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) {
+ // Handle struct case
+ assert(0);
+ } else {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(parser->curr_token->type));
+ }
+ } break;
+ }
+
+ parser_next_token(parser);
+ return NULL;
+}
+
+
+
+
+
+const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind) {
+ return ast_node_names[kind];
+}
+
+OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\
+ OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode));
+ node->kind = kind;
+ node->flags = 0;
+ node->token = NULL;
+ node->type = NULL;
+ node->next = NULL;
+ node->left = NULL;
+ node->right = NULL;
+
+ return node;
+}
+
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) {
+ OnyxParser parser;
+
+ bh_hash_init(bh_heap_allocator(), parser.identifiers);
+
+ OnyxTypeInfo* it = &builtin_types[0];
+ while (it->kind != 0xffffffff) {
+ OnyxAstNode* tmp = onyx_ast_node_new(alloc, ONYX_AST_NODE_KIND_TYPE);
+ tmp->type = it;
+ bh_hash_put(OnyxAstNode*, parser.identifiers, (char *)it->name, tmp);
+ it++;
+ }
+
+ parser.allocator = alloc;
+ parser.tokenizer = tokenizer;
+ parser.curr_token = tokenizer->tokens;
+ parser.prev_token = NULL;
+ parser.msgs = msgs;
+ parser.curr_scope = NULL;
+
+ return parser;
+}
+
+void onyx_parser_free(OnyxParser* parser) {
+ bh_hash_free(parser->identifiers);
+}
+
+OnyxAstNode* onyx_parse(OnyxParser *parser) {
+ OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM);
+
+ OnyxAstNode** prev_stmt = &program->next;
+ OnyxAstNode* curr_stmt = NULL;
+ while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) {
+ curr_stmt = parse_top_level_statement(parser);
+
+ // Building a linked list of statements down the "next" chain
+ if (curr_stmt != NULL && curr_stmt != &error_node) {
+ *prev_stmt = curr_stmt;
+ prev_stmt = &curr_stmt->next;
+ }
+ }
+
+ return program;
+}