From 2138964dfe72d10f792ada57938c071d851511e5 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sat, 23 May 2020 14:10:19 -0500 Subject: [PATCH] I hate windows line endings; other small changes; Implemented #11 --- bh.h | 3506 ++++++++++++++++++++++---------------------- docs/parse_grammar | 156 +- onyx | Bin 82224 -> 82384 bytes onyx.c | 122 +- onyxlex.c | 550 +++---- onyxlex.h | 178 +-- onyxmsgs.c | 88 +- onyxmsgs.h | 72 +- onyxparser.c | 1236 ++++++++-------- onyxparser.h | 378 ++--- onyxutils.c | 53 +- onyxutils.h | 10 +- progs/minimal.onyx | 30 +- 13 files changed, 3200 insertions(+), 3179 deletions(-) diff --git a/bh.h b/bh.h index c3679f23..95772a75 100644 --- a/bh.h +++ b/bh.h @@ -1,1753 +1,1753 @@ -#ifndef BH_H -#define BH_H - -#include -#include -#include -#include - -#include -#include -#include // TODO: Replace with needed functions -#include - -//------------------------------------------------------------------------------------- -// 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 +#ifndef BH_H +#define BH_H + +#include +#include +#include +#include + +#include +#include +#include // TODO: Replace with needed functions +#include + +//------------------------------------------------------------------------------------- +// 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 diff --git a/docs/parse_grammar b/docs/parse_grammar index 610444a2..909fe1c9 100644 --- a/docs/parse_grammar +++ b/docs/parse_grammar @@ -1,78 +1,78 @@ -Note: ~ is empty -Goal: Design the language to have no ambiguity (so a greedy algorithm can work) - - -SOURCE_FILE = TOP_LEVEL_STATEMENT ; SOURCE_FILE | ~ - -TOP_LEVEL_STATEMENT - = COMMENT -- Should comments not be passed to the parser? Depends if I need to look at them. Probably not - | USE_DECLARATION - | EXPORT_DECLARATION - | FOREIGN_DECLARATION - | TOP_LEVEL_DECLARATION - -COMMENT = TOKEN_TYPE_COMMENT - -USE_DECLARATION = use TOKEN_TYPE_LITERAL_STRING - -EXPORT_DECLARATION = export TOP_LEVEL_DECLARATION - -FOREIGN_DECLARATION = foreign TOKEN_TYPE_LITERAL_STRING TOKEN_TYPE_LITERAL_STRING :: TOP_LEVEL_VALUE - -TOP_LEVEL_DECLARATION = TOKEN_TYPE_SYMBOL :: TOP_LEVEL_VALUE - -TOP_LEVEL_VALUE - = FUNCTION_DECLARATION - | STRUCT_DECLARATION - -FUNCTION_DECLARATION = proc FUNCTION_TYPE BLOCK - -FUNCTION_TYPE = ( FUNCTION_PARAMS ) -> TOKEN_TYPE_SYMBOL - --- This may be too weird... -BLOCK = { STATEMENTS | --- -STATEMENTS = STATEMENT ; STATEMENTS | } - -STATEMENT - = ASSIGNMENT_STATEMENT - | IF_STATEMENT - | FOR_STATEMENT - | RETURN_STATEMENT - | EXPRESSION - -ASSIGNMENT_STATEMENT = TOKEN_TYPE_SYMBOL = EXPRESSION - -IF_STATEMENT - = if EXPRESSION BLOCK ELSE_IF_STATEMENT ELSE_STATEMENT - -ELSEIF_STATEMENT = elseif EXPRESSION BLOCK ELSEIF_STATEMENT | ~ - -ELSE_STATEMENT = else BLOCK | ~ - --- This needs to be better -FOR_STATEMENT = for STATEMENT ; EXPRESSION ; STATEMENT BLOCK - -RETURN_STATEMENT = return EXPRESSION - --- Remove abiguity in implementation -EXPRESSION - = EXPRESSION + EXPRESSION - | EXPRESSION - EXPRESSION - | EXPRESSION * EXPRESSION - | EXPRESSION / EXPRESSION - | EXPRESSION % EXPRESSION - | do BLOCK - | FUNCTION_CALL -- This could have some abiguity with just the symbol - | ( EXPRESSION ) - | TOKEN_TYPE_SYMBOL - -FUNCTION_CALL = TOKEN_TYPE_SYMBOL ( EXPRESSION_LIST ) - --- Implement just using a loop -COMMA_LIST(T) = T | T , COMMA_LIST(T) - -FUNCTION_PARAMS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) -EXPRESSION_LIST = COMMA_LIST(EXPRESSION) - -STRUCT_DECLARATION = struct { STRUCT_MEMBERS } -STRUCT_MEMBERS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) \ No newline at end of file +Note: ~ is empty +Goal: Design the language to have no ambiguity (so a greedy algorithm can work) + + +SOURCE_FILE = TOP_LEVEL_STATEMENT ; SOURCE_FILE | ~ + +TOP_LEVEL_STATEMENT + = COMMENT -- Should comments not be passed to the parser? Depends if I need to look at them. Probably not + | USE_DECLARATION + | EXPORT_DECLARATION + | FOREIGN_DECLARATION + | TOP_LEVEL_DECLARATION + +COMMENT = TOKEN_TYPE_COMMENT + +USE_DECLARATION = use TOKEN_TYPE_LITERAL_STRING + +EXPORT_DECLARATION = export TOP_LEVEL_DECLARATION + +FOREIGN_DECLARATION = foreign TOKEN_TYPE_LITERAL_STRING TOKEN_TYPE_LITERAL_STRING :: TOP_LEVEL_VALUE + +TOP_LEVEL_DECLARATION = TOKEN_TYPE_SYMBOL :: TOP_LEVEL_VALUE + +TOP_LEVEL_VALUE + = FUNCTION_DECLARATION + | STRUCT_DECLARATION + +FUNCTION_DECLARATION = proc FUNCTION_TYPE BLOCK + +FUNCTION_TYPE = ( FUNCTION_PARAMS ) -> TOKEN_TYPE_SYMBOL + +-- This may be too weird... +BLOCK = { STATEMENTS | --- +STATEMENTS = STATEMENT ; STATEMENTS | } + +STATEMENT + = ASSIGNMENT_STATEMENT + | IF_STATEMENT + | FOR_STATEMENT + | RETURN_STATEMENT + | EXPRESSION + +ASSIGNMENT_STATEMENT = TOKEN_TYPE_SYMBOL = EXPRESSION + +IF_STATEMENT + = if EXPRESSION BLOCK ELSE_IF_STATEMENT ELSE_STATEMENT + +ELSEIF_STATEMENT = elseif EXPRESSION BLOCK ELSEIF_STATEMENT | ~ + +ELSE_STATEMENT = else BLOCK | ~ + +-- This needs to be better +FOR_STATEMENT = for STATEMENT ; EXPRESSION ; STATEMENT BLOCK + +RETURN_STATEMENT = return EXPRESSION + +-- Remove abiguity in implementation +EXPRESSION + = EXPRESSION + EXPRESSION + | EXPRESSION - EXPRESSION + | EXPRESSION * EXPRESSION + | EXPRESSION / EXPRESSION + | EXPRESSION % EXPRESSION + | do BLOCK + | FUNCTION_CALL -- This could have some abiguity with just the symbol + | ( EXPRESSION ) + | TOKEN_TYPE_SYMBOL + +FUNCTION_CALL = TOKEN_TYPE_SYMBOL ( EXPRESSION_LIST ) + +-- Implement just using a loop +COMMA_LIST(T) = T | T , COMMA_LIST(T) + +FUNCTION_PARAMS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) +EXPRESSION_LIST = COMMA_LIST(EXPRESSION) + +STRUCT_DECLARATION = struct { STRUCT_MEMBERS } +STRUCT_MEMBERS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) diff --git a/onyx b/onyx index 3394ad0fe51e85ab2c41ca2c14cd02e966b8dc20..7759fcd93b530e775dfbe5c5d3faf33cff8f6c0d 100755 GIT binary patch delta 23991 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 Nns_?;2exI&{|^AfHnRW# delta 23422 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 ha0ylYP(mQDJO0#cXF*^bp!5EYviXLtype), it->pos.filename, it->pos.line, it->pos.column); - // onyx_token_null_toggle(*it); - // } - - bh_arena msg_arena; - bh_arena_init(&msg_arena, alloc, 4096); - bh_allocator msg_alloc = bh_arena_allocator(&msg_arena); - - OnyxMessages msgs; - onyx_message_create(msg_alloc, &msgs); - - bh_arena ast_arena; - bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB - bh_allocator ast_alloc = bh_arena_allocator(&ast_arena); - - OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs); - OnyxAstNode* program = onyx_parse(&parser); - - onyx_message_print(&msgs); - onyx_ast_print(program); - - bh_file_contents_delete(&fc); - onyx_tokenizer_free(&tokenizer); - onyx_parser_free(&parser); - bh_arena_free(&msg_arena); - bh_arena_free(&ast_arena); - - - return 0; -} +#define BH_NO_STRING +// #define BH_DEBUG +#define BH_DEFINE +#include "bh.h" + +#include "onyxlex.h" +#include "onyxmsgs.h" +#include "onyxparser.h" +#include "onyxutils.h" + +int main(int argc, char *argv[]) { + bh_file source_file; + bh_file_error err = bh_file_open(&source_file, argv[1]); + if (err != BH_FILE_ERROR_NONE) { + bh_printf_err("Failed to open file %s\n", argv[1]); + return EXIT_FAILURE; + } + + bh_allocator alloc = bh_heap_allocator(); + + bh_file_contents fc = bh_file_read_contents(alloc, &source_file); + bh_file_close(&source_file); + + OnyxTokenizer tokenizer = onyx_tokenizer_create(alloc, &fc); + onyx_lex_tokens(&tokenizer); + bh_arr(OnyxToken) token_arr = tokenizer.tokens; + + // bh_printf("There are %d tokens (Allocated space for %d tokens)\n", bh_arr_length(token_arr), bh_arr_capacity(token_arr)); + + // for (OnyxToken* it = token_arr; !bh_arr_end(token_arr, it); it++) { + // onyx_token_null_toggle(*it); + // bh_printf("%s (%s:%l:%l)\n", onyx_get_token_type_name(it->type), it->pos.filename, it->pos.line, it->pos.column); + // onyx_token_null_toggle(*it); + // } + + bh_arena msg_arena; + bh_arena_init(&msg_arena, alloc, 4096); + bh_allocator msg_alloc = bh_arena_allocator(&msg_arena); + + OnyxMessages msgs; + onyx_message_create(msg_alloc, &msgs); + + bh_arena ast_arena; + bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB + bh_allocator ast_alloc = bh_arena_allocator(&ast_arena); + + OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs); + OnyxAstNode* program = onyx_parse(&parser); + + onyx_message_print(&msgs); + onyx_ast_print(program); + + bh_file_contents_delete(&fc); + onyx_tokenizer_free(&tokenizer); + onyx_parser_free(&parser); + bh_arena_free(&msg_arena); + bh_arena_free(&ast_arena); + + + return 0; +} diff --git a/onyxlex.c b/onyxlex.c index 69b3d314..ca877d6c 100644 --- a/onyxlex.c +++ b/onyxlex.c @@ -1,275 +1,275 @@ -#include "bh.h" -#include "onyxlex.h" - -static const char* onyx_token_type_names[] = { - "TOKEN_TYPE_UNKNOWN", - "TOKEN_TYPE_END_STREAM", - - "TOKEN_TYPE_COMMENT", - - "struct", //"TOKEN_TYPE_KEYWORD_STRUCT", - "use", //"TOKEN_TYPE_KEYWORD_USE", - "export", //"TOKEN_TYPE_KEYWORD_EXPORT", - "if", //"TOKEN_TYPE_KEYWORD_IF", - "else", //"TOKEN_TYPE_KEYWORD_ELSE", - "for", //"TOKEN_TYPE_KEYWORD_FOR", - "do", //"TOKEN_TYPE_KEYWORD_DO", - "return", //"TOKEN_TYPE_KEYWORD_RETURN", - "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN", - "proc", //"TOKEN_TYPE_KEYWORD_PROC", - "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", - - "->", //"TOKEN_TYPE_RIGHT_ARROW", - "<-", //"TOKEN_TYPE_LEFT_ARROW", - "(", //"TOKEN_TYPE_OPEN_PAREN", - ")", //"TOKEN_TYPE_CLOSE_PAREN", - "{", //"TOKEN_TYPE_OPEN_BRACE", - "}", //"TOKEN_TYPE_CLOSE_BRACE", - "[", //"TOKEN_TYPE_OPEN_BRACKET", - "]", //"TOKEN_TYPE_CLOSE_BRACKET", - "<", //"TOKEN_TYPE_OPEN_ANGLE", - ">", //"TOKEN_TYPE_CLOSE_ANGLE", - - "+", // "TOKEN_TYPE_SYM_PLUS", - "-", // "TOKEN_TYPE_SYM_MINUS", - "*", // "TOKEN_TYPE_SYM_STAR", - "%", // "TOKEN_TYPE_SYM_PERCENT", - ".", // "TOKEN_TYPE_SYM_DOT", - "/", // "TOKEN_TYPE_SYM_FSLASH", - "\\", // "TOKEN_TYPE_SYM_BSLASH", - ":", // "TOKEN_TYPE_SYM_COLON", - ";", // "TOKEN_TYPE_SYM_SEMICOLON", - ",", // "TOKEN_TYPE_SYM_COMMA", - "=", // "TOKEN_TYPE_SYM_EQUALS", - "`", // "TOKEN_TYPE_SYM_GRAVE", - "~", // "TOKEN_TYPE_SYM_TILDE", - "!", // "TOKEN_TYPE_SYM_BANG", - "^", // "TOKEN_TYPE_SYM_CARET", - "&", // "TOKEN_TYPE_SYM_AMPERSAND", - - "TOKEN_TYPE_SYMBOL", - "TOKEN_TYPE_LITERAL_STRING", - "TOKEN_TYPE_LITERAL_NUMERIC", - - "TOKEN_TYPE_COUNT" -}; - -#ifndef LITERAL_TOKEN -#define LITERAL_TOKEN(token, token_type) \ - if (token_lit(tokenizer, &tk, token, token_type)) goto token_parsed; -#endif - -#ifndef INCREMENT_CURR_TOKEN -#define INCREMENT_CURR_TOKEN(tkn) { \ - if (*(tkn)->curr == '\n') { \ - (tkn)->line_number++; \ - (tkn)->line_start = (tkn)->curr + 1; \ - } \ - (tkn)->curr++; \ -} -#endif - -static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTokenType type) { - i64 len = chars_match(tokenizer->curr, lit); - if (len > 0) { - tk->type = type; - tk->token = tokenizer->curr; - tk->length = len; - tk->pos.line = tokenizer->line_number; - tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; - - tokenizer->curr += len; - - return 1; - } - return 0; -} - -const char* onyx_get_token_type_name(OnyxTokenType tkn_type) { - return onyx_token_type_names[tkn_type]; -} - -void onyx_token_null_toggle(OnyxToken tkn) { - static char backup = 0; - char tmp = tkn.token[tkn.length]; - tkn.token[tkn.length] = backup; - backup = tmp; -} - -OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { - OnyxToken tk; - - // Skip whitespace - while (char_is_whitespace(*tokenizer->curr) && tokenizer->curr != tokenizer->end) - INCREMENT_CURR_TOKEN(tokenizer) - - tk.type = TOKEN_TYPE_UNKNOWN; - tk.token = tokenizer->curr; - tk.length = 1; - tk.pos.filename = tokenizer->filename; - tk.pos.line = tokenizer->line_number; - tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; - - if (tokenizer->curr == tokenizer->end) { - tk.type = TOKEN_TYPE_END_STREAM; - goto token_parsed; - } - - // Comments - if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { - tokenizer->curr += 2; - tk.type = TOKEN_TYPE_COMMENT; - tk.token = tokenizer->curr; - u16 layers = 1; - - while (layers >= 1) { - INCREMENT_CURR_TOKEN(tokenizer); - - if (tokenizer->curr == tokenizer->end) { - tk.type = TOKEN_TYPE_END_STREAM; - break; - } - - if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { - layers++; - INCREMENT_CURR_TOKEN(tokenizer); - } - - if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') { - layers--; - INCREMENT_CURR_TOKEN(tokenizer); - } - } - - INCREMENT_CURR_TOKEN(tokenizer); - - tk.length = tokenizer->curr - tk.token - 2; - goto token_parsed; - } - - LITERAL_TOKEN("struct", TOKEN_TYPE_KEYWORD_STRUCT); - LITERAL_TOKEN("export", TOKEN_TYPE_KEYWORD_EXPORT); - LITERAL_TOKEN("use", TOKEN_TYPE_KEYWORD_USE); - LITERAL_TOKEN("if", TOKEN_TYPE_KEYWORD_IF); - LITERAL_TOKEN("else", TOKEN_TYPE_KEYWORD_ELSE); - LITERAL_TOKEN("foreign", TOKEN_TYPE_KEYWORD_FOREIGN); - LITERAL_TOKEN("for", TOKEN_TYPE_KEYWORD_FOR); - LITERAL_TOKEN("return", TOKEN_TYPE_KEYWORD_RETURN); - LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO); - LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC); - LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL); - LITERAL_TOKEN("->", TOKEN_TYPE_RIGHT_ARROW); - LITERAL_TOKEN("<-", TOKEN_TYPE_RIGHT_ARROW); - LITERAL_TOKEN("(", TOKEN_TYPE_OPEN_PAREN); - LITERAL_TOKEN(")", TOKEN_TYPE_CLOSE_PAREN); - LITERAL_TOKEN("{", TOKEN_TYPE_OPEN_BRACE); - LITERAL_TOKEN("}", TOKEN_TYPE_CLOSE_BRACE); - LITERAL_TOKEN("[", TOKEN_TYPE_OPEN_BRACKET); - LITERAL_TOKEN("]", TOKEN_TYPE_CLOSE_BRACKET); - LITERAL_TOKEN("<", TOKEN_TYPE_OPEN_ANGLE); - LITERAL_TOKEN(">", TOKEN_TYPE_CLOSE_ANGLE); - LITERAL_TOKEN("+", TOKEN_TYPE_SYM_PLUS); - LITERAL_TOKEN("-", TOKEN_TYPE_SYM_MINUS); - LITERAL_TOKEN("*", TOKEN_TYPE_SYM_STAR); - LITERAL_TOKEN(".", TOKEN_TYPE_SYM_DOT); - LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT); - LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH); - LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH); - LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON); - LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON); - LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA); - LITERAL_TOKEN("=", TOKEN_TYPE_SYM_EQUALS); - LITERAL_TOKEN("`", TOKEN_TYPE_SYM_GRAVE); - LITERAL_TOKEN("~", TOKEN_TYPE_SYM_TILDE); - LITERAL_TOKEN("!", TOKEN_TYPE_SYM_BANG); - LITERAL_TOKEN("^", TOKEN_TYPE_SYM_CARET); - LITERAL_TOKEN("&", TOKEN_TYPE_SYM_AMPERSAND); - - // Symbols - if (char_is_alpha(*tk.token)) { - u64 len = 0; - while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) { - len++; - INCREMENT_CURR_TOKEN(tokenizer); - } - - tk.length = len; - tk.type = TOKEN_TYPE_SYMBOL; - goto token_parsed; - } - - // String literal - if (*tk.token == '"') { - u64 len = 0; - u64 slash_count = 0; - - INCREMENT_CURR_TOKEN(tokenizer); - - while (!(*tokenizer->curr == '"' && slash_count == 0)) { - len++; - - if (*tokenizer->curr == '\\') { - slash_count += 1; - slash_count %= 2; - } else { - slash_count = 0; - } - - INCREMENT_CURR_TOKEN(tokenizer); - } - - INCREMENT_CURR_TOKEN(tokenizer); - - tk.token++; - tk.type = TOKEN_TYPE_LITERAL_STRING; - tk.length = len; - goto token_parsed; - } - - // Number literal - if (char_is_num(*tokenizer->curr)) { - u64 len = 0; - while (char_is_num(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '.') { - len++; - INCREMENT_CURR_TOKEN(tokenizer); - } - - tk.type = TOKEN_TYPE_LITERAL_NUMERIC; - tk.length = len; - } - - INCREMENT_CURR_TOKEN(tokenizer); - -token_parsed: - bh_arr_push(tokenizer->tokens, tk); - - return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1]; -} - -OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) { - OnyxTokenizer tknizer = { - .start = fc->data, - .curr = fc->data, - .end = fc->data + fc->length, - - .filename = fc->filename, - - .line_number = 1, - .line_start = fc->data, - .tokens = NULL, - }; - - bh_arr_new(allocator, tknizer.tokens, 512); - return tknizer; -} - -void onyx_tokenizer_free(OnyxTokenizer* tokenizer) { - bh_arr_free(tokenizer->tokens); -} - -void onyx_lex_tokens(OnyxTokenizer* tokenizer) { - OnyxToken* tk; - do { - tk = onyx_get_token(tokenizer); - } while (tk->type != TOKEN_TYPE_END_STREAM); -} +#include "bh.h" +#include "onyxlex.h" + +static const char* onyx_token_type_names[] = { + "TOKEN_TYPE_UNKNOWN", + "TOKEN_TYPE_END_STREAM", + + "TOKEN_TYPE_COMMENT", + + "struct", //"TOKEN_TYPE_KEYWORD_STRUCT", + "use", //"TOKEN_TYPE_KEYWORD_USE", + "export", //"TOKEN_TYPE_KEYWORD_EXPORT", + "if", //"TOKEN_TYPE_KEYWORD_IF", + "else", //"TOKEN_TYPE_KEYWORD_ELSE", + "for", //"TOKEN_TYPE_KEYWORD_FOR", + "do", //"TOKEN_TYPE_KEYWORD_DO", + "return", //"TOKEN_TYPE_KEYWORD_RETURN", + "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN", + "proc", //"TOKEN_TYPE_KEYWORD_PROC", + "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", + + "->", //"TOKEN_TYPE_RIGHT_ARROW", + "<-", //"TOKEN_TYPE_LEFT_ARROW", + "(", //"TOKEN_TYPE_OPEN_PAREN", + ")", //"TOKEN_TYPE_CLOSE_PAREN", + "{", //"TOKEN_TYPE_OPEN_BRACE", + "}", //"TOKEN_TYPE_CLOSE_BRACE", + "[", //"TOKEN_TYPE_OPEN_BRACKET", + "]", //"TOKEN_TYPE_CLOSE_BRACKET", + "<", //"TOKEN_TYPE_OPEN_ANGLE", + ">", //"TOKEN_TYPE_CLOSE_ANGLE", + + "+", // "TOKEN_TYPE_SYM_PLUS", + "-", // "TOKEN_TYPE_SYM_MINUS", + "*", // "TOKEN_TYPE_SYM_STAR", + "%", // "TOKEN_TYPE_SYM_PERCENT", + ".", // "TOKEN_TYPE_SYM_DOT", + "/", // "TOKEN_TYPE_SYM_FSLASH", + "\\", // "TOKEN_TYPE_SYM_BSLASH", + ":", // "TOKEN_TYPE_SYM_COLON", + ";", // "TOKEN_TYPE_SYM_SEMICOLON", + ",", // "TOKEN_TYPE_SYM_COMMA", + "=", // "TOKEN_TYPE_SYM_EQUALS", + "`", // "TOKEN_TYPE_SYM_GRAVE", + "~", // "TOKEN_TYPE_SYM_TILDE", + "!", // "TOKEN_TYPE_SYM_BANG", + "^", // "TOKEN_TYPE_SYM_CARET", + "&", // "TOKEN_TYPE_SYM_AMPERSAND", + + "TOKEN_TYPE_SYMBOL", + "TOKEN_TYPE_LITERAL_STRING", + "TOKEN_TYPE_LITERAL_NUMERIC", + + "TOKEN_TYPE_COUNT" +}; + +#ifndef LITERAL_TOKEN +#define LITERAL_TOKEN(token, token_type) \ + if (token_lit(tokenizer, &tk, token, token_type)) goto token_parsed; +#endif + +#ifndef INCREMENT_CURR_TOKEN +#define INCREMENT_CURR_TOKEN(tkn) { \ + if (*(tkn)->curr == '\n') { \ + (tkn)->line_number++; \ + (tkn)->line_start = (tkn)->curr + 1; \ + } \ + (tkn)->curr++; \ +} +#endif + +static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTokenType type) { + i64 len = chars_match(tokenizer->curr, lit); + if (len > 0) { + tk->type = type; + tk->token = tokenizer->curr; + tk->length = len; + tk->pos.line = tokenizer->line_number; + tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; + + tokenizer->curr += len; + + return 1; + } + return 0; +} + +const char* onyx_get_token_type_name(OnyxTokenType tkn_type) { + return onyx_token_type_names[tkn_type]; +} + +void onyx_token_null_toggle(OnyxToken tkn) { + static char backup = 0; + char tmp = tkn.token[tkn.length]; + tkn.token[tkn.length] = backup; + backup = tmp; +} + +OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { + OnyxToken tk; + + // Skip whitespace + while (char_is_whitespace(*tokenizer->curr) && tokenizer->curr != tokenizer->end) + INCREMENT_CURR_TOKEN(tokenizer) + + tk.type = TOKEN_TYPE_UNKNOWN; + tk.token = tokenizer->curr; + tk.length = 1; + tk.pos.filename = tokenizer->filename; + tk.pos.line = tokenizer->line_number; + tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; + + if (tokenizer->curr == tokenizer->end) { + tk.type = TOKEN_TYPE_END_STREAM; + goto token_parsed; + } + + // Comments + if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { + tokenizer->curr += 2; + tk.type = TOKEN_TYPE_COMMENT; + tk.token = tokenizer->curr; + u16 layers = 1; + + while (layers >= 1) { + INCREMENT_CURR_TOKEN(tokenizer); + + if (tokenizer->curr == tokenizer->end) { + tk.type = TOKEN_TYPE_END_STREAM; + break; + } + + if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { + layers++; + INCREMENT_CURR_TOKEN(tokenizer); + } + + if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') { + layers--; + INCREMENT_CURR_TOKEN(tokenizer); + } + } + + INCREMENT_CURR_TOKEN(tokenizer); + + tk.length = tokenizer->curr - tk.token - 2; + goto token_parsed; + } + + LITERAL_TOKEN("struct", TOKEN_TYPE_KEYWORD_STRUCT); + LITERAL_TOKEN("export", TOKEN_TYPE_KEYWORD_EXPORT); + LITERAL_TOKEN("use", TOKEN_TYPE_KEYWORD_USE); + LITERAL_TOKEN("if", TOKEN_TYPE_KEYWORD_IF); + LITERAL_TOKEN("else", TOKEN_TYPE_KEYWORD_ELSE); + LITERAL_TOKEN("foreign", TOKEN_TYPE_KEYWORD_FOREIGN); + LITERAL_TOKEN("for", TOKEN_TYPE_KEYWORD_FOR); + LITERAL_TOKEN("return", TOKEN_TYPE_KEYWORD_RETURN); + LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO); + LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC); + LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL); + LITERAL_TOKEN("->", TOKEN_TYPE_RIGHT_ARROW); + LITERAL_TOKEN("<-", TOKEN_TYPE_RIGHT_ARROW); + LITERAL_TOKEN("(", TOKEN_TYPE_OPEN_PAREN); + LITERAL_TOKEN(")", TOKEN_TYPE_CLOSE_PAREN); + LITERAL_TOKEN("{", TOKEN_TYPE_OPEN_BRACE); + LITERAL_TOKEN("}", TOKEN_TYPE_CLOSE_BRACE); + LITERAL_TOKEN("[", TOKEN_TYPE_OPEN_BRACKET); + LITERAL_TOKEN("]", TOKEN_TYPE_CLOSE_BRACKET); + LITERAL_TOKEN("<", TOKEN_TYPE_OPEN_ANGLE); + LITERAL_TOKEN(">", TOKEN_TYPE_CLOSE_ANGLE); + LITERAL_TOKEN("+", TOKEN_TYPE_SYM_PLUS); + LITERAL_TOKEN("-", TOKEN_TYPE_SYM_MINUS); + LITERAL_TOKEN("*", TOKEN_TYPE_SYM_STAR); + LITERAL_TOKEN(".", TOKEN_TYPE_SYM_DOT); + LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT); + LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH); + LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH); + LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON); + LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON); + LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA); + LITERAL_TOKEN("=", TOKEN_TYPE_SYM_EQUALS); + LITERAL_TOKEN("`", TOKEN_TYPE_SYM_GRAVE); + LITERAL_TOKEN("~", TOKEN_TYPE_SYM_TILDE); + LITERAL_TOKEN("!", TOKEN_TYPE_SYM_BANG); + LITERAL_TOKEN("^", TOKEN_TYPE_SYM_CARET); + LITERAL_TOKEN("&", TOKEN_TYPE_SYM_AMPERSAND); + + // Symbols + if (char_is_alpha(*tk.token)) { + u64 len = 0; + while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) { + len++; + INCREMENT_CURR_TOKEN(tokenizer); + } + + tk.length = len; + tk.type = TOKEN_TYPE_SYMBOL; + goto token_parsed; + } + + // String literal + if (*tk.token == '"') { + u64 len = 0; + u64 slash_count = 0; + + INCREMENT_CURR_TOKEN(tokenizer); + + while (!(*tokenizer->curr == '"' && slash_count == 0)) { + len++; + + if (*tokenizer->curr == '\\') { + slash_count += 1; + slash_count %= 2; + } else { + slash_count = 0; + } + + INCREMENT_CURR_TOKEN(tokenizer); + } + + INCREMENT_CURR_TOKEN(tokenizer); + + tk.token++; + tk.type = TOKEN_TYPE_LITERAL_STRING; + tk.length = len; + goto token_parsed; + } + + // Number literal + if (char_is_num(*tokenizer->curr)) { + u64 len = 0; + while (char_is_num(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '.') { + len++; + INCREMENT_CURR_TOKEN(tokenizer); + } + + tk.type = TOKEN_TYPE_LITERAL_NUMERIC; + tk.length = len; + } + + INCREMENT_CURR_TOKEN(tokenizer); + +token_parsed: + bh_arr_push(tokenizer->tokens, tk); + + return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1]; +} + +OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) { + OnyxTokenizer tknizer = { + .start = fc->data, + .curr = fc->data, + .end = fc->data + fc->length, + + .filename = fc->filename, + + .line_number = 1, + .line_start = fc->data, + .tokens = NULL, + }; + + bh_arr_new(allocator, tknizer.tokens, 512); + return tknizer; +} + +void onyx_tokenizer_free(OnyxTokenizer* tokenizer) { + bh_arr_free(tokenizer->tokens); +} + +void onyx_lex_tokens(OnyxTokenizer* tokenizer) { + OnyxToken* tk; + do { + tk = onyx_get_token(tokenizer); + } while (tk->type != TOKEN_TYPE_END_STREAM); +} diff --git a/onyxlex.h b/onyxlex.h index d2c07771..1fbce3a9 100644 --- a/onyxlex.h +++ b/onyxlex.h @@ -1,89 +1,89 @@ -#ifndef ONYXLEX_H -#define ONYXLEX_H - -#include "bh.h" - -typedef enum OnyxTokenType { - TOKEN_TYPE_UNKNOWN, - TOKEN_TYPE_END_STREAM, - - TOKEN_TYPE_COMMENT, - - TOKEN_TYPE_KEYWORD_STRUCT, - TOKEN_TYPE_KEYWORD_USE, - TOKEN_TYPE_KEYWORD_EXPORT, - TOKEN_TYPE_KEYWORD_IF, - TOKEN_TYPE_KEYWORD_ELSE, - TOKEN_TYPE_KEYWORD_FOR, - TOKEN_TYPE_KEYWORD_DO, - TOKEN_TYPE_KEYWORD_RETURN, - TOKEN_TYPE_KEYWORD_FOREIGN, - TOKEN_TYPE_KEYWORD_PROC, - TOKEN_TYPE_KEYWORD_GLOBAL, - - TOKEN_TYPE_RIGHT_ARROW, - TOKEN_TYPE_LEFT_ARROW, - TOKEN_TYPE_OPEN_PAREN, - TOKEN_TYPE_CLOSE_PAREN, - TOKEN_TYPE_OPEN_BRACE, - TOKEN_TYPE_CLOSE_BRACE, - TOKEN_TYPE_OPEN_BRACKET, - TOKEN_TYPE_CLOSE_BRACKET, - TOKEN_TYPE_OPEN_ANGLE, - TOKEN_TYPE_CLOSE_ANGLE, - - TOKEN_TYPE_SYM_PLUS, - TOKEN_TYPE_SYM_MINUS, - TOKEN_TYPE_SYM_STAR, - TOKEN_TYPE_SYM_PERCENT, - TOKEN_TYPE_SYM_DOT, - TOKEN_TYPE_SYM_FSLASH, - TOKEN_TYPE_SYM_BSLASH, - TOKEN_TYPE_SYM_COLON, - TOKEN_TYPE_SYM_SEMICOLON, - TOKEN_TYPE_SYM_COMMA, - TOKEN_TYPE_SYM_EQUALS, - TOKEN_TYPE_SYM_GRAVE, - TOKEN_TYPE_SYM_TILDE, - TOKEN_TYPE_SYM_BANG, - TOKEN_TYPE_SYM_CARET, - TOKEN_TYPE_SYM_AMPERSAND, - - TOKEN_TYPE_SYMBOL, - TOKEN_TYPE_LITERAL_STRING, - TOKEN_TYPE_LITERAL_NUMERIC, - - TOKEN_TYPE_COUNT -} OnyxTokenType; - -typedef struct OnyxFilePos { - const char* filename; - u64 line, column; -} OnyxFilePos; - -typedef struct OnyxToken { - OnyxTokenType type; - isize length; - char* token; - OnyxFilePos pos; -} OnyxToken; - -typedef struct OnyxTokenizer { - char *start, *curr, *end; - - const char* filename; - - char* line_start; - u64 line_number; - - bh_arr(OnyxToken) tokens; -} OnyxTokenizer; - -const char* onyx_get_token_type_name(OnyxTokenType tkn_type); -void onyx_token_null_toggle(OnyxToken tkn); -OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer); -OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc); -void onyx_tokenizer_free(OnyxTokenizer* tokenizer); -void onyx_lex_tokens(OnyxTokenizer* tokenizer); - -#endif +#ifndef ONYXLEX_H +#define ONYXLEX_H + +#include "bh.h" + +typedef enum OnyxTokenType { + TOKEN_TYPE_UNKNOWN, + TOKEN_TYPE_END_STREAM, + + TOKEN_TYPE_COMMENT, + + TOKEN_TYPE_KEYWORD_STRUCT, + TOKEN_TYPE_KEYWORD_USE, + TOKEN_TYPE_KEYWORD_EXPORT, + TOKEN_TYPE_KEYWORD_IF, + TOKEN_TYPE_KEYWORD_ELSE, + TOKEN_TYPE_KEYWORD_FOR, + TOKEN_TYPE_KEYWORD_DO, + TOKEN_TYPE_KEYWORD_RETURN, + TOKEN_TYPE_KEYWORD_FOREIGN, + TOKEN_TYPE_KEYWORD_PROC, + TOKEN_TYPE_KEYWORD_GLOBAL, + + TOKEN_TYPE_RIGHT_ARROW, + TOKEN_TYPE_LEFT_ARROW, + TOKEN_TYPE_OPEN_PAREN, + TOKEN_TYPE_CLOSE_PAREN, + TOKEN_TYPE_OPEN_BRACE, + TOKEN_TYPE_CLOSE_BRACE, + TOKEN_TYPE_OPEN_BRACKET, + TOKEN_TYPE_CLOSE_BRACKET, + TOKEN_TYPE_OPEN_ANGLE, + TOKEN_TYPE_CLOSE_ANGLE, + + TOKEN_TYPE_SYM_PLUS, + TOKEN_TYPE_SYM_MINUS, + TOKEN_TYPE_SYM_STAR, + TOKEN_TYPE_SYM_PERCENT, + TOKEN_TYPE_SYM_DOT, + TOKEN_TYPE_SYM_FSLASH, + TOKEN_TYPE_SYM_BSLASH, + TOKEN_TYPE_SYM_COLON, + TOKEN_TYPE_SYM_SEMICOLON, + TOKEN_TYPE_SYM_COMMA, + TOKEN_TYPE_SYM_EQUALS, + TOKEN_TYPE_SYM_GRAVE, + TOKEN_TYPE_SYM_TILDE, + TOKEN_TYPE_SYM_BANG, + TOKEN_TYPE_SYM_CARET, + TOKEN_TYPE_SYM_AMPERSAND, + + TOKEN_TYPE_SYMBOL, + TOKEN_TYPE_LITERAL_STRING, + TOKEN_TYPE_LITERAL_NUMERIC, + + TOKEN_TYPE_COUNT +} OnyxTokenType; + +typedef struct OnyxFilePos { + const char* filename; + u64 line, column; +} OnyxFilePos; + +typedef struct OnyxToken { + OnyxTokenType type; + isize length; + char* token; + OnyxFilePos pos; +} OnyxToken; + +typedef struct OnyxTokenizer { + char *start, *curr, *end; + + const char* filename; + + char* line_start; + u64 line_number; + + bh_arr(OnyxToken) tokens; +} OnyxTokenizer; + +const char* onyx_get_token_type_name(OnyxTokenType tkn_type); +void onyx_token_null_toggle(OnyxToken tkn); +OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer); +OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc); +void onyx_tokenizer_free(OnyxTokenizer* tokenizer); +void onyx_lex_tokens(OnyxTokenizer* tokenizer); + +#endif diff --git a/onyxmsgs.c b/onyxmsgs.c index 42cb3266..53b35975 100644 --- a/onyxmsgs.c +++ b/onyxmsgs.c @@ -1,44 +1,44 @@ - -#include "onyxmsgs.h" - -static const char* msg_formats[] = { - "expected token '%s', got '%s'", - "unexpected token '%s'", - "unknown type '%s'" -}; - -void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) { - OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage); - msg->type = type; - msg->pos = pos; - - va_list arg_list; - va_start(arg_list, pos); - bh_snprintf_va(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list); - va_end(arg_list); - - OnyxMessage** walker = &msgs->first; - while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next; - while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next; - - msg->next = *walker; - *walker = msg; -} - -void onyx_message_print(OnyxMessages* msgs) { - OnyxMessage* msg = msgs->first; - - while (msg) { - if (msg->pos.filename) { - bh_printf("(%s:%l:%l) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text); - } else { - bh_printf("(%l:%l) %s\n", msg->pos.line, msg->pos.column, msg->text); - } - msg = msg->next; - } -} - -void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { - msgs->allocator = allocator; - msgs->first = NULL; -} + +#include "onyxmsgs.h" + +static const char* msg_formats[] = { + "expected token '%s', got '%s'", + "unexpected token '%s'", + "unknown type '%s'" +}; + +void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) { + OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage); + msg->type = type; + msg->pos = pos; + + va_list arg_list; + va_start(arg_list, pos); + bh_snprintf_va(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list); + va_end(arg_list); + + OnyxMessage** walker = &msgs->first; + while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next; + while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next; + + msg->next = *walker; + *walker = msg; +} + +void onyx_message_print(OnyxMessages* msgs) { + OnyxMessage* msg = msgs->first; + + while (msg) { + if (msg->pos.filename) { + bh_printf("(%s:%l:%l) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text); + } else { + bh_printf("(%l:%l) %s\n", msg->pos.line, msg->pos.column, msg->text); + } + msg = msg->next; + } +} + +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { + msgs->allocator = allocator; + msgs->first = NULL; +} diff --git a/onyxmsgs.h b/onyxmsgs.h index 9d615d98..1a2ae502 100644 --- a/onyxmsgs.h +++ b/onyxmsgs.h @@ -1,36 +1,36 @@ -#ifndef ONYXMSGS_H -#define ONYXMSGS_H - -#include "bh.h" -#include "onyxlex.h" - -#include - -#define ONYX_MSG_BUFFER_SIZE 256 - -typedef enum OnyxMessageType { - ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, - ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, - ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, - - ONYX_MESSAGE_TYPE_COUNT, -} OnyxMessageType; - -typedef struct OnyxMessage { - OnyxMessageType type; - OnyxFilePos pos; - struct OnyxMessage* next; - char text[ONYX_MSG_BUFFER_SIZE]; -} OnyxMessage; - -typedef struct OnyxMessages { - bh_allocator allocator; - - OnyxMessage* first; -} OnyxMessages; - -void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...); -void onyx_message_print(OnyxMessages* msgs); -void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); - -#endif \ No newline at end of file +#ifndef ONYXMSGS_H +#define ONYXMSGS_H + +#include "bh.h" +#include "onyxlex.h" + +#include + +#define ONYX_MSG_BUFFER_SIZE 256 + +typedef enum OnyxMessageType { + ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, + ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, + ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, + + ONYX_MESSAGE_TYPE_COUNT, +} OnyxMessageType; + +typedef struct OnyxMessage { + OnyxMessageType type; + OnyxFilePos pos; + struct OnyxMessage* next; + char text[ONYX_MSG_BUFFER_SIZE]; +} OnyxMessage; + +typedef struct OnyxMessages { + bh_allocator allocator; + + OnyxMessage* first; +} OnyxMessages; + +void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...); +void onyx_message_print(OnyxMessages* msgs); +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); + +#endif diff --git a/onyxparser.c b/onyxparser.c index 27d6f037..475d38fc 100644 --- a/onyxparser.c +++ b/onyxparser.c @@ -1,610 +1,626 @@ - -#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 OnyxAstNode* parse_expression_statement(OnyxParser* parser); -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); - 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); - 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; -} - -static OnyxAstNode* parse_expression_statement(OnyxParser* parser) { - if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) return NULL; - 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; - return assignment; - } - } - - // 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; - return assignment; - } - - default: - parser_prev_token(parser); - } - - return NULL; -} - -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 = parse_expression_statement(parser); - if (ret != NULL) return ret; - } - - 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; -} + +#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; +} diff --git a/onyxparser.h b/onyxparser.h index b8d04e5c..270deba3 100644 --- a/onyxparser.h +++ b/onyxparser.h @@ -1,188 +1,190 @@ -#ifndef ONYXPARSER_H -#define ONYXPARSER_H - -#define BH_NO_STRING -#include "bh.h" - -#include "onyxlex.h" -#include "onyxmsgs.h" - -typedef union OnyxAstNode OnyxAstNode; -typedef struct OnyxAstNodeLocal OnyxAstNodeLocal; -typedef struct OnyxAstNodeScope OnyxAstNodeScope; -typedef struct OnyxAstNodeBlock OnyxAstNodeBlock; -typedef struct OnyxAstNodeParam OnyxAstNodeParam; -typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; - -typedef struct OnyxParser { - OnyxTokenizer *tokenizer; // NOTE: not used since all tokens are lexed before parsing starts - OnyxToken *prev_token; - OnyxToken *curr_token; - - // BUG: This way of handling identifiers will work for now, - // but it will not allow for shadowing. Also, variable names - // cannot be the same as any function or global variable. - // That will get annoying to program. - // NOTE: A table of the current identifiers in the current scope. - // If the identifier doesn't at the time of parsing, it is an error. - // Cleared at the end of a block. - bh_hash(OnyxAstNode*) identifiers; - OnyxAstNodeScope *curr_scope; - - OnyxMessages *msgs; - - bh_allocator allocator; -} OnyxParser; - -typedef enum OnyxAstNodeKind { - ONYX_AST_NODE_KIND_ERROR, - ONYX_AST_NODE_KIND_PROGRAM, - - ONYX_AST_NODE_KIND_FUNCDEF, - ONYX_AST_NODE_KIND_BLOCK, - ONYX_AST_NODE_KIND_SCOPE, - ONYX_AST_NODE_KIND_LOCAL, - - ONYX_AST_NODE_KIND_ADD, - ONYX_AST_NODE_KIND_MINUS, - ONYX_AST_NODE_KIND_MULTIPLY, - ONYX_AST_NODE_KIND_DIVIDE, - ONYX_AST_NODE_KIND_MODULUS, - ONYX_AST_NODE_KIND_NEGATE, - - ONYX_AST_NODE_KIND_TYPE, - ONYX_AST_NODE_KIND_LITERAL, - ONYX_AST_NODE_KIND_CAST, - ONYX_AST_NODE_KIND_PARAM, - ONYX_AST_NODE_KIND_CALL, - ONYX_AST_NODE_KIND_ASSIGNMENT, - ONYX_AST_NODE_KIND_RETURN, - - ONYX_AST_NODE_KIND_EQUAL, - ONYX_AST_NODE_KIND_NOT_EQUAL, - ONYX_AST_NODE_KIND_GREATER, - ONYX_AST_NODE_KIND_GREATER_EQUAL, - ONYX_AST_NODE_KIND_LESS, - ONYX_AST_NODE_KIND_LESS_EQUAL, - ONYX_AST_NODE_KIND_NOT, - - ONYX_AST_NODE_KIND_IF, - ONYX_AST_NODE_KIND_LOOP, - - ONYX_AST_NODE_KIND_COUNT -} OnyxAstNodeKind; - -typedef enum OnyxTypeInfoKind { - ONYX_TYPE_INFO_KIND_UNKNOWN, - ONYX_TYPE_INFO_KIND_VOID, - ONYX_TYPE_INFO_KIND_BOOL, - - ONYX_TYPE_INFO_KIND_UINT8, - ONYX_TYPE_INFO_KIND_UINT16, - ONYX_TYPE_INFO_KIND_UINT32, - ONYX_TYPE_INFO_KIND_UINT64, - - ONYX_TYPE_INFO_KIND_INT8, - ONYX_TYPE_INFO_KIND_INT16, - ONYX_TYPE_INFO_KIND_INT32, - ONYX_TYPE_INFO_KIND_INT64, - - ONYX_TYPE_INFO_KIND_FLOAT32, - ONYX_TYPE_INFO_KIND_FLOAT64, - ONYX_TYPE_INFO_KIND_SOFT_FLOAT, // 64-bits of data but could be treated as 32-bit -} OnyxTypeInfoKind; - -typedef struct OnyxTypeInfo { - OnyxTypeInfoKind kind; - u32 size; // in bytes - const char* name; - u32 is_int : 1; - u32 is_unsigned : 1; - u32 is_float : 1; - u32 is_bool : 1; -} OnyxTypeInfo; - -extern OnyxTypeInfo builtin_types[]; - -typedef enum OnyxAstFlags { - ONYX_AST_FLAG_HAS_RETURN = BH_BIT(1), - ONYX_AST_FLAG_TOP_LEVEL = BH_BIT(2), - ONYX_AST_FLAG_EXPORTED = BH_BIT(3), - ONYX_AST_FLAG_FUNCTION_PARAM = BH_BIT(3), -} OnyxAstFlags; - -struct OnyxAstNodeLocal { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; - OnyxTypeInfo *type; - OnyxAstNodeLocal *prev_local; - OnyxAstNode *unused1; - OnyxAstNode *unused2; -}; - -struct OnyxAstNodeScope { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; // NOTE: UNUSED - OnyxTypeInfo *type; // NOTE: UNUSED - OnyxAstNodeScope *prev_scope; - OnyxAstNodeLocal *last_local; - OnyxAstNode *unused; -}; - -struct OnyxAstNodeBlock { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; - OnyxTypeInfo *return_type; - OnyxAstNode *next; - OnyxAstNode *body; - OnyxAstNodeScope *scope; // NOTE: Only set on blocks belonging to functions -}; - -struct OnyxAstNodeParam { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; // Symbol name i.e. 'a', 'b' - OnyxTypeInfo *type; - OnyxAstNodeParam *next; - u64 param_count; - OnyxAstNode *right; -}; - -struct OnyxAstNodeFuncDef { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; // This will point to the symbol token to identify it - OnyxTypeInfo *return_type; - OnyxAstNode *next; - OnyxAstNodeBlock *body; - OnyxAstNodeParam *params; -}; - -union OnyxAstNode { - - // Generic node structure for capturing all binary ops and statements - struct { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; - OnyxTypeInfo *type; - OnyxAstNode *next; - OnyxAstNode *left; - OnyxAstNode *right; - }; - - OnyxAstNodeBlock as_block; - OnyxAstNodeFuncDef as_funcdef; - OnyxAstNodeParam as_param; -}; - -const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind); -OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind); -OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs); -void onyx_parser_free(OnyxParser* parser); -OnyxAstNode* onyx_parse(OnyxParser *parser); - -#endif // #ifndef ONYXPARSER_H +#ifndef ONYXPARSER_H +#define ONYXPARSER_H + +#define BH_NO_STRING +#include "bh.h" + +#include "onyxlex.h" +#include "onyxmsgs.h" + +typedef union OnyxAstNode OnyxAstNode; +typedef struct OnyxAstNodeLocal OnyxAstNodeLocal; +typedef struct OnyxAstNodeScope OnyxAstNodeScope; +typedef struct OnyxAstNodeBlock OnyxAstNodeBlock; +typedef struct OnyxAstNodeParam OnyxAstNodeParam; +typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; + +typedef struct OnyxParser { + OnyxTokenizer *tokenizer; // NOTE: not used since all tokens are lexed before parsing starts + OnyxToken *prev_token; + OnyxToken *curr_token; + + // BUG: This way of handling identifiers will work for now, + // but it will not allow for shadowing. Also, variable names + // cannot be the same as any function or global variable. + // That will get annoying to program. + // NOTE: A table of the current identifiers in the current scope. + // If the identifier doesn't at the time of parsing, it is an error. + // Cleared at the end of a block. + bh_hash(OnyxAstNode*) identifiers; + OnyxAstNodeScope *curr_scope; + + OnyxMessages *msgs; + + bh_allocator allocator; +} OnyxParser; + +typedef enum OnyxAstNodeKind { + ONYX_AST_NODE_KIND_ERROR, + ONYX_AST_NODE_KIND_PROGRAM, + + ONYX_AST_NODE_KIND_FUNCDEF, + ONYX_AST_NODE_KIND_BLOCK, + ONYX_AST_NODE_KIND_SCOPE, + ONYX_AST_NODE_KIND_LOCAL, + + ONYX_AST_NODE_KIND_ADD, + ONYX_AST_NODE_KIND_MINUS, + ONYX_AST_NODE_KIND_MULTIPLY, + ONYX_AST_NODE_KIND_DIVIDE, + ONYX_AST_NODE_KIND_MODULUS, + ONYX_AST_NODE_KIND_NEGATE, + + ONYX_AST_NODE_KIND_TYPE, + ONYX_AST_NODE_KIND_LITERAL, + ONYX_AST_NODE_KIND_CAST, + ONYX_AST_NODE_KIND_PARAM, + ONYX_AST_NODE_KIND_CALL, + ONYX_AST_NODE_KIND_ASSIGNMENT, + ONYX_AST_NODE_KIND_RETURN, + + ONYX_AST_NODE_KIND_EQUAL, + ONYX_AST_NODE_KIND_NOT_EQUAL, + ONYX_AST_NODE_KIND_GREATER, + ONYX_AST_NODE_KIND_GREATER_EQUAL, + ONYX_AST_NODE_KIND_LESS, + ONYX_AST_NODE_KIND_LESS_EQUAL, + ONYX_AST_NODE_KIND_NOT, + + ONYX_AST_NODE_KIND_IF, + ONYX_AST_NODE_KIND_LOOP, + + ONYX_AST_NODE_KIND_COUNT +} OnyxAstNodeKind; + +typedef enum OnyxTypeInfoKind { + ONYX_TYPE_INFO_KIND_UNKNOWN, + ONYX_TYPE_INFO_KIND_VOID, + ONYX_TYPE_INFO_KIND_BOOL, + + ONYX_TYPE_INFO_KIND_UINT8, + ONYX_TYPE_INFO_KIND_UINT16, + ONYX_TYPE_INFO_KIND_UINT32, + ONYX_TYPE_INFO_KIND_UINT64, + + ONYX_TYPE_INFO_KIND_INT8, + ONYX_TYPE_INFO_KIND_INT16, + ONYX_TYPE_INFO_KIND_INT32, + ONYX_TYPE_INFO_KIND_INT64, + + ONYX_TYPE_INFO_KIND_FLOAT32, + ONYX_TYPE_INFO_KIND_FLOAT64, + ONYX_TYPE_INFO_KIND_SOFT_FLOAT, // 64-bits of data but could be treated as 32-bit +} OnyxTypeInfoKind; + +typedef struct OnyxTypeInfo { + OnyxTypeInfoKind kind; + u32 size; // in bytes + const char* name; + u32 is_int : 1; + u32 is_unsigned : 1; + u32 is_float : 1; + u32 is_bool : 1; +} OnyxTypeInfo; + +extern OnyxTypeInfo builtin_types[]; + +// NOTE: Some of these flags will overlap since there are +// only 32-bits of flags to play with +typedef enum OnyxAstFlags { + // Top-level flags + ONYX_AST_FLAG_EXPORTED = BH_BIT(1), +} OnyxAstFlags; + +struct OnyxAstNodeLocal { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *type; + OnyxAstNodeLocal *prev_local; + OnyxAstNode *shadowed; + OnyxAstNode *unused2; +}; + +struct OnyxAstNodeScope { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // NOTE: UNUSED + OnyxTypeInfo *type; // NOTE: UNUSED + OnyxAstNodeScope *prev_scope; + OnyxAstNodeLocal *last_local; + OnyxAstNode *unused; +}; + +struct OnyxAstNodeBlock { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *return_type; + OnyxAstNode *next; + OnyxAstNode *body; + OnyxAstNodeScope *scope; // NOTE: Only set on blocks belonging to functions +}; + +struct OnyxAstNodeParam { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // Symbol name i.e. 'a', 'b' + OnyxTypeInfo *type; + OnyxAstNodeParam *next; + u64 param_count; + OnyxAstNode *right; +}; + +struct OnyxAstNodeFuncDef { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // This will point to the symbol token to identify it + OnyxTypeInfo *return_type; + OnyxAstNode *next; + OnyxAstNodeBlock *body; + OnyxAstNodeParam *params; +}; + +union OnyxAstNode { + + // Generic node structure for capturing all binary ops and statements + struct { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *type; + OnyxAstNode *next; + OnyxAstNode *left; + OnyxAstNode *right; + }; + + OnyxAstNodeBlock as_block; + OnyxAstNodeFuncDef as_funcdef; + OnyxAstNodeParam as_param; + OnyxAstNodeLocal as_local; + OnyxAstNodeScope as_scope; +}; + +const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind); +OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind); +OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs); +void onyx_parser_free(OnyxParser* parser); +OnyxAstNode* onyx_parse(OnyxParser *parser); + +#endif // #ifndef ONYXPARSER_H diff --git a/onyxutils.c b/onyxutils.c index 74b96e6b..02d191f5 100644 --- a/onyxutils.c +++ b/onyxutils.c @@ -1,26 +1,27 @@ -#include "onyxutils.h" - -void onyx_ast_print(OnyxAstNode* node) { - if (node == NULL) return; - - bh_printf("%s <%d> ", onyx_ast_node_kind_string(node->kind), node->flags); - if (node->token) - bh_printf("[%b] ", node->token->token, (int) node->token->length); - - if ((i64) node->left > 10) { // HACK: but okay - bh_printf("("); - onyx_ast_print(node->left); - bh_printf(") "); - } - if ((i64) node->right > 10) { // HACK: but okay - bh_printf("("); - onyx_ast_print(node->right); - bh_printf(")"); - } - - if (node->next) { - bh_printf("{"); - onyx_ast_print(node->next); - bh_printf("}"); - } -} +#include "onyxutils.h" + +void onyx_ast_print(OnyxAstNode* node) { + if (node == NULL) return; + + bh_printf("%s <%d> ", onyx_ast_node_kind_string(node->kind), node->flags); + if (node->token) + bh_printf("[%b] ", node->token->token, (int) node->token->length); + + if ((i64) node->left > 10) { // HACK: but okay + bh_printf("("); + onyx_ast_print(node->left); + bh_printf(") "); + } + if ((i64) node->right > 10) { // HACK: but okay + bh_printf("("); + onyx_ast_print(node->right); + bh_printf(")"); + } + + if (node->next) { + bh_printf("{"); + onyx_ast_print(node->next); + bh_printf("}"); + } + +} diff --git a/onyxutils.h b/onyxutils.h index a8e2e3a5..4da320b5 100644 --- a/onyxutils.h +++ b/onyxutils.h @@ -1,5 +1,5 @@ -#include "bh.h" - -#include "onyxparser.h" - -void onyx_ast_print(OnyxAstNode* program); +#include "bh.h" + +#include "onyxparser.h" + +void onyx_ast_print(OnyxAstNode* program); diff --git a/progs/minimal.onyx b/progs/minimal.onyx index 81dc7c8c..d4f55aca 100644 --- a/progs/minimal.onyx +++ b/progs/minimal.onyx @@ -1,14 +1,16 @@ -/* This is a comment */ - -log :: proc (a i32, b i32) -> i32 ---; - -export add :: proc (a i32, b i32) -> i32 { - /* More comments */ - return a + b; -}; - -export mul :: proc (a i32, b i32) -> i32 { - c: i32 = a - b; - d := a + 2; - return c * d; -}; +/* This is a comment */ + +log :: proc (a i32, b i32) -> i32 ---; + +export add :: proc (a i32, b i32) -> i32 { + /* More comments */ + return a + b; +}; + +export mul :: proc (a i32, b i32) -> i32 { + c: i32 = a - b; + d := a + 2; + + e: i32; + return c * d; +}; -- 2.25.1