├── .gitignore ├── Makefile ├── README.md ├── doc ├── LICENSE.md └── STYLE.md ├── src ├── allocator.c ├── allocator.h ├── allocator_arena.c ├── allocator_null.c ├── allocator_std.c ├── array.c ├── array.h ├── build.c ├── build.h ├── context.c ├── context.h ├── dump.c ├── dump.h ├── lexemes.h ├── lexer.c ├── lexer.h ├── main.c ├── parser.c ├── parser.h ├── path.c ├── path.h ├── profiler.c ├── profiler.h ├── profiler_null.c ├── profiler_spall.c ├── project.c ├── project.h ├── report.c ├── report.h ├── sched.c ├── sched.h ├── sched_async.c ├── sched_null.c ├── sched_sync.c ├── strbuf.c ├── strbuf.h ├── string.c ├── string.h ├── support.h ├── thread.c ├── thread.h ├── threadpool.c ├── threadpool.h ├── tree.c ├── tree.h ├── utility.c ├── utility.h └── vendor │ └── spall.h ├── tests └── main.odin └── unity.c /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !/.gitignore 3 | codin 4 | codin.exe -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Compilation options: 2 | # * LTO - Link time optimization 3 | # * ASAN - Address sanitizer 4 | # * TSAN - Thread sanitizer 5 | # * UBSAN - Undefined behavior sanitizer 6 | # * DEBUG - Debug build 7 | # * PROFILE - Profile build 8 | # * SRCDIR - Out of tree builds 9 | # * UNUSED - Removed unused references 10 | LTO ?= 0 11 | ASAN ?= 0 12 | TSAN ?= 0 13 | UBSAN ?= 0 14 | DEBUG ?= 0 15 | PROFILE ?= 0 16 | SRCDIR ?= src 17 | UNUSED ?= 1 18 | 19 | # Some recursive make functions to avoid shelling out. 20 | rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) 21 | uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1))) 22 | 23 | # Compiler and linker 24 | CC := gcc 25 | CC ?= clang 26 | LD := $(CC) 27 | 28 | # Determine build type. 29 | ifeq ($(DEBUG), 1) 30 | TYPE := debug 31 | else ifeq ($(PROFILE),1) 32 | TYPE := profile 33 | else 34 | TYPE := release 35 | endif 36 | 37 | BIN := codin 38 | 39 | OBJDIR := .build/$(TYPE)/objs 40 | DEPDIR := .build/$(TYPE)/deps 41 | 42 | # Collect all .c files for build in the source directory. 43 | SRCS := $(call rwildcard, $(SRCDIR)/, *c) 44 | 45 | # Generate object and dependency filenames. 46 | OBJS := $(filter %.o,$(SRCS:%.c=$(OBJDIR)/%.o)) 47 | 48 | DEPS := $(filter %.d,$(SRCS:%.c=$(DEPDIR)/%.d)) 49 | 50 | # 51 | # C flags 52 | # 53 | CFLAGS := -Wall 54 | CFLAGS += -Wextra 55 | CFLAGS += -std=c11 56 | CFLAGS += -D_DEFAULT_SOURCE 57 | 58 | ifeq ($(DEBUG),1) 59 | # Optimize for debugging. 60 | CFLAGS += -O0 61 | 62 | CFLAGS += -g 63 | 64 | # Ensure there's a frame pointer in debug builds. 65 | CFLAGS += -fno-omit-frame-pointer 66 | else ifeq ($(PROFILE),1) 67 | # Enable profile options in profile builds. 68 | CFLAGS += -pg 69 | CFLAGS += -no-pie 70 | 71 | # Enable debug symbols in profile builds. 72 | CFLAGS += -g 73 | 74 | # Use slightly less aggressive optimizations in profile builds. 75 | CFLAGS += -O2 76 | CFLAGS += -fno-inline-functions 77 | CFLAGS += -fno-inline-functions-called-once 78 | CFLAGS += -fno-optimize-sibling-calls 79 | else 80 | # Disable default C assertions. 81 | CFLAGS += -DNDEBUG 82 | 83 | # Highest optimization flag in release builds. 84 | CFLAGS += -O3 85 | 86 | # These are only needed for stack traces in debug builds. 87 | CFLAGS += -fno-unwind-tables 88 | CFLAGS += -fno-asynchronous-unwind-tables 89 | 90 | # Disable all the stack protection features in release builds. 91 | CFLAGS += -fno-stack-protector 92 | CFLAGS += -fno-stack-check 93 | 94 | ifeq ($(CC),gcc) 95 | CFLAGS += -fno-stack-clash-protection 96 | endif 97 | 98 | # Disable frame pointer in release builds when AddressSanitizer isn't present. 99 | ifeq ($(ASAN),1) 100 | CFLAGS += -fno-omit-frame-pointer 101 | else 102 | CFLAGS += -fomit-frame-pointer 103 | endif 104 | endif 105 | 106 | # Give each function and data it's own section so the linker can remove unused 107 | # references to each, producing smaller, tighter binaries. 108 | ifeq ($(UNUSED), 1) 109 | CFLAGS += -ffunction-sections 110 | CFLAGS += -fdata-sections 111 | endif 112 | 113 | # Enable link-time optimizations if requested. But not in debug builds. 114 | ifeq ($(LTO),1) 115 | ifeq ($(DEBUG),0) 116 | CFLAGS += -flto 117 | endif 118 | endif 119 | 120 | # Sanitizer selection. 121 | ifeq ($(ASAN),1) 122 | CFLAGS += -fsanitize=address 123 | endif 124 | ifeq ($(TSAN),1) 125 | CFLAGS += -fsanitize=thread -DRX_TSAN 126 | endif 127 | ifeq ($(UBSAN),1) 128 | CFLAGS += -fsanitize=undefined 129 | endif 130 | 131 | # 132 | # Dependency flags 133 | # 134 | DEPFLAGS := -MMD 135 | DEPFLAGS += -MP 136 | 137 | # 138 | # Linker flags. 139 | # 140 | LDFLAGS := -lm 141 | LDFLAGS += -lpthread 142 | 143 | # Strip unused symbols if requested. 144 | ifeq ($(UNUSED), 1) 145 | LDFLAGS += -Wl,--gc-sections 146 | endif 147 | 148 | # Enable profiling if requested. 149 | ifeq ($(PROFILE),1) 150 | LDFLAGS += -pg 151 | LDFLAGS += -no-pie 152 | endif 153 | 154 | # Enable link-time optimizations if requested. 155 | ifeq ($(LTO),1) 156 | LDFLAGS += -flto 157 | endif 158 | 159 | # Sanitizer selection. 160 | ifeq ($(ASAN),1) 161 | LDFLAGS += -fsanitize=address 162 | endif 163 | ifeq ($(TSAN),1) 164 | LDFLAGS += -fsanitize=thread 165 | endif 166 | ifeq ($(UBSAN),1) 167 | LDFLAGS += -fsanitize=undefined 168 | endif 169 | 170 | all: $(BIN) 171 | 172 | # Build artifact directories 173 | $(DEPDIR): 174 | @mkdir -p $(addprefix $(DEPDIR)/,$(call uniq,$(dir $(SRCS)))) 175 | $(OBJDIR): 176 | @mkdir -p $(addprefix $(OBJDIR)/,$(call uniq,$(dir $(SRCS)))) 177 | 178 | $(OBJDIR)/%.o: %.c $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR) 179 | $(CC) -MT $@ $(DEPFLAGS) -MF $(DEPDIR)/$*.Td $(CFLAGS) -c -o $@ $< 180 | @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d 181 | 182 | $(BIN): $(OBJS) 183 | $(LD) $(OBJS) $(LDFLAGS) -o $@ 184 | 185 | clean: 186 | rm -rf $(DEPDIR) $(OBJDIR) $(BIN) 187 | 188 | $(DEPS): 189 | include $(wildcard $(DEPS)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codin 2 | 3 | An Odin to C compiler written in C. 4 | 5 | ### Building 6 | 7 | To build on Linux 8 | ``` 9 | $ make 10 | ``` 11 | 12 | > On Linux you have some additional build options provided by the Makefile. Check the documentation at the top of the `Makefile` for them. 13 | 14 | To build on Windows 15 | ``` 16 | $ cl.exe unity.c 17 | ``` 18 | 19 | ### License 20 | Licensed under the MIT license. View copyright information [here](doc/LICENSE.md) -------------------------------------------------------------------------------- /doc/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2022, 2023 Dale Weiler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /doc/STYLE.md: -------------------------------------------------------------------------------- 1 | # Style 2 | 3 | This is the style guide of the c-odin compiler. 4 | 5 | * C11 6 | * Must compile as C++ as well 7 | * Types `PascalCase` 8 | * Functions `snake_case` 9 | * Constants `TITLECASE` 10 | * Soft 80-column limit 11 | * Indent with tabs 12 | * Align with spaces 13 | * Assume allocations can fail and handle accordingly 14 | * Put space between `if`, `switch`, and `for` and the expression 15 | * The bracing style looks like (1TBS) 16 | ```c 17 | if (x) { 18 | 19 | } else { 20 | 21 | } 22 | ``` 23 | * Put case statements on the same column of switch 24 | ```c 25 | switch (x) { 26 | case A: 27 | foo(); 28 | break; 29 | case B: 30 | bar(); 31 | break; 32 | } 33 | ``` 34 | * Every statement should have `{}` 35 | * Do not put `()` on `sizeof` operator unless needed 36 | * Do not put `()` on `return` 37 | * No dependencies -------------------------------------------------------------------------------- /src/allocator.c: -------------------------------------------------------------------------------- 1 | #include "allocator.h" 2 | 3 | extern const AllocatorOperations ALLOCATOR_STD; 4 | extern const AllocatorOperations ALLOCATOR_ARENA; 5 | extern const AllocatorOperations ALLOCATOR_NULL; 6 | 7 | Bool allocator_init(Allocator *allocator, const String name) { 8 | if (string_compare(name, SCLIT("std"))) { 9 | allocator->ops = &ALLOCATOR_STD; 10 | } else if (string_compare(name, SCLIT("arena"))) { 11 | allocator->ops = &ALLOCATOR_ARENA; 12 | } else { 13 | allocator->ops = &ALLOCATOR_NULL; 14 | } 15 | 16 | allocator->user = 0; 17 | 18 | return allocator->ops->init(allocator); 19 | } 20 | 21 | void allocator_fini(Allocator *allocator) { 22 | allocator->ops->fini(allocator); 23 | } 24 | 25 | Ptr allocator_allocate(Allocator *allocator, Size bytes) { 26 | return allocator->ops->allocate(allocator, bytes); 27 | } 28 | 29 | Ptr allocator_reallocate(Allocator *allocator, void *data, Size old_size, Size new_size) { 30 | return allocator->ops->reallocate(allocator, data, old_size, new_size); 31 | } 32 | 33 | void allocator_deallocate(Allocator *allocator, void *data) { 34 | allocator->ops->deallocate(allocator, data); 35 | } 36 | -------------------------------------------------------------------------------- /src/allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_ALLOCATOR_H 2 | #define CODIN_ALLOCATOR_H 3 | #include "string.h" 4 | 5 | typedef struct Allocator Allocator; 6 | typedef struct AllocatorOperations AllocatorOperations; 7 | 8 | struct AllocatorOperations { 9 | String name; 10 | Bool (*init)(Allocator *); 11 | void (*fini)(Allocator *); 12 | Ptr (*allocate)(Allocator *allocator, Size bytes); 13 | Ptr (*reallocate)(Allocator *allocator, void *data, Size old_size, Size new_size); 14 | void (*deallocate)(Allocator *allocator, void *data); 15 | }; 16 | 17 | struct Allocator { 18 | const AllocatorOperations *ops; 19 | void *user; 20 | }; 21 | 22 | Bool allocator_init(Allocator *allocator, const String name); 23 | void allocator_fini(Allocator *allocator); 24 | 25 | Ptr allocator_allocate(Allocator *allocator, Size bytes); 26 | Ptr allocator_reallocate(Allocator *allocator, void *data, Size old_size, Size new_size); 27 | void allocator_deallocate(Allocator *allocator, void *data); 28 | 29 | #endif // CODIN_ALLOCATOR_H -------------------------------------------------------------------------------- /src/allocator_arena.c: -------------------------------------------------------------------------------- 1 | #include // malloc, free 2 | #include // memcpy 3 | 4 | #include "allocator.h" 5 | #include "thread.h" 6 | 7 | #if defined(OS_WINDOWS) 8 | #define WIN32_LEAN_AND_MEAN 9 | #include 10 | #undef IN 11 | #endif 12 | 13 | typedef struct Arena Arena; 14 | typedef struct ArenaRegion ArenaRegion; 15 | 16 | static const Size ARENA_DEFAULT_CAPACITY = 1024 * 1024 * 1024; // 1 GiB 17 | static const Size ARENA_DEFAULT_ALIGNMENT = 16; 18 | 19 | struct Arena { 20 | Mutex mutex; 21 | ArenaRegion *beg THREAD_GUARDED(mutex); 22 | ArenaRegion *end THREAD_GUARDED(mutex); 23 | }; 24 | 25 | struct ArenaRegion { 26 | Mutex mutex; 27 | ArenaRegion *next THREAD_GUARDED(mutex); 28 | Size count THREAD_GUARDED(mutex); 29 | Size capacity THREAD_GUARDED(mutex); 30 | ALIGN(16) char data[] THREAD_GUARDED(mutex); // This should be 16 byte aligned 31 | }; 32 | 33 | static void *raw_alloc(Size size) { 34 | #if defined(OS_WINDOWS) 35 | // Because Windows is shit 36 | return HeapAlloc(GetProcessHeap(), 0, size); 37 | #else 38 | return malloc(size); 39 | #endif 40 | } 41 | 42 | static void raw_free(void *ptr) { 43 | #if defined(OS_WINDOWS) 44 | // Because Windows is shit 45 | HeapFree(GetProcessHeap(), 0, ptr); 46 | #else 47 | free(ptr); 48 | #endif 49 | } 50 | 51 | static void *raw_aligned_alloc(Size bytes, Size alignment) { 52 | const Size offset = alignment - 1 + sizeof(void*); 53 | void *p1 = raw_alloc(bytes + offset); 54 | if (!p1) { 55 | return 0; 56 | } 57 | void **p2 = RCAST(void **, (RCAST(Size, p1) + offset) & ~(alignment - 1)); 58 | p2[-1] = p1; 59 | return p2; 60 | } 61 | 62 | static void raw_aligned_free(void *p) { 63 | raw_free(RCAST(void **, p)[-1]); 64 | } 65 | 66 | static ArenaRegion *arena_allocator_new_region(Size capacity) 67 | THREAD_INTERNAL 68 | { 69 | const Size bytes = sizeof(ArenaRegion) + capacity; 70 | ArenaRegion *region = CAST(ArenaRegion *, raw_aligned_alloc(bytes, ARENA_DEFAULT_ALIGNMENT)); 71 | if (!region) { 72 | return 0; 73 | } 74 | mutex_init(®ion->mutex); 75 | region->next = 0; 76 | region->count = 0; 77 | region->capacity = capacity; 78 | return region; 79 | } 80 | 81 | static Bool arena_allocator_init(Allocator *allocator) 82 | THREAD_INTERNAL 83 | { 84 | Arena *arena = CAST(Arena *, raw_aligned_alloc(sizeof *arena, ARENA_DEFAULT_ALIGNMENT)); 85 | if (!arena) { 86 | return false; 87 | } 88 | 89 | mutex_init(&arena->mutex); 90 | 91 | arena->beg = arena_allocator_new_region(ARENA_DEFAULT_CAPACITY); 92 | arena->end = arena->beg; 93 | 94 | allocator->user = arena; 95 | 96 | return true; 97 | } 98 | 99 | static void arena_allocator_fini(Allocator *allocator) { 100 | Arena *arena = CAST(Arena *, allocator->user); 101 | mutex_lock(&arena->mutex); 102 | ArenaRegion *region = arena->beg; 103 | while (region) { 104 | ArenaRegion *const self = region; 105 | mutex_lock(&self->mutex); 106 | region = self->next; 107 | mutex_unlock(&self->mutex); 108 | mutex_fini(&self->mutex); 109 | raw_aligned_free(self); 110 | } 111 | mutex_unlock(&arena->mutex); 112 | mutex_fini(&arena->mutex); 113 | raw_aligned_free(arena); 114 | allocator->user = 0; 115 | } 116 | 117 | static Ptr arena_allocator_allocate(Allocator *allocator, Size size) { 118 | size = (size + ARENA_DEFAULT_ALIGNMENT - 1) & CAST(Size, -ARENA_DEFAULT_ALIGNMENT); 119 | 120 | Arena *const arena = CAST(Arena *, allocator->user); 121 | 122 | mutex_lock(&arena->mutex); 123 | ArenaRegion *region = arena->end; 124 | for (;;) { 125 | mutex_lock(®ion->mutex); 126 | if (!(region->count + size > region->capacity && region->next)) { 127 | arena->end = region; 128 | // Keep mutex locked throughout. 129 | break; 130 | } 131 | ArenaRegion *next = region->next; 132 | mutex_unlock(®ion->mutex); 133 | region = next; 134 | } 135 | 136 | if (region->count + size > region->capacity) { 137 | const Size capacity = (size + ARENA_DEFAULT_CAPACITY - 1) & -ARENA_DEFAULT_CAPACITY; 138 | ArenaRegion *next = arena_allocator_new_region(capacity); 139 | if (!next) { 140 | // Out of memory. 141 | mutex_unlock(®ion->mutex); 142 | mutex_unlock(&arena->mutex); 143 | return 0; 144 | } 145 | region->next = next; 146 | 147 | mutex_unlock(®ion->mutex); 148 | region = next; 149 | mutex_lock(®ion->mutex); 150 | } 151 | 152 | void *result = ®ion->data[region->count]; 153 | region->count += size; 154 | mutex_unlock(®ion->mutex); 155 | 156 | arena->end = region; 157 | mutex_unlock(&arena->mutex); 158 | 159 | return result; 160 | } 161 | 162 | static Ptr arena_allocator_reallocate(Allocator *allocator, void *old_data, Size old_size, Size new_size) { 163 | if (new_size <= old_size) { 164 | return old_data; 165 | } 166 | 167 | Ptr new_data = arena_allocator_allocate(allocator, new_size); 168 | return memcpy(new_data, old_data, old_size); 169 | } 170 | 171 | static void arena_allocator_deallocate(Allocator *allocator, void *data) { 172 | (void)allocator; 173 | (void)data; 174 | // Does nothing. 175 | } 176 | 177 | const AllocatorOperations ALLOCATOR_ARENA = { 178 | SLIT("arena"), 179 | arena_allocator_init, 180 | arena_allocator_fini, 181 | arena_allocator_allocate, 182 | arena_allocator_reallocate, 183 | arena_allocator_deallocate, 184 | }; -------------------------------------------------------------------------------- /src/allocator_null.c: -------------------------------------------------------------------------------- 1 | #include "allocator.h" 2 | 3 | static Bool allocator_null_init(Allocator *allocator) { 4 | (void)allocator; 5 | // Does nothing. 6 | return true; 7 | } 8 | 9 | static void allocator_null_fini(Allocator *allocator) { 10 | (void)allocator; 11 | // Does nothing. 12 | } 13 | 14 | static Ptr allocator_null_allocate(Allocator *allocator, Size bytes) { 15 | (void)allocator; 16 | (void)bytes; 17 | // Does nothing. 18 | return 0; 19 | } 20 | 21 | static Ptr allocator_null_reallocate(Allocator *allocator, void *data, Size old_size, Size new_size) { 22 | (void)allocator; 23 | (void)data; 24 | (void)old_size; 25 | (void)new_size; 26 | // Does nothing. 27 | return 0; 28 | } 29 | 30 | static void allocator_null_deallocate(Allocator *allocator, void *data) { 31 | (void)allocator; 32 | (void)data; 33 | // Does nothing. 34 | } 35 | 36 | const AllocatorOperations ALLOCATOR_NULL = { 37 | SLIT("null"), 38 | allocator_null_init, 39 | allocator_null_fini, 40 | allocator_null_allocate, 41 | allocator_null_reallocate, 42 | allocator_null_deallocate, 43 | }; -------------------------------------------------------------------------------- /src/allocator_std.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "allocator.h" 4 | #include "thread.h" 5 | 6 | static const Size DEFAULT_CAPACITY = 4096; 7 | static const Float32 RESIZE_THRESHOLD = 0.75; 8 | static const Uint32 PRIME1 = 73; 9 | static const Uint32 PRIME2 = 5009; 10 | 11 | #define TOMBSTONE RCAST(void *, 1) 12 | 13 | typedef struct StandardAllocator StandardAllocator; 14 | 15 | struct StandardAllocator { 16 | Size size THREAD_GUARDED(mutex); 17 | Size deleted THREAD_GUARDED(mutex); 18 | Size capacity THREAD_GUARDED(mutex); 19 | void **items THREAD_GUARDED(mutex); 20 | Mutex mutex; 21 | }; 22 | 23 | static Bool std_allocator_init(Allocator *ctx) 24 | THREAD_INTERNAL 25 | { 26 | StandardAllocator *allocator = CAST(StandardAllocator*, calloc(1, sizeof *allocator)); 27 | if (!allocator) { 28 | return false; 29 | } 30 | 31 | mutex_init(&allocator->mutex); 32 | allocator->size = 0; 33 | allocator->capacity = DEFAULT_CAPACITY; 34 | allocator->items = CAST(void**, calloc(allocator->capacity, sizeof *allocator->items)); 35 | if (!allocator->items) { 36 | free(allocator); 37 | return false; 38 | } 39 | 40 | ctx->user = allocator; 41 | 42 | return true; 43 | } 44 | 45 | static void std_allocator_fini(Allocator *ctx) { 46 | StandardAllocator *allocator = CAST(StandardAllocator *, ctx->user); 47 | 48 | mutex_lock(&allocator->mutex); 49 | const Size capacity = allocator->capacity; 50 | for (Size i = 0; i < capacity; i++) { 51 | void *data = allocator->items[i]; 52 | if (data == TOMBSTONE) { 53 | continue; 54 | } 55 | free(data); 56 | allocator->items[i] = TOMBSTONE; 57 | allocator->deleted++; 58 | } 59 | free(allocator->items); 60 | mutex_unlock(&allocator->mutex); 61 | 62 | mutex_fini(&allocator->mutex); 63 | free(allocator); 64 | } 65 | 66 | static Bool std_allocator_add_unlocked(StandardAllocator *allocator, void *item); 67 | static Bool std_allocator_maybe_rehash_unlocked(StandardAllocator *allocator) 68 | THREAD_REQUIRES(allocator->mutex) 69 | { 70 | if (allocator->size + allocator->deleted < CAST(Float32, allocator->capacity) * RESIZE_THRESHOLD) { 71 | return true; 72 | } 73 | 74 | Size capacity = allocator->capacity * 2; 75 | void **items = CAST(void**, calloc(capacity, sizeof *items)); 76 | if (!items) { 77 | return false; 78 | } 79 | 80 | void **old_items = allocator->items; 81 | Size old_capacity = allocator->capacity; 82 | 83 | allocator->capacity = capacity; 84 | allocator->items = items; 85 | allocator->deleted = 0; 86 | allocator->size = 0; 87 | 88 | for (Size i = 0; i < old_capacity; i++) { 89 | // NOTE(dweiler): This cannot fail since the capacity is strictly greater. 90 | std_allocator_add_unlocked(allocator, old_items[i]); 91 | } 92 | 93 | free(old_items); 94 | 95 | return true; 96 | } 97 | 98 | static Bool std_allocator_add_unlocked(StandardAllocator *allocator, void *item) 99 | THREAD_REQUIRES(allocator->mutex) 100 | { 101 | Uint64 hash = RCAST(Uint64, item); // TODO(dweiler): MixInt 102 | const Size mask = allocator->capacity - 1; 103 | 104 | Size index = (PRIME1 * hash) & mask; 105 | 106 | for (;;) { 107 | void *element = allocator->items[index]; 108 | if (element && element != TOMBSTONE) { 109 | if (element == item) { 110 | return false; 111 | } else { 112 | index = (index + PRIME2) & mask; 113 | } 114 | } else { 115 | break; 116 | } 117 | } 118 | 119 | allocator->size++; 120 | allocator->items[index] = item; 121 | 122 | return std_allocator_maybe_rehash_unlocked(allocator); 123 | } 124 | 125 | static Bool std_allocator_add(StandardAllocator *allocator, void *item) { 126 | if (!item || item == TOMBSTONE) { 127 | return false; 128 | } 129 | 130 | mutex_lock(&allocator->mutex); 131 | const Bool result = std_allocator_add_unlocked(allocator, item); 132 | mutex_unlock(&allocator->mutex); 133 | return result; 134 | } 135 | 136 | static Bool std_allocator_remove(StandardAllocator *allocator, void *item) { 137 | Uint64 hash = RCAST(Uint64, item); // TODO(dweiler): MixInt 138 | 139 | mutex_lock(&allocator->mutex); 140 | 141 | const Size mask = allocator->capacity - 1; 142 | Size index = mask & (PRIME1 * hash); 143 | 144 | for (;;) { 145 | void *element = allocator->items[index]; 146 | if (element) { 147 | if (element == item) { 148 | allocator->items[index] = TOMBSTONE; 149 | allocator->size--; 150 | allocator->deleted++; 151 | mutex_unlock(&allocator->mutex); 152 | return true; 153 | } else { 154 | index = mask & (index + PRIME2); 155 | } 156 | } else { 157 | break; 158 | } 159 | } 160 | 161 | mutex_unlock(&allocator->mutex); 162 | return false; 163 | } 164 | 165 | static Ptr std_allocator_allocate(Allocator *allocator, Size bytes) { 166 | void *data = malloc(bytes); 167 | if (!data) { 168 | return 0; 169 | } 170 | 171 | if (!std_allocator_add(CAST(StandardAllocator*, allocator->user), data)) { 172 | free(data); 173 | return 0; 174 | } 175 | 176 | return data; 177 | } 178 | 179 | static Ptr std_allocator_reallocate(Allocator *allocator, void *data, Size old_size, Size new_size) { 180 | if (!data) { 181 | return 0; 182 | } 183 | 184 | if (new_size <= old_size) { 185 | return data; 186 | } 187 | 188 | StandardAllocator *std_allocator = CAST(StandardAllocator*, allocator->user); 189 | std_allocator_remove(std_allocator, data); 190 | 191 | void *resize = realloc(data, new_size); 192 | if (!resize) { 193 | return 0; 194 | } 195 | 196 | if (!std_allocator_add(std_allocator, resize)) { 197 | free(resize); 198 | return 0; 199 | } 200 | 201 | return resize; 202 | } 203 | 204 | static void std_allocator_deallocate(Allocator *allocator, void *data) { 205 | if (!data) { 206 | return; 207 | } 208 | std_allocator_remove(CAST(StandardAllocator*, allocator->user), data); 209 | free(data); 210 | } 211 | 212 | const AllocatorOperations ALLOCATOR_STD = { 213 | SLIT("std"), 214 | std_allocator_init, 215 | std_allocator_fini, 216 | std_allocator_allocate, 217 | std_allocator_reallocate, 218 | std_allocator_deallocate, 219 | }; -------------------------------------------------------------------------------- /src/array.c: -------------------------------------------------------------------------------- 1 | #include "array.h" 2 | #include "context.h" 3 | #include "allocator.h" 4 | 5 | static Ptr array_create_(Context *context) { 6 | Allocator *allocator = &context->allocator; 7 | Array *array = allocator_allocate(allocator, sizeof *array); 8 | if (!array) { 9 | THROW(ERROR_OOM); 10 | } 11 | array->context = context; 12 | array->size = 0; 13 | array->capacity = 0; 14 | return array + 1; 15 | } 16 | 17 | void array_grow(void **const array, Size elements, Size type_size) { 18 | ASSERT(*array); 19 | if (!array_valid(*array)) { 20 | *array = array_create_(tagptr_ptr(*array)); 21 | } 22 | Array *meta = array_meta(*array); 23 | Context *const context = meta->context; 24 | Allocator *const allocator = &context->allocator; 25 | const Size old_capacity = meta->capacity; 26 | const Size new_capacity = ((old_capacity + elements) * 3) / 2; 27 | const Size old_size = old_capacity * type_size + sizeof *meta; 28 | const Size new_size = new_capacity * type_size + sizeof *meta; 29 | void *data = allocator_reallocate(allocator, meta, old_size, new_size); 30 | if (!data) { 31 | THROW(ERROR_OOM); 32 | } 33 | meta = CAST(Array *, data); 34 | meta->capacity = new_capacity; 35 | *array = meta + 1; 36 | } 37 | 38 | void array_delete(void *const array) { 39 | if (!array) return; 40 | Array *const meta = array_meta(array); 41 | Context *const context = meta->context; 42 | Allocator *const allocator = &context->allocator; 43 | allocator_deallocate(allocator, meta); 44 | } -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_ARRAY_H 2 | #define CODIN_ARRAY_H 3 | #include // memmove 4 | 5 | #include "support.h" // ALIGN, Size 6 | 7 | typedef struct Context Context; 8 | typedef struct Array Array; 9 | 10 | struct ALIGN(16) Array { 11 | Context *context; 12 | Size capacity; 13 | Size size; 14 | }; 15 | 16 | #define Array(T) T* 17 | 18 | #define array_make(context) \ 19 | tagptr(context, 0x1) 20 | 21 | #define array_valid(array) \ 22 | (tagptr_tag(array) != 0x1) 23 | 24 | #define array_meta(array) \ 25 | (&RCAST(Array*, (array))[-1]) 26 | 27 | #define array_try_grow(array, size_) \ 28 | ((array_valid(array) && array_meta(array)->size + (size_) < array_meta(array)->capacity) \ 29 | ? true \ 30 | : (array_grow(RCAST(void **, &(array)), (size_), sizeof *(array)), true)) 31 | 32 | #define array_size(array) \ 33 | (array_valid(array) ? array_meta(array)->size : 0) 34 | 35 | #define array_capacity(array) \ 36 | (array_valid(array) ? array_meta(array)->capacity : 0) 37 | 38 | #define array_expand(array, size_) \ 39 | (array_try_grow((array), (size_)) \ 40 | ? (array_meta(array)->size += (size_), true) \ 41 | : false) 42 | 43 | #define array_push(array, value) \ 44 | (array_try_grow((array), 1) \ 45 | ? ((array)[array_meta(array)->size++] = (value), true) \ 46 | : false) 47 | 48 | #define array_pop_front(array) \ 49 | (array_size(array) \ 50 | ? (memmove((array), &(array)[1], sizeof *(array) * (array_meta(array)->size - 1)), \ 51 | array_meta(array)->size--, \ 52 | true) \ 53 | : false) 54 | 55 | #define array_free(array) \ 56 | (void)(array_valid(array) ? (array_delete(array), (array) = 0) : 0) 57 | 58 | #define array_resize(array, size_) \ 59 | (array_valid(array) \ 60 | ? (array_meta(array)->size >= (size_) \ 61 | ? (array_meta(array)->size = (size_), true) \ 62 | : array_expand((array), (size_) - array_meta(array)->size)) \ 63 | : (array_grow(RCAST(void **, &(array)), (size_), sizeof *(array)), \ 64 | array_meta(array)->size = (size_), \ 65 | true)) 66 | 67 | #define array_last(array) \ 68 | ((array)[array_size(array) - 1]) 69 | 70 | #define array_clear(array) \ 71 | (void)(array_valid(array) ? array_meta(array)->size = 0 : 0) 72 | 73 | void array_grow(void **const array, Size elements, Size type_size); 74 | void array_delete(void *const array); 75 | 76 | #endif // CODIN_ARRAY_H -------------------------------------------------------------------------------- /src/build.c: -------------------------------------------------------------------------------- 1 | #include "build.h" 2 | #include "tree.h" 3 | #include "path.h" 4 | #include "parser.h" 5 | #include "report.h" 6 | 7 | #define BUILD_ERROR(location, fmt, ...) \ 8 | do { \ 9 | const Tree *const tree = (work)->tree; \ 10 | report_error(&tree->source, (location), context, (fmt), ## __VA_ARGS__); \ 11 | (work)->error = true; \ 12 | } while (0) 13 | 14 | static BuildWork *build_add_work(BuildContext *build, Package *package, String filename, Context *context); 15 | static void build_worker(void *data, Context *context); 16 | 17 | void build_init(BuildContext *ctx, String allocator, String scheduler, String profiler) 18 | THREAD_INTERNAL 19 | { 20 | // Needs to be initialized first. 21 | Context *const context = &ctx->context; 22 | context_init(context, allocator, profiler); 23 | 24 | PROF_ENTER(); 25 | 26 | sched_init(&ctx->sched, scheduler, context); 27 | project_init(&ctx->project, SCLIT("test"), context); 28 | mutex_init(&ctx->mutex); 29 | ctx->collections = array_make(context); 30 | ctx->work = array_make(context); 31 | 32 | // Set host platform and architecture. 33 | #if defined(OS_WINDOWS) 34 | ctx->settings.host_platform = PLATFORM_WINDOWS; 35 | #elif defined(OS_LINUX) 36 | ctx->settings.host_platform = PLATFORM_LINUX; 37 | #endif 38 | 39 | #if defined(ISA_AMD64) 40 | ctx->settings.host_arch = ARCH_AMD64; 41 | #elif defined(ISA_AARCH64) 42 | ctx->settings.host_arch = ARCH_AARCH64; 43 | #endif 44 | 45 | // TODO(dweiler): Support cross compilation. 46 | ctx->settings.target_platform = ctx->settings.host_platform; 47 | ctx->settings.target_arch = ctx->settings.host_arch; 48 | 49 | PROF_LEAVE(); 50 | } 51 | 52 | void build_fini(BuildContext *build) { 53 | Context *const context = &build->context; 54 | 55 | PROF_ENTER(); 56 | 57 | Allocator *const allocator = &context->allocator; 58 | mutex_lock(&build->mutex); 59 | const Size n_work = array_size(build->work); 60 | for (Size i = 0; i < n_work; i++) { 61 | BuildWork *const work = build->work[i]; 62 | tree_fini(work->tree); 63 | allocator_deallocate(allocator, work); 64 | } 65 | array_free(build->work); 66 | 67 | array_free(build->collections); 68 | mutex_unlock(&build->mutex); 69 | mutex_fini(&build->mutex); 70 | project_fini(&build->project); 71 | sched_fini(&build->sched); 72 | 73 | PROF_LEAVE(); 74 | 75 | context_fini(context); 76 | } 77 | 78 | void build_wait(BuildContext *build) { 79 | Context *const context = &build->context; 80 | PROF_ENTER(); 81 | sched_wait(&build->sched); 82 | PROF_LEAVE(); 83 | } 84 | 85 | Bool build_add_collection(BuildContext *build, String name, String path) { 86 | Context *const context = &build->context; 87 | 88 | PROF_ENTER(); 89 | 90 | if (!path_directory_exists(path, context)) { 91 | PROF_LEAVE(); 92 | return false; 93 | } 94 | 95 | const Size n_collections = array_size(build->collections); 96 | for (Size i = 0; i < n_collections; i++) { 97 | const Collection *const collection = &build->collections[i]; 98 | if (string_compare(collection->name, name)) { 99 | PROF_LEAVE(); 100 | return false; 101 | } 102 | } 103 | 104 | array_push(build->collections, LIT(Collection, name, path)); 105 | 106 | PROF_LEAVE(); 107 | 108 | return true; 109 | } 110 | 111 | static void build_add_package_internal(BuildContext *build, String pathname, Context *context); 112 | 113 | void build_add_package(BuildContext *build, String pathname) { 114 | build_add_package_internal(build, pathname, &build->context); 115 | } 116 | 117 | static Bool accepted_file(BuildContext *build, String filename) { 118 | const Platform target_platform = build->settings.target_platform; 119 | 120 | static const struct { 121 | String suffix; 122 | Platform platform; 123 | } PLATFORMS[] = { 124 | { SLIT("_linux.odin"), PLATFORM_LINUX }, 125 | { SLIT("_freebsd.odin"), PLATFORM_FREEBSD }, 126 | { SLIT("_darwin.odin"), PLATFORM_DARWIN }, 127 | { SLIT("_windows.odin"), PLATFORM_WINDOWS }, 128 | }; 129 | 130 | for (Size i = 0; i < sizeof PLATFORMS / sizeof *PLATFORMS; i++) { 131 | const String suffix = PLATFORMS[i].suffix; 132 | if (string_ends_with(filename, suffix)) { 133 | return target_platform == PLATFORMS[i].platform; 134 | } 135 | } 136 | 137 | if (string_ends_with(filename, SCLIT("_unix.odin"))) { 138 | return target_platform != PLATFORM_WINDOWS; 139 | } 140 | 141 | return true; 142 | } 143 | 144 | Package *project_add_package_internal(Project *project, String pathname, Context *context); 145 | 146 | static void build_add_package_internal(BuildContext *build, String pathname, Context *context) { 147 | PROF_ENTER(); 148 | 149 | Project *const project = &build->project; 150 | Package *package = project_add_package_internal(project, pathname, context); 151 | if (!package){ 152 | PROF_LEAVE(); 153 | return; // Package already exists 154 | } 155 | 156 | Array(String) filenames = path_list(pathname, context); 157 | const Size n_filenames = array_size(filenames); 158 | for (Size i = 0; i < n_filenames; i++) { 159 | const String filename = filenames[i]; 160 | if (!string_ends_with(filename, SCLIT(".odin"))) { 161 | continue; 162 | } 163 | 164 | if (!accepted_file(build, filename)) { 165 | continue; 166 | } 167 | 168 | const String path = path_cat(pathname, filename, context); 169 | sched_queue(&build->sched, build_add_work(build, package, path, context), build_worker, 0); 170 | } 171 | 172 | PROF_LEAVE(); 173 | } 174 | 175 | static String build_find_collection(BuildContext *build, String name, Context *context) { 176 | PROF_ENTER(); 177 | 178 | const Size n_collections = array_size(build->collections); 179 | for (Size i = 0; i < n_collections; i++) { 180 | const Collection *const collection = &build->collections[i]; 181 | if (string_compare(collection->name, name)) { 182 | PROF_LEAVE(); 183 | return collection->path; 184 | } 185 | } 186 | 187 | PROF_LEAVE(); 188 | 189 | return STRING_NIL; 190 | } 191 | 192 | static BuildWork *build_add_work(BuildContext *build, Package *package, String filename, Context *context) { 193 | PROF_ENTER(); 194 | Allocator *const allocator = &context->allocator; 195 | BuildWork *work = allocator_allocate(allocator, sizeof *work); 196 | work->build = build; 197 | work->package = package; 198 | work->tree = package_add_tree(package, filename); 199 | work->error = false; 200 | mutex_lock(&build->mutex); 201 | array_push(build->work, work); 202 | mutex_unlock(&build->mutex); 203 | PROF_LEAVE(); 204 | return work; 205 | } 206 | 207 | static void build_worker(void *data, Context *context) { 208 | BuildWork *const work = CAST(BuildWork *, data); 209 | 210 | PROF_ENTER(); 211 | 212 | Tree *const tree = work->tree; 213 | if (!parse(tree, context)) { 214 | BUILD_ERROR(0, "Could not open file"); 215 | } 216 | 217 | // The first statement should be a package declaration. 218 | const Statement *const first = tree->statements[0]; 219 | if (first->kind != STATEMENT_PACKAGE) { 220 | BUILD_ERROR(0, "Expected a package declaration at the beginning of the file"); 221 | } 222 | 223 | const PackageStatement *const package = RCAST(const PackageStatement *, first); 224 | if (string_compare(package->name, SCLIT("_"))) { 225 | BUILD_ERROR(&package->location, "Cannot name package '_'"); 226 | } else if (string_compare(package->name, SCLIT("intrinsics")) 227 | || string_compare(package->name, SCLIT("builtin"))) 228 | { 229 | BUILD_ERROR(&package->location, "Use of reserved package name: '%.*s'", SFMT(package->name)); 230 | } 231 | 232 | // Queue all the imports into the build. 233 | const Size n_imports = array_size(tree->imports); 234 | for (Size i = 0; i < n_imports; i++) { 235 | const ImportStatement *import = tree->imports[i]; 236 | 237 | // Ignore "builtin" and "intrinsics" imports. 238 | if (string_compare(import->collection, SCLIT("core"))) { 239 | if (string_compare(import->pathname, SCLIT("./intrinsics")) 240 | || string_compare(import->pathname, SCLIT("./builtin"))) 241 | { 242 | continue; 243 | } 244 | } 245 | String pathname = STRING_NIL; 246 | if (import->collection.length == 0) { 247 | // The import will be relative to the current package. 248 | pathname = work->package->pathname; 249 | } else { 250 | // Otherwise it will be relative to the collection. 251 | pathname = build_find_collection(work->build, import->collection, context); 252 | if (pathname.length == 0) { 253 | BUILD_ERROR(&import->location, "Cannot find package collection: '%.*s'", SFMT(import->collection)); 254 | } 255 | } 256 | 257 | const String splice = path_cat(pathname, import->pathname, context); 258 | const String result = path_canonicalize(splice, context); 259 | 260 | build_add_package_internal(work->build, result, context); 261 | } 262 | 263 | PROF_LEAVE(); 264 | } -------------------------------------------------------------------------------- /src/build.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_BUILD_H 2 | #define CODIN_BUILD_H 3 | #include "context.h" 4 | #include "sched.h" 5 | #include "project.h" 6 | 7 | typedef struct Tree Tree; 8 | 9 | typedef struct BuildWork BuildWork; 10 | typedef struct BuildContext BuildContext; 11 | typedef struct BuildSettings BuildSettings; 12 | 13 | enum Arch { 14 | ARCH_AMD64, 15 | ARCH_AARCH64, 16 | }; 17 | 18 | enum Platform { 19 | PLATFORM_LINUX, 20 | PLATFORM_FREEBSD, 21 | PLATFORM_DARWIN, 22 | PLATFORM_WINDOWS, 23 | }; 24 | 25 | typedef enum Arch Arch; 26 | typedef enum Platform Platform; 27 | 28 | struct BuildWork { 29 | BuildContext *build; 30 | const Package *package; 31 | Tree *tree; 32 | Bool error; 33 | }; 34 | 35 | struct BuildSettings { 36 | Arch host_arch; 37 | Arch target_arch; 38 | Platform host_platform; 39 | Platform target_platform; 40 | }; 41 | 42 | struct BuildContext { 43 | Context context; 44 | Sched sched; 45 | Project project; 46 | Array(Collection) collections; 47 | BuildSettings settings; 48 | 49 | Mutex mutex; 50 | Array(BuildWork*) work THREAD_GUARDED(mutex); 51 | }; 52 | 53 | void build_init(BuildContext *ctx, String allocator, String scheduler, String profiler); 54 | void build_fini(BuildContext *build); 55 | void build_wait(BuildContext *build); 56 | Bool build_add_collection(BuildContext *build, String name, String path); 57 | void build_add_package(BuildContext *build, String pathname); 58 | 59 | #endif // CODIN_BUILD_H -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | #include "context.h" 2 | #include "allocator.h" 3 | 4 | void context_init(Context *context, String allocator, String profiler) { 5 | allocator_init(&context->allocator, allocator); 6 | profiler_init(&context->profiler, profiler, context); 7 | } 8 | 9 | void context_fini(Context *context) { 10 | profiler_fini(&context->profiler); 11 | allocator_fini(&context->allocator); 12 | } -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_CONTEXT_H 2 | #define CODIN_CONTEXT_H 3 | #include // jmp_buf, setjmp, longjmp 4 | 5 | #include "allocator.h" 6 | #include "profiler.h" 7 | 8 | typedef struct Context Context; 9 | 10 | enum Error { 11 | ERROR_LEX, 12 | ERROR_PARSE, 13 | ERROR_BUILD, 14 | ERROR_OOM, 15 | ERROR_UNKNOWN, 16 | }; 17 | 18 | typedef enum Error Error; 19 | 20 | #define THROW(error) \ 21 | longjmp((context)->jmp, CAST(int, (error))) 22 | 23 | struct ALIGN(16) Context { 24 | Allocator allocator; 25 | Profiler profiler; 26 | jmp_buf jmp; 27 | }; 28 | 29 | void context_init(Context *context, String allocator, String profiler); 30 | void context_fini(Context *context); 31 | 32 | #endif // CODIN_CONTEXT_H -------------------------------------------------------------------------------- /src/dump.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_DUMP_H 2 | #define CODIN_DUMP_H 3 | #include "tree.h" 4 | 5 | Bool dump_tuple_expression(const Tree *tree, const TupleExpression *expression, Sint32 depth); 6 | Bool dump_unary_expression(const Tree *tree, const UnaryExpression *expression, Sint32 depth); 7 | Bool dump_binary_expression(const Tree *tree, const BinaryExpression *expression, Sint32 depth); 8 | Bool dump_ternary_expression(const Tree *tree, const TernaryExpression *expression, Sint32 depth); 9 | Bool dump_cast_expression(const Tree *tree, const CastExpression *expression, Sint32 depth); 10 | Bool dump_selector_expression(const Tree *tree, const SelectorExpression *expression, Sint32 depth); 11 | Bool dump_call_expression(const Tree *tree, const CallExpression *expression, Sint32 depth); 12 | Bool dump_type(const Tree *tree, const Type *type, Sint32 depth); 13 | Bool dump_assertion_expression(const Tree *tree, const AssertionExpression *expression, Sint32 depth); 14 | Bool dump_literal_expression(const Tree *tree, const LiteralExpression *expression, Sint32 depth); 15 | Bool dump_compound_literal_expression(const Tree *tree, const CompoundLiteralExpression *expression, Sint32 depth); 16 | Bool dump_identifier_expression(const Tree *tree, const IdentifierExpression *expression, Sint32 depth); 17 | 18 | Bool dump_fields(const Tree *tree, Array(Field*) const fields, Sint32 depth); 19 | Bool dump_procedure_type(const Tree *tree, const ProcedureType *type, Sint32 depth); 20 | Bool dump_pointer_type(const Tree *tree, const PointerType *type, Sint32 depth); 21 | Bool dump_multi_pointer_type(const Tree *tree, const MultiPointerType *type, Sint32 depth); 22 | Bool dump_slice_type(const Tree *tree, const SliceType *type, Sint32 depth); 23 | Bool dump_array_type(const Tree *tree, const ArrayType *type, Sint32 depth); 24 | Bool dump_dynamic_array_type(const Tree *tree, const ArrayType *type, Sint32 depth); 25 | Bool dump_bit_set_type(const Tree *tree, const BitSetType *type, Sint32 depth); 26 | Bool dump_typeid_type(const Tree *tree, const TypeidType *type, Sint32 depth); 27 | Bool dump_map_type(const Tree *tree, const MapType *type, Sint32 depth); 28 | Bool dump_matrix_type(const Tree *tree, const MatrixType *type, Sint32 depth); 29 | Bool dump_procedure_expression(const Tree *tree, const ProcedureExpression *expression, Sint32 depth); 30 | Bool dump_type(const Tree *tree, const Type *type, Sint32 depth); 31 | Bool dump_type_expression(const Tree *tree, const TypeExpression *expression, Sint32 depth); 32 | Bool dump_expression(const Tree *tree, const Expression *expression, Sint32 depth); 33 | Bool dump_block_statement(const Tree *tree, const BlockStatement *statement, Sint32 depth); 34 | Bool dump_import_statement(const Tree *tree, const ImportStatement *statement, Sint32 depth); 35 | Bool dump_expression_statement(const Tree *tree, const ExpressionStatement *statement, Sint32 depth); 36 | Bool dump_assignment_statement(const Tree *tree, const AssignmentStatement *statement, Sint32 depth); 37 | Bool dump_declaration_statement(const Tree *tree, const DeclarationStatement *statement, Sint32 depth); 38 | Bool dump_if_statement(const Tree *tree, const IfStatement *statement, Sint32 depth); 39 | Bool dump_return_statement(const Tree *tree, const ReturnStatement *statement, Sint32 depth); 40 | Bool dump_for_statement(const Tree *tree, const ForStatement *statement, Sint32 depth); 41 | Bool dump_defer_statement(const Tree *tree, const DeferStatement *statement, Sint32 depth); 42 | Bool dump_branch_statement(const Tree *tree, const BranchStatement *statement, Sint32 depth); 43 | Bool dump_statement(const Tree *tree, const Statement *statement, Sint32 depth); 44 | 45 | Bool dump_identifier(const Tree *tree, const Identifier *identifier, Sint32 depth); 46 | 47 | void dump(Tree *tree); 48 | 49 | #endif // CODIN_DUMP_H -------------------------------------------------------------------------------- /src/lexemes.h: -------------------------------------------------------------------------------- 1 | #ifndef KIND 2 | #define KIND(...) 3 | #endif 4 | 5 | #ifndef ASSIGNMENT 6 | #define ASSIGNMENT(...) 7 | #endif 8 | 9 | #ifndef LITERAL 10 | #define LITERAL(...) 11 | #endif 12 | 13 | #ifndef OPERATOR 14 | #define OPERATOR(...) 15 | #endif 16 | 17 | #ifndef KEYWORD 18 | #define KEYWORD(...) 19 | #endif 20 | 21 | // Directives begin with '#' 22 | #ifndef DIRECTIVE 23 | #define DIRECTIVE(...) 24 | #endif 25 | 26 | // Attributes begin with '@' 27 | #ifndef ATTRIBUTE 28 | #define ATTRIBUTE(...) 29 | #endif 30 | 31 | #ifndef CCONVENTION 32 | #define CCONVENTION(...) 33 | #endif 34 | 35 | // Token kinds 36 | // ENUM, NAME, ASI 37 | KIND(INVALID, "invalid", false) 38 | KIND(EOF, "end of file", false) 39 | KIND(COMMENT, "comment", false) 40 | KIND(IDENTIFIER, "identifier", true) 41 | KIND(LITERAL, "literal", true) 42 | KIND(OPERATOR, "operator", false) // ASI handled by OPERATOR ASI column 43 | KIND(KEYWORD, "keyword", false) // ASI handled by KEYWORD ASI column 44 | KIND(ASSIGNMENT, "assignment", false) 45 | KIND(DIRECTIVE, "directive", false) // '#' is a directive (not an operator) 46 | KIND(ATTRIBUTE, "attribute", false) // '@' is a attribute (not an operator) 47 | KIND(CONST, "const", false) // '$' is a constant (not an operator) 48 | KIND(SEMICOLON, "semicolon", false) // ';' is a terminator 49 | KIND(LBRACE, "left brace", false) // '{' is not an operator 50 | KIND(RBRACE, "right brace", true) // '}' is not an operator 51 | KIND(UNDEFINED, "undefined", true) // '---' is not an operator 52 | 53 | // Assignment tokens. 54 | // 55 | // These are different from operators because assignments are statements. 56 | // 57 | // ENUM, NAME 58 | ASSIGNMENT(EQ, "eq") // '=' 59 | ASSIGNMENT(ADD, "add") // '+=' 60 | ASSIGNMENT(SUB, "sub") // '-=' 61 | ASSIGNMENT(MUL, "mul") // '*=' 62 | ASSIGNMENT(QUO, "quot") // '/=' 63 | ASSIGNMENT(MOD, "mod") // '%=' 64 | ASSIGNMENT(REM, "rem") // '%%=' 65 | ASSIGNMENT(AND, "and") // '&=' 66 | ASSIGNMENT(OR, "or") // '|=' 67 | ASSIGNMENT(XOR, "xor") // '~=' 68 | ASSIGNMENT(ANDNOT, "andnot") // '&~=' 69 | ASSIGNMENT(SHL, "shl") // '<<=' 70 | ASSIGNMENT(SHR, "shr") // '>>=' 71 | ASSIGNMENT(CMPAND, "and") // '&&=' 72 | ASSIGNMENT(CMPOR, "or") // '||=' 73 | 74 | // Literal kinds 75 | // ENUM, NAME 76 | LITERAL(INTEGER, "integer") 77 | LITERAL(FLOAT, "float") 78 | LITERAL(IMAGINARY, "imaginary") 79 | LITERAL(RUNE, "rune") 80 | LITERAL(STRING, "string") 81 | 82 | // Operators 83 | // ENUM, MATCH, PRECEDENCE, NAMED, ASI 84 | OPERATOR(NOT, "!", 0, false, false) 85 | OPERATOR(POINTER, "^", 0, false, true) 86 | OPERATOR(ARROW, "->", 0, false, false) 87 | OPERATOR(LPAREN, "(", 0, false, false) 88 | OPERATOR(RPAREN, ")", 0, false, true) 89 | OPERATOR(LBRACKET, "[", 0, false, false) 90 | OPERATOR(RBRACKET, "]", 0, false, true) 91 | OPERATOR(COLON, ":", 0, false, false) 92 | OPERATOR(PERIOD, ".", 0, false, false) 93 | OPERATOR(COMMA, ",", 0, false, false) 94 | OPERATOR(IN, "in", 6, true, false) // Produces a value, therefore an operator. 95 | OPERATOR(NOT_IN, "not_in", 6, true, false) // Produces a value, therefore an operator. 96 | OPERATOR(AUTO_CAST, "auto_cast", 0, true, false) // Produces a value, therefore an operator. 97 | OPERATOR(CAST, "cast", 0, true, false) // Produces a value, therefore an operator. 98 | OPERATOR(TRANSMUTE, "transmute", 0, true, false) // Produces a value, therefore an operator. 99 | OPERATOR(OR_ELSE, "or_else", 1, true, false) // Produces a value, therefore an operator. 100 | OPERATOR(OR_RETURN, "or_return", 1, true, true) // Produces a value, therefore an operator. 101 | OPERATOR(QUESTION, "?", 1, false, true) 102 | OPERATOR(ELLIPSIS, "..", 2, false, false) 103 | OPERATOR(RANGEFULL, "..=", 2, false, false) 104 | OPERATOR(RANGEHALF, "..<", 2, false, false) 105 | OPERATOR(CMPOR, "||", 3, false, false) 106 | OPERATOR(CMPAND, "&&", 4, false, false) 107 | OPERATOR(CMPEQ, "==", 5, false, false) 108 | OPERATOR(NOTEQ, "!=", 5, false, false) 109 | OPERATOR(LT, "<", 5, false, false) 110 | OPERATOR(GT, ">", 5, false, false) 111 | OPERATOR(LTEQ, "<=", 5, false, false) 112 | OPERATOR(GTEQ, ">=", 5, false, false) 113 | OPERATOR(ADD, "+", 6, false, false) 114 | OPERATOR(SUB, "-", 6, false, false) 115 | OPERATOR(OR, "|", 6, false, false) 116 | OPERATOR(XOR, "~", 6, false, false) 117 | OPERATOR(QUO, "/", 7, false, false) 118 | OPERATOR(MUL, "*", 7, false, false) 119 | OPERATOR(MOD, "%", 7, false, false) 120 | OPERATOR(MODMOD, "%%", 7, false, false) 121 | OPERATOR(AND, "&", 7, false, false) 122 | OPERATOR(ANDNOT, "&~", 7, false, false) 123 | OPERATOR(SHL, "<<", 7, false, false) 124 | OPERATOR(SHR, ">>", 7, false, false) 125 | 126 | // Keywords 127 | // ENUM, MATCH, ASI 128 | KEYWORD(IMPORT, "import", false) 129 | KEYWORD(FOREIGN, "foreign", false) 130 | KEYWORD(PACKAGE, "package", false) 131 | KEYWORD(TYPEID, "typeid", true) 132 | KEYWORD(WHERE, "where", false) 133 | KEYWORD(WHEN, "when", false) // Can be an operator in (x when y else z) 134 | KEYWORD(IF, "if", false) // Can be an operator in (x if y else z) 135 | KEYWORD(ELSE, "else", false) 136 | KEYWORD(FOR, "for", false) 137 | KEYWORD(SWITCH, "switch", false) 138 | KEYWORD(DO, "do", false) 139 | KEYWORD(CASE, "case", false) 140 | KEYWORD(BREAK, "break", true) 141 | KEYWORD(CONTINUE, "continue", true) 142 | KEYWORD(FALLTHROUGH, "fallthrough", true) 143 | KEYWORD(DEFER, "defer", false) 144 | KEYWORD(RETURN, "return", true) 145 | KEYWORD(PROC, "proc", false) 146 | KEYWORD(STRUCT, "struct", false) 147 | KEYWORD(UNION, "union", false) 148 | KEYWORD(ENUM, "enum", false) 149 | KEYWORD(BIT_SET, "bit_set", false) 150 | KEYWORD(MAP, "map", false) 151 | KEYWORD(DYNAMIC, "dynamic", false) 152 | KEYWORD(DISTINCT, "distinct", false) 153 | KEYWORD(USING, "using", false) 154 | KEYWORD(CONTEXT, "context", false) 155 | KEYWORD(ASM, "asm", false) 156 | KEYWORD(MATRIX, "matrix", false) 157 | 158 | // Direcitves 159 | // ENUM, MATCH 160 | DIRECTIVE(OPTIONAL_OK, "optional_ok") 161 | DIRECTIVE(OPTIONAL_ALLOCATOR_ERROR, "optional_allocator_error") 162 | DIRECTIVE(BOUNDS_CHECK, "bounds_check") 163 | DIRECTIVE(NO_BOUNDS_CHECK, "no_bounds_check") 164 | DIRECTIVE(TYPE_ASSERT, "type_assert") 165 | DIRECTIVE(NO_TYPE_ASSERT, "no_type_assert") 166 | DIRECTIVE(ALIGN, "align") 167 | DIRECTIVE(RAW_UNION, "raw_union") 168 | DIRECTIVE(PACKED, "packed") 169 | DIRECTIVE(TYPE, "type") 170 | DIRECTIVE(SIMD, "simd") 171 | DIRECTIVE(SOA, "soa") 172 | DIRECTIVE(PARTIAL, "partial") 173 | DIRECTIVE(SPARSE, "sparse") 174 | DIRECTIVE(RELATIVE, "relative") 175 | DIRECTIVE(FORCE_INLINE, "force_inline") 176 | DIRECTIVE(FORCE_NO_INLINE, "force_no_inline") 177 | DIRECTIVE(NO_NIL, "no_nil") 178 | DIRECTIVE(SHARED_NIL, "shared_nil") 179 | DIRECTIVE(NO_ALIAS, "no_alias") 180 | DIRECTIVE(C_VARARG, "c_vararg") 181 | DIRECTIVE(ANY_INT, "any_int") 182 | DIRECTIVE(SUBTYPE, "subtype") 183 | DIRECTIVE(BY_PTR, "by_ptr") 184 | DIRECTIVE(ASSERT, "assert") 185 | DIRECTIVE(PANIC, "panic") 186 | DIRECTIVE(UNROLL, "unroll") 187 | DIRECTIVE(LOCATION, "location") 188 | DIRECTIVE(PROCEDURE, "procedure") 189 | DIRECTIVE(LOAD, "load") 190 | DIRECTIVE(LOAD_HASH, "load_hash") 191 | DIRECTIVE(DEFINED, "defined") 192 | DIRECTIVE(CONFIG, "config") 193 | DIRECTIVE(MAYBE, "maybe") 194 | DIRECTIVE(CALLER_LOCATION, "caller_location") 195 | DIRECTIVE(NO_COPY, "no_copy") 196 | DIRECTIVE(CONST, "const") 197 | 198 | // ENUM, MATCH, WHERE 199 | ATTRIBUTE(TEST, "test", ATTRIBUTE_FOR_PROC) 200 | ATTRIBUTE(EXPORT, "export", ATTRIBUTE_FOR_PROC | ATTRIBUTE_FOR_VAR) 201 | ATTRIBUTE(LINKAGE, "linkage", ATTRIBUTE_FOR_PROC | ATTRIBUTE_FOR_VAR) 202 | ATTRIBUTE(REQUIRE, "require", ATTRIBUTE_FOR_PROC | ATTRIBUTE_FOR_VAR) 203 | ATTRIBUTE(INIT, "init", ATTRIBUTE_FOR_PROC) 204 | ATTRIBUTE(DEFERRED, "deferred", ATTRIBUTE_FOR_PROC) 205 | ATTRIBUTE(DEFERRED_NONE, "deferred_none", ATTRIBUTE_FOR_PROC) 206 | ATTRIBUTE(DEFERRED_IN, "deferred_in", ATTRIBUTE_FOR_PROC) 207 | ATTRIBUTE(DEFERRED_OUT, "deferred_out", ATTRIBUTE_FOR_PROC) 208 | ATTRIBUTE(DEFERRED_IN_OUT, "deferred_in_out", ATTRIBUTE_FOR_PROC) 209 | ATTRIBUTE(LINK_NAME, "link_name", ATTRIBUTE_FOR_PROC | ATTRIBUTE_FOR_VAR) 210 | ATTRIBUTE(LINK_PREFIX, "link_prefix", ATTRIBUTE_FOR_PROC) 211 | ATTRIBUTE(DEPRECATED, "deprecated", ATTRIBUTE_FOR_PROC) 212 | ATTRIBUTE(WARNING, "warning", ATTRIBUTE_FOR_PROC) 213 | ATTRIBUTE(REQUIRE_RESULTS, "require_results", ATTRIBUTE_FOR_PROC) 214 | ATTRIBUTE(DISABLED, "disabled", ATTRIBUTE_FOR_PROC) 215 | ATTRIBUTE(COLD, "cold", ATTRIBUTE_FOR_PROC) 216 | ATTRIBUTE(OPTIMIZATION_MODE, "optimization_mode", ATTRIBUTE_FOR_PROC) 217 | ATTRIBUTE(OBJC_NAME, "objc_name", ATTRIBUTE_FOR_PROC) 218 | ATTRIBUTE(OBJC_IS_CLASS_METHOD, "objc_is_class_method", ATTRIBUTE_FOR_PROC) 219 | ATTRIBUTE(OBJC_TYPE, "objc_type", ATTRIBUTE_FOR_PROC) 220 | ATTRIBUTE(REQUIRE_TARGET_FEATURE, "require_target_feature", ATTRIBUTE_FOR_PROC) 221 | ATTRIBUTE(ENABLE_TARGET_FEATURE, "enable_target_feature", ATTRIBUTE_FOR_PROC) 222 | ATTRIBUTE(STATIC, "static", ATTRIBUTE_FOR_VAR) 223 | ATTRIBUTE(THREAD_LOCAL, "thread_local", ATTRIBUTE_FOR_VAR) 224 | ATTRIBUTE(LINK_SECTION, "link_section", ATTRIBUTE_FOR_VAR) 225 | ATTRIBUTE(PRIVATE, "private", ATTRIBUTE_FOR_PROC | ATTRIBUTE_FOR_CONST | ATTRIBUTE_FOR_TYPE) 226 | ATTRIBUTE(OBJC_CLASS, "objc_class", ATTRIBUTE_FOR_TYPE) 227 | 228 | // Calling conventions 229 | CCONVENTION(ODIN, "odin") 230 | CCONVENTION(CONTEXTLESS, "contextless") 231 | CCONVENTION(CDECL, "cdecl") 232 | CCONVENTION(STDCALL, "stdcall") 233 | CCONVENTION(FASTCALL, "fastcall") 234 | CCONVENTION(NONE, "none") 235 | CCONVENTION(NAKED, "naked") 236 | CCONVENTION(WIN64, "win64") 237 | CCONVENTION(SYSV, "sysv") 238 | CCONVENTION(SYSTEM, "system") 239 | 240 | #undef ATTRIBUTE 241 | #undef DIRECTIVE 242 | #undef KEYWORD 243 | #undef OPERATOR 244 | #undef LITERAL 245 | #undef ASSIGNMENT 246 | #undef KIND 247 | #undef CCONVENTION -------------------------------------------------------------------------------- /src/lexer.c: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | #include "support.h" 3 | #include "report.h" 4 | #include "context.h" 5 | 6 | const Token TOKEN_NIL = { KIND_INVALID, { 0, 0 }, { 0, 0 }, { CAST(LiteralKind, 0), } }; 7 | 8 | // Automatic semicolon insertion tables. 9 | #define KIND(enum, name, asi) asi, 10 | static const Bool KIND_ASI[] = { 11 | #include "lexemes.h" 12 | }; 13 | 14 | #define OPERATOR(enum, match, precedence, named, asi) asi, 15 | static const Bool OPERATOR_ASI[] = { 16 | #include "lexemes.h" 17 | }; 18 | 19 | #define KEYWORD(enum, match, asi) asi, 20 | static const Bool KEYWORD_ASI[] = { 21 | #include "lexemes.h" 22 | }; 23 | 24 | // Keyword LUT 25 | #define KEYWORD(ident, match, ...) SLIT(match), 26 | static const String KEYWORDS[] = { 27 | #include "lexemes.h" 28 | }; 29 | 30 | // Directive LUT 31 | #define DIRECTIVE(ident, match) SLIT(match), 32 | static const String DIRECTIVES[] = { 33 | #include "lexemes.h" 34 | }; 35 | 36 | // Named operator LUT. 37 | // 38 | // We use a preprocessing trick here to extract the named operators 39 | // out of the global table. 40 | // 41 | // Since this table will only contain named operators we need the 42 | // OperatorKind enumerator to return in operator_find as the loop 43 | // index will no longer match like it does for KEYWORDS. 44 | #define OPERATOR_IS_NAMED_true(ident, match) \ 45 | { SLIT(match), OPERATOR_ ## ident }, 46 | #define OPERATOR_IS_NAMED_false(...) 47 | #define OPERATOR(ident, match, prec, named, ...) \ 48 | OPERATOR_IS_NAMED_ ## named(ident, match) 49 | static const struct NamedOperator { String s; OperatorKind e; } NAMED_OPERATORS[] = { 50 | #include "lexemes.h" 51 | }; 52 | 53 | #define LEX_ERROR(...) \ 54 | do { \ 55 | report_error(lexer->input.source, &lexer->last_location, lexer->context, __VA_ARGS__); \ 56 | THROW(ERROR_LEX); \ 57 | } while (0) 58 | 59 | // Searches for a keyword 60 | static Bool keyword_find(String string, KeywordKind *result) { 61 | // The KEYWORDS table matches the KeywordKind order, so we can just 62 | // return the result of the loop counter here as an optimization. 63 | for (Size i = 0; i < sizeof KEYWORDS / sizeof *KEYWORDS; i++) { 64 | if (string_compare(string, KEYWORDS[i])) { 65 | *result = CAST(KeywordKind, i); 66 | return true; 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | // Searches for a operator 73 | static Bool operator_find(String string, OperatorKind *result) { 74 | for (Size i = 0; i < sizeof NAMED_OPERATORS / sizeof *NAMED_OPERATORS; i++) { 75 | const struct NamedOperator *op = &NAMED_OPERATORS[i]; 76 | if (string_compare(string, op->s)) { 77 | *result = op->e; 78 | return true; 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | static Bool directive_find(String string, DirectiveKind *result) { 85 | for (Size i = 0; i < sizeof DIRECTIVES / sizeof *DIRECTIVES; i++) { 86 | if (string_compare(string, DIRECTIVES[i])) { 87 | *result = CAST(DirectiveKind, i); 88 | return true; 89 | } 90 | } 91 | return false; 92 | } 93 | 94 | static FORCE_INLINE Bool is_char(Rune ch) { 95 | if (ch < 0x80) { 96 | if (ch == '_') { 97 | return true; 98 | } 99 | return ((CAST(Uint32, ch) | 0x20) - 0x61) < 26; // [a-z][A-Z] 100 | } 101 | // TODO(dweiler): UTF-8. 102 | return true; 103 | } 104 | 105 | static FORCE_INLINE Bool is_digit(Rune ch) { 106 | if (ch < 0x80) { 107 | return (CAST(Uint32, ch) - '0') < 10; 108 | } 109 | // TODO(dweiler): UTF-8. 110 | return false; 111 | } 112 | 113 | static Uint8 peekl(Lexer *lexer) { 114 | const Input *input = &lexer->input; 115 | return input->cur < input->end ? *input->cur : 0; 116 | } 117 | 118 | static Rune advancel(Lexer *lexer) { 119 | Context *context = lexer->context; 120 | if (lexer->rune == '\n') { 121 | lexer->this_location.column = 0; 122 | lexer->this_location.line++; 123 | } 124 | Input *input = &lexer->input; 125 | if (input->cur < input->end) { 126 | lexer->here = input->cur; 127 | Rune rune = *input->cur; 128 | if (rune == 0) { 129 | LEX_ERROR("Unexpected EOF"); 130 | input->cur++; 131 | } else if (rune & 0x80) { 132 | Uint32 state = UTF8_ACCEPT; 133 | while (input->cur < input->end && *input->cur & 0x80) { 134 | utf8_decode(&state, &rune, *input->cur++); 135 | } 136 | if (state != UTF8_ACCEPT) { 137 | LEX_ERROR("Malformed UTF-8"); 138 | } 139 | } else { 140 | input->cur++; 141 | } 142 | lexer->rune = rune; 143 | lexer->this_location.column++; 144 | } else { 145 | lexer->here = input->end; 146 | lexer->rune = RUNE_EOF; 147 | } 148 | return lexer->rune; 149 | } 150 | 151 | static void skip_line(Lexer *lexer) { 152 | while (lexer->rune != '\n' && lexer->rune != RUNE_EOF) { 153 | advancel(lexer); 154 | } 155 | } 156 | 157 | static void skip_whitespace(Lexer *lexer, Bool newline) { 158 | for (;;) { 159 | const Rune rune = lexer->rune; 160 | if (rune == ' ' || rune == '\t' || rune == '\r' || (!newline && rune == '\n')) { 161 | advancel(lexer); 162 | } else { 163 | break; 164 | } 165 | } 166 | } 167 | 168 | static Sint32 numeric_base(Rune ch) { 169 | /**/ if (ch >= '0' && ch <= '9') return ch - '0'; 170 | else if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; 171 | else if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; 172 | return 16; 173 | } 174 | 175 | static void scan(Lexer* lexer, Sint32 base) { 176 | while (numeric_base(lexer->rune) < base || lexer->rune == '_') { 177 | advancel(lexer); 178 | } 179 | } 180 | 181 | static FORCE_INLINE Token mkkind(const Lexer *lexer, Kind kind) { 182 | Token token; 183 | token.kind = kind; 184 | token.location = lexer->last_location; 185 | token.string = STRING_NIL; 186 | token.as_opaque = 0; 187 | return token; 188 | } 189 | 190 | static FORCE_INLINE Token mkop(const Lexer *lexer, OperatorKind kind) { 191 | Token token = mkkind(lexer, KIND_OPERATOR); 192 | token.as_operator = kind; 193 | return token; 194 | } 195 | 196 | static FORCE_INLINE Token mkassign(const Lexer *lexer, AssignmentKind kind) { 197 | Token token = mkkind(lexer, KIND_ASSIGNMENT); 198 | token.as_assignment = kind; 199 | return token; 200 | } 201 | 202 | static Token scan_numeric(Lexer *lexer, Bool dot) { 203 | Token token = mkkind(lexer, KIND_LITERAL); 204 | token.string.contents = CCAST(Uint8*, lexer->here); 205 | token.string.length = 1; 206 | token.as_literal = LITERAL_INTEGER; 207 | 208 | if (dot) { 209 | token.as_literal = LITERAL_FLOAT; 210 | token.string.contents--; 211 | token.string.length++; 212 | token.location.column--; 213 | scan(lexer, 10); 214 | goto L_exponent; 215 | } 216 | 217 | if (lexer->rune == '0') { 218 | switch (advancel(lexer)) { 219 | case 'b': 220 | advancel(lexer); 221 | scan(lexer, 2); 222 | break; 223 | case 'o': 224 | advancel(lexer); 225 | scan(lexer, 8); 226 | break; 227 | case 'd': 228 | advancel(lexer); 229 | scan(lexer, 10); 230 | break; 231 | case 'z': 232 | advancel(lexer); 233 | scan(lexer, 12); 234 | break; 235 | case 'x': 236 | advancel(lexer); 237 | scan(lexer, 16); 238 | break; 239 | case 'h': 240 | advancel(lexer); 241 | scan(lexer, 16); 242 | break; 243 | default: 244 | break; 245 | } 246 | } 247 | 248 | scan(lexer, 10); 249 | 250 | if (lexer->rune == '.') { 251 | // .. is an operator 252 | if (peekl(lexer) == '.') { 253 | token.string.length = lexer->here - token.string.contents; 254 | return token; 255 | } 256 | 257 | advancel(lexer); 258 | token.as_literal = LITERAL_FLOAT; 259 | scan(lexer, 10); 260 | } 261 | 262 | L_exponent: 263 | // Handle 'e' and 'E' exponents. 264 | if (lexer->rune == 'e' || lexer->rune == 'E') { 265 | advancel(lexer); 266 | token.as_literal = LITERAL_FLOAT; 267 | if (lexer->rune == '-' || lexer->rune == '+') { 268 | advancel(lexer); 269 | } 270 | scan(lexer, 10); 271 | } 272 | 273 | if (lexer->rune == 'i' || lexer->rune == 'j' || lexer->rune == 'k') { 274 | advancel(lexer); 275 | token.as_literal = LITERAL_IMAGINARY; 276 | } 277 | 278 | token.string.length = lexer->here - token.string.contents; 279 | 280 | return token; 281 | } 282 | 283 | static Bool scan_escape(Lexer *lexer) { 284 | Context *const context = lexer->context; 285 | switch (lexer->rune) { 286 | // \a, \b, \e, \f, \n, \r, \t, \v 287 | case 'a': case 'b': case 'e': case 'f': case 'n': case 'r': case 't': case 'v': 288 | advancel(lexer); 289 | return true; 290 | case '\\': case '\'': case '\"': 291 | advancel(lexer); 292 | return true; 293 | case 'x': 294 | advancel(lexer); 295 | for (Size i = 0; i < 2; i++) advancel(lexer); // "0x" 296 | return true; 297 | case 'u': 298 | advancel(lexer); 299 | for (Size i = 0; i < 4; i++) advancel(lexer); // u000 300 | return true; 301 | case 'U': 302 | advancel(lexer); 303 | for (Size i = 0; i < 8; i++) advancel(lexer); // U0000000 304 | return true; 305 | default: 306 | if (is_digit(lexer->rune)) { 307 | for (Size i = 0; i < 3; i++) advancel(lexer); // %d00 308 | return true; 309 | } else { 310 | if (lexer->rune < 0) { 311 | LEX_ERROR("Unterminated escape sequence"); 312 | } else { 313 | LEX_ERROR("Unknown escape sequence %d", CAST(int, lexer->rune)); 314 | } 315 | } 316 | } 317 | UNREACHABLE(); 318 | } 319 | 320 | Bool lexer_init(Lexer *lexer, const Source *source, Context *context) { 321 | const String *const string = &source->contents; 322 | if (string->length == 0) { 323 | return false; 324 | } 325 | 326 | lexer->context = context; 327 | 328 | lexer->input.source = source; 329 | lexer->input.cur = string->contents; 330 | lexer->input.end = string->contents + string->length; 331 | 332 | lexer->this_location.column = 0; 333 | lexer->this_location.line = 1; 334 | 335 | lexer->here = lexer->input.cur; 336 | lexer->rune = 0; 337 | lexer->asi = false; 338 | lexer->peek = array_make(context); 339 | 340 | if (advancel(lexer) == RUNE_BOM) { 341 | advancel(lexer); 342 | } 343 | 344 | return true; 345 | } 346 | 347 | void lexer_fini(Lexer *lexer) { 348 | array_free(lexer->peek); 349 | } 350 | 351 | static Token lexer_raw_next(Lexer *lexer); 352 | 353 | Token lexer_peek(Lexer *lexer) { 354 | const Token peek = lexer_raw_next(lexer); 355 | const Size index = array_size(lexer->peek); 356 | array_push(lexer->peek, peek); 357 | return index ? lexer->peek[index - 1] : peek; 358 | } 359 | 360 | static Token lexer_tokenize(Lexer *lexer) { 361 | Context *const context = lexer->context; 362 | 363 | skip_whitespace(lexer, lexer->asi); 364 | 365 | lexer->last_location = lexer->this_location; 366 | 367 | Token token = mkkind(lexer, KIND_INVALID); 368 | token.string.contents = CCAST(Uint8*, lexer->here); 369 | token.string.length = 1; // One rune. 370 | 371 | const Rune rune = lexer->rune; 372 | if (is_char(rune)) { 373 | // Looks like an identifier. 374 | token.kind = KIND_IDENTIFIER; 375 | while (is_char(lexer->rune) || is_digit(lexer->rune)) { 376 | advancel(lexer); 377 | } 378 | token.string.length = lexer->here - token.string.contents; 379 | // Check if this token is actually a keyword. 380 | if (keyword_find(token.string, &token.as_keyword)) { 381 | token.kind = KIND_KEYWORD; 382 | } 383 | // Check if this token is actually a operator. 384 | if (operator_find(token.string, &token.as_operator)) { 385 | token.kind = KIND_OPERATOR; 386 | } 387 | return token; 388 | } else if (is_digit(rune)) { 389 | return scan_numeric(lexer, false); 390 | } 391 | 392 | // Single character dispatch 393 | advancel(lexer); 394 | 395 | // #define USE_COMPUTED_GOTO 396 | 397 | #if defined(USE_COMPUTED_GOTO) 398 | // Can we use a threaded dispatch table. 399 | static const void *DISPATCH_TABLE[] = { 400 | [CAST(Uint8, RUNE_EOF)] = &&L_EOF, 401 | ['\n'] = &&L_LF, 402 | ['\\'] = &&L_BSLASH, 403 | ['\''] = &&L_SQUOTE, 404 | ['"'] = &&L_DQUOTE, 405 | ['`'] = &&L_GRAVE, 406 | ['.'] = &&L_PERIOD, 407 | ['{'] = &&L_LBRACE, 408 | ['}'] = &&L_RBRACE, 409 | [';'] = &&L_SEMI, 410 | ['@'] = &&L_AT, 411 | ['$'] = &&L_DOLLAR, 412 | ['?'] = &&L_QUESTION, 413 | ['^'] = &&L_CARET, 414 | [','] = &&L_COMMA, 415 | [':'] = &&L_COLON, 416 | ['('] = &&L_LPAREN, 417 | [')'] = &&L_RPAREN, 418 | ['['] = &&L_LBRACKET, 419 | [']'] = &&L_RBRACKET, 420 | ['%'] = &&L_PERCENT, 421 | ['*'] = &&L_STAR, 422 | ['='] = &&L_EQUAL, 423 | ['~'] = &&L_TILDE, 424 | ['!'] = &&L_EXCLAIM, 425 | ['+'] = &&L_PLUS, 426 | ['-'] = &&L_MINUS, 427 | ['#'] = &&L_POUND, 428 | ['/'] = &&L_FSLASH, 429 | ['<'] = &&L_LT, 430 | ['>'] = &&L_GT, 431 | ['&'] = &&L_AND, 432 | ['|'] = &&L_OR, 433 | }; 434 | #define CASE(_name, char) L ## _ ## _name 435 | #define SWITCH(code) goto *DISPATCH_TABLE[CAST(Uint8, code)]; 436 | #else 437 | #define CASE(_name, char) case char 438 | #define SWITCH(code) switch (code) 439 | #endif 440 | 441 | SWITCH(rune) { 442 | CASE(EOF, RUNE_EOF): 443 | token.kind = KIND_EOF; 444 | if (lexer->asi) { 445 | lexer->asi = false; 446 | token.string = SCLIT("\n"); 447 | token.kind = KIND_SEMICOLON; 448 | return token; 449 | } 450 | return token; 451 | CASE(LF, '\n'): 452 | lexer->asi = false; 453 | token.string = SCLIT("\n"); 454 | token.kind = KIND_SEMICOLON; 455 | return token; 456 | CASE(BSLASH, '\\'): 457 | // Line continuation. 458 | lexer->asi = false; 459 | return lexer_tokenize(lexer); 460 | CASE(SQUOTE, '\''): 461 | // Rune literal. 462 | { 463 | token.kind = KIND_LITERAL; 464 | token.as_literal = LITERAL_RUNE; 465 | Rune quote = rune; 466 | for (;;) { 467 | Rune ch = lexer->rune; 468 | if (ch == '\n' || ch < 0) { 469 | LEX_ERROR("Unterminated rune literal"); 470 | } 471 | advancel(lexer); 472 | if (ch == quote) { 473 | break; 474 | } 475 | if (ch == '\\' && !scan_escape(lexer)) { 476 | LEX_ERROR("Malformed rune literal"); 477 | } 478 | } 479 | token.string.length = lexer->here - token.string.contents; 480 | return token; 481 | } 482 | CASE(GRAVE, '`'): 483 | // Raw string literal 484 | // FALLTHROUGH(); 485 | CASE(DQUOTE, '"'): 486 | { 487 | const Rune quote = rune; 488 | for (;;) { 489 | const Rune ch = lexer->rune; 490 | if (ch == '\n' || ch < 0) { 491 | LEX_ERROR("Unterminated string literal"); 492 | break; 493 | } 494 | advancel(lexer); 495 | if (ch == quote) { 496 | break; 497 | } 498 | if (rune == '"' && (ch == '\\' && !scan_escape(lexer))) { 499 | LEX_ERROR("Malformed string literal"); 500 | } 501 | } 502 | token.kind = KIND_LITERAL; 503 | token.as_literal = LITERAL_STRING; 504 | token.string.length = lexer->here - token.string.contents; 505 | return token; 506 | } 507 | CASE(PERIOD, '.'): 508 | if (is_digit(lexer->rune)) { 509 | return scan_numeric(lexer, true); 510 | } else if (lexer->rune == '.') { 511 | switch (advancel(lexer)) { 512 | case '<': 513 | advancel(lexer); 514 | return mkop(lexer, OPERATOR_RANGEHALF); 515 | case '=': 516 | advancel(lexer); 517 | return mkop(lexer, OPERATOR_RANGEFULL); 518 | default: 519 | return mkop(lexer, OPERATOR_ELLIPSIS); 520 | } 521 | } else { 522 | return mkop(lexer, OPERATOR_PERIOD); 523 | } 524 | CASE(LBRACE, '{'): return mkkind(lexer, KIND_LBRACE); 525 | CASE(RBRACE, '}'): return mkkind(lexer, KIND_RBRACE); 526 | CASE(SEMI, ';'): return mkkind(lexer, KIND_SEMICOLON); 527 | CASE(AT, '@'): return mkkind(lexer, KIND_ATTRIBUTE); 528 | CASE(DOLLAR, '$'): return mkkind(lexer, KIND_CONST); 529 | CASE(QUESTION, '?'): return mkop(lexer, OPERATOR_QUESTION); 530 | CASE(CARET, '^'): return mkop(lexer, OPERATOR_POINTER); 531 | CASE(COMMA, ','): return mkop(lexer, OPERATOR_COMMA); 532 | CASE(COLON, ':'): return mkop(lexer, OPERATOR_COLON); 533 | CASE(LPAREN, '('): return mkop(lexer, OPERATOR_LPAREN); 534 | CASE(RPAREN, ')'): return mkop(lexer, OPERATOR_RPAREN); 535 | CASE(LBRACKET, '['): return mkop(lexer, OPERATOR_LBRACKET); 536 | CASE(RBRACKET, ']'): return mkop(lexer, OPERATOR_RBRACKET); 537 | CASE(PERCENT, '%'): 538 | switch (lexer->rune) { 539 | case '=': 540 | advancel(lexer); 541 | return mkassign(lexer, ASSIGNMENT_QUO); 542 | case '%': 543 | if (advancel(lexer) == '=') { 544 | advancel(lexer); 545 | return mkassign(lexer, ASSIGNMENT_REM); 546 | } 547 | return mkop(lexer, OPERATOR_MODMOD); 548 | default: 549 | return mkop(lexer, OPERATOR_MOD); 550 | } 551 | CASE(STAR, '*'): 552 | if (lexer->rune == '=') { 553 | advancel(lexer); 554 | return mkassign(lexer, ASSIGNMENT_MUL); 555 | } else { 556 | return mkop(lexer, OPERATOR_MUL); 557 | } 558 | CASE(EQUAL, '='): 559 | if (lexer->rune == '=') { 560 | advancel(lexer); 561 | return mkop(lexer, OPERATOR_CMPEQ); 562 | } else { 563 | return mkassign(lexer, ASSIGNMENT_EQ); 564 | } 565 | CASE(TILDE, '~'): 566 | if (lexer->rune == '=') { 567 | advancel(lexer); 568 | return mkassign(lexer, ASSIGNMENT_XOR); 569 | } else { 570 | return mkop(lexer, OPERATOR_XOR); 571 | } 572 | CASE(EXCLAIM, '!'): 573 | if (lexer->rune == '=') { 574 | advancel(lexer); 575 | return mkop(lexer, OPERATOR_NOTEQ); 576 | } else { 577 | return mkop(lexer, OPERATOR_NOT); 578 | } 579 | CASE(PLUS, '+'): 580 | switch (lexer->rune) { 581 | case '=': 582 | advancel(lexer); 583 | return mkassign(lexer, ASSIGNMENT_ADD); 584 | case '+': 585 | LEX_ERROR("The increment operator '++' does not exist"); 586 | default: 587 | return mkop(lexer, OPERATOR_ADD); 588 | } 589 | CASE(MINUS, '-'): 590 | switch (lexer->rune) { 591 | case '=': 592 | advancel(lexer); 593 | return mkassign(lexer, ASSIGNMENT_SUB); 594 | case '-': 595 | if (advancel(lexer) == '-') { 596 | advancel(lexer); 597 | return mkkind(lexer, KIND_UNDEFINED); 598 | } else { 599 | LEX_ERROR("The decrement operator '--' does not exist"); 600 | } 601 | case '>': 602 | advancel(lexer); 603 | return mkop(lexer, OPERATOR_ARROW); 604 | default: 605 | return mkop(lexer, OPERATOR_SUB); 606 | } 607 | CASE(POUND, '#'): 608 | while (is_char(lexer->rune)) { 609 | advancel(lexer); 610 | } 611 | token.string.contents += 1; // Skip '#' 612 | token.string.length = lexer->here - token.string.contents; 613 | if (directive_find(token.string, &token.as_directive)) { 614 | token.kind = KIND_DIRECTIVE; 615 | } else { 616 | LEX_ERROR("Unknown directive '%.*s'", SFMT(token.string)); 617 | } 618 | return token; 619 | CASE(FSLASH, '/'): 620 | switch (lexer->rune) { 621 | case '=': 622 | advancel(lexer); 623 | return mkassign(lexer, ASSIGNMENT_QUO); 624 | // Line comment. 625 | case '/': 626 | skip_line(lexer); 627 | return mkkind(lexer, KIND_COMMENT); 628 | // Block comment 629 | case '*': 630 | advancel(lexer); 631 | // Support nested block comments. 632 | for (int i = 1; i > 0; /**/) switch (lexer->rune) { 633 | case RUNE_EOF: 634 | return mkkind(lexer, KIND_EOF); 635 | case '/': 636 | if (advancel(lexer) == '*') { 637 | advancel(lexer); 638 | i++; 639 | } 640 | break; 641 | case '*': 642 | if (advancel(lexer) == '/') { 643 | advancel(lexer); 644 | i--; 645 | } 646 | break; 647 | default: 648 | advancel(lexer); 649 | } 650 | return mkkind(lexer, KIND_COMMENT); 651 | default: 652 | return mkop(lexer, OPERATOR_QUO); 653 | } 654 | CASE(LT, '<'): 655 | switch (lexer->rune) { 656 | case '=': 657 | advancel(lexer); 658 | return mkop(lexer, OPERATOR_LTEQ); 659 | case '<': 660 | if (advancel(lexer) == '=') { 661 | advancel(lexer); 662 | return mkassign(lexer, ASSIGNMENT_SHL); 663 | } 664 | return mkop(lexer, OPERATOR_SHL); 665 | default: 666 | return mkop(lexer, OPERATOR_LT); 667 | } 668 | CASE(GT, '>'): 669 | switch (lexer->rune) { 670 | case '=': 671 | advancel(lexer); 672 | return mkop(lexer, OPERATOR_GTEQ); 673 | case '>': 674 | if (advancel(lexer) == '=') { 675 | advancel(lexer); 676 | return mkassign(lexer, ASSIGNMENT_SHR); 677 | } 678 | return mkop(lexer, OPERATOR_SHR); 679 | default: 680 | return mkop(lexer, OPERATOR_GT); 681 | } 682 | CASE(AND, '&'): 683 | switch (lexer->rune) { 684 | case '~': 685 | if (advancel(lexer) == '=') { 686 | advancel(lexer); 687 | return mkassign(lexer, ASSIGNMENT_ANDNOT); 688 | } 689 | return mkop(lexer, OPERATOR_ANDNOT); 690 | case '=': 691 | advancel(lexer); 692 | return mkassign(lexer, ASSIGNMENT_AND); 693 | case '&': 694 | if (advancel(lexer) == '=') { 695 | advancel(lexer); 696 | return mkassign(lexer, ASSIGNMENT_AND); 697 | } 698 | return mkop(lexer, OPERATOR_CMPAND); 699 | default: 700 | return mkop(lexer, OPERATOR_AND); 701 | } 702 | CASE(OR, '|'): 703 | switch (lexer->rune) { 704 | case '=': 705 | advancel(lexer); 706 | return mkassign(lexer, ASSIGNMENT_OR); 707 | case '|': 708 | if (advancel(lexer) == '=') { 709 | advancel(lexer); 710 | return mkassign(lexer, ASSIGNMENT_CMPOR); 711 | } 712 | return mkop(lexer, OPERATOR_CMPOR); 713 | default: 714 | return mkop(lexer, OPERATOR_OR); 715 | } 716 | } 717 | 718 | LEX_ERROR("Unexpected rune '%d'", rune); 719 | } 720 | 721 | // Simple helper routines 722 | String kind_to_string(Kind kind) { 723 | #define KIND(enumerator, name, ...) SLIT(name), 724 | static const String KINDS[] = { 725 | #include "lexemes.h" 726 | }; 727 | return KINDS[kind]; 728 | } 729 | 730 | String literal_to_string(LiteralKind literal) { 731 | #define LITERAL(enumerator, name) SLIT(name), 732 | static const String LITERALS[] = { 733 | #include "lexemes.h" 734 | }; 735 | return LITERALS[literal]; 736 | } 737 | 738 | String keyword_to_string(KeywordKind keyword) { 739 | #define KEYWORD(ident, string, ...) SLIT(string), 740 | static const String KEYWORDS[] = { 741 | #include "lexemes.h" 742 | }; 743 | return KEYWORDS[keyword]; 744 | } 745 | 746 | String operator_to_string(OperatorKind op) { 747 | #define OPERATOR(ident, string, ...) SLIT(string), 748 | static const String OPERATORS[] = { 749 | #include "lexemes.h" 750 | }; 751 | return OPERATORS[op]; 752 | } 753 | 754 | String assignment_to_string(AssignmentKind assignment) { 755 | #define ASSIGNMENT(ident, string) SLIT(string), 756 | static const String ASSIGNMENTS[] = { 757 | #include "lexemes.h" 758 | }; 759 | return ASSIGNMENTS[assignment]; 760 | } 761 | 762 | String directive_to_string(DirectiveKind directive) { 763 | #define DIRECTIVE(ident, string) SLIT(string), 764 | static const String DIRECTIVES[] = { 765 | #include "lexemes.h" 766 | }; 767 | return DIRECTIVES[directive]; 768 | } 769 | 770 | String token_to_string(Token token) { 771 | #define KIND(enumerator, kind, ...) SLIT(#enumerator), 772 | static const String STRINGS[] = { 773 | #include "lexemes.h" 774 | }; 775 | switch (token.kind) { 776 | case KIND_COMMENT: 777 | FALLTHROUGH(); 778 | case KIND_IDENTIFIER: 779 | return token.string; 780 | case KIND_KEYWORD: 781 | return keyword_to_string(token.as_keyword); 782 | case KIND_LITERAL: 783 | return token.string; 784 | case KIND_OPERATOR: 785 | return operator_to_string(token.as_operator); 786 | case KIND_ASSIGNMENT: 787 | return assignment_to_string(token.as_assignment); 788 | default: 789 | return STRINGS[token.kind]; 790 | } 791 | UNREACHABLE(); 792 | } 793 | 794 | static Token lexer_raw_next(Lexer *lexer) { 795 | const Token token = lexer_tokenize(lexer); 796 | switch (token.kind) { 797 | case KIND_OPERATOR: 798 | lexer->asi = OPERATOR_ASI[token.as_operator]; 799 | break; 800 | case KIND_KEYWORD: 801 | lexer->asi = KEYWORD_ASI[token.as_keyword]; 802 | break; 803 | default: 804 | lexer->asi = KIND_ASI[token.kind]; 805 | break; 806 | } 807 | return token; 808 | } 809 | 810 | Token lexer_next(Lexer *lexer) { 811 | if (array_size(lexer->peek) != 0) { 812 | const Token token = lexer->peek[0]; 813 | array_pop_front(lexer->peek); 814 | return token; 815 | } 816 | return lexer_raw_next(lexer); 817 | } -------------------------------------------------------------------------------- /src/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_LEXER_H 2 | #define CODIN_LEXER_H 3 | #include "string.h" 4 | #include "array.h" 5 | 6 | typedef struct Context Context; 7 | 8 | typedef struct Lexer Lexer; 9 | typedef struct Token Token; 10 | typedef struct Location Location; 11 | typedef struct Source Source; 12 | typedef struct Input Input; 13 | 14 | #define KIND(kind, ...) KIND_ ## kind, 15 | enum Kind { 16 | #include "lexemes.h" 17 | KIND_COUNT, 18 | }; 19 | typedef enum Kind Kind; 20 | String kind_to_string(Kind kind); 21 | 22 | #define LITERAL(kind, ...) LITERAL_ ## kind, 23 | enum LiteralKind { 24 | #include "lexemes.h" 25 | LITERAL_COUNT, 26 | }; 27 | typedef enum LiteralKind LiteralKind; 28 | String literal_to_string(LiteralKind literal); 29 | 30 | #define OPERATOR(kind, ...) OPERATOR_ ## kind, 31 | enum OperatorKind { 32 | #include "lexemes.h" 33 | OPERATOR_COUNT, 34 | }; 35 | typedef enum OperatorKind OperatorKind; 36 | String operator_to_string(OperatorKind op); 37 | 38 | #define KEYWORD(kind, ...) KEYWORD_ ## kind, 39 | enum KeywordKind { 40 | #include "lexemes.h" 41 | KEYWORD_COUNT, 42 | }; 43 | typedef enum KeywordKind KeywordKind; 44 | String keyword_to_string(KeywordKind keyword); 45 | 46 | #define ASSIGNMENT(kind, ...) ASSIGNMENT_ ## kind, 47 | enum AssignmentKind { 48 | #include "lexemes.h" 49 | ASSIGNMENT_COUNT, 50 | }; 51 | typedef enum AssignmentKind AssignmentKind; 52 | String assignment_to_string(AssignmentKind assignment); 53 | 54 | #define DIRECTIVE(kind, ...) DIRECTIVE_ ## kind, 55 | enum DirectiveKind { 56 | #include "lexemes.h" 57 | DIRECTIVE_COUNT, 58 | }; 59 | 60 | typedef enum DirectiveKind DirectiveKind; 61 | 62 | String directive_to_string(DirectiveKind directive); 63 | 64 | struct Source { 65 | String name; 66 | String contents; 67 | }; 68 | 69 | struct Location { 70 | int column; 71 | int line; 72 | }; 73 | 74 | struct Token { 75 | Kind kind; 76 | Location location; 77 | String string; // comment, identifier 78 | union { 79 | LiteralKind as_literal; 80 | OperatorKind as_operator; 81 | KeywordKind as_keyword; 82 | AssignmentKind as_assignment; 83 | DirectiveKind as_directive; 84 | void *as_opaque; 85 | }; 86 | }; 87 | 88 | extern const Token TOKEN_NIL; 89 | 90 | STATIC_ASSERT(sizeof(Token) <= 64, "Too big"); 91 | 92 | String token_to_string(Token token); 93 | 94 | struct Input { 95 | const Source *source; 96 | Uint8 *cur; 97 | const Uint8 *end; 98 | }; 99 | 100 | struct Lexer { 101 | Context *context; 102 | Input input; 103 | Location this_location; 104 | Location last_location; 105 | const Uint8 *here; 106 | Rune rune; 107 | Bool asi; 108 | Array(Token) peek; 109 | }; 110 | 111 | Bool lexer_init(Lexer *lexer, const Source *source, Context *context); 112 | void lexer_fini(Lexer *lexer); 113 | 114 | Token lexer_next(Lexer *lexer); 115 | Token lexer_peek(Lexer *lexer); 116 | 117 | #endif // CODIN_LEXER_H -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "string.h" // String, SCLIT, SFMT 4 | #include "build.h" 5 | #include "dump.h" 6 | 7 | typedef struct Command Command; 8 | 9 | struct Command { 10 | String name; 11 | String description; 12 | }; 13 | 14 | static const Command COMMANDS[] = { 15 | { SLIT("dump-ast"), SLIT("dump the generated syntax tree to stdout.") }, 16 | }; 17 | 18 | String project_name(String path) { 19 | (void)path; 20 | return SCLIT("main"); 21 | } 22 | 23 | static int usage(const char *app) { 24 | printf("Usage:\n"); 25 | printf("\t%s command [arguments]\n", app); 26 | printf("Commands:\n"); 27 | Size max = 0; 28 | for (Size i = 0; i < sizeof(COMMANDS)/sizeof *COMMANDS; i++) { 29 | const Command *command = &COMMANDS[i]; 30 | if (command->name.length > max) max = command->name.length; 31 | } 32 | for (Size i = 0; i < sizeof(COMMANDS)/sizeof *COMMANDS; i++) { 33 | const Command *command = &COMMANDS[i]; 34 | printf("\t%.*s%*c\t%.*s\n", 35 | SFMT(command->name), 36 | CAST(Sint32, max - command->name.length), ' ', 37 | SFMT(command->description)); 38 | } 39 | printf("\n"); 40 | printf("For further details on a command, invoke command help:\n"); 41 | printf("\te.g. `%s build -help` or `%s help build\n", app, app); 42 | return 1; 43 | } 44 | 45 | 46 | static Bool dump_ast(String pathname) { 47 | Bool result = false; 48 | BuildContext build; 49 | build_init(&build, SCLIT("arena"), SCLIT("async"), SCLIT("spall")); 50 | // build_init(&build, SCLIT("arena"), SCLIT("sync")); 51 | build_add_collection(&build, SCLIT("core"), SCLIT("/home/graphitemaster/work/EmberGen/Odin/core")); 52 | build_add_collection(&build, SCLIT("vendor"), SCLIT("/home/graphitemaster/work/EmberGen/Odin/vendor")); 53 | build_add_package(&build, pathname); 54 | build_wait(&build); 55 | 56 | mutex_lock(&build.mutex); 57 | const Size n_work = array_size(build.work); 58 | for (Size i = 0; i < n_work; i++) { 59 | const BuildWork *const work = build.work[i]; 60 | if (work->error) { 61 | goto L_error; 62 | } 63 | // dump(work->tree); 64 | } 65 | 66 | result = true; 67 | 68 | L_error: 69 | mutex_unlock(&build.mutex); 70 | build_fini(&build); 71 | return result; 72 | } 73 | 74 | int main(int argc, char **argv) { 75 | const char *app = argv[0]; 76 | 77 | argc--; 78 | argv++; 79 | if (argc <= 1) { 80 | return usage(app); 81 | } 82 | 83 | Bool result = false; 84 | if (!strcmp(argv[0], "dump-ast")) { 85 | result = dump_ast(string_from_null(argv[1])); 86 | } else { 87 | return usage(app); 88 | } 89 | 90 | return result ? 0 : 1; 91 | } 92 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_PARSER_H 2 | #define CODIN_PARSER_H 3 | #include "string.h" 4 | 5 | typedef struct Context Context; 6 | typedef struct Tree Tree; 7 | 8 | Bool parse(Tree *tree, Context *context); 9 | 10 | #endif // CODIN_PARSER_H -------------------------------------------------------------------------------- /src/path.c: -------------------------------------------------------------------------------- 1 | #include "path.h" 2 | #include "context.h" 3 | #include "strbuf.h" 4 | 5 | #if defined(OS_POSIX) 6 | #include // free, realpath 7 | #include // mkdir 8 | #include // DIR, dirent, readdir 9 | #elif defined(OS_WINDOWS) 10 | #define WIN32_LEAN_AND_MEAN 11 | #include 12 | #endif 13 | 14 | Bool path_mkdir(String pathname, Context *context) { 15 | char *terminated = string_to_null(pathname, context); 16 | #if defined(OS_POSIX) 17 | (void)context; 18 | return mkdir(terminated, 0777) == 0; 19 | #elif defined(OS_WINDOWS) 20 | Uint16 *pathname_utf16 = 0; 21 | utf8_to_utf16(terminated, &pathname_utf16, context); 22 | allocator_deallocate(&context->allocator, terminated); 23 | const Bool result = CreateDirectoryW(RCAST(LPCWSTR, pathname_utf16), 0); 24 | allocator_deallocate(&context->allocator, pathname_utf16); 25 | return result; 26 | #else 27 | #error Missing implementation 28 | #endif 29 | allocator_deallocate(&context->allocator, terminated); 30 | return false; 31 | } 32 | 33 | Bool path_directory_exists(String pathname, Context *context) { 34 | char *terminated = string_to_null(pathname, context); 35 | #if defined(OS_POSIX) 36 | struct stat s = {}; 37 | if (stat(terminated, &s) == 0) { 38 | allocator_deallocate(&context->allocator, terminated); 39 | return (s.st_mode & S_IFDIR); 40 | } 41 | #elif defined(OS_WINDOWS) 42 | Uint16 *pathname_utf16 = 0; 43 | utf8_to_utf16(terminated, &pathname_utf16, context); 44 | allocator_deallocate(&context->allocator, terminated); 45 | const DWORD attributes = GetFileAttributesW(RCAST(LPCWSTR, pathname_utf16)); 46 | allocator_deallocate(&context->allocator, pathname_utf16); 47 | return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY); 48 | #else 49 | #error Missing implementation 50 | #endif 51 | allocator_deallocate(&context->allocator, terminated); 52 | return false; 53 | } 54 | 55 | Array(String) path_list(String path, Context *context) { 56 | Array(String) results = array_make(context); 57 | #if defined(OS_POSIX) 58 | char *terminated = string_to_null(path, context); 59 | DIR *dp = opendir(terminated); 60 | allocator_deallocate(&context->allocator, terminated); 61 | if (!dp) { 62 | goto L_error; 63 | } 64 | for (struct dirent *de = readdir(dp); de; de = readdir(dp)) { 65 | const char *name = de->d_name; 66 | if (de->d_type == DT_DIR) { 67 | continue; 68 | } 69 | array_push(results, string_copy_from_null(name, context)); 70 | } 71 | closedir(dp); 72 | return results; 73 | #elif defined(OS_WINDOWS) 74 | const char WILDCARD[] = "\\*"; 75 | char *terminated = allocator_allocate(&context->allocator, path.length + sizeof WILDCARD); 76 | memcpy(terminated, path.contents, path.length); 77 | memcpy(terminated + path.length, WILDCARD, sizeof WILDCARD); 78 | Uint16 *pathname_utf16 = 0; 79 | utf8_to_utf16(terminated, &pathname_utf16, context); 80 | allocator_deallocate(&context->allocator, terminated); 81 | WIN32_FIND_DATAW ent; 82 | HANDLE handle = FindFirstFileW(RCAST(LPCWSTR, pathname_utf16), &ent); 83 | allocator_deallocate(&context->allocator, pathname_utf16); 84 | if (handle == INVALID_HANDLE_VALUE) { 85 | goto L_error; 86 | } 87 | do { 88 | if (ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 89 | continue; 90 | } 91 | char *pathname_utf8 = 0; 92 | utf16_to_utf8(RCAST(const Uint16 *, ent.cFileName), &pathname_utf8, context); 93 | array_push(results, string_from_null(pathname_utf8)); 94 | } while (FindNextFileW(handle, &ent)); 95 | FindClose(handle); 96 | #else 97 | #error Missing implementation 98 | #endif 99 | return results; 100 | 101 | L_error: 102 | array_free(results); 103 | return 0; 104 | } 105 | 106 | String path_cat(String pathname, String filename, Context *context) { 107 | if (string_ends_with(pathname, SCLIT("/")) || 108 | string_ends_with(pathname, SCLIT("\\"))) 109 | { 110 | pathname.length--; 111 | } 112 | 113 | const Size length = pathname.length + filename.length + 1; 114 | Uint8* data = allocator_allocate(&context->allocator, length); 115 | if (pathname.contents) { 116 | memcpy(data, pathname.contents, pathname.length); 117 | } 118 | data[pathname.length] = '/'; 119 | memcpy(&data[pathname.length + 1], filename.contents, filename.length); 120 | return LIT(String, data, length); 121 | } 122 | 123 | String path_canonicalize(String pathname, Context *context) { 124 | char *terminated = string_to_null(pathname, context); 125 | #if defined(OS_POSIX) 126 | char *resolved = realpath(terminated, 0); 127 | String result = string_copy_from_null(resolved, context); 128 | free(resolved); 129 | return result; 130 | #elif defined(OS_WINDOWS) 131 | Allocator *const allocator = &context->allocator; 132 | // Convert pathname to UTF-16. 133 | Uint16 *pathname_utf16 = 0; 134 | utf8_to_utf16(terminated, &pathname_utf16, context); 135 | ULONG length = GetFullPathNameW(RCAST(LPCWSTR, pathname_utf16), 0, 0, 0); 136 | PWSTR buffer = allocator_allocate(allocator, length * sizeof(WCHAR)); 137 | if (GetFullPathNameW(RCAST(LPCWSTR, pathname_utf16), length, buffer, 0)) { 138 | allocator_deallocate(allocator, pathname_utf16); 139 | char *pathname_utf8 = 0; 140 | utf16_to_utf8(RCAST(const Uint16 *, buffer), &pathname_utf8, context); 141 | allocator_deallocate(allocator, buffer); 142 | return string_from_null(pathname_utf8); 143 | } 144 | allocator_deallocate(allocator, pathname_utf16); 145 | allocator_deallocate(allocator, buffer); 146 | #else 147 | #error Missing implementation 148 | #endif 149 | return STRING_NIL; 150 | } -------------------------------------------------------------------------------- /src/path.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_PATH_H 2 | #define CODIN_PATH_H 3 | #include "array.h" 4 | #include "string.h" 5 | 6 | typedef struct Context Context; 7 | 8 | Bool path_mkdir(String pathname, Context *context); 9 | Bool path_directory_exists(String pathname, Context *context); 10 | Array(String) path_list(String path, Context *context); 11 | String path_cat(String pathname, String filename, Context *context); 12 | String path_canonicalize(String pathname, Context *context); 13 | 14 | #endif // CODIN_PATH_H -------------------------------------------------------------------------------- /src/profiler.c: -------------------------------------------------------------------------------- 1 | #include "profiler.h" 2 | 3 | extern const ProfilerOperations PROFILER_NULL; 4 | extern const ProfilerOperations PROFILER_SPALL; 5 | 6 | void profiler_init(Profiler *profiler, String name, Context *context) { 7 | if (string_compare(name, PROFILER_SPALL.name)) { 8 | profiler->ops = &PROFILER_SPALL; 9 | } else { 10 | profiler->ops = &PROFILER_NULL; 11 | } 12 | profiler->instance = profiler->ops->init(context); 13 | } 14 | 15 | void profiler_fini(Profiler *profiler) { 16 | profiler->ops->fini(profiler->instance); 17 | } 18 | 19 | void profiler_enter(Profiler *profiler, String file, int line, String function) { 20 | profiler->ops->enter(profiler->instance, file, line, function); 21 | } 22 | 23 | void profiler_leave(Profiler *profiler) { 24 | profiler->ops->leave(profiler->instance); 25 | } -------------------------------------------------------------------------------- /src/profiler.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_PROFILER_H 2 | #define CODIN_PROFILER_H 3 | #include "string.h" 4 | 5 | typedef struct Context Context; 6 | 7 | typedef struct Profiler Profiler; 8 | typedef struct ProfilerOperations ProfilerOperations; 9 | 10 | struct ProfilerOperations { 11 | String name; 12 | void *(*init)(Context *); 13 | void (*fini)(void*); 14 | void (*enter)(void *, String file, int line, String function); 15 | void (*leave)(void *); 16 | }; 17 | 18 | struct Profiler { 19 | const ProfilerOperations *ops; 20 | void *instance; 21 | }; 22 | 23 | void profiler_init(Profiler *profiler, String name, Context *context); 24 | void profiler_fini(Profiler *profiler); 25 | void profiler_enter(Profiler *profiler, String, int line, String function); 26 | void profiler_leave(Profiler *profiler); 27 | 28 | #define PROF_ENTER() profiler_enter(&(context)->profiler, SCLIT(__FILE__), __LINE__, SCLIT(__FUNCTION__)) 29 | #define PROF_LEAVE() profiler_leave(&(context)->profiler) 30 | 31 | #endif -------------------------------------------------------------------------------- /src/profiler_null.c: -------------------------------------------------------------------------------- 1 | #include "profiler.h" 2 | 3 | static void *profiler_null_init(Context *context) { 4 | (void)context; 5 | return 0; 6 | } 7 | 8 | static void profiler_null_fini(void *ctx) { 9 | (void)ctx; 10 | } 11 | 12 | static void profiler_null_enter(void *ctx, String file, int line, String function) { 13 | (void)ctx; 14 | (void)file; 15 | (void)line; 16 | (void)function; 17 | } 18 | 19 | static void profiler_null_leave(void *ctx) { 20 | (void)ctx; 21 | } 22 | 23 | const ProfilerOperations PROFILER_NULL = { 24 | SLIT("null"), 25 | profiler_null_init, 26 | profiler_null_fini, 27 | profiler_null_enter, 28 | profiler_null_leave, 29 | }; -------------------------------------------------------------------------------- /src/profiler_spall.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "profiler.h" 4 | #include "allocator.h" 5 | #include "context.h" 6 | #include "utility.h" 7 | #include "thread.h" 8 | 9 | // Disable some warnings in the Spall header. 10 | #if defined(COMPILER_GCC) 11 | #pragma GCC diagnostic push 12 | #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 13 | #pragma GCC diagnostic ignored "-Wsign-compare" 14 | #elif defined(COMPILER_CLANG) 15 | #pragma clang diagnostic push 16 | #pragma clang diagnostic ignored "-Wmissing-field-initializers" 17 | #pragma clang diagnostic ignored "-Wsign-compare" 18 | #endif 19 | #include "vendor/spall.h" 20 | #if defined(COMPILER_GCC) 21 | #pragma GCC diagnostic pop 22 | #elif defined(COMPILER_CLANG) 23 | #pragma clang diagnostic pop 24 | #endif 25 | 26 | #if defined(OS_LINUX) 27 | #include // struct perf_event_attr, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS 28 | #include // mmap, PROT_READ, MAP_SHARED, MAP_FAILED 29 | #include // __rdtsc 30 | #include // clock_gettime, usleep, CLOCK_MONOTONIC_RAW 31 | #include // close, usleep, syscall 32 | 33 | static FORCE_INLINE Uint64 rdtsc(void) { 34 | return __rdtsc(); 35 | } 36 | 37 | static inline Uint64 rdtsc_fallback_freq(void) { 38 | // Estimate TSC frequency with the monotonic clock as a fallback. 39 | struct timespec clock; 40 | clock_gettime(CLOCK_MONOTONIC_RAW, &clock); 41 | Sint64 c_beg = clock.tv_sec * 1e9 + clock.tv_nsec; 42 | Uint64 t_beg = rdtsc(); 43 | usleep(2000); 44 | clock_gettime(CLOCK_MONOTONIC_RAW, &clock); 45 | Sint64 c_end = clock.tv_sec * 1e9 + clock.tv_nsec; 46 | Uint64 t_end = rdtsc(); 47 | return (t_end - t_beg) * 1000000000 / (c_end - c_beg); 48 | } 49 | 50 | static inline Uint64 rdtsc_freq(void) { 51 | struct perf_event_attr pe; 52 | pe.type = PERF_TYPE_HARDWARE; 53 | pe.size = sizeof pe; 54 | pe.config = PERF_COUNT_HW_INSTRUCTIONS; 55 | pe.disabled = 1; 56 | pe.exclude_kernel = 1; 57 | pe.exclude_hv = 1; 58 | 59 | #define PERF_EVENT_OPEN_SYSCALL 298 // x86_64 60 | 61 | int fd = -1; 62 | struct perf_event_mmap_page *page = 0; 63 | Uint64 tsc_freq = 0; 64 | 65 | const int page_size = sysconf(_SC_PAGESIZE); 66 | 67 | fd = syscall(PERF_EVENT_OPEN_SYSCALL, &pe, 0, -1, -1, 0); 68 | if (fd == -1) { 69 | goto L_fallback; 70 | } 71 | 72 | page = RCAST(struct perf_event_mmap_page *, mmap(0, page_size, PROT_READ, MAP_SHARED, fd, 0)); 73 | if (page == MAP_FAILED) { 74 | goto L_fallback; 75 | } 76 | 77 | if (page->cap_user_time != -1) { 78 | // User time not supported. 79 | goto L_fallback; 80 | } 81 | 82 | tsc_freq = 1000000000ull << (page->time_shift / 2); 83 | tsc_freq /= page->time_mult >> (page->time_shift - page->time_shift / 2); 84 | 85 | munmap(page, page_size); 86 | close(fd); 87 | 88 | return tsc_freq; 89 | 90 | L_fallback: 91 | munmap(page, page_size); 92 | close(fd); 93 | 94 | return rdtsc_fallback_freq(); 95 | } 96 | 97 | #elif defined(OS_WINDOWS) 98 | 99 | static FORCE_INLINE Uint64 rdtsc(void) { 100 | return __rdtsc(); 101 | } 102 | 103 | static inline Uint64 rdtsc_fallback_freq(void) { 104 | LARGE_INTEGER freq; 105 | QueryPerformanceFrequency(&freq); 106 | LARGE_INTEGER q_beg; 107 | QueryPerformanceCounter(&q_beg); 108 | Uint64 t_beg = rdtsc(); 109 | Sleep(2); 110 | LARGE_INTEGER q_end; 111 | QueryPerformanceCounter(&q_end); 112 | Uint64 t_end = rdtsc(); 113 | return (t_end - t_beg) * freq.QuadPart / (q_end.QuadPart - q_beg.QuadPart); 114 | } 115 | 116 | static inline Uint64 rdtsc_freq(void) { 117 | HMODULE ntdll = LoadLibraryA("ntdll.dll"); 118 | if (!ntdll) { 119 | goto L_fallback; 120 | } 121 | 122 | int (*NtQuerySystemInformation)(int, void *, unsigned int, unsigned int *) = 0; 123 | *RCAST(void **, &NtQuerySystemInformation) = GetProcAddress(ntdll, "NtQuerySystemInformation"); 124 | if (!NtQuerySystemInformation) { 125 | goto L_fallback; 126 | } 127 | 128 | volatile Uint64 *HSUV = 0; 129 | Uint32 size = 0; 130 | Sint32 result = NtQuerySystemInformation(0xc5, RCAST(void **, &HSUV), sizeof HSUV, &size); 131 | if (size != sizeof HSUV || result < 0) { 132 | goto L_fallback; 133 | } 134 | 135 | FreeLibrary(ntdll); 136 | return (10000000ull << 32) / (HSUV[1] >> 32); 137 | 138 | L_fallback: 139 | FreeLibrary(ntdll); 140 | return rdtsc_fallback_freq(); 141 | } 142 | 143 | #endif 144 | 145 | typedef struct SpallShared SpallShared; 146 | typedef struct SpallThread SpallThread; 147 | 148 | struct THREAD_CAPABILITY("mutex") SpallShared { 149 | SpallProfile ctx; 150 | _Atomic(Bool) lock; 151 | Size count; 152 | }; 153 | 154 | static SpallShared g_spall; 155 | 156 | struct SpallThread { 157 | Allocator *allocator; 158 | SpallBuffer buffer THREAD_GUARDED(g_spall); 159 | _Atomic(Uint32) tid; 160 | }; 161 | 162 | static void spall_shared_lock(SpallShared *shared) 163 | THREAD_ACQUIRES(shared) 164 | THREAD_INTERNAL 165 | { 166 | for (;;) { 167 | if (!atomic_exchange_explicit(&shared->lock, true, memory_order_acquire)) { 168 | return; 169 | } 170 | while (atomic_load_explicit(&shared->lock, memory_order_relaxed)) { 171 | // RELAX 172 | } 173 | } 174 | } 175 | 176 | static void spall_shared_unlock(SpallShared *shared) 177 | THREAD_REQUIRES(shared) 178 | THREAD_RELEASES(shared) 179 | THREAD_INTERNAL 180 | { 181 | atomic_store_explicit(&shared->lock, false, memory_order_release); 182 | } 183 | 184 | static void spall_shared_init(SpallShared *shared) { 185 | spall_shared_lock(shared); 186 | if (shared->count == 0) { 187 | shared->ctx = spall_init_file("profile.spall", 1000000.0 / rdtsc_freq()); 188 | } 189 | shared->count++; 190 | spall_shared_unlock(shared); 191 | } 192 | 193 | static void spall_shared_fini(SpallShared *shared) 194 | THREAD_EXCLUDES(g_spall) 195 | { 196 | spall_shared_lock(shared); 197 | if (shared->count == 1) { 198 | spall_quit(&shared->ctx); 199 | } 200 | shared->count--; 201 | spall_shared_unlock(shared); 202 | } 203 | 204 | static void *profiler_spall_init(Context *context) 205 | THREAD_INTERNAL 206 | { 207 | spall_shared_init(&g_spall); 208 | Allocator *const allocator = &context->allocator; 209 | SpallThread *const thread = allocator_allocate(allocator, sizeof *thread); 210 | const Size capacity = 1024 * 1024 * 1024; // 1 GiB 211 | thread->allocator = allocator; 212 | thread->tid = thread_id(); 213 | thread->buffer.length = capacity; 214 | thread->buffer.data = allocator_allocate(allocator, capacity); 215 | spall_buffer_init(&g_spall.ctx, &thread->buffer); 216 | return thread; 217 | } 218 | 219 | static void profiler_spall_fini(void *ctx) { 220 | SpallThread *const thread = CAST(SpallThread *, ctx); 221 | Allocator *const allocator = thread->allocator; 222 | spall_shared_lock(&g_spall); 223 | spall_buffer_quit(&g_spall.ctx, &thread->buffer); 224 | allocator_deallocate(allocator, thread->buffer.data); 225 | allocator_deallocate(allocator, thread); 226 | spall_shared_unlock(&g_spall); 227 | spall_shared_fini(&g_spall); 228 | } 229 | 230 | static void profiler_spall_enter(void *ctx, String file, int line, String function) { 231 | (void)file; 232 | (void)line; 233 | SpallThread *const thread = CAST(SpallThread *, ctx); 234 | spall_buffer_begin_ex( 235 | &g_spall.ctx, 236 | &thread->buffer, 237 | RCAST(const char *, function.contents), 238 | function.length, 239 | rdtsc(), 240 | thread->tid, 241 | 0); 242 | } 243 | 244 | static void profiler_spall_leave(void *ctx) { 245 | SpallThread *const thread = CAST(SpallThread *, ctx); 246 | spall_buffer_end_ex( 247 | &g_spall.ctx, 248 | &thread->buffer, 249 | rdtsc(), 250 | thread->tid, 251 | 0); 252 | } 253 | 254 | const ProfilerOperations PROFILER_SPALL = { 255 | SLIT("spall"), 256 | profiler_spall_init, 257 | profiler_spall_fini, 258 | profiler_spall_enter, 259 | profiler_spall_leave, 260 | }; -------------------------------------------------------------------------------- /src/project.c: -------------------------------------------------------------------------------- 1 | #include "project.h" 2 | #include "allocator.h" 3 | #include "context.h" 4 | 5 | void project_init(Project *project, String name, Context *context) 6 | THREAD_INTERNAL 7 | { 8 | mutex_init(&project->mutex); 9 | project->context = context; 10 | project->name = name; 11 | project->packages = array_make(context); 12 | } 13 | 14 | void project_fini(Project *project) { 15 | Context *const context = project->context; 16 | 17 | PROF_ENTER(); 18 | 19 | mutex_lock(&project->mutex); 20 | 21 | const Size n_packages = array_size(project->packages); 22 | for (Size i = 0; i < n_packages; i++) { 23 | Package *const package = project->packages[i]; 24 | package_fini(package); 25 | allocator_deallocate(&context->allocator, package); 26 | } 27 | 28 | array_free(project->packages); 29 | 30 | mutex_unlock(&project->mutex); 31 | mutex_fini(&project->mutex); 32 | 33 | PROF_LEAVE(); 34 | } 35 | 36 | Package *project_add_package_internal(Project *project, String pathname, Context *context) { 37 | PROF_ENTER(); 38 | 39 | mutex_lock(&project->mutex); 40 | 41 | const Size n_packages = array_size(project->packages); 42 | for (Size i = 0; i < n_packages; i++) { 43 | Package *const package = project->packages[i]; 44 | if (string_compare(package->pathname, pathname)) { 45 | mutex_unlock(&project->mutex); 46 | PROF_LEAVE(); 47 | return 0; 48 | } 49 | } 50 | 51 | Package *package = allocator_allocate(&context->allocator, sizeof *package); 52 | package_init(package, pathname, context); 53 | 54 | array_push(project->packages, package); 55 | 56 | mutex_unlock(&project->mutex); 57 | 58 | PROF_LEAVE(); 59 | 60 | return package; 61 | } 62 | 63 | Package *project_add_package(Project *project, String pathname) { 64 | return project_add_package_internal(project, pathname, project->context); 65 | } 66 | 67 | void package_init(Package *package, String pathname, Context *context) 68 | THREAD_INTERNAL 69 | { 70 | package->context = context; 71 | mutex_init(&package->mutex); 72 | package->pathname = pathname; 73 | package->trees = array_make(context); 74 | } 75 | 76 | void package_fini(Package *package) { 77 | Context *const context = package->context; 78 | 79 | PROF_ENTER(); 80 | 81 | mutex_lock(&package->mutex); 82 | const Size n_trees = array_size(package->trees); 83 | for (Size i = 0; i < n_trees; i++) { 84 | Tree *const tree = package->trees[i]; 85 | tree_fini(tree); 86 | allocator_deallocate(&context->allocator, tree); 87 | } 88 | array_free(package->trees); 89 | mutex_unlock(&package->mutex); 90 | 91 | mutex_fini(&package->mutex); 92 | 93 | PROF_LEAVE(); 94 | } 95 | 96 | Tree *package_add_tree(Package *package, String filename) { 97 | Context *const context = package->context; 98 | 99 | PROF_ENTER(); 100 | 101 | mutex_lock(&package->mutex); 102 | 103 | Tree *tree = allocator_allocate(&context->allocator, sizeof *tree); 104 | tree->source.name = filename; 105 | 106 | array_push(package->trees, tree); 107 | mutex_unlock(&package->mutex); 108 | 109 | PROF_LEAVE(); 110 | 111 | return tree; 112 | } -------------------------------------------------------------------------------- /src/project.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_PROJECT_H 2 | #define CODIN_PROJECT_H 3 | #include "tree.h" 4 | #include "thread.h" 5 | 6 | typedef struct Project Project; 7 | typedef struct Package Package; 8 | typedef struct Collection Collection; 9 | 10 | struct Collection { 11 | String name; 12 | String path; 13 | }; 14 | 15 | struct Project { 16 | Context *context; 17 | Mutex mutex; 18 | String name THREAD_GUARDED(mutex); 19 | Array(Package*) packages THREAD_GUARDED(mutex); 20 | }; 21 | 22 | void project_init(Project *project, String name, Context *context); 23 | void project_fini(Project *project); 24 | Package *project_add_package(Project *project, String pathname); 25 | 26 | struct Package { 27 | Context *context; 28 | Mutex mutex; 29 | String pathname; 30 | Array(Tree*) trees THREAD_GUARDED(mutex); 31 | }; 32 | 33 | void package_init(Package *package, String pathname, Context *context); 34 | void package_fini(Package *package); 35 | Tree *package_add_tree(Package *package, String filename); 36 | 37 | #endif -------------------------------------------------------------------------------- /src/report.c: -------------------------------------------------------------------------------- 1 | #include // va_list, va_start, va_end 2 | #include // abort 3 | #include // fprintf, fputc 4 | 5 | #include "report.h" 6 | #include "support.h" 7 | #include "string.h" 8 | #include "lexer.h" 9 | #include "strbuf.h" 10 | 11 | NORETURN void report_assertion(const char *expression, const char *file, int line) { 12 | fprintf(stderr, "Assertion failed: %s:%d: %s\n", file, line, expression); 13 | abort(); 14 | } 15 | 16 | void report_error(const Source *source, const Location *location, Context *context, const char *fmt, ...) { 17 | StrBuf buf; 18 | strbuf_init(&buf, context); 19 | strbuf_put_string(&buf, source->name); // %.*s 20 | strbuf_put_rune(&buf, ':'); // : 21 | if (location) { 22 | strbuf_put_int(&buf, location->line, 10); // %d 23 | strbuf_put_int(&buf, location->column, 10); // %d 24 | } 25 | strbuf_put_string(&buf, SCLIT(": ERROR ")); 26 | va_list va; 27 | va_start(va, fmt); 28 | strbuf_put_fmtv(&buf, fmt, va); 29 | strbuf_put_rune(&buf, '\n'); 30 | const String result = strbuf_result(&buf); 31 | fprintf(stderr, "%.*s", SFMT(result)); 32 | strbuf_fini(&buf); 33 | } 34 | 35 | void report_warning(const Source *source, const Location *location, Context *context, const char *fmt, ...) { 36 | StrBuf buf; 37 | strbuf_init(&buf, context); 38 | strbuf_put_string(&buf, source->name); // %.*s 39 | strbuf_put_rune(&buf, ':'); // : 40 | if (location) { 41 | strbuf_put_int(&buf, location->line, 10); // %d 42 | strbuf_put_int(&buf, location->column, 10); // %d 43 | } 44 | strbuf_put_string(&buf, SCLIT(": WARNING ")); 45 | va_list va; 46 | va_start(va, fmt); 47 | strbuf_put_fmtv(&buf, fmt, va); 48 | strbuf_put_rune(&buf, '\n'); 49 | const String result = strbuf_result(&buf); 50 | fprintf(stderr, "%.*s", SFMT(result)); 51 | strbuf_fini(&buf); 52 | } -------------------------------------------------------------------------------- /src/report.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_REPORT_H 2 | #define CODIN_REPORT_H 3 | 4 | typedef struct Source Source; 5 | typedef struct Location Location; 6 | typedef struct Context Context; 7 | 8 | void report_error(const Source *source, const Location *location, Context *context, const char *fmt, ...); 9 | void report_warning(const Source *source, const Location *location, Context *context, const char *fmt, ...); 10 | 11 | #endif // CODIN_REPORT_H -------------------------------------------------------------------------------- /src/sched.c: -------------------------------------------------------------------------------- 1 | #include "sched.h" 2 | 3 | extern const SchedOperations SCHED_SYNC; 4 | extern const SchedOperations SCHED_ASYNC; 5 | extern const SchedOperations SCHED_NULL; 6 | 7 | Bool sched_init(Sched *sched, String name, Context *context) { 8 | const SchedOperations *operations = 0; 9 | if (string_compare(name, SCHED_SYNC.name)) { 10 | operations = &SCHED_SYNC; 11 | } else if (string_compare(name, SCHED_ASYNC.name)) { 12 | operations = &SCHED_ASYNC; 13 | } else { 14 | operations = &SCHED_NULL; 15 | } 16 | sched->operations = operations; 17 | return operations->init(context, &sched->instance); 18 | } 19 | 20 | void sched_fini(Sched *sched) { 21 | sched->operations->fini(sched->instance); 22 | } 23 | 24 | void sched_queue(Sched *sched, void *data, void (*work)(void *data, Context *context), void (*dispose)(void *data, Context *context)) { 25 | sched->operations->queue(sched->instance, data, work, dispose); 26 | } 27 | 28 | void sched_wait(Sched *sched) { 29 | sched->operations->wait(sched->instance); 30 | } -------------------------------------------------------------------------------- /src/sched.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_SCHED_H 2 | #define CODIN_SCHED_H 3 | #include "string.h" 4 | 5 | typedef struct Context Context; 6 | 7 | typedef struct Sched Sched; 8 | typedef struct SchedOperations SchedOperations; 9 | 10 | struct Sched { 11 | const SchedOperations *operations; 12 | void *instance; 13 | }; 14 | 15 | struct SchedOperations { 16 | String name; 17 | Bool (*init)(Context *context, void **instance); 18 | void (*fini)(void *ctx); 19 | Bool (*queue)(void *ctx, void *data, void (*func)(void *data, Context *context), void (*dispose)(void *data, Context *context)); 20 | void (*wait)(void *ctx); 21 | }; 22 | 23 | Bool sched_init(Sched *sched, String name, Context *context); 24 | void sched_fini(Sched *sched); 25 | void sched_queue(Sched *sched, void *data, void (*func)(void *data, Context *context), void (*dispose)(void *data, Context *context)); 26 | void sched_wait(Sched *sched); 27 | 28 | #endif // CODIN_SCHEDULER_H -------------------------------------------------------------------------------- /src/sched_async.c: -------------------------------------------------------------------------------- 1 | #include "sched.h" 2 | #include "context.h" 3 | #include "threadpool.h" 4 | #include "allocator.h" 5 | 6 | typedef struct SchedAsync SchedAsync; 7 | typedef struct SchedAsyncWork SchedAsyncWork; 8 | 9 | struct SchedAsync { 10 | Context *context; 11 | Mutex mutex; 12 | ThreadPool pool THREAD_GUARDED(mutex); 13 | Cond cond THREAD_GUARDED(mutex); 14 | Size count THREAD_GUARDED(mutex); 15 | }; 16 | 17 | struct SchedAsyncWork { 18 | SchedAsync *sched; 19 | void *data; 20 | void (*func)(void *data, Context *context); 21 | void (*dispose)(void *data, Context *context); 22 | }; 23 | 24 | static void _sched_async_work_func(void *data, Context *context) { 25 | PROF_ENTER(); 26 | SchedAsyncWork *const work = RCAST(SchedAsyncWork *, data); 27 | if (!setjmp(context->jmp)) { 28 | work->func(work->data, context); 29 | } 30 | if (work->dispose) { 31 | work->dispose(work->data, context); 32 | } 33 | SchedAsync *const sched = work->sched; 34 | mutex_lock(&sched->mutex); 35 | sched->count--; 36 | cond_signal(&sched->cond); 37 | mutex_unlock(&sched->mutex); 38 | PROF_LEAVE(); 39 | } 40 | 41 | static void _sched_async_work_dispose(void *data, Context *context) { 42 | PROF_ENTER(); 43 | Allocator *const allocator = &context->allocator; 44 | allocator_deallocate(allocator, data); 45 | PROF_LEAVE(); 46 | } 47 | 48 | Bool sched_async_init(Context *context, void **instance) 49 | THREAD_INTERNAL 50 | { 51 | Allocator *const allocator = &context->allocator; 52 | SchedAsync *const sched = allocator_allocate(allocator, sizeof *sched); 53 | if (!sched) { 54 | THROW(ERROR_OOM); 55 | } 56 | if (!threadpool_init(&sched->pool, 16, context)) { 57 | allocator_deallocate(allocator, sched); 58 | return false; 59 | } 60 | sched->context = context; 61 | mutex_init(&sched->mutex); 62 | cond_init(&sched->cond); 63 | sched->count = 0; 64 | *instance = RCAST(void *, sched); 65 | return true; 66 | } 67 | 68 | static void sched_async_fini(void *ctx) { 69 | SchedAsync *const sched = CAST(SchedAsync *, ctx); 70 | Context *const context = sched->context; 71 | 72 | PROF_ENTER(); 73 | 74 | mutex_lock(&sched->mutex); 75 | ASSERT(sched->count == 0); 76 | mutex_unlock(&sched->mutex); 77 | 78 | threadpool_fini(&sched->pool); 79 | mutex_fini(&sched->mutex); 80 | cond_fini(&sched->cond); 81 | allocator_deallocate(&context->allocator, sched); 82 | 83 | PROF_LEAVE(); 84 | } 85 | 86 | static Bool sched_async_queue(void *ctx, void *data, void (*func)(void *data, Context *context), void (*dispose)(void *data, Context *context)) { 87 | SchedAsync *const sched = CAST(SchedAsync *, ctx); 88 | Context *const context = sched->context; 89 | Allocator *const allocator = &context->allocator; 90 | SchedAsyncWork *const work = allocator_allocate(allocator, sizeof *work); 91 | if (!work) { 92 | THROW(ERROR_OOM); 93 | } 94 | 95 | work->sched = sched; 96 | work->data = data; 97 | work->func = func; 98 | work->dispose = dispose; 99 | 100 | mutex_lock(&sched->mutex); 101 | sched->count++; 102 | mutex_unlock(&sched->mutex); 103 | 104 | threadpool_queue( 105 | &sched->pool, 106 | _sched_async_work_func, 107 | work, 108 | _sched_async_work_dispose); 109 | 110 | return true; 111 | } 112 | 113 | static void sched_async_wait(void *ctx) { 114 | SchedAsync *sched = CAST(SchedAsync *, ctx); 115 | mutex_lock(&sched->mutex); 116 | while (sched->count) { 117 | cond_wait(&sched->cond, &sched->mutex); 118 | } 119 | mutex_unlock(&sched->mutex); 120 | } 121 | 122 | const SchedOperations SCHED_ASYNC = { 123 | SLIT("async"), 124 | sched_async_init, 125 | sched_async_fini, 126 | sched_async_queue, 127 | sched_async_wait, 128 | }; 129 | 130 | -------------------------------------------------------------------------------- /src/sched_null.c: -------------------------------------------------------------------------------- 1 | #include "sched.h" 2 | 3 | Bool sched_null_init(Context *context, void **instance) { 4 | (void)context; 5 | (void)instance; 6 | // Does nothing. 7 | return true; 8 | } 9 | 10 | void sched_null_fini(void *ctx) { 11 | (void)ctx; 12 | // Does nothing. 13 | } 14 | 15 | Bool sched_null_queue(void *ctx, void *data, void (*func)(void *data, Context *context), void (*dispose)(void *data, Context *context)) { 16 | (void)ctx; 17 | (void)data; 18 | (void)func; 19 | (void)dispose; 20 | // Does nothing. 21 | return true; 22 | } 23 | 24 | void sched_null_wait(void *ctx) { 25 | (void)ctx; 26 | // Does nothing. 27 | } 28 | 29 | const SchedOperations SCHED_NULL = { 30 | SLIT("null"), 31 | sched_null_init, 32 | sched_null_fini, 33 | sched_null_queue, 34 | sched_null_wait, 35 | }; -------------------------------------------------------------------------------- /src/sched_sync.c: -------------------------------------------------------------------------------- 1 | 2 | #include "sched.h" 3 | #include "array.h" 4 | #include "context.h" 5 | #include "allocator.h" 6 | 7 | typedef struct SchedSync SchedSync; 8 | typedef struct SchedSyncWork SchedSyncWork; 9 | 10 | struct SchedSyncWork { 11 | void *ctx; 12 | void *data; 13 | void (*func)(void *data, Context *context); 14 | void (*dispose)(void *data, Context *context); 15 | }; 16 | 17 | struct SchedSync { 18 | Context *context; 19 | Array(SchedSyncWork) work; 20 | }; 21 | 22 | static Bool sched_sync_init(Context *context, void **instance) { 23 | Allocator *const allocator = &context->allocator; 24 | SchedSync *const sched = allocator_allocate(allocator, sizeof *sched); 25 | if (!sched) { 26 | THROW(ERROR_OOM); 27 | } 28 | sched->context = context; 29 | sched->work = array_make(context); 30 | *instance = RCAST(void *, sched); 31 | return true; 32 | } 33 | 34 | static void sched_sync_fini(void *ctx) { 35 | SchedSync *const sched = CAST(SchedSync *, ctx); 36 | Context *const context = sched->context; 37 | ASSERT(array_size(sched->work) == 0); 38 | array_free(sched->work); 39 | Allocator *allocator = &context->allocator; 40 | allocator_deallocate(allocator, sched); 41 | } 42 | 43 | static Bool sched_sync_queue(void *ctx, void *data, void (*func)(void *data, Context *context), void (*dispose)(void *data, Context *context)) { 44 | SchedSync *sched = CAST(SchedSync *, ctx); 45 | return array_push(sched->work, LIT(SchedSyncWork, ctx, data, func, dispose)); 46 | } 47 | 48 | static void sched_sync_wait(void *ctx) { 49 | SchedSync *const sched = CAST(SchedSync *, ctx); 50 | Context *const context = sched->context; 51 | PROF_ENTER(); 52 | for (volatile Size i = 0; i < array_size(sched->work); i++) { 53 | const SchedSyncWork *work = &sched->work[i]; 54 | if (!setjmp(sched->context->jmp)) { 55 | work->func(work->data, context); 56 | } 57 | } 58 | const Size n_work = array_size(sched->work); 59 | for (Size i = 0; i < n_work; i++) { 60 | const SchedSyncWork *work = &sched->work[i]; 61 | if (work->dispose) { 62 | work->dispose(work->data, context); 63 | } 64 | } 65 | array_clear(sched->work); 66 | PROF_LEAVE(); 67 | } 68 | 69 | const SchedOperations SCHED_SYNC = { 70 | SLIT("sync"), 71 | sched_sync_init, 72 | sched_sync_fini, 73 | sched_sync_queue, 74 | sched_sync_wait, 75 | }; -------------------------------------------------------------------------------- /src/strbuf.c: -------------------------------------------------------------------------------- 1 | #include // memcpy 2 | #include 3 | 4 | #include "strbuf.h" 5 | 6 | void strbuf_init(StrBuf *strbuf, Context *context) { 7 | strbuf->context = context; 8 | strbuf->contents = array_make(context); 9 | } 10 | 11 | void strbuf_fini(StrBuf *strbuf) { 12 | array_free(strbuf->contents); 13 | } 14 | 15 | void strbuf_clear(StrBuf *strbuf) { 16 | array_clear(strbuf->contents); 17 | } 18 | 19 | void strbuf_put_byte(StrBuf *strbuf, Uint8 byte) { 20 | array_push(strbuf->contents, byte); 21 | } 22 | 23 | void strbuf_put_rune(StrBuf *strbuf, Rune ch) { 24 | // Decode Rune into individual UTF-8 bytes. 25 | if (ch <= 0x7f) { 26 | strbuf_put_byte(strbuf, ch); 27 | } else if (ch <= 0x7ff) { 28 | strbuf_put_byte(strbuf, (ch >> 6) & 0x1f); 29 | strbuf_put_byte(strbuf, ch & 0x3f); 30 | } else if (ch <= 0xffff) { 31 | strbuf_put_byte(strbuf, ((ch >> 12) & 0x0f)); 32 | strbuf_put_byte(strbuf, ((ch >> 6) & 0x3f)); 33 | strbuf_put_byte(strbuf, ch & 0x3f); 34 | } else { 35 | strbuf_put_byte(strbuf, ((ch >> 18) & 0x07)); 36 | strbuf_put_byte(strbuf, ((ch >> 12) & 0x3f)); 37 | strbuf_put_byte(strbuf, ((ch >> 6) & 0x3f)); 38 | strbuf_put_byte(strbuf, ch & 0x3f); 39 | } 40 | } 41 | 42 | void strbuf_put_string(StrBuf *strbuf, String string) { 43 | const Size size = array_size(strbuf->contents); 44 | array_expand(strbuf->contents, string.length); 45 | memcpy(&strbuf->contents[size], string.contents, string.length); 46 | } 47 | 48 | void strbuf_put_int(StrBuf *strbuf, Sint32 i, Sint32 base) { 49 | char buffer[sizeof(Sint32) * 8 + 1 + 1]; 50 | static const char DIGITS[37] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 51 | 52 | // Start filling from the end 53 | char* p = &buffer[sizeof buffer - 1]; 54 | *p = '\0'; 55 | 56 | // Work with negative `int` 57 | int an = i < 0 ? i : -i; 58 | do { 59 | *(--p) = DIGITS[-(an % base)]; 60 | an /= base; 61 | } while (an); 62 | 63 | if (i < 0) { 64 | *(--p) = '-'; 65 | } 66 | 67 | const Size used = &buffer[sizeof(buffer)] - p; 68 | const Size index = array_size(strbuf->contents); 69 | 70 | array_expand(strbuf->contents, used); 71 | 72 | memcpy(&strbuf->contents[index], p, used); 73 | } 74 | 75 | void strbuf_put_fmtv(StrBuf *strbuf, const char *fmt, va_list va) { 76 | va_list ap; 77 | va_copy(ap, va); 78 | 79 | const long bytes = vsnprintf(0, 0, fmt, va) + 1; 80 | 81 | const Size offset = array_size(strbuf->contents); 82 | array_expand(strbuf->contents, bytes); 83 | 84 | vsnprintf(RCAST(char *, &strbuf->contents[offset]), bytes, fmt, ap); 85 | va_end(ap); 86 | 87 | array_meta(strbuf->contents)->size--; 88 | } 89 | 90 | void strbuf_put_fmt(StrBuf *strbuf, const char *fmt, ...) { 91 | va_list va; 92 | va_start(va, fmt); 93 | strbuf_put_fmtv(strbuf, fmt, va); 94 | va_end(va); 95 | } 96 | 97 | String strbuf_result(StrBuf *strbuf) { 98 | Array(Uint8) contents = strbuf->contents; 99 | return LIT(String, contents, array_size(contents)); 100 | } -------------------------------------------------------------------------------- /src/strbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_STRBUF_H 2 | #define CODIN_STRBUF_H 3 | #include 4 | 5 | #include "string.h" 6 | #include "array.h" 7 | 8 | typedef struct Context Context; 9 | typedef struct StrBuf StrBuf; 10 | 11 | struct StrBuf { 12 | Context *context; 13 | Array(Uint8) contents; 14 | }; 15 | 16 | void strbuf_init(StrBuf *strbuf, Context *context); 17 | void strbuf_fini(StrBuf *strbuf); 18 | void strbuf_clear(StrBuf *strbuf); 19 | void strbuf_put_rune(StrBuf *strbuf, Rune ch); 20 | void strbuf_put_string(StrBuf *strbuf, String string); 21 | void strbuf_put_int(StrBuf *strbuf, Sint32 i, Sint32 base); 22 | void strbuf_put_fmt(StrBuf *strbuf, const char *fmt, ...); 23 | void strbuf_put_fmtv(StrBuf *strbuf, const char *fmt, va_list va); 24 | String strbuf_result(StrBuf *strbuf); 25 | 26 | #endif // CODIN_STRBUF -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | #include // memcpy, strlen, memcmp 2 | #include // malloc, free 3 | 4 | #include "string.h" 5 | #include "context.h" 6 | #include "allocator.h" 7 | 8 | static void *our_memrrchr(const void *m, int c, size_t n) { 9 | const unsigned char *s = CAST(const unsigned char *, m); 10 | c = CAST(unsigned char, c); 11 | while (n--) if (s[n] == c) return CCAST(unsigned char *, s + n); 12 | return 0; 13 | } 14 | 15 | const String STRING_NIL = { 0, 0 }; 16 | 17 | String string_copy_from_data(const Uint8 *data, Size length, Context *context) { 18 | if (length == 0) { 19 | return STRING_NIL; 20 | } 21 | Allocator *const allocator = &context->allocator; 22 | Uint8 *const storage = allocator_allocate(allocator, length); 23 | if (!storage) { 24 | THROW(ERROR_OOM); 25 | } 26 | memcpy(storage, data, length); 27 | return LIT(String, storage, length); 28 | } 29 | 30 | String string_copy_from_null(const char *string, Context *context) { 31 | if (string == 0) { 32 | return STRING_NIL; 33 | } 34 | const Size length = strlen(string); 35 | return string_copy_from_data(RCAST(const Uint8 *, string), length, context); 36 | } 37 | 38 | String string_from_null(const char *string) { 39 | return LIT(String, RCAST(Uint8*, CCAST(char*, string)), strlen(string)); 40 | } 41 | 42 | String string_copy(String string, Context *context) { 43 | return string_copy_from_data(string.contents, string.length, context); 44 | } 45 | 46 | Bool string_compare(String lhs, String rhs) { 47 | return lhs.length == rhs.length && 48 | memcmp(lhs.contents, rhs.contents, lhs.length) == 0; 49 | } 50 | 51 | String string_unquote(String string, const char *quote_set) { 52 | if (string.length == 0) { 53 | return STRING_NIL; 54 | } 55 | const char *ch = strchr(quote_set, string.contents[0]); 56 | if (ch && string.contents[string.length - 1] == *ch) { 57 | return LIT(String, string.contents + 1, string.length - 2); 58 | } 59 | return string; 60 | } 61 | 62 | void string_free(String string, Context *context) { 63 | Allocator *const allocator = &context->allocator; 64 | allocator_deallocate(allocator, string.contents); 65 | } 66 | 67 | char *string_to_null(String string, Context *context) { 68 | Allocator *const allocator = &context->allocator; 69 | const Size length = string.length; 70 | char *const result = allocator_allocate(allocator, length + 1); 71 | if (!result) { 72 | THROW(ERROR_OOM); 73 | } 74 | if (string.contents) { 75 | memcpy(result, string.contents, length); 76 | } 77 | result[length] = '\0'; 78 | return result; 79 | } 80 | 81 | Bool string_starts_with(String string, String prefix) { 82 | return string.length >= prefix.length && 83 | memcmp(string.contents, prefix.contents, prefix.length) == 0; 84 | } 85 | 86 | Bool string_ends_with(String string, String suffix) { 87 | return string.length >= suffix.length && 88 | memcmp(string.contents + (string.length - suffix.length), suffix.contents, suffix.length) == 0; 89 | } 90 | 91 | Bool string_find_first_byte(String string, Uint8 byte, Size *index) { 92 | const Uint8 *const find = CAST(const Uint8*, memchr(string.contents, byte, string.length)); 93 | if (find) { 94 | *index = find - string.contents; 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | Bool string_find_last_byte(String string, Uint8 byte, Size *index) { 101 | const Uint8 *const find = CAST(const Uint8*, our_memrrchr(string.contents, byte, string.length)); 102 | if (find) { 103 | *index = find - string.contents; 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | String string_slice(String string, Size from, Size len) { 110 | return LIT(String, string.contents + from, len); 111 | } 112 | 113 | static void utf8_to_utf16_core(const char *const source, Uint16 *destination, Size *const length) { 114 | Uint32 code_point = 0; 115 | Size elements = 0; 116 | for (const char *element = source; *element; element++) { 117 | const Uint8 ch = CAST(Uint8, *element); 118 | if (ch <= 0x7f) { 119 | code_point = CAST(Uint16, ch); 120 | } else if (ch <= 0xbf) { 121 | code_point = (code_point << 6) | (ch & 0x3f); 122 | } else if (ch <= 0xdf) { 123 | code_point = ch & 0x1f; 124 | } else if (ch <= 0xef) { 125 | code_point = ch & 0x0f; 126 | } else { 127 | code_point = ch & 0x07; 128 | } 129 | if (((*element & 0xc0) != 0x80) && code_point <= 0x10ffff) { 130 | if (code_point > 0xffff) { 131 | elements += 2; 132 | if (destination) { 133 | *destination++ = CAST(Uint16, 0xd800 + (code_point >> 10)); 134 | *destination++ = CAST(Uint16, 0xdc00 + (code_point & 0x03ff)); 135 | } 136 | } else if (code_point < 0xd800 || code_point >= 0xe000) { 137 | elements += 1; 138 | if (destination) { 139 | *destination++ = CAST(Uint16, code_point); 140 | } 141 | } 142 | } 143 | } 144 | if (length) { 145 | *length = elements; 146 | } 147 | } 148 | 149 | void utf8_to_utf16(const char *source, Uint16 **const destination, Context *context) { 150 | Size length = 0; 151 | utf8_to_utf16_core(source, 0, &length); 152 | 153 | Allocator *allocator = &context->allocator; 154 | Uint16 *dest = allocator_allocate(allocator, (length + 1) * sizeof *dest); 155 | if (!dest) { 156 | THROW(ERROR_OOM); 157 | } 158 | 159 | utf8_to_utf16_core(source, dest, 0); 160 | dest[length] = 0; 161 | *destination = dest; 162 | } 163 | 164 | static void utf16_to_utf8_core(const Uint16 *const source, char *destination, Size *const length) { 165 | Size elements = 0; 166 | Uint32 code_point = 0; 167 | for (const Uint16 *element = source; *element; element++) { 168 | if (*element >= 0xd800 && *element <= 0xdbff) { 169 | code_point = ((*element - 0xd800) << 10) + 0x10000; 170 | } else { 171 | if (*element >= 0xdc00 && *element <= 0xdfff) { 172 | code_point |= *element - 0xdc00; 173 | } else { 174 | code_point = *element; 175 | } 176 | if (code_point < 0x7f) { 177 | elements += 1; 178 | if (destination) { 179 | *destination++ = CAST(char, code_point); 180 | } 181 | } else if (code_point <= 0x7ff) { 182 | elements += 2; 183 | if (destination) { 184 | *destination++ = CAST(char, 0xc0 | ((code_point >> 6) & 0x1f)); 185 | *destination++ = CAST(char, 0x80 | (code_point & 0x3f)); 186 | } 187 | } else if (code_point <= 0xffff) { 188 | elements += 3; 189 | if (destination) { 190 | *destination++ = CAST(char, 0xe0 | ((code_point >> 12) & 0x0f)); 191 | *destination++ = CAST(char, 0x80 | ((code_point >> 6) & 0x3f)); 192 | *destination++ = CAST(char, 0x80 | (code_point & 0x3f)); 193 | } 194 | } else { 195 | elements += 4; 196 | if (destination) { 197 | *destination++ = CAST(char, 0xf0 | ((code_point >> 18) & 0x07)); 198 | *destination++ = CAST(char, 0x80 | ((code_point >> 12) & 0x3f)); 199 | *destination++ = CAST(char, 0x80 | ((code_point >> 6) & 0x3f)); 200 | *destination++ = CAST(char, 0x80 | (code_point & 0x3f)); 201 | } 202 | } 203 | code_point = 0; 204 | } 205 | } 206 | if (length) { 207 | *length = elements; 208 | } 209 | } 210 | 211 | void utf16_to_utf8(const Uint16 *source, char **destination, Context *context) { 212 | Size length = 0; 213 | utf16_to_utf8_core(source, 0, &length); 214 | 215 | Allocator *const allocator = &context->allocator; 216 | char *dest = allocator_allocate(allocator, (length + 1) * sizeof *dest); 217 | if (!dest) { 218 | THROW(ERROR_OOM); 219 | } 220 | 221 | utf16_to_utf8_core(source, dest, 0); 222 | dest[length] = 0; 223 | *destination = dest; 224 | } 225 | 226 | // Copyright (c) 2008-2010 Bjoern Hoehrmann 227 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. 228 | static const Uint8 UTF8_DECODE_LUT[] = { 229 | // The first part of the table maps bytes to character classes that 230 | // to reduce the size of the transition table and create bitmasks. 231 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 232 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 233 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 234 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 235 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 236 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 237 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 238 | 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, 239 | 240 | // The second part is a transition table that maps a combination 241 | // of a state of the automaton and a character class to a state. 242 | 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 243 | 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 244 | 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 245 | 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 246 | 12,36,12,12,12,12,12,12,12,12,12,12, 247 | }; 248 | 249 | Uint32 utf8_decode(Uint32 *state, Rune *codep, Uint32 byte) { 250 | const Uint32 type = UTF8_DECODE_LUT[byte]; 251 | *codep = (*state != UTF8_ACCEPT) ? 252 | (byte & 0x3fu) | (*codep << 6) : 253 | (0xff >> type) & byte; 254 | *state = UTF8_DECODE_LUT[256 + *state + type]; 255 | return *state; 256 | } -------------------------------------------------------------------------------- /src/string.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_STRING_H 2 | #define CODIN_STRING_H 3 | #include "support.h" 4 | 5 | typedef struct Context Context; 6 | typedef struct String String; 7 | 8 | // This string isn't NUL terminated. 9 | struct String { 10 | Uint8* contents; 11 | Size length; 12 | }; 13 | 14 | // Define a literal initializer for a String. This does not produce a compound- 15 | // literal though, use SCLIT for that. The reason for this distinction has to 16 | // do with static initializers, the use of a compound-literal is not allowed in 17 | // a static initializer because a compound-literal isn't considered a constant 18 | // expression, while an aggregate initializer-list of a static storage String is. 19 | // 20 | // Use this for static String or string arrays. 21 | #define SINIT(content) \ 22 | RCAST(Uint8*, CCAST(char*, content)), sizeof(content) - 1 23 | #define SLIT(content) \ 24 | { SINIT(content) } 25 | 26 | // Use this anywhere SLIT won't work, like taking the address of a String or 27 | // passing a String to a function. 28 | #define SCLIT(content) \ 29 | LIT(String, SINIT(content)) 30 | 31 | #define SFMT(string) \ 32 | CAST(Sint32, string.length), RCAST(const char *, string.contents) 33 | 34 | extern const String STRING_NIL; 35 | 36 | String string_copy_from_data(const Uint8 *data, Size size, Context *context); 37 | String string_copy_from_null(const char *string, Context *context); 38 | 39 | String string_from_null(const char *string); 40 | 41 | String string_copy(String string, Context *context); 42 | Bool string_compare(String lhs, String rhs); 43 | String string_unquote(String string, const char *quote_set); 44 | void string_free(String string, Context *context); 45 | char* string_to_null(String string, Context *context); 46 | Bool string_starts_with(String string, String prefix); 47 | Bool string_ends_with(String string, String suffix); 48 | 49 | Bool string_find_first_byte(String String, Uint8 byte, Size *index); 50 | Bool string_find_last_byte(String string, Uint8 byte, Size *index); 51 | 52 | String string_slice(String string, Size from, Size len); 53 | 54 | void utf8_to_utf16(const char *source, Uint16 **const destination, Context *context); 55 | void utf16_to_utf8(const Uint16 *source, char **const destination, Context *context); 56 | 57 | #define UTF8_ACCEPT 0 58 | #define UTF8_REJECT 12 59 | 60 | Uint32 utf8_decode(Uint32 *state, Rune *codep, Uint32 byte); 61 | 62 | #endif // CODIN_STRING_H -------------------------------------------------------------------------------- /src/support.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_SUPPORT_H 2 | #define CODIN_SUPPORT_H 3 | #include 4 | #include 5 | 6 | // OS_{} 7 | #if defined(_WIN32) 8 | #define OS_WINDOWS 9 | #elif defined(__linux__) 10 | #define OS_POSIX 11 | #define OS_LINUX 12 | #else 13 | #error Unsupported platform 14 | #endif 15 | 16 | // COMPILER_{} 17 | #if defined(__clang__) 18 | #define COMPILER_CLANG 19 | #elif defined(__GNUC__) || defined(__GNUG__) 20 | #define COMPILER_GCC 21 | #elif defined(_MSC_VER) 22 | #define COMPILER_MSVC 23 | #else 24 | #error Unsupported compiler 25 | #endif 26 | 27 | // ISA_{} 28 | #if defined(__x86_64__) || defined(_M_X64) 29 | #define ISA_AMD64 30 | #elif defined(__aarch64__) 31 | #define ISA_AARCH64 32 | #else 33 | #error Unsupported architecture 34 | #endif 35 | 36 | // FORCE_INLINE 37 | #if defined(COMPILER_MSVC) 38 | #define FORCE_INLINE __forceinline 39 | #else 40 | #define FORCE_INLINE __attribute__((always_inline)) inline 41 | #endif 42 | 43 | // UNREACHABLE() 44 | #if defined(COMPILER_MSVC) 45 | #define UNREACHABLE() __assume(0) 46 | #else 47 | #define UNREACHABLE() __builtin_unreachable() 48 | #endif 49 | 50 | // FALLTHROUGH() 51 | #if defined(COMPILER_MSVC) 52 | #define FALLTHROUGH() 53 | #elif defined(__cplusplus) 54 | #define FALLTHROUGH() [[fallthrough]] 55 | #else 56 | #define FALLTHROUGH() __attribute__((__fallthrough__)) 57 | #endif 58 | 59 | // NORETURN 60 | #if defined(COMPILER_MSVC) 61 | #define NORETURN __declspec(noreturn) 62 | #elif defined(__cplusplus) 63 | #define NORETURN [[noreturn]] 64 | #else 65 | #define NORETURN __attribute__((__noreturn__)) 66 | #endif 67 | 68 | // ALIGN(n) 69 | #if defined(COMPILER_MSVC) 70 | #define ALIGN(n) __declspec(align(n)) 71 | #elif defined(__cplusplus) 72 | #define ALIGN(n) alignas(n) 73 | #else 74 | #define ALIGN(n) __attribute__((__aligned__(n))) 75 | #endif 76 | 77 | // LIT 78 | #if defined(__cplusplus) 79 | #define LIT(T, ...) (T { __VA_ARGS__ }) 80 | #else 81 | #define LIT(T, ...) ((T) { __VA_ARGS__ }) 82 | #endif 83 | 84 | // STATIC_ASSERT(expr, message) 85 | #if defined(__cplusplus) 86 | #define STATIC_ASSERT(expr, message) \ 87 | static_assert(expr, message) 88 | #else 89 | #if __STDC_VERSION__ >= 201112L 90 | #define STATIC_ASSERT(expr, message) \ 91 | _Static_assert(expr, message) 92 | #else 93 | #define STATIC_ASSERT(expr, ...) \ 94 | typedef int static_assert_ ## __LINE__ [(expr) ? 1 : -1] 95 | #endif 96 | #endif 97 | 98 | // CAST(T, expr) 99 | // RCAST(T, expr) 100 | // CCAST(T, expr) 101 | #if defined(__cplusplus) 102 | #define CAST(T, expr) static_cast(expr) 103 | #define RCAST(T, expr) reinterpret_cast(expr) 104 | #define CCAST(T, expr) const_cast(expr) 105 | #else 106 | #define CAST(T, expr) ((T)(expr)) 107 | #define RCAST(T, expr) ((T)(expr)) 108 | #define CCAST(T, expr) ((T)(expr)) 109 | #endif 110 | 111 | // Integer types 112 | typedef int8_t Sint8; 113 | typedef uint8_t Uint8; 114 | typedef int16_t Sint16; 115 | typedef uint16_t Uint16; 116 | typedef int32_t Sint32; 117 | typedef uint32_t Uint32; 118 | typedef int64_t Sint64; 119 | typedef uint64_t Uint64; 120 | 121 | typedef Uint16 Float16; 122 | typedef float Float32; 123 | typedef double Float64; 124 | 125 | typedef size_t Size; 126 | 127 | // Bool 128 | #if defined(__cplusplus) 129 | typedef bool Bool; 130 | #else 131 | typedef _Bool Bool; 132 | #define true CAST(Bool, 1) 133 | #define false CAST(Bool, 0) 134 | #endif 135 | 136 | // Rune 137 | typedef int32_t Rune; // Unicode codepoint. 138 | #define RUNE_MAX CAST(Rune, 0x0010ffff) 139 | #define RUNE_BOM CAST(Rune, 0xfeff) 140 | #define RUNE_EOF CAST(Rune, -1) 141 | 142 | // ASSERT 143 | NORETURN void report_assertion(const char *expression, const char *file, int line); 144 | #define ASSERT(expression) \ 145 | CAST(void, (expression) ? CAST(void, 0) : report_assertion(#expression, __FILE__, __LINE__)) 146 | 147 | // Ptr 148 | #if defined(__cplusplus) 149 | struct Ptr { 150 | constexpr Ptr(void *p = 0) noexcept : p{p} {} 151 | template operator T*() const noexcept { return CAST(T*, p); }; 152 | constexpr operator bool() const noexcept { return p; } 153 | void *p = 0; 154 | }; 155 | #define PTR_TO_BITS(ptr) RCAST(Uint64, (ptr).p) 156 | #define PTR_FROM_BITS(bits) Ptr(RCAST(void *, (bits))) 157 | #else 158 | typedef void *Ptr; 159 | #define PTR_TO_BITS(ptr) RCAST(Uint64, ptr) 160 | #define PTR_FROM_BITS(bits) RCAST(Ptr, (bits)) 161 | #endif 162 | 163 | // Support bitwise operators on enumerators in C++ like C. 164 | #if defined(__cplusplus) 165 | template 166 | constexpr T operator~ (T a) noexcept { 167 | return CAST(T, ~CAST(int, a)); 168 | } 169 | template 170 | constexpr T operator| (T a, T b) noexcept { 171 | return CAST(T, CAST(int, a) | CAST(int, b)); 172 | } 173 | template 174 | constexpr T operator& (T a, T b) noexcept { 175 | return CAST(T, CAST(int, a) & CAST(int, b)); 176 | } 177 | template 178 | constexpr T operator^ (T a, T b) noexcept { 179 | return CAST(T, CAST(int, a) ^ CAST(int, b)); 180 | } 181 | template 182 | constexpr T& operator|= (T& a, T b) noexcept { 183 | return RCAST(T&, RCAST(int&, a) |= CAST(int, b)); 184 | } 185 | template 186 | constexpr T& operator&= (T& a, T b) noexcept { 187 | return RCAST(T&, RCAST(int&, a) &= CAST(int, b)); 188 | } 189 | template 190 | constexpr T& operator^= (T& a, T b) noexcept { 191 | return RCAST(T&, RCAST(int&, a) ^= CAST(int, b)); 192 | } 193 | #endif 194 | 195 | // Tagged pointer helpers assuming 16-byte aligned pointers 196 | static FORCE_INLINE Ptr tagptr(Ptr ptr, Uint8 tag) { 197 | return PTR_FROM_BITS(PTR_TO_BITS(ptr) | tag); 198 | } 199 | 200 | static FORCE_INLINE Uint8 tagptr_tag(Ptr ptr) { 201 | return PTR_TO_BITS(ptr) & 15; 202 | } 203 | 204 | static FORCE_INLINE Ptr tagptr_ptr(Ptr ptr) { 205 | return PTR_FROM_BITS(PTR_TO_BITS(ptr) & ~15); 206 | } 207 | 208 | #if defined(__clang__) && defined(__cplusplus) 209 | #define THREAD_ATTRIBUTE(x) __attribute__((x)) 210 | #else 211 | #define THREAD_ATTRIBUTE(x) 212 | #endif 213 | 214 | #define THREAD_GUARDED(...) THREAD_ATTRIBUTE(guarded_by(__VA_ARGS__)) 215 | #define THREAD_CAPABILITY(...) THREAD_ATTRIBUTE(capability(__VA_ARGS__)) 216 | #define THREAD_ACQUIRES(...) THREAD_ATTRIBUTE(acquire_capability(__VA_ARGS__)) 217 | #define THREAD_RELEASES(...) THREAD_ATTRIBUTE(release_capability(__VA_ARGS__)) 218 | #define THREAD_TRY_ACQUIRE(...) THREAD_ATTRIBUTE(try_acquire_capability(__VA_ARGS__)) 219 | #define THREAD_EXCLUDES(...) THREAD_ATTRIBUTE(locks_excluded(__VA_ARGS__)) 220 | #define THREAD_REQUIRES(...) THREAD_ATTRIBUTE(requires_capability(__VA_ARGS__)) 221 | #define THREAD_INTERNAL THREAD_ATTRIBUTE(no_thread_safety_analysis) 222 | 223 | #endif // CODIN_SUPPORT_H -------------------------------------------------------------------------------- /src/thread.c: -------------------------------------------------------------------------------- 1 | #include // abort 2 | 3 | #include "thread.h" 4 | #include "context.h" 5 | #include "allocator.h" 6 | 7 | #if defined(OS_POSIX) 8 | #include 9 | #include 10 | #elif defined(OS_WINDOWS) 11 | #define WIN32_LEAN_AND_MEAN 12 | #include 13 | #include 14 | #else 15 | #error Unknown OS 16 | #endif 17 | 18 | typedef struct State State; 19 | 20 | struct State { 21 | Allocator *allocator; 22 | int (*proc)(void *data); 23 | void *data; 24 | }; 25 | 26 | #if defined(OS_POSIX) 27 | static void* thread_proc(void *data) 28 | #elif defined(OS_WINDOWS) 29 | static unsigned thread_proc(void *data) 30 | #endif 31 | { 32 | State *state = CAST(State*, data); 33 | Allocator *allocator = state->allocator; 34 | #if defined(OS_POSIX) 35 | // Block all signal delivery to this thread. 36 | sigset_t mask; 37 | sigfillset(&mask); 38 | pthread_sigmask(SIG_BLOCK, &mask, 0); 39 | #endif 40 | state->proc(state->data); 41 | allocator_deallocate(allocator, state); 42 | return 0; 43 | } 44 | 45 | void thread_create(Thread *thread, int (*proc)(void*), void *data, Context *context) { 46 | Allocator *allocator = &context->allocator; 47 | State *state = allocator_allocate(allocator, sizeof *state); 48 | state->allocator = allocator; 49 | state->proc = proc; 50 | state->data = data; 51 | #if defined(OS_POSIX) 52 | pthread_t *handle = RCAST(pthread_t*, thread->storage); 53 | if (pthread_create(handle, 0, thread_proc, state) != 0) { 54 | THROW(ERROR_UNKNOWN); 55 | } 56 | #elif defined(OS_WINDOWS) 57 | uintptr_t tid = _beginthreadex( 58 | 0, 59 | 0, 60 | thread_proc, 61 | state, 62 | 0, 63 | 0); 64 | if (tid == 0) { 65 | THROW(ERROR_UNKNOWN); 66 | } 67 | HANDLE handle = RCAST(HANDLE, tid); 68 | *RCAST(HANDLE*, thread->storage) = handle; 69 | #endif 70 | } 71 | 72 | Bool thread_join(Thread *thread, Context *context) { 73 | PROF_ENTER(); 74 | #if defined(OS_POSIX) 75 | pthread_t *handle = RCAST(pthread_t*, thread->storage); 76 | if (pthread_join(*handle, 0) != 0) { 77 | PROF_LEAVE(); 78 | return false; 79 | } 80 | #elif defined(OS_WINDOWS) 81 | HANDLE handle = *RCAST(HANDLE*, thread->storage); 82 | if (WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0) { 83 | PROF_LEAVE(); 84 | return false; 85 | } 86 | CloseHandle(handle); 87 | #endif 88 | PROF_LEAVE(); 89 | return true; 90 | } 91 | 92 | Uint32 thread_id(void) { 93 | #if defined(OS_POSIX) 94 | return CAST(Uint32, pthread_self()); 95 | #elif defined(OS_WINDOWS) 96 | return GetCurrentThreadId(); 97 | #endif 98 | return 0; 99 | } 100 | 101 | void mutex_init(Mutex *mutex) { 102 | #if defined(OS_POSIX) 103 | pthread_mutex_t *handle = RCAST(pthread_mutex_t *, mutex->storage); 104 | pthread_mutexattr_t attributes; 105 | if (pthread_mutexattr_init(&attributes) != 0 106 | || pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_NORMAL) != 0 107 | || pthread_mutex_init(handle, &attributes) != 0 108 | || pthread_mutexattr_destroy(&attributes) != 0) 109 | { 110 | abort(); 111 | } 112 | #elif defined(OS_WINDOWS) 113 | SRWLOCK *handle = RCAST(SRWLOCK *, mutex->storage); 114 | InitializeSRWLock(handle); // just zeros the memory 115 | #endif 116 | } 117 | 118 | void mutex_fini(Mutex *mutex) { 119 | #if defined(OS_POSIX) 120 | pthread_mutex_t *handle = RCAST(pthread_mutex_t *, mutex->storage); 121 | if (pthread_mutex_destroy(handle) != 0) { 122 | abort(); 123 | } 124 | #elif defined(OS_WINDOWS) 125 | #endif 126 | } 127 | 128 | void mutex_lock(Mutex *mutex) 129 | THREAD_INTERNAL 130 | { 131 | #if defined(OS_POSIX) 132 | pthread_mutex_t *handle = RCAST(pthread_mutex_t *, mutex->storage); 133 | if (pthread_mutex_lock(handle) != 0) { 134 | abort(); 135 | } 136 | #elif defined(OS_WINDOWS) 137 | SRWLOCK *handle = RCAST(SRWLOCK *, mutex->storage); 138 | AcquireSRWLockExclusive(handle); 139 | #endif 140 | } 141 | 142 | void mutex_unlock(Mutex *mutex) 143 | THREAD_INTERNAL 144 | { 145 | #if defined(OS_POSIX) 146 | pthread_mutex_t *handle = RCAST(pthread_mutex_t *, mutex->storage); 147 | if (pthread_mutex_unlock(handle) != 0) { 148 | abort(); 149 | } 150 | #elif defined(OS_WINDOWS) 151 | SRWLOCK *handle = RCAST(SRWLOCK *, mutex->storage); 152 | ReleaseSRWLockExclusive(handle); 153 | #endif 154 | } 155 | 156 | void cond_init(Cond *cond) { 157 | #if defined(OS_POSIX) 158 | pthread_cond_t *handle = RCAST(pthread_cond_t *, cond->storage); 159 | if (pthread_cond_init(handle, 0) != 0) { 160 | abort(); 161 | } 162 | #elif defined(OS_WINDOWS) 163 | CONDITION_VARIABLE *handle = RCAST(CONDITION_VARIABLE*, cond->storage); 164 | InitializeConditionVariable(handle); 165 | #endif 166 | } 167 | 168 | void cond_fini(Cond *cond) { 169 | #if defined(OS_POSIX) 170 | pthread_cond_t *handle = RCAST(pthread_cond_t *, cond->storage); 171 | if (pthread_cond_destroy(handle) != 0) { 172 | abort(); 173 | } 174 | #elif defined(OS_WINDOWS) 175 | (void)cond; 176 | // There is no destruction for CONDITION_VARIABLE on Windows. 177 | #endif 178 | } 179 | 180 | void cond_wait(Cond *cond, Mutex *mutex) { 181 | #if defined(OS_POSIX) 182 | pthread_cond_t *cond_handle = RCAST(pthread_cond_t *, cond->storage); 183 | pthread_mutex_t *mutex_handle = RCAST(pthread_mutex_t *, mutex->storage); 184 | if (pthread_cond_wait(cond_handle, mutex_handle) != 0) { 185 | abort(); 186 | } 187 | #elif defined(OS_WINDOWS) 188 | CONDITION_VARIABLE *cond_handle = RCAST(CONDITION_VARIABLE*, cond->storage); 189 | SRWLOCK *mutex_handle = RCAST(SRWLOCK*, mutex->storage); 190 | if (!SleepConditionVariableSRW(cond_handle, mutex_handle, INFINITE, 0)) { 191 | abort(); 192 | } 193 | #endif 194 | } 195 | 196 | void cond_signal(Cond *cond) { 197 | #if defined(OS_POSIX) 198 | pthread_cond_t *handle = RCAST(pthread_cond_t*, cond->storage); 199 | if (pthread_cond_signal(handle) != 0) { 200 | abort(); 201 | } 202 | #elif defined(OS_WINDOWS) 203 | CONDITION_VARIABLE *handle = RCAST(CONDITION_VARIABLE*, cond->storage); 204 | WakeConditionVariable(handle); 205 | #endif 206 | } 207 | 208 | void cond_broadcast(Cond *cond) { 209 | #if defined(OS_POSIX) 210 | pthread_cond_t *handle = RCAST(pthread_cond_t*, cond->storage); 211 | if (pthread_cond_broadcast(handle) != 0) { 212 | abort(); 213 | } 214 | #elif defined(OS_WINDOWS) 215 | CONDITION_VARIABLE *handle = RCAST(CONDITION_VARIABLE*, cond->storage); 216 | WakeAllConditionVariable(handle); 217 | #endif 218 | } 219 | 220 | void waitgroup_init(WaitGroup *wg, Size count) 221 | THREAD_INTERNAL 222 | { 223 | mutex_init(&wg->mutex); 224 | cond_init(&wg->cond); 225 | wg->count = count; 226 | } 227 | 228 | void waitgroup_fini(WaitGroup *wg) { 229 | cond_fini(&wg->cond); 230 | mutex_fini(&wg->mutex); 231 | } 232 | 233 | void waitgroup_signal(WaitGroup *wg) { 234 | mutex_lock(&wg->mutex); 235 | wg->count--; 236 | cond_signal(&wg->cond); 237 | mutex_unlock(&wg->mutex); 238 | } 239 | 240 | void waitgroup_wait(WaitGroup *wg) { 241 | mutex_lock(&wg->mutex); 242 | while (wg->count) { 243 | cond_wait(&wg->cond, &wg->mutex); 244 | } 245 | mutex_unlock(&wg->mutex); 246 | } -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_THREAD_H 2 | #define CODIN_THREAD_H 3 | #include "support.h" 4 | 5 | typedef struct Context Context; 6 | 7 | typedef struct Thread Thread; 8 | typedef struct Mutex Mutex; 9 | typedef struct Cond Cond; 10 | typedef struct WaitGroup WaitGroup; 11 | 12 | struct ALIGN(16) Thread { 13 | Uint8 storage[64]; 14 | }; 15 | 16 | struct ALIGN(16) Cond { 17 | Uint8 storage[64]; 18 | }; 19 | 20 | struct ALIGN(16) THREAD_CAPABILITY("mutex") Mutex { 21 | Uint8 storage[128]; 22 | }; 23 | 24 | struct WaitGroup { 25 | Mutex mutex; 26 | Cond cond THREAD_GUARDED(mutex); 27 | Size count THREAD_GUARDED(mutex); 28 | }; 29 | 30 | 31 | void thread_create(Thread *thread, int (*proc)(void*), void *data, Context *context); 32 | Bool thread_join(Thread *thread, Context *context); 33 | Uint32 thread_id(void); 34 | 35 | // Mutex 36 | void mutex_init(Mutex *mutex); 37 | void mutex_fini(Mutex *mutex) 38 | THREAD_EXCLUDES(*mutex); 39 | void mutex_lock(Mutex *mutex) 40 | THREAD_ACQUIRES(*mutex); 41 | void mutex_unlock(Mutex *mutex) 42 | THREAD_REQUIRES(*mutex) 43 | THREAD_RELEASES(*mutex); 44 | 45 | // Cond 46 | void cond_init(Cond *cond); 47 | void cond_fini(Cond *cond); 48 | void cond_wait(Cond *cond, Mutex *mutex) THREAD_REQUIRES(*mutex); 49 | void cond_signal(Cond *cond); 50 | void cond_broadcast(Cond *cond); 51 | 52 | // WaitGroup 53 | void waitgroup_init(WaitGroup *wg, Size count); 54 | void waitgroup_fini(WaitGroup *wg); 55 | void waitgroup_signal(WaitGroup *wg); 56 | void waitgroup_wait(WaitGroup *wg); 57 | 58 | #endif // CODIN_THREAD_H -------------------------------------------------------------------------------- /src/threadpool.c: -------------------------------------------------------------------------------- 1 | #include "threadpool.h" 2 | #include "context.h" 3 | 4 | struct ThreadPoolWork { 5 | void (*function)(void *user, Context *context); 6 | void *user; 7 | void (*dispose)(void *user, Context *context); 8 | }; 9 | 10 | static int threadpool_worker(void *user) { 11 | ThreadPool *pool = CAST(ThreadPool*, user); 12 | 13 | const String allocator = pool->context->allocator.ops->name; 14 | const String profiler = pool->context->profiler.ops->name; 15 | 16 | Context ctx; 17 | Context *context = &ctx; 18 | context_init(context, allocator, profiler); 19 | 20 | PROF_ENTER(); 21 | 22 | for (;;) { 23 | mutex_lock(&pool->mutex); 24 | while (!pool->quit && array_size(pool->work) == 0) { 25 | cond_wait(&pool->cond, &pool->mutex); 26 | } 27 | 28 | if (pool->quit && array_size(pool->work) == 0) { 29 | mutex_unlock(&pool->mutex); 30 | break; 31 | } 32 | 33 | Array *meta = array_meta(pool->work); 34 | ThreadPoolWork work = pool->work[--meta->size]; 35 | mutex_unlock(&pool->mutex); 36 | 37 | work.function(work.user, context); 38 | 39 | if (work.dispose) { 40 | work.dispose(work.user, context); 41 | } 42 | } 43 | 44 | PROF_LEAVE(); 45 | 46 | context_fini(context); 47 | 48 | return 0; 49 | } 50 | 51 | Bool threadpool_init(ThreadPool *pool, Size n_threads, Context *context) 52 | THREAD_INTERNAL 53 | { 54 | cond_init(&pool->cond); 55 | mutex_init(&pool->mutex); 56 | 57 | pool->context = context; 58 | pool->threads = array_make(context); 59 | pool->work = array_make(context); 60 | pool->quit = false; 61 | 62 | array_resize(pool->threads, n_threads); 63 | for (Size i = 0; i < n_threads; i++) { 64 | thread_create(&pool->threads[i], threadpool_worker, pool, context); 65 | } 66 | return true; 67 | } 68 | 69 | void threadpool_fini(ThreadPool *pool) { 70 | Context *const context = pool->context; 71 | 72 | PROF_ENTER(); 73 | 74 | mutex_lock(&pool->mutex); 75 | pool->quit = true; 76 | cond_broadcast(&pool->cond); 77 | mutex_unlock(&pool->mutex); 78 | 79 | const Size n_threads = array_size(pool->threads); 80 | for (Size i = 0; i < n_threads; i++) { 81 | thread_join(&pool->threads[i], context); 82 | } 83 | 84 | array_free(pool->threads); 85 | 86 | cond_fini(&pool->cond); 87 | mutex_fini(&pool->mutex); 88 | 89 | PROF_LEAVE(); 90 | } 91 | 92 | void threadpool_queue(ThreadPool *pool, void (*function)(void*, Context*), void *user, void (*dispose)(void*, Context*)) { 93 | mutex_lock(&pool->mutex); 94 | const ThreadPoolWork work = LIT(ThreadPoolWork, function, user, dispose); 95 | array_push(pool->work, work); 96 | cond_signal(&pool->cond); 97 | mutex_unlock(&pool->mutex); 98 | } -------------------------------------------------------------------------------- /src/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_THREADPOOL_H 2 | #define CODIN_THREADPOOL_H 3 | 4 | #include "thread.h" 5 | #include "array.h" 6 | 7 | typedef struct Context Context; 8 | typedef struct ThreadPool ThreadPool; 9 | typedef struct ThreadPoolWork ThreadPoolWork; 10 | 11 | struct ThreadPool { 12 | Context *context; 13 | Array(Thread) threads; 14 | Mutex mutex; 15 | Array(ThreadPoolWork) work THREAD_GUARDED(mutex); 16 | Cond cond THREAD_GUARDED(mutex); 17 | Bool quit THREAD_GUARDED(mutex); 18 | }; 19 | 20 | Bool threadpool_init(ThreadPool *pool, Size n_threads, Context *context); 21 | void threadpool_fini(ThreadPool *pool); 22 | void threadpool_queue(ThreadPool *pool, void (*function)(void*, Context *context), void *user, void (*dispose)(void*, Context *context)); 23 | 24 | #endif // CODIN_THREADPOOL_H -------------------------------------------------------------------------------- /src/tree.c: -------------------------------------------------------------------------------- 1 | #include "tree.h" 2 | #include "context.h" 3 | #include "allocator.h" 4 | #include "path.h" 5 | 6 | TupleExpression *tree_new_tuple_expression(Tree *tree, Array(Expression*) expressions) { 7 | Allocator *const allocator = &tree->context->allocator; 8 | TupleExpression *const expression = allocator_allocate(allocator, sizeof *expression); 9 | expression->base.kind = EXPRESSION_TUPLE; 10 | expression->expressions = expressions; 11 | return expression; 12 | } 13 | 14 | UnaryExpression *tree_new_unary_expression(Tree *tree, OperatorKind operation, Expression *operand) { 15 | Allocator *const allocator = &tree->context->allocator; 16 | UnaryExpression *const expression = allocator_allocate(allocator, sizeof *expression); 17 | expression->base.kind = EXPRESSION_UNARY; 18 | expression->operation = operation; 19 | expression->operand = operand; 20 | return expression; 21 | } 22 | 23 | BinaryExpression *tree_new_binary_expression(Tree *tree, OperatorKind operation, Expression *lhs, Expression *rhs) { 24 | Allocator *const allocator = &tree->context->allocator; 25 | BinaryExpression *const expression = allocator_allocate(allocator, sizeof *expression); 26 | expression->base.kind = EXPRESSION_BINARY; 27 | expression->operation = operation; 28 | expression->lhs = lhs; 29 | expression->rhs = rhs; 30 | return expression; 31 | } 32 | 33 | TernaryExpression *tree_new_ternary_expression(Tree *tree, Expression *on_true, KeywordKind operation, Expression *cond, Expression *on_false) { 34 | Allocator *const allocator = &tree->context->allocator; 35 | TernaryExpression *const expression = allocator_allocate(allocator, sizeof *expression); 36 | expression->base.kind = EXPRESSION_TERNARY; 37 | expression->on_true = on_true; 38 | expression->operation = operation; 39 | expression->cond = cond; 40 | expression->on_false = on_false; 41 | return expression; 42 | } 43 | 44 | CastExpression *tree_new_cast_expression(Tree *tree, OperatorKind kind, Type *type, Expression *expression) { 45 | Allocator *const allocator = &tree->context->allocator; 46 | CastExpression *const expr = allocator_allocate(allocator, sizeof *expr); 47 | expr->base.kind = EXPRESSION_CAST; 48 | expr->kind = kind; 49 | expr->type = type; 50 | expr->expression = expression; 51 | return expr; 52 | } 53 | 54 | SelectorExpression *tree_new_selector_expression(Tree *tree, Expression *operand, Identifier *identifier) { 55 | Allocator *const allocator = &tree->context->allocator; 56 | SelectorExpression *const expression = allocator_allocate(allocator, sizeof *expression); 57 | expression->base.kind = EXPRESSION_SELECTOR; 58 | expression->operand = operand; 59 | expression->identifier = identifier; 60 | return expression; 61 | } 62 | 63 | CallExpression *tree_new_call_expression(Tree *tree, Expression *operand, Array(Field*) arguments) { 64 | Allocator *const allocator = &tree->context->allocator; 65 | CallExpression *const expression = allocator_allocate(allocator, sizeof *expression); 66 | expression->base.kind = EXPRESSION_CALL; 67 | expression->operand = operand; 68 | expression->arguments = arguments; 69 | return expression; 70 | } 71 | 72 | AssertionExpression *tree_new_assertion_expression(Tree *tree, Expression *operand, Type *type) { 73 | Allocator *const allocator = &tree->context->allocator; 74 | AssertionExpression *const expression = allocator_allocate(allocator, sizeof *expression); 75 | expression->base.kind = EXPRESSION_ASSERTION; 76 | expression->operand = operand; 77 | expression->type = type; 78 | return expression; 79 | } 80 | 81 | ProcedureExpression *tree_new_procedure_expression(Tree *tree, ProcedureType *type, TupleExpression *where_clauses,BlockStatement *body) { 82 | Allocator *const allocator = &tree->context->allocator; 83 | ProcedureExpression *const expression = allocator_allocate(allocator, sizeof *expression); 84 | expression->base.kind = EXPRESSION_PROCEDURE; 85 | expression->type = type; 86 | expression->where_clauses = where_clauses; 87 | expression->body = body; 88 | return expression; 89 | } 90 | 91 | TypeExpression *tree_new_type_expression(Tree *tree, Type *type) { 92 | Allocator *const allocator = &tree->context->allocator; 93 | TypeExpression *const expression = allocator_allocate(allocator, sizeof *expression); 94 | expression->base.kind = EXPRESSION_TYPE; 95 | expression->type = type; 96 | return expression; 97 | } 98 | 99 | IndexExpression *tree_new_index_expression(Tree *tree, Expression *operand, Expression *lhs, Expression *rhs) { 100 | Allocator *const allocator = &tree->context->allocator; 101 | IndexExpression *const expression = allocator_allocate(allocator, sizeof *expression); 102 | expression->base.kind = EXPRESSION_INDEX; 103 | expression->operand = operand; 104 | expression->lhs = lhs; 105 | expression->rhs = rhs; 106 | return expression; 107 | } 108 | 109 | SliceExpression *tree_new_slice_expression(Tree *tree, Expression *operand, Expression *lhs, Expression *rhs) { 110 | Allocator *const allocator = &tree->context->allocator; 111 | SliceExpression *const expression = allocator_allocate(allocator, sizeof *expression); 112 | expression->base.kind = EXPRESSION_SLICE; 113 | expression->operand = operand; 114 | expression->lhs = lhs; 115 | expression->rhs = rhs; 116 | return expression; 117 | } 118 | 119 | LiteralExpression *tree_new_literal_expression(Tree *tree, LiteralKind kind, String value) { 120 | Allocator *const allocator = &tree->context->allocator; 121 | LiteralExpression *const expression = allocator_allocate(allocator, sizeof *expression); 122 | expression->base.kind = EXPRESSION_LITERAL; 123 | expression->kind = kind; 124 | expression->value = value; 125 | return expression; 126 | } 127 | 128 | CompoundLiteralExpression *tree_new_compound_literal_expression(Tree *tree, Type *type, Array(Field*) fields) { 129 | Allocator *const allocator = &tree->context->allocator; 130 | CompoundLiteralExpression *const expression = allocator_allocate(allocator, sizeof *expression); 131 | expression->base.kind = EXPRESSION_COMPOUND_LITERAL; 132 | expression->type = type; 133 | expression->fields = fields; 134 | return expression; 135 | } 136 | 137 | IdentifierExpression *tree_new_identifier_expression(Tree *tree, Identifier *identifier) { 138 | Allocator *const allocator = &tree->context->allocator; 139 | IdentifierExpression *const expression = allocator_allocate(allocator, sizeof *expression); 140 | expression->base.kind = EXPRESSION_IDENTIFIER; 141 | expression->identifier = identifier; 142 | return expression; 143 | } 144 | 145 | ContextExpression *tree_new_context_expression(Tree *tree) { 146 | Allocator *const allocator = &tree->context->allocator; 147 | ContextExpression *const expression = allocator_allocate(allocator, sizeof *expression); 148 | expression->base.kind = EXPRESSION_CONTEXT; 149 | return expression; 150 | } 151 | 152 | UndefinedExpression *tree_new_undefined_expression(Tree *tree) { 153 | Allocator *const allocator = &tree->context->allocator; 154 | UndefinedExpression *const expression = allocator_allocate(allocator, sizeof *expression); 155 | expression->base.kind = EXPRESSION_UNDEFINED; 156 | return expression; 157 | } 158 | 159 | ProcedureGroupExpression *tree_new_procedure_group_expression(Tree *tree, Array(Expression*) expressions) { 160 | Allocator *const allocator = &tree->context->allocator; 161 | ProcedureGroupExpression *const expression = allocator_allocate(allocator, sizeof *expression); 162 | expression->base.kind = EXPRESSION_PROCEDURE_GROUP; 163 | expression->expressions = expressions; 164 | return expression; 165 | } 166 | 167 | // Statements. 168 | EmptyStatement *tree_new_empty_statement(Tree *tree) { 169 | Allocator *const allocator = &tree->context->allocator; 170 | EmptyStatement *const statement = allocator_allocate(allocator, sizeof *statement); 171 | statement->base.kind = STATEMENT_EMPTY; 172 | return statement; 173 | } 174 | 175 | ImportStatement *tree_new_import_statement(Tree *tree, String name, String collection, String pathname, Bool is_using) { 176 | Context *const context = tree->context; 177 | Allocator *const allocator = &context->allocator; 178 | ImportStatement *statement = allocator_allocate(allocator, sizeof *statement); 179 | statement->base.kind = STATEMENT_IMPORT; 180 | statement->name = name; 181 | statement->collection = collection; 182 | if (string_starts_with(pathname, SCLIT("."))) { 183 | statement->pathname = pathname; 184 | } else { 185 | statement->pathname = path_cat(SCLIT("."), pathname, context); 186 | } 187 | statement->is_using = is_using; 188 | statement->location = tree->tokens[array_size(tree->tokens) - 1].location; 189 | array_push(tree->imports, statement); 190 | return statement; 191 | } 192 | 193 | ExpressionStatement *tree_new_expression_statement(Tree *tree, Expression *expression) { 194 | Allocator *const allocator = &tree->context->allocator; 195 | ExpressionStatement *const statement = allocator_allocate(allocator, sizeof *statement); 196 | statement->base.kind = STATEMENT_EXPRESSION; 197 | statement->expression = expression; 198 | return statement; 199 | } 200 | 201 | BlockStatement *tree_new_block_statement(Tree *tree, BlockFlag flags, Array(Statement*) statements) { 202 | Allocator *const allocator = &tree->context->allocator; 203 | BlockStatement *const statement = allocator_allocate(allocator, sizeof *statement); 204 | statement->base.kind = STATEMENT_BLOCK; 205 | statement->flags = flags; 206 | statement->statements = statements; 207 | return statement; 208 | } 209 | 210 | AssignmentStatement *tree_new_assignment_statement(Tree *tree, AssignmentKind assignment, TupleExpression *lhs, TupleExpression *rhs) { 211 | Allocator *const allocator = &tree->context->allocator; 212 | AssignmentStatement *const statement = allocator_allocate(allocator, sizeof *statement); 213 | statement->base.kind = STATEMENT_ASSIGNMENT; 214 | statement->assignment = assignment; 215 | statement->lhs = lhs; 216 | statement->rhs = rhs; 217 | return statement; 218 | } 219 | 220 | DeclarationStatement *tree_new_declaration_statement(Tree *tree, Type *type, Array(Identifier*) names, TupleExpression *values, Bool is_using) { 221 | Allocator *const allocator = &tree->context->allocator; 222 | DeclarationStatement *const statement = allocator_allocate(allocator, sizeof *statement); 223 | statement->base.kind = STATEMENT_DECLARATION; 224 | statement->type = type; 225 | statement->names = names; 226 | statement->values = values; 227 | statement->is_using = is_using; 228 | statement->attributes = 0; 229 | return statement; 230 | } 231 | 232 | IfStatement *tree_new_if_statement(Tree *tree, Statement *init, Expression *cond, BlockStatement *body, BlockStatement *elif) { 233 | Allocator *const allocator = &tree->context->allocator; 234 | IfStatement *const statement = allocator_allocate(allocator, sizeof *statement); 235 | statement->base.kind = STATEMENT_IF; 236 | statement->body = body; 237 | statement->cond = cond; 238 | statement->elif = elif; 239 | statement->init = init; 240 | return statement; 241 | } 242 | 243 | WhenStatement *tree_new_when_statement(Tree *tree, Expression *cond, BlockStatement *body, BlockStatement *elif) { 244 | Allocator *const allocator = &tree->context->allocator; 245 | WhenStatement *const statement = allocator_allocate(allocator, sizeof *statement); 246 | statement->base.kind = STATEMENT_WHEN; 247 | statement->body = body; 248 | statement->cond = cond; 249 | statement->elif = elif; 250 | return statement; 251 | } 252 | 253 | ForStatement *tree_new_for_statement(Tree *tree, Statement *init, Expression *cond, BlockStatement *body, Statement *post) { 254 | Allocator *const allocator = &tree->context->allocator; 255 | ForStatement *const statement = allocator_allocate(allocator, sizeof *statement); 256 | statement->base.kind = STATEMENT_FOR; 257 | statement->body = body; 258 | statement->cond = cond; 259 | statement->init = init; 260 | statement->post = post; 261 | return statement; 262 | } 263 | 264 | SwitchStatement *tree_new_switch_statement(Tree *tree, Statement *init, Expression *cond, Array(CaseClause*) clauses) { 265 | Allocator *const allocator = &tree->context->allocator; 266 | SwitchStatement *const statement = allocator_allocate(allocator, sizeof *statement); 267 | statement->base.kind = STATEMENT_SWITCH; 268 | statement->init = init; 269 | statement->cond = cond; 270 | statement->clauses = clauses; 271 | return statement; 272 | } 273 | 274 | ReturnStatement *tree_new_return_statement(Tree *tree, TupleExpression *result) { 275 | Allocator *const allocator = &tree->context->allocator; 276 | ReturnStatement *const statement = allocator_allocate(allocator, sizeof *statement); 277 | statement->base.kind = STATEMENT_RETURN; 278 | statement->result = result; 279 | return statement; 280 | } 281 | 282 | DeferStatement *tree_new_defer_statement(Tree *tree, Statement *stmt) { 283 | Allocator *const allocator = &tree->context->allocator; 284 | DeferStatement *const statement = allocator_allocate(allocator, sizeof *statement); 285 | statement->base.kind = STATEMENT_DEFER; 286 | statement->statement = stmt; 287 | return statement; 288 | } 289 | 290 | BranchStatement *tree_new_branch_statement(Tree *tree, KeywordKind branch, Identifier *label) { 291 | Allocator *const allocator = &tree->context->allocator; 292 | BranchStatement *const statement = allocator_allocate(allocator, sizeof *statement); 293 | statement->base.kind = STATEMENT_BRANCH; 294 | statement->branch = branch; 295 | statement->label = label; 296 | return statement; 297 | } 298 | 299 | ForeignBlockStatement *tree_new_foreign_block_statement(Tree *tree, Identifier *name, BlockStatement *body) { 300 | Allocator *const allocator = &tree->context->allocator; 301 | ForeignBlockStatement *const statement = allocator_allocate(allocator, sizeof *statement); 302 | statement->base.kind = STATEMENT_FOREIGN_BLOCK; 303 | statement->name = name; 304 | statement->body = body; 305 | statement->attributes = 0; 306 | return statement; 307 | } 308 | 309 | ForeignImportStatement *tree_new_foreign_import_statement(Tree *tree, String name, Array(String) sources) { 310 | Allocator *const allocator = &tree->context->allocator; 311 | ForeignImportStatement *const statement = allocator_allocate(allocator, sizeof *statement); 312 | statement->base.kind = STATEMENT_FOREIGN_IMPORT; 313 | statement->name = name; 314 | statement->sources = sources; 315 | statement->attributes = 0; 316 | return statement; 317 | } 318 | 319 | UsingStatement *tree_new_using_statement(Tree *tree, TupleExpression *list) { 320 | Allocator *const allocator = &tree->context->allocator; 321 | UsingStatement *const statement = allocator_allocate(allocator, sizeof *statement); 322 | statement->base.kind = STATEMENT_USING; 323 | statement->list = list; 324 | return statement; 325 | } 326 | 327 | PackageStatement *tree_new_package_statement(Tree *tree, String name) { 328 | Allocator *const allocator = &tree->context->allocator; 329 | PackageStatement *const statement = allocator_allocate(allocator, sizeof *statement); 330 | statement->base.kind = STATEMENT_PACKAGE; 331 | statement->name = name; 332 | statement->location = tree->tokens[array_size(tree->tokens) - 1].location; 333 | return statement; 334 | } 335 | 336 | Identifier *tree_new_identifier(Tree *tree, String contents, Bool poly) { 337 | Allocator *const allocator = &tree->context->allocator; 338 | Identifier *const identifier = allocator_allocate(allocator, sizeof *identifier); 339 | identifier->contents = contents; 340 | identifier->poly = poly; 341 | identifier->token = array_size(tree->tokens) - 1; 342 | return identifier; 343 | } 344 | 345 | CaseClause *tree_new_case_clause(Tree *tree, TupleExpression *expressions, Array(Statement*) statements) { 346 | Allocator *const allocator = &tree->context->allocator; 347 | CaseClause *const clause = allocator_allocate(allocator, sizeof *clause); 348 | clause->expressions = expressions; 349 | clause->statements = statements; 350 | return clause; 351 | } 352 | 353 | // Types 354 | 355 | static Ptr new_type(Tree *tree, TypeKind kind, Size sizeof_type) { 356 | Allocator *const allocator = &tree->context->allocator; 357 | Type *const type = allocator_allocate(allocator, sizeof_type); 358 | type->kind = kind; 359 | type->poly = false; 360 | return type; 361 | } 362 | 363 | // ^T 364 | PointerType *tree_new_pointer_type(Tree *tree, Type *value_type) { 365 | PointerType *type = new_type(tree, TYPE_POINTER, sizeof *type); 366 | type->type = value_type; 367 | return type; 368 | } 369 | 370 | // [^]T 371 | MultiPointerType *tree_new_multi_pointer_type(Tree *tree, Type *value_type) { 372 | MultiPointerType *type = new_type(tree, TYPE_MULTI_POINTER, sizeof *type); 373 | type->type = value_type; 374 | return type; 375 | } 376 | 377 | // []T 378 | SliceType *tree_new_slice_type(Tree *tree, Type *value_type) { 379 | SliceType *type = new_type(tree, TYPE_SLICE, sizeof *type); 380 | type->type = value_type; 381 | return type; 382 | } 383 | 384 | // [N]T 385 | // [?]T 386 | ArrayType *tree_new_array_type(Tree *tree, Type *value_type, Expression *count) { 387 | ArrayType *type = new_type(tree, TYPE_ARRAY, sizeof *type); 388 | type->type = value_type; 389 | type->count = count; 390 | return type; 391 | } 392 | 393 | // [dynamic]T 394 | DynamicArrayType *tree_new_dynamic_array_type(Tree *tree, Type *value_type) { 395 | DynamicArrayType *type = new_type(tree, TYPE_DYNAMIC_ARRAY, sizeof *type); 396 | type->type = value_type; 397 | return type; 398 | } 399 | 400 | // bit_set[T] 401 | // bit_set[T; U] 402 | BitSetType *tree_new_bit_set_type(Tree *tree, Expression *expression, Type *underlying) { 403 | BitSetType *type = new_type(tree, TYPE_BIT_SET, sizeof *type); 404 | type->expression = expression; 405 | type->underlying = underlying; 406 | return type; 407 | } 408 | 409 | // typeid 410 | TypeidType *tree_new_typeid_type(Tree *tree, Type *specialization) { 411 | TypeidType *type = new_type(tree, TYPE_TYPEID, sizeof *type); 412 | type->specialization = specialization; 413 | return type; 414 | } 415 | 416 | // map[K]V 417 | MapType *tree_new_map_type(Tree *tree, Type *key, Type *value) { 418 | MapType *type = new_type(tree, TYPE_MAP, sizeof *type); 419 | type->key = key; 420 | type->value = value; 421 | return type; 422 | } 423 | 424 | // matrix[R,C]T 425 | MatrixType *tree_new_matrix_type(Tree *tree, Expression *rows, Expression *columns, Type *base_type) { 426 | MatrixType *type = new_type(tree, TYPE_MATRIX, sizeof *type); 427 | type->rows = rows; 428 | type->columns = columns; 429 | type->type = base_type; 430 | return type; 431 | } 432 | 433 | // distinct T 434 | DistinctType *tree_new_distinct_type(Tree *tree, Type *base_type) { 435 | DistinctType *type = new_type(tree, TYPE_DISTINCT, sizeof *type); 436 | type->type = base_type; 437 | return type; 438 | } 439 | 440 | // enum 441 | EnumType *tree_new_enum_type(Tree *tree, Type *base_type, Array(Field*) fields) { 442 | EnumType *type = new_type(tree, TYPE_ENUM, sizeof *type); 443 | type->type = base_type; 444 | type->fields = fields; 445 | return type; 446 | } 447 | 448 | // struct 449 | ConcreteStructType *tree_new_concrete_struct_type(Tree *tree, StructFlag flags, Expression *align, Array(Field*) fields, TupleExpression *where_clauses) { 450 | ConcreteStructType *type = new_type(tree, TYPE_STRUCT, sizeof *type); 451 | type->base.kind = STRUCT_CONCRETE; 452 | type->base.flags = flags; 453 | type->base.align = align; 454 | type->base.fields = fields; 455 | type->base.where_clauses = where_clauses; 456 | return type; 457 | } 458 | 459 | // struct() 460 | GenericStructType *tree_new_generic_struct_type(Tree *tree, StructFlag flags, Expression *align, Array(Field*) parameters, Array(Field*) fields, TupleExpression *where_clauses) { 461 | GenericStructType *type = new_type(tree, TYPE_STRUCT, sizeof *type); 462 | type->base.kind = STRUCT_GENERIC; 463 | type->base.flags = flags; 464 | type->base.align = align; 465 | type->base.fields = fields; 466 | type->base.where_clauses = where_clauses; 467 | type->parameters = parameters; 468 | return type; 469 | } 470 | 471 | // union 472 | ConcreteUnionType *tree_new_concrete_union_type(Tree *tree, UnionFlag flags, Expression *align, Array(Type*) variants, TupleExpression *where_clauses) { 473 | ConcreteUnionType *type = new_type(tree, TYPE_UNION, sizeof *type); 474 | type->base.kind = UNION_GENERIC; 475 | type->base.flags = flags; 476 | type->base.align = align; 477 | type->base.variants = variants; 478 | type->base.where_clauses = where_clauses; 479 | return type; 480 | } 481 | 482 | // union() 483 | GenericUnionType *tree_new_generic_union_type(Tree *tree, UnionFlag flags, Expression *align, Array(Field*) parameters, Array(Type*) variants, TupleExpression *where_clauses) { 484 | GenericUnionType *type = new_type(tree, TYPE_UNION, sizeof *type); 485 | type->base.kind = UNION_GENERIC; 486 | type->base.flags = flags; 487 | type->base.align = align; 488 | type->base.variants = variants; 489 | type->base.where_clauses = where_clauses; 490 | type->parameters = parameters; 491 | return type; 492 | } 493 | 494 | // $T or $T/$U 495 | PolyType *tree_new_poly_type(Tree *tree, Type *base_type, Type *specialization) { 496 | PolyType *type = new_type(tree, TYPE_POLY, sizeof *type); 497 | type->type = base_type; 498 | type->specialization = specialization; 499 | return type; 500 | } 501 | 502 | ExpressionType *tree_new_expression_type(Tree *tree, Expression *expression) { 503 | ExpressionType *type = new_type(tree, TYPE_EXPRESSION, sizeof *type); 504 | type->expression = expression; 505 | return type; 506 | } 507 | 508 | ConcreteProcedureType *tree_new_concrete_procedure_type(Tree *tree, Array(Field*) params, Array(Field*) results, ProcedureFlag flags, CallingConvention convention) { 509 | ConcreteProcedureType *type = new_type(tree, TYPE_PROCEDURE, sizeof *type); 510 | type->base.kind = PROCEDURE_CONCRETE; 511 | type->base.convention = convention; 512 | type->base.params = params; 513 | type->base.results = results; 514 | type->base.flags = flags; 515 | return type; 516 | } 517 | 518 | GenericProcedureType *tree_new_generic_procedure_type(Tree *tree, Array(Field*) params, Array(Field*) results, ProcedureFlag flags, CallingConvention convention) { 519 | GenericProcedureType *const type = new_type(tree, TYPE_PROCEDURE, sizeof *type); 520 | type->base.kind = PROCEDURE_GENERIC; 521 | type->base.convention = convention; 522 | type->base.params = params; 523 | type->base.results = results; 524 | type->base.flags = flags; 525 | return type; 526 | } 527 | 528 | Field *tree_new_field(Tree *tree, Type *type, Identifier *name, Expression *value, String tag, FieldFlag flags) { 529 | Allocator *const allocator = &tree->context->allocator; 530 | Field *field = allocator_allocate(allocator, sizeof *field); 531 | field->type = type; 532 | field->name = name; 533 | field->value = value; 534 | field->tag = tag; 535 | field->flags = flags; 536 | return field; 537 | } 538 | 539 | void tree_init(Tree *tree, String filename, Context *context) { 540 | tree->context = context; 541 | tree->source.name = filename; 542 | tree->source.contents = STRING_NIL; 543 | tree->statements = array_make(context); 544 | tree->imports = array_make(context); 545 | tree->tokens = array_make(context); 546 | } 547 | 548 | void tree_fini(Tree *tree) { 549 | array_free(tree->statements); 550 | array_free(tree->imports); 551 | array_free(tree->tokens); 552 | } 553 | 554 | void tree_record_token(Tree *tree, Token token) { 555 | array_push(tree->tokens, token); 556 | } -------------------------------------------------------------------------------- /src/tree.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_TREE_H 2 | #define CODIN_TREE_H 3 | #include "array.h" 4 | #include "lexer.h" 5 | 6 | typedef struct Context Context; 7 | 8 | typedef struct Tree Tree; 9 | 10 | // Expressions. 11 | typedef struct Expression Expression; 12 | typedef struct TupleExpression TupleExpression; 13 | typedef struct UnaryExpression UnaryExpression; 14 | typedef struct BinaryExpression BinaryExpression; 15 | typedef struct TernaryExpression TernaryExpression; 16 | typedef struct CastExpression CastExpression; 17 | typedef struct SelectorExpression SelectorExpression; 18 | typedef struct CallExpression CallExpression; 19 | typedef struct AssertionExpression AssertionExpression; 20 | typedef struct ValueExpression ValueExpression; 21 | typedef struct ProcedureExpression ProcedureExpression; 22 | typedef struct TypeExpression TypeExpression; 23 | typedef struct IndexExpression IndexExpression; 24 | typedef struct SliceExpression SliceExpression; 25 | typedef struct LiteralExpression LiteralExpression; 26 | typedef struct CompoundLiteralExpression CompoundLiteralExpression; 27 | typedef struct IdentifierExpression IdentifierExpression; 28 | typedef struct ContextExpression ContextExpression; 29 | typedef struct UndefinedExpression UndefinedExpression; 30 | typedef struct ProcedureGroupExpression ProcedureGroupExpression; 31 | 32 | // Statements. 33 | typedef struct Statement Statement; 34 | typedef struct EmptyStatement EmptyStatement; 35 | typedef struct ImportStatement ImportStatement; 36 | typedef struct ExpressionStatement ExpressionStatement; 37 | typedef struct BlockStatement BlockStatement; 38 | typedef struct AssignmentStatement AssignmentStatement; 39 | typedef struct DeclarationStatement DeclarationStatement; 40 | typedef struct IfStatement IfStatement; 41 | typedef struct WhenStatement WhenStatement; 42 | typedef struct ReturnStatement ReturnStatement; 43 | typedef struct ForStatement ForStatement; 44 | typedef struct SwitchStatement SwitchStatement; 45 | typedef struct DeferStatement DeferStatement; 46 | typedef struct BranchStatement BranchStatement; 47 | typedef struct ForeignBlockStatement ForeignBlockStatement; 48 | typedef struct ForeignImportStatement ForeignImportStatement; 49 | typedef struct UsingStatement UsingStatement; 50 | typedef struct PackageStatement PackageStatement; 51 | 52 | // Types. 53 | typedef struct Type Type; 54 | typedef struct BuiltinType BuiltinType; 55 | typedef struct ProcedureType ProcedureType; 56 | typedef struct ConcreteProcedureType ConcreteProcedureType; 57 | typedef struct GenericProcedureType GenericProcedureType; 58 | typedef struct PointerType PointerType; 59 | typedef struct MultiPointerType MultiPointerType; 60 | typedef struct SliceType SliceType; 61 | typedef struct ArrayType ArrayType; 62 | typedef struct DynamicArrayType DynamicArrayType; 63 | typedef struct BitSetType BitSetType; 64 | typedef struct TypeidType TypeidType; 65 | typedef struct MapType MapType; 66 | typedef struct MatrixType MatrixType; 67 | typedef struct DistinctType DistinctType; 68 | typedef struct EnumType EnumType; 69 | typedef struct ExpressionType ExpressionType; 70 | typedef struct StructType StructType; 71 | typedef struct ConcreteStructType ConcreteStructType; 72 | typedef struct GenericStructType GenericStructType; 73 | typedef struct UnionType UnionType; 74 | typedef struct ConcreteUnionType ConcreteUnionType; 75 | typedef struct GenericUnionType GenericUnionType; 76 | typedef struct PolyType PolyType; 77 | 78 | // Misc. 79 | typedef struct Identifier Identifier; 80 | typedef struct Field Field; 81 | typedef struct CaseClause CaseClause; 82 | 83 | enum ExpressionKind { 84 | EXPRESSION_TUPLE = 0, 85 | EXPRESSION_UNARY = 1, // operand 86 | EXPRESSION_BINARY = 2, // lhs rhs 87 | EXPRESSION_TERNARY = 3, // lhs cond else rhs 88 | EXPRESSION_CAST = 4, // auto_cast operand, cast(T)operand, transmute(T)operand 89 | EXPRESSION_SELECTOR = 5, // base.field or .enumerator 90 | EXPRESSION_CALL = 6, // operand(..args) 91 | EXPRESSION_ASSERTION = 7, // operand.(T) or operand.? 92 | EXPRESSION_PROCEDURE = 8, // proc() {} 93 | EXPRESSION_TYPE = 9, // T 94 | EXPRESSION_INDEX = 10, // x[n], x[:], x[n:], x[:n], x[a:b], x[a,b] 95 | EXPRESSION_SLICE = 11, // []T 96 | EXPRESSION_LITERAL = 12, // int, float, rune, string 97 | EXPRESSION_COMPOUND_LITERAL = 13, // T{...} 98 | EXPRESSION_IDENTIFIER = 14, // ident 99 | EXPRESSION_CONTEXT = 15, // context 100 | EXPRESSION_UNDEFINED = 16, // --- 101 | EXPRESSION_PROCEDURE_GROUP = 17, // proc{...} 102 | }; 103 | 104 | enum StatementKind { 105 | STATEMENT_EMPTY = 0, 106 | STATEMENT_BLOCK = 1, 107 | STATEMENT_IMPORT = 2, // import 108 | STATEMENT_EXPRESSION = 3, 109 | STATEMENT_ASSIGNMENT = 4, // =, +=, -=, *=, /=, %=, %%=, &=, |=, ~=, &~=, <<=, >>=, &&=, ||= 110 | STATEMENT_DECLARATION = 5, 111 | STATEMENT_IF = 6, 112 | STATEMENT_WHEN = 7, 113 | STATEMENT_RETURN = 8, 114 | STATEMENT_FOR = 9, 115 | STATEMENT_SWITCH = 10, 116 | STATEMENT_DEFER = 11, 117 | STATEMENT_BRANCH = 12, // break, continue, fallthrough 118 | STATEMENT_FOREIGN_BLOCK = 13, // foreign 119 | STATEMENT_FOREIGN_IMPORT = 14, // foreign import 120 | STATEMENT_USING = 15, // using 121 | STATEMENT_PACKAGE = 16, // package 122 | }; 123 | 124 | enum ProcedureKind { 125 | PROCEDURE_CONCRETE, 126 | PROCEDURE_GENERIC, 127 | }; 128 | 129 | enum StructKind { 130 | STRUCT_CONCRETE, 131 | STRUCT_GENERIC, 132 | }; 133 | 134 | enum UnionKind { 135 | UNION_CONCRETE, 136 | UNION_GENERIC, 137 | }; 138 | 139 | enum TypeKind { 140 | TYPE_BUILTIN = 0, // b{8,16,32,64}, f{16,32,64}(le|be), (i|u)8, (i|u){16,32,64,128}(le|be), 141 | TYPE_PROCEDURE = 1, // proc 142 | TYPE_POINTER = 2, // ^T 143 | TYPE_MULTI_POINTER = 3, // [^]T 144 | TYPE_SLICE = 4, // []T 145 | TYPE_ARRAY = 5, // [N]T or [?]T 146 | TYPE_DYNAMIC_ARRAY = 6, // [dynamic]T 147 | TYPE_BIT_SET = 7, // bit_set[T] or bit_set[T; U] 148 | TYPE_TYPEID = 8, // typeid 149 | TYPE_MAP = 9, // map[K]V 150 | TYPE_MATRIX = 10, // matrix[R,C]T 151 | TYPE_DISTINCT = 11, // distinct T 152 | TYPE_ENUM = 12, // enum 153 | TYPE_STRUCT = 13, // struct 154 | TYPE_UNION = 14, // union 155 | TYPE_POLY = 15, // $T or $T/$U 156 | TYPE_EXPRESSION = 16, // Expression which evaluates to a Type* 157 | }; 158 | 159 | enum BuiltinTypeKind { 160 | BUILTIN_TYPE_SINT = 0, // i8,i{16,32,64,128}(le|be) 161 | BUILTIN_TYPE_UINT = 1, // u8,u{16,32,64,128}(le|be) 162 | BUILTIN_TYPE_FLOAT = 2, // f{16,32,64}(le|be) 163 | BUILTIN_TYPE_BOOL = 3, // b{8,16,32,64} 164 | BUILTIN_TYPE_STRING = 4, // string 165 | BUILTIN_TYPE_CSTRING = 5, // cstring 166 | BUILTIN_TYPE_POINTER = 6, // rawptr 167 | BUILTIN_TYPE_UINTPTR = 7, // uintptr 168 | }; 169 | 170 | enum Endianess { 171 | ENDIANESS_NA, // Not applicable. 172 | ENDIANESS_LITTLE, 173 | ENDIANESS_BIG, 174 | }; 175 | 176 | enum BlockFlag { 177 | BLOCK_FLAG_BOUNDS_CHECK = 1 << 0, 178 | BLOCK_FLAG_TYPE_ASSERT = 1 << 1, 179 | }; 180 | 181 | enum ProcedureFlag { 182 | PROC_FLAG_DIVERGING = 1 << 0, 183 | PROC_FLAG_OPTIONAL_OK = 1 << 1, 184 | PROC_FLAG_OPTIONAL_ALLOCATION_ERROR = 1 << 2, 185 | PROC_FLAG_BOUNDS_CHECK = 1 << 3, 186 | PROC_FLAG_TYPE_ASSERT = 1 << 4, 187 | PROC_FLAG_FORCE_INLINE = 1 << 5, 188 | }; 189 | 190 | enum StructFlag { 191 | STRUCT_FLAG_PACKED = 1 << 0, // #packed 192 | STRUCT_FLAG_UNCOPYABLE = 1 << 1, // #no_copy 193 | STRUCT_FLAG_UNION = 1 << 2, // #raw_union 194 | }; 195 | 196 | enum UnionFlag { 197 | UNION_FLAG_NO_NIL = 1 << 0, // #no_nil 198 | UNION_FLAG_SHARED_NIL = 1 << 1, // #shared_nil 199 | UNION_FLAG_MAYBE = 1 << 2, // #maybe 200 | }; 201 | 202 | enum FieldFlag { 203 | FIELD_FLAG_ANY_INT = 1 << 0, // #any_int 204 | FIELD_FLAG_C_VARARG = 1 << 1, // #c_vararg 205 | FIELD_FLAG_NO_ALIAS = 1 << 2, // #no_alias 206 | FIELD_FLAG_SUBTYPE = 1 << 3, // #subtype 207 | FIELD_FLAG_CONST = 1 << 4, // #const 208 | FIELD_FLAG_USING = 1 << 5, // using 209 | }; 210 | 211 | #define CCONVENTION(enumerator, string) CCONV_ ## enumerator, 212 | enum CallingConvention { 213 | CCONV_INVALID, 214 | #include "lexemes.h" 215 | }; 216 | 217 | typedef enum ExpressionKind ExpressionKind; 218 | typedef enum StatementKind StatementKind; 219 | typedef enum ProcedureKind ProcedureKind; 220 | typedef enum StructKind StructKind; 221 | typedef enum UnionKind UnionKind; 222 | typedef enum TypeKind TypeKind; 223 | typedef enum BuiltinTypeKind BuiltinTypeKind; 224 | typedef enum Endianess Endianess; 225 | 226 | typedef enum BlockFlag BlockFlag; 227 | typedef enum ProcedureFlag ProcedureFlag; 228 | typedef enum StructFlag StructFlag; 229 | typedef enum UnionFlag UnionFlag; 230 | typedef enum FieldFlag FieldFlag; 231 | 232 | typedef enum CallingConvention CallingConvention; 233 | 234 | String procedure_flags_to_string(ProcedureFlag flags, Context *context); 235 | 236 | // Expressions. 237 | struct Expression { 238 | ExpressionKind kind; 239 | }; 240 | 241 | struct TupleExpression { 242 | Expression base; 243 | Array(Expression*) expressions; 244 | }; 245 | 246 | // 247 | struct UnaryExpression { 248 | Expression base; 249 | OperatorKind operation; 250 | Expression *operand; 251 | }; 252 | 253 | // 254 | struct BinaryExpression { 255 | Expression base; 256 | OperatorKind operation; 257 | Expression *lhs; 258 | Expression *rhs; 259 | }; 260 | 261 | // 262 | // ? : 263 | struct TernaryExpression { 264 | Expression base; 265 | // One of: 266 | // KEYWORD_IF 267 | // KEYWORD_WHEN 268 | KeywordKind operation; 269 | Expression *on_true; 270 | Expression *cond; 271 | Expression *on_false; 272 | }; 273 | 274 | // cast(T)expr 275 | // (T)expr 276 | // T(expr) 277 | struct CastExpression { 278 | Expression base; 279 | // One of: 280 | // OPERATOR_CAST 281 | // OPERATOR_AUTO_CAST 282 | // OPERATOR_TRANSMUTE 283 | OperatorKind kind; 284 | Type *type; 285 | Expression *expression; 286 | }; 287 | 288 | // . 289 | // . 290 | struct SelectorExpression { 291 | Expression base; 292 | Expression *operand; 293 | Identifier *identifier; 294 | }; 295 | 296 | // (..) 297 | struct CallExpression { 298 | Expression base; 299 | Expression *operand; 300 | Array(Field*) arguments; 301 | }; 302 | 303 | // .(T) 304 | // .? 305 | struct AssertionExpression { 306 | Expression base; 307 | Expression *operand; 308 | Type *type; 309 | }; 310 | 311 | struct ProcedureExpression { 312 | Expression base; 313 | ProcedureType *type; 314 | TupleExpression *where_clauses; 315 | BlockStatement *body; 316 | }; 317 | 318 | struct TypeExpression { 319 | Expression base; 320 | Type *type; 321 | }; 322 | 323 | struct IndexExpression { 324 | Expression base; 325 | Expression *operand; 326 | Expression *lhs; 327 | Expression *rhs; 328 | }; 329 | 330 | struct SliceExpression { 331 | Expression base; 332 | Expression *operand; 333 | Expression *lhs; 334 | Expression *rhs; 335 | }; 336 | 337 | struct LiteralExpression { 338 | Expression base; 339 | LiteralKind kind; 340 | String value; 341 | }; 342 | 343 | struct CompoundLiteralExpression { 344 | Expression base; 345 | Type *type; 346 | Array(Field*) fields; 347 | }; 348 | 349 | struct IdentifierExpression { 350 | Expression base; 351 | Identifier *identifier; 352 | }; 353 | 354 | struct ContextExpression { 355 | Expression base; 356 | }; 357 | 358 | struct UndefinedExpression { 359 | Expression base; 360 | }; 361 | 362 | struct ProcedureGroupExpression { 363 | Expression base; 364 | Array(Expression*) expressions; 365 | }; 366 | 367 | // Statements. 368 | struct Statement { 369 | StatementKind kind; 370 | }; 371 | 372 | struct EmptyStatement { 373 | Statement base; 374 | }; 375 | 376 | struct ImportStatement { 377 | Statement base; 378 | String name; // Optional 379 | String collection; // "core" 380 | String pathname; // "./fmt" 381 | Bool is_using; 382 | Location location; // location of the import statement 383 | }; 384 | 385 | struct ExpressionStatement { 386 | Statement base; 387 | Expression *expression; 388 | }; 389 | 390 | struct BlockStatement { 391 | Statement base; 392 | BlockFlag flags; 393 | Array(Statement*) statements; 394 | Identifier *label; 395 | }; 396 | 397 | struct AssignmentStatement { 398 | Statement base; 399 | AssignmentKind assignment; 400 | TupleExpression *lhs; 401 | TupleExpression *rhs; 402 | }; 403 | 404 | struct DeclarationStatement { 405 | Statement base; 406 | Type *type; 407 | Array(Identifier*) names; 408 | TupleExpression *values; 409 | Array(Field*) attributes; 410 | Bool is_using; 411 | }; 412 | 413 | struct IfStatement { 414 | Statement base; 415 | Statement *init; 416 | Expression *cond; 417 | BlockStatement *body; 418 | BlockStatement *elif; 419 | Identifier *label; 420 | }; 421 | 422 | struct WhenStatement { 423 | Statement base; 424 | Expression *cond; 425 | BlockStatement *body; 426 | BlockStatement *elif; 427 | }; 428 | 429 | struct ReturnStatement { 430 | Statement base; 431 | TupleExpression *result; 432 | }; 433 | 434 | struct ForStatement { 435 | Statement base; 436 | Statement *init; 437 | Expression *cond; 438 | BlockStatement *body; 439 | Statement *post; 440 | Identifier *label; 441 | }; 442 | 443 | struct SwitchStatement { 444 | Statement base; 445 | Statement *init; 446 | Expression *cond; 447 | Array(CaseClause*) clauses; 448 | Identifier *label; 449 | }; 450 | 451 | struct DeferStatement { 452 | Statement base; 453 | Statement *statement; 454 | }; 455 | 456 | struct BranchStatement { 457 | Statement base; 458 | KeywordKind branch; 459 | Identifier *label; // Optional label. 460 | }; 461 | 462 | struct ForeignBlockStatement { 463 | Statement base; 464 | Identifier *name; 465 | BlockStatement *body; 466 | Array(Field*) attributes; 467 | }; 468 | 469 | struct ForeignImportStatement { 470 | Statement base; 471 | String name; 472 | Array(String) sources; 473 | Array(Field*) attributes; 474 | }; 475 | 476 | struct UsingStatement { 477 | Statement base; 478 | TupleExpression *list; 479 | }; 480 | 481 | struct PackageStatement { 482 | Statement base; 483 | String name; 484 | Location location; 485 | }; 486 | 487 | // Types. 488 | struct Type { 489 | TypeKind kind; 490 | Bool poly; 491 | }; 492 | 493 | struct BuiltinType { 494 | Type base; 495 | String identifier; 496 | BuiltinTypeKind kind; 497 | Uint16 size_of; 498 | Uint16 align_of; 499 | Endianess endianess; 500 | }; 501 | 502 | struct IdentifierType { 503 | Type base; 504 | Identifier *identifier; 505 | }; 506 | 507 | struct ProcedureType { 508 | Type base; 509 | ProcedureKind kind; 510 | ProcedureFlag flags; 511 | CallingConvention convention; 512 | Array(Field*) params; 513 | Array(Field*) results; 514 | }; 515 | 516 | struct ConcreteProcedureType { 517 | ProcedureType base; 518 | }; 519 | 520 | struct GenericProcedureType { 521 | ProcedureType base; 522 | }; 523 | 524 | struct PointerType { 525 | Type base; 526 | Type *type; 527 | }; 528 | 529 | struct MultiPointerType { 530 | Type base; 531 | Type *type; 532 | }; 533 | 534 | struct SliceType { 535 | Type base; 536 | Type *type; 537 | }; 538 | 539 | struct ArrayType { 540 | Type base; 541 | Type *type; 542 | Expression *count; 543 | }; 544 | 545 | struct DynamicArrayType { 546 | Type base; 547 | Type *type; 548 | }; 549 | 550 | struct BitSetType { 551 | Type base; 552 | Expression *expression; 553 | Type *underlying; 554 | }; 555 | 556 | struct TypeidType { 557 | Type base; 558 | Type *specialization; 559 | }; 560 | 561 | struct MapType { 562 | Type base; 563 | Type *key; 564 | Type *value; 565 | }; 566 | 567 | struct MatrixType { 568 | Type base; 569 | Expression *columns; 570 | Expression *rows; 571 | Type *type; 572 | }; 573 | 574 | struct DistinctType { 575 | Type base; 576 | Type *type; 577 | }; 578 | 579 | struct EnumType { 580 | Type base; 581 | Type *type; // The T in 'enum T' 582 | Array(Field*) fields; 583 | }; 584 | 585 | struct ExpressionType { 586 | Type base; 587 | // Can be one of: 588 | // EXPRESSION_IDENTIFIER 589 | // EXPRESSION_TYPE 590 | Expression *expression; 591 | }; 592 | 593 | struct StructType { 594 | Type base; 595 | StructKind kind; 596 | StructFlag flags; 597 | Expression *align; 598 | Array(Field*) fields; 599 | TupleExpression *where_clauses; 600 | }; 601 | 602 | struct ConcreteStructType { 603 | StructType base; 604 | }; 605 | 606 | struct GenericStructType { 607 | StructType base; 608 | Array(Field*) parameters; 609 | }; 610 | 611 | struct UnionType { 612 | Type base; 613 | UnionKind kind; 614 | UnionFlag flags; 615 | Expression *align; 616 | Array(Type*) variants; 617 | TupleExpression *where_clauses; 618 | }; 619 | 620 | struct ConcreteUnionType { 621 | UnionType base; 622 | }; 623 | 624 | struct GenericUnionType { 625 | UnionType base; 626 | Array(Field*) parameters; 627 | }; 628 | 629 | struct PolyType { 630 | Type base; 631 | Type *type; 632 | Type *specialization; 633 | }; 634 | 635 | // Misc. 636 | struct Identifier { 637 | String contents; 638 | Bool poly; 639 | Uint32 token; 640 | }; 641 | 642 | struct Field { 643 | Identifier *name; // Always present 644 | Type *type; // Optional. 645 | Expression *value; // Optional. 646 | String tag; // Optional. 647 | FieldFlag flags; 648 | }; 649 | 650 | struct CaseClause { 651 | TupleExpression *expressions; 652 | Array(Statement*) statements; 653 | }; 654 | 655 | static inline String calling_convention_to_string(CallingConvention cc) { 656 | #define CCONVENTION(enumerator, name, ...) SLIT(name), 657 | static const String TABLE[] = { 658 | #include "lexemes.h" 659 | }; 660 | return TABLE[cc]; 661 | }; 662 | 663 | struct Tree { 664 | Context *context; 665 | Source source; 666 | Array(Statement*) statements; 667 | Array(ImportStatement*) imports; 668 | Array(Token) tokens; // Recorded tokens for diagnostics. 669 | }; 670 | 671 | void tree_init(Tree *tree, String filename, Context *context); 672 | void tree_fini(Tree *tree); 673 | 674 | void tree_record_token(Tree *tree, Token token); 675 | 676 | // Expressions 677 | TupleExpression *tree_new_tuple_expression(Tree *tree, Array(Expression*) expressions); 678 | UnaryExpression *tree_new_unary_expression(Tree *tree, OperatorKind operation, Expression *operand); 679 | BinaryExpression *tree_new_binary_expression(Tree *tree, OperatorKind operation, Expression *lhs, Expression *rhs); 680 | TernaryExpression *tree_new_ternary_expression(Tree *tree, Expression *on_true, KeywordKind operation, Expression *cond, Expression *on_false); 681 | CastExpression *tree_new_cast_expression(Tree *tree, OperatorKind kind, Type *type, Expression *expression); 682 | SelectorExpression *tree_new_selector_expression(Tree *tree, Expression *operand, Identifier *identifier); 683 | CallExpression *tree_new_call_expression(Tree *tree, Expression *operand, Array(Field*) arguments); 684 | AssertionExpression *tree_new_assertion_expression(Tree *tree, Expression *operand, Type *type); 685 | ProcedureExpression *tree_new_procedure_expression(Tree *tree, ProcedureType *type, TupleExpression *where_clauses, BlockStatement *body); 686 | TypeExpression *tree_new_type_expression(Tree *tree, Type *type); 687 | IndexExpression *tree_new_index_expression(Tree *tree, Expression *operand, Expression *lhs, Expression *rhs); 688 | SliceExpression *tree_new_slice_expression(Tree *tree, Expression *operand, Expression *lhs, Expression *rhs); 689 | LiteralExpression *tree_new_literal_expression(Tree *tree, LiteralKind kind, String value); 690 | CompoundLiteralExpression *tree_new_compound_literal_expression(Tree *tree, Type *type, Array(Field*) fields); 691 | IdentifierExpression *tree_new_identifier_expression(Tree *tree, Identifier *identifier); 692 | ContextExpression *tree_new_context_expression(Tree *tree); 693 | UndefinedExpression *tree_new_undefined_expression(Tree *tree); 694 | ProcedureGroupExpression *tree_new_procedure_group_expression(Tree *tree, Array(Expression*) expressions); 695 | 696 | // Statements 697 | EmptyStatement *tree_new_empty_statement(Tree *tree); 698 | ImportStatement *tree_new_import_statement(Tree *tree, String name, String collection, String pathname, Bool is_using); 699 | ExpressionStatement *tree_new_expression_statement(Tree *tree, Expression *expression); 700 | BlockStatement *tree_new_block_statement(Tree *tree, BlockFlag flags, Array(Statement*) statements); 701 | AssignmentStatement *tree_new_assignment_statement(Tree *tree, AssignmentKind assignment, TupleExpression *lhs, TupleExpression *rhs); 702 | DeclarationStatement *tree_new_declaration_statement(Tree *tree, Type *type, Array(Identifier*) names, TupleExpression *values, Bool is_using); 703 | IfStatement *tree_new_if_statement(Tree *tree, Statement *init, Expression *cond, BlockStatement *body, BlockStatement *elif); 704 | WhenStatement *tree_new_when_statement(Tree *tree, Expression *cond, BlockStatement *body, BlockStatement *elif); 705 | ForStatement *tree_new_for_statement(Tree *tree, Statement *init, Expression *cond, BlockStatement *body, Statement *post); 706 | SwitchStatement *tree_new_switch_statement(Tree *tree, Statement *init, Expression *cond, Array(CaseClause*) clauses); 707 | ReturnStatement *tree_new_return_statement(Tree *tree, TupleExpression *result); 708 | DeferStatement *tree_new_defer_statement(Tree *tree, Statement *stmt); 709 | BranchStatement *tree_new_branch_statement(Tree *tree, KeywordKind branch, Identifier *label); 710 | ForeignBlockStatement *tree_new_foreign_block_statement(Tree *tree, Identifier *name, BlockStatement *body); 711 | ForeignImportStatement *tree_new_foreign_import_statement(Tree *tree, String name, Array(String) sources); 712 | UsingStatement *tree_new_using_statement(Tree *tree, TupleExpression *list); 713 | PackageStatement *tree_new_package_statement(Tree *tree, String package); 714 | 715 | // Types 716 | ConcreteProcedureType *tree_new_concrete_procedure_type(Tree *tree, Array(Field*) params, Array(Field*) results, ProcedureFlag flags, CallingConvention convention); 717 | GenericProcedureType *tree_new_generic_procedure_type(Tree *tree, Array(Field*) params, Array(Field*) results, ProcedureFlag flags, CallingConvention convention); 718 | PointerType *tree_new_pointer_type(Tree *Tree, Type *type); 719 | MultiPointerType *tree_new_multi_pointer_type(Tree *Tree, Type *type); 720 | SliceType *tree_new_slice_type(Tree *tree, Type *type); 721 | ArrayType *tree_new_array_type(Tree *Tree, Type *type, Expression *count); 722 | DynamicArrayType *tree_new_dynamic_array_type(Tree *tree, Type *type); 723 | BitSetType *tree_new_bit_set_type(Tree *tree, Expression *expression, Type *underlying); 724 | TypeidType *tree_new_typeid_type(Tree *tree, Type *specialization); 725 | MapType *tree_new_map_type(Tree *tree, Type *key, Type *value); 726 | MatrixType *tree_new_matrix_type(Tree *tree, Expression *rows, Expression *columns, Type *type); 727 | DistinctType *tree_new_distinct_type(Tree *tree, Type *type); 728 | EnumType *tree_new_enum_type(Tree *tree, Type *base_type, Array(Field*) fields); 729 | ExpressionType *tree_new_expression_type(Tree *tree, Expression *expression); 730 | ConcreteStructType *tree_new_concrete_struct_type(Tree *tree, StructFlag flags, Expression *align, Array(Field*) fields, TupleExpression *where_clauses); 731 | GenericStructType *tree_new_generic_struct_type(Tree *tree, StructFlag flags, Expression *align, Array(Field*) parameters, Array(Field*) fields, TupleExpression *where_clauses); 732 | ConcreteUnionType *tree_new_concrete_union_type(Tree *tree, UnionFlag flags, Expression *align, Array(Type*) variants, TupleExpression *where_clauses); 733 | GenericUnionType *tree_new_generic_union_type(Tree *tree, UnionFlag flags, Expression *align, Array(Field*) parameters, Array(Type*) variants, TupleExpression *where_clauses); 734 | PolyType *tree_new_poly_type(Tree *tree, Type *type, Type *specialization); 735 | 736 | Field *tree_new_field(Tree *tree, Type *type, Identifier *name, Expression *value, String tag, FieldFlag flags); 737 | Identifier *tree_new_identifier(Tree *tree, String contents, Bool poly); 738 | CaseClause *tree_new_case_clause(Tree *tree, TupleExpression *expressions, Array(Statement*) statements); 739 | 740 | #endif // CODIN_TREE_H -------------------------------------------------------------------------------- /src/utility.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "utility.h" 4 | #include "strbuf.h" 5 | #include "context.h" 6 | 7 | #if defined(OS_POSIX) 8 | #include 9 | #elif defined(OS_WINDOWS) 10 | #define WIN32_LEAN_AND_MEAN 11 | #include 12 | #endif 13 | 14 | static Bool sizefile(FILE *fp, Uint64 *size) { 15 | if (fseek(fp, 0, SEEK_END) != 0) { 16 | return false; 17 | } 18 | 19 | const long tell = ftell(fp); 20 | if (tell < 0) { 21 | fseek(fp, 0, SEEK_SET); 22 | return false; 23 | } 24 | 25 | fseek(fp, 0, SEEK_SET); 26 | *size = CAST(Uint64, tell); 27 | 28 | return true; 29 | } 30 | 31 | Array(Uint8) readfile(String filename, Context *context) { 32 | // Need to introduce the NUL to call fopen. 33 | char *terminated = string_to_null(filename, context); 34 | FILE *fp = fopen(terminated, "rb"); 35 | allocator_deallocate(&context->allocator, terminated); 36 | if (!fp) { 37 | return 0; 38 | } 39 | 40 | // Disable buffering since we're reading the entire thing. 41 | setvbuf(fp, NULL, _IONBF, 0); 42 | 43 | Array(Uint8) result = array_make(context); 44 | Uint64 size = 0; 45 | if (!sizefile(fp, &size)) { 46 | while (!feof(fp)) { 47 | const Uint8 ch = fgetc(fp); 48 | array_push(result, ch); 49 | } 50 | return result; 51 | } 52 | 53 | array_resize(result, size); 54 | if (fread(result, size, 1, fp) != 1) { 55 | fclose(fp); 56 | return 0; 57 | } 58 | 59 | fclose(fp); 60 | return result; 61 | } 62 | 63 | // Half float support 64 | static const Uint32 CODIN_f16_base[] = { 65 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 66 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 67 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 68 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 69 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 70 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 71 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 72 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 73 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 74 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 75 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 76 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 77 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 78 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 79 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 80 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 81 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 82 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 83 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 84 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 85 | 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 86 | 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 87 | 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 88 | 0x00000c00, 0x00001000, 0x00001400, 0x00001800, 0x00001c00, 89 | 0x00002000, 0x00002400, 0x00002800, 0x00002c00, 0x00003000, 90 | 0x00003400, 0x00003800, 0x00003c00, 0x00004000, 0x00004400, 91 | 0x00004800, 0x00004c00, 0x00005000, 0x00005400, 0x00005800, 92 | 0x00005c00, 0x00006000, 0x00006400, 0x00006800, 0x00006c00, 93 | 0x00007000, 0x00007400, 0x00007800, 0x00007c00, 0x00007c00, 94 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 95 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 96 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 97 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 98 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 99 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 100 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 101 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 102 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 103 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 104 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 105 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 106 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 107 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 108 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 109 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 110 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 111 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 112 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 113 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 114 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 115 | 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 0x00007c00, 116 | 0x00007c00, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 117 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 118 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 119 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 120 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 121 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 122 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 123 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 124 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 125 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 126 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 127 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 128 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 129 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 130 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 131 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 132 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 133 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 134 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 135 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008000, 136 | 0x00008000, 0x00008000, 0x00008000, 0x00008000, 0x00008001, 137 | 0x00008002, 0x00008004, 0x00008008, 0x00008010, 0x00008020, 138 | 0x00008040, 0x00008080, 0x00008100, 0x00008200, 0x00008400, 139 | 0x00008800, 0x00008c00, 0x00009000, 0x00009400, 0x00009800, 140 | 0x00009c00, 0x0000a000, 0x0000a400, 0x0000a800, 0x0000ac00, 141 | 0x0000b000, 0x0000b400, 0x0000b800, 0x0000bc00, 0x0000c000, 142 | 0x0000c400, 0x0000c800, 0x0000cc00, 0x0000d000, 0x0000d400, 143 | 0x0000d800, 0x0000dc00, 0x0000e000, 0x0000e400, 0x0000e800, 144 | 0x0000ec00, 0x0000f000, 0x0000f400, 0x0000f800, 0x0000fc00, 145 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 146 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 147 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 148 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 149 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 150 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 151 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 152 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 153 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 154 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 155 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 156 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 157 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 158 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 159 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 160 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 161 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 162 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 163 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 164 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 165 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 166 | 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 0x0000fc00, 167 | 0x0000fc00, 0x0000fc00 168 | }; 169 | 170 | static const Uint8 CODIN_f16_shift[] = { 171 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 172 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 173 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 174 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 175 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 176 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 177 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 178 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 179 | 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 180 | 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 181 | 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 182 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 183 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 184 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 185 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 186 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 187 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 188 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 189 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 190 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, 191 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 192 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 193 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 194 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 195 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 196 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 197 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 198 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 199 | 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 200 | 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 201 | 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 202 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 203 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 204 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 205 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 206 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 207 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 208 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 209 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 210 | 0x18, 0x18, 0x18, 0x18, 0x0d 211 | }; 212 | 213 | Float16 f32_to_f16(Float32 f) { 214 | const union { Float32 f; Uint32 u; } s = { f }; 215 | const Uint32 e = s.u >> 23; 216 | const Uint32 base = CODIN_f16_base[e & 0x1ff]; 217 | const Uint8 shift = CODIN_f16_shift[e & 0x1ff]; 218 | return base + ((s.u & 0x007fffff) >> shift); 219 | } 220 | 221 | Float32 f16_to_f32(Float16 f) { 222 | union { Uint32 u; Float32 f; } o = { CAST(Uint32, (f & 0x7fff) << 13) }; 223 | const Uint32 exp = 0x0f800000 & o.u; 224 | o.u += 0x38000000; 225 | if (exp == 0x0f800000) o.u += 0x38000000; 226 | if (exp == 0x00000000) o.u += 0x00000001, o.f -= 0x1p-14; 227 | o.u |= (f & 0x8000) << 16; 228 | return o.f; 229 | } -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef CODIN_UTILITY_H 2 | #define CODIN_UTILITY_H 3 | #include "array.h" 4 | #include "string.h" 5 | 6 | typedef struct Context Context; 7 | 8 | Array(Uint8) readfile(String filename, Context *context); 9 | 10 | Float16 f32_to_f16(Float32 x); 11 | 12 | #endif // CODIN_UTILITY_H -------------------------------------------------------------------------------- /src/vendor/spall.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2023 Phillip Trudeau-Tavara 2 | // SPDX-License-Identifier: MIT 3 | 4 | /* 5 | 6 | TODO: Optional Helper APIs: 7 | 8 | - Compression API: would require a mutexed lockable context (yuck...) 9 | - Either using a ZIP library, a name cache + TIDPID cache, or both (but ZIP is likely more than enough!!!) 10 | - begin()/end() writes compressed chunks to a caller-determined destination 11 | - The destination can be the buffered-writing API or a custom user destination 12 | - Ultimately need to take a lock with some granularity... can that be the caller's responsibility? 13 | 14 | - Counter Event: should allow tracking arbitrary named values with a single event, for memory and frame profiling 15 | 16 | - Ring-buffer API 17 | spall_ring_init 18 | spall_ring_emit_begin 19 | spall_ring_emit_end 20 | spall_ring_flush 21 | */ 22 | 23 | #ifndef SPALL_H 24 | #define SPALL_H 25 | 26 | #if !defined(_MSC_VER) || defined(__clang__) 27 | #define SPALL_NOINSTRUMENT __attribute__((no_instrument_function)) 28 | #define SPALL_FORCEINLINE __attribute__((always_inline)) 29 | #else 30 | #define _CRT_SECURE_NO_WARNINGS 31 | #define SPALL_NOINSTRUMENT // Can't noinstrument on MSVC! 32 | #define SPALL_FORCEINLINE __forceinline 33 | #endif 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define SPALL_FN static inline SPALL_NOINSTRUMENT 41 | 42 | #define SPALL_MIN(a, b) (((a) < (b)) ? (a) : (b)) 43 | 44 | #pragma pack(push, 1) 45 | 46 | typedef struct SpallHeader { 47 | uint64_t magic_header; // = 0x0BADF00D 48 | uint64_t version; // = 1 49 | double timestamp_unit; 50 | uint64_t must_be_0; 51 | } SpallHeader; 52 | 53 | enum { 54 | SpallEventType_Invalid = 0, 55 | SpallEventType_Custom_Data = 1, // Basic readers can skip this. 56 | SpallEventType_StreamOver = 2, 57 | 58 | SpallEventType_Begin = 3, 59 | SpallEventType_End = 4, 60 | SpallEventType_Instant = 5, 61 | 62 | SpallEventType_Overwrite_Timestamp = 6, // Retroactively change timestamp units - useful for incrementally improving RDTSC frequency. 63 | SpallEventType_Pad_Skip = 7, 64 | }; 65 | 66 | typedef struct SpallBeginEvent { 67 | uint8_t type; // = SpallEventType_Begin 68 | uint8_t category; 69 | 70 | uint32_t pid; 71 | uint32_t tid; 72 | double when; 73 | 74 | uint8_t name_length; 75 | uint8_t args_length; 76 | } SpallBeginEvent; 77 | 78 | typedef struct SpallBeginEventMax { 79 | SpallBeginEvent event; 80 | char name_bytes[255]; 81 | char args_bytes[255]; 82 | } SpallBeginEventMax; 83 | 84 | typedef struct SpallEndEvent { 85 | uint8_t type; // = SpallEventType_End 86 | uint32_t pid; 87 | uint32_t tid; 88 | double when; 89 | } SpallEndEvent; 90 | 91 | typedef struct SpallPadSkipEvent { 92 | uint8_t type; // = SpallEventType_Pad_Skip 93 | uint32_t size; 94 | } SpallPadSkipEvent; 95 | 96 | #pragma pack(pop) 97 | 98 | typedef struct SpallProfile SpallProfile; 99 | 100 | // Important!: If you define your own callbacks, mark them SPALL_NOINSTRUMENT! 101 | typedef bool (*SpallWriteCallback)(SpallProfile *self, const void *data, size_t length); 102 | typedef bool (*SpallFlushCallback)(SpallProfile *self); 103 | typedef void (*SpallCloseCallback)(SpallProfile *self); 104 | 105 | struct SpallProfile { 106 | double timestamp_unit; 107 | bool is_json; 108 | SpallWriteCallback write; 109 | SpallFlushCallback flush; 110 | SpallCloseCallback close; 111 | void *data; 112 | }; 113 | 114 | // Important!: If you are writing Begin/End events, then do NOT write 115 | // events for the same PID + TID pair on different buffers!!! 116 | typedef struct SpallBuffer { 117 | void *data; 118 | size_t length; 119 | 120 | // Internal data - don't assign this 121 | size_t head; 122 | SpallProfile *ctx; 123 | } SpallBuffer; 124 | 125 | #ifdef __cplusplus 126 | extern "C" { 127 | #endif 128 | 129 | #if defined(SPALL_BUFFER_PROFILING) && !defined(SPALL_BUFFER_PROFILING_GET_TIME) 130 | #error "You must #define SPALL_BUFFER_PROFILING_GET_TIME() to profile buffer flushes." 131 | #endif 132 | 133 | SPALL_FN SPALL_FORCEINLINE void spall__buffer_profile(SpallProfile *ctx, SpallBuffer *wb, double spall_time_begin, double spall_time_end, const char *name, int name_len); 134 | #ifdef SPALL_BUFFER_PROFILING 135 | #define SPALL_BUFFER_PROFILE_BEGIN() double spall_time_begin = (SPALL_BUFFER_PROFILING_GET_TIME()) 136 | // Don't call this with anything other than a string literal 137 | #define SPALL_BUFFER_PROFILE_END(name) spall__buffer_profile(ctx, wb, spall_time_begin, (SPALL_BUFFER_PROFILING_GET_TIME()), "" name "", sizeof("" name "") - 1) 138 | #else 139 | #define SPALL_BUFFER_PROFILE_BEGIN() 140 | #define SPALL_BUFFER_PROFILE_END(name) 141 | #endif 142 | 143 | SPALL_FN SPALL_FORCEINLINE bool spall__file_write(SpallProfile *ctx, const void *p, size_t n) { 144 | if (!ctx->data) return false; 145 | #ifdef SPALL_DEBUG 146 | if (feof((FILE *)ctx->data)) return false; 147 | if (ferror((FILE *)ctx->data)) return false; 148 | #endif 149 | 150 | if (fwrite(p, n, 1, (FILE *)ctx->data) != 1) return false; 151 | return true; 152 | } 153 | SPALL_FN bool spall__file_flush(SpallProfile *ctx) { 154 | if (!ctx->data) return false; 155 | if (fflush((FILE *)ctx->data)) return false; 156 | return true; 157 | } 158 | SPALL_FN void spall__file_close(SpallProfile *ctx) { 159 | if (!ctx->data) return; 160 | 161 | if (ctx->is_json) { 162 | #ifdef SPALL_DEBUG 163 | if (!feof((FILE *)ctx->data) && !ferror((FILE *)ctx->data)) 164 | #endif 165 | { 166 | fseek((FILE *)ctx->data, -2, SEEK_CUR); // seek back to overwrite trailing comma 167 | fwrite("\n]}\n", sizeof("\n]}\n") - 1, 1, (FILE *)ctx->data); 168 | } 169 | } 170 | fflush((FILE *)ctx->data); 171 | fclose((FILE *)ctx->data); 172 | ctx->data = NULL; 173 | } 174 | 175 | SPALL_FN SPALL_FORCEINLINE bool spall__buffer_flush(SpallProfile *ctx, SpallBuffer *wb) { 176 | // precon: wb 177 | // precon: wb->data 178 | // precon: wb->head <= wb->length 179 | // precon: !ctx || ctx->write 180 | #ifdef SPALL_DEBUG 181 | if (wb->ctx != ctx) return false; // Buffer must be bound to this context (or to NULL) 182 | #endif 183 | 184 | if (wb->head && ctx) { 185 | SPALL_BUFFER_PROFILE_BEGIN(); 186 | if (!ctx->write) return false; 187 | if (ctx->write == spall__file_write) { 188 | if (!spall__file_write(ctx, wb->data, wb->head)) return false; 189 | } else { 190 | if (!ctx->write(ctx, wb->data, wb->head)) return false; 191 | } 192 | SPALL_BUFFER_PROFILE_END("Buffer Flush"); 193 | } 194 | wb->head = 0; 195 | return true; 196 | } 197 | 198 | SPALL_FN SPALL_FORCEINLINE bool spall__buffer_write(SpallProfile *ctx, SpallBuffer *wb, void *p, size_t n) { 199 | // precon: !wb || wb->head < wb->length 200 | // precon: !ctx || ctx->write 201 | if (!wb) return ctx->write && ctx->write(ctx, p, n); 202 | #ifdef SPALL_DEBUG 203 | if (wb->ctx != ctx) return false; // Buffer must be bound to this context (or to NULL) 204 | #endif 205 | if (wb->head + n > wb->length && !spall__buffer_flush(ctx, wb)) return false; 206 | if (n > wb->length) { 207 | SPALL_BUFFER_PROFILE_BEGIN(); 208 | if (!ctx->write || !ctx->write(ctx, p, n)) return false; 209 | SPALL_BUFFER_PROFILE_END("Unbuffered Write"); 210 | return true; 211 | } 212 | memcpy((char *)wb->data + wb->head, p, n); 213 | wb->head += n; 214 | return true; 215 | } 216 | 217 | SPALL_FN bool spall_buffer_flush(SpallProfile *ctx, SpallBuffer *wb) { 218 | #ifdef SPALL_DEBUG 219 | if (!wb) return false; 220 | if (!wb->data) return false; 221 | #endif 222 | 223 | if (!spall__buffer_flush(ctx, wb)) return false; 224 | return true; 225 | } 226 | 227 | SPALL_FN bool spall_buffer_init(SpallProfile *ctx, SpallBuffer *wb) { 228 | if (!spall_buffer_flush(NULL, wb)) return false; 229 | wb->ctx = ctx; 230 | return true; 231 | } 232 | SPALL_FN bool spall_buffer_quit(SpallProfile *ctx, SpallBuffer *wb) { 233 | if (!spall_buffer_flush(ctx, wb)) return false; 234 | wb->ctx = NULL; 235 | return true; 236 | } 237 | 238 | SPALL_FN bool spall_buffer_abort(SpallBuffer *wb) { 239 | if (!wb) return false; 240 | wb->ctx = NULL; 241 | if (!spall__buffer_flush(NULL, wb)) return false; 242 | return true; 243 | } 244 | 245 | SPALL_FN size_t spall_build_header(void *buffer, size_t rem_size, double timestamp_unit) { 246 | size_t header_size = sizeof(SpallHeader); 247 | if (header_size > rem_size) { 248 | return 0; 249 | } 250 | 251 | SpallHeader *header = (SpallHeader *)buffer; 252 | header->magic_header = 0x0BADF00D; 253 | header->version = 1; 254 | header->timestamp_unit = timestamp_unit; 255 | header->must_be_0 = 0; 256 | return header_size; 257 | } 258 | SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, signed long name_len, const char *args, signed long args_len, double when, uint32_t tid, uint32_t pid) { 259 | SpallBeginEventMax *ev = (SpallBeginEventMax *)buffer; 260 | uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?) 261 | uint8_t trunc_args_len = (uint8_t)SPALL_MIN(args_len, 255); // will be interpreted as truncated in the app (?) 262 | 263 | size_t ev_size = sizeof(SpallBeginEvent) + trunc_name_len + trunc_args_len; 264 | if (ev_size > rem_size) { 265 | return 0; 266 | } 267 | 268 | ev->event.type = SpallEventType_Begin; 269 | ev->event.category = 0; 270 | ev->event.pid = pid; 271 | ev->event.tid = tid; 272 | ev->event.when = when; 273 | ev->event.name_length = trunc_name_len; 274 | ev->event.args_length = trunc_args_len; 275 | memcpy(ev->name_bytes, name, trunc_name_len); 276 | memcpy(ev->name_bytes + trunc_name_len, args, trunc_args_len); 277 | 278 | return ev_size; 279 | } 280 | SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, double when, uint32_t tid, uint32_t pid) { 281 | size_t ev_size = sizeof(SpallEndEvent); 282 | if (ev_size > rem_size) { 283 | return 0; 284 | } 285 | 286 | SpallEndEvent *ev = (SpallEndEvent *)buffer; 287 | ev->type = SpallEventType_End; 288 | ev->pid = pid; 289 | ev->tid = tid; 290 | ev->when = when; 291 | 292 | return ev_size; 293 | } 294 | 295 | SPALL_FN void spall_quit(SpallProfile *ctx) { 296 | if (!ctx) return; 297 | if (ctx->close) ctx->close(ctx); 298 | 299 | memset(ctx, 0, sizeof(*ctx)); 300 | } 301 | 302 | SPALL_FN SpallProfile spall_init_callbacks(double timestamp_unit, 303 | SpallWriteCallback write, 304 | SpallFlushCallback flush, 305 | SpallCloseCallback close, 306 | void *userdata, 307 | bool is_json) { 308 | SpallProfile ctx; 309 | memset(&ctx, 0, sizeof(ctx)); 310 | if (timestamp_unit < 0) return ctx; 311 | ctx.timestamp_unit = timestamp_unit; 312 | ctx.is_json = is_json; 313 | ctx.data = userdata; 314 | ctx.write = write; 315 | ctx.flush = flush; 316 | ctx.close = close; 317 | 318 | if (ctx.is_json) { 319 | if (!ctx.write(&ctx, "{\"traceEvents\":[\n", sizeof("{\"traceEvents\":[\n") - 1)) { spall_quit(&ctx); return ctx; } 320 | } else { 321 | SpallHeader header; 322 | size_t len = spall_build_header(&header, sizeof(header), timestamp_unit); 323 | if (!ctx.write(&ctx, &header, len)) { spall_quit(&ctx); return ctx; } 324 | } 325 | 326 | return ctx; 327 | } 328 | 329 | SPALL_FN SpallProfile spall_init_file_ex(const char *filename, double timestamp_unit, bool is_json) { 330 | SpallProfile ctx; 331 | memset(&ctx, 0, sizeof(ctx)); 332 | if (!filename) return ctx; 333 | ctx.data = fopen(filename, "wb"); // TODO: handle utf8 and long paths on windows 334 | if (ctx.data) { // basically freopen() but we don't want to force users to lug along another macro define 335 | fclose((FILE *)ctx.data); 336 | ctx.data = fopen(filename, "ab"); 337 | } 338 | if (!ctx.data) { spall_quit(&ctx); return ctx; } 339 | ctx = spall_init_callbacks(timestamp_unit, spall__file_write, spall__file_flush, spall__file_close, ctx.data, is_json); 340 | return ctx; 341 | } 342 | 343 | SPALL_FN SpallProfile spall_init_file (const char* filename, double timestamp_unit) { return spall_init_file_ex(filename, timestamp_unit, false); } 344 | SPALL_FN SpallProfile spall_init_file_json(const char* filename, double timestamp_unit) { return spall_init_file_ex(filename, timestamp_unit, true); } 345 | 346 | SPALL_FN bool spall_flush(SpallProfile *ctx) { 347 | #ifdef SPALL_DEBUG 348 | if (!ctx) return false; 349 | #endif 350 | 351 | if (!ctx->flush || !ctx->flush(ctx)) return false; 352 | return true; 353 | } 354 | 355 | SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, const char *args, signed long args_len, double when, uint32_t tid, uint32_t pid) { 356 | #ifdef SPALL_DEBUG 357 | if (!ctx) return false; 358 | if (!name) return false; 359 | if (name_len <= 0) return false; 360 | if (!wb) return false; 361 | #endif 362 | 363 | if (ctx->is_json) { 364 | char buf[1024]; 365 | int buf_len = snprintf(buf, sizeof(buf), 366 | "{\"ph\":\"B\",\"ts\":%f,\"pid\":%u,\"tid\":%u,\"name\":\"%.*s\",\"args\":\"%.*s\"},\n", 367 | when * ctx->timestamp_unit, pid, tid, (int)(uint8_t)name_len, name, (int)(uint8_t)args_len, args); 368 | if (buf_len <= 0) return false; 369 | if (buf_len >= sizeof(buf)) return false; 370 | if (!spall__buffer_write(ctx, wb, buf, buf_len)) return false; 371 | } else { 372 | if ((wb->head + sizeof(SpallBeginEventMax)) > wb->length) { 373 | if (!spall__buffer_flush(ctx, wb)) { 374 | return false; 375 | } 376 | } 377 | 378 | wb->head += spall_build_begin((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, args, args_len, when, tid, pid); 379 | } 380 | 381 | return true; 382 | } 383 | 384 | SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_ex(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, double when, uint32_t tid, uint32_t pid) { 385 | return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when, tid, pid); 386 | } 387 | 388 | SPALL_FN bool spall_buffer_begin(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, double when) { 389 | return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when, 0, 0); 390 | } 391 | 392 | SPALL_FN SPALL_FORCEINLINE bool spall_buffer_end_ex(SpallProfile *ctx, SpallBuffer *wb, double when, uint32_t tid, uint32_t pid) { 393 | #ifdef SPALL_DEBUG 394 | if (!ctx) return false; 395 | if (!wb) return false; 396 | #endif 397 | 398 | if (ctx->is_json) { 399 | char buf[512]; 400 | int buf_len = snprintf(buf, sizeof(buf), 401 | "{\"ph\":\"E\",\"ts\":%f,\"pid\":%u,\"tid\":%u},\n", 402 | when * ctx->timestamp_unit, pid, tid); 403 | if (buf_len <= 0) return false; 404 | if (buf_len >= sizeof(buf)) return false; 405 | if (!spall__buffer_write(ctx, wb, buf, buf_len)) return false; 406 | } else { 407 | if ((wb->head + sizeof(SpallEndEvent)) > wb->length) { 408 | if (!spall__buffer_flush(ctx, wb)) { 409 | return false; 410 | } 411 | } 412 | 413 | wb->head += spall_build_end((char *)wb->data + wb->head, wb->length - wb->head, when, tid, pid); 414 | } 415 | 416 | return true; 417 | } 418 | 419 | SPALL_FN bool spall_buffer_end(SpallProfile *ctx, SpallBuffer *wb, double when) { return spall_buffer_end_ex(ctx, wb, when, 0, 0); } 420 | 421 | SPALL_FN SPALL_FORCEINLINE void spall__buffer_profile(SpallProfile *ctx, SpallBuffer *wb, double spall_time_begin, double spall_time_end, const char *name, int name_len) { 422 | // precon: ctx 423 | // precon: ctx->write 424 | char temp_buffer_data[2048]; 425 | SpallBuffer temp_buffer = { temp_buffer_data, sizeof(temp_buffer_data) }; 426 | if (!spall_buffer_begin_ex(ctx, &temp_buffer, name, name_len, spall_time_begin, (uint32_t)(uintptr_t)wb->data, 4222222222)) return; 427 | if (!spall_buffer_end_ex(ctx, &temp_buffer, spall_time_end, (uint32_t)(uintptr_t)wb->data, 4222222222)) return; 428 | if (ctx->write) ctx->write(ctx, temp_buffer_data, temp_buffer.head); 429 | } 430 | 431 | #ifdef __cplusplus 432 | } 433 | #endif 434 | 435 | #endif // SPALL_H 436 | -------------------------------------------------------------------------------- /tests/main.odin: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "core:fmt" 4 | 5 | main :: proc() { 6 | fmt.printf("Hello, world!"); 7 | } -------------------------------------------------------------------------------- /unity.c: -------------------------------------------------------------------------------- 1 | // Unity build script. Just compile this with cl.exe on Windows 2 | #include "src/allocator_arena.c" 3 | #include "src/allocator_null.c" 4 | #include "src/allocator_std.c" 5 | #include "src/allocator.c" 6 | #include "src/array.c" 7 | #include "src/build.c" 8 | #include "src/context.c" 9 | #include "src/dump.c" 10 | #include "src/lexer.c" 11 | #include "src/main.c" 12 | #include "src/parser.c" 13 | #include "src/path.c" 14 | #include "src/profiler_null.c" 15 | #include "src/profiler_spall.c" 16 | #include "src/profiler.c" 17 | #include "src/project.c" 18 | #include "src/report.c" 19 | #include "src/sched_async.c" 20 | #include "src/sched_null.c" 21 | #include "src/sched_sync.c" 22 | #include "src/sched.c" 23 | #include "src/strbuf.c" 24 | #include "src/string.c" 25 | #include "src/thread.c" 26 | #include "src/threadpool.c" 27 | #include "src/tree.c" 28 | #include "src/utility.c" --------------------------------------------------------------------------------