OBJ_FILES=\
- onyxlex.o \
- onyxparser.o \
- onyxmsgs.o \
- onyxutils.o \
- onyx.o
+ src/onyxlex.o \
+ src/onyxparser.o \
+ src/onyxmsgs.o \
+ src/onyxutils.o \
+ src/onyx.o
CC=gcc
-INCLUDES=
+INCLUDES=-I./include
LIBS=
FLAGS=-g
-%.o: %.c bh.h
+%.o: %.c include/bh.h
$(CC) $(FLAGS) -c $< -o $@ $(INCLUDES)
onyx: $(OBJ_FILES)
+++ /dev/null
-#ifndef BH_H
-#define BH_H
-
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h> // TODO: Replace with needed functions
-#include <assert.h>
-
-//-------------------------------------------------------------------------------------
-// Better types
-//-------------------------------------------------------------------------------------
-typedef unsigned char u8;
-typedef unsigned short u16;
-typedef unsigned int u32;
-typedef unsigned long u64;
-typedef unsigned long long u128;
-typedef signed char i8;
-typedef signed short i16;
-typedef signed int i32;
-typedef signed long i64;
-typedef signed long long i128;
-typedef unsigned long isize;
-typedef i32 b32;
-typedef void* ptr;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better character functions
-//-------------------------------------------------------------------------------------
-b32 char_is_alpha(const char a);
-b32 char_is_num(const char a);
-b32 char_is_alphanum(const char a);
-b32 char_is_whitespace(const char a);
-b32 char_in_range(const char lo, const char hi, const char a);
-char charset_contains(const char* charset, char ch);
-i64 chars_match(char* ptr1, char* ptr2);
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better math functions
-//-------------------------------------------------------------------------------------
-#define bh_max(a, b) ((a) > (b) ? (a) : (b))
-#define bh_min(a, b) ((a) < (b) ? (a) : (b))
-#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))
-#define bh_abs(x) ((x) < 0 ? -(x) : (x))
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Helpful macros
-//-------------------------------------------------------------------------------------
-#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem)
-#define bh_aligh_of(Type) bh_offset_of(struct { char c; Type member; }, member)
-#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
-
-#define bh_pointer_add(ptr, amm) ((void *)((u8 *) ptr + amm))
-#define BH_BIT(x) (1 << (x))
-#define BH_MASK_SET(var, set, mask) ((set) ? (var) |= (mask) : (var) &= ~(mask))
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Custom allocators
-//-------------------------------------------------------------------------------------
-
-typedef enum bh_allocator_actions {
- bh_allocator_action_alloc,
- bh_allocator_action_free,
- bh_allocator_action_resize,
-} bh_allocator_actions;
-
-#define BH_ALLOCATOR_PROC(name) \
-ptr name(ptr data, bh_allocator_actions action, \
- isize size, isize alignment, \
- void* prev_memory, \
- u64 flags)
-
-typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
-
-typedef struct bh_allocator {
- bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
- ptr data; // Pointer to the other data for the allocator
-} bh_allocator;
-
-typedef enum bh_allocator_flags {
- bh_allocator_flag_clear = 1 // Sets all memory to be 0
-} bh_allocator_flags;
-
-ptr bh_alloc(bh_allocator a, isize size);
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
-ptr bh_resize(bh_allocator a, ptr data, isize new_size);
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
-void bh_free(bh_allocator a, ptr data);
-
-#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T))
-#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n))
-
-// NOTE: This should get optimized out since alignment should be a power of two
-#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment)
-
-
-
-
-// HEAP ALLOCATOR
-// Essentially a wrapper for malloc, free and realloc
-bh_allocator bh_heap_allocator(void);
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
-
-
-
-
-
-// ARENA ALLOCATOR
-typedef struct bh_arena {
- bh_allocator backing;
- ptr first_arena, current_arena;
- isize size, arena_size; // in bytes
-} bh_arena;
-
-typedef struct bh__arena_internal {
- ptr next_arena;
- void* data; // Not actually a pointer, just used for the offset
-} bh__arena_internal;
-
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
-void bh_arena_free(bh_arena* alloc);
-bh_allocator bh_arena_allocator(bh_arena* alloc);
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
-
-
-
-// SCRATCH ALLOCATOR
-typedef struct bh_scratch {
- bh_allocator backing;
- ptr memory, end, curr;
-} bh_scratch;
-
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
-void bh_scratch_free(bh_scratch* scratch);
-bh_allocator bh_scratch_allocator(bh_scratch* scratch);
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better strings
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_STRING
-
-typedef struct bh__string {
- u64 length;
- u64 capacity;
-} bh__string;
-
-typedef char bh_string;
-
-#define bh__stringhead(x) (((bh__string *)(x)) - 1)
-
-#define bh_string_new(x) _Generic((x), \
- unsigned long: bh_string_new_cap, \
- unsigned int: bh_string_new_cap, \
- int: bh_string_new_cap, \
- long: bh_string_new_cap, \
- const char*: bh_string_new_str, \
- char*: bh_string_new_str)(x)
-
-#define bh_string_append(str1, str2) _Generic((str2), \
- bh_string*: bh_string_append_bh_string, \
- char*: bh_string_append_cstr, \
- const char*: bh_string_append_cstr)(str1, str2)
-
-#define bh_string_replace_at(dest, src, offset) _Generic((src), \
- bh_string*: bh_string_replace_at_bh_string, \
- char*: bh_string_replace_at_cstr, \
- const char*: bh_string_replace_at_cstr)(dest, src, offset)
-
-#define bh_string_insert_at(dest, src, offset) _Generic((src), \
- bh_string*: bh_string_insert_at_bh_string, \
- char*: bh_string_insert_at_cstr, \
- const char*: bh_string_insert_at_cstr)(dest, src, offset)
-
-bh_string bh_string_new_cap(unsigned long cap);
-bh_string bh_string_new_str(const char* cstr);
-b32 bh_string_delete(bh_string* str);
-b32 bh_string_ensure_capacity(bh_string* str, u64 cap);
-void bh_string_append_bh_string(bh_string* str1, bh_string* str2);
-void bh_string_append_cstr(bh_string* str1, const char* str2);
-void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset);
-void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset);
-void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset);
-void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset);
-void bh_string_trim_end(bh_string* str, const char* charset);
-void bh_string_trim_begin(bh_string* str, const char* charset);
-void bh_string_trim_end_space(bh_string* str);
-// TEMP
-void bh_string_print(bh_string* str);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better files
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_FILE
-
-typedef enum bh_file_error {
- BH_FILE_ERROR_NONE,
- BH_FILE_ERROR_INVALID,
- BH_FILE_ERROR_BAD_FD,
-} bh_file_error;
-
-typedef enum bh_file_mode {
- BH_FILE_MODE_READ = 1 << 0,
- BH_FILE_MODE_WRITE = 1 << 1,
- BH_FILE_MODE_APPEND = 1 << 2,
- BH_FILE_MODE_RW = 1 << 3,
-
- BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
-} bh_file_mode;
-
-typedef enum bh_file_whence {
- BH_FILE_WHENCE_BEGIN = SEEK_SET,
- BH_FILE_WHENCE_CURRENT = SEEK_CUR,
- BH_FILE_WHENCE_END = SEEK_END,
-} bh_file_whence;
-
-typedef int bh_file_descriptor;
-
-typedef struct bh_file {
- bh_file_descriptor fd;
- char const* filename;
-} bh_file;
-
-typedef enum bh_file_standard {
- BH_FILE_STANDARD_INPUT,
- BH_FILE_STANDARD_OUTPUT,
- BH_FILE_STANDARD_ERROR
-} bh_file_standard;
-
-typedef struct bh_file_contents {
- bh_allocator allocator;
- const char *filename;
- isize length;
- void* data;
-} bh_file_contents;
-
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
-
-bh_file_error bh_file_create(bh_file* file, char const* filename);
-bh_file_error bh_file_open(bh_file* file, char const* filename);
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
-static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset);
-i64 bh_file_seek_to_end(bh_file* file);
-i64 bh_file_skip(bh_file* file, i64 bytes);
-i64 bh_file_tell(bh_file* file);
-bh_file_error bh_file_close(bh_file* file);
-i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
-i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
-i64 bh_file_size(bh_file* file);
-
-#define bh_file_read_contents(allocator_, x) _Generic((x), \
- bh_file*: bh_file_read_contents_bh_file, \
- const char*: bh_file_read_contents_direct, \
- char*: bh_file_read_contents_direct)((allocator_), x)
-
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
-i32 bh_file_contents_delete(bh_file_contents* contents);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Alternate printing
-//-------------------------------------------------------------------------------------
-// Barebones implementation of printf. Does not support all format options
-// Currently supports:
-// %c - chars
-// %_(u)d - ints where _ is:
-// nothing - decimal
-// o - octal
-// x - hexadecimal
-// %_(u)l - longs where _ is:
-// nothing - decimal
-// o - octal
-// x - hexadecimal
-// %f - floating points
-// %s - null terminated strings
-// %p - pointers
-// %% - literal %
-
-typedef struct bh__print_format {
- u32 base;
-} bh__print_format;
-
-isize bh_printf(char const *fmt, ...);
-isize bh_printf_va(char const *fmt, va_list va);
-isize bh_printf_err(char const *fmt, ...);
-isize bh_printf_err_va(char const *fmt, va_list va);
-isize bh_fprintf(bh_file* f, char const *fmt, ...);
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
-char* bh_bprintf(char const *fmt, ...);
-char* bh_bprintf_va(char const *fmt, va_list va);
-isize bh_snprintf(char *str, isize n, char const *fmt, ...);
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better debug functions
-//-------------------------------------------------------------------------------------
-#ifdef BH_DEBUG
-
-void* bh__debug_malloc(size_t size, const char* file, u64 line);
-void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line);
-void bh__debug_free(void* ptr, const char* file, u64 line);
-void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line);
-
-#ifdef BH_DEFINE
-
-void* bh__debug_malloc(size_t size, const char* file, u64 line) {
- void* p = malloc(size);
- bh_printf("[DEBUG] %p = malloc(%d) at %s:%d\n", p, size, file, line);
- return p;
-}
-
-void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line) {
- void* p = aligned_alloc(size, alignment);
- bh_printf("[DEBUG] %p = aligned_alloc(%d, %d) at %s:%d\n", p, alignment, size, file, line);
- return p;
-}
-
-void bh__debug_free(void* ptr, const char* file, u64 line) {
- bh_printf("[DEBUG] free(%p) at %s:%d\n", ptr, file, line);
- free(ptr);
-}
-
-void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line) {
- void* p = realloc(ptr, size);
- bh_printf("[DEBUG] %p = realloc(%p, %d) at %s:%d\n", p, ptr, size, file, line);
- return p;
-}
-
-#endif
-
-#define malloc(size) (bh__debug_malloc(size, __FILE__, __LINE__))
-#define aligned_alloc(size, alignment) (bh__debug_aligned_alloc(size, alignment, __FILE__, __LINE__))
-#define free(ptr) (bh__debug_free(ptr, __FILE__, __LINE__))
-#define realloc(ptr, size) (bh__debug_realloc(ptr, size, __FILE__, __LINE__))
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better dynamically-sized arrays
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_ARRAY
-
-typedef struct bh__arr {
- bh_allocator allocator;
- i32 length, capacity;
-} bh__arr;
-
-#ifndef BH_ARR_GROW_FORMULA
-#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4)
-#endif
-
-#define bh_arr(T) T*
-#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1)
-
-#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
-#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0)
-#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0)
-#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
-#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
-
-#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length])
-#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1])
-#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)])
-
-#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_free(arr) (bh__arr_free((void**) &(arr)))
-#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
-
-#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_set_length(arr, n) ( \
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), n), \
- bh__arrhead(arr)->length = n)
-
-#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
-
-#define bh_arr_insert_end(arr, n) ( \
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
- bh__arrhead(arr)->length += n)
-
-#define bh_arr_push(arr, value) ( \
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1), \
- arr[bh__arrhead(arr)->length++] = value)
-
-#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1)
-#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0)
-
-#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
-#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length])
-
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
-b32 bh__arr_free(void **arr);
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// HASH TABLE FUNCTIONS
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_HASHTABLE
-
-#define BH__HASH_STORED_KEY_SIZE 64
-typedef struct bh__hash_entry {
- char key[BH__HASH_STORED_KEY_SIZE];
- i32 value; // NOTE: Not actually an i32, just used as a placeholder for offset
-} bh__hash_entry;
-
-#define BH__HASH_MODULUS 1021
-#define BH__HASH_KEYSIZE 64
-#ifdef BH_DEFINE
-u64 bh__hash_function(const char* str, i32 len) {
- u64 hash = 5381;
- i32 c, l = 0;
- if (len == 0) len = BH__HASH_KEYSIZE;
-
- while ((c = *str++) && l++ < len) {
- hash = (hash << 5) + hash + c;
- }
-
- return hash % BH__HASH_MODULUS;
-}
-#endif
-
-typedef struct bh_hash_iterator {
- ptr *tab, *endtab;
- i32 elemsize, arrlen;
- bh__hash_entry* entry;
-} bh_hash_iterator;
-
-typedef struct bh__hash {
- bh_allocator allocator;
- ptr arrs[BH__HASH_MODULUS];
-} bh__hash;
-
-#define bh_hash(T) T*
-
-#ifdef BH_HASH_SIZE_SAFE
- #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))
- #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))
- #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = (T) value))
- #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((bh__hash *) tab, sizeof(T), key)))
- #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key))))
- #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((bh__hash *) tab, sizeof(T), key))
-
- #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))
- #define bh_hash_iter_key(it) (it.entry->key)
- #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value))
-#else
- #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))
- #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))
- #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = value)
- #define bh_hash_has(T, tab, key) (bh__hash_has((bh__hash *) tab, sizeof(T), key))
- #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key)))
- #define bh_hash_delete(T, tab, key) (bh__hash_delete((bh__hash *) tab, sizeof(T), key))
-
- #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))
- #define bh_hash_iter_key(it) (it.entry->key)
- #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value))
-#endif
-
-b32 bh__hash_init(bh_allocator allocator, bh__hash **table);
-b32 bh__hash_free(bh__hash **table);
-ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key);
-b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key);
-ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key);
-void bh__hash_delete(bh__hash *table, i32 elemsize, char *key);
-bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize);
-b32 bh_hash_iter_next(bh_hash_iterator* it);
-
-#endif
-
-#ifdef BH_DEFINE
-#undef BH_DEFINE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// IMPLEMENTATIONS
-//-------------------------------------------------------------------------------------
-
-//-------------------------------------------------------------------------------------
-// CHAR FUNCTIONS
-//-------------------------------------------------------------------------------------
-b32 char_is_alpha(const char a) {
- return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
-}
-
-b32 char_is_num(const char a) {
- return ('0' <= a && a <= '9');
-}
-
-b32 char_is_alphanum(const char a) {
- return char_is_alpha(a) || char_is_num(a);
-}
-
-b32 char_is_whitespace(const char a) {
- return charset_contains(" \t\r\n", a);
-}
-
-b32 char_in_range(const char lo, const char hi, const char a) {
- return lo <= a <= hi;
-}
-
-char charset_contains(const char* charset, char ch) {
- while (*charset) {
- if (*charset == ch) return ch;
- charset++;
- }
-
- return 0;
-}
-
-i64 chars_match(char* ptr1, char* ptr2) {
- i64 len = 0;
- while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
- return *ptr2 == '\0' ? len : 0;
-}
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// CUSTOM ALLOCATORS IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-
-
-ptr bh_alloc(bh_allocator a, isize size) {
- return bh_alloc_aligned(a, size, 16);
-}
-
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
- return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0);
-}
-
-ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
- return bh_resize_aligned(a, data, new_size, 16);
-}
-
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
- return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
-}
-
-void bh_free(bh_allocator a, ptr data) {
- if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
-}
-
-
-
-// HEAP ALLOCATOR IMPLEMENTATION
-
-bh_allocator bh_heap_allocator(void) {
- return (bh_allocator) {
- .proc = bh_heap_allocator_proc,
- .data = NULL
- };
-}
-
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
- retval = aligned_alloc(alignment, size);
-
- if (flags & bh_allocator_flag_clear && retval != NULL) {
- memset(retval, 0, size);
- }
- } break;
-
- case bh_allocator_action_resize: {
- // TODO: Maybe replace with better custom function
- retval = realloc(prev_memory, size);
- } break;
-
- case bh_allocator_action_free: {
- free(prev_memory);
- } break;
- }
-
- return retval;
-}
-
-
-
-
-
-
-
-// ARENA ALLOCATOR IMPLEMENTATION
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
- arena_size = bh_max(arena_size, sizeof(ptr));
- ptr data = bh_alloc(backing, arena_size);
-
- alloc->backing = backing;
- alloc->arena_size = arena_size;
- alloc->size = sizeof(ptr);
- alloc->first_arena = data;
- alloc->current_arena = data;
-
- ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
-}
-
-void bh_arena_free(bh_arena* alloc) {
- bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
- bh__arena_internal *trailer = walker;
- while (walker != NULL) {
- walker = walker->next_arena;
- bh_free(alloc->backing, trailer);
- trailer = walker;
- }
-
- alloc->first_arena = NULL;
- alloc->current_arena = NULL;
- alloc->arena_size = 0;
- alloc->size = 0;
-}
-
-bh_allocator bh_arena_allocator(bh_arena* alloc) {
- return (bh_allocator) {
- .proc = bh_arena_allocator_proc,
- .data = alloc,
- };
-}
-
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
- bh_arena* alloc_arena = (bh_arena*) data;
-
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
-
- // TODO: Do this better because right now bh__align is bad
- // size = bh__align(size, alignment);
- if (size > alloc_arena->arena_size - sizeof(ptr)) {
- // Size too large for the arena
- return NULL;
- }
-
- if (alloc_arena->size + size >= alloc_arena->arena_size) {
- alloc_arena->size = sizeof(ptr);
- bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
-
- if (new_arena == NULL) {
- bh_printf_err("Arena Allocator: couldn't allocate new arena");
- return NULL;
- }
-
- new_arena->next_arena = NULL;
- ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
- alloc_arena->current_arena = new_arena;
- }
-
- retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
- alloc_arena->size += size;
- } break;
-
- case bh_allocator_action_resize: {
- // Do nothing since this is a fixed allocator
- } break;
-
- case bh_allocator_action_free: {
- // Do nothing since this allocator isn't made for freeing memory
- } break;
- }
-
- return retval;
-}
-
-
-
-
-// SCRATCH ALLOCATOR IMPLEMENTATION
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
- ptr memory = bh_alloc(backing, scratch_size);
-
- scratch->backing = backing;
- scratch->memory = memory;
- scratch->curr = memory;
- scratch->end = memory + scratch_size;
-}
-
-void bh_scratch_free(bh_scratch* scratch) {
- bh_free(scratch->backing, scratch->memory);
-
- scratch->memory = NULL;
- scratch->curr = NULL;
- scratch->end = NULL;
-}
-
-bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
- return (bh_allocator) {
- .proc = bh_scratch_allocator_proc,
- .data = scratch,
- };
-}
-
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
- bh_scratch* scratch = (bh_scratch*) data;
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
- if (size > scratch->end - scratch->memory) {
- return NULL;
- }
-
- retval = scratch->curr;
- scratch->curr += size;
-
- if (scratch->curr >= scratch->end) {
- scratch->curr = scratch->memory;
- retval = scratch->curr;
- }
- } break;
-
- case bh_allocator_action_free:
- case bh_allocator_action_resize:
- // Do nothing
- break;
- }
-
- return retval;
-}
-
-
-//-------------------------------------------------------------------------------------
-// STRING IMPLEMENTATION (BROKEN)
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_STRING
-
-bh_string* bh_string_new_cap(unsigned long cap) {
- bh__string* str;
- str = (bh__string*) malloc(sizeof(*str) + sizeof(char) * cap + 1);
- str[0] = 0;
- return str + 1;
-}
-
-bh_string* bh_string_new_str(const char* cstr) {
- const i32 len = strlen(cstr);
- bh__string* str;
- i32 i;
-
- str = malloc(sizeof(*str) + sizeof(char) * len + 1);
- char* data = (char*) (str + 1);
- for (i = 0; i < len; i++) {
- data[i] = cstr[i];
- }
-
- data[len] = 0; // Always null terminate the string
-
- str->length = len;
- str->capacity = len;
- return str + 1;
-}
-
-b32 bh_string_delete(bh_string** str) {
- bh__string* strptr = bh__stringhead(*str);
- free(strptr);
- str->length = 0;
- str->capacity = 0;
- return 1;
-}
-
-b32 bh_string_grow(bh_string** str, u64 cap) {
- bh__string* strptr = bh__stringhead(*str);
- if (strptr->capacity >= cap) return 1;
-
- void* p;
- p = realloc(strptr, sizeof(*strptr) + sizeof(char) * cap + 1);
-
- strptr->capacity = cap;
-
- return 1;
-}
-
-void bh_string_append_bh_string(bh_string** str1, bh_string** str2) {
- if (!bh_string_ensure_capacity(str1, str1->length + str2->length)) return;
-
- //TODO: Replace with custom memory management
- memcpy(str1->data + str1->length, str2->data, str2->length);
- str1->length += str2->length;
-}
-
-void bh_string_append_cstr(bh_string* str1, const char* str2) {
- const i32 str2len = strlen(str2);
- if (!bh_string_ensure_capacity(str1, str1->length + str2len)) return;
-
- //TODO: Replace with custom memory management
- memcpy(str1->data + str1->length, str2, str2len);
- str1->length += str2len;
-}
-
-void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {
- if (offset > dest->length) return;
- if (!bh_string_ensure_capacity(dest, offset + src->length)) return;
-
- memcpy(dest->data + offset, src->data, src->length);
- if (offset + src->length > dest->length)
- dest->length = offset + src->length;
-}
-
-void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset) {
- if (offset > dest->length) return;
- const i32 srclen = strlen(src);
- if (!bh_string_ensure_capacity(dest, offset + srclen)) return;
-
- memcpy(dest->data + offset, src, srclen);
- if (offset + srclen > dest->length)
- dest->length = offset + srclen;
-}
-
-void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {
- if (!bh_string_ensure_capacity(dest, dest->length + src->length)) return;
-
- memmove(dest->data + offset + src->length, dest->data + offset, dest->length + src->length - offset);
- memcpy(dest->data + offset, src->data, src->length);
- dest->length += src->length;
-}
-
-void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset) {
- const i32 srclen = strlen(src);
- if (!bh_string_ensure_capacity(dest, dest->length + srclen)) return;
-
- // TODO: Use something better. This copies to a seperate buffer first
- memmove(dest->data + offset + srclen, dest->data + offset, dest->length + srclen - offset);
- memcpy(dest->data + offset, src, srclen);
- dest->length += srclen;
-}
-
-
-void bh_string_trim_end(bh_string* str, const char* charset) {
- while (charset_contains(charset, str->data[str->length - 1]))
- str->length--;
-}
-
-void bh_string_trim_begin(bh_string* str, const char* charset) {
- u32 off = 0, i;
- while (charset_contains(charset, str->data[off])) off++;
-
- if (off == 0) return;
-
- for (i = 0; i < str->length - off; i++) {
- str->data[i] = str->data[i + off];
- }
-
- str->length -= off;
-}
-
-void bh_string_trim_end_space(bh_string* str) {
- bh_string_trim_end(str, " \t\n\r");
-}
-
-// TEMP
-void bh_string_print(bh_string* str) {
- write(STDOUT_FILENO, str->data, str->length);
-}
-
-#endif // ifndef BH_NO_STRING
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// FILE IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_FILE
-
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
- i32 sd_fd = -1;
- const char* filename = NULL;
-
- switch (stand) {
- case BH_FILE_STANDARD_INPUT:
- sd_fd = STDIN_FILENO;
- filename = "stdin"; // These are constants in the data section so everything should be okay
- break;
- case BH_FILE_STANDARD_OUTPUT:
- sd_fd = STDOUT_FILENO;
- filename = "stdout";
- break;
- case BH_FILE_STANDARD_ERROR:
- sd_fd = STDERR_FILENO;
- filename = "stderr";
- break;
- default:
- return BH_FILE_ERROR_BAD_FD;
- }
-
- file->fd = sd_fd;
- file->filename = filename;
-
- return BH_FILE_ERROR_NONE;
-}
-
-bh_file_error bh_file_create(bh_file* file, const char* filename) {
- // Need to do this to avoid compiler complaining about types
- bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
- return bh_file_open_mode(file, write_rw, filename);
-}
-
-bh_file_error bh_file_open(bh_file* file, const char* filename) {
- return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
-}
-
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
-
- i32 os_mode = 0;
-
- switch (mode & BH_FILE_MODE_MODES) {
- case BH_FILE_MODE_READ: os_mode = O_RDONLY; break;
- case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
- case BH_FILE_MODE_APPEND: os_mode = O_RDONLY | O_APPEND | O_CREAT; break;
- case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break;
- case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
- case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break;
- //default: // TODO Handle errors
- }
-
- file->fd = open(filename, os_mode,
- S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
- );
- if (file->fd < 0) {
- return BH_FILE_ERROR_INVALID;
- }
-
- // TODO: Set this using some allocator
- file->filename = filename;
-
- return BH_FILE_ERROR_NONE;
-}
-
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
- file->filename = filename; // This may be unsafe
- file->fd = fd;
- return BH_FILE_ERROR_NONE;
-}
-
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
- isize res = pread(file->fd, buffer, buff_size, offset);
- if (res < 0) return 0;
- if (bytes_read) *bytes_read = res;
- return 1;
-}
-
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
- isize res;
- i64 current_offset = 0;
- bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_CURRENT, ¤t_offset);
- if (current_offset == offset) {
- // Standard in and out do like pwrite()
- res = write(file->fd, buffer, buff_size);
- } else {
- res = pwrite(file->fd, buffer, buff_size, offset);
- }
- if (res < 0) return 0;
- if (bytes_wrote) *bytes_wrote = res;
-
- return 1;
-}
-
-static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset) {
- i64 res = lseek(fd, offset, whence);
- if (res < 0) return 0;
- if (new_offset) *new_offset = res;
- return 1;
-}
-
-// Returns new offset
-i64 bh_file_seek_to(bh_file* file, i64 offset) {
- i64 new_offset = -1;
- bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_seek_to_end(bh_file* file) {
- i64 new_offset = -1;
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_skip(bh_file* file, i64 bytes) {
- i64 new_offset = 0;
- bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_tell(bh_file* file) {
- i64 new_offset = 0;
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
- return new_offset;
-}
-
-bh_file_error bh_file_close(bh_file* file) {
- bh_file_error err = BH_FILE_ERROR_NONE;
- i32 res = close(file->fd);
- if (res < 0)
- err = BH_FILE_ERROR_INVALID;
-
- return err;
-}
-
-b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
- return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
-}
-
-b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
- return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
-}
-
-i64 bh_file_size(bh_file* file) {
- i64 size = 0;
- i64 prev = bh_file_tell(file);
- bh_file_seek_to_end(file);
- size = bh_file_tell(file);
- bh_file_seek_to(file, prev);
- return size;
-}
-
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
- bh_file_contents fc = {
- .allocator = alloc,
- .filename = file->filename,
- .length = 0, .data = NULL
- };
-
- isize size = bh_file_size(file);
- if (size <= 0) return fc;
-
- fc.data = bh_alloc(alloc, size + 1);
- fc.length = size;
- bh_file_read_at(file, 0, fc.data, fc.length, NULL);
- ((u8*) fc.data)[fc.length] = '\0';
-
- return fc;
-}
-
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
- bh_file file;
- bh_file_open(&file, filename);
- bh_file_contents fc = bh_file_read_contents(alloc, &file);
- bh_file_close(&file);
- return fc;
-}
-
-b32 bh_file_contents_delete(bh_file_contents* contents) {
- bh_free(contents->allocator, contents->data);
- contents->length = 0;
- return 1;
-}
-
-#endif // ifndef BH_NO_FILE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// ALTERNATE PRINTF IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-isize bh_printf(char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_printf_va(fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh_printf_va(char const *fmt, va_list va) {
- bh_file file;
- bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
- return bh_fprintf_va(&file, fmt, va);
-}
-
-isize bh_printf_err(char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_printf_err_va(fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh_printf_err_va(char const *fmt, va_list va) {
- bh_file file;
- bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
- return bh_fprintf_va(&file, fmt, va);
-}
-
-isize bh_fprintf(bh_file* f, char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_fprintf_va(f, fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
- static char buf[4096];
- isize len = bh_snprintf_va(buf, sizeof(buf), fmt, va);
- bh_file_write(f, buf, len - 1);
- return len;
-}
-
-char* bh_bprintf(char const *fmt, ...) {
- char* res;
- va_list va;
- va_start(va, fmt);
- res = bh_bprintf_va(fmt, va);
- va_end(va);
- return res;
-}
-
-char* bh_bprintf_va(char const *fmt, va_list va) {
- static char buffer[4096];
- bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
- return buffer;
-}
-
-isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_snprintf_va(str, n, fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh__print_string(char* dest, isize n, char* src) {
- isize len = 0;
- while (n-- && (*dest++ = *src++)) len++;
- return len;
-}
-
-isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
- char buf[128];
- buf[127] = 0;
- char* walker = buf + 127;
- u32 base = format.base ? format.base : 10, tmp;
-
- while (value > 0) {
- tmp = value % base;
- if (tmp > 9) {
- switch (tmp) {
- case 10: tmp = 'a'; break;
- case 11: tmp = 'b'; break;
- case 12: tmp = 'c'; break;
- case 13: tmp = 'd'; break;
- case 14: tmp = 'e'; break;
- case 15: tmp = 'f'; break;
- }
- } else {
- tmp += '0';
- }
-
- *--walker = tmp;
- value /= base;
- }
-
- if (format.base == 16) {
- *--walker = 'x';
- *--walker = '0';
- }
-
- return bh__print_string(str, n, walker);
-}
-
-isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
- char buf[128];
- buf[127] = 0;
- char* walker = buf + 127;
- u32 base = format.base ? format.base : 10, tmp;
-
- b32 negative = value < 0;
- if (negative) value = -value;
-
- if (value == 0) {
- *--walker = '0';
- } else {
- while (value > 0) {
- tmp = value % base;
- if (tmp > 9) {
- switch (tmp) {
- case 10: tmp = 'a'; break;
- case 11: tmp = 'b'; break;
- case 12: tmp = 'c'; break;
- case 13: tmp = 'd'; break;
- case 14: tmp = 'e'; break;
- case 15: tmp = 'f'; break;
- }
- } else {
- tmp += '0';
- }
-
- *--walker = tmp;
- value /= base;
- }
- }
-
- if (negative) {
- *--walker = '-';
- }
-
- if (format.base == 16) {
- *--walker = 'x';
- *--walker = '0';
- }
-
- return bh__print_string(str, n, walker);
-}
-
-// TODO: This is very hacked together but for now it will work.
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
- char const *text_start = str;
- isize res;
-
- while (*fmt) {
- bh__print_format format = { 0 };
- isize len = 0;
-
- while (*fmt && *fmt != '%') {
- *(str++) = *(fmt++);
- }
-
- if (!*fmt) goto end_of_format;
-
- fmt++;
-
- switch (*fmt++) {
- case 'o': format.base = 8; break;
- case 'x': format.base = 16; break;
- default: fmt--;
- }
-
- switch (*fmt) {
- case 'c': {
- char c = (char) va_arg(va, int);
- *(str++) = c;
- } break;
-
- case 'd': {
- i64 value = (i64) va_arg(va, int);
- len = bh__printi64(str, n, format, value);
- } break;
-
- case 'l': {
- i64 value = (i64) va_arg(va, long);
- len = bh__printi64(str, n, format, value);
- } break;
-
- case 'p': {
- u64 value = (u64) va_arg(va, ptr);
- format.base = 16;
- len = bh__printu64(str, n, format, value);
- } break;
-
- case 's': {
- char* s = va_arg(va, char *);
- len = bh__print_string(str, n, s);
- } break;
-
- case 'b': { // String with a length (not null terminated)
- char* s = va_arg(va, char *);
- i32 l = va_arg(va, int);
- len = bh__print_string(str, bh_min(l, n), s);
- } break;
-
- default: fmt--;
- }
-
- fmt++;
-
-end_of_format:
- str += len;
- n -= len;
- }
-
- return str - text_start + 1;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// ARRAY IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_ARRAY
-
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
- bh__arr* arrptr;
-
- if (*arr == NULL) {
- if (cap == 0 && elemsize == 0) return 1;
-
- arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
- if (arrptr == NULL) return 0;
-
- arrptr->allocator = alloc;
- arrptr->capacity = cap;
- arrptr->length = 0;
-
- } else {
- arrptr = bh__arrhead(*arr);
-
- if (arrptr->capacity < cap) {
- void* p;
- i32 newcap = arrptr->capacity;
- while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
-
- p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
-
- if (p) {
- arrptr = (bh__arr *) p;
- arrptr->capacity = newcap;
- } else {
- return 0;
- }
- }
- }
-
- *arr = arrptr + 1;
- return 1;
-}
-
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
- if (*arr == NULL) return 0;
-
- bh__arr* arrptr = bh__arrhead(*arr);
- cap = bh_max(cap, arrptr->length);
-
- if (arrptr->capacity > cap) {
- void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
-
- if (p) {
- arrptr = (bh__arr *) p;
- arrptr->capacity = cap;
- } else {
- return 0;
- }
- }
-
- *arr = arrptr + 1;
- return 1;
-}
-
-b32 bh__arr_free(void **arr) {
- bh__arr* arrptr = bh__arrhead(*arr);
- bh_free(arrptr->allocator, arrptr);
- *arr = NULL;
-}
-
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
- bh__arr* arrptr = bh__arrhead(arr);
-
- const i32 cap = arrptr->length;
-
- void* newarr = NULL;
- bh__arr_grow(alloc, &newarr, elemsize, cap);
- bh__arrhead(newarr)->length = cap;
- bh__arrhead(newarr)->capacity = cap;
- memcpy(newarr, arr, elemsize * arrptr->length);
-
- return newarr;
-}
-
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
- bh__arr* arrptr = bh__arrhead(*arr);
-
- if (index >= arrptr->length) return; // Can't delete past the end of the array
- if (numelems <= 0) return; // Can't delete nothing
-
- memmove(
- (char *)(*arr) + elemsize * index, // Target
- (char *)(*arr) + elemsize * (index + numelems), // Source
- elemsize * (arrptr->length - (index + numelems))); // Length
- arrptr->length -= numelems;
-}
-
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
- if (numelems) {
- if (*arr == NULL) {
- bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
- return;
- }
-
- bh__arr* arrptr = bh__arrhead(*arr);
- if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
- memmove(
- (char *)(*arr) + elemsize * (index + numelems),
- (char *)(*arr) + elemsize * index,
- elemsize * (arrptr->length - index));
- arrptr->length += numelems;
- }
-}
-
-#endif // ifndef BH_NO_ARRAY
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// HASHTABLE IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_HASHTABLE
-
-b32 bh__hash_init(bh_allocator allocator, bh__hash **table) {
- *table = bh_alloc(allocator, sizeof(bh__hash));
- if (*table == NULL) return 0;
-
- (*table)->allocator = allocator;
-
- for (i32 i = 0; i < BH__HASH_MODULUS; i++) {
- (*table)->arrs[i] = NULL;
- }
-
- return 1;
-}
-
-b32 bh__hash_free(bh__hash **table) {
- for (i32 i = 0; i < BH__HASH_MODULUS; i++) {
- if ((*table)->arrs[i] != NULL) {
- bh_arr_free((*table)->arrs[i]);
- }
- }
-
- bh_free((*table)->allocator, *table);
- *table = NULL;
-}
-
-// Assumes NULL terminated string for key
-ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key) {
- u64 index = bh__hash_function(key, 0);
-
- elemsize += BH__HASH_STORED_KEY_SIZE;
-
- ptr arrptr = table->arrs[index];
- i32 len = bh_arr_length(arrptr);
-
- while (len--) {
- if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) goto found_matching;
- arrptr = bh_pointer_add(arrptr, elemsize);
- }
-
- // Didn't find it in the array, make a new one
- arrptr = table->arrs[index];
- len = bh_arr_length(arrptr);
- bh__arr_grow(table->allocator, &arrptr, elemsize, len + 1);
- bh__arrhead(arrptr)->length++;
- table->arrs[index] = arrptr;
-
- arrptr = bh_pointer_add(arrptr, elemsize * len);
- strncpy(arrptr, key, BH__HASH_STORED_KEY_SIZE);
-
-found_matching:
- return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);
-}
-
-b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) {
- u64 index = bh__hash_function(key, 0);
-
- ptr arrptr = table->arrs[index];
- if (arrptr == NULL) return 0;
-
- i32 len = bh_arr_length(arrptr);
- i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
-
- while (len--) {
- if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1;
- arrptr = bh_pointer_add(arrptr, stride);
- }
-
- return 0;
-}
-
-ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) {
- u64 index = bh__hash_function(key, 0);
-
- ptr arrptr = table->arrs[index];
- if (arrptr == NULL) return NULL;
-
- i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
-
- i32 len = bh_arr_length(arrptr);
- while (len--) {
- if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) {
- return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);
- }
-
- arrptr = bh_pointer_add(arrptr, stride);
- }
-
- return NULL;
-}
-
-void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) {
- u64 index = bh__hash_function(key, 0);
-
- ptr arrptr = table->arrs[index], walker;
- if (arrptr == NULL) return; // Didn't exist
- walker = arrptr;
-
- i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
- i32 i = 0;
-
- i32 len = bh_arr_length(arrptr);
- while (len && strncmp(key, (char *) walker, BH__HASH_STORED_KEY_SIZE) != 0) {
- walker = bh_pointer_add(walker, stride);
- i++, len--;
- }
-
- if (len == 0) return; // Didn't exist
-
- bh__arr_deleten((void **) &arrptr, stride, i, 1);
- table->arrs[index] = arrptr;
-}
-
-bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize) {
- bh_hash_iterator it = {
- .tab = table->arrs,
- .endtab = table->arrs + BH__HASH_MODULUS,
- .elemsize = elemsize,
- .entry = NULL
- };
- return it;
-}
-
-b32 bh_hash_iter_next(bh_hash_iterator* it) {
- if (it->tab == NULL) return 0;
-
- if (it->entry != NULL) {
- it->arrlen--;
- if (it->arrlen <= 0) {
- it->tab++;
- goto step_to_next;
- }
-
- it->entry = (bh__hash_entry *)bh_pointer_add(it->entry, BH__HASH_STORED_KEY_SIZE + it->elemsize);
- return 1;
- }
-
-step_to_next:
- // Step forward to find next valid
- while (*it->tab == NULL && it->tab != it->endtab) {
- it->tab++;
- }
-
- if (it->tab == it->endtab) return 0;
-
- it->entry = *it->tab;
- it->arrlen = bh_arr_length(it->entry);
- if (it->arrlen <= 0) {
- it->tab++;
- goto step_to_next;
- }
- return 1;
-}
-
-#endif // ifndef BH_NO_HASHTABLE
-
-#endif // ifdef BH_DEFINE
-
-#endif // ifndef BH_H
--- /dev/null
+#ifndef BH_H
+#define BH_H
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h> // TODO: Replace with needed functions
+#include <assert.h>
+
+//-------------------------------------------------------------------------------------
+// Better types
+//-------------------------------------------------------------------------------------
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long u64;
+typedef unsigned long long u128;
+typedef signed char i8;
+typedef signed short i16;
+typedef signed int i32;
+typedef signed long i64;
+typedef signed long long i128;
+typedef unsigned long isize;
+typedef i32 b32;
+typedef void* ptr;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better character functions
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a);
+b32 char_is_num(const char a);
+b32 char_is_alphanum(const char a);
+b32 char_is_whitespace(const char a);
+b32 char_in_range(const char lo, const char hi, const char a);
+char charset_contains(const char* charset, char ch);
+i64 chars_match(char* ptr1, char* ptr2);
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better math functions
+//-------------------------------------------------------------------------------------
+#define bh_max(a, b) ((a) > (b) ? (a) : (b))
+#define bh_min(a, b) ((a) < (b) ? (a) : (b))
+#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))
+#define bh_abs(x) ((x) < 0 ? -(x) : (x))
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Helpful macros
+//-------------------------------------------------------------------------------------
+#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem)
+#define bh_aligh_of(Type) bh_offset_of(struct { char c; Type member; }, member)
+#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
+
+#define bh_pointer_add(ptr, amm) ((void *)((u8 *) ptr + amm))
+#define BH_BIT(x) (1 << (x))
+#define BH_MASK_SET(var, set, mask) ((set) ? (var) |= (mask) : (var) &= ~(mask))
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Custom allocators
+//-------------------------------------------------------------------------------------
+
+typedef enum bh_allocator_actions {
+ bh_allocator_action_alloc,
+ bh_allocator_action_free,
+ bh_allocator_action_resize,
+} bh_allocator_actions;
+
+#define BH_ALLOCATOR_PROC(name) \
+ptr name(ptr data, bh_allocator_actions action, \
+ isize size, isize alignment, \
+ void* prev_memory, \
+ u64 flags)
+
+typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
+
+typedef struct bh_allocator {
+ bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
+ ptr data; // Pointer to the other data for the allocator
+} bh_allocator;
+
+typedef enum bh_allocator_flags {
+ bh_allocator_flag_clear = 1 // Sets all memory to be 0
+} bh_allocator_flags;
+
+ptr bh_alloc(bh_allocator a, isize size);
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
+ptr bh_resize(bh_allocator a, ptr data, isize new_size);
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
+void bh_free(bh_allocator a, ptr data);
+
+#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T))
+#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n))
+
+// NOTE: This should get optimized out since alignment should be a power of two
+#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment)
+
+
+
+
+// HEAP ALLOCATOR
+// Essentially a wrapper for malloc, free and realloc
+bh_allocator bh_heap_allocator(void);
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
+
+
+
+
+
+// ARENA ALLOCATOR
+typedef struct bh_arena {
+ bh_allocator backing;
+ ptr first_arena, current_arena;
+ isize size, arena_size; // in bytes
+} bh_arena;
+
+typedef struct bh__arena_internal {
+ ptr next_arena;
+ void* data; // Not actually a pointer, just used for the offset
+} bh__arena_internal;
+
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
+void bh_arena_free(bh_arena* alloc);
+bh_allocator bh_arena_allocator(bh_arena* alloc);
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+
+
+
+// SCRATCH ALLOCATOR
+typedef struct bh_scratch {
+ bh_allocator backing;
+ ptr memory, end, curr;
+} bh_scratch;
+
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
+void bh_scratch_free(bh_scratch* scratch);
+bh_allocator bh_scratch_allocator(bh_scratch* scratch);
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better strings
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_STRING
+
+typedef struct bh__string {
+ u64 length;
+ u64 capacity;
+} bh__string;
+
+typedef char bh_string;
+
+#define bh__stringhead(x) (((bh__string *)(x)) - 1)
+
+#define bh_string_new(x) _Generic((x), \
+ unsigned long: bh_string_new_cap, \
+ unsigned int: bh_string_new_cap, \
+ int: bh_string_new_cap, \
+ long: bh_string_new_cap, \
+ const char*: bh_string_new_str, \
+ char*: bh_string_new_str)(x)
+
+#define bh_string_append(str1, str2) _Generic((str2), \
+ bh_string*: bh_string_append_bh_string, \
+ char*: bh_string_append_cstr, \
+ const char*: bh_string_append_cstr)(str1, str2)
+
+#define bh_string_replace_at(dest, src, offset) _Generic((src), \
+ bh_string*: bh_string_replace_at_bh_string, \
+ char*: bh_string_replace_at_cstr, \
+ const char*: bh_string_replace_at_cstr)(dest, src, offset)
+
+#define bh_string_insert_at(dest, src, offset) _Generic((src), \
+ bh_string*: bh_string_insert_at_bh_string, \
+ char*: bh_string_insert_at_cstr, \
+ const char*: bh_string_insert_at_cstr)(dest, src, offset)
+
+bh_string bh_string_new_cap(unsigned long cap);
+bh_string bh_string_new_str(const char* cstr);
+b32 bh_string_delete(bh_string* str);
+b32 bh_string_ensure_capacity(bh_string* str, u64 cap);
+void bh_string_append_bh_string(bh_string* str1, bh_string* str2);
+void bh_string_append_cstr(bh_string* str1, const char* str2);
+void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset);
+void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset);
+void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset);
+void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset);
+void bh_string_trim_end(bh_string* str, const char* charset);
+void bh_string_trim_begin(bh_string* str, const char* charset);
+void bh_string_trim_end_space(bh_string* str);
+// TEMP
+void bh_string_print(bh_string* str);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better files
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+typedef enum bh_file_error {
+ BH_FILE_ERROR_NONE,
+ BH_FILE_ERROR_INVALID,
+ BH_FILE_ERROR_BAD_FD,
+} bh_file_error;
+
+typedef enum bh_file_mode {
+ BH_FILE_MODE_READ = 1 << 0,
+ BH_FILE_MODE_WRITE = 1 << 1,
+ BH_FILE_MODE_APPEND = 1 << 2,
+ BH_FILE_MODE_RW = 1 << 3,
+
+ BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
+} bh_file_mode;
+
+typedef enum bh_file_whence {
+ BH_FILE_WHENCE_BEGIN = SEEK_SET,
+ BH_FILE_WHENCE_CURRENT = SEEK_CUR,
+ BH_FILE_WHENCE_END = SEEK_END,
+} bh_file_whence;
+
+typedef int bh_file_descriptor;
+
+typedef struct bh_file {
+ bh_file_descriptor fd;
+ char const* filename;
+} bh_file;
+
+typedef enum bh_file_standard {
+ BH_FILE_STANDARD_INPUT,
+ BH_FILE_STANDARD_OUTPUT,
+ BH_FILE_STANDARD_ERROR
+} bh_file_standard;
+
+typedef struct bh_file_contents {
+ bh_allocator allocator;
+ const char *filename;
+ isize length;
+ void* data;
+} bh_file_contents;
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
+
+bh_file_error bh_file_create(bh_file* file, char const* filename);
+bh_file_error bh_file_open(bh_file* file, char const* filename);
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
+static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset);
+i64 bh_file_seek_to_end(bh_file* file);
+i64 bh_file_skip(bh_file* file, i64 bytes);
+i64 bh_file_tell(bh_file* file);
+bh_file_error bh_file_close(bh_file* file);
+i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
+i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
+i64 bh_file_size(bh_file* file);
+
+#define bh_file_read_contents(allocator_, x) _Generic((x), \
+ bh_file*: bh_file_read_contents_bh_file, \
+ const char*: bh_file_read_contents_direct, \
+ char*: bh_file_read_contents_direct)((allocator_), x)
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
+i32 bh_file_contents_delete(bh_file_contents* contents);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Alternate printing
+//-------------------------------------------------------------------------------------
+// Barebones implementation of printf. Does not support all format options
+// Currently supports:
+// %c - chars
+// %_(u)d - ints where _ is:
+// nothing - decimal
+// o - octal
+// x - hexadecimal
+// %_(u)l - longs where _ is:
+// nothing - decimal
+// o - octal
+// x - hexadecimal
+// %f - floating points
+// %s - null terminated strings
+// %p - pointers
+// %% - literal %
+
+typedef struct bh__print_format {
+ u32 base;
+} bh__print_format;
+
+isize bh_printf(char const *fmt, ...);
+isize bh_printf_va(char const *fmt, va_list va);
+isize bh_printf_err(char const *fmt, ...);
+isize bh_printf_err_va(char const *fmt, va_list va);
+isize bh_fprintf(bh_file* f, char const *fmt, ...);
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
+char* bh_bprintf(char const *fmt, ...);
+char* bh_bprintf_va(char const *fmt, va_list va);
+isize bh_snprintf(char *str, isize n, char const *fmt, ...);
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better debug functions
+//-------------------------------------------------------------------------------------
+#ifdef BH_DEBUG
+
+void* bh__debug_malloc(size_t size, const char* file, u64 line);
+void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line);
+void bh__debug_free(void* ptr, const char* file, u64 line);
+void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line);
+
+#ifdef BH_DEFINE
+
+void* bh__debug_malloc(size_t size, const char* file, u64 line) {
+ void* p = malloc(size);
+ bh_printf("[DEBUG] %p = malloc(%d) at %s:%d\n", p, size, file, line);
+ return p;
+}
+
+void* bh__debug_aligned_alloc(size_t size, size_t alignment, const char* file, u64 line) {
+ void* p = aligned_alloc(size, alignment);
+ bh_printf("[DEBUG] %p = aligned_alloc(%d, %d) at %s:%d\n", p, alignment, size, file, line);
+ return p;
+}
+
+void bh__debug_free(void* ptr, const char* file, u64 line) {
+ bh_printf("[DEBUG] free(%p) at %s:%d\n", ptr, file, line);
+ free(ptr);
+}
+
+void* bh__debug_realloc(void* ptr, size_t size, const char* file, u64 line) {
+ void* p = realloc(ptr, size);
+ bh_printf("[DEBUG] %p = realloc(%p, %d) at %s:%d\n", p, ptr, size, file, line);
+ return p;
+}
+
+#endif
+
+#define malloc(size) (bh__debug_malloc(size, __FILE__, __LINE__))
+#define aligned_alloc(size, alignment) (bh__debug_aligned_alloc(size, alignment, __FILE__, __LINE__))
+#define free(ptr) (bh__debug_free(ptr, __FILE__, __LINE__))
+#define realloc(ptr, size) (bh__debug_realloc(ptr, size, __FILE__, __LINE__))
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better dynamically-sized arrays
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+typedef struct bh__arr {
+ bh_allocator allocator;
+ i32 length, capacity;
+} bh__arr;
+
+#ifndef BH_ARR_GROW_FORMULA
+#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4)
+#endif
+
+#define bh_arr(T) T*
+#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1)
+
+#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
+#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0)
+#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0)
+#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
+#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
+
+#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length])
+#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1])
+#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)])
+
+#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_free(arr) (bh__arr_free((void**) &(arr)))
+#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
+
+#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_set_length(arr, n) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), n), \
+ bh__arrhead(arr)->length = n)
+
+#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
+
+#define bh_arr_insert_end(arr, n) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
+ bh__arrhead(arr)->length += n)
+
+#define bh_arr_push(arr, value) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1), \
+ arr[bh__arrhead(arr)->length++] = value)
+
+#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1)
+#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0)
+
+#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
+#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length])
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_free(void **arr);
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// HASH TABLE FUNCTIONS
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_HASHTABLE
+
+#define BH__HASH_STORED_KEY_SIZE 64
+typedef struct bh__hash_entry {
+ char key[BH__HASH_STORED_KEY_SIZE];
+ i32 value; // NOTE: Not actually an i32, just used as a placeholder for offset
+} bh__hash_entry;
+
+#define BH__HASH_MODULUS 1021
+#define BH__HASH_KEYSIZE 64
+#ifdef BH_DEFINE
+u64 bh__hash_function(const char* str, i32 len) {
+ u64 hash = 5381;
+ i32 c, l = 0;
+ if (len == 0) len = BH__HASH_KEYSIZE;
+
+ while ((c = *str++) && l++ < len) {
+ hash = (hash << 5) + hash + c;
+ }
+
+ return hash % BH__HASH_MODULUS;
+}
+#endif
+
+typedef struct bh_hash_iterator {
+ ptr *tab, *endtab;
+ i32 elemsize, arrlen;
+ bh__hash_entry* entry;
+} bh_hash_iterator;
+
+typedef struct bh__hash {
+ bh_allocator allocator;
+ ptr arrs[BH__HASH_MODULUS];
+} bh__hash;
+
+#define bh_hash(T) T*
+
+#ifdef BH_HASH_SIZE_SAFE
+ #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))
+ #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))
+ #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = (T) value))
+ #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((bh__hash *) tab, sizeof(T), key)))
+ #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key))))
+ #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((bh__hash *) tab, sizeof(T), key))
+
+ #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))
+ #define bh_hash_iter_key(it) (it.entry->key)
+ #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value))
+#else
+ #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab))
+ #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab))
+ #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = value)
+ #define bh_hash_has(T, tab, key) (bh__hash_has((bh__hash *) tab, sizeof(T), key))
+ #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key)))
+ #define bh_hash_delete(T, tab, key) (bh__hash_delete((bh__hash *) tab, sizeof(T), key))
+
+ #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((bh__hash *) tab, sizeof(T)))
+ #define bh_hash_iter_key(it) (it.entry->key)
+ #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value))
+#endif
+
+b32 bh__hash_init(bh_allocator allocator, bh__hash **table);
+b32 bh__hash_free(bh__hash **table);
+ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key);
+b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key);
+ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key);
+void bh__hash_delete(bh__hash *table, i32 elemsize, char *key);
+bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize);
+b32 bh_hash_iter_next(bh_hash_iterator* it);
+
+#endif
+
+#ifdef BH_DEFINE
+#undef BH_DEFINE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// IMPLEMENTATIONS
+//-------------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------
+// CHAR FUNCTIONS
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a) {
+ return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
+}
+
+b32 char_is_num(const char a) {
+ return ('0' <= a && a <= '9');
+}
+
+b32 char_is_alphanum(const char a) {
+ return char_is_alpha(a) || char_is_num(a);
+}
+
+b32 char_is_whitespace(const char a) {
+ return charset_contains(" \t\r\n", a);
+}
+
+b32 char_in_range(const char lo, const char hi, const char a) {
+ return lo <= a <= hi;
+}
+
+char charset_contains(const char* charset, char ch) {
+ while (*charset) {
+ if (*charset == ch) return ch;
+ charset++;
+ }
+
+ return 0;
+}
+
+i64 chars_match(char* ptr1, char* ptr2) {
+ i64 len = 0;
+ while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+ return *ptr2 == '\0' ? len : 0;
+}
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CUSTOM ALLOCATORS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+
+
+ptr bh_alloc(bh_allocator a, isize size) {
+ return bh_alloc_aligned(a, size, 16);
+}
+
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
+ return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0);
+}
+
+ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
+ return bh_resize_aligned(a, data, new_size, 16);
+}
+
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
+ return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
+}
+
+void bh_free(bh_allocator a, ptr data) {
+ if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
+}
+
+
+
+// HEAP ALLOCATOR IMPLEMENTATION
+
+bh_allocator bh_heap_allocator(void) {
+ return (bh_allocator) {
+ .proc = bh_heap_allocator_proc,
+ .data = NULL
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+ retval = aligned_alloc(alignment, size);
+
+ if (flags & bh_allocator_flag_clear && retval != NULL) {
+ memset(retval, 0, size);
+ }
+ } break;
+
+ case bh_allocator_action_resize: {
+ // TODO: Maybe replace with better custom function
+ retval = realloc(prev_memory, size);
+ } break;
+
+ case bh_allocator_action_free: {
+ free(prev_memory);
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+
+
+
+// ARENA ALLOCATOR IMPLEMENTATION
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
+ arena_size = bh_max(arena_size, sizeof(ptr));
+ ptr data = bh_alloc(backing, arena_size);
+
+ alloc->backing = backing;
+ alloc->arena_size = arena_size;
+ alloc->size = sizeof(ptr);
+ alloc->first_arena = data;
+ alloc->current_arena = data;
+
+ ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
+}
+
+void bh_arena_free(bh_arena* alloc) {
+ bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
+ bh__arena_internal *trailer = walker;
+ while (walker != NULL) {
+ walker = walker->next_arena;
+ bh_free(alloc->backing, trailer);
+ trailer = walker;
+ }
+
+ alloc->first_arena = NULL;
+ alloc->current_arena = NULL;
+ alloc->arena_size = 0;
+ alloc->size = 0;
+}
+
+bh_allocator bh_arena_allocator(bh_arena* alloc) {
+ return (bh_allocator) {
+ .proc = bh_arena_allocator_proc,
+ .data = alloc,
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
+ bh_arena* alloc_arena = (bh_arena*) data;
+
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+
+ // TODO: Do this better because right now bh__align is bad
+ // size = bh__align(size, alignment);
+ if (size > alloc_arena->arena_size - sizeof(ptr)) {
+ // Size too large for the arena
+ return NULL;
+ }
+
+ if (alloc_arena->size + size >= alloc_arena->arena_size) {
+ alloc_arena->size = sizeof(ptr);
+ bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
+
+ if (new_arena == NULL) {
+ bh_printf_err("Arena Allocator: couldn't allocate new arena");
+ return NULL;
+ }
+
+ new_arena->next_arena = NULL;
+ ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
+ alloc_arena->current_arena = new_arena;
+ }
+
+ retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
+ alloc_arena->size += size;
+ } break;
+
+ case bh_allocator_action_resize: {
+ // Do nothing since this is a fixed allocator
+ } break;
+
+ case bh_allocator_action_free: {
+ // Do nothing since this allocator isn't made for freeing memory
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+// SCRATCH ALLOCATOR IMPLEMENTATION
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
+ ptr memory = bh_alloc(backing, scratch_size);
+
+ scratch->backing = backing;
+ scratch->memory = memory;
+ scratch->curr = memory;
+ scratch->end = memory + scratch_size;
+}
+
+void bh_scratch_free(bh_scratch* scratch) {
+ bh_free(scratch->backing, scratch->memory);
+
+ scratch->memory = NULL;
+ scratch->curr = NULL;
+ scratch->end = NULL;
+}
+
+bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
+ return (bh_allocator) {
+ .proc = bh_scratch_allocator_proc,
+ .data = scratch,
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
+ bh_scratch* scratch = (bh_scratch*) data;
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+ if (size > scratch->end - scratch->memory) {
+ return NULL;
+ }
+
+ retval = scratch->curr;
+ scratch->curr += size;
+
+ if (scratch->curr >= scratch->end) {
+ scratch->curr = scratch->memory;
+ retval = scratch->curr;
+ }
+ } break;
+
+ case bh_allocator_action_free:
+ case bh_allocator_action_resize:
+ // Do nothing
+ break;
+ }
+
+ return retval;
+}
+
+
+//-------------------------------------------------------------------------------------
+// STRING IMPLEMENTATION (BROKEN)
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_STRING
+
+bh_string* bh_string_new_cap(unsigned long cap) {
+ bh__string* str;
+ str = (bh__string*) malloc(sizeof(*str) + sizeof(char) * cap + 1);
+ str[0] = 0;
+ return str + 1;
+}
+
+bh_string* bh_string_new_str(const char* cstr) {
+ const i32 len = strlen(cstr);
+ bh__string* str;
+ i32 i;
+
+ str = malloc(sizeof(*str) + sizeof(char) * len + 1);
+ char* data = (char*) (str + 1);
+ for (i = 0; i < len; i++) {
+ data[i] = cstr[i];
+ }
+
+ data[len] = 0; // Always null terminate the string
+
+ str->length = len;
+ str->capacity = len;
+ return str + 1;
+}
+
+b32 bh_string_delete(bh_string** str) {
+ bh__string* strptr = bh__stringhead(*str);
+ free(strptr);
+ str->length = 0;
+ str->capacity = 0;
+ return 1;
+}
+
+b32 bh_string_grow(bh_string** str, u64 cap) {
+ bh__string* strptr = bh__stringhead(*str);
+ if (strptr->capacity >= cap) return 1;
+
+ void* p;
+ p = realloc(strptr, sizeof(*strptr) + sizeof(char) * cap + 1);
+
+ strptr->capacity = cap;
+
+ return 1;
+}
+
+void bh_string_append_bh_string(bh_string** str1, bh_string** str2) {
+ if (!bh_string_ensure_capacity(str1, str1->length + str2->length)) return;
+
+ //TODO: Replace with custom memory management
+ memcpy(str1->data + str1->length, str2->data, str2->length);
+ str1->length += str2->length;
+}
+
+void bh_string_append_cstr(bh_string* str1, const char* str2) {
+ const i32 str2len = strlen(str2);
+ if (!bh_string_ensure_capacity(str1, str1->length + str2len)) return;
+
+ //TODO: Replace with custom memory management
+ memcpy(str1->data + str1->length, str2, str2len);
+ str1->length += str2len;
+}
+
+void bh_string_replace_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {
+ if (offset > dest->length) return;
+ if (!bh_string_ensure_capacity(dest, offset + src->length)) return;
+
+ memcpy(dest->data + offset, src->data, src->length);
+ if (offset + src->length > dest->length)
+ dest->length = offset + src->length;
+}
+
+void bh_string_replace_at_cstr(bh_string* dest, const char* src, u64 offset) {
+ if (offset > dest->length) return;
+ const i32 srclen = strlen(src);
+ if (!bh_string_ensure_capacity(dest, offset + srclen)) return;
+
+ memcpy(dest->data + offset, src, srclen);
+ if (offset + srclen > dest->length)
+ dest->length = offset + srclen;
+}
+
+void bh_string_insert_at_bh_string(bh_string* dest, bh_string* src, u64 offset) {
+ if (!bh_string_ensure_capacity(dest, dest->length + src->length)) return;
+
+ memmove(dest->data + offset + src->length, dest->data + offset, dest->length + src->length - offset);
+ memcpy(dest->data + offset, src->data, src->length);
+ dest->length += src->length;
+}
+
+void bh_string_insert_at_cstr(bh_string* dest, const char* src, u64 offset) {
+ const i32 srclen = strlen(src);
+ if (!bh_string_ensure_capacity(dest, dest->length + srclen)) return;
+
+ // TODO: Use something better. This copies to a seperate buffer first
+ memmove(dest->data + offset + srclen, dest->data + offset, dest->length + srclen - offset);
+ memcpy(dest->data + offset, src, srclen);
+ dest->length += srclen;
+}
+
+
+void bh_string_trim_end(bh_string* str, const char* charset) {
+ while (charset_contains(charset, str->data[str->length - 1]))
+ str->length--;
+}
+
+void bh_string_trim_begin(bh_string* str, const char* charset) {
+ u32 off = 0, i;
+ while (charset_contains(charset, str->data[off])) off++;
+
+ if (off == 0) return;
+
+ for (i = 0; i < str->length - off; i++) {
+ str->data[i] = str->data[i + off];
+ }
+
+ str->length -= off;
+}
+
+void bh_string_trim_end_space(bh_string* str) {
+ bh_string_trim_end(str, " \t\n\r");
+}
+
+// TEMP
+void bh_string_print(bh_string* str) {
+ write(STDOUT_FILENO, str->data, str->length);
+}
+
+#endif // ifndef BH_NO_STRING
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FILE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
+ i32 sd_fd = -1;
+ const char* filename = NULL;
+
+ switch (stand) {
+ case BH_FILE_STANDARD_INPUT:
+ sd_fd = STDIN_FILENO;
+ filename = "stdin"; // These are constants in the data section so everything should be okay
+ break;
+ case BH_FILE_STANDARD_OUTPUT:
+ sd_fd = STDOUT_FILENO;
+ filename = "stdout";
+ break;
+ case BH_FILE_STANDARD_ERROR:
+ sd_fd = STDERR_FILENO;
+ filename = "stderr";
+ break;
+ default:
+ return BH_FILE_ERROR_BAD_FD;
+ }
+
+ file->fd = sd_fd;
+ file->filename = filename;
+
+ return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_create(bh_file* file, const char* filename) {
+ // Need to do this to avoid compiler complaining about types
+ bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
+ return bh_file_open_mode(file, write_rw, filename);
+}
+
+bh_file_error bh_file_open(bh_file* file, const char* filename) {
+ return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
+}
+
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
+
+ i32 os_mode = 0;
+
+ switch (mode & BH_FILE_MODE_MODES) {
+ case BH_FILE_MODE_READ: os_mode = O_RDONLY; break;
+ case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
+ case BH_FILE_MODE_APPEND: os_mode = O_RDONLY | O_APPEND | O_CREAT; break;
+ case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break;
+ case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
+ case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break;
+ //default: // TODO Handle errors
+ }
+
+ file->fd = open(filename, os_mode,
+ S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
+ );
+ if (file->fd < 0) {
+ return BH_FILE_ERROR_INVALID;
+ }
+
+ // TODO: Set this using some allocator
+ file->filename = filename;
+
+ return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
+ file->filename = filename; // This may be unsafe
+ file->fd = fd;
+ return BH_FILE_ERROR_NONE;
+}
+
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
+ isize res = pread(file->fd, buffer, buff_size, offset);
+ if (res < 0) return 0;
+ if (bytes_read) *bytes_read = res;
+ return 1;
+}
+
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
+ isize res;
+ i64 current_offset = 0;
+ bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_CURRENT, ¤t_offset);
+ if (current_offset == offset) {
+ // Standard in and out do like pwrite()
+ res = write(file->fd, buffer, buff_size);
+ } else {
+ res = pwrite(file->fd, buffer, buff_size, offset);
+ }
+ if (res < 0) return 0;
+ if (bytes_wrote) *bytes_wrote = res;
+
+ return 1;
+}
+
+static b32 bh__file_seek_wrapper(i32 fd, i64 offset, bh_file_whence whence, i64* new_offset) {
+ i64 res = lseek(fd, offset, whence);
+ if (res < 0) return 0;
+ if (new_offset) *new_offset = res;
+ return 1;
+}
+
+// Returns new offset
+i64 bh_file_seek_to(bh_file* file, i64 offset) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_seek_to_end(bh_file* file) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_skip(bh_file* file, i64 bytes) {
+ i64 new_offset = 0;
+ bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_tell(bh_file* file) {
+ i64 new_offset = 0;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
+ return new_offset;
+}
+
+bh_file_error bh_file_close(bh_file* file) {
+ bh_file_error err = BH_FILE_ERROR_NONE;
+ i32 res = close(file->fd);
+ if (res < 0)
+ err = BH_FILE_ERROR_INVALID;
+
+ return err;
+}
+
+b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
+ return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
+ return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+i64 bh_file_size(bh_file* file) {
+ i64 size = 0;
+ i64 prev = bh_file_tell(file);
+ bh_file_seek_to_end(file);
+ size = bh_file_tell(file);
+ bh_file_seek_to(file, prev);
+ return size;
+}
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
+ bh_file_contents fc = {
+ .allocator = alloc,
+ .filename = file->filename,
+ .length = 0, .data = NULL
+ };
+
+ isize size = bh_file_size(file);
+ if (size <= 0) return fc;
+
+ fc.data = bh_alloc(alloc, size + 1);
+ fc.length = size;
+ bh_file_read_at(file, 0, fc.data, fc.length, NULL);
+ ((u8*) fc.data)[fc.length] = '\0';
+
+ return fc;
+}
+
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
+ bh_file file;
+ bh_file_open(&file, filename);
+ bh_file_contents fc = bh_file_read_contents(alloc, &file);
+ bh_file_close(&file);
+ return fc;
+}
+
+b32 bh_file_contents_delete(bh_file_contents* contents) {
+ bh_free(contents->allocator, contents->data);
+ contents->length = 0;
+ return 1;
+}
+
+#endif // ifndef BH_NO_FILE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ALTERNATE PRINTF IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+isize bh_printf(char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_printf_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_printf_va(char const *fmt, va_list va) {
+ bh_file file;
+ bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
+ return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_printf_err(char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_printf_err_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_printf_err_va(char const *fmt, va_list va) {
+ bh_file file;
+ bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
+ return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_fprintf(bh_file* f, char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_fprintf_va(f, fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
+ static char buf[4096];
+ isize len = bh_snprintf_va(buf, sizeof(buf), fmt, va);
+ bh_file_write(f, buf, len - 1);
+ return len;
+}
+
+char* bh_bprintf(char const *fmt, ...) {
+ char* res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_bprintf_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+char* bh_bprintf_va(char const *fmt, va_list va) {
+ static char buffer[4096];
+ bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+ return buffer;
+}
+
+isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_snprintf_va(str, n, fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh__print_string(char* dest, isize n, char* src) {
+ isize len = 0;
+ while (n-- && (*dest++ = *src++)) len++;
+ return len;
+}
+
+isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
+ char buf[128];
+ buf[127] = 0;
+ char* walker = buf + 127;
+ u32 base = format.base ? format.base : 10, tmp;
+
+ while (value > 0) {
+ tmp = value % base;
+ if (tmp > 9) {
+ switch (tmp) {
+ case 10: tmp = 'a'; break;
+ case 11: tmp = 'b'; break;
+ case 12: tmp = 'c'; break;
+ case 13: tmp = 'd'; break;
+ case 14: tmp = 'e'; break;
+ case 15: tmp = 'f'; break;
+ }
+ } else {
+ tmp += '0';
+ }
+
+ *--walker = tmp;
+ value /= base;
+ }
+
+ if (format.base == 16) {
+ *--walker = 'x';
+ *--walker = '0';
+ }
+
+ return bh__print_string(str, n, walker);
+}
+
+isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
+ char buf[128];
+ buf[127] = 0;
+ char* walker = buf + 127;
+ u32 base = format.base ? format.base : 10, tmp;
+
+ b32 negative = value < 0;
+ if (negative) value = -value;
+
+ if (value == 0) {
+ *--walker = '0';
+ } else {
+ while (value > 0) {
+ tmp = value % base;
+ if (tmp > 9) {
+ switch (tmp) {
+ case 10: tmp = 'a'; break;
+ case 11: tmp = 'b'; break;
+ case 12: tmp = 'c'; break;
+ case 13: tmp = 'd'; break;
+ case 14: tmp = 'e'; break;
+ case 15: tmp = 'f'; break;
+ }
+ } else {
+ tmp += '0';
+ }
+
+ *--walker = tmp;
+ value /= base;
+ }
+ }
+
+ if (negative) {
+ *--walker = '-';
+ }
+
+ if (format.base == 16) {
+ *--walker = 'x';
+ *--walker = '0';
+ }
+
+ return bh__print_string(str, n, walker);
+}
+
+// TODO: This is very hacked together but for now it will work.
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
+ char const *text_start = str;
+ isize res;
+
+ while (*fmt) {
+ bh__print_format format = { 0 };
+ isize len = 0;
+
+ while (*fmt && *fmt != '%') {
+ *(str++) = *(fmt++);
+ }
+
+ if (!*fmt) goto end_of_format;
+
+ fmt++;
+
+ switch (*fmt++) {
+ case 'o': format.base = 8; break;
+ case 'x': format.base = 16; break;
+ default: fmt--;
+ }
+
+ switch (*fmt) {
+ case 'c': {
+ char c = (char) va_arg(va, int);
+ *(str++) = c;
+ } break;
+
+ case 'd': {
+ i64 value = (i64) va_arg(va, int);
+ len = bh__printi64(str, n, format, value);
+ } break;
+
+ case 'l': {
+ i64 value = (i64) va_arg(va, long);
+ len = bh__printi64(str, n, format, value);
+ } break;
+
+ case 'p': {
+ u64 value = (u64) va_arg(va, ptr);
+ format.base = 16;
+ len = bh__printu64(str, n, format, value);
+ } break;
+
+ case 's': {
+ char* s = va_arg(va, char *);
+ len = bh__print_string(str, n, s);
+ } break;
+
+ case 'b': { // String with a length (not null terminated)
+ char* s = va_arg(va, char *);
+ i32 l = va_arg(va, int);
+ len = bh__print_string(str, bh_min(l, n), s);
+ } break;
+
+ default: fmt--;
+ }
+
+ fmt++;
+
+end_of_format:
+ str += len;
+ n -= len;
+ }
+
+ return str - text_start + 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ARRAY IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
+ bh__arr* arrptr;
+
+ if (*arr == NULL) {
+ if (cap == 0 && elemsize == 0) return 1;
+
+ arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
+ if (arrptr == NULL) return 0;
+
+ arrptr->allocator = alloc;
+ arrptr->capacity = cap;
+ arrptr->length = 0;
+
+ } else {
+ arrptr = bh__arrhead(*arr);
+
+ if (arrptr->capacity < cap) {
+ void* p;
+ i32 newcap = arrptr->capacity;
+ while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
+
+ p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
+
+ if (p) {
+ arrptr = (bh__arr *) p;
+ arrptr->capacity = newcap;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ *arr = arrptr + 1;
+ return 1;
+}
+
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
+ if (*arr == NULL) return 0;
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ cap = bh_max(cap, arrptr->length);
+
+ if (arrptr->capacity > cap) {
+ void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
+
+ if (p) {
+ arrptr = (bh__arr *) p;
+ arrptr->capacity = cap;
+ } else {
+ return 0;
+ }
+ }
+
+ *arr = arrptr + 1;
+ return 1;
+}
+
+b32 bh__arr_free(void **arr) {
+ bh__arr* arrptr = bh__arrhead(*arr);
+ bh_free(arrptr->allocator, arrptr);
+ *arr = NULL;
+}
+
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
+ bh__arr* arrptr = bh__arrhead(arr);
+
+ const i32 cap = arrptr->length;
+
+ void* newarr = NULL;
+ bh__arr_grow(alloc, &newarr, elemsize, cap);
+ bh__arrhead(newarr)->length = cap;
+ bh__arrhead(newarr)->capacity = cap;
+ memcpy(newarr, arr, elemsize * arrptr->length);
+
+ return newarr;
+}
+
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
+ bh__arr* arrptr = bh__arrhead(*arr);
+
+ if (index >= arrptr->length) return; // Can't delete past the end of the array
+ if (numelems <= 0) return; // Can't delete nothing
+
+ memmove(
+ (char *)(*arr) + elemsize * index, // Target
+ (char *)(*arr) + elemsize * (index + numelems), // Source
+ elemsize * (arrptr->length - (index + numelems))); // Length
+ arrptr->length -= numelems;
+}
+
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
+ if (numelems) {
+ if (*arr == NULL) {
+ bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
+ return;
+ }
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
+ memmove(
+ (char *)(*arr) + elemsize * (index + numelems),
+ (char *)(*arr) + elemsize * index,
+ elemsize * (arrptr->length - index));
+ arrptr->length += numelems;
+ }
+}
+
+#endif // ifndef BH_NO_ARRAY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// HASHTABLE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_HASHTABLE
+
+b32 bh__hash_init(bh_allocator allocator, bh__hash **table) {
+ *table = bh_alloc(allocator, sizeof(bh__hash));
+ if (*table == NULL) return 0;
+
+ (*table)->allocator = allocator;
+
+ for (i32 i = 0; i < BH__HASH_MODULUS; i++) {
+ (*table)->arrs[i] = NULL;
+ }
+
+ return 1;
+}
+
+b32 bh__hash_free(bh__hash **table) {
+ for (i32 i = 0; i < BH__HASH_MODULUS; i++) {
+ if ((*table)->arrs[i] != NULL) {
+ bh_arr_free((*table)->arrs[i]);
+ }
+ }
+
+ bh_free((*table)->allocator, *table);
+ *table = NULL;
+}
+
+// Assumes NULL terminated string for key
+ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ elemsize += BH__HASH_STORED_KEY_SIZE;
+
+ ptr arrptr = table->arrs[index];
+ i32 len = bh_arr_length(arrptr);
+
+ while (len--) {
+ if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) goto found_matching;
+ arrptr = bh_pointer_add(arrptr, elemsize);
+ }
+
+ // Didn't find it in the array, make a new one
+ arrptr = table->arrs[index];
+ len = bh_arr_length(arrptr);
+ bh__arr_grow(table->allocator, &arrptr, elemsize, len + 1);
+ bh__arrhead(arrptr)->length++;
+ table->arrs[index] = arrptr;
+
+ arrptr = bh_pointer_add(arrptr, elemsize * len);
+ strncpy(arrptr, key, BH__HASH_STORED_KEY_SIZE);
+
+found_matching:
+ return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);
+}
+
+b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) return 0;
+
+ i32 len = bh_arr_length(arrptr);
+ i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
+
+ while (len--) {
+ if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1;
+ arrptr = bh_pointer_add(arrptr, stride);
+ }
+
+ return 0;
+}
+
+ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) return NULL;
+
+ i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
+
+ i32 len = bh_arr_length(arrptr);
+ while (len--) {
+ if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) {
+ return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE);
+ }
+
+ arrptr = bh_pointer_add(arrptr, stride);
+ }
+
+ return NULL;
+}
+
+void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) {
+ u64 index = bh__hash_function(key, 0);
+
+ ptr arrptr = table->arrs[index], walker;
+ if (arrptr == NULL) return; // Didn't exist
+ walker = arrptr;
+
+ i32 stride = elemsize + BH__HASH_STORED_KEY_SIZE;
+ i32 i = 0;
+
+ i32 len = bh_arr_length(arrptr);
+ while (len && strncmp(key, (char *) walker, BH__HASH_STORED_KEY_SIZE) != 0) {
+ walker = bh_pointer_add(walker, stride);
+ i++, len--;
+ }
+
+ if (len == 0) return; // Didn't exist
+
+ bh__arr_deleten((void **) &arrptr, stride, i, 1);
+ table->arrs[index] = arrptr;
+}
+
+bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize) {
+ bh_hash_iterator it = {
+ .tab = table->arrs,
+ .endtab = table->arrs + BH__HASH_MODULUS,
+ .elemsize = elemsize,
+ .entry = NULL
+ };
+ return it;
+}
+
+b32 bh_hash_iter_next(bh_hash_iterator* it) {
+ if (it->tab == NULL) return 0;
+
+ if (it->entry != NULL) {
+ it->arrlen--;
+ if (it->arrlen <= 0) {
+ it->tab++;
+ goto step_to_next;
+ }
+
+ it->entry = (bh__hash_entry *)bh_pointer_add(it->entry, BH__HASH_STORED_KEY_SIZE + it->elemsize);
+ return 1;
+ }
+
+step_to_next:
+ // Step forward to find next valid
+ while (*it->tab == NULL && it->tab != it->endtab) {
+ it->tab++;
+ }
+
+ if (it->tab == it->endtab) return 0;
+
+ it->entry = *it->tab;
+ it->arrlen = bh_arr_length(it->entry);
+ if (it->arrlen <= 0) {
+ it->tab++;
+ goto step_to_next;
+ }
+ return 1;
+}
+
+#endif // ifndef BH_NO_HASHTABLE
+
+#endif // ifdef BH_DEFINE
+
+#endif // ifndef BH_H
--- /dev/null
+#ifndef ONYXLEX_H
+#define ONYXLEX_H
+
+#include "bh.h"
+
+typedef enum OnyxTokenType {
+ TOKEN_TYPE_UNKNOWN,
+ TOKEN_TYPE_END_STREAM,
+
+ TOKEN_TYPE_COMMENT,
+
+ TOKEN_TYPE_KEYWORD_STRUCT,
+ TOKEN_TYPE_KEYWORD_USE,
+ TOKEN_TYPE_KEYWORD_EXPORT,
+ TOKEN_TYPE_KEYWORD_IF,
+ TOKEN_TYPE_KEYWORD_ELSE,
+ TOKEN_TYPE_KEYWORD_FOR,
+ TOKEN_TYPE_KEYWORD_DO,
+ TOKEN_TYPE_KEYWORD_RETURN,
+ TOKEN_TYPE_KEYWORD_CONST,
+ TOKEN_TYPE_KEYWORD_FOREIGN,
+ TOKEN_TYPE_KEYWORD_PROC,
+ TOKEN_TYPE_KEYWORD_GLOBAL,
+ TOKEN_TYPE_KEYWORD_CAST,
+
+ TOKEN_TYPE_RIGHT_ARROW,
+ TOKEN_TYPE_LEFT_ARROW,
+ TOKEN_TYPE_OPEN_PAREN,
+ TOKEN_TYPE_CLOSE_PAREN,
+ TOKEN_TYPE_OPEN_BRACE,
+ TOKEN_TYPE_CLOSE_BRACE,
+ TOKEN_TYPE_OPEN_BRACKET,
+ TOKEN_TYPE_CLOSE_BRACKET,
+ TOKEN_TYPE_OPEN_ANGLE,
+ TOKEN_TYPE_CLOSE_ANGLE,
+
+ TOKEN_TYPE_SYM_PLUS,
+ TOKEN_TYPE_SYM_MINUS,
+ TOKEN_TYPE_SYM_STAR,
+ TOKEN_TYPE_SYM_PERCENT,
+ TOKEN_TYPE_SYM_DOT,
+ TOKEN_TYPE_SYM_FSLASH,
+ TOKEN_TYPE_SYM_BSLASH,
+ TOKEN_TYPE_SYM_COLON,
+ TOKEN_TYPE_SYM_SEMICOLON,
+ TOKEN_TYPE_SYM_COMMA,
+ TOKEN_TYPE_SYM_EQUALS,
+ TOKEN_TYPE_SYM_GRAVE,
+ TOKEN_TYPE_SYM_TILDE,
+ TOKEN_TYPE_SYM_BANG,
+ TOKEN_TYPE_SYM_CARET,
+ TOKEN_TYPE_SYM_AMPERSAND,
+
+ TOKEN_TYPE_SYMBOL,
+ TOKEN_TYPE_LITERAL_STRING,
+ TOKEN_TYPE_LITERAL_NUMERIC,
+
+ TOKEN_TYPE_COUNT
+} OnyxTokenType;
+
+typedef struct OnyxFilePos {
+ const char* filename;
+ u64 line, column;
+} OnyxFilePos;
+
+typedef struct OnyxToken {
+ OnyxTokenType type;
+ isize length;
+ char* token;
+ OnyxFilePos pos;
+} OnyxToken;
+
+typedef struct OnyxTokenizer {
+ char *start, *curr, *end;
+
+ const char* filename;
+
+ char* line_start;
+ u64 line_number;
+
+ bh_arr(OnyxToken) tokens;
+} OnyxTokenizer;
+
+const char* onyx_get_token_type_name(OnyxTokenType tkn_type);
+void onyx_token_null_toggle(OnyxToken tkn);
+OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer);
+OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc);
+void onyx_tokenizer_free(OnyxTokenizer* tokenizer);
+void onyx_lex_tokens(OnyxTokenizer* tokenizer);
+
+#endif
--- /dev/null
+#ifndef ONYXMSGS_H
+#define ONYXMSGS_H
+
+#include "bh.h"
+#include "onyxlex.h"
+
+#include <stdarg.h>
+
+#define ONYX_MSG_BUFFER_SIZE 256
+
+typedef enum OnyxMessageType {
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
+ ONYX_MESSAGE_TYPE_UNKNOWN_TYPE,
+ ONYX_MESSAGE_TYPE_NOT_LVAL,
+ ONYX_MESSAGE_TYPE_ASSIGN_CONST,
+ ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
+ ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION,
+ ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE,
+ ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH,
+
+ ONYX_MESSAGE_TYPE_COUNT,
+} OnyxMessageType;
+
+typedef struct OnyxMessage {
+ OnyxMessageType type;
+ OnyxFilePos pos;
+ struct OnyxMessage* next;
+ char text[ONYX_MSG_BUFFER_SIZE];
+} OnyxMessage;
+
+typedef struct OnyxMessages {
+ bh_allocator allocator;
+
+ OnyxMessage* first;
+} OnyxMessages;
+
+void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...);
+void onyx_message_print(OnyxMessages* msgs);
+b32 onyx_message_has_errors(OnyxMessages* msgs);
+void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs);
+
+#endif
--- /dev/null
+#ifndef ONYXPARSER_H
+#define ONYXPARSER_H
+
+#define BH_NO_STRING
+#include "bh.h"
+
+#include "onyxlex.h"
+#include "onyxmsgs.h"
+
+typedef union OnyxAstNode OnyxAstNode;
+typedef struct OnyxAstNodeLocal OnyxAstNodeLocal;
+typedef struct OnyxAstNodeScope OnyxAstNodeScope;
+typedef struct OnyxAstNodeBlock OnyxAstNodeBlock;
+typedef struct OnyxAstNodeParam OnyxAstNodeParam;
+typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef;
+
+typedef struct OnyxParser {
+ OnyxTokenizer *tokenizer; // NOTE: not used since all tokens are lexed before parsing starts
+ OnyxToken *prev_token;
+ OnyxToken *curr_token;
+
+ // BUG: This way of handling identifiers will work for now,
+ // but it will not allow for shadowing. Also, variable names
+ // cannot be the same as any function or global variable.
+ // That will get annoying to program.
+ // NOTE: A table of the current identifiers in the current scope.
+ // If the identifier doesn't at the time of parsing, it is an error.
+ // Cleared at the end of a block.
+ bh_hash(OnyxAstNode*) identifiers;
+ OnyxAstNodeScope *curr_scope;
+
+ OnyxMessages *msgs;
+
+ bh_allocator allocator;
+} OnyxParser;
+
+typedef enum OnyxAstNodeKind {
+ ONYX_AST_NODE_KIND_ERROR,
+ ONYX_AST_NODE_KIND_PROGRAM,
+
+ ONYX_AST_NODE_KIND_FUNCDEF,
+ ONYX_AST_NODE_KIND_BLOCK,
+ ONYX_AST_NODE_KIND_SCOPE,
+ ONYX_AST_NODE_KIND_LOCAL,
+
+ ONYX_AST_NODE_KIND_ADD,
+ ONYX_AST_NODE_KIND_MINUS,
+ ONYX_AST_NODE_KIND_MULTIPLY,
+ ONYX_AST_NODE_KIND_DIVIDE,
+ ONYX_AST_NODE_KIND_MODULUS,
+ ONYX_AST_NODE_KIND_NEGATE,
+
+ ONYX_AST_NODE_KIND_TYPE,
+ ONYX_AST_NODE_KIND_LITERAL,
+ ONYX_AST_NODE_KIND_CAST,
+ ONYX_AST_NODE_KIND_PARAM,
+ ONYX_AST_NODE_KIND_CALL,
+ ONYX_AST_NODE_KIND_ASSIGNMENT,
+ ONYX_AST_NODE_KIND_RETURN,
+
+ ONYX_AST_NODE_KIND_EQUAL,
+ ONYX_AST_NODE_KIND_NOT_EQUAL,
+ ONYX_AST_NODE_KIND_GREATER,
+ ONYX_AST_NODE_KIND_GREATER_EQUAL,
+ ONYX_AST_NODE_KIND_LESS,
+ ONYX_AST_NODE_KIND_LESS_EQUAL,
+ ONYX_AST_NODE_KIND_NOT,
+
+ ONYX_AST_NODE_KIND_IF,
+ ONYX_AST_NODE_KIND_LOOP,
+
+ ONYX_AST_NODE_KIND_COUNT
+} OnyxAstNodeKind;
+
+typedef enum OnyxTypeInfoKind {
+ ONYX_TYPE_INFO_KIND_UNKNOWN,
+ ONYX_TYPE_INFO_KIND_VOID,
+ ONYX_TYPE_INFO_KIND_BOOL,
+
+ ONYX_TYPE_INFO_KIND_UINT8,
+ ONYX_TYPE_INFO_KIND_UINT16,
+ ONYX_TYPE_INFO_KIND_UINT32,
+ ONYX_TYPE_INFO_KIND_UINT64,
+
+ ONYX_TYPE_INFO_KIND_INT8,
+ ONYX_TYPE_INFO_KIND_INT16,
+ ONYX_TYPE_INFO_KIND_INT32,
+ ONYX_TYPE_INFO_KIND_INT64,
+
+ ONYX_TYPE_INFO_KIND_FLOAT32,
+ ONYX_TYPE_INFO_KIND_FLOAT64,
+ ONYX_TYPE_INFO_KIND_SOFT_FLOAT, // 64-bits of data but could be treated as 32-bit
+} OnyxTypeInfoKind;
+
+typedef struct OnyxTypeInfo {
+ OnyxTypeInfoKind kind;
+ u32 size; // in bytes
+ const char* name;
+ u32 is_int : 1;
+ u32 is_unsigned : 1;
+ u32 is_float : 1;
+ u32 is_bool : 1;
+ u32 is_known : 1;
+} OnyxTypeInfo;
+
+extern OnyxTypeInfo builtin_types[];
+
+// NOTE: Some of these flags will overlap since there are
+// only 32-bits of flags to play with
+typedef enum OnyxAstFlags {
+ // Top-level flags
+ ONYX_AST_FLAG_EXPORTED = BH_BIT(0),
+ ONYX_AST_FLAG_LVAL = BH_BIT(1),
+ ONYX_AST_FLAG_CONST = BH_BIT(2),
+ ONYX_AST_FLAG_COMPTIME = BH_BIT(3),
+} OnyxAstFlags;
+
+struct OnyxAstNodeLocal {
+ OnyxAstNodeKind kind;
+ u32 flags;
+ OnyxToken *token;
+ OnyxTypeInfo *type;
+ OnyxAstNodeLocal *prev_local;
+ OnyxAstNode *shadowed;
+ OnyxAstNode *unused;
+};
+
+// NOTE: Needs to have shadowed in the same position as OnyxAstNodeLocal
+struct OnyxAstNodeParam {
+ OnyxAstNodeKind kind;
+ u32 flags;
+ OnyxToken *token; // Symbol name i.e. 'a', 'b'
+ OnyxTypeInfo *type;
+ OnyxAstNodeParam *next;
+ OnyxAstNode *shadowed;
+ u64 param_count;
+};
+
+struct OnyxAstNodeScope {
+ OnyxAstNodeKind kind;
+ u32 flags;
+ OnyxToken *token; // NOTE: UNUSED
+ OnyxTypeInfo *type; // NOTE: UNUSED
+ OnyxAstNodeScope *prev_scope;
+ OnyxAstNodeLocal *last_local;
+ OnyxAstNode *unused;
+};
+
+struct OnyxAstNodeBlock {
+ OnyxAstNodeKind kind;
+ u32 flags;
+ OnyxToken *token;
+ OnyxTypeInfo *return_type;
+ OnyxAstNode *next;
+ OnyxAstNode *body;
+ OnyxAstNodeScope *scope; // NOTE: Only set on blocks belonging to functions
+};
+
+struct OnyxAstNodeFuncDef {
+ OnyxAstNodeKind kind;
+ u32 flags;
+ OnyxToken *token; // This will point to the symbol token to identify it
+ OnyxTypeInfo *return_type;
+ OnyxAstNode *next;
+ OnyxAstNodeBlock *body;
+ OnyxAstNodeParam *params;
+};
+
+union OnyxAstNode {
+
+ // Generic node structure for capturing all binary ops and statements
+ struct {
+ OnyxAstNodeKind kind;
+ u32 flags;
+ OnyxToken *token;
+ OnyxTypeInfo *type;
+ OnyxAstNode *next;
+ OnyxAstNode *left;
+ OnyxAstNode *right;
+ };
+
+ OnyxAstNodeBlock as_block;
+ OnyxAstNodeFuncDef as_funcdef;
+ OnyxAstNodeParam as_param;
+ OnyxAstNodeLocal as_local;
+ OnyxAstNodeScope as_scope;
+};
+
+const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind);
+OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind);
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs);
+void onyx_parser_free(OnyxParser* parser);
+OnyxAstNode* onyx_parse(OnyxParser *parser);
+
+#endif // #ifndef ONYXPARSER_H
--- /dev/null
+#include "bh.h"
+
+#include "onyxparser.h"
+
+void onyx_ast_print(OnyxAstNode* program, i32 indent);
+++ /dev/null
-#define BH_NO_STRING
-// #define BH_DEBUG
-#define BH_DEFINE
-#include "bh.h"
-
-#include "onyxlex.h"
-#include "onyxmsgs.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-int main(int argc, char *argv[]) {
- bh_file source_file;
- bh_file_error err = bh_file_open(&source_file, argv[1]);
- if (err != BH_FILE_ERROR_NONE) {
- bh_printf_err("Failed to open file %s\n", argv[1]);
- return EXIT_FAILURE;
- }
-
- bh_allocator alloc = bh_heap_allocator();
-
- bh_file_contents fc = bh_file_read_contents(alloc, &source_file);
- bh_file_close(&source_file);
-
- OnyxTokenizer tokenizer = onyx_tokenizer_create(alloc, &fc);
- onyx_lex_tokens(&tokenizer);
- bh_arr(OnyxToken) token_arr = tokenizer.tokens;
-
-#if 0
- bh_printf("There are %d tokens (Allocated space for %d tokens)\n", bh_arr_length(token_arr), bh_arr_capacity(token_arr));
-
- for (OnyxToken* it = token_arr; !bh_arr_end(token_arr, it); it++) {
- onyx_token_null_toggle(*it);
- bh_printf("%s (%s:%l:%l)\n", onyx_get_token_type_name(it->type), it->pos.filename, it->pos.line, it->pos.column);
- onyx_token_null_toggle(*it);
- }
-#endif
-
- bh_arena msg_arena;
- bh_arena_init(&msg_arena, alloc, 4096);
- bh_allocator msg_alloc = bh_arena_allocator(&msg_arena);
-
- OnyxMessages msgs;
- onyx_message_create(msg_alloc, &msgs);
-
- bh_arena ast_arena;
- bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB
- bh_allocator ast_alloc = bh_arena_allocator(&ast_arena);
-
- OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs);
- OnyxAstNode* program = onyx_parse(&parser);
-
- // NOTE: if there are errors, assume the parse tree was generated wrong,
- // even if it may have still been generated correctly.
- if (onyx_message_has_errors(&msgs)) {
- onyx_message_print(&msgs);
- goto main_exit;
- } else {
- onyx_ast_print(program, 0);
- bh_printf("\nNo errors.\n");
- }
-
-
-main_exit: // NOTE: Cleanup, since C doesn't have defer
- bh_arena_free(&msg_arena);
- bh_arena_free(&ast_arena);
- onyx_parser_free(&parser);
- onyx_tokenizer_free(&tokenizer);
- bh_file_contents_delete(&fc);
-
- return 0;
-}
+++ /dev/null
-#include "bh.h"
-#include "onyxlex.h"
-
-static const char* onyx_token_type_names[] = {
- "TOKEN_TYPE_UNKNOWN",
- "TOKEN_TYPE_END_STREAM",
-
- "TOKEN_TYPE_COMMENT",
-
- "struct", //"TOKEN_TYPE_KEYWORD_STRUCT",
- "use", //"TOKEN_TYPE_KEYWORD_USE",
- "export", //"TOKEN_TYPE_KEYWORD_EXPORT",
- "if", //"TOKEN_TYPE_KEYWORD_IF",
- "else", //"TOKEN_TYPE_KEYWORD_ELSE",
- "for", //"TOKEN_TYPE_KEYWORD_FOR",
- "do", //"TOKEN_TYPE_KEYWORD_DO",
- "return", //"TOKEN_TYPE_KEYWORD_RETURN",
- "const", //"TOKEN_TYPE_KEYWORD_CONST",
- "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN",
- "proc", //"TOKEN_TYPE_KEYWORD_PROC",
- "global", //"TOKEN_TYPE_KEYWORD_GLOBAL",
- "as", //"TOKEN_TYPE_KEYWORD_CAST",
-
- "->", //"TOKEN_TYPE_RIGHT_ARROW",
- "<-", //"TOKEN_TYPE_LEFT_ARROW",
- "(", //"TOKEN_TYPE_OPEN_PAREN",
- ")", //"TOKEN_TYPE_CLOSE_PAREN",
- "{", //"TOKEN_TYPE_OPEN_BRACE",
- "}", //"TOKEN_TYPE_CLOSE_BRACE",
- "[", //"TOKEN_TYPE_OPEN_BRACKET",
- "]", //"TOKEN_TYPE_CLOSE_BRACKET",
- "<", //"TOKEN_TYPE_OPEN_ANGLE",
- ">", //"TOKEN_TYPE_CLOSE_ANGLE",
-
- "+", // "TOKEN_TYPE_SYM_PLUS",
- "-", // "TOKEN_TYPE_SYM_MINUS",
- "*", // "TOKEN_TYPE_SYM_STAR",
- "%", // "TOKEN_TYPE_SYM_PERCENT",
- ".", // "TOKEN_TYPE_SYM_DOT",
- "/", // "TOKEN_TYPE_SYM_FSLASH",
- "\\", // "TOKEN_TYPE_SYM_BSLASH",
- ":", // "TOKEN_TYPE_SYM_COLON",
- ";", // "TOKEN_TYPE_SYM_SEMICOLON",
- ",", // "TOKEN_TYPE_SYM_COMMA",
- "=", // "TOKEN_TYPE_SYM_EQUALS",
- "`", // "TOKEN_TYPE_SYM_GRAVE",
- "~", // "TOKEN_TYPE_SYM_TILDE",
- "!", // "TOKEN_TYPE_SYM_BANG",
- "^", // "TOKEN_TYPE_SYM_CARET",
- "&", // "TOKEN_TYPE_SYM_AMPERSAND",
-
- "TOKEN_TYPE_SYMBOL",
- "TOKEN_TYPE_LITERAL_STRING",
- "TOKEN_TYPE_LITERAL_NUMERIC",
-
- "TOKEN_TYPE_COUNT"
-};
-
-#ifndef LITERAL_TOKEN
-#define LITERAL_TOKEN(token, token_type) \
- if (token_lit(tokenizer, &tk, token, token_type)) goto token_parsed;
-#endif
-
-#ifndef INCREMENT_CURR_TOKEN
-#define INCREMENT_CURR_TOKEN(tkn) { \
- if (*(tkn)->curr == '\n') { \
- (tkn)->line_number++; \
- (tkn)->line_start = (tkn)->curr + 1; \
- } \
- (tkn)->curr++; \
-}
-#endif
-
-static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTokenType type) {
- i64 len = chars_match(tokenizer->curr, lit);
- if (len > 0) {
- tk->type = type;
- tk->token = tokenizer->curr;
- tk->length = len;
- tk->pos.line = tokenizer->line_number;
- tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
-
- tokenizer->curr += len;
-
- return 1;
- }
- return 0;
-}
-
-const char* onyx_get_token_type_name(OnyxTokenType tkn_type) {
- return onyx_token_type_names[tkn_type];
-}
-
-void onyx_token_null_toggle(OnyxToken tkn) {
- static char backup = 0;
- char tmp = tkn.token[tkn.length];
- tkn.token[tkn.length] = backup;
- backup = tmp;
-}
-
-OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
- OnyxToken tk;
-
- // Skip whitespace
- while (char_is_whitespace(*tokenizer->curr) && tokenizer->curr != tokenizer->end)
- INCREMENT_CURR_TOKEN(tokenizer)
-
- tk.type = TOKEN_TYPE_UNKNOWN;
- tk.token = tokenizer->curr;
- tk.length = 1;
- tk.pos.filename = tokenizer->filename;
- tk.pos.line = tokenizer->line_number;
- tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
-
- if (tokenizer->curr == tokenizer->end) {
- tk.type = TOKEN_TYPE_END_STREAM;
- goto token_parsed;
- }
-
- // Comments
- if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
- tokenizer->curr += 2;
- tk.type = TOKEN_TYPE_COMMENT;
- tk.token = tokenizer->curr;
- u16 layers = 1;
-
- while (layers >= 1) {
- INCREMENT_CURR_TOKEN(tokenizer);
-
- if (tokenizer->curr == tokenizer->end) {
- tk.type = TOKEN_TYPE_END_STREAM;
- break;
- }
-
- if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
- layers++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') {
- layers--;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
-
- tk.length = tokenizer->curr - tk.token - 2;
- goto token_parsed;
- }
-
- LITERAL_TOKEN("struct", TOKEN_TYPE_KEYWORD_STRUCT);
- LITERAL_TOKEN("export", TOKEN_TYPE_KEYWORD_EXPORT);
- LITERAL_TOKEN("use", TOKEN_TYPE_KEYWORD_USE);
- LITERAL_TOKEN("if", TOKEN_TYPE_KEYWORD_IF);
- LITERAL_TOKEN("else", TOKEN_TYPE_KEYWORD_ELSE);
- LITERAL_TOKEN("foreign", TOKEN_TYPE_KEYWORD_FOREIGN);
- LITERAL_TOKEN("for", TOKEN_TYPE_KEYWORD_FOR);
- LITERAL_TOKEN("return", TOKEN_TYPE_KEYWORD_RETURN);
- LITERAL_TOKEN("const", TOKEN_TYPE_KEYWORD_CONST);
- LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO);
- LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC);
- LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL);
- LITERAL_TOKEN("as", TOKEN_TYPE_KEYWORD_CAST);
- LITERAL_TOKEN("->", TOKEN_TYPE_RIGHT_ARROW);
- LITERAL_TOKEN("<-", TOKEN_TYPE_RIGHT_ARROW);
- LITERAL_TOKEN("(", TOKEN_TYPE_OPEN_PAREN);
- LITERAL_TOKEN(")", TOKEN_TYPE_CLOSE_PAREN);
- LITERAL_TOKEN("{", TOKEN_TYPE_OPEN_BRACE);
- LITERAL_TOKEN("}", TOKEN_TYPE_CLOSE_BRACE);
- LITERAL_TOKEN("[", TOKEN_TYPE_OPEN_BRACKET);
- LITERAL_TOKEN("]", TOKEN_TYPE_CLOSE_BRACKET);
- LITERAL_TOKEN("<", TOKEN_TYPE_OPEN_ANGLE);
- LITERAL_TOKEN(">", TOKEN_TYPE_CLOSE_ANGLE);
- LITERAL_TOKEN("+", TOKEN_TYPE_SYM_PLUS);
- LITERAL_TOKEN("-", TOKEN_TYPE_SYM_MINUS);
- LITERAL_TOKEN("*", TOKEN_TYPE_SYM_STAR);
- LITERAL_TOKEN(".", TOKEN_TYPE_SYM_DOT);
- LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT);
- LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH);
- LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH);
- LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON);
- LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON);
- LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA);
- LITERAL_TOKEN("=", TOKEN_TYPE_SYM_EQUALS);
- LITERAL_TOKEN("`", TOKEN_TYPE_SYM_GRAVE);
- LITERAL_TOKEN("~", TOKEN_TYPE_SYM_TILDE);
- LITERAL_TOKEN("!", TOKEN_TYPE_SYM_BANG);
- LITERAL_TOKEN("^", TOKEN_TYPE_SYM_CARET);
- LITERAL_TOKEN("&", TOKEN_TYPE_SYM_AMPERSAND);
-
- // Symbols
- if (char_is_alpha(*tk.token)) {
- u64 len = 0;
- while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- tk.length = len;
- tk.type = TOKEN_TYPE_SYMBOL;
- goto token_parsed;
- }
-
- // String literal
- if (*tk.token == '"') {
- u64 len = 0;
- u64 slash_count = 0;
-
- INCREMENT_CURR_TOKEN(tokenizer);
-
- while (!(*tokenizer->curr == '"' && slash_count == 0)) {
- len++;
-
- if (*tokenizer->curr == '\\') {
- slash_count += 1;
- slash_count %= 2;
- } else {
- slash_count = 0;
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
-
- tk.token++;
- tk.type = TOKEN_TYPE_LITERAL_STRING;
- tk.length = len;
- goto token_parsed;
- }
-
- // Number literal
- if (char_is_num(*tokenizer->curr)) {
- u32 len = 1;
- while (char_is_num(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '.') {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- tk.type = TOKEN_TYPE_LITERAL_NUMERIC;
- tk.length = len;
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
-
-token_parsed:
- bh_arr_push(tokenizer->tokens, tk);
-
- return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1];
-}
-
-OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) {
- OnyxTokenizer tknizer = {
- .start = fc->data,
- .curr = fc->data,
- .end = fc->data + fc->length,
-
- .filename = fc->filename,
-
- .line_number = 1,
- .line_start = fc->data,
- .tokens = NULL,
- };
-
- bh_arr_new(allocator, tknizer.tokens, 512);
- return tknizer;
-}
-
-void onyx_tokenizer_free(OnyxTokenizer* tokenizer) {
- bh_arr_free(tokenizer->tokens);
-}
-
-void onyx_lex_tokens(OnyxTokenizer* tokenizer) {
- OnyxToken* tk;
- do {
- tk = onyx_get_token(tokenizer);
- } while (tk->type != TOKEN_TYPE_END_STREAM);
-}
+++ /dev/null
-#ifndef ONYXLEX_H
-#define ONYXLEX_H
-
-#include "bh.h"
-
-typedef enum OnyxTokenType {
- TOKEN_TYPE_UNKNOWN,
- TOKEN_TYPE_END_STREAM,
-
- TOKEN_TYPE_COMMENT,
-
- TOKEN_TYPE_KEYWORD_STRUCT,
- TOKEN_TYPE_KEYWORD_USE,
- TOKEN_TYPE_KEYWORD_EXPORT,
- TOKEN_TYPE_KEYWORD_IF,
- TOKEN_TYPE_KEYWORD_ELSE,
- TOKEN_TYPE_KEYWORD_FOR,
- TOKEN_TYPE_KEYWORD_DO,
- TOKEN_TYPE_KEYWORD_RETURN,
- TOKEN_TYPE_KEYWORD_CONST,
- TOKEN_TYPE_KEYWORD_FOREIGN,
- TOKEN_TYPE_KEYWORD_PROC,
- TOKEN_TYPE_KEYWORD_GLOBAL,
- TOKEN_TYPE_KEYWORD_CAST,
-
- TOKEN_TYPE_RIGHT_ARROW,
- TOKEN_TYPE_LEFT_ARROW,
- TOKEN_TYPE_OPEN_PAREN,
- TOKEN_TYPE_CLOSE_PAREN,
- TOKEN_TYPE_OPEN_BRACE,
- TOKEN_TYPE_CLOSE_BRACE,
- TOKEN_TYPE_OPEN_BRACKET,
- TOKEN_TYPE_CLOSE_BRACKET,
- TOKEN_TYPE_OPEN_ANGLE,
- TOKEN_TYPE_CLOSE_ANGLE,
-
- TOKEN_TYPE_SYM_PLUS,
- TOKEN_TYPE_SYM_MINUS,
- TOKEN_TYPE_SYM_STAR,
- TOKEN_TYPE_SYM_PERCENT,
- TOKEN_TYPE_SYM_DOT,
- TOKEN_TYPE_SYM_FSLASH,
- TOKEN_TYPE_SYM_BSLASH,
- TOKEN_TYPE_SYM_COLON,
- TOKEN_TYPE_SYM_SEMICOLON,
- TOKEN_TYPE_SYM_COMMA,
- TOKEN_TYPE_SYM_EQUALS,
- TOKEN_TYPE_SYM_GRAVE,
- TOKEN_TYPE_SYM_TILDE,
- TOKEN_TYPE_SYM_BANG,
- TOKEN_TYPE_SYM_CARET,
- TOKEN_TYPE_SYM_AMPERSAND,
-
- TOKEN_TYPE_SYMBOL,
- TOKEN_TYPE_LITERAL_STRING,
- TOKEN_TYPE_LITERAL_NUMERIC,
-
- TOKEN_TYPE_COUNT
-} OnyxTokenType;
-
-typedef struct OnyxFilePos {
- const char* filename;
- u64 line, column;
-} OnyxFilePos;
-
-typedef struct OnyxToken {
- OnyxTokenType type;
- isize length;
- char* token;
- OnyxFilePos pos;
-} OnyxToken;
-
-typedef struct OnyxTokenizer {
- char *start, *curr, *end;
-
- const char* filename;
-
- char* line_start;
- u64 line_number;
-
- bh_arr(OnyxToken) tokens;
-} OnyxTokenizer;
-
-const char* onyx_get_token_type_name(OnyxTokenType tkn_type);
-void onyx_token_null_toggle(OnyxToken tkn);
-OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer);
-OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc);
-void onyx_tokenizer_free(OnyxTokenizer* tokenizer);
-void onyx_lex_tokens(OnyxTokenizer* tokenizer);
-
-#endif
+++ /dev/null
-
-#include "onyxmsgs.h"
-
-static const char* msg_formats[] = {
- "expected token '%s', got '%s'",
- "unexpected token '%s'",
- "unknown type '%s'",
- "expected lval '%s'",
- "attempt to assign to constant '%s'",
- "unknown symbol '%s'",
- "redefinition of function '%s'",
- "mismatched types for binary operator, '%s', '%s'",
- "mismatched types on assignment, '%s', '%s'",
-};
-
-void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) {
- OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage);
- msg->type = type;
- msg->pos = pos;
-
- va_list arg_list;
- va_start(arg_list, pos);
- bh_snprintf_va(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list);
- va_end(arg_list);
-
- OnyxMessage** walker = &msgs->first;
- while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next;
- while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next;
-
- msg->next = *walker;
- *walker = msg;
-}
-
-void onyx_message_print(OnyxMessages* msgs) {
- OnyxMessage* msg = msgs->first;
-
- while (msg) {
- if (msg->pos.filename) {
- bh_printf("(%s:%l:%l) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text);
- } else {
- bh_printf("(%l:%l) %s\n", msg->pos.line, msg->pos.column, msg->text);
- }
- msg = msg->next;
- }
-}
-
-b32 onyx_message_has_errors(OnyxMessages* msgs) {
- return msgs->first != NULL;
-}
-
-void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) {
- msgs->allocator = allocator;
- msgs->first = NULL;
-}
+++ /dev/null
-#ifndef ONYXMSGS_H
-#define ONYXMSGS_H
-
-#include "bh.h"
-#include "onyxlex.h"
-
-#include <stdarg.h>
-
-#define ONYX_MSG_BUFFER_SIZE 256
-
-typedef enum OnyxMessageType {
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
- ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
- ONYX_MESSAGE_TYPE_UNKNOWN_TYPE,
- ONYX_MESSAGE_TYPE_NOT_LVAL,
- ONYX_MESSAGE_TYPE_ASSIGN_CONST,
- ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
- ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION,
- ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE,
- ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH,
-
- ONYX_MESSAGE_TYPE_COUNT,
-} OnyxMessageType;
-
-typedef struct OnyxMessage {
- OnyxMessageType type;
- OnyxFilePos pos;
- struct OnyxMessage* next;
- char text[ONYX_MSG_BUFFER_SIZE];
-} OnyxMessage;
-
-typedef struct OnyxMessages {
- bh_allocator allocator;
-
- OnyxMessage* first;
-} OnyxMessages;
-
-void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...);
-void onyx_message_print(OnyxMessages* msgs);
-b32 onyx_message_has_errors(OnyxMessages* msgs);
-void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs);
-
-#endif
+++ /dev/null
-
-#include "onyxlex.h"
-#include "onyxmsgs.h"
-#include "onyxparser.h"
-
-static const char* ast_node_names[] = {
- "ERROR",
- "PROGRAM",
-
- "FUNCDEF",
- "BLOCK",
- "SCOPE",
- "LOCAL",
-
- "ADD",
- "MINUS",
- "MULTIPLY",
- "DIVIDE",
- "MODULUS",
- "NEGATE",
-
- "TYPE",
- "LITERAL",
- "CAST",
- "PARAM",
- "CALL",
- "ASSIGN",
- "RETURN",
-
- "EQUAL",
- "NOT_EQUAL",
- "GREATER",
- "GREATER_EQUAL",
- "LESS",
- "LESS_EQUAL",
- "NOT",
-
- "IF",
- "LOOP",
-
- "ONYX_AST_NODE_KIND_COUNT",
-};
-
-struct OnyxTypeInfo builtin_types[] = {
- { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" },
- { ONYX_TYPE_INFO_KIND_VOID, 0, "void", 0, 0, 0, 0, 1 },
-
- { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1, 1 },
-
- { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0, 1 },
- { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0, 1 },
-
- { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0, 1 },
- { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0, 1 },
-
- { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0, 1 },
- { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0, 1},
- { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0, 1 },
-
- { 0xffffffff } // Sentinel
-};
-
-static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } };
-
-// NOTE: Forward declarations
-static void parser_next_token(OnyxParser* parser);
-static void parser_prev_token(OnyxParser* parser);
-static b32 is_terminating_token(OnyxTokenType token_type);
-static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type);
-static OnyxAstNodeScope* enter_scope(OnyxParser* parser);
-static OnyxAstNodeScope* leave_scope(OnyxParser* parser);
-static void insert_identifier(OnyxParser* parser, OnyxAstNode* ident, b32 is_local);
-static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident);
-static OnyxAstNode* parse_factor(OnyxParser* parser);
-static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left);
-static OnyxAstNode* parse_expression(OnyxParser* parser);
-static OnyxAstNode* parse_if_stmt(OnyxParser* parser);
-static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret);
-static OnyxAstNode* parse_return_statement(OnyxParser* parser);
-static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function);
-static OnyxAstNode* parse_statement(OnyxParser* parser);
-static OnyxTypeInfo* parse_type(OnyxParser* parser);
-static OnyxAstNodeParam* parse_function_params(OnyxParser* parser);
-static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser);
-static OnyxAstNode* parse_top_level_statement(OnyxParser* parser);
-
-static void parser_next_token(OnyxParser* parser) {
- parser->prev_token = parser->curr_token;
- parser->curr_token++;
- while (parser->curr_token->type == TOKEN_TYPE_COMMENT) parser->curr_token++;
-}
-
-static void parser_prev_token(OnyxParser* parser) {
- // TODO: This is probably wrong
- parser->prev_token--;
- while (parser->prev_token->type == TOKEN_TYPE_COMMENT) parser->prev_token--;
- parser->curr_token = parser->prev_token;
- parser->prev_token--;
-}
-
-static b32 is_terminating_token(OnyxTokenType token_type) {
- switch (token_type) {
- case TOKEN_TYPE_SYM_SEMICOLON:
- case TOKEN_TYPE_CLOSE_BRACE:
- case TOKEN_TYPE_OPEN_BRACE:
- case TOKEN_TYPE_END_STREAM:
- return 1;
- default:
- return 0;
- }
-}
-
-static void find_token(OnyxParser* parser, OnyxTokenType token_type) {
- while (parser->curr_token->type != token_type && !is_terminating_token(parser->curr_token->type)) {
- parser_next_token(parser);
- }
-}
-
-// Advances to next token no matter what
-static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) {
- OnyxToken* token = parser->curr_token;
- parser_next_token(parser);
-
- if (token->type != token_type) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
- token->pos,
- onyx_get_token_type_name(token_type), onyx_get_token_type_name(token->type));
- return NULL;
- }
-
- return token;
-}
-
-static OnyxAstNodeScope* enter_scope(OnyxParser* parser) {
- OnyxAstNodeScope* scope = (OnyxAstNodeScope*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE);
- scope->prev_scope = parser->curr_scope;
- parser->curr_scope = scope;
- return scope;
-}
-
-static OnyxAstNodeScope* leave_scope(OnyxParser* parser) {
- // NOTE: Can't leave a scope if there is no scope
- assert(parser->curr_scope != NULL);
-
- for (OnyxAstNodeLocal *walker = parser->curr_scope->last_local; walker != NULL; walker = walker->prev_local) {
- remove_identifier(parser, (OnyxAstNode *) walker);
- }
-
- parser->curr_scope = parser->curr_scope->prev_scope;
- return parser->curr_scope;
-}
-
-static OnyxAstNode* lookup_identifier(OnyxParser* parser, OnyxToken* token) {
- OnyxAstNode* ident = NULL;
-
- onyx_token_null_toggle(*token);
- if (bh_hash_has(OnyxAstNode*, parser->identifiers, token->token)) {
- ident = bh_hash_get(OnyxAstNode*, parser->identifiers, token->token);
- }
- onyx_token_null_toggle(*token);
-
- return ident;
-}
-
-static void insert_identifier(OnyxParser* parser, OnyxAstNode* ident, b32 is_local) {
- OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident;
- if (is_local) {
- OnyxAstNodeScope* scope = parser->curr_scope;
- local->prev_local = scope->last_local;
- scope->last_local = local;
- }
-
- onyx_token_null_toggle(*local->token);
- if (bh_hash_has(OnyxAstNode*, parser->identifiers, local->token->token)) {
- local->shadowed = bh_hash_get(OnyxAstNode*, parser->identifiers, local->token->token);
- }
-
- bh_hash_put(OnyxAstNodeLocal*, parser->identifiers, local->token->token, local);
- onyx_token_null_toggle(*local->token);
-}
-
-static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident) {
- OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident;
-
- onyx_token_null_toggle(*local->token);
- if (local->shadowed) {
- bh_hash_put(OnyxAstNode*, parser->identifiers, local->token->token, local->shadowed);
- } else {
- bh_hash_delete(OnyxAstNode*, parser->identifiers, local->token->token);
- }
- onyx_token_null_toggle(*local->token);
-}
-
-static OnyxAstNode* parse_factor(OnyxParser* parser) {
- switch (parser->curr_token->type) {
- case TOKEN_TYPE_OPEN_PAREN:
- {
- parser_next_token(parser);
- OnyxAstNode* expr = parse_expression(parser);
- expect(parser, TOKEN_TYPE_CLOSE_PAREN);
- return expr;
- }
-
- case TOKEN_TYPE_SYMBOL:
- {
- OnyxToken* sym_token = expect(parser, TOKEN_TYPE_SYMBOL);
- OnyxAstNode* sym_node = lookup_identifier(parser, sym_token);
- if (sym_node == NULL) {
- onyx_token_null_toggle(*sym_token);
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
- sym_token->pos, sym_token->token);
- onyx_token_null_toggle(*sym_token);
- }
-
- // TODO: Handle calling a function
- return sym_node;
- }
-
- case TOKEN_TYPE_LITERAL_NUMERIC:
- {
- OnyxAstNode* lit_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL);
- lit_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_INT64];
- lit_node->token = expect(parser, TOKEN_TYPE_LITERAL_NUMERIC);
- lit_node->flags |= ONYX_AST_FLAG_COMPTIME;
- return lit_node;
- }
-
- default:
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
- parser->curr_token->pos,
- onyx_get_token_type_name(parser->curr_token->type));
- }
-
- return NULL;
-}
-
-static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left) {
- OnyxAstNodeKind kind = -1;
-
- switch (parser->curr_token->type) {
- case TOKEN_TYPE_SYM_PLUS: kind = ONYX_AST_NODE_KIND_ADD; break;
- case TOKEN_TYPE_SYM_MINUS: kind = ONYX_AST_NODE_KIND_MINUS; break;
- case TOKEN_TYPE_SYM_STAR: kind = ONYX_AST_NODE_KIND_MULTIPLY; break;
- case TOKEN_TYPE_SYM_FSLASH: kind = ONYX_AST_NODE_KIND_DIVIDE; break;
- case TOKEN_TYPE_SYM_PERCENT: kind = ONYX_AST_NODE_KIND_MODULUS; break;
- }
-
- if (kind != -1) {
- OnyxToken* bin_op_tok = parser->curr_token;
- parser_next_token(parser);
-
- OnyxAstNode* right = parse_factor(parser);
-
- // NOTE: Typechecking like this will not be here for long but
- // want to start working on it early
- if (left->type != right->type) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE,
- bin_op_tok->pos,
- left->type->name, right->type->name);
- return &error_node;
- }
-
- OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind);
- bin_op->left = left;
- bin_op->right = right;
- bin_op->type = left->type;
- return bin_op;
- }
-
- return &error_node;
-}
-
-static OnyxAstNode* parse_expression(OnyxParser* parser) {
- OnyxAstNode* left = parse_factor(parser);
-
- switch (parser->curr_token->type) {
- case TOKEN_TYPE_SYM_PLUS:
- case TOKEN_TYPE_SYM_MINUS:
- case TOKEN_TYPE_SYM_STAR:
- case TOKEN_TYPE_SYM_FSLASH:
- case TOKEN_TYPE_SYM_PERCENT:
- {
- OnyxAstNode* expr = parse_bin_op(parser, left);
- return expr;
- }
- case TOKEN_TYPE_KEYWORD_CAST:
- {
- parser_next_token(parser);
- OnyxTypeInfo* type = parse_type(parser);
-
- // NOTE: This may be premature in the parser but a cast
- // node doesn't need to exist if the source and target
- // types are the same
- if (type == left->type) return left;
-
- OnyxAstNode* cast_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_CAST);
- cast_node->type = type;
- cast_node->left = left;
-
- return cast_node;
- }
- }
-
- return left;
-}
-
-static OnyxAstNode* parse_if_stmt(OnyxParser* parser) {
- return &error_node;
-}
-
-// Returns 1 if the symbol was consumed. Returns 0 otherwise
-// ret is set to the statement to insert
-static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) {
- if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) return 0;
- OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);
-
- switch (parser->curr_token->type) {
- // NOTE: Declaration
- case TOKEN_TYPE_SYM_COLON:
- {
- parser_next_token(parser);
- OnyxTypeInfo* type = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];
- u32 flags = ONYX_AST_FLAG_LVAL;
-
- // NOTE: var: const ...
- if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_CONST) {
- parser_next_token(parser);
- flags |= ONYX_AST_FLAG_CONST;
- }
-
- // NOTE: var: type
- if (parser->curr_token->type == TOKEN_TYPE_SYMBOL) {
- type = parse_type(parser);
- }
-
- OnyxAstNodeLocal* local = (OnyxAstNodeLocal*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LOCAL);
- local->token = symbol;
- local->type = type;
- local->flags |= flags;
-
- insert_identifier(parser, (OnyxAstNode *) local, 1);
-
- if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) {
- OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);
- assignment->token = parser->curr_token;
- parser_next_token(parser);
-
- OnyxAstNode* expr = parse_expression(parser);
- assignment->right = expr;
- assignment->left = (OnyxAstNode*) local;
-
- if (local->type->is_known && local->type != expr->type) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH,
- assignment->token->pos,
- local->type->name, expr->type->name);
- return 1;
- } else if (!local->type->is_known) {
- local->type = expr->type;
- }
- *ret = assignment;
- }
- return 1;
- }
-
- // NOTE: Assignment
- case TOKEN_TYPE_SYM_EQUALS:
- {
- parser_next_token(parser);
-
- OnyxAstNode* lval = lookup_identifier(parser, symbol);
-
- if (lval != NULL && lval->flags & ONYX_AST_FLAG_LVAL && (lval->flags & ONYX_AST_FLAG_CONST) == 0) {
- OnyxAstNode* rval = parse_expression(parser);
- OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);
- assignment->right = rval;
- assignment->left = lval;
- *ret = assignment;
- return 1;
- }
-
- onyx_token_null_toggle(*symbol);
- if (lval == NULL) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
- symbol->pos, symbol->token);
- }
-
- else if ((lval->flags & ONYX_AST_FLAG_LVAL) == 0) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_NOT_LVAL,
- symbol->pos, symbol->token);
- }
-
- else if (lval->flags & ONYX_AST_FLAG_CONST) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_ASSIGN_CONST,
- symbol->pos, symbol->token);
- }
- onyx_token_null_toggle(*symbol);
-
- find_token(parser, TOKEN_TYPE_SYM_SEMICOLON);
- return 1;
- }
-
- default:
- parser_prev_token(parser);
- }
-
- return 0;
-}
-
-static OnyxAstNode* parse_return_statement(OnyxParser* parser) {
- expect(parser, TOKEN_TYPE_KEYWORD_RETURN);
-
- OnyxAstNode* return_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_RETURN);
- return_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_VOID];
- OnyxAstNode* expr = NULL;
-
- if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {
- expr = parse_expression(parser);
-
- if (expr == NULL || expr == &error_node) {
- return &error_node;
- } else {
- return_node->next = expr;
- }
- }
-
- return return_node;
-}
-
-static OnyxAstNode* parse_statement(OnyxParser* parser) {
- switch (parser->curr_token->type) {
- case TOKEN_TYPE_KEYWORD_RETURN:
- return parse_return_statement(parser);
-
- case TOKEN_TYPE_OPEN_BRACE:
- return (OnyxAstNode *) parse_block(parser, 0);
-
- case TOKEN_TYPE_SYMBOL:
- {
- OnyxAstNode* ret = NULL;
- if (parse_symbol_statement(parser, &ret)) return ret;
- // fallthrough
- }
-
- case TOKEN_TYPE_OPEN_PAREN:
- case TOKEN_TYPE_SYM_PLUS:
- case TOKEN_TYPE_SYM_MINUS:
- case TOKEN_TYPE_SYM_BANG:
- case TOKEN_TYPE_LITERAL_NUMERIC:
- case TOKEN_TYPE_LITERAL_STRING:
- return parse_expression(parser);
-
- case TOKEN_TYPE_KEYWORD_IF:
- return parse_if_stmt(parser);
-
- default:
- return NULL;
- }
-}
-
-static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function) {
- // --- is for an empty block
- if (parser->curr_token->type == TOKEN_TYPE_SYM_MINUS) {
- expect(parser, TOKEN_TYPE_SYM_MINUS);
- expect(parser, TOKEN_TYPE_SYM_MINUS);
- expect(parser, TOKEN_TYPE_SYM_MINUS);
- return NULL;
- }
-
- expect(parser, TOKEN_TYPE_OPEN_BRACE);
-
- OnyxAstNodeBlock* block = (OnyxAstNodeBlock *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_BLOCK);
- if (belongs_to_function) {
- OnyxAstNodeScope* scope = enter_scope(parser);
- block->scope = scope;
- }
-
- OnyxAstNode** next = &block->body;
- OnyxAstNode* stmt = NULL;
- while (parser->curr_token->type != TOKEN_TYPE_CLOSE_BRACE) {
- stmt = parse_statement(parser);
-
- if (stmt != NULL && stmt->kind != ONYX_AST_NODE_KIND_ERROR) {
- *next = stmt;
- next = &stmt->next;
- }
-
- if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
- parser->curr_token->pos,
- onyx_get_token_type_name(TOKEN_TYPE_SYM_SEMICOLON),
- onyx_get_token_type_name(parser->curr_token->type));
-
- find_token(parser, TOKEN_TYPE_SYM_SEMICOLON);
- }
- parser_next_token(parser);
- }
-
- expect(parser, TOKEN_TYPE_CLOSE_BRACE);
-
- if (belongs_to_function) {
- leave_scope(parser);
- }
-
- return block;
-}
-
-static OnyxTypeInfo* parse_type(OnyxParser* parser) {
- OnyxTypeInfo* type_info = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];
-
- OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);
- if (symbol == NULL) return type_info;
-
- onyx_token_null_toggle(*symbol);
-
- if (!bh_hash_has(OnyxAstNode*, parser->identifiers, symbol->token)) {
- onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->token);
- } else {
- OnyxAstNode* type_info_node = bh_hash_get(OnyxAstNode*, parser->identifiers, symbol->token);
-
- if (type_info_node->kind == ONYX_AST_NODE_KIND_TYPE) {
- type_info = type_info_node->type;
- }
- }
-
- onyx_token_null_toggle(*symbol);
- return type_info;
-}
-
-static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) {
- expect(parser, TOKEN_TYPE_OPEN_PAREN);
-
- if (parser->curr_token->type == TOKEN_TYPE_CLOSE_PAREN) {
- parser_next_token(parser);
- return NULL;
- }
-
- OnyxAstNodeParam* first_param = NULL;
- u64 param_count = 0;
-
- OnyxAstNodeParam* curr_param = NULL;
- OnyxAstNodeParam* trailer = NULL;
-
- OnyxToken* symbol;
- while (parser->curr_token->type != TOKEN_TYPE_CLOSE_PAREN) {
- if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser);
- param_count++;
-
- symbol = expect(parser, TOKEN_TYPE_SYMBOL);
-
- curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM);
- curr_param->token = symbol;
- curr_param->type = parse_type(parser);
-
- if (first_param == NULL) first_param = curr_param;
-
- curr_param->next = NULL;
- if (trailer) trailer->next = curr_param;
-
- trailer = curr_param;
- }
-
- first_param->param_count = param_count;
-
- parser_next_token(parser); // Skip the )
- return first_param;
-}
-
-static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) {
- expect(parser, TOKEN_TYPE_KEYWORD_PROC);
-
- OnyxAstNodeFuncDef* func_def = (OnyxAstNodeFuncDef *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_FUNCDEF);
-
- OnyxAstNodeParam* params = parse_function_params(parser);
- func_def->params = params;
-
- expect(parser, TOKEN_TYPE_RIGHT_ARROW);
-
- OnyxTypeInfo* return_type = parse_type(parser);
- func_def->return_type = return_type;
-
- for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {
- insert_identifier(parser, (OnyxAstNode *) p, 0);
- }
-
- func_def->body = parse_block(parser, 1);
-
- for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {
- remove_identifier(parser, (OnyxAstNode *) p);
- }
-
- return func_def;
-}
-
-static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) {
- switch (parser->curr_token->type) {
- case TOKEN_TYPE_KEYWORD_USE:
- assert(0);
- break;
-
- case TOKEN_TYPE_KEYWORD_EXPORT:
- {
- expect(parser, TOKEN_TYPE_KEYWORD_EXPORT);
- if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
- parser->curr_token->pos,
- onyx_get_token_type_name(TOKEN_TYPE_SYMBOL),
- onyx_get_token_type_name(parser->curr_token->type));
- break;
- }
-
- OnyxAstNode* top_level_decl = parse_top_level_statement(parser);
- top_level_decl->flags |= ONYX_AST_FLAG_EXPORTED;
- return top_level_decl;
- }
-
- case TOKEN_TYPE_SYMBOL:
- {
- OnyxToken* symbol = parser->curr_token;
- parser_next_token(parser);
-
- expect(parser, TOKEN_TYPE_SYM_COLON);
- expect(parser, TOKEN_TYPE_SYM_COLON);
-
- if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) {
- OnyxAstNodeFuncDef* func_def = parse_function_definition(parser);
- func_def->token = symbol;
-
- onyx_token_null_toggle(*symbol);
-
- if (!bh_hash_has(OnyxAstNode *, parser->identifiers, symbol->token)) {
- bh_hash_put(OnyxAstNode *, parser->identifiers, symbol->token, (OnyxAstNode *) func_def);
- } else {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION,
- symbol->pos,
- symbol->token);
-
- // NOTE: I really wish C had defer...
- onyx_token_null_toggle(*symbol);
- return NULL;
- }
-
- onyx_token_null_toggle(*symbol);
- return (OnyxAstNode *) func_def;
-
- } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) {
- // Handle struct case
- assert(0);
- } else {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
- parser->curr_token->pos,
- onyx_get_token_type_name(parser->curr_token->type));
- break;
- }
- }
- }
-
- parser_next_token(parser);
- return NULL;
-}
-
-
-
-
-
-const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind) {
- return ast_node_names[kind];
-}
-
-OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\
- OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode));
- node->kind = kind;
- node->flags = 0;
- node->token = NULL;
- node->type = NULL;
- node->next = NULL;
- node->left = NULL;
- node->right = NULL;
-
- return node;
-}
-
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) {
- OnyxParser parser;
-
- bh_hash_init(bh_heap_allocator(), parser.identifiers);
-
- OnyxTypeInfo* it = &builtin_types[0];
- while (it->kind != 0xffffffff) {
- OnyxAstNode* tmp = onyx_ast_node_new(alloc, ONYX_AST_NODE_KIND_TYPE);
- tmp->type = it;
- bh_hash_put(OnyxAstNode*, parser.identifiers, (char *)it->name, tmp);
- it++;
- }
-
- parser.allocator = alloc;
- parser.tokenizer = tokenizer;
- parser.curr_token = tokenizer->tokens;
- parser.prev_token = NULL;
- parser.msgs = msgs;
- parser.curr_scope = NULL;
-
- return parser;
-}
-
-void onyx_parser_free(OnyxParser* parser) {
- bh_hash_free(parser->identifiers);
-}
-
-OnyxAstNode* onyx_parse(OnyxParser *parser) {
- OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM);
-
- OnyxAstNode** prev_stmt = &program->next;
- OnyxAstNode* curr_stmt = NULL;
- while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) {
- curr_stmt = parse_top_level_statement(parser);
-
- // Building a linked list of statements down the "next" chain
- if (curr_stmt != NULL && curr_stmt != &error_node) {
- *prev_stmt = curr_stmt;
- prev_stmt = &curr_stmt->next;
- }
- }
-
- return program;
-}
+++ /dev/null
-#ifndef ONYXPARSER_H
-#define ONYXPARSER_H
-
-#define BH_NO_STRING
-#include "bh.h"
-
-#include "onyxlex.h"
-#include "onyxmsgs.h"
-
-typedef union OnyxAstNode OnyxAstNode;
-typedef struct OnyxAstNodeLocal OnyxAstNodeLocal;
-typedef struct OnyxAstNodeScope OnyxAstNodeScope;
-typedef struct OnyxAstNodeBlock OnyxAstNodeBlock;
-typedef struct OnyxAstNodeParam OnyxAstNodeParam;
-typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef;
-
-typedef struct OnyxParser {
- OnyxTokenizer *tokenizer; // NOTE: not used since all tokens are lexed before parsing starts
- OnyxToken *prev_token;
- OnyxToken *curr_token;
-
- // BUG: This way of handling identifiers will work for now,
- // but it will not allow for shadowing. Also, variable names
- // cannot be the same as any function or global variable.
- // That will get annoying to program.
- // NOTE: A table of the current identifiers in the current scope.
- // If the identifier doesn't at the time of parsing, it is an error.
- // Cleared at the end of a block.
- bh_hash(OnyxAstNode*) identifiers;
- OnyxAstNodeScope *curr_scope;
-
- OnyxMessages *msgs;
-
- bh_allocator allocator;
-} OnyxParser;
-
-typedef enum OnyxAstNodeKind {
- ONYX_AST_NODE_KIND_ERROR,
- ONYX_AST_NODE_KIND_PROGRAM,
-
- ONYX_AST_NODE_KIND_FUNCDEF,
- ONYX_AST_NODE_KIND_BLOCK,
- ONYX_AST_NODE_KIND_SCOPE,
- ONYX_AST_NODE_KIND_LOCAL,
-
- ONYX_AST_NODE_KIND_ADD,
- ONYX_AST_NODE_KIND_MINUS,
- ONYX_AST_NODE_KIND_MULTIPLY,
- ONYX_AST_NODE_KIND_DIVIDE,
- ONYX_AST_NODE_KIND_MODULUS,
- ONYX_AST_NODE_KIND_NEGATE,
-
- ONYX_AST_NODE_KIND_TYPE,
- ONYX_AST_NODE_KIND_LITERAL,
- ONYX_AST_NODE_KIND_CAST,
- ONYX_AST_NODE_KIND_PARAM,
- ONYX_AST_NODE_KIND_CALL,
- ONYX_AST_NODE_KIND_ASSIGNMENT,
- ONYX_AST_NODE_KIND_RETURN,
-
- ONYX_AST_NODE_KIND_EQUAL,
- ONYX_AST_NODE_KIND_NOT_EQUAL,
- ONYX_AST_NODE_KIND_GREATER,
- ONYX_AST_NODE_KIND_GREATER_EQUAL,
- ONYX_AST_NODE_KIND_LESS,
- ONYX_AST_NODE_KIND_LESS_EQUAL,
- ONYX_AST_NODE_KIND_NOT,
-
- ONYX_AST_NODE_KIND_IF,
- ONYX_AST_NODE_KIND_LOOP,
-
- ONYX_AST_NODE_KIND_COUNT
-} OnyxAstNodeKind;
-
-typedef enum OnyxTypeInfoKind {
- ONYX_TYPE_INFO_KIND_UNKNOWN,
- ONYX_TYPE_INFO_KIND_VOID,
- ONYX_TYPE_INFO_KIND_BOOL,
-
- ONYX_TYPE_INFO_KIND_UINT8,
- ONYX_TYPE_INFO_KIND_UINT16,
- ONYX_TYPE_INFO_KIND_UINT32,
- ONYX_TYPE_INFO_KIND_UINT64,
-
- ONYX_TYPE_INFO_KIND_INT8,
- ONYX_TYPE_INFO_KIND_INT16,
- ONYX_TYPE_INFO_KIND_INT32,
- ONYX_TYPE_INFO_KIND_INT64,
-
- ONYX_TYPE_INFO_KIND_FLOAT32,
- ONYX_TYPE_INFO_KIND_FLOAT64,
- ONYX_TYPE_INFO_KIND_SOFT_FLOAT, // 64-bits of data but could be treated as 32-bit
-} OnyxTypeInfoKind;
-
-typedef struct OnyxTypeInfo {
- OnyxTypeInfoKind kind;
- u32 size; // in bytes
- const char* name;
- u32 is_int : 1;
- u32 is_unsigned : 1;
- u32 is_float : 1;
- u32 is_bool : 1;
- u32 is_known : 1;
-} OnyxTypeInfo;
-
-extern OnyxTypeInfo builtin_types[];
-
-// NOTE: Some of these flags will overlap since there are
-// only 32-bits of flags to play with
-typedef enum OnyxAstFlags {
- // Top-level flags
- ONYX_AST_FLAG_EXPORTED = BH_BIT(0),
- ONYX_AST_FLAG_LVAL = BH_BIT(1),
- ONYX_AST_FLAG_CONST = BH_BIT(2),
- ONYX_AST_FLAG_COMPTIME = BH_BIT(3),
-} OnyxAstFlags;
-
-struct OnyxAstNodeLocal {
- OnyxAstNodeKind kind;
- u32 flags;
- OnyxToken *token;
- OnyxTypeInfo *type;
- OnyxAstNodeLocal *prev_local;
- OnyxAstNode *shadowed;
- OnyxAstNode *unused;
-};
-
-// NOTE: Needs to have shadowed in the same position as OnyxAstNodeLocal
-struct OnyxAstNodeParam {
- OnyxAstNodeKind kind;
- u32 flags;
- OnyxToken *token; // Symbol name i.e. 'a', 'b'
- OnyxTypeInfo *type;
- OnyxAstNodeParam *next;
- OnyxAstNode *shadowed;
- u64 param_count;
-};
-
-struct OnyxAstNodeScope {
- OnyxAstNodeKind kind;
- u32 flags;
- OnyxToken *token; // NOTE: UNUSED
- OnyxTypeInfo *type; // NOTE: UNUSED
- OnyxAstNodeScope *prev_scope;
- OnyxAstNodeLocal *last_local;
- OnyxAstNode *unused;
-};
-
-struct OnyxAstNodeBlock {
- OnyxAstNodeKind kind;
- u32 flags;
- OnyxToken *token;
- OnyxTypeInfo *return_type;
- OnyxAstNode *next;
- OnyxAstNode *body;
- OnyxAstNodeScope *scope; // NOTE: Only set on blocks belonging to functions
-};
-
-struct OnyxAstNodeFuncDef {
- OnyxAstNodeKind kind;
- u32 flags;
- OnyxToken *token; // This will point to the symbol token to identify it
- OnyxTypeInfo *return_type;
- OnyxAstNode *next;
- OnyxAstNodeBlock *body;
- OnyxAstNodeParam *params;
-};
-
-union OnyxAstNode {
-
- // Generic node structure for capturing all binary ops and statements
- struct {
- OnyxAstNodeKind kind;
- u32 flags;
- OnyxToken *token;
- OnyxTypeInfo *type;
- OnyxAstNode *next;
- OnyxAstNode *left;
- OnyxAstNode *right;
- };
-
- OnyxAstNodeBlock as_block;
- OnyxAstNodeFuncDef as_funcdef;
- OnyxAstNodeParam as_param;
- OnyxAstNodeLocal as_local;
- OnyxAstNodeScope as_scope;
-};
-
-const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind);
-OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind);
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs);
-void onyx_parser_free(OnyxParser* parser);
-OnyxAstNode* onyx_parse(OnyxParser *parser);
-
-#endif // #ifndef ONYXPARSER_H
+++ /dev/null
-#include "onyxutils.h"
-#include "onyxlex.h"
-#include "onyxparser.h"
-
-#define print_indent { if (indent > 0) bh_printf("\n"); for (int i = 0; i < indent; i++) bh_printf(" "); }
-
-void onyx_ast_print(OnyxAstNode* node, i32 indent) {
- if (node == NULL) return;
-
- print_indent;
- bh_printf("(%d) %s ", node->flags, onyx_ast_node_kind_string(node->kind));
-
- switch (node->kind) {
- case ONYX_AST_NODE_KIND_PROGRAM: {
- if (node->next)
- onyx_ast_print(node->next, indent + 1);
-
- break;
- }
-
- case ONYX_AST_NODE_KIND_FUNCDEF: {
- bh_printf("(%b) ", node->token->token, node->token->length);
- OnyxAstNodeFuncDef* fd = &node->as_funcdef;
-
- print_indent;
- bh_printf("Params ");
- if (fd->params)
- onyx_ast_print((OnyxAstNode *) fd->params, 0);
-
- print_indent;
- bh_printf("Returns %s", fd->return_type->name);
-
- print_indent;
- bh_printf("Body");
- if (fd->body)
- onyx_ast_print((OnyxAstNode *) fd->body, indent + 1);
-
- if (fd->next)
- onyx_ast_print((OnyxAstNode *) fd->next, indent);
-
- break;
- }
-
- case ONYX_AST_NODE_KIND_PARAM: {
- OnyxAstNodeParam* param = &node->as_param;
- bh_printf("%b %s", param->token->token, param->token->length, param->type->name);
- if (param->next && indent == 0) {
- bh_printf(", ");
- onyx_ast_print((OnyxAstNode *) param->next, 0);
- }
-
- break;
- }
-
- case ONYX_AST_NODE_KIND_BLOCK: {
- OnyxAstNodeBlock* block = &node->as_block;
- if (block->scope) {
- onyx_ast_print((OnyxAstNode *) block->scope, indent + 1);
- }
-
- if (block->body) {
- onyx_ast_print((OnyxAstNode *) block->body, indent + 1);
- }
-
- break;
- }
-
- case ONYX_AST_NODE_KIND_SCOPE: {
- OnyxAstNodeScope* scope = &node->as_scope;
- if (scope->last_local) {
- onyx_ast_print((OnyxAstNode *) scope->last_local, 0);
- }
-
- break;
- }
-
- case ONYX_AST_NODE_KIND_LOCAL: {
- OnyxAstNodeLocal* local = &node->as_local;
- bh_printf("%b %s", local->token->token, local->token->length, local->type->name);
- if (local->prev_local && indent == 0) {
- bh_printf(", ");
- onyx_ast_print((OnyxAstNode *) local->prev_local, 0);
- }
- break;
- }
-
- case ONYX_AST_NODE_KIND_RETURN: {
- if (node->next) {
- onyx_ast_print(node->next, indent + 1);
- }
-
- break;
- }
-
- case ONYX_AST_NODE_KIND_LITERAL: {
- bh_printf("%b", node->token->token, node->token->length);
- break;
- }
-
- case ONYX_AST_NODE_KIND_CAST: {
- bh_printf("to %s ", node->type->name);
- onyx_ast_print(node->left, indent + 1);
- if (node->next) {
- onyx_ast_print(node->next, indent);
- }
- break;
- }
-
- default: {
- onyx_ast_print(node->left, indent + 1);
- onyx_ast_print(node->right, indent + 1);
- if (node->next) {
- onyx_ast_print(node->next, indent);
- }
- break;
- }
- }
-}
+++ /dev/null
-#include "bh.h"
-
-#include "onyxparser.h"
-
-void onyx_ast_print(OnyxAstNode* program, i32 indent);
--- /dev/null
+#define BH_NO_STRING
+// #define BH_DEBUG
+#define BH_DEFINE
+#include "bh.h"
+
+#include "onyxlex.h"
+#include "onyxmsgs.h"
+#include "onyxparser.h"
+#include "onyxutils.h"
+
+int main(int argc, char *argv[]) {
+ bh_file source_file;
+ bh_file_error err = bh_file_open(&source_file, argv[1]);
+ if (err != BH_FILE_ERROR_NONE) {
+ bh_printf_err("Failed to open file %s\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ bh_allocator alloc = bh_heap_allocator();
+
+ bh_file_contents fc = bh_file_read_contents(alloc, &source_file);
+ bh_file_close(&source_file);
+
+ OnyxTokenizer tokenizer = onyx_tokenizer_create(alloc, &fc);
+ onyx_lex_tokens(&tokenizer);
+ bh_arr(OnyxToken) token_arr = tokenizer.tokens;
+
+#if 0
+ bh_printf("There are %d tokens (Allocated space for %d tokens)\n", bh_arr_length(token_arr), bh_arr_capacity(token_arr));
+
+ for (OnyxToken* it = token_arr; !bh_arr_end(token_arr, it); it++) {
+ onyx_token_null_toggle(*it);
+ bh_printf("%s (%s:%l:%l)\n", onyx_get_token_type_name(it->type), it->pos.filename, it->pos.line, it->pos.column);
+ onyx_token_null_toggle(*it);
+ }
+#endif
+
+ bh_arena msg_arena;
+ bh_arena_init(&msg_arena, alloc, 4096);
+ bh_allocator msg_alloc = bh_arena_allocator(&msg_arena);
+
+ OnyxMessages msgs;
+ onyx_message_create(msg_alloc, &msgs);
+
+ bh_arena ast_arena;
+ bh_arena_init(&ast_arena, alloc, 16 * 1024 * 1024); // 16MB
+ bh_allocator ast_alloc = bh_arena_allocator(&ast_arena);
+
+ OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs);
+ OnyxAstNode* program = onyx_parse(&parser);
+
+ // NOTE: if there are errors, assume the parse tree was generated wrong,
+ // even if it may have still been generated correctly.
+ if (onyx_message_has_errors(&msgs)) {
+ onyx_message_print(&msgs);
+ goto main_exit;
+ } else {
+ onyx_ast_print(program, 0);
+ bh_printf("\nNo errors.\n");
+ }
+
+
+main_exit: // NOTE: Cleanup, since C doesn't have defer
+ bh_arena_free(&msg_arena);
+ bh_arena_free(&ast_arena);
+ onyx_parser_free(&parser);
+ onyx_tokenizer_free(&tokenizer);
+ bh_file_contents_delete(&fc);
+
+ return 0;
+}
--- /dev/null
+#include "bh.h"
+#include "onyxlex.h"
+
+static const char* onyx_token_type_names[] = {
+ "TOKEN_TYPE_UNKNOWN",
+ "TOKEN_TYPE_END_STREAM",
+
+ "TOKEN_TYPE_COMMENT",
+
+ "struct", //"TOKEN_TYPE_KEYWORD_STRUCT",
+ "use", //"TOKEN_TYPE_KEYWORD_USE",
+ "export", //"TOKEN_TYPE_KEYWORD_EXPORT",
+ "if", //"TOKEN_TYPE_KEYWORD_IF",
+ "else", //"TOKEN_TYPE_KEYWORD_ELSE",
+ "for", //"TOKEN_TYPE_KEYWORD_FOR",
+ "do", //"TOKEN_TYPE_KEYWORD_DO",
+ "return", //"TOKEN_TYPE_KEYWORD_RETURN",
+ "const", //"TOKEN_TYPE_KEYWORD_CONST",
+ "foreign", //"TOKEN_TYPE_KEYWORD_FOREIGN",
+ "proc", //"TOKEN_TYPE_KEYWORD_PROC",
+ "global", //"TOKEN_TYPE_KEYWORD_GLOBAL",
+ "as", //"TOKEN_TYPE_KEYWORD_CAST",
+
+ "->", //"TOKEN_TYPE_RIGHT_ARROW",
+ "<-", //"TOKEN_TYPE_LEFT_ARROW",
+ "(", //"TOKEN_TYPE_OPEN_PAREN",
+ ")", //"TOKEN_TYPE_CLOSE_PAREN",
+ "{", //"TOKEN_TYPE_OPEN_BRACE",
+ "}", //"TOKEN_TYPE_CLOSE_BRACE",
+ "[", //"TOKEN_TYPE_OPEN_BRACKET",
+ "]", //"TOKEN_TYPE_CLOSE_BRACKET",
+ "<", //"TOKEN_TYPE_OPEN_ANGLE",
+ ">", //"TOKEN_TYPE_CLOSE_ANGLE",
+
+ "+", // "TOKEN_TYPE_SYM_PLUS",
+ "-", // "TOKEN_TYPE_SYM_MINUS",
+ "*", // "TOKEN_TYPE_SYM_STAR",
+ "%", // "TOKEN_TYPE_SYM_PERCENT",
+ ".", // "TOKEN_TYPE_SYM_DOT",
+ "/", // "TOKEN_TYPE_SYM_FSLASH",
+ "\\", // "TOKEN_TYPE_SYM_BSLASH",
+ ":", // "TOKEN_TYPE_SYM_COLON",
+ ";", // "TOKEN_TYPE_SYM_SEMICOLON",
+ ",", // "TOKEN_TYPE_SYM_COMMA",
+ "=", // "TOKEN_TYPE_SYM_EQUALS",
+ "`", // "TOKEN_TYPE_SYM_GRAVE",
+ "~", // "TOKEN_TYPE_SYM_TILDE",
+ "!", // "TOKEN_TYPE_SYM_BANG",
+ "^", // "TOKEN_TYPE_SYM_CARET",
+ "&", // "TOKEN_TYPE_SYM_AMPERSAND",
+
+ "TOKEN_TYPE_SYMBOL",
+ "TOKEN_TYPE_LITERAL_STRING",
+ "TOKEN_TYPE_LITERAL_NUMERIC",
+
+ "TOKEN_TYPE_COUNT"
+};
+
+#ifndef LITERAL_TOKEN
+#define LITERAL_TOKEN(token, token_type) \
+ if (token_lit(tokenizer, &tk, token, token_type)) goto token_parsed;
+#endif
+
+#ifndef INCREMENT_CURR_TOKEN
+#define INCREMENT_CURR_TOKEN(tkn) { \
+ if (*(tkn)->curr == '\n') { \
+ (tkn)->line_number++; \
+ (tkn)->line_start = (tkn)->curr + 1; \
+ } \
+ (tkn)->curr++; \
+}
+#endif
+
+static b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, OnyxTokenType type) {
+ i64 len = chars_match(tokenizer->curr, lit);
+ if (len > 0) {
+ tk->type = type;
+ tk->token = tokenizer->curr;
+ tk->length = len;
+ tk->pos.line = tokenizer->line_number;
+ tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
+
+ tokenizer->curr += len;
+
+ return 1;
+ }
+ return 0;
+}
+
+const char* onyx_get_token_type_name(OnyxTokenType tkn_type) {
+ return onyx_token_type_names[tkn_type];
+}
+
+void onyx_token_null_toggle(OnyxToken tkn) {
+ static char backup = 0;
+ char tmp = tkn.token[tkn.length];
+ tkn.token[tkn.length] = backup;
+ backup = tmp;
+}
+
+OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
+ OnyxToken tk;
+
+ // Skip whitespace
+ while (char_is_whitespace(*tokenizer->curr) && tokenizer->curr != tokenizer->end)
+ INCREMENT_CURR_TOKEN(tokenizer)
+
+ tk.type = TOKEN_TYPE_UNKNOWN;
+ tk.token = tokenizer->curr;
+ tk.length = 1;
+ tk.pos.filename = tokenizer->filename;
+ tk.pos.line = tokenizer->line_number;
+ tk.pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
+
+ if (tokenizer->curr == tokenizer->end) {
+ tk.type = TOKEN_TYPE_END_STREAM;
+ goto token_parsed;
+ }
+
+ // Comments
+ if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
+ tokenizer->curr += 2;
+ tk.type = TOKEN_TYPE_COMMENT;
+ tk.token = tokenizer->curr;
+ u16 layers = 1;
+
+ while (layers >= 1) {
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ if (tokenizer->curr == tokenizer->end) {
+ tk.type = TOKEN_TYPE_END_STREAM;
+ break;
+ }
+
+ if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
+ layers++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') {
+ layers--;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ tk.length = tokenizer->curr - tk.token - 2;
+ goto token_parsed;
+ }
+
+ LITERAL_TOKEN("struct", TOKEN_TYPE_KEYWORD_STRUCT);
+ LITERAL_TOKEN("export", TOKEN_TYPE_KEYWORD_EXPORT);
+ LITERAL_TOKEN("use", TOKEN_TYPE_KEYWORD_USE);
+ LITERAL_TOKEN("if", TOKEN_TYPE_KEYWORD_IF);
+ LITERAL_TOKEN("else", TOKEN_TYPE_KEYWORD_ELSE);
+ LITERAL_TOKEN("foreign", TOKEN_TYPE_KEYWORD_FOREIGN);
+ LITERAL_TOKEN("for", TOKEN_TYPE_KEYWORD_FOR);
+ LITERAL_TOKEN("return", TOKEN_TYPE_KEYWORD_RETURN);
+ LITERAL_TOKEN("const", TOKEN_TYPE_KEYWORD_CONST);
+ LITERAL_TOKEN("do", TOKEN_TYPE_KEYWORD_DO);
+ LITERAL_TOKEN("proc", TOKEN_TYPE_KEYWORD_PROC);
+ LITERAL_TOKEN("global", TOKEN_TYPE_KEYWORD_GLOBAL);
+ LITERAL_TOKEN("as", TOKEN_TYPE_KEYWORD_CAST);
+ LITERAL_TOKEN("->", TOKEN_TYPE_RIGHT_ARROW);
+ LITERAL_TOKEN("<-", TOKEN_TYPE_RIGHT_ARROW);
+ LITERAL_TOKEN("(", TOKEN_TYPE_OPEN_PAREN);
+ LITERAL_TOKEN(")", TOKEN_TYPE_CLOSE_PAREN);
+ LITERAL_TOKEN("{", TOKEN_TYPE_OPEN_BRACE);
+ LITERAL_TOKEN("}", TOKEN_TYPE_CLOSE_BRACE);
+ LITERAL_TOKEN("[", TOKEN_TYPE_OPEN_BRACKET);
+ LITERAL_TOKEN("]", TOKEN_TYPE_CLOSE_BRACKET);
+ LITERAL_TOKEN("<", TOKEN_TYPE_OPEN_ANGLE);
+ LITERAL_TOKEN(">", TOKEN_TYPE_CLOSE_ANGLE);
+ LITERAL_TOKEN("+", TOKEN_TYPE_SYM_PLUS);
+ LITERAL_TOKEN("-", TOKEN_TYPE_SYM_MINUS);
+ LITERAL_TOKEN("*", TOKEN_TYPE_SYM_STAR);
+ LITERAL_TOKEN(".", TOKEN_TYPE_SYM_DOT);
+ LITERAL_TOKEN("%", TOKEN_TYPE_SYM_PERCENT);
+ LITERAL_TOKEN("/", TOKEN_TYPE_SYM_FSLASH);
+ LITERAL_TOKEN("\\", TOKEN_TYPE_SYM_BSLASH);
+ LITERAL_TOKEN(":", TOKEN_TYPE_SYM_COLON);
+ LITERAL_TOKEN(";", TOKEN_TYPE_SYM_SEMICOLON);
+ LITERAL_TOKEN(",", TOKEN_TYPE_SYM_COMMA);
+ LITERAL_TOKEN("=", TOKEN_TYPE_SYM_EQUALS);
+ LITERAL_TOKEN("`", TOKEN_TYPE_SYM_GRAVE);
+ LITERAL_TOKEN("~", TOKEN_TYPE_SYM_TILDE);
+ LITERAL_TOKEN("!", TOKEN_TYPE_SYM_BANG);
+ LITERAL_TOKEN("^", TOKEN_TYPE_SYM_CARET);
+ LITERAL_TOKEN("&", TOKEN_TYPE_SYM_AMPERSAND);
+
+ // Symbols
+ if (char_is_alpha(*tk.token)) {
+ u64 len = 0;
+ while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ tk.length = len;
+ tk.type = TOKEN_TYPE_SYMBOL;
+ goto token_parsed;
+ }
+
+ // String literal
+ if (*tk.token == '"') {
+ u64 len = 0;
+ u64 slash_count = 0;
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ while (!(*tokenizer->curr == '"' && slash_count == 0)) {
+ len++;
+
+ if (*tokenizer->curr == '\\') {
+ slash_count += 1;
+ slash_count %= 2;
+ } else {
+ slash_count = 0;
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ tk.token++;
+ tk.type = TOKEN_TYPE_LITERAL_STRING;
+ tk.length = len;
+ goto token_parsed;
+ }
+
+ // Number literal
+ if (char_is_num(*tokenizer->curr)) {
+ u32 len = 1;
+ while (char_is_num(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '.') {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ tk.type = TOKEN_TYPE_LITERAL_NUMERIC;
+ tk.length = len;
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+token_parsed:
+ bh_arr_push(tokenizer->tokens, tk);
+
+ return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1];
+}
+
+OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) {
+ OnyxTokenizer tknizer = {
+ .start = fc->data,
+ .curr = fc->data,
+ .end = fc->data + fc->length,
+
+ .filename = fc->filename,
+
+ .line_number = 1,
+ .line_start = fc->data,
+ .tokens = NULL,
+ };
+
+ bh_arr_new(allocator, tknizer.tokens, 512);
+ return tknizer;
+}
+
+void onyx_tokenizer_free(OnyxTokenizer* tokenizer) {
+ bh_arr_free(tokenizer->tokens);
+}
+
+void onyx_lex_tokens(OnyxTokenizer* tokenizer) {
+ OnyxToken* tk;
+ do {
+ tk = onyx_get_token(tokenizer);
+ } while (tk->type != TOKEN_TYPE_END_STREAM);
+}
--- /dev/null
+
+#include "onyxmsgs.h"
+
+static const char* msg_formats[] = {
+ "expected token '%s', got '%s'",
+ "unexpected token '%s'",
+ "unknown type '%s'",
+ "expected lval '%s'",
+ "attempt to assign to constant '%s'",
+ "unknown symbol '%s'",
+ "redefinition of function '%s'",
+ "mismatched types for binary operator, '%s', '%s'",
+ "mismatched types on assignment, '%s', '%s'",
+};
+
+void onyx_message_add(OnyxMessages* msgs, OnyxMessageType type, OnyxFilePos pos, ...) {
+ OnyxMessage* msg = bh_alloc_item(msgs->allocator, OnyxMessage);
+ msg->type = type;
+ msg->pos = pos;
+
+ va_list arg_list;
+ va_start(arg_list, pos);
+ bh_snprintf_va(msg->text, ONYX_MSG_BUFFER_SIZE, msg_formats[type], arg_list);
+ va_end(arg_list);
+
+ OnyxMessage** walker = &msgs->first;
+ while (*walker && (*walker)->pos.line < pos.line) walker = &(*walker)->next;
+ while (*walker && (*walker)->pos.line == pos.line && (*walker)->pos.column < pos.column) walker = &(*walker)->next;
+
+ msg->next = *walker;
+ *walker = msg;
+}
+
+void onyx_message_print(OnyxMessages* msgs) {
+ OnyxMessage* msg = msgs->first;
+
+ while (msg) {
+ if (msg->pos.filename) {
+ bh_printf("(%s:%l:%l) %s\n", msg->pos.filename, msg->pos.line, msg->pos.column, msg->text);
+ } else {
+ bh_printf("(%l:%l) %s\n", msg->pos.line, msg->pos.column, msg->text);
+ }
+ msg = msg->next;
+ }
+}
+
+b32 onyx_message_has_errors(OnyxMessages* msgs) {
+ return msgs->first != NULL;
+}
+
+void onyx_message_create(bh_allocator allocator, OnyxMessages* msgs) {
+ msgs->allocator = allocator;
+ msgs->first = NULL;
+}
--- /dev/null
+
+#include "onyxlex.h"
+#include "onyxmsgs.h"
+#include "onyxparser.h"
+
+static const char* ast_node_names[] = {
+ "ERROR",
+ "PROGRAM",
+
+ "FUNCDEF",
+ "BLOCK",
+ "SCOPE",
+ "LOCAL",
+
+ "ADD",
+ "MINUS",
+ "MULTIPLY",
+ "DIVIDE",
+ "MODULUS",
+ "NEGATE",
+
+ "TYPE",
+ "LITERAL",
+ "CAST",
+ "PARAM",
+ "CALL",
+ "ASSIGN",
+ "RETURN",
+
+ "EQUAL",
+ "NOT_EQUAL",
+ "GREATER",
+ "GREATER_EQUAL",
+ "LESS",
+ "LESS_EQUAL",
+ "NOT",
+
+ "IF",
+ "LOOP",
+
+ "ONYX_AST_NODE_KIND_COUNT",
+};
+
+struct OnyxTypeInfo builtin_types[] = {
+ { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" },
+ { ONYX_TYPE_INFO_KIND_VOID, 0, "void", 0, 0, 0, 0, 1 },
+
+ { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1, 1 },
+
+ { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0, 1 },
+ { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0, 1 },
+
+ { ONYX_TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0, 1 },
+ { ONYX_TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0, 1 },
+
+ { ONYX_TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0, 1 },
+ { ONYX_TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0, 1},
+ { ONYX_TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0, 1 },
+
+ { 0xffffffff } // Sentinel
+};
+
+static OnyxAstNode error_node = { { ONYX_AST_NODE_KIND_ERROR, 0, NULL, &builtin_types[0], NULL, NULL, NULL } };
+
+// NOTE: Forward declarations
+static void parser_next_token(OnyxParser* parser);
+static void parser_prev_token(OnyxParser* parser);
+static b32 is_terminating_token(OnyxTokenType token_type);
+static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type);
+static OnyxAstNodeScope* enter_scope(OnyxParser* parser);
+static OnyxAstNodeScope* leave_scope(OnyxParser* parser);
+static void insert_identifier(OnyxParser* parser, OnyxAstNode* ident, b32 is_local);
+static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident);
+static OnyxAstNode* parse_factor(OnyxParser* parser);
+static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left);
+static OnyxAstNode* parse_expression(OnyxParser* parser);
+static OnyxAstNode* parse_if_stmt(OnyxParser* parser);
+static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret);
+static OnyxAstNode* parse_return_statement(OnyxParser* parser);
+static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function);
+static OnyxAstNode* parse_statement(OnyxParser* parser);
+static OnyxTypeInfo* parse_type(OnyxParser* parser);
+static OnyxAstNodeParam* parse_function_params(OnyxParser* parser);
+static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser);
+static OnyxAstNode* parse_top_level_statement(OnyxParser* parser);
+
+static void parser_next_token(OnyxParser* parser) {
+ parser->prev_token = parser->curr_token;
+ parser->curr_token++;
+ while (parser->curr_token->type == TOKEN_TYPE_COMMENT) parser->curr_token++;
+}
+
+static void parser_prev_token(OnyxParser* parser) {
+ // TODO: This is probably wrong
+ parser->prev_token--;
+ while (parser->prev_token->type == TOKEN_TYPE_COMMENT) parser->prev_token--;
+ parser->curr_token = parser->prev_token;
+ parser->prev_token--;
+}
+
+static b32 is_terminating_token(OnyxTokenType token_type) {
+ switch (token_type) {
+ case TOKEN_TYPE_SYM_SEMICOLON:
+ case TOKEN_TYPE_CLOSE_BRACE:
+ case TOKEN_TYPE_OPEN_BRACE:
+ case TOKEN_TYPE_END_STREAM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void find_token(OnyxParser* parser, OnyxTokenType token_type) {
+ while (parser->curr_token->type != token_type && !is_terminating_token(parser->curr_token->type)) {
+ parser_next_token(parser);
+ }
+}
+
+// Advances to next token no matter what
+static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) {
+ OnyxToken* token = parser->curr_token;
+ parser_next_token(parser);
+
+ if (token->type != token_type) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ token->pos,
+ onyx_get_token_type_name(token_type), onyx_get_token_type_name(token->type));
+ return NULL;
+ }
+
+ return token;
+}
+
+static OnyxAstNodeScope* enter_scope(OnyxParser* parser) {
+ OnyxAstNodeScope* scope = (OnyxAstNodeScope*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE);
+ scope->prev_scope = parser->curr_scope;
+ parser->curr_scope = scope;
+ return scope;
+}
+
+static OnyxAstNodeScope* leave_scope(OnyxParser* parser) {
+ // NOTE: Can't leave a scope if there is no scope
+ assert(parser->curr_scope != NULL);
+
+ for (OnyxAstNodeLocal *walker = parser->curr_scope->last_local; walker != NULL; walker = walker->prev_local) {
+ remove_identifier(parser, (OnyxAstNode *) walker);
+ }
+
+ parser->curr_scope = parser->curr_scope->prev_scope;
+ return parser->curr_scope;
+}
+
+static OnyxAstNode* lookup_identifier(OnyxParser* parser, OnyxToken* token) {
+ OnyxAstNode* ident = NULL;
+
+ onyx_token_null_toggle(*token);
+ if (bh_hash_has(OnyxAstNode*, parser->identifiers, token->token)) {
+ ident = bh_hash_get(OnyxAstNode*, parser->identifiers, token->token);
+ }
+ onyx_token_null_toggle(*token);
+
+ return ident;
+}
+
+static void insert_identifier(OnyxParser* parser, OnyxAstNode* ident, b32 is_local) {
+ OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident;
+ if (is_local) {
+ OnyxAstNodeScope* scope = parser->curr_scope;
+ local->prev_local = scope->last_local;
+ scope->last_local = local;
+ }
+
+ onyx_token_null_toggle(*local->token);
+ if (bh_hash_has(OnyxAstNode*, parser->identifiers, local->token->token)) {
+ local->shadowed = bh_hash_get(OnyxAstNode*, parser->identifiers, local->token->token);
+ }
+
+ bh_hash_put(OnyxAstNodeLocal*, parser->identifiers, local->token->token, local);
+ onyx_token_null_toggle(*local->token);
+}
+
+static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident) {
+ OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident;
+
+ onyx_token_null_toggle(*local->token);
+ if (local->shadowed) {
+ bh_hash_put(OnyxAstNode*, parser->identifiers, local->token->token, local->shadowed);
+ } else {
+ bh_hash_delete(OnyxAstNode*, parser->identifiers, local->token->token);
+ }
+ onyx_token_null_toggle(*local->token);
+}
+
+static OnyxAstNode* parse_factor(OnyxParser* parser) {
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_OPEN_PAREN:
+ {
+ parser_next_token(parser);
+ OnyxAstNode* expr = parse_expression(parser);
+ expect(parser, TOKEN_TYPE_CLOSE_PAREN);
+ return expr;
+ }
+
+ case TOKEN_TYPE_SYMBOL:
+ {
+ OnyxToken* sym_token = expect(parser, TOKEN_TYPE_SYMBOL);
+ OnyxAstNode* sym_node = lookup_identifier(parser, sym_token);
+ if (sym_node == NULL) {
+ onyx_token_null_toggle(*sym_token);
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
+ sym_token->pos, sym_token->token);
+ onyx_token_null_toggle(*sym_token);
+ }
+
+ // TODO: Handle calling a function
+ return sym_node;
+ }
+
+ case TOKEN_TYPE_LITERAL_NUMERIC:
+ {
+ OnyxAstNode* lit_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL);
+ lit_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_INT64];
+ lit_node->token = expect(parser, TOKEN_TYPE_LITERAL_NUMERIC);
+ lit_node->flags |= ONYX_AST_FLAG_COMPTIME;
+ return lit_node;
+ }
+
+ default:
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(parser->curr_token->type));
+ }
+
+ return NULL;
+}
+
+static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left) {
+ OnyxAstNodeKind kind = -1;
+
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_SYM_PLUS: kind = ONYX_AST_NODE_KIND_ADD; break;
+ case TOKEN_TYPE_SYM_MINUS: kind = ONYX_AST_NODE_KIND_MINUS; break;
+ case TOKEN_TYPE_SYM_STAR: kind = ONYX_AST_NODE_KIND_MULTIPLY; break;
+ case TOKEN_TYPE_SYM_FSLASH: kind = ONYX_AST_NODE_KIND_DIVIDE; break;
+ case TOKEN_TYPE_SYM_PERCENT: kind = ONYX_AST_NODE_KIND_MODULUS; break;
+ }
+
+ if (kind != -1) {
+ OnyxToken* bin_op_tok = parser->curr_token;
+ parser_next_token(parser);
+
+ OnyxAstNode* right = parse_factor(parser);
+
+ // NOTE: Typechecking like this will not be here for long but
+ // want to start working on it early
+ if (left->type != right->type) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE,
+ bin_op_tok->pos,
+ left->type->name, right->type->name);
+ return &error_node;
+ }
+
+ OnyxAstNode* bin_op = onyx_ast_node_new(parser->allocator, kind);
+ bin_op->left = left;
+ bin_op->right = right;
+ bin_op->type = left->type;
+ return bin_op;
+ }
+
+ return &error_node;
+}
+
+static OnyxAstNode* parse_expression(OnyxParser* parser) {
+ OnyxAstNode* left = parse_factor(parser);
+
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_SYM_PLUS:
+ case TOKEN_TYPE_SYM_MINUS:
+ case TOKEN_TYPE_SYM_STAR:
+ case TOKEN_TYPE_SYM_FSLASH:
+ case TOKEN_TYPE_SYM_PERCENT:
+ {
+ OnyxAstNode* expr = parse_bin_op(parser, left);
+ return expr;
+ }
+ case TOKEN_TYPE_KEYWORD_CAST:
+ {
+ parser_next_token(parser);
+ OnyxTypeInfo* type = parse_type(parser);
+
+ // NOTE: This may be premature in the parser but a cast
+ // node doesn't need to exist if the source and target
+ // types are the same
+ if (type == left->type) return left;
+
+ OnyxAstNode* cast_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_CAST);
+ cast_node->type = type;
+ cast_node->left = left;
+
+ return cast_node;
+ }
+ }
+
+ return left;
+}
+
+static OnyxAstNode* parse_if_stmt(OnyxParser* parser) {
+ return &error_node;
+}
+
+// Returns 1 if the symbol was consumed. Returns 0 otherwise
+// ret is set to the statement to insert
+static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) {
+ if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) return 0;
+ OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+
+ switch (parser->curr_token->type) {
+ // NOTE: Declaration
+ case TOKEN_TYPE_SYM_COLON:
+ {
+ parser_next_token(parser);
+ OnyxTypeInfo* type = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];
+ u32 flags = ONYX_AST_FLAG_LVAL;
+
+ // NOTE: var: const ...
+ if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_CONST) {
+ parser_next_token(parser);
+ flags |= ONYX_AST_FLAG_CONST;
+ }
+
+ // NOTE: var: type
+ if (parser->curr_token->type == TOKEN_TYPE_SYMBOL) {
+ type = parse_type(parser);
+ }
+
+ OnyxAstNodeLocal* local = (OnyxAstNodeLocal*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LOCAL);
+ local->token = symbol;
+ local->type = type;
+ local->flags |= flags;
+
+ insert_identifier(parser, (OnyxAstNode *) local, 1);
+
+ if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS) {
+ OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);
+ assignment->token = parser->curr_token;
+ parser_next_token(parser);
+
+ OnyxAstNode* expr = parse_expression(parser);
+ assignment->right = expr;
+ assignment->left = (OnyxAstNode*) local;
+
+ if (local->type->is_known && local->type != expr->type) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH,
+ assignment->token->pos,
+ local->type->name, expr->type->name);
+ return 1;
+ } else if (!local->type->is_known) {
+ local->type = expr->type;
+ }
+ *ret = assignment;
+ }
+ return 1;
+ }
+
+ // NOTE: Assignment
+ case TOKEN_TYPE_SYM_EQUALS:
+ {
+ parser_next_token(parser);
+
+ OnyxAstNode* lval = lookup_identifier(parser, symbol);
+
+ if (lval != NULL && lval->flags & ONYX_AST_FLAG_LVAL && (lval->flags & ONYX_AST_FLAG_CONST) == 0) {
+ OnyxAstNode* rval = parse_expression(parser);
+ OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT);
+ assignment->right = rval;
+ assignment->left = lval;
+ *ret = assignment;
+ return 1;
+ }
+
+ onyx_token_null_toggle(*symbol);
+ if (lval == NULL) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
+ symbol->pos, symbol->token);
+ }
+
+ else if ((lval->flags & ONYX_AST_FLAG_LVAL) == 0) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_NOT_LVAL,
+ symbol->pos, symbol->token);
+ }
+
+ else if (lval->flags & ONYX_AST_FLAG_CONST) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_ASSIGN_CONST,
+ symbol->pos, symbol->token);
+ }
+ onyx_token_null_toggle(*symbol);
+
+ find_token(parser, TOKEN_TYPE_SYM_SEMICOLON);
+ return 1;
+ }
+
+ default:
+ parser_prev_token(parser);
+ }
+
+ return 0;
+}
+
+static OnyxAstNode* parse_return_statement(OnyxParser* parser) {
+ expect(parser, TOKEN_TYPE_KEYWORD_RETURN);
+
+ OnyxAstNode* return_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_RETURN);
+ return_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_VOID];
+ OnyxAstNode* expr = NULL;
+
+ if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {
+ expr = parse_expression(parser);
+
+ if (expr == NULL || expr == &error_node) {
+ return &error_node;
+ } else {
+ return_node->next = expr;
+ }
+ }
+
+ return return_node;
+}
+
+static OnyxAstNode* parse_statement(OnyxParser* parser) {
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_KEYWORD_RETURN:
+ return parse_return_statement(parser);
+
+ case TOKEN_TYPE_OPEN_BRACE:
+ return (OnyxAstNode *) parse_block(parser, 0);
+
+ case TOKEN_TYPE_SYMBOL:
+ {
+ OnyxAstNode* ret = NULL;
+ if (parse_symbol_statement(parser, &ret)) return ret;
+ // fallthrough
+ }
+
+ case TOKEN_TYPE_OPEN_PAREN:
+ case TOKEN_TYPE_SYM_PLUS:
+ case TOKEN_TYPE_SYM_MINUS:
+ case TOKEN_TYPE_SYM_BANG:
+ case TOKEN_TYPE_LITERAL_NUMERIC:
+ case TOKEN_TYPE_LITERAL_STRING:
+ return parse_expression(parser);
+
+ case TOKEN_TYPE_KEYWORD_IF:
+ return parse_if_stmt(parser);
+
+ default:
+ return NULL;
+ }
+}
+
+static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function) {
+ // --- is for an empty block
+ if (parser->curr_token->type == TOKEN_TYPE_SYM_MINUS) {
+ expect(parser, TOKEN_TYPE_SYM_MINUS);
+ expect(parser, TOKEN_TYPE_SYM_MINUS);
+ expect(parser, TOKEN_TYPE_SYM_MINUS);
+ return NULL;
+ }
+
+ expect(parser, TOKEN_TYPE_OPEN_BRACE);
+
+ OnyxAstNodeBlock* block = (OnyxAstNodeBlock *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_BLOCK);
+ if (belongs_to_function) {
+ OnyxAstNodeScope* scope = enter_scope(parser);
+ block->scope = scope;
+ }
+
+ OnyxAstNode** next = &block->body;
+ OnyxAstNode* stmt = NULL;
+ while (parser->curr_token->type != TOKEN_TYPE_CLOSE_BRACE) {
+ stmt = parse_statement(parser);
+
+ if (stmt != NULL && stmt->kind != ONYX_AST_NODE_KIND_ERROR) {
+ *next = stmt;
+ next = &stmt->next;
+ }
+
+ if (parser->curr_token->type != TOKEN_TYPE_SYM_SEMICOLON) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(TOKEN_TYPE_SYM_SEMICOLON),
+ onyx_get_token_type_name(parser->curr_token->type));
+
+ find_token(parser, TOKEN_TYPE_SYM_SEMICOLON);
+ }
+ parser_next_token(parser);
+ }
+
+ expect(parser, TOKEN_TYPE_CLOSE_BRACE);
+
+ if (belongs_to_function) {
+ leave_scope(parser);
+ }
+
+ return block;
+}
+
+static OnyxTypeInfo* parse_type(OnyxParser* parser) {
+ OnyxTypeInfo* type_info = &builtin_types[ONYX_TYPE_INFO_KIND_UNKNOWN];
+
+ OnyxToken* symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+ if (symbol == NULL) return type_info;
+
+ onyx_token_null_toggle(*symbol);
+
+ if (!bh_hash_has(OnyxAstNode*, parser->identifiers, symbol->token)) {
+ onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->token);
+ } else {
+ OnyxAstNode* type_info_node = bh_hash_get(OnyxAstNode*, parser->identifiers, symbol->token);
+
+ if (type_info_node->kind == ONYX_AST_NODE_KIND_TYPE) {
+ type_info = type_info_node->type;
+ }
+ }
+
+ onyx_token_null_toggle(*symbol);
+ return type_info;
+}
+
+static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) {
+ expect(parser, TOKEN_TYPE_OPEN_PAREN);
+
+ if (parser->curr_token->type == TOKEN_TYPE_CLOSE_PAREN) {
+ parser_next_token(parser);
+ return NULL;
+ }
+
+ OnyxAstNodeParam* first_param = NULL;
+ u64 param_count = 0;
+
+ OnyxAstNodeParam* curr_param = NULL;
+ OnyxAstNodeParam* trailer = NULL;
+
+ OnyxToken* symbol;
+ while (parser->curr_token->type != TOKEN_TYPE_CLOSE_PAREN) {
+ if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser);
+ param_count++;
+
+ symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+
+ curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM);
+ curr_param->token = symbol;
+ curr_param->type = parse_type(parser);
+
+ if (first_param == NULL) first_param = curr_param;
+
+ curr_param->next = NULL;
+ if (trailer) trailer->next = curr_param;
+
+ trailer = curr_param;
+ }
+
+ first_param->param_count = param_count;
+
+ parser_next_token(parser); // Skip the )
+ return first_param;
+}
+
+static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) {
+ expect(parser, TOKEN_TYPE_KEYWORD_PROC);
+
+ OnyxAstNodeFuncDef* func_def = (OnyxAstNodeFuncDef *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_FUNCDEF);
+
+ OnyxAstNodeParam* params = parse_function_params(parser);
+ func_def->params = params;
+
+ expect(parser, TOKEN_TYPE_RIGHT_ARROW);
+
+ OnyxTypeInfo* return_type = parse_type(parser);
+ func_def->return_type = return_type;
+
+ for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {
+ insert_identifier(parser, (OnyxAstNode *) p, 0);
+ }
+
+ func_def->body = parse_block(parser, 1);
+
+ for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) {
+ remove_identifier(parser, (OnyxAstNode *) p);
+ }
+
+ return func_def;
+}
+
+static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) {
+ switch (parser->curr_token->type) {
+ case TOKEN_TYPE_KEYWORD_USE:
+ assert(0);
+ break;
+
+ case TOKEN_TYPE_KEYWORD_EXPORT:
+ {
+ expect(parser, TOKEN_TYPE_KEYWORD_EXPORT);
+ if (parser->curr_token->type != TOKEN_TYPE_SYMBOL) {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(TOKEN_TYPE_SYMBOL),
+ onyx_get_token_type_name(parser->curr_token->type));
+ break;
+ }
+
+ OnyxAstNode* top_level_decl = parse_top_level_statement(parser);
+ top_level_decl->flags |= ONYX_AST_FLAG_EXPORTED;
+ return top_level_decl;
+ }
+
+ case TOKEN_TYPE_SYMBOL:
+ {
+ OnyxToken* symbol = parser->curr_token;
+ parser_next_token(parser);
+
+ expect(parser, TOKEN_TYPE_SYM_COLON);
+ expect(parser, TOKEN_TYPE_SYM_COLON);
+
+ if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_PROC) {
+ OnyxAstNodeFuncDef* func_def = parse_function_definition(parser);
+ func_def->token = symbol;
+
+ onyx_token_null_toggle(*symbol);
+
+ if (!bh_hash_has(OnyxAstNode *, parser->identifiers, symbol->token)) {
+ bh_hash_put(OnyxAstNode *, parser->identifiers, symbol->token, (OnyxAstNode *) func_def);
+ } else {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION,
+ symbol->pos,
+ symbol->token);
+
+ // NOTE: I really wish C had defer...
+ onyx_token_null_toggle(*symbol);
+ return NULL;
+ }
+
+ onyx_token_null_toggle(*symbol);
+ return (OnyxAstNode *) func_def;
+
+ } else if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_STRUCT) {
+ // Handle struct case
+ assert(0);
+ } else {
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
+ parser->curr_token->pos,
+ onyx_get_token_type_name(parser->curr_token->type));
+ break;
+ }
+ }
+ }
+
+ parser_next_token(parser);
+ return NULL;
+}
+
+
+
+
+
+const char* onyx_ast_node_kind_string(OnyxAstNodeKind kind) {
+ return ast_node_names[kind];
+}
+
+OnyxAstNode* onyx_ast_node_new(bh_allocator alloc, OnyxAstNodeKind kind) {\
+ OnyxAstNode* node = (OnyxAstNode *) bh_alloc(alloc, sizeof(OnyxAstNode));
+ node->kind = kind;
+ node->flags = 0;
+ node->token = NULL;
+ node->type = NULL;
+ node->next = NULL;
+ node->left = NULL;
+ node->right = NULL;
+
+ return node;
+}
+
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) {
+ OnyxParser parser;
+
+ bh_hash_init(bh_heap_allocator(), parser.identifiers);
+
+ OnyxTypeInfo* it = &builtin_types[0];
+ while (it->kind != 0xffffffff) {
+ OnyxAstNode* tmp = onyx_ast_node_new(alloc, ONYX_AST_NODE_KIND_TYPE);
+ tmp->type = it;
+ bh_hash_put(OnyxAstNode*, parser.identifiers, (char *)it->name, tmp);
+ it++;
+ }
+
+ parser.allocator = alloc;
+ parser.tokenizer = tokenizer;
+ parser.curr_token = tokenizer->tokens;
+ parser.prev_token = NULL;
+ parser.msgs = msgs;
+ parser.curr_scope = NULL;
+
+ return parser;
+}
+
+void onyx_parser_free(OnyxParser* parser) {
+ bh_hash_free(parser->identifiers);
+}
+
+OnyxAstNode* onyx_parse(OnyxParser *parser) {
+ OnyxAstNode* program = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PROGRAM);
+
+ OnyxAstNode** prev_stmt = &program->next;
+ OnyxAstNode* curr_stmt = NULL;
+ while (parser->curr_token->type != TOKEN_TYPE_END_STREAM) {
+ curr_stmt = parse_top_level_statement(parser);
+
+ // Building a linked list of statements down the "next" chain
+ if (curr_stmt != NULL && curr_stmt != &error_node) {
+ *prev_stmt = curr_stmt;
+ prev_stmt = &curr_stmt->next;
+ }
+ }
+
+ return program;
+}
--- /dev/null
+#include "onyxutils.h"
+#include "onyxlex.h"
+#include "onyxparser.h"
+
+#define print_indent { if (indent > 0) bh_printf("\n"); for (int i = 0; i < indent; i++) bh_printf(" "); }
+
+void onyx_ast_print(OnyxAstNode* node, i32 indent) {
+ if (node == NULL) return;
+
+ print_indent;
+ bh_printf("(%d) %s ", node->flags, onyx_ast_node_kind_string(node->kind));
+
+ switch (node->kind) {
+ case ONYX_AST_NODE_KIND_PROGRAM: {
+ if (node->next)
+ onyx_ast_print(node->next, indent + 1);
+
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_FUNCDEF: {
+ bh_printf("(%b) ", node->token->token, node->token->length);
+ OnyxAstNodeFuncDef* fd = &node->as_funcdef;
+
+ print_indent;
+ bh_printf("Params ");
+ if (fd->params)
+ onyx_ast_print((OnyxAstNode *) fd->params, 0);
+
+ print_indent;
+ bh_printf("Returns %s", fd->return_type->name);
+
+ print_indent;
+ bh_printf("Body");
+ if (fd->body)
+ onyx_ast_print((OnyxAstNode *) fd->body, indent + 1);
+
+ if (fd->next)
+ onyx_ast_print((OnyxAstNode *) fd->next, indent);
+
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_PARAM: {
+ OnyxAstNodeParam* param = &node->as_param;
+ bh_printf("%b %s", param->token->token, param->token->length, param->type->name);
+ if (param->next && indent == 0) {
+ bh_printf(", ");
+ onyx_ast_print((OnyxAstNode *) param->next, 0);
+ }
+
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_BLOCK: {
+ OnyxAstNodeBlock* block = &node->as_block;
+ if (block->scope) {
+ onyx_ast_print((OnyxAstNode *) block->scope, indent + 1);
+ }
+
+ if (block->body) {
+ onyx_ast_print((OnyxAstNode *) block->body, indent + 1);
+ }
+
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_SCOPE: {
+ OnyxAstNodeScope* scope = &node->as_scope;
+ if (scope->last_local) {
+ onyx_ast_print((OnyxAstNode *) scope->last_local, 0);
+ }
+
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_LOCAL: {
+ OnyxAstNodeLocal* local = &node->as_local;
+ bh_printf("%b %s", local->token->token, local->token->length, local->type->name);
+ if (local->prev_local && indent == 0) {
+ bh_printf(", ");
+ onyx_ast_print((OnyxAstNode *) local->prev_local, 0);
+ }
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_RETURN: {
+ if (node->next) {
+ onyx_ast_print(node->next, indent + 1);
+ }
+
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_LITERAL: {
+ bh_printf("%b", node->token->token, node->token->length);
+ break;
+ }
+
+ case ONYX_AST_NODE_KIND_CAST: {
+ bh_printf("to %s ", node->type->name);
+ onyx_ast_print(node->left, indent + 1);
+ if (node->next) {
+ onyx_ast_print(node->next, indent);
+ }
+ break;
+ }
+
+ default: {
+ onyx_ast_print(node->left, indent + 1);
+ onyx_ast_print(node->right, indent + 1);
+ if (node->next) {
+ onyx_ast_print(node->next, indent);
+ }
+ break;
+ }
+ }
+}