From: Brendan Hansen Date: Tue, 2 Jun 2020 02:22:51 +0000 (-0500) Subject: Cleaning up directory structure X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fa6d6fcd51c46ad5b56e02d2a1d83215e565cfe9;p=onyx.git Cleaning up directory structure --- diff --git a/Makefile b/Makefile index 156ee281..e3213a83 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ OBJ_FILES=\ - onyxlex.o \ - onyxparser.o \ - onyxmsgs.o \ - onyxutils.o \ - onyx.o + src/onyxlex.o \ + src/onyxparser.o \ + src/onyxmsgs.o \ + src/onyxutils.o \ + src/onyx.o CC=gcc -INCLUDES= +INCLUDES=-I./include LIBS= FLAGS=-g -%.o: %.c bh.h +%.o: %.c include/bh.h $(CC) $(FLAGS) -c $< -o $@ $(INCLUDES) onyx: $(OBJ_FILES) diff --git a/bh.h b/bh.h deleted file mode 100644 index 95772a75..00000000 --- a/bh.h +++ /dev/null @@ -1,1753 +0,0 @@ -#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/include/bh.h b/include/bh.h new file mode 100644 index 00000000..95772a75 --- /dev/null +++ b/include/bh.h @@ -0,0 +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 diff --git a/include/onyxlex.h b/include/onyxlex.h new file mode 100644 index 00000000..b1b5eb06 --- /dev/null +++ b/include/onyxlex.h @@ -0,0 +1,91 @@ +#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_CONST, + TOKEN_TYPE_KEYWORD_FOREIGN, + TOKEN_TYPE_KEYWORD_PROC, + TOKEN_TYPE_KEYWORD_GLOBAL, + TOKEN_TYPE_KEYWORD_CAST, + + 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/include/onyxmsgs.h b/include/onyxmsgs.h new file mode 100644 index 00000000..6c6a19e8 --- /dev/null +++ b/include/onyxmsgs.h @@ -0,0 +1,43 @@ +#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_NOT_LVAL, + ONYX_MESSAGE_TYPE_ASSIGN_CONST, + ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, + ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION, + ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, + ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, + + 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); +b32 onyx_message_has_errors(OnyxMessages* msgs); +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); + +#endif diff --git a/include/onyxparser.h b/include/onyxparser.h new file mode 100644 index 00000000..b2496f69 --- /dev/null +++ b/include/onyxparser.h @@ -0,0 +1,195 @@ +#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; + u32 is_known : 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(0), + ONYX_AST_FLAG_LVAL = BH_BIT(1), + ONYX_AST_FLAG_CONST = BH_BIT(2), + ONYX_AST_FLAG_COMPTIME = BH_BIT(3), +} OnyxAstFlags; + +struct OnyxAstNodeLocal { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *type; + OnyxAstNodeLocal *prev_local; + OnyxAstNode *shadowed; + OnyxAstNode *unused; +}; + +// NOTE: Needs to have shadowed in the same position as OnyxAstNodeLocal +struct OnyxAstNodeParam { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // Symbol name i.e. 'a', 'b' + OnyxTypeInfo *type; + OnyxAstNodeParam *next; + OnyxAstNode *shadowed; + u64 param_count; +}; + +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 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/include/onyxutils.h b/include/onyxutils.h new file mode 100644 index 00000000..63b6f601 --- /dev/null +++ b/include/onyxutils.h @@ -0,0 +1,5 @@ +#include "bh.h" + +#include "onyxparser.h" + +void onyx_ast_print(OnyxAstNode* program, i32 indent); diff --git a/onyx b/onyx index 37a1774a..a16d7ae6 100755 Binary files a/onyx and b/onyx differ diff --git a/onyx.c b/onyx.c deleted file mode 100644 index ceb09703..00000000 --- a/onyx.c +++ /dev/null @@ -1,71 +0,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; - -#if 0 - 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); - } -#endif - - 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); - - // NOTE: if there are errors, assume the parse tree was generated wrong, - // even if it may have still been generated correctly. - if (onyx_message_has_errors(&msgs)) { - onyx_message_print(&msgs); - goto main_exit; - } else { - onyx_ast_print(program, 0); - bh_printf("\nNo errors.\n"); - } - - -main_exit: // NOTE: Cleanup, since C doesn't have defer - bh_arena_free(&msg_arena); - bh_arena_free(&ast_arena); - onyx_parser_free(&parser); - onyx_tokenizer_free(&tokenizer); - bh_file_contents_delete(&fc); - - return 0; -} diff --git a/onyxlex.c b/onyxlex.c deleted file mode 100644 index d76c93f3..00000000 --- a/onyxlex.c +++ /dev/null @@ -1,279 +0,0 @@ -#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", - "const", //"TOKEN_TYPE_KEYWORD_CONST", - "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN", - "proc", //"TOKEN_TYPE_KEYWORD_PROC", - "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", - "as", //"TOKEN_TYPE_KEYWORD_CAST", - - "->", //"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("const", TOKEN_TYPE_KEYWORD_CONST); - LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO); - LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC); - LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL); - LITERAL_TOKEN("as", TOKEN_TYPE_KEYWORD_CAST); - 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)) { - u32 len = 1; - 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 deleted file mode 100644 index b1b5eb06..00000000 --- a/onyxlex.h +++ /dev/null @@ -1,91 +0,0 @@ -#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_CONST, - TOKEN_TYPE_KEYWORD_FOREIGN, - TOKEN_TYPE_KEYWORD_PROC, - TOKEN_TYPE_KEYWORD_GLOBAL, - TOKEN_TYPE_KEYWORD_CAST, - - 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 deleted file mode 100644 index a2e55512..00000000 --- a/onyxmsgs.c +++ /dev/null @@ -1,54 +0,0 @@ - -#include "onyxmsgs.h" - -static const char* msg_formats[] = { - "expected token '%s', got '%s'", - "unexpected token '%s'", - "unknown type '%s'", - "expected lval '%s'", - "attempt to assign to constant '%s'", - "unknown symbol '%s'", - "redefinition of function '%s'", - "mismatched types for binary operator, '%s', '%s'", - "mismatched types on assignment, '%s', '%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; - } -} - -b32 onyx_message_has_errors(OnyxMessages* msgs) { - return msgs->first != NULL; -} - -void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { - msgs->allocator = allocator; - msgs->first = NULL; -} diff --git a/onyxmsgs.h b/onyxmsgs.h deleted file mode 100644 index 6c6a19e8..00000000 --- a/onyxmsgs.h +++ /dev/null @@ -1,43 +0,0 @@ -#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_NOT_LVAL, - ONYX_MESSAGE_TYPE_ASSIGN_CONST, - ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, - ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION, - ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, - ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, - - 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); -b32 onyx_message_has_errors(OnyxMessages* msgs); -void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); - -#endif diff --git a/onyxparser.c b/onyxparser.c deleted file mode 100644 index 103d8e3c..00000000 --- a/onyxparser.c +++ /dev/null @@ -1,737 +0,0 @@ - -#include "onyxlex.h" -#include "onyxmsgs.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", 0, 0, 0, 0, 1 }, - - { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1, 1 }, - - { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0, 1 }, - { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0, 1 }, - - { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0, 1 }, - { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0, 1 }, - - { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0, 1 }, - { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0, 1}, - { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0, 1 }, - - { 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, OnyxAstNode* ident, b32 is_local); -static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident); -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_symbol_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; - } -} - -static void find_token(OnyxParser* parser, OnyxTokenType token_type) { - while (parser->curr_token->type != token_type && !is_terminating_token(parser->curr_token->type)) { - parser_next_token(parser); - } -} - -// 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) { - remove_identifier(parser, (OnyxAstNode *) walker); - } - - 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, OnyxAstNode* ident, b32 is_local) { - OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident; - if (is_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(OnyxAstNode*, 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 void remove_identifier(OnyxParser* parser, OnyxAstNode* ident) { - OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident; - - onyx_token_null_toggle(*local->token); - if (local->shadowed) { - bh_hash_put(OnyxAstNode*, parser->identifiers, local->token->token, local->shadowed); - } else { - bh_hash_delete(OnyxAstNode*, parser->identifiers, local->token->token); - } - 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); - if (sym_node == NULL) { - onyx_token_null_toggle(*sym_token); - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, - sym_token->pos, sym_token->token); - onyx_token_null_toggle(*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); - lit_node->flags |= ONYX_AST_FLAG_COMPTIME; - 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) { - OnyxToken* bin_op_tok = parser->curr_token; - parser_next_token(parser); - - OnyxAstNode* right = parse_factor(parser); - - // NOTE: Typechecking like this will not be here for long but - // want to start working on it early - if (left->type != right->type) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, - bin_op_tok->pos, - left->type->name, right->type->name); - return &error_node; - } - - OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind); - bin_op->left = left; - bin_op->right = right; - bin_op->type = left->type; - 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; - } - case TOKEN_TYPE_KEYWORD_CAST: - { - parser_next_token(parser); - OnyxTypeInfo* type = parse_type(parser); - - // NOTE: This may be premature in the parser but a cast - // node doesn't need to exist if the source and target - // types are the same - if (type == left->type) return left; - - OnyxAstNode* cast_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_CAST); - cast_node->type = type; - cast_node->left = left; - - return cast_node; - } - } - - 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_symbol_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]; - u32 flags = ONYX_AST_FLAG_LVAL; - - // NOTE: var: const ... - if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_CONST) { - parser_next_token(parser); - flags |= ONYX_AST_FLAG_CONST; - } - - // 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; - local->flags |= flags; - - insert_identifier(parser, (OnyxAstNode *) local, 1); - - if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) { - OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); - assignment->token = parser->curr_token; - parser_next_token(parser); - - OnyxAstNode* expr = parse_expression(parser); - assignment->right = expr; - assignment->left = (OnyxAstNode*) local; - - if (local->type->is_known && local->type != expr->type) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, - assignment->token->pos, - local->type->name, expr->type->name); - return 1; - } else if (!local->type->is_known) { - local->type = expr->type; - } - *ret = assignment; - } - return 1; - } - - // NOTE: Assignment - case TOKEN_TYPE_SYM_EQUALS: - { - parser_next_token(parser); - - OnyxAstNode* lval = lookup_identifier(parser, symbol); - - if (lval != NULL && lval->flags & ONYX_AST_FLAG_LVAL && (lval->flags & ONYX_AST_FLAG_CONST) == 0) { - 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; - } - - onyx_token_null_toggle(*symbol); - if (lval == NULL) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, - symbol->pos, symbol->token); - } - - else if ((lval->flags & ONYX_AST_FLAG_LVAL) == 0) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_NOT_LVAL, - symbol->pos, symbol->token); - } - - else if (lval->flags & ONYX_AST_FLAG_CONST) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_ASSIGN_CONST, - symbol->pos, symbol->token); - } - onyx_token_null_toggle(*symbol); - - find_token(parser, TOKEN_TYPE_SYM_SEMICOLON); - 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_symbol_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)); - - find_token(parser, TOKEN_TYPE_SYM_SEMICOLON); - } - 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; - - for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { - insert_identifier(parser, (OnyxAstNode *) p, 0); - } - - func_def->body = parse_block(parser, 1); - - for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { - remove_identifier(parser, (OnyxAstNode *) p); - } - - 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; - } - - 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; - - onyx_token_null_toggle(*symbol); - - if (!bh_hash_has(OnyxAstNode *, parser->identifiers, symbol->token)) { - bh_hash_put(OnyxAstNode *, parser->identifiers, symbol->token, (OnyxAstNode *) func_def); - } else { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION, - symbol->pos, - symbol->token); - - // NOTE: I really wish C had defer... - onyx_token_null_toggle(*symbol); - return NULL; - } - - onyx_token_null_toggle(*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 deleted file mode 100644 index b2496f69..00000000 --- a/onyxparser.h +++ /dev/null @@ -1,195 +0,0 @@ -#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; - u32 is_known : 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(0), - ONYX_AST_FLAG_LVAL = BH_BIT(1), - ONYX_AST_FLAG_CONST = BH_BIT(2), - ONYX_AST_FLAG_COMPTIME = BH_BIT(3), -} OnyxAstFlags; - -struct OnyxAstNodeLocal { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; - OnyxTypeInfo *type; - OnyxAstNodeLocal *prev_local; - OnyxAstNode *shadowed; - OnyxAstNode *unused; -}; - -// NOTE: Needs to have shadowed in the same position as OnyxAstNodeLocal -struct OnyxAstNodeParam { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; // Symbol name i.e. 'a', 'b' - OnyxTypeInfo *type; - OnyxAstNodeParam *next; - OnyxAstNode *shadowed; - u64 param_count; -}; - -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 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 deleted file mode 100644 index e40e0cb1..00000000 --- a/onyxutils.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "onyxutils.h" -#include "onyxlex.h" -#include "onyxparser.h" - -#define print_indent { if (indent > 0) bh_printf("\n"); for (int i = 0; i < indent; i++) bh_printf(" "); } - -void onyx_ast_print(OnyxAstNode* node, i32 indent) { - if (node == NULL) return; - - print_indent; - bh_printf("(%d) %s ", node->flags, onyx_ast_node_kind_string(node->kind)); - - switch (node->kind) { - case ONYX_AST_NODE_KIND_PROGRAM: { - if (node->next) - onyx_ast_print(node->next, indent + 1); - - break; - } - - case ONYX_AST_NODE_KIND_FUNCDEF: { - bh_printf("(%b) ", node->token->token, node->token->length); - OnyxAstNodeFuncDef* fd = &node->as_funcdef; - - print_indent; - bh_printf("Params "); - if (fd->params) - onyx_ast_print((OnyxAstNode *) fd->params, 0); - - print_indent; - bh_printf("Returns %s", fd->return_type->name); - - print_indent; - bh_printf("Body"); - if (fd->body) - onyx_ast_print((OnyxAstNode *) fd->body, indent + 1); - - if (fd->next) - onyx_ast_print((OnyxAstNode *) fd->next, indent); - - break; - } - - case ONYX_AST_NODE_KIND_PARAM: { - OnyxAstNodeParam* param = &node->as_param; - bh_printf("%b %s", param->token->token, param->token->length, param->type->name); - if (param->next && indent == 0) { - bh_printf(", "); - onyx_ast_print((OnyxAstNode *) param->next, 0); - } - - break; - } - - case ONYX_AST_NODE_KIND_BLOCK: { - OnyxAstNodeBlock* block = &node->as_block; - if (block->scope) { - onyx_ast_print((OnyxAstNode *) block->scope, indent + 1); - } - - if (block->body) { - onyx_ast_print((OnyxAstNode *) block->body, indent + 1); - } - - break; - } - - case ONYX_AST_NODE_KIND_SCOPE: { - OnyxAstNodeScope* scope = &node->as_scope; - if (scope->last_local) { - onyx_ast_print((OnyxAstNode *) scope->last_local, 0); - } - - break; - } - - case ONYX_AST_NODE_KIND_LOCAL: { - OnyxAstNodeLocal* local = &node->as_local; - bh_printf("%b %s", local->token->token, local->token->length, local->type->name); - if (local->prev_local && indent == 0) { - bh_printf(", "); - onyx_ast_print((OnyxAstNode *) local->prev_local, 0); - } - break; - } - - case ONYX_AST_NODE_KIND_RETURN: { - if (node->next) { - onyx_ast_print(node->next, indent + 1); - } - - break; - } - - case ONYX_AST_NODE_KIND_LITERAL: { - bh_printf("%b", node->token->token, node->token->length); - break; - } - - case ONYX_AST_NODE_KIND_CAST: { - bh_printf("to %s ", node->type->name); - onyx_ast_print(node->left, indent + 1); - if (node->next) { - onyx_ast_print(node->next, indent); - } - break; - } - - default: { - onyx_ast_print(node->left, indent + 1); - onyx_ast_print(node->right, indent + 1); - if (node->next) { - onyx_ast_print(node->next, indent); - } - break; - } - } -} diff --git a/onyxutils.h b/onyxutils.h deleted file mode 100644 index 63b6f601..00000000 --- a/onyxutils.h +++ /dev/null @@ -1,5 +0,0 @@ -#include "bh.h" - -#include "onyxparser.h" - -void onyx_ast_print(OnyxAstNode* program, i32 indent); diff --git a/src/onyx.c b/src/onyx.c new file mode 100644 index 00000000..ceb09703 --- /dev/null +++ b/src/onyx.c @@ -0,0 +1,71 @@ +#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; + +#if 0 + 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); + } +#endif + + 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); + + // NOTE: if there are errors, assume the parse tree was generated wrong, + // even if it may have still been generated correctly. + if (onyx_message_has_errors(&msgs)) { + onyx_message_print(&msgs); + goto main_exit; + } else { + onyx_ast_print(program, 0); + bh_printf("\nNo errors.\n"); + } + + +main_exit: // NOTE: Cleanup, since C doesn't have defer + bh_arena_free(&msg_arena); + bh_arena_free(&ast_arena); + onyx_parser_free(&parser); + onyx_tokenizer_free(&tokenizer); + bh_file_contents_delete(&fc); + + return 0; +} diff --git a/src/onyxlex.c b/src/onyxlex.c new file mode 100644 index 00000000..d76c93f3 --- /dev/null +++ b/src/onyxlex.c @@ -0,0 +1,279 @@ +#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", + "const", //"TOKEN_TYPE_KEYWORD_CONST", + "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN", + "proc", //"TOKEN_TYPE_KEYWORD_PROC", + "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", + "as", //"TOKEN_TYPE_KEYWORD_CAST", + + "->", //"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("const", TOKEN_TYPE_KEYWORD_CONST); + LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO); + LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC); + LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL); + LITERAL_TOKEN("as", TOKEN_TYPE_KEYWORD_CAST); + 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)) { + u32 len = 1; + 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/src/onyxmsgs.c b/src/onyxmsgs.c new file mode 100644 index 00000000..a2e55512 --- /dev/null +++ b/src/onyxmsgs.c @@ -0,0 +1,54 @@ + +#include "onyxmsgs.h" + +static const char* msg_formats[] = { + "expected token '%s', got '%s'", + "unexpected token '%s'", + "unknown type '%s'", + "expected lval '%s'", + "attempt to assign to constant '%s'", + "unknown symbol '%s'", + "redefinition of function '%s'", + "mismatched types for binary operator, '%s', '%s'", + "mismatched types on assignment, '%s', '%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; + } +} + +b32 onyx_message_has_errors(OnyxMessages* msgs) { + return msgs->first != NULL; +} + +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { + msgs->allocator = allocator; + msgs->first = NULL; +} diff --git a/src/onyxparser.c b/src/onyxparser.c new file mode 100644 index 00000000..103d8e3c --- /dev/null +++ b/src/onyxparser.c @@ -0,0 +1,737 @@ + +#include "onyxlex.h" +#include "onyxmsgs.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", 0, 0, 0, 0, 1 }, + + { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1, 1 }, + + { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0, 1 }, + { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0, 1 }, + + { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0, 1 }, + { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0, 1 }, + + { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0, 1 }, + { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0, 1}, + { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0, 1 }, + + { 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, OnyxAstNode* ident, b32 is_local); +static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident); +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_symbol_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; + } +} + +static void find_token(OnyxParser* parser, OnyxTokenType token_type) { + while (parser->curr_token->type != token_type && !is_terminating_token(parser->curr_token->type)) { + parser_next_token(parser); + } +} + +// 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) { + remove_identifier(parser, (OnyxAstNode *) walker); + } + + 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, OnyxAstNode* ident, b32 is_local) { + OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident; + if (is_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(OnyxAstNode*, 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 void remove_identifier(OnyxParser* parser, OnyxAstNode* ident) { + OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident; + + onyx_token_null_toggle(*local->token); + if (local->shadowed) { + bh_hash_put(OnyxAstNode*, parser->identifiers, local->token->token, local->shadowed); + } else { + bh_hash_delete(OnyxAstNode*, parser->identifiers, local->token->token); + } + 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); + if (sym_node == NULL) { + onyx_token_null_toggle(*sym_token); + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, + sym_token->pos, sym_token->token); + onyx_token_null_toggle(*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); + lit_node->flags |= ONYX_AST_FLAG_COMPTIME; + 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) { + OnyxToken* bin_op_tok = parser->curr_token; + parser_next_token(parser); + + OnyxAstNode* right = parse_factor(parser); + + // NOTE: Typechecking like this will not be here for long but + // want to start working on it early + if (left->type != right->type) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, + bin_op_tok->pos, + left->type->name, right->type->name); + return &error_node; + } + + OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind); + bin_op->left = left; + bin_op->right = right; + bin_op->type = left->type; + 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; + } + case TOKEN_TYPE_KEYWORD_CAST: + { + parser_next_token(parser); + OnyxTypeInfo* type = parse_type(parser); + + // NOTE: This may be premature in the parser but a cast + // node doesn't need to exist if the source and target + // types are the same + if (type == left->type) return left; + + OnyxAstNode* cast_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_CAST); + cast_node->type = type; + cast_node->left = left; + + return cast_node; + } + } + + 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_symbol_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]; + u32 flags = ONYX_AST_FLAG_LVAL; + + // NOTE: var: const ... + if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_CONST) { + parser_next_token(parser); + flags |= ONYX_AST_FLAG_CONST; + } + + // 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; + local->flags |= flags; + + insert_identifier(parser, (OnyxAstNode *) local, 1); + + if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) { + OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); + assignment->token = parser->curr_token; + parser_next_token(parser); + + OnyxAstNode* expr = parse_expression(parser); + assignment->right = expr; + assignment->left = (OnyxAstNode*) local; + + if (local->type->is_known && local->type != expr->type) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, + assignment->token->pos, + local->type->name, expr->type->name); + return 1; + } else if (!local->type->is_known) { + local->type = expr->type; + } + *ret = assignment; + } + return 1; + } + + // NOTE: Assignment + case TOKEN_TYPE_SYM_EQUALS: + { + parser_next_token(parser); + + OnyxAstNode* lval = lookup_identifier(parser, symbol); + + if (lval != NULL && lval->flags & ONYX_AST_FLAG_LVAL && (lval->flags & ONYX_AST_FLAG_CONST) == 0) { + 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; + } + + onyx_token_null_toggle(*symbol); + if (lval == NULL) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, + symbol->pos, symbol->token); + } + + else if ((lval->flags & ONYX_AST_FLAG_LVAL) == 0) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_NOT_LVAL, + symbol->pos, symbol->token); + } + + else if (lval->flags & ONYX_AST_FLAG_CONST) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_ASSIGN_CONST, + symbol->pos, symbol->token); + } + onyx_token_null_toggle(*symbol); + + find_token(parser, TOKEN_TYPE_SYM_SEMICOLON); + 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_symbol_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)); + + find_token(parser, TOKEN_TYPE_SYM_SEMICOLON); + } + 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; + + for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { + insert_identifier(parser, (OnyxAstNode *) p, 0); + } + + func_def->body = parse_block(parser, 1); + + for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { + remove_identifier(parser, (OnyxAstNode *) p); + } + + 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; + } + + 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; + + onyx_token_null_toggle(*symbol); + + if (!bh_hash_has(OnyxAstNode *, parser->identifiers, symbol->token)) { + bh_hash_put(OnyxAstNode *, parser->identifiers, symbol->token, (OnyxAstNode *) func_def); + } else { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION, + symbol->pos, + symbol->token); + + // NOTE: I really wish C had defer... + onyx_token_null_toggle(*symbol); + return NULL; + } + + onyx_token_null_toggle(*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/src/onyxutils.c b/src/onyxutils.c new file mode 100644 index 00000000..e40e0cb1 --- /dev/null +++ b/src/onyxutils.c @@ -0,0 +1,118 @@ +#include "onyxutils.h" +#include "onyxlex.h" +#include "onyxparser.h" + +#define print_indent { if (indent > 0) bh_printf("\n"); for (int i = 0; i < indent; i++) bh_printf(" "); } + +void onyx_ast_print(OnyxAstNode* node, i32 indent) { + if (node == NULL) return; + + print_indent; + bh_printf("(%d) %s ", node->flags, onyx_ast_node_kind_string(node->kind)); + + switch (node->kind) { + case ONYX_AST_NODE_KIND_PROGRAM: { + if (node->next) + onyx_ast_print(node->next, indent + 1); + + break; + } + + case ONYX_AST_NODE_KIND_FUNCDEF: { + bh_printf("(%b) ", node->token->token, node->token->length); + OnyxAstNodeFuncDef* fd = &node->as_funcdef; + + print_indent; + bh_printf("Params "); + if (fd->params) + onyx_ast_print((OnyxAstNode *) fd->params, 0); + + print_indent; + bh_printf("Returns %s", fd->return_type->name); + + print_indent; + bh_printf("Body"); + if (fd->body) + onyx_ast_print((OnyxAstNode *) fd->body, indent + 1); + + if (fd->next) + onyx_ast_print((OnyxAstNode *) fd->next, indent); + + break; + } + + case ONYX_AST_NODE_KIND_PARAM: { + OnyxAstNodeParam* param = &node->as_param; + bh_printf("%b %s", param->token->token, param->token->length, param->type->name); + if (param->next && indent == 0) { + bh_printf(", "); + onyx_ast_print((OnyxAstNode *) param->next, 0); + } + + break; + } + + case ONYX_AST_NODE_KIND_BLOCK: { + OnyxAstNodeBlock* block = &node->as_block; + if (block->scope) { + onyx_ast_print((OnyxAstNode *) block->scope, indent + 1); + } + + if (block->body) { + onyx_ast_print((OnyxAstNode *) block->body, indent + 1); + } + + break; + } + + case ONYX_AST_NODE_KIND_SCOPE: { + OnyxAstNodeScope* scope = &node->as_scope; + if (scope->last_local) { + onyx_ast_print((OnyxAstNode *) scope->last_local, 0); + } + + break; + } + + case ONYX_AST_NODE_KIND_LOCAL: { + OnyxAstNodeLocal* local = &node->as_local; + bh_printf("%b %s", local->token->token, local->token->length, local->type->name); + if (local->prev_local && indent == 0) { + bh_printf(", "); + onyx_ast_print((OnyxAstNode *) local->prev_local, 0); + } + break; + } + + case ONYX_AST_NODE_KIND_RETURN: { + if (node->next) { + onyx_ast_print(node->next, indent + 1); + } + + break; + } + + case ONYX_AST_NODE_KIND_LITERAL: { + bh_printf("%b", node->token->token, node->token->length); + break; + } + + case ONYX_AST_NODE_KIND_CAST: { + bh_printf("to %s ", node->type->name); + onyx_ast_print(node->left, indent + 1); + if (node->next) { + onyx_ast_print(node->next, indent); + } + break; + } + + default: { + onyx_ast_print(node->left, indent + 1); + onyx_ast_print(node->right, indent + 1); + if (node->next) { + onyx_ast_print(node->next, indent); + } + break; + } + } +}