From 56b52d21e150838b45e1c13792a4da3c5a5f5e3a Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 22 May 2020 21:00:10 -0500 Subject: [PATCH] More parse progress --- Makefile | 1 + bh.h | 3493 ++++++++++++++++++++++---------------------- docs/parse_grammar | 154 +- onyx | Bin 72264 -> 82224 bytes onyx.c | 120 +- onyxlex.c | 550 +++---- onyxlex.h | 178 +-- onyxmsgs.c | 88 +- onyxmsgs.h | 70 +- onyxparser.c | 940 +++++++----- onyxparser.h | 342 +++-- onyxutils.c | 26 + onyxutils.h | 5 + progs/minimal.onyx | 22 +- 14 files changed, 3178 insertions(+), 2811 deletions(-) create mode 100644 onyxutils.c create mode 100644 onyxutils.h diff --git a/Makefile b/Makefile index b204645f..156ee281 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ OBJ_FILES=\ onyxlex.o \ onyxparser.o \ onyxmsgs.o \ + onyxutils.o \ onyx.o CC=gcc diff --git a/bh.h b/bh.h index 282608ae..c3679f23 100644 --- a/bh.h +++ b/bh.h @@ -1,1740 +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) { - 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) { - // 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; - - 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); - } - - return 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]; - i32 len = bh_arr_length(arrptr); - if (arrptr == NULL) return; // Didn't exist - - i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; - i32 i = 0; - - while (len && strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) != 0) { - arrptr = bh_pointer_add(arrptr, stride); - i++, len--; - } - - if (len == 0) return; // Didn't exist - - bh__arr_deleten((void **) &arrptr, elemsize, i, 1); -} - -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); - return 1; -} - -#endif // ifndef BH_NO_HASHTABLE - -#endif // ifdef BH_DEFINE - -#endif // ifndef BH_H +#ifndef BH_H +#define BH_H + +#include +#include +#include +#include + +#include +#include +#include // TODO: Replace with needed functions +#include + +//------------------------------------------------------------------------------------- +// Better types +//------------------------------------------------------------------------------------- +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long u64; +typedef unsigned long long u128; +typedef signed char i8; +typedef signed short i16; +typedef signed int i32; +typedef signed long i64; +typedef signed long long i128; +typedef unsigned long isize; +typedef i32 b32; +typedef void* ptr; + + + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// Better character functions +//------------------------------------------------------------------------------------- +b32 char_is_alpha(const char a); +b32 char_is_num(const char a); +b32 char_is_alphanum(const char a); +b32 char_is_whitespace(const char a); +b32 char_in_range(const char lo, const char hi, const char a); +char charset_contains(const char* charset, char ch); +i64 chars_match(char* ptr1, char* ptr2); + + + + + + + +//------------------------------------------------------------------------------------- +// Better math functions +//------------------------------------------------------------------------------------- +#define bh_max(a, b) ((a) > (b) ? (a) : (b)) +#define bh_min(a, b) ((a) < (b) ? (a) : (b)) +#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v)))) +#define bh_abs(x) ((x) < 0 ? -(x) : (x)) + + + + + +//------------------------------------------------------------------------------------- +// Helpful macros +//------------------------------------------------------------------------------------- +#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem) +#define bh_aligh_of(Type) bh_offset_of(struct { char c; Type member; }, member) +#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0) + +#define bh_pointer_add(ptr, amm) ((void *)((u8 *) ptr + amm)) +#define BH_BIT(x) (1 << (x)) +#define BH_MASK_SET(var, set, mask) ((set) ? (var) |= (mask) : (var) &= ~(mask)) + + + + + + + +//------------------------------------------------------------------------------------- +// Custom allocators +//------------------------------------------------------------------------------------- + +typedef enum bh_allocator_actions { + bh_allocator_action_alloc, + bh_allocator_action_free, + bh_allocator_action_resize, +} bh_allocator_actions; + +#define BH_ALLOCATOR_PROC(name) \ +ptr name(ptr data, bh_allocator_actions action, \ + isize size, isize alignment, \ + void* prev_memory, \ + u64 flags) + +typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type + +typedef struct bh_allocator { + bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions + ptr data; // Pointer to the other data for the allocator +} bh_allocator; + +typedef enum bh_allocator_flags { + bh_allocator_flag_clear = 1 // Sets all memory to be 0 +} bh_allocator_flags; + +ptr bh_alloc(bh_allocator a, isize size); +ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment); +ptr bh_resize(bh_allocator a, ptr data, isize new_size); +ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment); +void bh_free(bh_allocator a, ptr data); + +#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T)) +#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n)) + +// NOTE: This should get optimized out since alignment should be a power of two +#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment) + + + + +// HEAP ALLOCATOR +// Essentially a wrapper for malloc, free and realloc +bh_allocator bh_heap_allocator(void); +BH_ALLOCATOR_PROC(bh_heap_allocator_proc); + + + + + +// ARENA ALLOCATOR +typedef struct bh_arena { + bh_allocator backing; + ptr first_arena, current_arena; + isize size, arena_size; // in bytes +} bh_arena; + +typedef struct bh__arena_internal { + ptr next_arena; + void* data; // Not actually a pointer, just used for the offset +} bh__arena_internal; + +void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size); +void bh_arena_free(bh_arena* alloc); +bh_allocator bh_arena_allocator(bh_arena* alloc); +BH_ALLOCATOR_PROC(bh_arena_allocator_proc); + + + +// SCRATCH ALLOCATOR +typedef struct bh_scratch { + bh_allocator backing; + ptr memory, end, curr; +} bh_scratch; + +void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size); +void bh_scratch_free(bh_scratch* scratch); +bh_allocator bh_scratch_allocator(bh_scratch* scratch); +BH_ALLOCATOR_PROC(bh_scratch_allocator_proc); + + + + + + + + +//------------------------------------------------------------------------------------- +// Better strings +//------------------------------------------------------------------------------------- +#ifndef BH_NO_STRING + +typedef struct bh__string { + u64 length; + u64 capacity; +} bh__string; + +typedef char bh_string; + +#define bh__stringhead(x) (((bh__string *)(x)) - 1) + +#define bh_string_new(x) _Generic((x), \ + unsigned long: bh_string_new_cap, \ + unsigned int: bh_string_new_cap, \ + int: bh_string_new_cap, \ + long: bh_string_new_cap, \ + const char*: bh_string_new_str, \ + char*: bh_string_new_str)(x) + +#define bh_string_append(str1, str2) _Generic((str2), \ + bh_string*: bh_string_append_bh_string, \ + char*: bh_string_append_cstr, \ + const char*: bh_string_append_cstr)(str1, str2) + +#define bh_string_replace_at(dest, src, offset) _Generic((src), \ + bh_string*: bh_string_replace_at_bh_string, \ + char*: bh_string_replace_at_cstr, \ + const char*: bh_string_replace_at_cstr)(dest, src, offset) + +#define bh_string_insert_at(dest, src, offset) _Generic((src), \ + bh_string*: bh_string_insert_at_bh_string, \ + char*: bh_string_insert_at_cstr, \ + const char*: bh_string_insert_at_cstr)(dest, src, offset) + +bh_string bh_string_new_cap(unsigned long cap); +bh_string bh_string_new_str(const char* cstr); +b32 bh_string_delete(bh_string* str); +b32 bh_string_ensure_capacity(bh_string* str, u64 cap); +void bh_string_append_bh_string(bh_string* str1, bh_string* str2); +void bh_string_append_cstr(bh_string* str1, const char* str2); +void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset); +void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset); +void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset); +void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset); +void bh_string_trim_end(bh_string* str, const char* charset); +void bh_string_trim_begin(bh_string* str, const char* charset); +void bh_string_trim_end_space(bh_string* str); +// TEMP +void bh_string_print(bh_string* str); + +#endif + + + + + + + + + + +//------------------------------------------------------------------------------------- +// Better files +//------------------------------------------------------------------------------------- +#ifndef BH_NO_FILE + +typedef enum bh_file_error { + BH_FILE_ERROR_NONE, + BH_FILE_ERROR_INVALID, + BH_FILE_ERROR_BAD_FD, +} bh_file_error; + +typedef enum bh_file_mode { + BH_FILE_MODE_READ = 1 << 0, + BH_FILE_MODE_WRITE = 1 << 1, + BH_FILE_MODE_APPEND = 1 << 2, + BH_FILE_MODE_RW = 1 << 3, + + BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW +} bh_file_mode; + +typedef enum bh_file_whence { + BH_FILE_WHENCE_BEGIN = SEEK_SET, + BH_FILE_WHENCE_CURRENT = SEEK_CUR, + BH_FILE_WHENCE_END = SEEK_END, +} bh_file_whence; + +typedef int bh_file_descriptor; + +typedef struct bh_file { + bh_file_descriptor fd; + char const* filename; +} bh_file; + +typedef enum bh_file_standard { + BH_FILE_STANDARD_INPUT, + BH_FILE_STANDARD_OUTPUT, + BH_FILE_STANDARD_ERROR +} bh_file_standard; + +typedef struct bh_file_contents { + bh_allocator allocator; + const char *filename; + isize length; + void* data; +} bh_file_contents; + +bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand); + +bh_file_error bh_file_create(bh_file* file, char const* filename); +bh_file_error bh_file_open(bh_file* file, char const* filename); +bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename); +bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename); +b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read); +b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote); +static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset); +i64 bh_file_seek_to_end(bh_file* file); +i64 bh_file_skip(bh_file* file, i64 bytes); +i64 bh_file_tell(bh_file* file); +bh_file_error bh_file_close(bh_file* file); +i32 bh_file_read(bh_file* file, void* buffer, isize buff_size); +i32 bh_file_write(bh_file* file, void* buffer, isize buff_size); +i64 bh_file_size(bh_file* file); + +#define bh_file_read_contents(allocator_, x) _Generic((x), \ + bh_file*: bh_file_read_contents_bh_file, \ + const char*: bh_file_read_contents_direct, \ + char*: bh_file_read_contents_direct)((allocator_), x) + +bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file); +bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename); +i32 bh_file_contents_delete(bh_file_contents* contents); + +#endif + + + + + + + + + + +//------------------------------------------------------------------------------------- +// Alternate printing +//------------------------------------------------------------------------------------- +// Barebones implementation of printf. Does not support all format options +// Currently supports: +// %c - chars +// %_(u)d - ints where _ is: +// nothing - decimal +// o - octal +// x - hexadecimal +// %_(u)l - longs where _ is: +// nothing - decimal +// o - octal +// x - hexadecimal +// %f - floating points +// %s - null terminated strings +// %p - pointers +// %% - literal % + +typedef struct bh__print_format { + u32 base; +} bh__print_format; + +isize bh_printf(char const *fmt, ...); +isize bh_printf_va(char const *fmt, va_list va); +isize bh_printf_err(char const *fmt, ...); +isize bh_printf_err_va(char const *fmt, va_list va); +isize bh_fprintf(bh_file* f, char const *fmt, ...); +isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va); +char* bh_bprintf(char const *fmt, ...); +char* bh_bprintf_va(char const *fmt, va_list va); +isize bh_snprintf(char *str, isize n, char const *fmt, ...); +isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va); + + + + + + + + +//------------------------------------------------------------------------------------- +// Better debug functions +//------------------------------------------------------------------------------------- +#ifdef BH_DEBUG + +void* bh__debug_malloc(size_t size, const char* file, u64 line); +void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line); +void bh__debug_free(void* ptr, const char* file, u64 line); +void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line); + +#ifdef BH_DEFINE + +void* bh__debug_malloc(size_t size, const char* file, u64 line) { + void* p = malloc(size); + bh_printf("[DEBUG] %p = malloc(%d) at %s:%d\n", p, size, file, line); + return p; +} + +void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line) { + void* p = aligned_alloc(size, alignment); + bh_printf("[DEBUG] %p = aligned_alloc(%d, %d) at %s:%d\n", p, alignment, size, file, line); + return p; +} + +void bh__debug_free(void* ptr, const char* file, u64 line) { + bh_printf("[DEBUG] free(%p) at %s:%d\n", ptr, file, line); + free(ptr); +} + +void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line) { + void* p = realloc(ptr, size); + bh_printf("[DEBUG] %p = realloc(%p, %d) at %s:%d\n", p, ptr, size, file, line); + return p; +} + +#endif + +#define malloc(size) (bh__debug_malloc(size, __FILE__, __LINE__)) +#define aligned_alloc(size, alignment) (bh__debug_aligned_alloc(size, alignment, __FILE__, __LINE__)) +#define free(ptr) (bh__debug_free(ptr, __FILE__, __LINE__)) +#define realloc(ptr, size) (bh__debug_realloc(ptr, size, __FILE__, __LINE__)) + +#endif + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// Better dynamically-sized arrays +//------------------------------------------------------------------------------------- +#ifndef BH_NO_ARRAY + +typedef struct bh__arr { + bh_allocator allocator; + i32 length, capacity; +} bh__arr; + +#ifndef BH_ARR_GROW_FORMULA +#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4) +#endif + +#define bh_arr(T) T* +#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1) + +#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator()) +#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0) +#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0) +#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0) +#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0) + +#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length]) +#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1]) +#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)]) + +#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap)) +#define bh_arr_free(arr) (bh__arr_free((void**) &(arr))) +#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr)))) + +#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap)) +#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap)) +#define bh_arr_set_length(arr, n) ( \ + bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), n), \ + bh__arrhead(arr)->length = n) + +#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n)) + +#define bh_arr_insert_end(arr, n) ( \ + bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \ + bh__arrhead(arr)->length += n) + +#define bh_arr_push(arr, value) ( \ + bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1), \ + arr[bh__arrhead(arr)->length++] = value) + +#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1) +#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0) + +#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n)) +#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length]) + +b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap); +b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap); +b32 bh__arr_free(void **arr); +void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize); +void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems); +void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems); + +#endif + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// HASH TABLE FUNCTIONS +//------------------------------------------------------------------------------------- +#ifndef BH_NO_HASHTABLE + +#define BH__HASH_STORED_KEY_SIZE 64 +typedef struct bh__hash_entry { + char key[BH__HASH_STORED_KEY_SIZE]; + i32 value; // NOTE: Not actually an i32, just used as a placeholder for offset +} bh__hash_entry; + +#define BH__HASH_MODULUS 1021 +#define BH__HASH_KEYSIZE 64 +#ifdef BH_DEFINE +u64 bh__hash_function(const char* str, i32 len) { + u64 hash = 5381; + i32 c, l = 0; + if (len == 0) len = BH__HASH_KEYSIZE; + + while ((c = *str++) && l++ < len) { + hash = (hash << 5) + hash + c; + } + + return hash % BH__HASH_MODULUS; +} +#endif + +typedef struct bh_hash_iterator { + ptr *tab, *endtab; + i32 elemsize, arrlen; + bh__hash_entry* entry; +} bh_hash_iterator; + +typedef struct bh__hash { + bh_allocator allocator; + ptr arrs[BH__HASH_MODULUS]; +} bh__hash; + +#define bh_hash(T) T* + +#ifdef BH_HASH_SIZE_SAFE + #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab)) + #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab)) + #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = (T) value)) + #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((bh__hash *) tab, sizeof(T), key))) + #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key)))) + #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((bh__hash *) tab, sizeof(T), key)) + + #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((bh__hash *) tab, sizeof(T))) + #define bh_hash_iter_key(it) (it.entry->key) + #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value)) +#else + #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab)) + #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab)) + #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = value) + #define bh_hash_has(T, tab, key) (bh__hash_has((bh__hash *) tab, sizeof(T), key)) + #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key))) + #define bh_hash_delete(T, tab, key) (bh__hash_delete((bh__hash *) tab, sizeof(T), key)) + + #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((bh__hash *) tab, sizeof(T))) + #define bh_hash_iter_key(it) (it.entry->key) + #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value)) +#endif + +b32 bh__hash_init(bh_allocator allocator, bh__hash **table); +b32 bh__hash_free(bh__hash **table); +ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key); +b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key); +ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key); +void bh__hash_delete(bh__hash *table, i32 elemsize, char *key); +bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize); +b32 bh_hash_iter_next(bh_hash_iterator* it); + +#endif + +#ifdef BH_DEFINE +#undef BH_DEFINE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// IMPLEMENTATIONS +//------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------- +// CHAR FUNCTIONS +//------------------------------------------------------------------------------------- +b32 char_is_alpha(const char a) { + return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z'); +} + +b32 char_is_num(const char a) { + return ('0' <= a && a <= '9'); +} + +b32 char_is_alphanum(const char a) { + return char_is_alpha(a) || char_is_num(a); +} + +b32 char_is_whitespace(const char a) { + return charset_contains(" \t\r\n", a); +} + +b32 char_in_range(const char lo, const char hi, const char a) { + return lo <= a <= hi; +} + +char charset_contains(const char* charset, char ch) { + while (*charset) { + if (*charset == ch) return ch; + charset++; + } + + return 0; +} + +i64 chars_match(char* ptr1, char* ptr2) { + i64 len = 0; + while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++; + return *ptr2 == '\0' ? len : 0; +} + + + + + + + +//------------------------------------------------------------------------------------- +// CUSTOM ALLOCATORS IMPLEMENTATION +//------------------------------------------------------------------------------------- + + +ptr bh_alloc(bh_allocator a, isize size) { + return bh_alloc_aligned(a, size, 16); +} + +ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) { + return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0); +} + +ptr bh_resize(bh_allocator a, ptr data, isize new_size) { + return bh_resize_aligned(a, data, new_size, 16); +} + +ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) { + return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0); +} + +void bh_free(bh_allocator a, ptr data) { + if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0); +} + + + +// HEAP ALLOCATOR IMPLEMENTATION + +bh_allocator bh_heap_allocator(void) { + return (bh_allocator) { + .proc = bh_heap_allocator_proc, + .data = NULL + }; +} + +BH_ALLOCATOR_PROC(bh_heap_allocator_proc) { + ptr retval = NULL; + + switch (action) { + case bh_allocator_action_alloc: { + retval = aligned_alloc(alignment, size); + + if (flags & bh_allocator_flag_clear && retval != NULL) { + memset(retval, 0, size); + } + } break; + + case bh_allocator_action_resize: { + // TODO: Maybe replace with better custom function + retval = realloc(prev_memory, size); + } break; + + case bh_allocator_action_free: { + free(prev_memory); + } break; + } + + return retval; +} + + + + + + + +// ARENA ALLOCATOR IMPLEMENTATION +void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) { + arena_size = bh_max(arena_size, sizeof(ptr)); + ptr data = bh_alloc(backing, arena_size); + + alloc->backing = backing; + alloc->arena_size = arena_size; + alloc->size = sizeof(ptr); + alloc->first_arena = data; + alloc->current_arena = data; + + ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL; +} + +void bh_arena_free(bh_arena* alloc) { + bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena; + bh__arena_internal *trailer = walker; + while (walker != NULL) { + walker = walker->next_arena; + bh_free(alloc->backing, trailer); + trailer = walker; + } + + alloc->first_arena = NULL; + alloc->current_arena = NULL; + alloc->arena_size = 0; + alloc->size = 0; +} + +bh_allocator bh_arena_allocator(bh_arena* alloc) { + return (bh_allocator) { + .proc = bh_arena_allocator_proc, + .data = alloc, + }; +} + +BH_ALLOCATOR_PROC(bh_arena_allocator_proc) { + bh_arena* alloc_arena = (bh_arena*) data; + + ptr retval = NULL; + + switch (action) { + case bh_allocator_action_alloc: { + + // TODO: Do this better because right now bh__align is bad + // size = bh__align(size, alignment); + if (size > alloc_arena->arena_size - sizeof(ptr)) { + // Size too large for the arena + return NULL; + } + + if (alloc_arena->size + size >= alloc_arena->arena_size) { + alloc_arena->size = sizeof(ptr); + bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size); + + if (new_arena == NULL) { + bh_printf_err("Arena Allocator: couldn't allocate new arena"); + return NULL; + } + + new_arena->next_arena = NULL; + ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena; + alloc_arena->current_arena = new_arena; + } + + retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size); + alloc_arena->size += size; + } break; + + case bh_allocator_action_resize: { + // Do nothing since this is a fixed allocator + } break; + + case bh_allocator_action_free: { + // Do nothing since this allocator isn't made for freeing memory + } break; + } + + return retval; +} + + + + +// SCRATCH ALLOCATOR IMPLEMENTATION +void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) { + ptr memory = bh_alloc(backing, scratch_size); + + scratch->backing = backing; + scratch->memory = memory; + scratch->curr = memory; + scratch->end = memory + scratch_size; +} + +void bh_scratch_free(bh_scratch* scratch) { + bh_free(scratch->backing, scratch->memory); + + scratch->memory = NULL; + scratch->curr = NULL; + scratch->end = NULL; +} + +bh_allocator bh_scratch_allocator(bh_scratch* scratch) { + return (bh_allocator) { + .proc = bh_scratch_allocator_proc, + .data = scratch, + }; +} + +BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) { + bh_scratch* scratch = (bh_scratch*) data; + ptr retval = NULL; + + switch (action) { + case bh_allocator_action_alloc: { + if (size > scratch->end - scratch->memory) { + return NULL; + } + + retval = scratch->curr; + scratch->curr += size; + + if (scratch->curr >= scratch->end) { + scratch->curr = scratch->memory; + retval = scratch->curr; + } + } break; + + case bh_allocator_action_free: + case bh_allocator_action_resize: + // Do nothing + break; + } + + return retval; +} + + +//------------------------------------------------------------------------------------- +// STRING IMPLEMENTATION (BROKEN) +//------------------------------------------------------------------------------------- +#ifndef BH_NO_STRING + +bh_string* bh_string_new_cap(unsigned long cap) { + bh__string* str; + str = (bh__string*) malloc(sizeof(*str) + sizeof(char) * cap + 1); + str[0] = 0; + return str + 1; +} + +bh_string* bh_string_new_str(const char* cstr) { + const i32 len = strlen(cstr); + bh__string* str; + i32 i; + + str = malloc(sizeof(*str) + sizeof(char) * len + 1); + char* data = (char*) (str + 1); + for (i = 0; i < len; i++) { + data[i] = cstr[i]; + } + + data[len] = 0; // Always null terminate the string + + str->length = len; + str->capacity = len; + return str + 1; +} + +b32 bh_string_delete(bh_string** str) { + bh__string* strptr = bh__stringhead(*str); + free(strptr); + str->length = 0; + str->capacity = 0; + return 1; +} + +b32 bh_string_grow(bh_string** str, u64 cap) { + bh__string* strptr = bh__stringhead(*str); + if (strptr->capacity >= cap) return 1; + + void* p; + p = realloc(strptr, sizeof(*strptr) + sizeof(char) * cap + 1); + + strptr->capacity = cap; + + return 1; +} + +void bh_string_append_bh_string(bh_string** str1, bh_string** str2) { + if (!bh_string_ensure_capacity(str1, str1->length + str2->length)) return; + + //TODO: Replace with custom memory management + memcpy(str1->data + str1->length, str2->data, str2->length); + str1->length += str2->length; +} + +void bh_string_append_cstr(bh_string* str1, const char* str2) { + const i32 str2len = strlen(str2); + if (!bh_string_ensure_capacity(str1, str1->length + str2len)) return; + + //TODO: Replace with custom memory management + memcpy(str1->data + str1->length, str2, str2len); + str1->length += str2len; +} + +void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset) { + if (offset > dest->length) return; + if (!bh_string_ensure_capacity(dest, offset + src->length)) return; + + memcpy(dest->data + offset, src->data, src->length); + if (offset + src->length > dest->length) + dest->length = offset + src->length; +} + +void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset) { + if (offset > dest->length) return; + const i32 srclen = strlen(src); + if (!bh_string_ensure_capacity(dest, offset + srclen)) return; + + memcpy(dest->data + offset, src, srclen); + if (offset + srclen > dest->length) + dest->length = offset + srclen; +} + +void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset) { + if (!bh_string_ensure_capacity(dest, dest->length + src->length)) return; + + memmove(dest->data + offset + src->length, dest->data + offset, dest->length + src->length - offset); + memcpy(dest->data + offset, src->data, src->length); + dest->length += src->length; +} + +void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset) { + const i32 srclen = strlen(src); + if (!bh_string_ensure_capacity(dest, dest->length + srclen)) return; + + // TODO: Use something better. This copies to a seperate buffer first + memmove(dest->data + offset + srclen, dest->data + offset, dest->length + srclen - offset); + memcpy(dest->data + offset, src, srclen); + dest->length += srclen; +} + + +void bh_string_trim_end(bh_string* str, const char* charset) { + while (charset_contains(charset, str->data[str->length - 1])) + str->length--; +} + +void bh_string_trim_begin(bh_string* str, const char* charset) { + u32 off = 0, i; + while (charset_contains(charset, str->data[off])) off++; + + if (off == 0) return; + + for (i = 0; i < str->length - off; i++) { + str->data[i] = str->data[i + off]; + } + + str->length -= off; +} + +void bh_string_trim_end_space(bh_string* str) { + bh_string_trim_end(str, " \t\n\r"); +} + +// TEMP +void bh_string_print(bh_string* str) { + write(STDOUT_FILENO, str->data, str->length); +} + +#endif // ifndef BH_NO_STRING + + + + + + + + + +//------------------------------------------------------------------------------------- +// FILE IMPLEMENTATION +//------------------------------------------------------------------------------------- +#ifndef BH_NO_FILE + +bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) { + i32 sd_fd = -1; + const char* filename = NULL; + + switch (stand) { + case BH_FILE_STANDARD_INPUT: + sd_fd = STDIN_FILENO; + filename = "stdin"; // These are constants in the data section so everything should be okay + break; + case BH_FILE_STANDARD_OUTPUT: + sd_fd = STDOUT_FILENO; + filename = "stdout"; + break; + case BH_FILE_STANDARD_ERROR: + sd_fd = STDERR_FILENO; + filename = "stderr"; + break; + default: + return BH_FILE_ERROR_BAD_FD; + } + + file->fd = sd_fd; + file->filename = filename; + + return BH_FILE_ERROR_NONE; +} + +bh_file_error bh_file_create(bh_file* file, const char* filename) { + // Need to do this to avoid compiler complaining about types + bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW); + return bh_file_open_mode(file, write_rw, filename); +} + +bh_file_error bh_file_open(bh_file* file, const char* filename) { + return bh_file_open_mode(file, BH_FILE_MODE_READ, filename); +} + +bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) { + + i32 os_mode = 0; + + switch (mode & BH_FILE_MODE_MODES) { + case BH_FILE_MODE_READ: os_mode = O_RDONLY; break; + case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break; + case BH_FILE_MODE_APPEND: os_mode = O_RDONLY | O_APPEND | O_CREAT; break; + case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break; + case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break; + case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break; + //default: // TODO Handle errors + } + + file->fd = open(filename, os_mode, + S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw- + ); + if (file->fd < 0) { + return BH_FILE_ERROR_INVALID; + } + + // TODO: Set this using some allocator + file->filename = filename; + + return BH_FILE_ERROR_NONE; +} + +bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) { + file->filename = filename; // This may be unsafe + file->fd = fd; + return BH_FILE_ERROR_NONE; +} + +b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) { + isize res = pread(file->fd, buffer, buff_size, offset); + if (res < 0) return 0; + if (bytes_read) *bytes_read = res; + return 1; +} + +b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) { + isize res; + i64 current_offset = 0; + bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_CURRENT, ¤t_offset); + if (current_offset == offset) { + // Standard in and out do like pwrite() + res = write(file->fd, buffer, buff_size); + } else { + res = pwrite(file->fd, buffer, buff_size, offset); + } + if (res < 0) return 0; + if (bytes_wrote) *bytes_wrote = res; + + return 1; +} + +static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset) { + i64 res = lseek(fd, offset, whence); + if (res < 0) return 0; + if (new_offset) *new_offset = res; + return 1; +} + +// Returns new offset +i64 bh_file_seek_to(bh_file* file, i64 offset) { + i64 new_offset = -1; + bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset); + return new_offset; +} + +i64 bh_file_seek_to_end(bh_file* file) { + i64 new_offset = -1; + bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset); + return new_offset; +} + +i64 bh_file_skip(bh_file* file, i64 bytes) { + i64 new_offset = 0; + bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset); + return new_offset; +} + +i64 bh_file_tell(bh_file* file) { + i64 new_offset = 0; + bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset); + return new_offset; +} + +bh_file_error bh_file_close(bh_file* file) { + bh_file_error err = BH_FILE_ERROR_NONE; + i32 res = close(file->fd); + if (res < 0) + err = BH_FILE_ERROR_INVALID; + + return err; +} + +b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) { + return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL); +} + +b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) { + return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL); +} + +i64 bh_file_size(bh_file* file) { + i64 size = 0; + i64 prev = bh_file_tell(file); + bh_file_seek_to_end(file); + size = bh_file_tell(file); + bh_file_seek_to(file, prev); + return size; +} + +bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) { + bh_file_contents fc = { + .allocator = alloc, + .filename = file->filename, + .length = 0, .data = NULL + }; + + isize size = bh_file_size(file); + if (size <= 0) return fc; + + fc.data = bh_alloc(alloc, size + 1); + fc.length = size; + bh_file_read_at(file, 0, fc.data, fc.length, NULL); + ((u8*) fc.data)[fc.length] = '\0'; + + return fc; +} + +bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) { + bh_file file; + bh_file_open(&file, filename); + bh_file_contents fc = bh_file_read_contents(alloc, &file); + bh_file_close(&file); + return fc; +} + +b32 bh_file_contents_delete(bh_file_contents* contents) { + bh_free(contents->allocator, contents->data); + contents->length = 0; + return 1; +} + +#endif // ifndef BH_NO_FILE + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// ALTERNATE PRINTF IMPLEMENTATION +//------------------------------------------------------------------------------------- +isize bh_printf(char const *fmt, ...) { + isize res; + va_list va; + va_start(va, fmt); + res = bh_printf_va(fmt, va); + va_end(va); + return res; +} + +isize bh_printf_va(char const *fmt, va_list va) { + bh_file file; + bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT); + return bh_fprintf_va(&file, fmt, va); +} + +isize bh_printf_err(char const *fmt, ...) { + isize res; + va_list va; + va_start(va, fmt); + res = bh_printf_err_va(fmt, va); + va_end(va); + return res; +} + +isize bh_printf_err_va(char const *fmt, va_list va) { + bh_file file; + bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR); + return bh_fprintf_va(&file, fmt, va); +} + +isize bh_fprintf(bh_file* f, char const *fmt, ...) { + isize res; + va_list va; + va_start(va, fmt); + res = bh_fprintf_va(f, fmt, va); + va_end(va); + return res; +} + +isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) { + static char buf[4096]; + isize len = bh_snprintf_va(buf, sizeof(buf), fmt, va); + bh_file_write(f, buf, len - 1); + return len; +} + +char* bh_bprintf(char const *fmt, ...) { + char* res; + va_list va; + va_start(va, fmt); + res = bh_bprintf_va(fmt, va); + va_end(va); + return res; +} + +char* bh_bprintf_va(char const *fmt, va_list va) { + static char buffer[4096]; + bh_snprintf_va(buffer, sizeof(buffer), fmt, va); + return buffer; +} + +isize bh_snprintf(char *str, isize n, char const *fmt, ...) { + isize res; + va_list va; + va_start(va, fmt); + res = bh_snprintf_va(str, n, fmt, va); + va_end(va); + return res; +} + +isize bh__print_string(char* dest, isize n, char* src) { + isize len = 0; + while (n-- && (*dest++ = *src++)) len++; + return len; +} + +isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) { + char buf[128]; + buf[127] = 0; + char* walker = buf + 127; + u32 base = format.base ? format.base : 10, tmp; + + while (value > 0) { + tmp = value % base; + if (tmp > 9) { + switch (tmp) { + case 10: tmp = 'a'; break; + case 11: tmp = 'b'; break; + case 12: tmp = 'c'; break; + case 13: tmp = 'd'; break; + case 14: tmp = 'e'; break; + case 15: tmp = 'f'; break; + } + } else { + tmp += '0'; + } + + *--walker = tmp; + value /= base; + } + + if (format.base == 16) { + *--walker = 'x'; + *--walker = '0'; + } + + return bh__print_string(str, n, walker); +} + +isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) { + char buf[128]; + buf[127] = 0; + char* walker = buf + 127; + u32 base = format.base ? format.base : 10, tmp; + + b32 negative = value < 0; + if (negative) value = -value; + + if (value == 0) { + *--walker = '0'; + } else { + while (value > 0) { + tmp = value % base; + if (tmp > 9) { + switch (tmp) { + case 10: tmp = 'a'; break; + case 11: tmp = 'b'; break; + case 12: tmp = 'c'; break; + case 13: tmp = 'd'; break; + case 14: tmp = 'e'; break; + case 15: tmp = 'f'; break; + } + } else { + tmp += '0'; + } + + *--walker = tmp; + value /= base; + } + } + + if (negative) { + *--walker = '-'; + } + + if (format.base == 16) { + *--walker = 'x'; + *--walker = '0'; + } + + return bh__print_string(str, n, walker); +} + +// TODO: This is very hacked together but for now it will work. +isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) { + char const *text_start = str; + isize res; + + while (*fmt) { + bh__print_format format = { 0 }; + isize len = 0; + + while (*fmt && *fmt != '%') { + *(str++) = *(fmt++); + } + + if (!*fmt) goto end_of_format; + + fmt++; + + switch (*fmt++) { + case 'o': format.base = 8; break; + case 'x': format.base = 16; break; + default: fmt--; + } + + switch (*fmt) { + case 'c': { + char c = (char) va_arg(va, int); + *(str++) = c; + } break; + + case 'd': { + i64 value = (i64) va_arg(va, int); + len = bh__printi64(str, n, format, value); + } break; + + case 'l': { + i64 value = (i64) va_arg(va, long); + len = bh__printi64(str, n, format, value); + } break; + + case 'p': { + u64 value = (u64) va_arg(va, ptr); + format.base = 16; + len = bh__printu64(str, n, format, value); + } break; + + case 's': { + char* s = va_arg(va, char *); + len = bh__print_string(str, n, s); + } break; + + case 'b': { // String with a length (not null terminated) + char* s = va_arg(va, char *); + i32 l = va_arg(va, int); + len = bh__print_string(str, bh_min(l, n), s); + } break; + + default: fmt--; + } + + fmt++; + +end_of_format: + str += len; + n -= len; + } + + return str - text_start + 1; +} + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// ARRAY IMPLEMENTATION +//------------------------------------------------------------------------------------- +#ifndef BH_NO_ARRAY + +b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) { + bh__arr* arrptr; + + if (*arr == NULL) { + if (cap == 0 && elemsize == 0) return 1; + + arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap); + if (arrptr == NULL) return 0; + + arrptr->allocator = alloc; + arrptr->capacity = cap; + arrptr->length = 0; + + } else { + arrptr = bh__arrhead(*arr); + + if (arrptr->capacity < cap) { + void* p; + i32 newcap = arrptr->capacity; + while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap); + + p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap); + + if (p) { + arrptr = (bh__arr *) p; + arrptr->capacity = newcap; + } else { + return 0; + } + } + } + + *arr = arrptr + 1; + return 1; +} + +b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) { + if (*arr == NULL) return 0; + + bh__arr* arrptr = bh__arrhead(*arr); + cap = bh_max(cap, arrptr->length); + + if (arrptr->capacity > cap) { + void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap); + + if (p) { + arrptr = (bh__arr *) p; + arrptr->capacity = cap; + } else { + return 0; + } + } + + *arr = arrptr + 1; + return 1; +} + +b32 bh__arr_free(void **arr) { + bh__arr* arrptr = bh__arrhead(*arr); + bh_free(arrptr->allocator, arrptr); + *arr = NULL; +} + +void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) { + bh__arr* arrptr = bh__arrhead(arr); + + const i32 cap = arrptr->length; + + void* newarr = NULL; + bh__arr_grow(alloc, &newarr, elemsize, cap); + bh__arrhead(newarr)->length = cap; + bh__arrhead(newarr)->capacity = cap; + memcpy(newarr, arr, elemsize * arrptr->length); + + return newarr; +} + +void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) { + bh__arr* arrptr = bh__arrhead(*arr); + + if (index >= arrptr->length) return; // Can't delete past the end of the array + if (numelems <= 0) return; // Can't delete nothing + + memmove( + (char *)(*arr) + elemsize * index, // Target + (char *)(*arr) + elemsize * (index + numelems), // Source + elemsize * (arrptr->length - (index + numelems))); // Length + arrptr->length -= numelems; +} + +void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) { + if (numelems) { + if (*arr == NULL) { + bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array + return; + } + + bh__arr* arrptr = bh__arrhead(*arr); + if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case + memmove( + (char *)(*arr) + elemsize * (index + numelems), + (char *)(*arr) + elemsize * index, + elemsize * (arrptr->length - index)); + arrptr->length += numelems; + } +} + +#endif // ifndef BH_NO_ARRAY + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------- +// HASHTABLE IMPLEMENTATION +//------------------------------------------------------------------------------------- +#ifndef BH_NO_HASHTABLE + +b32 bh__hash_init(bh_allocator allocator, bh__hash **table) { + *table = bh_alloc(allocator, sizeof(bh__hash)); + if (*table == NULL) return 0; + + (*table)->allocator = allocator; + + for (i32 i = 0; i < BH__HASH_MODULUS; i++) { + (*table)->arrs[i] = NULL; + } + + return 1; +} + +b32 bh__hash_free(bh__hash **table) { + for (i32 i = 0; i < BH__HASH_MODULUS; i++) { + if ((*table)->arrs[i] != NULL) { + bh_arr_free((*table)->arrs[i]); + } + } + + bh_free((*table)->allocator, *table); + *table = NULL; +} + +// Assumes NULL terminated string for key +ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key) { + u64 index = bh__hash_function(key, 0); + + elemsize += BH__HASH_STORED_KEY_SIZE; + + ptr arrptr = table->arrs[index]; + i32 len = bh_arr_length(arrptr); + + while (len--) { + if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) goto found_matching; + arrptr = bh_pointer_add(arrptr, elemsize); + } + + // Didn't find it in the array, make a new one + arrptr = table->arrs[index]; + len = bh_arr_length(arrptr); + bh__arr_grow(table->allocator, &arrptr, elemsize, len + 1); + bh__arrhead(arrptr)->length++; + table->arrs[index] = arrptr; + + arrptr = bh_pointer_add(arrptr, elemsize * len); + strncpy(arrptr, key, BH__HASH_STORED_KEY_SIZE); + +found_matching: + return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE); +} + +b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) { + u64 index = bh__hash_function(key, 0); + + ptr arrptr = table->arrs[index]; + if (arrptr == NULL) return 0; + + i32 len = bh_arr_length(arrptr); + i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; + + while (len--) { + if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1; + arrptr = bh_pointer_add(arrptr, stride); + } + + return 0; +} + +ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) { + u64 index = bh__hash_function(key, 0); + + ptr arrptr = table->arrs[index]; + if (arrptr == NULL) return NULL; + + i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; + + i32 len = bh_arr_length(arrptr); + while (len--) { + if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) { + return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE); + } + + arrptr = bh_pointer_add(arrptr, stride); + } + + return NULL; +} + +void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) { + u64 index = bh__hash_function(key, 0); + + ptr arrptr = table->arrs[index], walker; + if (arrptr == NULL) return; // Didn't exist + walker = arrptr; + + i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE; + i32 i = 0; + + i32 len = bh_arr_length(arrptr); + while (len && strncmp(key, (char *) walker, BH__HASH_STORED_KEY_SIZE) != 0) { + walker = bh_pointer_add(walker, stride); + i++, len--; + } + + if (len == 0) return; // Didn't exist + + bh__arr_deleten((void **) &arrptr, stride, i, 1); + table->arrs[index] = arrptr; +} + +bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize) { + bh_hash_iterator it = { + .tab = table->arrs, + .endtab = table->arrs + BH__HASH_MODULUS, + .elemsize = elemsize, + .entry = NULL + }; + return it; +} + +b32 bh_hash_iter_next(bh_hash_iterator* it) { + if (it->tab == NULL) return 0; + + if (it->entry != NULL) { + it->arrlen--; + if (it->arrlen <= 0) { + it->tab++; + goto step_to_next; + } + + it->entry = (bh__hash_entry *)bh_pointer_add(it->entry, BH__HASH_STORED_KEY_SIZE + it->elemsize); + return 1; + } + +step_to_next: + // Step forward to find next valid + while (*it->tab == NULL && it->tab != it->endtab) { + it->tab++; + } + + if (it->tab == it->endtab) return 0; + + it->entry = *it->tab; + it->arrlen = bh_arr_length(it->entry); + if (it->arrlen <= 0) { + it->tab++; + goto step_to_next; + } + return 1; +} + +#endif // ifndef BH_NO_HASHTABLE + +#endif // ifdef BH_DEFINE + +#endif // ifndef BH_H diff --git a/docs/parse_grammar b/docs/parse_grammar index 821b5120..610444a2 100644 --- a/docs/parse_grammar +++ b/docs/parse_grammar @@ -1,78 +1,78 @@ -Note: ~ is empty -Goal: Design the language to have no ambiguity (so a greedy algorithm can work) - - -SOURCE_FILE = TOP_LEVEL_STATEMENT ; SOURCE_FILE | ~ - -TOP_LEVEL_STATEMENT - = COMMENT -- Should comments not be passed to the parser? Depends if I need to look at them. Probably not - | USE_DECLARATION - | EXPORT_DECLARATION - | FOREIGN_DECLARATION - | TOP_LEVEL_DECLARATION - -COMMENT = TOKEN_TYPE_COMMENT - -USE_DECLARATION = use TOKEN_TYPE_LITERAL_STRING - -EXPORT_DECLARATION = export TOP_LEVEL_DECLARATION - -FOREIGN_DECLARATION = foreign TOKEN_TYPE_LITERAL_STRING TOKEN_TYPE_LITERAL_STRING :: TOP_LEVEL_VALUE - -TOP_LEVEL_DECLARATION = TOKEN_TYPE_SYMBOL :: TOP_LEVEL_VALUE - -TOP_LEVEL_VALUE - = FUNCTION_DECLARATION - | STRUCT_DECLARATION - -FUNCTION_DECLARATION = proc FUNCTION_TYPE BLOCK - -FUNCTION_TYPE = ( FUNCTION_PARAMS ) -> TOKEN_TYPE_SYMBOL - --- This may be too weird... -BLOCK = { STATEMENTS | --- -STATEMENTS = STATEMENT ; STATEMENTS | } - -STATEMENT - = ASSIGNMENT_STATEMENT - | IF_STATEMENT - | FOR_STATEMENT - | RETURN_STATEMENT - | EXPRESSION - -ASSIGNMENT_STATEMENT = TOKEN_TYPE_SYMBOL = EXPRESSION - -IF_STATEMENT - = if EXPRESSION BLOCK ELSE_IF_STATEMENT ELSE_STATEMENT - -ELSEIF_STATEMENT = elseif EXPRESSION BLOCK ELSEIF_STATEMENT | ~ - -ELSE_STATEMENT = else BLOCK | ~ - --- This needs to be better -FOR_STATEMENT = for STATEMENT ; EXPRESSION ; STATEMENT BLOCK - -RETURN_STATEMENT = return EXPRESSION - --- Remove abiguity in implementation -EXPRESSION - = EXPRESSION + EXPRESSION - | EXPRESSION - EXPRESSION - | EXPRESSION * EXPRESSION - | EXPRESSION / EXPRESSION - | EXPRESSION % EXPRESSION - | do BLOCK - | FUNCTION_CALL -- This could have some abiguity with just the symbol - | ( EXPRESSION ) - | TOKEN_TYPE_SYMBOL - -FUNCTION_CALL = TOKEN_TYPE_SYMBOL ( EXPRESSION_LIST ) - --- Implement just using a loop -COMMA_LIST(T) = T | T , COMMA_LIST(T) - -FUNCTION_PARAMS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) -EXPRESSION_LIST = COMMA_LIST(EXPRESSION) - -STRUCT_DECLARATION = struct { STRUCT_MEMBERS } +Note: ~ is empty +Goal: Design the language to have no ambiguity (so a greedy algorithm can work) + + +SOURCE_FILE = TOP_LEVEL_STATEMENT ; SOURCE_FILE | ~ + +TOP_LEVEL_STATEMENT + = COMMENT -- Should comments not be passed to the parser? Depends if I need to look at them. Probably not + | USE_DECLARATION + | EXPORT_DECLARATION + | FOREIGN_DECLARATION + | TOP_LEVEL_DECLARATION + +COMMENT = TOKEN_TYPE_COMMENT + +USE_DECLARATION = use TOKEN_TYPE_LITERAL_STRING + +EXPORT_DECLARATION = export TOP_LEVEL_DECLARATION + +FOREIGN_DECLARATION = foreign TOKEN_TYPE_LITERAL_STRING TOKEN_TYPE_LITERAL_STRING :: TOP_LEVEL_VALUE + +TOP_LEVEL_DECLARATION = TOKEN_TYPE_SYMBOL :: TOP_LEVEL_VALUE + +TOP_LEVEL_VALUE + = FUNCTION_DECLARATION + | STRUCT_DECLARATION + +FUNCTION_DECLARATION = proc FUNCTION_TYPE BLOCK + +FUNCTION_TYPE = ( FUNCTION_PARAMS ) -> TOKEN_TYPE_SYMBOL + +-- This may be too weird... +BLOCK = { STATEMENTS | --- +STATEMENTS = STATEMENT ; STATEMENTS | } + +STATEMENT + = ASSIGNMENT_STATEMENT + | IF_STATEMENT + | FOR_STATEMENT + | RETURN_STATEMENT + | EXPRESSION + +ASSIGNMENT_STATEMENT = TOKEN_TYPE_SYMBOL = EXPRESSION + +IF_STATEMENT + = if EXPRESSION BLOCK ELSE_IF_STATEMENT ELSE_STATEMENT + +ELSEIF_STATEMENT = elseif EXPRESSION BLOCK ELSEIF_STATEMENT | ~ + +ELSE_STATEMENT = else BLOCK | ~ + +-- This needs to be better +FOR_STATEMENT = for STATEMENT ; EXPRESSION ; STATEMENT BLOCK + +RETURN_STATEMENT = return EXPRESSION + +-- Remove abiguity in implementation +EXPRESSION + = EXPRESSION + EXPRESSION + | EXPRESSION - EXPRESSION + | EXPRESSION * EXPRESSION + | EXPRESSION / EXPRESSION + | EXPRESSION % EXPRESSION + | do BLOCK + | FUNCTION_CALL -- This could have some abiguity with just the symbol + | ( EXPRESSION ) + | TOKEN_TYPE_SYMBOL + +FUNCTION_CALL = TOKEN_TYPE_SYMBOL ( EXPRESSION_LIST ) + +-- Implement just using a loop +COMMA_LIST(T) = T | T , COMMA_LIST(T) + +FUNCTION_PARAMS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) +EXPRESSION_LIST = COMMA_LIST(EXPRESSION) + +STRUCT_DECLARATION = struct { STRUCT_MEMBERS } STRUCT_MEMBERS = COMMA_LIST(TOKEN_TYPE_SYMBOL :: TOKEN_TYPE_SYMBOL) \ No newline at end of file diff --git a/onyx b/onyx index 53dfc750a1dc8812c5d94fbc5b338c236053f2a9..3394ad0fe51e85ab2c41ca2c14cd02e966b8dc20 100755 GIT binary patch literal 82224 zcmeFa34D~*)j$3`lgwl$Gh`;i7WQE^?7_N#f(^^)pacREMFjyN2+AfTf|gA~lrg3T zSE^LeYDH@;bt_WAsA$ky8(XSrwKcZrJ8@_mmugfrzwh_l`#kexNV>egzVGMv|9>3K z+vjo33bMN?>uHGD>)F~I0=c!1Bu zk42ZBw&-eB>IE%78Tetnu`ZU5PkSq{VWpOIrf!XEFR9t0pjK)a5dp9sbuFxxBGQOt ztkj#O;U}onsz>!$uUOYB*7dBkSu19xR{PjC{>;|xOHZc&$B&hgmY?GH$`&)fd!-bQ zOSjT+_*kh`Zzk$d&wuq3(uA|LypHTrtM#{1-CSez@@1Eta?n(b)vMJ{7HM(^b(OowKXfjcR`sj;%C18qd%N{y8owHJOAAIs?(o+^W*)) zPGeo`2ibr5^8hHd>$a)WxdXVr1Na+&NAQ#Wp&PWvzgGwFsU5&i>Ht2e1Nh<&;J0)D z{|N91e$qcP0BBGD5gov9>;S&01Ndbfz_057&Z#E?T>9rc0NT@Y72pHXiCKm}E~5LL zVyrD&vvSdj)hb>E-j6EL^tSSW~)i`SMkZfLOS8ZRwga1zl0P zVr^-ev3zZ5>E#9ri%@mp@@1EO|oXv0~w}mBx}arKQGM)22?EJpaT|V@^qBPqMSA+=-)3 zHs(*AT{M4j>6+3@m#r-;T{F9A^72(HOJ^^T(J&#)RWkkchH4(vEaH||igMYG2$c+3ncorQHpMo@a9}ORy1~1U?!Zi4) z8Xil7pRVD>Y4AxJJ|_(x)$j#r@Uu01X&QXGhObV8&(!esY4AB3z9kL5K*P7C!Amu~ zCJnw^!*`^?S84dJH24|~-<<|8)9^iM@GCWZZyJ2PhS#RSuh#HGY48miUY`cvq~Q%| z@GTnNmTsSY4F=Md~6zghlUrX!SB@YSQ>n%h8L&7@7C}+Y4BYdz90>LpN20@ zgFm6+tJC1GY54jyc$0>2NrM}gOLrLC(%>EquStXFX!wpacsC8-l?Lyv;k(n|12lY3 z8a!Xa_ol%|Xn1WJ{6q~ulmSa7YIq_Iex8Omr@=4Q@Rl_A zA`LfgZtMTcG~Am8U#a1tGtr|Wy4St`77pB1< z)bLmu{4otLPJ=(C;d9d9Piy#sH247x->$AWCx6W6@Ep@>4kx{CwcEFwI==Bab+h%>XIN&Y&FS_fz1JJ_!fIhL2QT2o^y zK7+s5h%&pZsb0z-vCOV&s+IBwEVGN6_DK0pEYr1`c1ij7EVHYcYNY&Ymf1y3TcrF7 z%j}w_)lz<*Wp+u^0x9ofnO)ITEak^oW*0OSO8H@y>9S1)QofI6x@uEI%6GENu4xKM z`DT`LST>}51Iz4+rsl5!7|i-hYEYKXn7>SozkYEn{-;>Qk>=vr)2iKTb8;_sF9fa!;UhNkZ{ueEW>s9?XX z!hhZiw0f!xmRGP|^~skBo1tH$e(brgGh*@P*mH?*$IO>vuYX;Z2ZkSRPz(cP*b%=i z6UKp$Q9e3WF`*6>pc(WD#HuE|f>NRrTAYaC>m_$C=uE1!#lYv|7r+wjA^E(fC7@sq zUQ`f^Z%oAE<&Cj;NkhEoaI7kth*cHBxk?&h@i$`ey2KsB4I>thw!|tnwiuhP21R34 z$>DfNkrsnAirU2}m$c|SVPI37ugeit2HW`aIva@%eLfbiuc$R+i|REQ$yv>dv?XRL z(s;3VFa0&?-piJ}`~o$PZ#-5NJr=9(@*-jAoj-pE)~m{N%@Z_Kp~$?LSs#4L*cNR~ z0iF*&<^F7Q#MtHvJ{4`<=1wACqiRhonK=90*fmGDk!5`UQdns*cn;2&aC zCflDz#sw8*e=kOYCbWL8n()0eG89QxtH~NPS$P^6muryi(quo?WHZyq&?1susmX58 zWFynaxVVFCp(b0T$$V*KT=PNpZA~^_lf4HEI1R$B0Lc1jvaXu!x%SDvL;#uw`D=?Z z$oJF8LWVHWVy$>u z{UFFDYqH^*tO=guGzhotAnU8iGBw$YX=L3L+2_+i4TJpQGi8u_)5y9jvcsC}DNVL9 zjVw=*{X~=9q{%KwBkQ5aZqQ^)HQC8&WIYvGsV1AK$#T=kdMmO?nyjZL`wIr9(;&PX zfkAp`vX4JiEqytStdAo5v`87`RZaFl8d+aO_PQo}K$BgQM%GV}J)+4rX|j1~WD!Mn zy(T+flZ{Iw>#xWz(PRahtZN$C07W)Ille5+hnRw#1{tWx@-*3>{;mx2%QUh%n_t<-+w_spQ z%3EI{eO+`55+hj}tB7u~cVz#YTeiEEI+fp%y`r_7;+BzHx|eV)2%trY%m}Vdv6Rdf zvgj5rKXuVv`B1*;A}l+)iQ@+ZEAQnFqb|CQ4MSQZX=$OEaz;&ZT1|3lP4di| z^UUw;57}=hxli)_)Cd%#@d`?Rjb=@t}aeg zO$t@V%=p6`lnEZR>O~zSCJ@4X2t4i>*+7%p;*}8n~LKp$D&Dn@@B4y4f8Z{A&d6M4P0&9%7tnu>E)>ajyKzIczZfpS+f+w{F+*d6| zm)F>6G#Jqr20ehf>TfV3<18SrGAdfk^7kxCi%AuuETF)_SkkJ(kZPa6Ldh=eK`j`0 zy2}2bgB`YFJW^K|qq&N&&7%!e^YCgOs;HHa&=1va%Sg*nk(!E@&g%jdE#bBJGRv}( zNFwD~&bW?pnsZ^Ze5lqCFJ~yK0ZiG;v=KT3`*b-UMiW)Wxai@sa{)#c@C02j{z6ss zFbZ!;fzcz>U!SjHrO`y|>ipK#k=E6DvH0%}&^`9@E3pr|as*E!Qc7l$B^{Xl56$J zImlys&;?22?hnu^;S5?eJX(@hhzuCpZcpp#Q0r=MYeB+f!z&KbEuj&*L-`BZQN#STXQdzAdGRZl7u!xku8h#du;}v;`6=s_}R+=cmLp zwyWhUr%B9f5UaOKaN+6XZ5h8E5uYSeEonzR%dC39=~<>xcOJ}F!hA%@Z_n7bruaj{ z4vbxm;jV%V;D)#s8`B=Mvotenz^lM)11jLBFvMY(UpA$pWms8RMN24HDVqsBCVu~= zcu500_b@jhs!0t85E629xA*Vq|z>ZWDGA@h#vei(`tbiZ(ax z(v@PC`*)Jc;{HzuL>wB0hE;ccbXf#!WsYh%AbSVM7%Vb48esyNG8&UJM3^)T6vOG} z*c9u=%EViBwv1cbb7Jw24#?CV|CJiH860`mu(kUabL>VKqP)H?3J;TJTLw(zRkXO* z&4^<~=u(L$V3fIEo@f{d8gs#`c0Ypx7MRvX)WyltNDf2>XKo|e6B!PlWG*s{Y9zhL zFbYyrvGHhY*&Iq>V;OP}&S!i#>c;`qN)3PA)|bDnF~ zX5;Eo?Y;(r?3KG1g7$(0KbH4pi`|>#90G*xLs7O_Lc=NQeq}ioEdzpEKZ1#xE~ZbC z6$~^A;TXueZApZ!uWcitJY-Rw%+sBX*0Zh~ytMHq!Suk_gC0NJ<%nM~UyD5Wz@Q>g z+}Tk6Z?|PY`8-R~-H3)*`5nqW4N|t8U5z1e_E*vKkgA+rHV4v8DBIoF*mNzm)Qz1e z%%NUNl{b{JIM~Dgkp`WX@4CTR@MWlxrZ5ZdGebGS}t?fznI%c!*dIbAaB; z9=Kh#7#e745|mP_*#~fPw6GccnlYTSN+T%3oT;hYcC<*@I8Z@{y2JwIhiaK9>xAw- zh21+Y+EVUAVJHeobP3tYf|$qr@fvDROL!_;2CduJW|*FKhGUqHL_vnBB|jJAJUNpk zS=Fi1Bs4a8hDDIyMnP_!Tax`PNBIJRtR6{vQPx+Fr|B}ib; zSGQ3PoxEaWvjGo9gROI3gM%O@jVH`z zc>qTbXrq^}DI98Oee(3S6&;+{gL7(!BS(S&;w~~1*t_Bse_Q1*|75G2u!mV8eHe8Q z9gvVVY+3z@SWbG3_RcNuMm!y5+sBrxy6CEP(FS%K(ng)C)0O38j}oc-o}BuNmcr=_ za6+W05upx<&L<)Ghbda^QXpQ>Ls-vg9`33yQS(3SX-^tnOSc~4#N1ABoeZHD8;o*d zQKTCntt-qEJ;lvwumn7k)H3sEhk#%;l5>NV-?n!O_NGs?-|~@|sG%JzV&fxCIg1UH zw(Dl-US>1yVgygQpRl_kHQ8Gl<+JXA9L?+tB~OUQSd*4gzeO4=`NDj*H2ED=MSI)Z zO@7`;tIIh+D753U$tlPEXe6~$rzF{$cEIeBw#+y)V48u|gOyiX;}c#Y)_6shH}XtC zUk+_FRP=3>b4z3U`c6%W5Q=v7?St3}73LBEX0aZMXpg|?QgOPE5uB=z#pgA|OI}4>s2-df_QCPs-7+)1$7$O#ddaJ5y{s!l z{{T*JuPX$Foew$kYEDu#5eJ-Zq3Tu~9D9)!2D#?w4uazww>jcxm8+%CZ(A=((Uxg( ziApYZJW@%FsZBh`m9@HPSFHL9j04p$GztMYX~Q8%#J((-hKNUNlIO*$s)cb?B^mEi zQ=5?LBf}0f4L>C{dJ=-cWy8JK4k|n_l*)48@8CHXcLRCI*U%GM%kM$-Q3=qRW4D3V zMj+S%1$IeVh3F%K>R4lTD|@JDEYB5n(OLy6+6GdPm+bzRqn@&_X^>D|eCAkG2a9ZNQP=^~;??Zot+i#5;Lzxn;LxI);Lws?!J+f^1c#Q^28XVw z4-PGF3=ZAc?C_~dJ}6_1wvy~)RbGr*@4kFaMarS-F%!n(mHC2|r0O>6K=rE?G>U8q zEIHAOXf*QC2=aM#l?5QP)zN$~8%t)@mBmcrm4!&EDy3biCtt>eFc?qo zRkw+dYV0_xZzIQ8{B=0v0lrL4e2BXaOZAFcbZlh=6v?iDF~bd(?3s1R24Y`4%0Z*H zetCFowdTeDi^}ELYuh5KWJ3}m&qr-AjH4_m>!+qw4UC6sfMjpLfz`7#MBguX!x3+W zS#>}es~i0*xe3OGDc2{j)SHK1xEJOyPd4&Q(& zRi@Dzdv2^a3Qs~axxWi;9irC6MAaNw_aq$b!agMad!^eTTerbVHw+|Y4fL<rd>3x&gzpPCw{w+Xk&hOV zaqW@tWLH>94>y^w)loMrIq_!DC3zlifknqgKPmU1)+d;7 zDxxhsLlL~Sw)~IcRdziLmsG$#83b_xSMxk|>1vHqtt`wg3qHm9myof~m+_KCA`#0eEDYS$;Un>a)_U9Om{a}#oFk|e zmhQG%O|wpuqFFg+2F^|;jY>PTN6zz>YWm))15@#|R5paBMmxc&q`~4+FKyi?P30b< zwVs32HeR)x<`lHoclO*xrM@R6^b637B8x`_83*sBfMi$cLWyEFZ86FaGZV@dJCrsIw8;}R_>g80)ePWk`c!etuRw+w z1~zw^7_D_&|3bd#+_=|^X7PCDjRhyeDiX=U*<0_p^}b@geFQFo(X6eUwJ^|*s3~zT z?zQgYZH2oP+9$SaG7 z+V^B|n$_fJ45CRmGk}v`uyj6;0(Bm{NIM^4-r7^D5Ik=ksIlGG#n4W!LgNf_7uBM# z_oA=&fF1gtp$--1!@VcEJGeCg2JCk)`n~CG;l>u>1YVKwVsm-uN*%>NXyfFRen>sG zSESgEtc3u^Q7oGRA$CUUG{BqAO>B7{rop#y4p1xJUl4GXT=F6?Yte=mzKBAoehI0f z-9;1wl&Bn0s|@>w;0RJ%OKgpluchK!1=5f0`I@*%TgU2W>0Q;4W6^1-9mI=v5HB9k znEI`RJge`V#|7(xzKxmQqByaE+R5mA1bslqfNfO3%3|2AGjuTMsUkMrG_QPgG5YH?SB3QScZdeG+>+;W7X~_ELbnV zB+6Fpe$awt?Ik4H6JirpC7;N_$?7|=w9_6_YQ$r~tcxC#OA$;XRYk{Glh1VO5^&+v zGJ!dPD-~Ch60wfHg+$QaNha>6CuQ&j%HTZQ7_4Xsteey3GUF?}e6;Tzn^AzH^UhH! zB}X7L5v+Onc#LrcUOpa^#^Lhu*d(I7%m7uR)Na`K6ig(#tD*PiE7<)?5Kh^FLH?`s zE{1nSG)x1!RT)N4*Jx(E{1aJ|rC+%=%OSr-?Ag|ag=(~6^>-{nVtY8WtibhHfg51V zi=c}*XvM}uMwuTphh!+lL-P2;UMpm#gPrTa(VprYhtMcr3F+AmOd2pRS3|`rR(O3M zT?E3`Ad&Wsi`JKO_ewI}304(>=WC$o{j#XjoW$j*!ZOA~itHS=`uy>b9tswINyz^afo7J3ZMN&@2t@n#Fe3 z<4w^l!-@V*Q@}Ub8?Vb=0pc#@Sd*!fOVh)*7bD` zYy2a{*>vcZrnv6IN3BH_hX$wOYmVdIj^b&YwP#q0pW6ZJ?#HoqTzJZP@?=oNqsJ;X z9z)jwNnJ5YZ0}`j)hxl@m-(Inmp8|mXplm2&Dew>b%zMu=-2s)fr-H zwwg}?zh-0A9uFhAF5y#+m;6%-2cOZ~8udV68~0oA8C$OfXx3im25uKd=Wc97_JC{` zY9nvQ@&LoJnmajbn5Hz^Lk*@&gE0O%6_jIpCC~2a!3L9&wX17H5jxt>?lATyMv|K$ z?~t4|s}R<#ibAf$+Ro$ci(0l0A?1j}kJx&T;{ZYFlVlMV6wjP5(FOly1t4SSMZZZ|RjjDNQ}-@T`zHrcCJ8 zD(+SNB=(!AZm;Yeh;vSE6I6`FpPVDzciVqBs;EMM+`6|30YdL~LVz(H-qVptWmaqf z3{dA&y(*nl$53+49w)uvFiHW80+%bVPw%hlXrsNn^OsD}LvT-=`+$%)ymg6TmWyv_XoS4L}n&Tg0W9<2kTae1IC2jn*XIHUcY=rp6<`;>xF@y4on)=EFVv>l4wIVRE~yhX97+YLp%D&F6_X=IB8mebrZMcQ@l##Q z$rur~lP?91)_si=NNU+hH{1WG2k0%&I_#xpEyt$2t*y?V1zYvtr9-v*9OU)Pe9ch? z{A21C1QV8aBd>VVf9x7WO;~yVsvA`oP<+*GqD7U`$?_(|iLr}MpS$*JSF#_JuRma$ zhOr9EjjSsrw;kjYmx&`Nf+OG{%w3qbCShIW){=e{L%s;%zwow@iX!s5)rvJ-bF`L& zG`?SX(UZkcJAT_VWxGNoX>;XE(Z9j`>RPX&b~75W6hu{(tO4;}mBUji9 z1F}`s%wnP@PuZ--K0^?$K3>U%rD8w7eOF#xrHYAczS&&c=KS;_o?O}Irz0q+^OICq z2noq4b&|h?`LQa$i1`sJznJ-m%9k?VMdg|>TRzC*rn$UitUnG?04JeDT$$1Kpj15}Q%`G2K4+JbhQ3!2^oxT@O8?t8J%K9}B~i;5Nd`7OOK#rAsNkCA8h z{@3_zRNecmVD&!pR_`-!^*-}f?=x@pKJ#|(v*74`7TffGn)WcaGK|&sn|h&!Wts4C zMi^ODIi3XnzE`kQVN2D;;uTG@rETjK=j^k+qT=XkY;>^2#8xw2vIB#^1_K`DXnoV8 zcsgO-tJg&jp&yJfFr;#~m&`$ii*XY3*9uih6DKagW3=G)e|2HvsC7Kn&d0AP z+g;xa1?hT7lV=8_*I~+wqJCt`DlzX?;TPBp4&KjdbG0*C)61u{y4AL|e(!{ARX zTGDfHr~g>fTK)r^;DphGRndd!|8db>vEZ5b!p*ljb;AKf{efD2Yann4spvN1-LNH3 zzC%&b8h>~eORwSKk#{0PC7z4lNwv?Xg~#7))vFUv$)R}B!71ut{UP=sE<4v(Y^*iP zLwr?Ro-OCUrY!Urv=06RM^}#IJQy4!iB}GCYHZ}2SUR-adFUI3UyJdJnnkHLM|wJa zyqLe5)~s(vnzpN+5*_f%mck6^s=_YgqP3f@Bq{Ro=+R_X{DOtCDGOmVvla^y_Dito zQ4~*))^7N-(()*^j6q8|-9v){b;OnX$QP8dctykeE%gg>Dq;Y=jzX`bj>Usf24NcAF18g zf!c}o)wUE?s==`(>Vwql2nC!%foxeF8k4%1lKaN=D_5sGrEM+10s$Upmxkg|J~QCe zlu@(otuq=o2=o_|@0wk@%#LsAH9+dvJjP!ySw*su!FJ>vAH! z|8cE-+Am{{2izVQ1*txa7rm`GV963?VGaD&Wg$CkH!II#zuWLhB6vWmlD;ci|=gnmpoR)Fe()`ccC0=-Yrn z8g7`>uOcf2t;ean?~9kz$BSOA*!U{mw}jRF*`Qr+stOr1j^7}fU(4 z$1Cf0gp^WYp56jiTA())M*o8i>c-|GO%j5>!sK6|B&Bmk0uoK$! zdznjdnQy6S{rVOzZ-fo5+GQT8DmiAcsmiS9*)4KB=D9y*a5n=_ABf4nB6u=Fi(dLTi;X*J%XJL+Ii>Ydyg7E{DJ4ptf z1P8}7%?4uFCkwE0kmk_7LO}F_x9unNkl!QdX!SN?vf?Le;7f|uv z6v*o9P5P%?4zW5^DDo<)T-j_Mpc4Rq>oF7Vm-%7R~gSSK3KM zOghfiJp8Ej@But)VWhVE@t*^2Pe53iZL5RVA=}KC{a(!6qFao`$~LiHWwmSKFPh+; zftVYJw&qUo6+mqvS4}+ELrc=^9)c<`tG)>A#hWRZ9V=}#AvR9{Stnih&lP@}uVz)G zie1#P!b@$X{*mU%(d{Wb|Hq^G_R; zYtu|=ZrunNqbR|xcZ-iQ`b*zDDCT{;E_xVXLla47G^Te(Lo&B2`nDK2fx%-|tY|Q0 zPQ|sk42iu?Cn|qLTKY?&D8L+9RsMDiLFcD2_hZ=9B~FvjpX@!LY|2Zmzhx0+Xuy6F zdM4QEKGczJK4-6sHj3Km*E6Zhu6kHGgr>6nD(_Cnw8_T5ifI6%(cQ%Jk_PdlnO+nD zh6kkXY#Qu~e>yaNWTUC~N`2IF1}NSoVCJ}xB-re!a3>Z;*`fhZS-V-XtX|DEj;{EBndVz}B1|~+Yq~0pzsXgpdXlhN858gz6ih%`DN@)op1ueuIndi%Yp$ctadiX7} zYb&gJS!B0`>!m8R8JaXte3GMG^1!ccyX3E-it5^U*BspgOU3ukmAkqpi;*1X&6R|r z`7ll>R$aM8Naa^v&%neVuUrm5#eRMpWtb8i0naJ)@^Gq$?2k)`Tt5mWV~;Hnf39{Mu(kCqN~iAVWMXgWSvT`e__tMbOrQ#9a^ zcm_q@E8?*_CAFomt_-!dwmxkzTn^&zQ9%V`@nz?iNLFs}(NQ0<5YOpgO>m z|E3-GjIZ!D9=sakO|h4l3yO0T`#p;NUSS`_-QIo?QLTS{;z_|*-A4RW?rZB3qhF(% zAVvOJJds1&)=TY5sR`)hUsD$aMX@JU)22r8)oByav`JytT9RCnX>H5{H)|45`mKde@M^lU?tJWBcSK%L5DJx%tf9hn_ zno|7RDfkyrRxL6vUB2p)h06{6BJucQsBxX74ijK{w=HQX3!3^N8Bqm0qU zT;nw3bYp}u!B}8yGWr`A8$+B*oO?mhq#4tkkZDtAM`ul(Mun$NKdU{=^pc|Jtf`Zo zsL3-*(31IsU=AZXYu1ceM)9l}XU#%`&n%ffc}nz5W74!4lg}~EojjvBY9KptnlW+8 z6r*VB^pbOpqLOK|rxs7Uz?d@iys1;7M$wEZCDQ<(9zAQ~?5IJr8d@!5^2Bpz8^sgJ z8ce|D+;gX%HQksMon10(x)GgO0?yNC%$~1uXTj)bj-gWxofbX!T!UY_40>bgnNVv+ zu`y%%1#{*@#{B6srbOqTGZkH@8dtvZ@|COBtu(G&wQR9*$*NV$jq(AE<)p9-U6DeOC|y}b_by#jR=PM+w(9cI zm5~z$uRUQzOM6{6z6H6F>9tla**~?Tnv(_!*9$Q}HtyKcD08seI6cX&8T6*{! zklu^2u@mX!XHg&Ndr03#dI$W}z{cQV_-O=b^9!x5g-9QJv9)yp(g*MuYAe!%Nbg5_ z(V^DXLr5F&c#eNBSX-(o=tlb|YPfvyciMbO+LRk?uu0_;u7rdIi!Jq(4WR2X75vWgUz3 z5~OpHmLc7Mv@e#}ok*WY`V7)@4x`;j7a%=`^oHNUZus*oS#Q8@NarA(i*yCj4Ma@zWed{rNa@pakp{4d*no5)(w#`3MfwcVo;YK_ zjdVTIV@Q94v^W09j`wZUM|u|0xk#@=x&i4sNOvOr`yWsr>AinMeWZUxdJO4P@1Q<@ zrMz<^>LX1eor^T~F6tva;ZLZKbTiUtke-j7+uKNQLV6788tgfGLWdZ^cd1d|AzY5rp;(ZeWcqygx???{So{I>C;H> zXBiKI4Lx?d2*{gF(-7LYjP)#>q%&&cy9oH%DtGY zSj)m+C}@$r6X40fpW?({q43o28T?!T{B+pZu0O3hzAhVoC5r1z|VH#T{CR{i-Erz_@!;|i){Q>;NJ&+ zloP*9@s9v@Kk%ObU*p6_D>7WyrF1w1ngx%+7ET)1R9goB4B&TxZ)}5q%f@?MhB5u| z*4BI4;P15Y`N01Y_=nrzAF%N;;Lm>oeFZtTjWWiVBna@aNk2-M}vfUfkUBm1%+&{)d1s0iJe9$$!C?p8)==pS8B$*M|T1ZT?=YPn-9) zkB^{EKJX^ilgFI=(FYm3MTF=u4Kz3H!}{x_$;h#FSPuNPKgSx{20zip-w6EfXIfjI zZ-d`2csZbl;QtK#%}zO%5WwgY8$dG?YpNH0W9yQU;i4wQ?*x9_!PeFv zB5&he_u75-4DinaU+Bb7tjM?;6oUYJ8~AsCZ)0!TYrJX)t5HrS_6A>JEq|N<%eP#Q z+HwW~U-MFH>u4wbGC@oIDZt+Y{6HuE;uL-{@b>{f&WSHe;kN?+bKt47Eq{Ire?Rcg z0$oO%*ok%|eh~0c;HNq9 zuCwg=Q-Hq$__N#Kqc)!Z@aDDFOV|*q7b!tna$V){+0d@Yle{+u*m__Kk!==D4!TxWfv%YJ+&3_y4F96@3jaYv-@SgzRk^DozHv!+#TAKjA;uo#0 z)otW&v*mlSCN~4$-Wo*t`M`e-{0YwbmpIxFyc_%4gHF8bS(`uq#iNISm%WEI#+Ib& zZv*}b;Agd|uR2)T5B&SUKi>ww-{yY^_-)unm$bpF4r2WT@cV(kxDEaSyS^9uYOZR?)^{&e8Gweinf+dsY8hA{@`hez7ff6%U<5Bz@MCphuSeMP?*@V^JX z!HIYM#;(tQ(dy>caYk)}pJU^<0skxDJF34M`0Ib&as5NU9|XRm{Y3)!3Y>2`+Fy8a zPWd_T9m&rJel5;JW1adt=GYkURlxt*iFf_cZXf>zujx2Pbu{@vax``urEmZooP0-ZuE1f{y^U4S0Ve)<~y* zT&qWmiUw&O0ZruH*477+x7)~dLT%$jk9yGj05mr^XY-gBbpPb&YYB! zf1^8=Q*fOpmNT{@t0<>%P0rYfIRz7Q@+aj)KnMCsIbPl>7`<`!T>w55_08uS3w&dN zZ!GYQ1-`MsHx~HD0^eBR8w-46fp0AE|GEVh@Gqs~6VWM;Rs2{fj)U^amx~3Q4R!g* zGL^xvQ_82XIwEN=RWulv;isoc@FFiFIi9uovG9NYx^)%HJM82*JX*TDbRoPyIA*2 zI@9xXe=pJLl{&pnr?>0$hdO;qr!VRB4V}KP(@%BkkpQ(}bkpe&ot~o8Gj*D-&p#`B zYa;EEe4_m{>oSGzqtgPNo~qN+bvj9>QJtQx)9E^$sna<+U7*uaoi5kuDxI#;X_-#1 z)aiPiUaiv&I^CqxEjqncr(1P;oldvu^ah>YsMGK2v__{l>-1Kg-mcRfI=xe;J9T=u zPIu|_KAk?H)7Ny`q*LQ^Wmk_*b9CBGr@eJLK&SaS9ih_`b$Yr^C+YNTozB$hc{;sV zr;BvDOs6Y#TCUR#I=xn>H|q3Oo!+O@2X*?GPM^~0(>gt%({`Q90emx!&YC~pNrQ9# zA?}EVTk{F$DZ-mH+?uyI4YJPBG&rXR;+oUooL0#5lQcNzG4gNGa7#bZlK+=!aL#Mw z-*6&%kymB~~C@U%fM zoxAlEmo8t?cSaAe!FYZ(SW)z|)*e-~zZLy_tfY#5jNFSF27kY~y^o?_+s~r!H(pW6 zwQf5V6#aWcEc#Npvp|;;(at9b%~f}L%~RobftNt^0e;$RxXbhof$zFpS?@E+%v!_5 zo3(<8>ESa0m&;v(retKgm*JVB%j>R0DbwrrvMWF{8Z;i}v+jae*8o=~@^0^?`1aC` zGX_Z(IV0o5^9g+iH8M_>>|hLljMJF~<1Jte*-L?Td&>ax@d;^W_$0~VrD$fCNzC#U zX=WZP`S_SLvj-2;7ggw?YRI{fo5;g6?fPlOa4D7*oDDXXe=LW6-|8U@X ze0L6vz;~Ab|24+0fqdxDEpP_Dy9b6qZeAc4-#r4k!1fG$fx5i{n<1-r;9bD`1l|R_ zZ{Rb0_X}K#T9Lq3(DV=RZht`F4CDs}egw)vfm(bI4h(=kLjvCg!&G9J zJAV$k9<>k*Ec34Ib9$CtZkR!n@?6`OLgEbM^8O39GPBK>2dS!Cy z)adm(sXu4Mx`B#N9>yVS?wkNzZw@K*bB?Ht?KPYGELnsS5gFTeZSFpw`h3CK8Qb@4 zh6Wsky-s7p7E!IM^sFzB@`PsrT&TA+V-Z`CvAtnaUp8Ygtwjm_ZlFdVUp7E#6ZuRZ za&Cqwex3xndm*Zt-}wcK8QcH5$%sVtN#qqF!R0fE`YtbBX_yDtGa1{PLDW~9g8xPz zKRHlE5&c3DwQTBsH(=(gs<>sd(f^%@kjm>q<`n&ZDS~aAF&r*rjk%L`Lr+oUFy3as z`;=V{73Q*OuI)zmY-&A^lf5rv;jIxN?OV2DwP9W?tHJhF#=r+@(K#;yH%PNgb>L6H zviBV{&m8)^Yx{Cz&{^aeW1m?Ilsx)CeRHR>b4+^+lmVr3mf}9kmzAMJ7j|ur_2xh_ zV~Xh%)yAnu1-H~NVo)U0;J;I&m&79Mf4C&3uk8g?>i_X2k!xFLu3|p^V#RSw;=i{J zj)B=bUI)2i>2;8cRws7SD*WUylf$|>w6XdsWsYuTuY z&AmJe``vz-y*?9c?g^fT3qeg)PoiKF-e3%kkfx{rlFtSPM z$@PGy`=7BA=bN6+SD_&7^>krfL)zu(%654}H{&ef=|+-B$eoRR_sz)XLxJyuqD@gA3XCyyU71q z%8i81#CKMOmUT8;-swZiVzwabI<{BIy51pcn?qJy$$F1!-k@Z$GqbA5GcWWwW!VS6jqdjxxfq#(Mf~*>vS;^Y&kae>|)-8^9-Rh8a+Y@5L)7X~VIfF(*cT(;h zx?S_xU%{6ti`jy#JBL9DCF?GStep;7-*>d@ZilRU9PQe*Pi)wea_^-M`9|<%B!yYI zRTRKYPF9#%BQ%IQbXH6sV1+KsheFltm_FM5o9SD36+&TqL1-G0) zG3J3_;4NT7fsgT>8@LGk!+|33=^Wti5OoQx0H3abyO8e|_z>US1A8GcFHjA5kHDMY z(=%`lc=ifB30b|3Y|($}6Ku)|@YhW_hyTJ{U<|4nf!@fPfeZ+A1-1Z^5#ST0%s?*4 z-GKzYJ%J_Clmp0n18dMAU!V*evja28H*gP`1-^@XCnH;=%-al5a2fdIWzC<5%F0a^ zP^=NEq-!ty1+tl;pR;!_c@cSU?|Jm*MFKbbg?bmTxL6hYhI(I3^-EQ;PpJ24?B*q^ zh~ERsrm(C_n<1bx1+K+UAHzG5JeDm*!GED2S=E!5(-i}k0^kb#1!`vosINERffczY zAOp9TlbPGg!Rz*Ny69Vw^^T(xDl!{r|mHPjUKuw!`r$l>yjD3>1q23bF5UJtKF zna?*m(*z@dl-aqr(eD^EGToHt%h<9#0xYVqRpxL2bik_Lodisx1N$-zy%p}?f@YYr zTx5KYJgvklQK=8`&N^@$(1J#Rf_ZFP2mT0_Fwd0=uI&p3mQ!?zi^B=0qk-c=WuB+8 zJRUs+#Gq_Kr@OXj0jb^fm$rcETEJ4HKVPDnr@B}(<5Z=K{2~Oh=FXcSe!v>+jzPik znX$cb(*QZ!oa*AZ=doh|hcfUS%RKB1lqQT-9C6$k%zgNH7Y&}Veaq${E<8!emfu4`*4)WeQT0@_$)3vC zzIXG`fgt@A5a{j|s=}}bbgV=y!O1_1o%6a$;vFX{4$q?_`7w8H08+K% zb-Nt{h5ccvK_BDnZ@yu2`lknSAvE7I*_$|#59|9a#p_m;7<0jKFphig=n64o$ZMM9 zI!!`!LZhL5zMldf7Qn1AipC0cnmfO+HGDXwVF7h`%#twhG%|SHdyNU=zplJ>o}Nxe*&$2JeNei3lp>N}vO?C}$zQ)z0+-m$9pIyi zOC+$DvHgk711}|qD})0T;&#MbXR0}&aNwK}itDw&IYT0&l?>~mb|7~t=30}huy{{? z1Kn+^R7j3g#P_KL3e5mgDKu4EV9t;tt#%(xH5h+m0dtn4jaQ->=E(w>dK}ApY?dcm zEC)`Y4r4Wk1p`;G4X4=*hi_&Nf5owQ&2S}De(w!gb7uu;N63`(l^Tk>H}{hf)XF~Q z$Q;(7EB!?)JQ-C6NA}Q1$b55q>dp2JxS>jjOe~=vVoeJ0Ay5wefSc zFf28OFQds4q|ejzb8PxtR8{l|ML(w>S2weXp#Je$gKLC09z2C){LH=KjCHj5|rQP}UP&@lZWPgW@ zzX@Ob2{1~56UEy=DlQ_Ti^9Go4wt^CbI=`gA@OMWd5k&w17`beO~IInP>VjT~O4dCFLsN0950 z`bg?2C(}-H)suC4e;k)`dh*OPo`FvzhgUsW6Uf<{vj_l{N7Gp$>x^a~LaB(8*MA=9 zb0y-GD-ow$i8$q|h?9?$-rNzO>7pV|-p==+ki%un+l6(Fu8gF4yHXKv4tE9KZX}81 zFyiFx{s8ifI4wl#Ujh0jk&(V^HGJgh%_FLlM4Y@m-$Oo!5hri2_oO|HIC=XJ90C_s z_L0=vm*)XL+w4KV2}76B?F00$nfWQHG79wZr~6PSWe$BuZ;lF~13%`@5>(8SYnY*w zg?&k$94dwqfjti7CBdo3J~Hh+PS^jTs*d`%vA)#kt!m8axnk|5hAG-|+tuSC*37ns zp!^25NbZDgRyDI(6X%ajd9pLerb#olZ{5_3RyDI}Rorm&R*d~LL&o+Uo4Q5Vq#(xt zu8THxlL@m6x#Fg06Z9sbu@ZA*rm+wMUe5{Vp%&+a^t*kY!v6bn!sXzoFWlYCd+vfB zxNxsPKGRD_&jNp3xJwo}ec_&!x^U0x=)yhIy9V@L{=Fu3yWmaTE_hS7?7q}3yIhB= zW4+rGgc}=?LH{0nhx~F;?3asTzg!gi<)YXx7sY#9gQ&1`g17sd21Zzpw8%nlFbs7O0SzSi;~5dvFDmh=$6-M7x`aHxsgtc z8G9m9PTIb_9^EFP6THz-+h0Z>Jrd7T(D_S~r2#qFS{ znmV9e3>15E#(*dwF=I~+&8%c?cgVWgA?p@LyKZ&Jx~)-c$g{EMcDhca6ZhSoJ9N9| zv%iAhrz~a*Ja-bIWZmVEwbLQ%`;KNwe?HX)-O?8zeH{Q61DY9)YdOiTfanY{SvkH zp9(L^@k`X!FHu{+L~Z>Nwe?HX)-O?8zeH{Q61DY9)YdOiTfanY{cnO#PrpQM{VHnf zW7O6&H5;0%c&cj3Ij5tgeu>)pC2H%JsI7ksOrGJFsI6b3wtk7)`Xy@Xm#D2@qPBjC z+WIAG>zAmlUqx+wjM{qU{T2cP61DZre;l$@M=hXOqZ6aHo`tHT7`63W@&dr#Fr&7f zMFL0EHq5B4XR#{w4TTxC^^~e&pHP@lTh9_zMAX(tVV+CRgn-Tz$lHZ3Mi!&Co@Id%+w>d%u~;2M{&)~Z1<7dFM#%Cb2IMAe-n8>H{*@S1e)>1pH7qT1#g$l zxZ4 z?Bl~G{KyPDp4pj88F7~91?Fn*w>#g2uVeV>!b}&|?Q)g)^SBZuBXb8?=Xk&;WZwA#dG^4;)Vzy) zw1zv`6t9s@B2(tv9549in-BUizLXBTl@5JL@{s7zm3QGPpBG%lRtr30WV5pQ@N1Bj zyDZOc<5#DFS-1`WBm5hx8Ga0kVCPO6(=@vQ-F-b&);7s7*RjIt9wfa46upqde`z)&>9KLHlfNMwf03ZT0!rzOJX`5?-B zO)S*K{4XGSEJ4ejP(&-+!^FYV2uIPSM)*!) z`!{F?c_cFXm>;7;o;<8H`w)ff3*e0S5*&+O%T6eVTh-Y#WfzSuGQ2qI(zSpQkj^qvHz@nWYNYnpvs zd>4?LdT^TMc@I-%Sk4h)IY)%$91*?@)iT0zjtJKy?+(j3A}r^Ku$&{pa*hbgIU+3Q zh_IX^!h=BHDZC4ka>7u`2!{9II~104L|D!dVL3;HjtI*+ zA}r^Ku$&{pa*hbgIU+3Qh_IX^!k3~}B)k<;`iJEl5tegASk4jQTG(Q6Sk4h)IY)%$ z91)gtL|D!d;m_e6`oU?|xL;6pIY%H00lNiN6oMnf2&yOq=S6RD5dc{enAZKM6s@u*6To5*yDt@BoY85{rnDG-C5-{iO8xKJ3jyCpv#fd zmv7C5pbql%e?u$-3SlYnww>T*>0;%Xfp4#m@9qk!N0zr!U`)s$D#N z`KqadezD`rnSg3Ja{BVs(9BBKc89E+9kOn5wCh%ftlKUV8}jt!yPb|43G(#iyF<5& zM=oFRACy%&ZaQiydEQ#gs30d|j9i1^Hsf*XIh* z>=%}^Us%q5VLAJS z>=%}^Us%q5VLAJSW0CG zrC14k#`f9`L+<2;Gr;{MUaf36fq#tL)rp(njO}l47{W&`uFmvmJW|8 z=8Z!J5IbBE@7XluZ)7!65%1bKg!kC4(Q&kwzkD!+uYX(xY<|Y}#0De(8t!(V^6(Gn z9x9cV8}f&<#01^Yr$hPH<|z-ee4TLvG&2u+rY}*n=%uT$SI8g7J@6wQ=BH^k8#INv zb2;h`FXATd5fAgY&>b%Oz(?e$$LBR8DCNgepHB$fUT)n+{+(Oi+dbnKD^~Is!9=kh z@IA%xcHzi7dByT}&GL!OLuDg-yN4Z{v3>XEQ6pd{^9~Q^PFz@zs%G(f9_|w%a+HMG zt>TK5fJ@!@OG;ri_r^k=Uh+F=O891@OeTdaHAjEs$Q!b zA5)rMYw=TxUaJ+|s}-%#iY`@(R%%5TD9yI%%5#-6H|XLVWAq3dP|Pc&L*KkaY4KBA znQx)@l-;gqtIQQ`mAN8KnKf;dDYKMO($`tim2T@T-Hg%I?7;Jcdf`H)1y4Ktm^+`d zl{v4iGUv5b=Dajz&Trd=7ineYC}rmA%Cm>oqi(0#t*8P+_Nv-R~HQx;$wIh@^Io-Ie-#g=A!&eMwAtM$bQhlWa#Y^}%~ zrAW5b8%hzYSgaJWiqn)>zZSdH7~RBa=9rtZZ`AK)Ua_SmA#J!kAw1^RHdVq^h16tl z%*}%ZJ#~rLN?6wP#j}@qyNOY;Zrwf#i?5SWLrE< zs^op@R#u;**ng(kL;n-W;9X&GGtV3N{7fZqwTEp6?^+C$wcZtdq0ms#&ngxx_D3x3 z3*Of(i!{p-eE%lKk_Ta=4#mI0 zLkcx@@7O#PzYwPA`O9uYm#n~IvBb??314>(y>cOotjO1(qe{6JT`FjdwlQl;aoM_5 zqzxNJRqk_VYoin_*Ww?=n$6FsF(kO$&43G^aH%O}xm2cMPCRjrBD_!~2LA!+5%}%C zP3{$dWNiP*X5&N#i}|sZ$Rf~;l0Zs@d!;zs0^`JEnuspP&l;O(n-xep&*nW7EIzaCM zH#tCWHb%QS*X2nA%Fb1a%io5T2F%3Dq#BT?MIMp{D0=MfbOZ8q1D??h$P>e}fvN#L z(i+e!tpUAt0}ks3SmpXn_=C`FL0A;*sTH*R%G~)V6dZmpnga@s1I$Y_up-BXW$4;9 z@H-0^Q=Zycm~j#qJ(qKUPs*pxQmy~9O3;1%U>enWpVoJqv|iD3BPPY<$|jo%s} z<0xAg@|nU2jyk`aw*%}>WAq;>Fhkg>zrl^jjV-X80%hE0=z+OZhHZunTW-e^G)AVI zguaX|_eWG!iHckYn1u0KgU0Bi6!S$UO>)9aMbw}YwCi`am@hK*^sq^5{-sp0H9wY_ z(!4K5jQMdU=c8ZgrGAdh@|>R3oOtHg3(|~_g+Kd&cKA4xvk%6aa*9tfbu(^_2p`os ze-{bt9JiNehSB%1DIW;5d4>|0dJ_4VmivLoL<^8glrXuL`^C^x~M(@Kc-JtLYm4 zvaN=%IW+7{mU}W~E1+3UP;H05<@d%Q9S{3}f_c@sJmihOmpg#nnaqEGnyT=KUGFB= zlO4?NOnt1|JY23Jc5BskZC2)gNE#<=5e|^%kAxwXHDmN%&d;|?`TBDd=Wg&|e$1WC zwt(AH0;E50*Z#C?^9VA$!(uo}E}6cs4Y3#V=Qs=|Ts?5f8l?qXp!h>TVINLUbdYLm zU?>jXl?oiuXR`kP>F(>p~PN&W(J#Q%BKR5&?4S~jW6hn0edZcA~ zA9v|JOt&B8^qA0d84BNQD)^wGrwJT$3A7M{Bb>fs0Ym)`fe}NXaS!$MbOwi^ltXXp zG->cKYw*<$T|wz-pn~59y^S;zMjgyV_3bg}ZimbV58C4|COg%45^*cQ1jdo4v_Z@PdcF3)BDszIYYF!P@1>@02(_3c(evGz-3*F{=)lW%Q z4NU$CA-~=s|CU3(!EmeTH3+>U7_Fh|euN1e76LJcz^YqJbwA?p$8q^1LZC19gO$jC zqp*wDx@hrw=yHU@#($1$LQgvMrW|^gBhr6p9Htsvj?i>6J$v3y^0K6YBQY*5B&9(+ zHxhOtLc>%mh5YLd`MR}Wm*BGiys!XhAigr4gn z7MV1pCo0e{r$+P@e zye5rs!19fXd|!IFk>wi|`J#=#huB*hp^~V{Wcgkp>>X(Stguv&8ppbc-Bpo)!RX`k zs7AxSOBAj8*zn&;SC@G*e15Oa;i}=kP+hpkaXG9@8@D$70U5*l;Rgl#aVaMB+)cL{ z?DqxxfPOyv+psSCNQ_^C`#_SQ)kvlPIQ$=5G?5n_S~Cu<9~)X)Nk3**NlCn><;P)d zqkSIPcO3FdZ!=2L7>*0P8TPrrn?hb(z$C>5-VFO);LWh( z0<4qYceuIhF!{ha=KV_H23p}G^xUS$=25N$?d;4@-8}G2W2uuSoVII=e=vI-evw?l8k2GBdLG3W^sIW`WD8+^2f)`p*xSA9B6PYiEK{$?kC z5&1V}a)eL>5FN#h;}}&osc}++NsM41k_7!I)B1|gf+O=0dhSJ{1^h=1DXVEYarP<3 ze^Bu6aqzz{_@8p{H~bm7)OWV{R$(Ucq^(dyR*cE9Kq@jaiw*5nwG3 z-FkH$7RcUk_43e)rPo5g;dNGR?Jiz38<9&z>l6S*z@$?r+U@9V7M+%EwIWx4toApS zbk^-&y#p*O0RTw@uDH&MC}41|E0G{{PSnk@HK7@~VCLm^$xb!$*g z6CkVHDuki5>tmtat92#UuO=bwx&E2GtEm)h%3>-{h#x^dB8*Zfd4rR$ma5{*d86b+ z)d}^ZND51oyh#Yj-J7qJs?wsShFYj>#ZoGMjlRTmu(u>#&X+WYYrS_24{~o^3<4!_ z`nZFM{r_bXOGqQtY{KZhp7p*2mf*FL>-<)-itUSNv7KITY-RN-pH+V3Dz?=dT;s&} zo13Vuy{dLuEm_@1*FexW)rDMxzS(be4O^{!>=tAYYaBH$@hOX{g|%s3BU~$F#0a6} zMU3}WzqQ_GjA^|$N5Et@h{-JEVj~sXS+{jHv3t8~qm5OKjQGC8?*f|^B8r{H+&exW zY6`*yzqtzh*JjmDaN|fv;ilHu*)n4@S2?kYZRu*)KFBhz@y5$*7lz2?CPc1qLu8!} zk@bEfUFkRB>-+|`%F#0dfG=p^Gv_)m=-{+^A)>@T6YwNX-QbU6^!W7;4JIV8CAwva zx`wP964TV6G-+zQlA>nD75+l8)f%xa^Gp9Kzw}8Np2p!Czk8$6Ar6c5UCjE=G8Z;?reRKsP4|ErAAUhNm#HLeKAXs`9EV);Vp zuQ1ZDbB(rM8*R`^MUUK6ukumiD1N=e2PapHzJN}AApV+QNXZ)_9M)5NV z@wZF&U=L)EEpE)elN^7m zMB#-^#a%ILA#KEN%i310RK8=~5kbe)dK831q5lOZ#XAwGEk=K=P*bUpZ%Su}Go_J~ zRm!Fd#gQ1EL-38=R53SM zw1;!~6aW|)I696q4wHium}Lu_%}*n5p^rYM&Xi8gm*>Ol>(%(wP;*HQHFv8zwe!p_ zHTu&gD_j$bss@}Q;jJs8>I)ihHmcq&Rc0%d@P>L2IIN)uqUw}7RjNEa{hPnwX5{1QsYu-q{8^A9x^z;NL*eyxzsCP7)$D9jpq^3ndClO(Xn6gq z+0x<4?8&Me4;@r%hNd3#--|}3)KeOCAR4*qtp6@jrmkhWF452IVO3d_i|{(=>x-oY zrTnXh)!CiD#1Z)zsXY|^0KZ{uk{+(98O6^M^=14<8K4f}Qbog`4b|fUhC;W(;SDQr zs^J{bozaU?)vP|a^ho!K9@UumWPjxNqmgB5NBFk7@Nd+qr_1xZDDNBeGd~xOz&mbd zfB6b*_gCXDlgWG`mH)@O4EYcC04TWLB=j7IGFy2>-ofrA^J*svAsV9!sU6YEu9HCs zPs z(TCK|+2;9^>b#l{ud7$z?P`V>p95~Vu@1kx%IaKsUe%peT`GNARVr$3;^A{A!m;az zn&+p&ch#N3@z_}TLv_AfPM4Z-@mV$YU{~`juDOVd(KD)By;1&pnqsvmm;LU~zr36ZDOcTv%M>QUFZZ>jSr zMAcWyFU+2teN21d#c25;_^p%`q92!gQrDvxJs0`-qeS!^1WXKHnm?!N=gHzC7wAU> z48c)K843Rz_4Oc1KnF8j&F6sW3`+Rcp&b z849rq`ZzUL#&ND|D!gUIPpDo^V5A&gTQ~SkH8fkEojVzBg(~aeA!T)`Jlpg$A_ak- zpNl;7dgRKBUEwf1g!W4gTYvJa@D^@>GU=mDW5dG>8=&n@L&a?h`^4Yh4DC2tN@t3j z(periLUWbOkHMAT;#C_czqe6yWwzRgToDQ%SfSR0Z>_7Scf*?yK2~Z65nsC(;t3<# zjNx44g8>jxqfs>uXP}UCuIW6P(jE1X)%h4k(!$oC{VJ;SOIwCl zslPyx5aN2lnP453^PVPQxlq-rPeVNrRFLWooZ5V@rC+w^u2IPUF4z_JLTNh zZ5m-Nr5|qAYNJEVE=Gjl+?p)~ocui;y@+4X@xoPCGpuX2`MmBky&fIA{~L&pvxKRQ zk?_OlIZ;p#MX7xc6KVSP#Z@siujZzx|FX8O87DR0OmTc&T{DLq@_ICwpY8fqv$}9n z?UESxdbFgvx2Unr<;Zj_vaF#ias?4;QqRMp5n3v{$_N{C>eRyzQK;E9`wC@08M#UA zh>RUjx4fX{$_OweRX06Xc>{gHv2AO+wt@oZS51gJ-!=Kp%$|&_cs(*1Qx~QYV^!Du zxeZ;BXF{>L5*RAYHS7P=r_K#Ee^`Pv=1C&WQ{fmlHK#ruUc2(laontwPa*!zDc#f+ zqjN}|y)>)J&m+WBP^QYhJfse)A*J*ocQ8j^``43O1y{26XZ>m=pX4QpvAn9@RP$0Kb8P^C9XcUJc*T=#`E7V#< z8g;3pP6DA0B0BgBf#^m9RqcWr)tRnlAMS_hLdk)I8wqjVV;=|yHMLh;d|w` zSVyr|YhR>s1&uhcCyc|&+K`aC4SB^9Hv!!tj- zbnXn2QSC&G!(B@f#*!V;lO)O6b2Al~k@l?Dqt91VJv2%1eSpWNTqV_`B_!D za6x?t!%fW8}o7K=6=`;{4iuW5QMSW_hIa0)9(=`pVsBIHEM}$b;HKot^ z^_Ib8o|Z?xv(eJ$zXy@}vTnsa7&ZAnW1Rh_wWV!)l;Hv z5|U3-CFD9dR1=Q8mXE38CCKFzRPusqhFM+0UnNp|76A2z`U#!Rk;gYzE>$Y!%1?;+ zQ1l|Vo_Ly0V8VH4(ZOlS=K2_qWD-7ckS) z*}H#W0d6;7M8w8;gs!Gea*gfc%w)>WCMQw_Z$`t;{(-nPk}M{z1PBb0hz5c0#f zog6Nvb6I;dpGsMyX-rw763m0Q9F{xE>GqYbm88Li@m#*xkj@q@eC{Uw&_dn^;ysXABY_Y_-2o_ZVW?ZaVC#;wQnzw}gy7#>gNi987hqfpsmDvR+d58K_HgYo{BZqi>T zRDoArlEdlZjHjLsttu66OZW`X5bNFF6YuYA^_F8e?T-)c@2^^icsC3UeoYHGwL%sv zxizsLhJQ3Ue5^F-ErV1rFxb*h6?QGkp3LWlH3*b5$(dBXpw(d0Cxkq*Fg*74Ct9uH z60ob>Y)|yZJNNl&fO7XHx-HvIWv9~lT(+7%eL~l^x3|BgwJSd8+IBU|+pF#0!wA(< z+oA_(SZ_;sd1h3Ep+Re3@BW6?&71Wn5gRs56ieCE&aqTBl}`^NnamH5@5I>Vrfpjr zHjOoG8iidBAKL`U6;s3T%1w9&2!_wBw!@VAuxFx<9o*v|A-`cG_mV-#9lT0)p$u<{6p1M&!iw^D zZNpV=^d#E$Q@G}!oX(Es>}(FB(E7=$8l}D6iPkP_GM}2VDTZcXhka1_;E>(EzqfU; zGtq0?)$+B1Ar0sDv$MB7p;>G1PP7c(xrJS39QCP+)Y&_TJ)xNYK=y$I8e*nVtyK6H zTHPM&7`5U~9jjp?ra9ti?{3*=4<`E1%HJRFMyyLtrR>6R4($;6sSW8QC#rIDf1F$N#j=80|T9RnR!Yvf>rkkH5-BQQfplkc6OCul< zmIZ{xDzuNLvvzLMCFB-H>S~Ll)?lnBRVXCKMD6w{+Br0h$wHBhLE36fZ?u%vjUdaQ zP>rze6Yzbp%~G~hNR3p(WoOVg0@Z-V@0L2Gjzc0aZ#2CK6vY%9IpTIMuZ@*;!md=U zh=Eo(64$bihk!LsPkdmYWnWx(=EOT(L2#F&Ff*a)7gNWJIz+Gx^11^soy;6V)166; z7SRXLqC&4A52JU>+ z7QKlkTcXx8g22&SE+ZyPp+_sx^{Ujg5dsB;c1W-d z0z^}Sr-P|f2I3sfRINxN(N`6&`&QZpt;`f+v&Y!FIapUwZyXOX{Z=}Q*^#219)Yc< zN7FEDu90qKom{LQC(h&Hg8JaO96CncoZ=-}xQ)n#A&%h4P}12n^`%Xea-*5Q-4cto zXq>rPong?`z-^uPccS;teEU3oe=mY)ymc_%hWL($xoVkP+N49xTx*Pg3GPjx1GboL z;v>R2c$e*Hni?NOt~?q8tC&xsLzDMvriP=}@L(aFqL)>z<}h@rmR}1k0|V%Ta$je$N}~byR)Omz72pm9 zTk!LcMaXlX0b7UD*FYD7mfxuo#vQ({1cI;-n9uE44>i{JqT={&y}bHAt4;g?6x z{_erfzV0Ef7W=qQ0Itnrgq0@`$n=l{{ec}sE~Q$bwAJ048C3MG(Lef??a_w#}7YOpcL<*`vAq zL=q2d`P7(QpgvC$-D7KP($0;J7VuS;+*B$LrAz}prti^(!-`X!v{0jNM&l2f5#r5E zi|p;N+dI4CHaenl`+=z0%F?BVi(Et#KG}XHYLR$Z82%Yf+z6awib-JCH4>Y z?H_dDTD^N)+U#~$ySkNQxzVGB4Ta@-7>H_$s7xVSKPN(2v=0Ya04$%!G9Yk_*0D(x zfHgUu#K@D*!dgWbPaS7Tp*D7596ooH;$Y^st0nE=(KaWBZ@=eh#(H;$6fM1_)I6~E( z1EQl%A7Lbw&w>cIG!v3=M zMF5}2i2G3#!Yy52aDk8nHHwH$wH&E-_L8LnpLv8N_*nr;GJ)1eihYmbhU0`>L)I0t z8#I$9!x;(~j?;n843MG`rVhGWn8UUi z-V+57+}Ma#wyHzVaM_v zOhV(t^mr<(h3iITG8?X{gT;@vl+u|vY)t2qlaq)}1ZDkCmWoy(SIQ5k7>jN&&yE90 zTY-^sj9WtAMi@FM_9lAcj@~(tImJ`!;g+Lai_^?AZQ1QG;XDSxdAQLNtD2@!Jcl3~ zE(Hq*qMZ-&Gfc6DTKX_u(dLKYY1X_8hUE*39at=8-HoM=!6SlY4}(?V5vy1gUSmC5 z6~4rJttvcfycKGxc2-ct13j96D&N2#RLrs742`fIU?We!X?%bTv2@?p8S>7ItB&azv~fsz6DP z*mtAK5y9tas~r*RrUhVH5nN;a?d9N&WdMYsucrd~x?13W9DuJ8cmq~#dynl%gs{!B zkpq=SM?3yPRpd|aK~lL#0(52=f1w_q1-u&nzs2~gtOn7`5h?gJp>I|ro44D)!}!-* zNzs?#^N$vx|3jf~zMuP3r0Mu8{DmyogU~VWV*U(ps+U=`IY`JjmZBhnkK(;$I%IXH zwOZi8@4&?b9{kqaT>=k&!LCK%!7u9d03IU$%II=Q-*k)$e(-C4#|0kzV&D@34}Ryi~d%-p(_fh4J=(3w|(eUxO8#c$;WJ{J#b85MC4y#^<{MUjaG%NZJu5 z3kn?q55D40pDiH#;8&Js7*1vTkIxBy@SAXF1RmUbXl@ZY-&q8H0q_QYk(_!Q?RNvk znLxWk--RIlgPVgW^cx6nmbEGo#A<=B!>inM+^kR57J&zMblffQ;P#*ifd_Z%v<1HY zK27)?q5q)3gYR`87kF@c;3otg-12rnQN0!(UjqKMkWYuPb3ysC&}5GG?6=U)rPcVM?`e|!gIf;F3w*&QcIeL$esE8K6#@@_LlAFDRW0{!!4Gcf z6ki1Yh~NkJL8T9E5I@09Y(63I;2u^_2t2rT+p_`>ZpHaEhA-4(S@2KXt?wE8{7(W8 z?$-RLi_rNS!Cx8IbWFQ{1^kHW72G>vm4UC-_+g3Xn*@I0h=xC*PZoX1s+!&p3Vv|Q zy_CRzx=s_gUFehqKDODT0)Gqe z2D$d0ejxb49k?zDJh%hPhk!4~zB4KimqJ(+UQIsrfGcl<1paYqF;UD)dne1pPm%G91&qlx5@fDq4V`Njc4p? zDaX&Nt;{wJ|B_H!E$}nqcTWp^-6HY!9>9s8V7!eBo#2Mt{fp2U6@1>=6J?qm@{b8T zxVIgB{*2^5BkfnOJimTs5&F*wesG`Ue=YFf-hQtwLg%}JAKXXsg200tLjJkHgF9n> zAn@Ss>6Zl_+#{{_3SHmeCX5>yE=uy8b_jlOPsL7wzq&^w-!FOY6L@Ej2J94g8gTOK zpngjPM_fAbbOU3Ki`~u&o#3WyUle$&=u6DbIt#d_AO*^azRE!Q4em$tF2nt}l$sa( z;6~c?c|D>N*{<_^QuwSBcyQNRY#_q;&+pUt{rY4z2|Tz-@eY9p_rd8AcyO!NLxA5R zG`*+)!T!5&yk%PG1h-RqMBu@#(4G-^aM!x82>iUXJElFghV#Wj5pxol6B#w%S`_TVi+l=h0v%rk9kII}XC};`FYzou$ z=K+cls5AGZXOi@Evsr<_v^mCl7cV;nh_KlO*l{qimCi0G%B&b*YMvKSKqoNOhE@h| zVz^UtTBi<+Bph8e6L(w)8wM7fOz1_MSXySg+R?N5l-3jld8Cj7x$FoQ2|%(wm%>gR zc4q>dj9@tiFD7LRacA!Iw4a%vVgms-FS$Z7+9*7W2%Msw$~ZH33~^=~JrTv&x~?+2 z2(+)~HQjJv>zP3Y_-CJGQl7<5!AD!M)^LH|Vj+PTJ78TpsI-I%{y43p5nC5#^O;k> zda*{RP;eEdS*7X1NuGA|BRfk-=xq&N7s3TP;f1s?fi*w=Tws!YC$PYQCzEOVZ~->g zlh^4Wt4>f&1q`=ZFnNxY~Eh!vw0H9}TwWF`xG`iylidSVdGvtl}S*Ud+G!rg&=#4CM z4Jmn_JN~iPG&4z$Qf6G;%!`|IaE4MIyPV=^cnw1nPJT0gZq5x3roJ%aLk7GV7vB6u zVZ@x}F~^`|F2jGcA^ATqU(Ol+&3wE$8~!nT$Oj(FeMc`+*fq=H|1}|G&NRP72OjHK zPA0#Z*P-Wb3?)1H&HTPO-w+U3AZh(md^L=R0tp4#U5xr#Ts+ne(F$n4J1-$~Dlh1oE486y_}AH|PuO|F^M)c~WBp@lD#C|0~dF$k60B z>p?nx$^3tgykvVOzgaiZC;8u}%Hc5fY05Qnd=~cz#pE~hmwUcIxh(7V_}Be0`OW#S zaLb$DnICvc=jjLtyul^KSMTGOXqocOI*kr8Y#a>@d~$vQN$Y30L*+L+82ZBGH{;Ld zHsR7?&Oi5D^9}haNcqH{_b~Yk>?#0)FuFJVMU8Qy&tU3v5SP|j1Yv~voQi{a-B5?~(uj literal 72264 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 z z>)H9Qou*zYJ@fW^?V1}muUoVpU*%h}#3-Yb$(O(vRIgpPU{%$|s#W~D-0G^;Rcop_ zoK;JztCohV*Ir+>COmA&hG8Sa%hy&*4thEb-qv2Y`o?utimqtLhVzH4!vEs%kPSIT zQA%Ef2xoU^blsS|zG}^)@MM1P@S^Iq>(37_SsPombj`5puza^LgbJ^z+7w5kBGB?=cO<&H?#KIEc<;sgDZCHj zJq3RbcNyMu@m_}adc14#9)1k`@je%S>UyU4$qf9p`15I-_EqFB#+NLMUc>yZ1F@X)%7Y;3$EZ8(qTyb2%pSHIuhx> zKikzc3+bNcy1J^7zKZl7q<=>GDAG%x@9KIT>1L!Sky?n&7Gja1IUkJCQaVM15TO?ZXN&4(a_bqdwA)aCf*F>9kk7x^^P{ z7VcqNkUod>5YkFqW_z+AHyrU`q;B}psYowEx)kZ3k#0vi1kU+eNa>`XM|v~T!$==Q z>Os8r4WxsScK;dbBVCSkDbjyOx*h3=7-<5jm2DV*L0W|LTU>XL zp7-;vE`EFA0i-*S{tW4Eq#wNryFj|J9bdqQv=ylZ%Y6rF5z_m(<3u_L9-rTKcm>iO zNH-(hjdTKR?I63h_7(g##o(_|AL&e_yO9pT0skP6d~<}`4C0A z3V-9i0_mXRsE>3R(%neQ{)qZWe~L7LH0K2D7T@Cd2+}g77o6uLBjjteP~Zt*9a2m*Xijo|9~fb)5BrcgdGx)-+oEPg}gZGy2b#=)V;pE)lt*(o@`&^GfBE|bA;0wOr)y2PUG;I7_K}*{^ z0nP`0Y%2Z+g{OQ^;av>;WzdIHe@3JG4o{ueJu%?4a~SB$4|H|8*bloM*WVnx2k_B9 zI1|Ml2rUkRX+^acHnOUz9|(SsdKw- zO}4?WI81e^0g6G#py!BgU?@-R{t8QA4VMuYsro9@fE4{IR;t>q!|gC2SGD0l}EJB z?Y>Odh@2Imc?WC6>1>)j)&c(u;O|1->8Gnv?8LgF9ZMGRJ`j!M+WXuW?}Ea zD=DW9*BuU?dzTl0kEh|cJ9zGCO0ZYKZGN(TMY4VFbp`=HG<7bx2RQSQd!S~1N^Ym`ioQg5Bx#kpHIcRe&q0<4g4zX?PRUB=jgIz{msB{0{+Uh`f7ls|G<9- z_-E7bPdohg0Y4pk#L6_h8X(qh2Ywsy*QVj;IQ2V$KLGr^H2l>Lz7T8lN1sxE9PppR z-t$cLX9K?;`14ZvuS@Aa@V5bfVJe;z5M}8)6~KH$HHGsAeSWsZMyk8x7~v47xOe}(yxR=>ij-wFK3I4k@xP5%FIVDs@C$L)_(mFjr{KeYZ3h0=z>iOr4{LzCR75mLvlBFb z#`)wSWk)% ztIL|1TfRPb+~nM{$+<;Sa>Jkl{ghlwe$U8{bKN2EVN;*IKGVQw8u&~DpK0JT4Sc47 z&ouCv20qikXBzlS1D|Q&GY$MB8nA!Q*8crk9&`D!Qyg98wGMZJeBlTxuh%7Tf_rIQ zwtpwLuP!5eC9gg@wSO;{u@Am%{GUGQTFdhN`cR9oue=T-2F@44pz@OMUO^6F5qZgZ z965v!-D&HEG)IK|J(;c+$7ZGlh7yi~yUdMIEZTLbhh%I-ME+b4O zFT4G-b=juF^{4a?%?aoeT@LE{T}iyu#C5FDAJ&S%HL8>+Yrg;N#Qq)lOupaN2#w`Z`J92I?a^lzbjkY!lxy%e`neL zU1j@slZ->tQkZvS4bUDngfuz#=ChI9TA zXa8QUJx@4K33pwC54`MoOEViKT5d$d$l$k{-6us z|M6?v&EZe2YyaM@{rk4|@7Z2*(M9Kniz*k#)>OyBpDP(tQZ{OIOiH6`M~^Eh8&fj6 zSi$kQpHi9X0KOv-k9F|1J5QyVpNc*oBft*h(UF0QVu9_;RnhrW^gn?oSM<*ifY
  • lXi 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 X=Rh=HMI`cR=|3Zm^J88D@7DY;++?NV diff --git a/onyx.c b/onyx.c index 1c494b9b..9c3b424a 100644 --- a/onyx.c +++ b/onyx.c @@ -1,59 +1,61 @@ -#define BH_NO_STRING -// #define BH_DEBUG -#define BH_DEFINE -#include "bh.h" - -#include "onyxlex.h" -#include "onyxmsgs.h" -#include "onyxparser.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; - - // 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); - // printf("%s (%s:%ld:%ld)\n", onyx_get_token_type_name(it->type), it->pos.filename, it->pos.line, it->pos.column); - // onyx_token_null_toggle(*it); - // } - - bh_arena msg_arena; - bh_arena_init(&msg_arena, alloc, 4096); - bh_allocator msg_alloc = bh_arena_allocator(&msg_arena); - - OnyxMessages msgs; - onyx_message_create(msg_alloc, &msgs); - - bh_arena ast_arena; - bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB - bh_allocator ast_alloc = bh_arena_allocator(&ast_arena); - - OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs); - OnyxAstNode* program = onyx_parse(&parser); - - onyx_message_print(&msgs); - - bh_file_contents_delete(&fc); - onyx_tokenizer_free(&tokenizer); - onyx_parser_free(&parser); - bh_arena_free(&msg_arena); - bh_arena_free(&ast_arena); - - - return 0; -} +#define BH_NO_STRING +// #define BH_DEBUG +#define BH_DEFINE +#include "bh.h" + +#include "onyxlex.h" +#include "onyxmsgs.h" +#include "onyxparser.h" +#include "onyxutils.h" + +int main(int argc, char *argv[]) { + bh_file source_file; + bh_file_error err = bh_file_open(&source_file, argv[1]); + if (err != BH_FILE_ERROR_NONE) { + bh_printf_err("Failed to open file %s\n", argv[1]); + return EXIT_FAILURE; + } + + bh_allocator alloc = bh_heap_allocator(); + + bh_file_contents fc = bh_file_read_contents(alloc, &source_file); + bh_file_close(&source_file); + + OnyxTokenizer tokenizer = onyx_tokenizer_create(alloc, &fc); + onyx_lex_tokens(&tokenizer); + bh_arr(OnyxToken) token_arr = tokenizer.tokens; + + // bh_printf("There are %d tokens (Allocated space for %d tokens)\n", bh_arr_length(token_arr), bh_arr_capacity(token_arr)); + + // for (OnyxToken* it = token_arr; !bh_arr_end(token_arr, it); it++) { + // onyx_token_null_toggle(*it); + // bh_printf("%s (%s:%l:%l)\n", onyx_get_token_type_name(it->type), it->pos.filename, it->pos.line, it->pos.column); + // onyx_token_null_toggle(*it); + // } + + bh_arena msg_arena; + bh_arena_init(&msg_arena, alloc, 4096); + bh_allocator msg_alloc = bh_arena_allocator(&msg_arena); + + OnyxMessages msgs; + onyx_message_create(msg_alloc, &msgs); + + bh_arena ast_arena; + bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB + bh_allocator ast_alloc = bh_arena_allocator(&ast_arena); + + OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs); + OnyxAstNode* program = onyx_parse(&parser); + + onyx_message_print(&msgs); + onyx_ast_print(program); + + bh_file_contents_delete(&fc); + onyx_tokenizer_free(&tokenizer); + onyx_parser_free(&parser); + bh_arena_free(&msg_arena); + bh_arena_free(&ast_arena); + + + return 0; +} diff --git a/onyxlex.c b/onyxlex.c index ca877d6c..69b3d314 100644 --- a/onyxlex.c +++ b/onyxlex.c @@ -1,275 +1,275 @@ -#include "bh.h" -#include "onyxlex.h" - -static const char* onyx_token_type_names[] = { - "TOKEN_TYPE_UNKNOWN", - "TOKEN_TYPE_END_STREAM", - - "TOKEN_TYPE_COMMENT", - - "struct", //"TOKEN_TYPE_KEYWORD_STRUCT", - "use", //"TOKEN_TYPE_KEYWORD_USE", - "export", //"TOKEN_TYPE_KEYWORD_EXPORT", - "if", //"TOKEN_TYPE_KEYWORD_IF", - "else", //"TOKEN_TYPE_KEYWORD_ELSE", - "for", //"TOKEN_TYPE_KEYWORD_FOR", - "do", //"TOKEN_TYPE_KEYWORD_DO", - "return", //"TOKEN_TYPE_KEYWORD_RETURN", - "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN", - "proc", //"TOKEN_TYPE_KEYWORD_PROC", - "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", - - "->", //"TOKEN_TYPE_RIGHT_ARROW", - "<-", //"TOKEN_TYPE_LEFT_ARROW", - "(", //"TOKEN_TYPE_OPEN_PAREN", - ")", //"TOKEN_TYPE_CLOSE_PAREN", - "{", //"TOKEN_TYPE_OPEN_BRACE", - "}", //"TOKEN_TYPE_CLOSE_BRACE", - "[", //"TOKEN_TYPE_OPEN_BRACKET", - "]", //"TOKEN_TYPE_CLOSE_BRACKET", - "<", //"TOKEN_TYPE_OPEN_ANGLE", - ">", //"TOKEN_TYPE_CLOSE_ANGLE", - - "+", // "TOKEN_TYPE_SYM_PLUS", - "-", // "TOKEN_TYPE_SYM_MINUS", - "*", // "TOKEN_TYPE_SYM_STAR", - "%", // "TOKEN_TYPE_SYM_PERCENT", - ".", // "TOKEN_TYPE_SYM_DOT", - "/", // "TOKEN_TYPE_SYM_FSLASH", - "\\", // "TOKEN_TYPE_SYM_BSLASH", - ":", // "TOKEN_TYPE_SYM_COLON", - ";", // "TOKEN_TYPE_SYM_SEMICOLON", - ",", // "TOKEN_TYPE_SYM_COMMA", - "=", // "TOKEN_TYPE_SYM_EQUALS", - "`", // "TOKEN_TYPE_SYM_GRAVE", - "~", // "TOKEN_TYPE_SYM_TILDE", - "!", // "TOKEN_TYPE_SYM_BANG", - "^", // "TOKEN_TYPE_SYM_CARET", - "&", // "TOKEN_TYPE_SYM_AMPERSAND", - - "TOKEN_TYPE_SYMBOL", - "TOKEN_TYPE_LITERAL_STRING", - "TOKEN_TYPE_LITERAL_NUMERIC", - - "TOKEN_TYPE_COUNT" -}; - -#ifndef LITERAL_TOKEN -#define LITERAL_TOKEN(token, token_type) \ - if (token_lit(tokenizer, &tk, token, token_type)) goto token_parsed; -#endif - -#ifndef INCREMENT_CURR_TOKEN -#define INCREMENT_CURR_TOKEN(tkn) { \ - if (*(tkn)->curr == '\n') { \ - (tkn)->line_number++; \ - (tkn)->line_start = (tkn)->curr + 1; \ - } \ - (tkn)->curr++; \ -} -#endif - -static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTokenType type) { - i64 len = chars_match(tokenizer->curr, lit); - if (len > 0) { - tk->type = type; - tk->token = tokenizer->curr; - tk->length = len; - tk->pos.line = tokenizer->line_number; - tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; - - tokenizer->curr += len; - - return 1; - } - return 0; -} - -const char* onyx_get_token_type_name(OnyxTokenType tkn_type) { - return onyx_token_type_names[tkn_type]; -} - -void onyx_token_null_toggle(OnyxToken tkn) { - static char backup = 0; - char tmp = tkn.token[tkn.length]; - tkn.token[tkn.length] = backup; - backup = tmp; -} - -OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { - OnyxToken tk; - - // Skip whitespace - while (char_is_whitespace(*tokenizer->curr) && tokenizer->curr != tokenizer->end) - INCREMENT_CURR_TOKEN(tokenizer) - - tk.type = TOKEN_TYPE_UNKNOWN; - tk.token = tokenizer->curr; - tk.length = 1; - tk.pos.filename = tokenizer->filename; - tk.pos.line = tokenizer->line_number; - tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; - - if (tokenizer->curr == tokenizer->end) { - tk.type = TOKEN_TYPE_END_STREAM; - goto token_parsed; - } - - // Comments - if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { - tokenizer->curr += 2; - tk.type = TOKEN_TYPE_COMMENT; - tk.token = tokenizer->curr; - u16 layers = 1; - - while (layers >= 1) { - INCREMENT_CURR_TOKEN(tokenizer); - - if (tokenizer->curr == tokenizer->end) { - tk.type = TOKEN_TYPE_END_STREAM; - break; - } - - if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { - layers++; - INCREMENT_CURR_TOKEN(tokenizer); - } - - if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') { - layers--; - INCREMENT_CURR_TOKEN(tokenizer); - } - } - - INCREMENT_CURR_TOKEN(tokenizer); - - tk.length = tokenizer->curr - tk.token - 2; - goto token_parsed; - } - - LITERAL_TOKEN("struct", TOKEN_TYPE_KEYWORD_STRUCT); - LITERAL_TOKEN("export", TOKEN_TYPE_KEYWORD_EXPORT); - LITERAL_TOKEN("use", TOKEN_TYPE_KEYWORD_USE); - LITERAL_TOKEN("if", TOKEN_TYPE_KEYWORD_IF); - LITERAL_TOKEN("else", TOKEN_TYPE_KEYWORD_ELSE); - LITERAL_TOKEN("foreign", TOKEN_TYPE_KEYWORD_FOREIGN); - LITERAL_TOKEN("for", TOKEN_TYPE_KEYWORD_FOR); - LITERAL_TOKEN("return", TOKEN_TYPE_KEYWORD_RETURN); - LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO); - LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC); - LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL); - LITERAL_TOKEN("->", TOKEN_TYPE_RIGHT_ARROW); - LITERAL_TOKEN("<-", TOKEN_TYPE_RIGHT_ARROW); - LITERAL_TOKEN("(", TOKEN_TYPE_OPEN_PAREN); - LITERAL_TOKEN(")", TOKEN_TYPE_CLOSE_PAREN); - LITERAL_TOKEN("{", TOKEN_TYPE_OPEN_BRACE); - LITERAL_TOKEN("}", TOKEN_TYPE_CLOSE_BRACE); - LITERAL_TOKEN("[", TOKEN_TYPE_OPEN_BRACKET); - LITERAL_TOKEN("]", TOKEN_TYPE_CLOSE_BRACKET); - LITERAL_TOKEN("<", TOKEN_TYPE_OPEN_ANGLE); - LITERAL_TOKEN(">", TOKEN_TYPE_CLOSE_ANGLE); - LITERAL_TOKEN("+", TOKEN_TYPE_SYM_PLUS); - LITERAL_TOKEN("-", TOKEN_TYPE_SYM_MINUS); - LITERAL_TOKEN("*", TOKEN_TYPE_SYM_STAR); - LITERAL_TOKEN(".", TOKEN_TYPE_SYM_DOT); - LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT); - LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH); - LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH); - LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON); - LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON); - LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA); - LITERAL_TOKEN("=", TOKEN_TYPE_SYM_EQUALS); - LITERAL_TOKEN("`", TOKEN_TYPE_SYM_GRAVE); - LITERAL_TOKEN("~", TOKEN_TYPE_SYM_TILDE); - LITERAL_TOKEN("!", TOKEN_TYPE_SYM_BANG); - LITERAL_TOKEN("^", TOKEN_TYPE_SYM_CARET); - LITERAL_TOKEN("&", TOKEN_TYPE_SYM_AMPERSAND); - - // Symbols - if (char_is_alpha(*tk.token)) { - u64 len = 0; - while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) { - len++; - INCREMENT_CURR_TOKEN(tokenizer); - } - - tk.length = len; - tk.type = TOKEN_TYPE_SYMBOL; - goto token_parsed; - } - - // String literal - if (*tk.token == '"') { - u64 len = 0; - u64 slash_count = 0; - - INCREMENT_CURR_TOKEN(tokenizer); - - while (!(*tokenizer->curr == '"' && slash_count == 0)) { - len++; - - if (*tokenizer->curr == '\\') { - slash_count += 1; - slash_count %= 2; - } else { - slash_count = 0; - } - - INCREMENT_CURR_TOKEN(tokenizer); - } - - INCREMENT_CURR_TOKEN(tokenizer); - - tk.token++; - tk.type = TOKEN_TYPE_LITERAL_STRING; - tk.length = len; - goto token_parsed; - } - - // Number literal - if (char_is_num(*tokenizer->curr)) { - u64 len = 0; - while (char_is_num(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '.') { - len++; - INCREMENT_CURR_TOKEN(tokenizer); - } - - tk.type = TOKEN_TYPE_LITERAL_NUMERIC; - tk.length = len; - } - - INCREMENT_CURR_TOKEN(tokenizer); - -token_parsed: - bh_arr_push(tokenizer->tokens, tk); - - return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1]; -} - -OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) { - OnyxTokenizer tknizer = { - .start = fc->data, - .curr = fc->data, - .end = fc->data + fc->length, - - .filename = fc->filename, - - .line_number = 1, - .line_start = fc->data, - .tokens = NULL, - }; - - bh_arr_new(allocator, tknizer.tokens, 512); - return tknizer; -} - -void onyx_tokenizer_free(OnyxTokenizer* tokenizer) { - bh_arr_free(tokenizer->tokens); -} - -void onyx_lex_tokens(OnyxTokenizer* tokenizer) { - OnyxToken* tk; - do { - tk = onyx_get_token(tokenizer); - } while (tk->type != TOKEN_TYPE_END_STREAM); -} +#include "bh.h" +#include "onyxlex.h" + +static const char* onyx_token_type_names[] = { + "TOKEN_TYPE_UNKNOWN", + "TOKEN_TYPE_END_STREAM", + + "TOKEN_TYPE_COMMENT", + + "struct", //"TOKEN_TYPE_KEYWORD_STRUCT", + "use", //"TOKEN_TYPE_KEYWORD_USE", + "export", //"TOKEN_TYPE_KEYWORD_EXPORT", + "if", //"TOKEN_TYPE_KEYWORD_IF", + "else", //"TOKEN_TYPE_KEYWORD_ELSE", + "for", //"TOKEN_TYPE_KEYWORD_FOR", + "do", //"TOKEN_TYPE_KEYWORD_DO", + "return", //"TOKEN_TYPE_KEYWORD_RETURN", + "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN", + "proc", //"TOKEN_TYPE_KEYWORD_PROC", + "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", + + "->", //"TOKEN_TYPE_RIGHT_ARROW", + "<-", //"TOKEN_TYPE_LEFT_ARROW", + "(", //"TOKEN_TYPE_OPEN_PAREN", + ")", //"TOKEN_TYPE_CLOSE_PAREN", + "{", //"TOKEN_TYPE_OPEN_BRACE", + "}", //"TOKEN_TYPE_CLOSE_BRACE", + "[", //"TOKEN_TYPE_OPEN_BRACKET", + "]", //"TOKEN_TYPE_CLOSE_BRACKET", + "<", //"TOKEN_TYPE_OPEN_ANGLE", + ">", //"TOKEN_TYPE_CLOSE_ANGLE", + + "+", // "TOKEN_TYPE_SYM_PLUS", + "-", // "TOKEN_TYPE_SYM_MINUS", + "*", // "TOKEN_TYPE_SYM_STAR", + "%", // "TOKEN_TYPE_SYM_PERCENT", + ".", // "TOKEN_TYPE_SYM_DOT", + "/", // "TOKEN_TYPE_SYM_FSLASH", + "\\", // "TOKEN_TYPE_SYM_BSLASH", + ":", // "TOKEN_TYPE_SYM_COLON", + ";", // "TOKEN_TYPE_SYM_SEMICOLON", + ",", // "TOKEN_TYPE_SYM_COMMA", + "=", // "TOKEN_TYPE_SYM_EQUALS", + "`", // "TOKEN_TYPE_SYM_GRAVE", + "~", // "TOKEN_TYPE_SYM_TILDE", + "!", // "TOKEN_TYPE_SYM_BANG", + "^", // "TOKEN_TYPE_SYM_CARET", + "&", // "TOKEN_TYPE_SYM_AMPERSAND", + + "TOKEN_TYPE_SYMBOL", + "TOKEN_TYPE_LITERAL_STRING", + "TOKEN_TYPE_LITERAL_NUMERIC", + + "TOKEN_TYPE_COUNT" +}; + +#ifndef LITERAL_TOKEN +#define LITERAL_TOKEN(token, token_type) \ + if (token_lit(tokenizer, &tk, token, token_type)) goto token_parsed; +#endif + +#ifndef INCREMENT_CURR_TOKEN +#define INCREMENT_CURR_TOKEN(tkn) { \ + if (*(tkn)->curr == '\n') { \ + (tkn)->line_number++; \ + (tkn)->line_start = (tkn)->curr + 1; \ + } \ + (tkn)->curr++; \ +} +#endif + +static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTokenType type) { + i64 len = chars_match(tokenizer->curr, lit); + if (len > 0) { + tk->type = type; + tk->token = tokenizer->curr; + tk->length = len; + tk->pos.line = tokenizer->line_number; + tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; + + tokenizer->curr += len; + + return 1; + } + return 0; +} + +const char* onyx_get_token_type_name(OnyxTokenType tkn_type) { + return onyx_token_type_names[tkn_type]; +} + +void onyx_token_null_toggle(OnyxToken tkn) { + static char backup = 0; + char tmp = tkn.token[tkn.length]; + tkn.token[tkn.length] = backup; + backup = tmp; +} + +OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { + OnyxToken tk; + + // Skip whitespace + while (char_is_whitespace(*tokenizer->curr) && tokenizer->curr != tokenizer->end) + INCREMENT_CURR_TOKEN(tokenizer) + + tk.type = TOKEN_TYPE_UNKNOWN; + tk.token = tokenizer->curr; + tk.length = 1; + tk.pos.filename = tokenizer->filename; + tk.pos.line = tokenizer->line_number; + tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1; + + if (tokenizer->curr == tokenizer->end) { + tk.type = TOKEN_TYPE_END_STREAM; + goto token_parsed; + } + + // Comments + if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { + tokenizer->curr += 2; + tk.type = TOKEN_TYPE_COMMENT; + tk.token = tokenizer->curr; + u16 layers = 1; + + while (layers >= 1) { + INCREMENT_CURR_TOKEN(tokenizer); + + if (tokenizer->curr == tokenizer->end) { + tk.type = TOKEN_TYPE_END_STREAM; + break; + } + + if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') { + layers++; + INCREMENT_CURR_TOKEN(tokenizer); + } + + if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') { + layers--; + INCREMENT_CURR_TOKEN(tokenizer); + } + } + + INCREMENT_CURR_TOKEN(tokenizer); + + tk.length = tokenizer->curr - tk.token - 2; + goto token_parsed; + } + + LITERAL_TOKEN("struct", TOKEN_TYPE_KEYWORD_STRUCT); + LITERAL_TOKEN("export", TOKEN_TYPE_KEYWORD_EXPORT); + LITERAL_TOKEN("use", TOKEN_TYPE_KEYWORD_USE); + LITERAL_TOKEN("if", TOKEN_TYPE_KEYWORD_IF); + LITERAL_TOKEN("else", TOKEN_TYPE_KEYWORD_ELSE); + LITERAL_TOKEN("foreign", TOKEN_TYPE_KEYWORD_FOREIGN); + LITERAL_TOKEN("for", TOKEN_TYPE_KEYWORD_FOR); + LITERAL_TOKEN("return", TOKEN_TYPE_KEYWORD_RETURN); + LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO); + LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC); + LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL); + LITERAL_TOKEN("->", TOKEN_TYPE_RIGHT_ARROW); + LITERAL_TOKEN("<-", TOKEN_TYPE_RIGHT_ARROW); + LITERAL_TOKEN("(", TOKEN_TYPE_OPEN_PAREN); + LITERAL_TOKEN(")", TOKEN_TYPE_CLOSE_PAREN); + LITERAL_TOKEN("{", TOKEN_TYPE_OPEN_BRACE); + LITERAL_TOKEN("}", TOKEN_TYPE_CLOSE_BRACE); + LITERAL_TOKEN("[", TOKEN_TYPE_OPEN_BRACKET); + LITERAL_TOKEN("]", TOKEN_TYPE_CLOSE_BRACKET); + LITERAL_TOKEN("<", TOKEN_TYPE_OPEN_ANGLE); + LITERAL_TOKEN(">", TOKEN_TYPE_CLOSE_ANGLE); + LITERAL_TOKEN("+", TOKEN_TYPE_SYM_PLUS); + LITERAL_TOKEN("-", TOKEN_TYPE_SYM_MINUS); + LITERAL_TOKEN("*", TOKEN_TYPE_SYM_STAR); + LITERAL_TOKEN(".", TOKEN_TYPE_SYM_DOT); + LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT); + LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH); + LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH); + LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON); + LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON); + LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA); + LITERAL_TOKEN("=", TOKEN_TYPE_SYM_EQUALS); + LITERAL_TOKEN("`", TOKEN_TYPE_SYM_GRAVE); + LITERAL_TOKEN("~", TOKEN_TYPE_SYM_TILDE); + LITERAL_TOKEN("!", TOKEN_TYPE_SYM_BANG); + LITERAL_TOKEN("^", TOKEN_TYPE_SYM_CARET); + LITERAL_TOKEN("&", TOKEN_TYPE_SYM_AMPERSAND); + + // Symbols + if (char_is_alpha(*tk.token)) { + u64 len = 0; + while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) { + len++; + INCREMENT_CURR_TOKEN(tokenizer); + } + + tk.length = len; + tk.type = TOKEN_TYPE_SYMBOL; + goto token_parsed; + } + + // String literal + if (*tk.token == '"') { + u64 len = 0; + u64 slash_count = 0; + + INCREMENT_CURR_TOKEN(tokenizer); + + while (!(*tokenizer->curr == '"' && slash_count == 0)) { + len++; + + if (*tokenizer->curr == '\\') { + slash_count += 1; + slash_count %= 2; + } else { + slash_count = 0; + } + + INCREMENT_CURR_TOKEN(tokenizer); + } + + INCREMENT_CURR_TOKEN(tokenizer); + + tk.token++; + tk.type = TOKEN_TYPE_LITERAL_STRING; + tk.length = len; + goto token_parsed; + } + + // Number literal + if (char_is_num(*tokenizer->curr)) { + u64 len = 0; + while (char_is_num(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '.') { + len++; + INCREMENT_CURR_TOKEN(tokenizer); + } + + tk.type = TOKEN_TYPE_LITERAL_NUMERIC; + tk.length = len; + } + + INCREMENT_CURR_TOKEN(tokenizer); + +token_parsed: + bh_arr_push(tokenizer->tokens, tk); + + return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1]; +} + +OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) { + OnyxTokenizer tknizer = { + .start = fc->data, + .curr = fc->data, + .end = fc->data + fc->length, + + .filename = fc->filename, + + .line_number = 1, + .line_start = fc->data, + .tokens = NULL, + }; + + bh_arr_new(allocator, tknizer.tokens, 512); + return tknizer; +} + +void onyx_tokenizer_free(OnyxTokenizer* tokenizer) { + bh_arr_free(tokenizer->tokens); +} + +void onyx_lex_tokens(OnyxTokenizer* tokenizer) { + OnyxToken* tk; + do { + tk = onyx_get_token(tokenizer); + } while (tk->type != TOKEN_TYPE_END_STREAM); +} diff --git a/onyxlex.h b/onyxlex.h index 1fbce3a9..d2c07771 100644 --- a/onyxlex.h +++ b/onyxlex.h @@ -1,89 +1,89 @@ -#ifndef ONYXLEX_H -#define ONYXLEX_H - -#include "bh.h" - -typedef enum OnyxTokenType { - TOKEN_TYPE_UNKNOWN, - TOKEN_TYPE_END_STREAM, - - TOKEN_TYPE_COMMENT, - - TOKEN_TYPE_KEYWORD_STRUCT, - TOKEN_TYPE_KEYWORD_USE, - TOKEN_TYPE_KEYWORD_EXPORT, - TOKEN_TYPE_KEYWORD_IF, - TOKEN_TYPE_KEYWORD_ELSE, - TOKEN_TYPE_KEYWORD_FOR, - TOKEN_TYPE_KEYWORD_DO, - TOKEN_TYPE_KEYWORD_RETURN, - TOKEN_TYPE_KEYWORD_FOREIGN, - TOKEN_TYPE_KEYWORD_PROC, - TOKEN_TYPE_KEYWORD_GLOBAL, - - TOKEN_TYPE_RIGHT_ARROW, - TOKEN_TYPE_LEFT_ARROW, - TOKEN_TYPE_OPEN_PAREN, - TOKEN_TYPE_CLOSE_PAREN, - TOKEN_TYPE_OPEN_BRACE, - TOKEN_TYPE_CLOSE_BRACE, - TOKEN_TYPE_OPEN_BRACKET, - TOKEN_TYPE_CLOSE_BRACKET, - TOKEN_TYPE_OPEN_ANGLE, - TOKEN_TYPE_CLOSE_ANGLE, - - TOKEN_TYPE_SYM_PLUS, - TOKEN_TYPE_SYM_MINUS, - TOKEN_TYPE_SYM_STAR, - TOKEN_TYPE_SYM_PERCENT, - TOKEN_TYPE_SYM_DOT, - TOKEN_TYPE_SYM_FSLASH, - TOKEN_TYPE_SYM_BSLASH, - TOKEN_TYPE_SYM_COLON, - TOKEN_TYPE_SYM_SEMICOLON, - TOKEN_TYPE_SYM_COMMA, - TOKEN_TYPE_SYM_EQUALS, - TOKEN_TYPE_SYM_GRAVE, - TOKEN_TYPE_SYM_TILDE, - TOKEN_TYPE_SYM_BANG, - TOKEN_TYPE_SYM_CARET, - TOKEN_TYPE_SYM_AMPERSAND, - - TOKEN_TYPE_SYMBOL, - TOKEN_TYPE_LITERAL_STRING, - TOKEN_TYPE_LITERAL_NUMERIC, - - TOKEN_TYPE_COUNT -} OnyxTokenType; - -typedef struct OnyxFilePos { - const char* filename; - u64 line, column; -} OnyxFilePos; - -typedef struct OnyxToken { - OnyxTokenType type; - isize length; - char* token; - OnyxFilePos pos; -} OnyxToken; - -typedef struct OnyxTokenizer { - char *start, *curr, *end; - - const char* filename; - - char* line_start; - u64 line_number; - - bh_arr(OnyxToken) tokens; -} OnyxTokenizer; - -const char* onyx_get_token_type_name(OnyxTokenType tkn_type); -void onyx_token_null_toggle(OnyxToken tkn); -OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer); -OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc); -void onyx_tokenizer_free(OnyxTokenizer* tokenizer); -void onyx_lex_tokens(OnyxTokenizer* tokenizer); - -#endif +#ifndef ONYXLEX_H +#define ONYXLEX_H + +#include "bh.h" + +typedef enum OnyxTokenType { + TOKEN_TYPE_UNKNOWN, + TOKEN_TYPE_END_STREAM, + + TOKEN_TYPE_COMMENT, + + TOKEN_TYPE_KEYWORD_STRUCT, + TOKEN_TYPE_KEYWORD_USE, + TOKEN_TYPE_KEYWORD_EXPORT, + TOKEN_TYPE_KEYWORD_IF, + TOKEN_TYPE_KEYWORD_ELSE, + TOKEN_TYPE_KEYWORD_FOR, + TOKEN_TYPE_KEYWORD_DO, + TOKEN_TYPE_KEYWORD_RETURN, + TOKEN_TYPE_KEYWORD_FOREIGN, + TOKEN_TYPE_KEYWORD_PROC, + TOKEN_TYPE_KEYWORD_GLOBAL, + + TOKEN_TYPE_RIGHT_ARROW, + TOKEN_TYPE_LEFT_ARROW, + TOKEN_TYPE_OPEN_PAREN, + TOKEN_TYPE_CLOSE_PAREN, + TOKEN_TYPE_OPEN_BRACE, + TOKEN_TYPE_CLOSE_BRACE, + TOKEN_TYPE_OPEN_BRACKET, + TOKEN_TYPE_CLOSE_BRACKET, + TOKEN_TYPE_OPEN_ANGLE, + TOKEN_TYPE_CLOSE_ANGLE, + + TOKEN_TYPE_SYM_PLUS, + TOKEN_TYPE_SYM_MINUS, + TOKEN_TYPE_SYM_STAR, + TOKEN_TYPE_SYM_PERCENT, + TOKEN_TYPE_SYM_DOT, + TOKEN_TYPE_SYM_FSLASH, + TOKEN_TYPE_SYM_BSLASH, + TOKEN_TYPE_SYM_COLON, + TOKEN_TYPE_SYM_SEMICOLON, + TOKEN_TYPE_SYM_COMMA, + TOKEN_TYPE_SYM_EQUALS, + TOKEN_TYPE_SYM_GRAVE, + TOKEN_TYPE_SYM_TILDE, + TOKEN_TYPE_SYM_BANG, + TOKEN_TYPE_SYM_CARET, + TOKEN_TYPE_SYM_AMPERSAND, + + TOKEN_TYPE_SYMBOL, + TOKEN_TYPE_LITERAL_STRING, + TOKEN_TYPE_LITERAL_NUMERIC, + + TOKEN_TYPE_COUNT +} OnyxTokenType; + +typedef struct OnyxFilePos { + const char* filename; + u64 line, column; +} OnyxFilePos; + +typedef struct OnyxToken { + OnyxTokenType type; + isize length; + char* token; + OnyxFilePos pos; +} OnyxToken; + +typedef struct OnyxTokenizer { + char *start, *curr, *end; + + const char* filename; + + char* line_start; + u64 line_number; + + bh_arr(OnyxToken) tokens; +} OnyxTokenizer; + +const char* onyx_get_token_type_name(OnyxTokenType tkn_type); +void onyx_token_null_toggle(OnyxToken tkn); +OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer); +OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc); +void onyx_tokenizer_free(OnyxTokenizer* tokenizer); +void onyx_lex_tokens(OnyxTokenizer* tokenizer); + +#endif diff --git a/onyxmsgs.c b/onyxmsgs.c index 53b35975..42cb3266 100644 --- a/onyxmsgs.c +++ b/onyxmsgs.c @@ -1,44 +1,44 @@ - -#include "onyxmsgs.h" - -static const char* msg_formats[] = { - "expected token '%s', got '%s'", - "unexpected token '%s'", - "unknown type '%s'" -}; - -void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) { - OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage); - msg->type = type; - msg->pos = pos; - - va_list arg_list; - va_start(arg_list, pos); - bh_snprintf_va(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list); - va_end(arg_list); - - OnyxMessage** walker = &msgs->first; - while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next; - while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next; - - msg->next = *walker; - *walker = msg; -} - -void onyx_message_print(OnyxMessages* msgs) { - OnyxMessage* msg = msgs->first; - - while (msg) { - if (msg->pos.filename) { - bh_printf("(%s:%l:%l) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text); - } else { - bh_printf("(%l:%l) %s\n", msg->pos.line, msg->pos.column, msg->text); - } - msg = msg->next; - } -} - -void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { - msgs->allocator = allocator; - msgs->first = NULL; -} + +#include "onyxmsgs.h" + +static const char* msg_formats[] = { + "expected token '%s', got '%s'", + "unexpected token '%s'", + "unknown type '%s'" +}; + +void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) { + OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage); + msg->type = type; + msg->pos = pos; + + va_list arg_list; + va_start(arg_list, pos); + bh_snprintf_va(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list); + va_end(arg_list); + + OnyxMessage** walker = &msgs->first; + while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next; + while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next; + + msg->next = *walker; + *walker = msg; +} + +void onyx_message_print(OnyxMessages* msgs) { + OnyxMessage* msg = msgs->first; + + while (msg) { + if (msg->pos.filename) { + bh_printf("(%s:%l:%l) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text); + } else { + bh_printf("(%l:%l) %s\n", msg->pos.line, msg->pos.column, msg->text); + } + msg = msg->next; + } +} + +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) { + msgs->allocator = allocator; + msgs->first = NULL; +} diff --git a/onyxmsgs.h b/onyxmsgs.h index db3a22ce..9d615d98 100644 --- a/onyxmsgs.h +++ b/onyxmsgs.h @@ -1,36 +1,36 @@ -#ifndef ONYXMSGS_H -#define ONYXMSGS_H - -#include "bh.h" -#include "onyxlex.h" - -#include - -#define ONYX_MSG_BUFFER_SIZE 256 - -typedef enum OnyxMessageType { - ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, - ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, - ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, - - ONYX_MESSAGE_TYPE_COUNT, -} OnyxMessageType; - -typedef struct OnyxMessage { - OnyxMessageType type; - OnyxFilePos pos; - struct OnyxMessage* next; - char text[ONYX_MSG_BUFFER_SIZE]; -} OnyxMessage; - -typedef struct OnyxMessages { - bh_allocator allocator; - - OnyxMessage* first; -} OnyxMessages; - -void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...); -void onyx_message_print(OnyxMessages* msgs); -void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); - +#ifndef ONYXMSGS_H +#define ONYXMSGS_H + +#include "bh.h" +#include "onyxlex.h" + +#include + +#define ONYX_MSG_BUFFER_SIZE 256 + +typedef enum OnyxMessageType { + ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, + ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, + ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, + + ONYX_MESSAGE_TYPE_COUNT, +} OnyxMessageType; + +typedef struct OnyxMessage { + OnyxMessageType type; + OnyxFilePos pos; + struct OnyxMessage* next; + char text[ONYX_MSG_BUFFER_SIZE]; +} OnyxMessage; + +typedef struct OnyxMessages { + bh_allocator allocator; + + OnyxMessage* first; +} OnyxMessages; + +void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...); +void onyx_message_print(OnyxMessages* msgs); +void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs); + #endif \ No newline at end of file diff --git a/onyxparser.c b/onyxparser.c index e5cfe1e8..27d6f037 100644 --- a/onyxparser.c +++ b/onyxparser.c @@ -1,330 +1,610 @@ - -#include "onyxlex.h" -#include "onyxparser.h" - -struct OnyxTypeInfo builtin_types[] = { - { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" }, - { ONYX_TYPE_INFO_KIND_VOID, 0, "void" }, - - { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1 }, - - { ONYX_TYPE_INFO_KIND_UINT8, 1, "u8", 1, 1, 0, 0 }, - { ONYX_TYPE_INFO_KIND_UINT16, 2, "u16", 1, 1, 0, 0 }, - { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0 }, - { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0 }, - - { ONYX_TYPE_INFO_KIND_INT8, 1, "i8", 1, 0, 0, 0 }, - { ONYX_TYPE_INFO_KIND_INT16, 2, "i16", 1, 0, 0, 0 }, - { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0 }, - { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0 }, - - { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0 }, - { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0 }, - { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0 }, - - { 0xffffffff } // Sentinel -}; - -static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } }; - -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 b32 is_terminating_token(OnyxTokenType token_type) { - switch (token_type) { - case TOKEN_TYPE_SYM_SEMICOLON: - case TOKEN_TYPE_CLOSE_BRACE: - case TOKEN_TYPE_OPEN_BRACE: - case TOKEN_TYPE_END_STREAM: - return 1; - default: - return 0; - } -} - -// Advances to next token no matter what -static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) { - OnyxToken* token = parser->curr_token; - parser_next_token(parser); - - if (token->type != token_type) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, - token->pos, - onyx_get_token_type_name(token_type), onyx_get_token_type_name(token->type)); - return NULL; - } - - return token; -} - -static OnyxAstNode* parse_factor(OnyxParser* parser) { - return &error_node; -} - -static OnyxAstNode* parse_expression(OnyxParser* parser) { - return &error_node; -} - -static OnyxAstNode* parse_if_stmt(OnyxParser* parser) { - return &error_node; -} - -static OnyxAstNode* parse_expression_statement(OnyxParser* parser) { - -} - -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; - - parser_next_token(parser); - if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) { - expr = parse_expression(parser); - - if (expr == NULL || expr == &error_node) { - return &error_node; - } - } - - return return_node; -} - -static OnyxAstNodeBlock* parse_block(OnyxParser* parser); - -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); - - case TOKEN_TYPE_SYMBOL: - 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_statement(parser); - - case TOKEN_TYPE_KEYWORD_IF: - return parse_if_stmt(parser); - - case TOKEN_TYPE_SYM_SEMICOLON: - return NULL; - - default: - parser_next_token(parser); - return NULL; - } -} - -static OnyxAstNodeBlock* parse_block(OnyxParser* parser) { - // --- 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); - - 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; - } - - expect(parser, TOKEN_TYPE_SYM_SEMICOLON); - } - - 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; - - 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); - - 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; - } - - 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); - func_def->param_count = 0; - - OnyxAstNodeParam* params = parse_function_params(parser); - func_def->params = params; - - for (OnyxAstNode* walker = (OnyxAstNode *) params; walker != NULL; walker = walker->next) - func_def->param_count++; - - expect(parser, TOKEN_TYPE_RIGHT_ARROW); - - OnyxTypeInfo* return_type = parse_type(parser); - func_def->return_type = return_type; - - func_def->body = parse_block(parser); - 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: - assert(0); - break; - - case TOKEN_TYPE_SYMBOL: { - OnyxToken* symbol = parser->curr_token; - parser_next_token(parser); - - expect(parser, TOKEN_TYPE_SYM_COLON); - expect(parser, TOKEN_TYPE_SYM_COLON); - - if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) { - OnyxAstNodeFuncDef* func_def = parse_function_definition(parser); - func_def->token = symbol; - return (OnyxAstNode *) func_def; - - } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) { - // Handle struct case - assert(0); - } else { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, - parser->curr_token->pos, - onyx_get_token_type_name(parser->curr_token->type)); - } - } break; - } - - parser_next_token(parser); - return NULL; -} - - - - - - - -OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\ - OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode)); - node->kind = kind; - - 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; - - return parser; -} - -void onyx_parser_free(OnyxParser* parser) { - bh_hash_free(parser->identifiers); -} - -OnyxAstNode* onyx_parse(OnyxParser *parser) { - OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM); - - OnyxAstNode** prev_stmt = &program->next; - OnyxAstNode* curr_stmt = NULL; - while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) { - curr_stmt = parse_top_level_statement(parser); - - // Building a linked list of statements down the "next" chain - if (curr_stmt != NULL && curr_stmt != &error_node) { - *prev_stmt = curr_stmt; - prev_stmt = &curr_stmt->next; - } - } - - return program; -} + +#include "onyxlex.h" +#include "onyxparser.h" + +static const char* ast_node_names[] = { + "ERROR", + "PROGRAM", + + "FUNCDEF", + "BLOCK", + "SCOPE", + "LOCAL", + + "ADD", + "MINUS", + "MULTIPLY", + "DIVIDE", + "MODULUS", + "NEGATE", + + "TYPE", + "LITERAL", + "CAST", + "PARAM", + "CALL", + "ASSIGN", + "RETURN", + + "EQUAL", + "NOT_EQUAL", + "GREATER", + "GREATER_EQUAL", + "LESS", + "LESS_EQUAL", + "NOT", + + "IF", + "LOOP", + + "ONYX_AST_NODE_KIND_COUNT", +}; + +struct OnyxTypeInfo builtin_types[] = { + { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" }, + { ONYX_TYPE_INFO_KIND_VOID, 0, "void" }, + + { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1 }, + + { ONYX_TYPE_INFO_KIND_UINT8, 1, "u8", 1, 1, 0, 0 }, + { ONYX_TYPE_INFO_KIND_UINT16, 2, "u16", 1, 1, 0, 0 }, + { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0 }, + { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0 }, + + { ONYX_TYPE_INFO_KIND_INT8, 1, "i8", 1, 0, 0, 0 }, + { ONYX_TYPE_INFO_KIND_INT16, 2, "i16", 1, 0, 0, 0 }, + { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0 }, + { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0 }, + + { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0 }, + { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0 }, + { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0 }, + + { 0xffffffff } // Sentinel +}; + +static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } }; + +// NOTE: Forward declarations +static void parser_next_token(OnyxParser* parser); +static void parser_prev_token(OnyxParser* parser); +static b32 is_terminating_token(OnyxTokenType token_type); +static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type); +static OnyxAstNodeScope* enter_scope(OnyxParser* parser); +static OnyxAstNodeScope* leave_scope(OnyxParser* parser); +static void insert_identifier(OnyxParser* parser, OnyxAstNodeLocal* local); +static OnyxAstNode* parse_factor(OnyxParser* parser); +static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left); +static OnyxAstNode* parse_expression(OnyxParser* parser); +static OnyxAstNode* parse_if_stmt(OnyxParser* parser); +static OnyxAstNode* parse_expression_statement(OnyxParser* parser); +static OnyxAstNode* parse_return_statement(OnyxParser* parser); +static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function); +static OnyxAstNode* parse_statement(OnyxParser* parser); +static OnyxTypeInfo* parse_type(OnyxParser* parser); +static OnyxAstNodeParam* parse_function_params(OnyxParser* parser); +static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser); +static OnyxAstNode* parse_top_level_statement(OnyxParser* parser); + +static void parser_next_token(OnyxParser* parser) { + parser->prev_token = parser->curr_token; + parser->curr_token++; + while (parser->curr_token->type == TOKEN_TYPE_COMMENT) parser->curr_token++; +} + +static void parser_prev_token(OnyxParser* parser) { + // TODO: This is probably wrong + parser->prev_token--; + while (parser->prev_token->type == TOKEN_TYPE_COMMENT) parser->prev_token--; + parser->curr_token = parser->prev_token; + parser->prev_token--; +} + +static b32 is_terminating_token(OnyxTokenType token_type) { + switch (token_type) { + case TOKEN_TYPE_SYM_SEMICOLON: + case TOKEN_TYPE_CLOSE_BRACE: + case TOKEN_TYPE_OPEN_BRACE: + case TOKEN_TYPE_END_STREAM: + return 1; + default: + return 0; + } +} + +// Advances to next token no matter what +static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) { + OnyxToken* token = parser->curr_token; + parser_next_token(parser); + + if (token->type != token_type) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, + token->pos, + onyx_get_token_type_name(token_type), onyx_get_token_type_name(token->type)); + return NULL; + } + + return token; +} + +static OnyxAstNodeScope* enter_scope(OnyxParser* parser) { + OnyxAstNodeScope* scope = (OnyxAstNodeScope*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE); + scope->prev_scope = parser->curr_scope; + parser->curr_scope = scope; + return scope; +} + +static OnyxAstNodeScope* leave_scope(OnyxParser* parser) { + // NOTE: Can't leave a scope if there is no scope + assert(parser->curr_scope != NULL); + + for (OnyxAstNodeLocal *walker = parser->curr_scope->last_local; walker != NULL; walker = walker->prev_local) { + onyx_token_null_toggle(*walker->token); + bh_hash_delete(OnyxAstNode*, parser->identifiers, walker->token->token); + onyx_token_null_toggle(*walker->token); + } + + parser->curr_scope = parser->curr_scope->prev_scope; + return parser->curr_scope; +} + +static OnyxAstNode* lookup_identifier(OnyxParser* parser, OnyxToken* token) { + OnyxAstNode* ident = NULL; + + onyx_token_null_toggle(*token); + if (bh_hash_has(OnyxAstNode*, parser->identifiers, token->token)) { + ident = bh_hash_get(OnyxAstNode*, parser->identifiers, token->token); + } + onyx_token_null_toggle(*token); + + return ident; +} + +static void insert_identifier(OnyxParser* parser, OnyxAstNodeLocal* local) { + OnyxAstNodeScope* scope = parser->curr_scope; + local->prev_local = scope->last_local; + scope->last_local = local; + + onyx_token_null_toggle(*local->token); + bh_hash_put(OnyxAstNodeLocal*, parser->identifiers, local->token->token, local); + onyx_token_null_toggle(*local->token); +} + +static OnyxAstNode* parse_factor(OnyxParser* parser) { + switch (parser->curr_token->type) { + case TOKEN_TYPE_OPEN_PAREN: { + parser_next_token(parser); + OnyxAstNode* expr = parse_expression(parser); + expect(parser, TOKEN_TYPE_CLOSE_PAREN); + return expr; + } + + case TOKEN_TYPE_SYMBOL: { + OnyxToken* sym_token = expect(parser, TOKEN_TYPE_SYMBOL); + OnyxAstNode* sym_node = lookup_identifier(parser, sym_token); + + // TODO: Handle calling a function + return sym_node; + } + + case TOKEN_TYPE_LITERAL_NUMERIC: { + OnyxAstNode* lit_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL); + lit_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_INT64]; + lit_node->token = expect(parser, TOKEN_TYPE_LITERAL_NUMERIC); + return lit_node; + } + + default: + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, + parser->curr_token->pos, + onyx_get_token_type_name(parser->curr_token->type)); + } + + return NULL; +} + +static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left) { + OnyxAstNodeKind kind = -1; + + switch (parser->curr_token->type) { + case TOKEN_TYPE_SYM_PLUS: kind = ONYX_AST_NODE_KIND_ADD; break; + case TOKEN_TYPE_SYM_MINUS: kind = ONYX_AST_NODE_KIND_MINUS; break; + case TOKEN_TYPE_SYM_STAR: kind = ONYX_AST_NODE_KIND_MULTIPLY; break; + case TOKEN_TYPE_SYM_FSLASH: kind = ONYX_AST_NODE_KIND_DIVIDE; break; + case TOKEN_TYPE_SYM_PERCENT: kind = ONYX_AST_NODE_KIND_MODULUS; break; + } + + if (kind != -1) { + parser_next_token(parser); + OnyxAstNode* right = parse_factor(parser); + + OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind); + bin_op->left = left; + bin_op->right = right; + return bin_op; + } + + return &error_node; +} + +static OnyxAstNode* parse_expression(OnyxParser* parser) { + OnyxAstNode* left = parse_factor(parser); + + switch (parser->curr_token->type) { + case TOKEN_TYPE_SYM_PLUS: + case TOKEN_TYPE_SYM_MINUS: + case TOKEN_TYPE_SYM_STAR: + case TOKEN_TYPE_SYM_FSLASH: + case TOKEN_TYPE_SYM_PERCENT: { + OnyxAstNode* expr = parse_bin_op(parser, left); + return expr; + } + } + + return left; +} + +static OnyxAstNode* parse_if_stmt(OnyxParser* parser) { + return &error_node; +} + +static OnyxAstNode* parse_expression_statement(OnyxParser* parser) { + if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) return NULL; + OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL); + + switch (parser->curr_token->type) { + // NOTE: Declaration + case TOKEN_TYPE_SYM_COLON: { + parser_next_token(parser); + OnyxTypeInfo* type = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN]; + + // NOTE: var: type + if (parser->curr_token->type == TOKEN_TYPE_SYMBOL) { + type = parse_type(parser); + } + + OnyxAstNodeLocal* local = (OnyxAstNodeLocal*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LOCAL); + local->token = symbol; + local->type = type; + + insert_identifier(parser, local); + + if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) { + parser_next_token(parser); + + OnyxAstNode* expr = parse_expression(parser); + OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); + assignment->right = expr; + assignment->left = (OnyxAstNode*) local; + return assignment; + } + } + + // NOTE: Assignment + case TOKEN_TYPE_SYM_EQUALS: { + parser_next_token(parser); + + OnyxAstNode* lval = lookup_identifier(parser, symbol); + + if (lval == NULL) { + // TODO: error handling + } + + OnyxAstNode* rval = parse_expression(parser); + OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); + assignment->right = rval; + assignment->left = lval; + return assignment; + } + + default: + parser_prev_token(parser); + } + + return NULL; +} + +static OnyxAstNode* parse_return_statement(OnyxParser* parser) { + expect(parser, TOKEN_TYPE_KEYWORD_RETURN); + + OnyxAstNode* return_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_RETURN); + return_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_VOID]; + OnyxAstNode* expr = NULL; + + if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) { + expr = parse_expression(parser); + + if (expr == NULL || expr == &error_node) { + return &error_node; + } else { + return_node->next = expr; + } + } + + return return_node; +} + +static OnyxAstNode* parse_statement(OnyxParser* parser) { + switch (parser->curr_token->type) { + case TOKEN_TYPE_KEYWORD_RETURN: + return parse_return_statement(parser); + + case TOKEN_TYPE_OPEN_BRACE: + return (OnyxAstNode *) parse_block(parser, 0); + + case TOKEN_TYPE_SYMBOL: { + OnyxAstNode* ret = parse_expression_statement(parser); + if (ret != NULL) return ret; + } + + case TOKEN_TYPE_OPEN_PAREN: + case TOKEN_TYPE_SYM_PLUS: + case TOKEN_TYPE_SYM_MINUS: + case TOKEN_TYPE_SYM_BANG: + case TOKEN_TYPE_LITERAL_NUMERIC: + case TOKEN_TYPE_LITERAL_STRING: + return parse_expression(parser); + + case TOKEN_TYPE_KEYWORD_IF: + return parse_if_stmt(parser); + + default: + return NULL; + } +} + +static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function) { + // --- is for an empty block + if (parser->curr_token->type == TOKEN_TYPE_SYM_MINUS) { + expect(parser, TOKEN_TYPE_SYM_MINUS); + expect(parser, TOKEN_TYPE_SYM_MINUS); + expect(parser, TOKEN_TYPE_SYM_MINUS); + return NULL; + } + + expect(parser, TOKEN_TYPE_OPEN_BRACE); + + OnyxAstNodeBlock* block = (OnyxAstNodeBlock *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_BLOCK); + if (belongs_to_function) { + OnyxAstNodeScope* scope = enter_scope(parser); + block->scope = scope; + } + + OnyxAstNode** next = &block->body; + OnyxAstNode* stmt = NULL; + while (parser->curr_token->type != TOKEN_TYPE_CLOSE_BRACE) { + stmt = parse_statement(parser); + + if (stmt != NULL && stmt->kind != ONYX_AST_NODE_KIND_ERROR) { + *next = stmt; + next = &stmt->next; + } + + if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, + parser->curr_token->pos, + onyx_get_token_type_name(TOKEN_TYPE_SYM_SEMICOLON), + onyx_get_token_type_name(parser->curr_token->type)); + } + parser_next_token(parser); + } + + expect(parser, TOKEN_TYPE_CLOSE_BRACE); + + if (belongs_to_function) { + leave_scope(parser); + } + + return block; +} + +static OnyxTypeInfo* parse_type(OnyxParser* parser) { + OnyxTypeInfo* type_info = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN]; + + OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL); + if (symbol == NULL) return type_info; + + onyx_token_null_toggle(*symbol); + + if (!bh_hash_has(OnyxAstNode*, parser->identifiers, symbol->token)) { + onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->token); + } else { + OnyxAstNode* type_info_node = bh_hash_get(OnyxAstNode*, parser->identifiers, symbol->token); + + if (type_info_node->kind == ONYX_AST_NODE_KIND_TYPE) { + type_info = type_info_node->type; + } + } + + onyx_token_null_toggle(*symbol); + return type_info; +} + +static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) { + expect(parser, TOKEN_TYPE_OPEN_PAREN); + + if (parser->curr_token->type == TOKEN_TYPE_CLOSE_PAREN) { + parser_next_token(parser); + return NULL; + } + + OnyxAstNodeParam* first_param = NULL; + u64 param_count = 0; + + OnyxAstNodeParam* curr_param = NULL; + OnyxAstNodeParam* trailer = NULL; + + OnyxToken* symbol; + while (parser->curr_token->type != TOKEN_TYPE_CLOSE_PAREN) { + if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser); + param_count++; + + symbol = expect(parser, TOKEN_TYPE_SYMBOL); + + curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM); + curr_param->token = symbol; + curr_param->type = parse_type(parser); + + if (first_param == NULL) first_param = curr_param; + + curr_param->next = NULL; + if (trailer) trailer->next = curr_param; + + trailer = curr_param; + } + + first_param->param_count = param_count; + + parser_next_token(parser); // Skip the ) + return first_param; +} + +static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) { + expect(parser, TOKEN_TYPE_KEYWORD_PROC); + + OnyxAstNodeFuncDef* func_def = (OnyxAstNodeFuncDef *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_FUNCDEF); + + OnyxAstNodeParam* params = parse_function_params(parser); + func_def->params = params; + + expect(parser, TOKEN_TYPE_RIGHT_ARROW); + + OnyxTypeInfo* return_type = parse_type(parser); + func_def->return_type = return_type; + + // TODO: Add params to parser.identifiers + for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { + onyx_token_null_toggle(*p->token); + bh_hash_put(OnyxAstNode*, parser->identifiers, p->token->token, (OnyxAstNode*) p); + onyx_token_null_toggle(*p->token); + } + + func_def->body = parse_block(parser, 1); + + // TODO: Remove params from parser.identifiers + for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { + onyx_token_null_toggle(*p->token); + bh_hash_delete(OnyxAstNode*, parser->identifiers, p->token->token); + onyx_token_null_toggle(*p->token); + } + return func_def; +} + +static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) { + switch (parser->curr_token->type) { + case TOKEN_TYPE_KEYWORD_USE: + assert(0); + break; + + case TOKEN_TYPE_KEYWORD_EXPORT: { + expect(parser, TOKEN_TYPE_KEYWORD_EXPORT); + if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_EXPECTED_TOKEN, + parser->curr_token->pos, + onyx_get_token_type_name(TOKEN_TYPE_SYMBOL), + onyx_get_token_type_name(parser->curr_token->type)); + break; + } + + OnyxAstNode* top_level_decl = parse_top_level_statement(parser); + top_level_decl->flags |= ONYX_AST_FLAG_EXPORTED; + return top_level_decl; + } break; + + case TOKEN_TYPE_SYMBOL: { + OnyxToken* symbol = parser->curr_token; + parser_next_token(parser); + + expect(parser, TOKEN_TYPE_SYM_COLON); + expect(parser, TOKEN_TYPE_SYM_COLON); + + if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) { + OnyxAstNodeFuncDef* func_def = parse_function_definition(parser); + func_def->token = symbol; + return (OnyxAstNode *) func_def; + + } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) { + // Handle struct case + assert(0); + } else { + onyx_message_add(parser->msgs, + ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, + parser->curr_token->pos, + onyx_get_token_type_name(parser->curr_token->type)); + } + } break; + } + + parser_next_token(parser); + return NULL; +} + + + + + +const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind) { + return ast_node_names[kind]; +} + +OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\ + OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode)); + node->kind = kind; + node->flags = 0; + node->token = NULL; + node->type = NULL; + node->next = NULL; + node->left = NULL; + node->right = NULL; + + return node; +} + +OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) { + OnyxParser parser; + + bh_hash_init(bh_heap_allocator(), parser.identifiers); + + OnyxTypeInfo* it = &builtin_types[0]; + while (it->kind != 0xffffffff) { + OnyxAstNode* tmp = onyx_ast_node_new(alloc, ONYX_AST_NODE_KIND_TYPE); + tmp->type = it; + bh_hash_put(OnyxAstNode*, parser.identifiers, (char *)it->name, tmp); + it++; + } + + parser.allocator = alloc; + parser.tokenizer = tokenizer; + parser.curr_token = tokenizer->tokens; + parser.prev_token = NULL; + parser.msgs = msgs; + parser.curr_scope = NULL; + + return parser; +} + +void onyx_parser_free(OnyxParser* parser) { + bh_hash_free(parser->identifiers); +} + +OnyxAstNode* onyx_parse(OnyxParser *parser) { + OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM); + + OnyxAstNode** prev_stmt = &program->next; + OnyxAstNode* curr_stmt = NULL; + while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) { + curr_stmt = parse_top_level_statement(parser); + + // Building a linked list of statements down the "next" chain + if (curr_stmt != NULL && curr_stmt != &error_node) { + *prev_stmt = curr_stmt; + prev_stmt = &curr_stmt->next; + } + } + + return program; +} diff --git a/onyxparser.h b/onyxparser.h index 54c2553c..b8d04e5c 100644 --- a/onyxparser.h +++ b/onyxparser.h @@ -1,154 +1,188 @@ -#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 OnyxAstNodeBlock OnyxAstNodeBlock; -typedef struct OnyxAstNodeParam OnyxAstNodeParam; -typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; - -typedef struct OnyxParser { - OnyxTokenizer *tokenizer; - OnyxToken *prev_token; - OnyxToken *curr_token; - - bh_hash(OnyxAstNode*) identifiers; - - 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_ADD, - ONYX_AST_NODE_KIND_SUB, - ONYX_AST_NODE_KIND_MUL, - 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_RETURN, - - ONYX_AST_NODE_KIND_EQUAL, - ONYX_AST_NODE_KIND_NOT_EQUAL, - ONYX_AST_NODE_KIND_GREATER, - ONYX_AST_NODE_KIND_GREATER_EQUAL, - ONYX_AST_NODE_KIND_LESS, - ONYX_AST_NODE_KIND_LESS_EQUAL, - ONYX_AST_NODE_KIND_NOT, - - ONYX_AST_NODE_KIND_IF, - ONYX_AST_NODE_KIND_LOOP, - - ONYX_AST_NODE_KIND_COUNT -} OnyxAstNodeKind; - -typedef enum OnyxTypeInfoKind { - ONYX_TYPE_INFO_KIND_UNKNOWN, - ONYX_TYPE_INFO_KIND_VOID, - ONYX_TYPE_INFO_KIND_BOOL, - - ONYX_TYPE_INFO_KIND_UINT8, - ONYX_TYPE_INFO_KIND_UINT16, - ONYX_TYPE_INFO_KIND_UINT32, - ONYX_TYPE_INFO_KIND_UINT64, - - ONYX_TYPE_INFO_KIND_INT8, - ONYX_TYPE_INFO_KIND_INT16, - ONYX_TYPE_INFO_KIND_INT32, - ONYX_TYPE_INFO_KIND_INT64, - - ONYX_TYPE_INFO_KIND_FLOAT32, - ONYX_TYPE_INFO_KIND_FLOAT64, - ONYX_TYPE_INFO_KIND_SOFT_FLOAT, // 64-bits of data but could be treated as 32-bit -} OnyxTypeInfoKind; - -typedef struct OnyxTypeInfo { - OnyxTypeInfoKind kind; - u32 size; // in bytes - const char* name; - u32 is_int : 1; - u32 is_unsigned : 1; - u32 is_float : 1; - u32 is_bool : 1; -} OnyxTypeInfo; - -extern OnyxTypeInfo builtin_types[]; - -typedef enum OnyxAstFlags { - ONYX_AST_BLOCK_FLAG_HAS_RETURN = BH_BIT(1), - ONYX_AST_BLOCK_FLAG_TOP_LEVEL = BH_BIT(2), - ONYX_AST_BLOCK_FLAG_EXPORTED = BH_BIT(3), -} OnyxAstFlags; - -struct OnyxAstNodeBlock { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; - OnyxTypeInfo *return_type; - OnyxAstNode *next; - OnyxAstNode *body; - OnyxAstNode *unused1; -}; - -struct OnyxAstNodeParam { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; // Symbol name i.e. 'a', 'b' - OnyxTypeInfo *type; - OnyxAstNodeParam *next; - OnyxAstNode *left; - OnyxAstNode *right; -}; - -struct OnyxAstNodeFuncDef { - OnyxAstNodeKind kind; - u32 flags; - OnyxToken *token; // This will point to the symbol token to identify it - OnyxTypeInfo *return_type; - OnyxAstNodeBlock *body; - OnyxAstNodeParam *params; - u64 param_count; // Same size as ptr -}; - -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; -}; - -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 \ No newline at end of file +#ifndef ONYXPARSER_H +#define ONYXPARSER_H + +#define BH_NO_STRING +#include "bh.h" + +#include "onyxlex.h" +#include "onyxmsgs.h" + +typedef union OnyxAstNode OnyxAstNode; +typedef struct OnyxAstNodeLocal OnyxAstNodeLocal; +typedef struct OnyxAstNodeScope OnyxAstNodeScope; +typedef struct OnyxAstNodeBlock OnyxAstNodeBlock; +typedef struct OnyxAstNodeParam OnyxAstNodeParam; +typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; + +typedef struct OnyxParser { + OnyxTokenizer *tokenizer; // NOTE: not used since all tokens are lexed before parsing starts + OnyxToken *prev_token; + OnyxToken *curr_token; + + // BUG: This way of handling identifiers will work for now, + // but it will not allow for shadowing. Also, variable names + // cannot be the same as any function or global variable. + // That will get annoying to program. + // NOTE: A table of the current identifiers in the current scope. + // If the identifier doesn't at the time of parsing, it is an error. + // Cleared at the end of a block. + bh_hash(OnyxAstNode*) identifiers; + OnyxAstNodeScope *curr_scope; + + OnyxMessages *msgs; + + bh_allocator allocator; +} OnyxParser; + +typedef enum OnyxAstNodeKind { + ONYX_AST_NODE_KIND_ERROR, + ONYX_AST_NODE_KIND_PROGRAM, + + ONYX_AST_NODE_KIND_FUNCDEF, + ONYX_AST_NODE_KIND_BLOCK, + ONYX_AST_NODE_KIND_SCOPE, + ONYX_AST_NODE_KIND_LOCAL, + + ONYX_AST_NODE_KIND_ADD, + ONYX_AST_NODE_KIND_MINUS, + ONYX_AST_NODE_KIND_MULTIPLY, + ONYX_AST_NODE_KIND_DIVIDE, + ONYX_AST_NODE_KIND_MODULUS, + ONYX_AST_NODE_KIND_NEGATE, + + ONYX_AST_NODE_KIND_TYPE, + ONYX_AST_NODE_KIND_LITERAL, + ONYX_AST_NODE_KIND_CAST, + ONYX_AST_NODE_KIND_PARAM, + ONYX_AST_NODE_KIND_CALL, + ONYX_AST_NODE_KIND_ASSIGNMENT, + ONYX_AST_NODE_KIND_RETURN, + + ONYX_AST_NODE_KIND_EQUAL, + ONYX_AST_NODE_KIND_NOT_EQUAL, + ONYX_AST_NODE_KIND_GREATER, + ONYX_AST_NODE_KIND_GREATER_EQUAL, + ONYX_AST_NODE_KIND_LESS, + ONYX_AST_NODE_KIND_LESS_EQUAL, + ONYX_AST_NODE_KIND_NOT, + + ONYX_AST_NODE_KIND_IF, + ONYX_AST_NODE_KIND_LOOP, + + ONYX_AST_NODE_KIND_COUNT +} OnyxAstNodeKind; + +typedef enum OnyxTypeInfoKind { + ONYX_TYPE_INFO_KIND_UNKNOWN, + ONYX_TYPE_INFO_KIND_VOID, + ONYX_TYPE_INFO_KIND_BOOL, + + ONYX_TYPE_INFO_KIND_UINT8, + ONYX_TYPE_INFO_KIND_UINT16, + ONYX_TYPE_INFO_KIND_UINT32, + ONYX_TYPE_INFO_KIND_UINT64, + + ONYX_TYPE_INFO_KIND_INT8, + ONYX_TYPE_INFO_KIND_INT16, + ONYX_TYPE_INFO_KIND_INT32, + ONYX_TYPE_INFO_KIND_INT64, + + ONYX_TYPE_INFO_KIND_FLOAT32, + ONYX_TYPE_INFO_KIND_FLOAT64, + ONYX_TYPE_INFO_KIND_SOFT_FLOAT, // 64-bits of data but could be treated as 32-bit +} OnyxTypeInfoKind; + +typedef struct OnyxTypeInfo { + OnyxTypeInfoKind kind; + u32 size; // in bytes + const char* name; + u32 is_int : 1; + u32 is_unsigned : 1; + u32 is_float : 1; + u32 is_bool : 1; +} OnyxTypeInfo; + +extern OnyxTypeInfo builtin_types[]; + +typedef enum OnyxAstFlags { + ONYX_AST_FLAG_HAS_RETURN = BH_BIT(1), + ONYX_AST_FLAG_TOP_LEVEL = BH_BIT(2), + ONYX_AST_FLAG_EXPORTED = BH_BIT(3), + ONYX_AST_FLAG_FUNCTION_PARAM = BH_BIT(3), +} OnyxAstFlags; + +struct OnyxAstNodeLocal { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *type; + OnyxAstNodeLocal *prev_local; + OnyxAstNode *unused1; + OnyxAstNode *unused2; +}; + +struct OnyxAstNodeScope { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // NOTE: UNUSED + OnyxTypeInfo *type; // NOTE: UNUSED + OnyxAstNodeScope *prev_scope; + OnyxAstNodeLocal *last_local; + OnyxAstNode *unused; +}; + +struct OnyxAstNodeBlock { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *return_type; + OnyxAstNode *next; + OnyxAstNode *body; + OnyxAstNodeScope *scope; // NOTE: Only set on blocks belonging to functions +}; + +struct OnyxAstNodeParam { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // Symbol name i.e. 'a', 'b' + OnyxTypeInfo *type; + OnyxAstNodeParam *next; + u64 param_count; + OnyxAstNode *right; +}; + +struct OnyxAstNodeFuncDef { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // This will point to the symbol token to identify it + OnyxTypeInfo *return_type; + OnyxAstNode *next; + OnyxAstNodeBlock *body; + OnyxAstNodeParam *params; +}; + +union OnyxAstNode { + + // Generic node structure for capturing all binary ops and statements + struct { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *type; + OnyxAstNode *next; + OnyxAstNode *left; + OnyxAstNode *right; + }; + + OnyxAstNodeBlock as_block; + OnyxAstNodeFuncDef as_funcdef; + OnyxAstNodeParam as_param; +}; + +const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind); +OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind); +OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs); +void onyx_parser_free(OnyxParser* parser); +OnyxAstNode* onyx_parse(OnyxParser *parser); + +#endif // #ifndef ONYXPARSER_H diff --git a/onyxutils.c b/onyxutils.c new file mode 100644 index 00000000..74b96e6b --- /dev/null +++ b/onyxutils.c @@ -0,0 +1,26 @@ +#include "onyxutils.h" + +void onyx_ast_print(OnyxAstNode* node) { + if (node == NULL) return; + + bh_printf("%s <%d> ", onyx_ast_node_kind_string(node->kind), node->flags); + if (node->token) + bh_printf("[%b] ", node->token->token, (int) node->token->length); + + if ((i64) node->left > 10) { // HACK: but okay + bh_printf("("); + onyx_ast_print(node->left); + bh_printf(") "); + } + if ((i64) node->right > 10) { // HACK: but okay + bh_printf("("); + onyx_ast_print(node->right); + bh_printf(")"); + } + + if (node->next) { + bh_printf("{"); + onyx_ast_print(node->next); + bh_printf("}"); + } +} diff --git a/onyxutils.h b/onyxutils.h new file mode 100644 index 00000000..a8e2e3a5 --- /dev/null +++ b/onyxutils.h @@ -0,0 +1,5 @@ +#include "bh.h" + +#include "onyxparser.h" + +void onyx_ast_print(OnyxAstNode* program); diff --git a/progs/minimal.onyx b/progs/minimal.onyx index bbb2d1c8..81dc7c8c 100644 --- a/progs/minimal.onyx +++ b/progs/minimal.onyx @@ -1,8 +1,14 @@ -/* This is a comment */ - -log :: proc (a i32, b i32) -> i32 ---; - -add :: proc (a i32, b i32) -> i32 { - /* More comments */ - return a + b; -}; \ No newline at end of file +/* This is a comment */ + +log :: proc (a i32, b i32) -> i32 ---; + +export add :: proc (a i32, b i32) -> i32 { + /* More comments */ + return a + b; +}; + +export mul :: proc (a i32, b i32) -> i32 { + c: i32 = a - b; + d := a + 2; + return c * d; +}; -- 2.25.1