From 2ac2f85512594aa4f14c747fabbcf85ffccfc3cd Mon Sep 17 00:00:00 2001 From: "Javier B. Torres" Date: Thu, 5 Feb 2026 22:34:48 -0300 Subject: [PATCH] start work on new interpreter --- .editorconfig | 2 +- meson.build | 85 ++++++++++++++-------- next/core/arena.c | 26 +++++++ next/core/callable.c | 97 +++++++++++++++++++++++++ next/core/compiler.c | 2 + next/core/gc.c | 169 +++++++++++++++++++++++++++++++++++++++++++ next/core/list.c | 2 + next/core/opcodes.h | 12 +++ next/core/sleb128.c | 45 ++++++++++++ next/core/sleb128.h | 10 +++ next/core/string.c | 33 +++++++++ next/core/tuple.c | 10 +++ next/core/vm.c | 164 +++++++++++++++++++++++++++++++++++++++++ next/include/growl.h | 135 ++++++++++++++++++++++++++++++++++ next/main.c | 18 +++++ 15 files changed, 780 insertions(+), 30 deletions(-) create mode 100644 next/core/arena.c create mode 100644 next/core/callable.c create mode 100644 next/core/compiler.c create mode 100644 next/core/gc.c create mode 100644 next/core/list.c create mode 100644 next/core/opcodes.h create mode 100644 next/core/sleb128.c create mode 100644 next/core/sleb128.h create mode 100644 next/core/string.c create mode 100644 next/core/tuple.c create mode 100644 next/core/vm.c create mode 100644 next/include/growl.h create mode 100644 next/main.c diff --git a/.editorconfig b/.editorconfig index 7ecbc3d..7c14f37 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,4 +10,4 @@ indent_size = 2 [meson.build] indent_style = space -indent_size = 2 +indent_size = 4 diff --git a/meson.build b/meson.build index d3141f1..90cb202 100644 --- a/meson.build +++ b/meson.build @@ -1,38 +1,65 @@ project( - 'growl', - 'c', - meson_version : '>= 1.3.0', - version : '0.1', - default_options : ['buildtype=debugoptimized', 'c_std=gnu11', 'warning_level=3'], + 'growl', + 'c', + 'cpp', + meson_version: '>= 1.3.0', + version: '0.1', + default_options: [ + 'buildtype=debugoptimized', + 'c_std=gnu11', + 'cpp_std=c++20', + 'warning_level=3', + ], ) libutf = subproject('libutf') libutf_dep = libutf.get_variable('libutf_dep') -sources = [ - 'src/arena.c', - 'src/chunk.c', - 'src/compile.c', - 'src/debug.c', - 'src/dictionary.c', - 'src/file.c', - 'src/lexer.c', - 'src/object.c', - 'src/gc.c', - 'src/parser.c', - 'src/primitive.c', - 'src/print.c', - 'src/stream.c', - 'src/string.c', - 'src/userdata.c', - 'src/vm.c', - 'src/vendor/linenoise.c', - 'src/vendor/yar.c', +growl_sources = [ + 'src/arena.c', + 'src/chunk.c', + 'src/compile.c', + 'src/debug.c', + 'src/dictionary.c', + 'src/file.c', + 'src/lexer.c', + 'src/object.c', + 'src/gc.c', + 'src/parser.c', + 'src/primitive.c', + 'src/print.c', + 'src/stream.c', + 'src/string.c', + 'src/userdata.c', + 'src/vm.c', + 'src/vendor/linenoise.c', + 'src/vendor/yar.c', ] -exe = executable( - 'growl', - 'src/main.c', sources, - dependencies : [libutf_dep], - install : true, +growl = executable( + 'growl', + 'src/main.c', + growl_sources, + dependencies: [libutf_dep], + install: true, +) + +growlnext_sources = [ + 'next/core/arena.c', + 'next/core/callable.c', + 'next/core/compiler.c', + 'next/core/gc.c', + 'next/core/list.c', + 'next/core/sleb128.c', + 'next/core/string.c', + 'next/core/tuple.c', + 'next/core/vm.c', + 'next/main.c', +] + +growlnext = executable( + 'growlnext', + growlnext_sources, + include_directories: ['next/include'], + install: true, ) diff --git a/next/core/arena.c b/next/core/arena.c new file mode 100644 index 0000000..c7b6247 --- /dev/null +++ b/next/core/arena.c @@ -0,0 +1,26 @@ +#include +#include +#include + +void growl_arena_init(GrowlGCArena *arena, size_t size) { + arena->start = arena->free = malloc(size); + if (arena->start == NULL) + abort(); + arena->end = arena->start + size; +} + +void growl_arena_free(GrowlGCArena *arena) { + free(arena->start); + arena->start = arena->end = arena->free = NULL; +} + +void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align, + size_t count) { + ptrdiff_t padding = -(uintptr_t)arena->start & (align - 1); + ptrdiff_t available = arena->end - arena->start - padding; + if (available < 0 || count > available / size) + abort(); + void *p = arena->start + padding; + arena->start += padding + count * size; + return memset(p, 0, count * size); +} diff --git a/next/core/callable.c b/next/core/callable.c new file mode 100644 index 0000000..71052ba --- /dev/null +++ b/next/core/callable.c @@ -0,0 +1,97 @@ +#include +#include + +int growl_callable(Growl obj) { + if (obj == GROWL_NIL || GROWL_IMM(obj)) + return 0; + GrowlObjectHeader *hdr = GROWL_UNBOX(obj); + switch (hdr->type) { + case GROWL_QUOTATION: + case GROWL_COMPOSE: + case GROWL_CURRY: + return 1; + default: + return 0; + } +} + +Growl growl_make_quotation(GrowlVM *vm, const uint8_t *code, size_t code_size, + const Growl *constants, size_t constants_size) { + size_t constants_obj_size = sizeof(GrowlObjectHeader) + sizeof(GrowlTuple) + + constants_size * sizeof(Growl); + GrowlObjectHeader *constants_hdr = + growl_gc_alloc_tenured(vm, constants_obj_size); + constants_hdr->type = GROWL_TUPLE; + GrowlTuple *constants_tuple = (GrowlTuple *)(constants_hdr + 1); + + constants_tuple->count = constants_size; + for (size_t i = 0; i < constants_size; ++i) { + constants_tuple->data[i] = constants[i]; + } + + size_t quotation_obj_size = + sizeof(GrowlObjectHeader) + sizeof(GrowlQuotation) + code_size; + GrowlObjectHeader *quotation_hdr = + growl_gc_alloc_tenured(vm, quotation_obj_size); + quotation_hdr->type = GROWL_QUOTATION; + GrowlQuotation *quotation = (GrowlQuotation *)(quotation_hdr + 1); + + quotation->constants = GROWL_BOX(constants_hdr); + quotation->count = code_size; + memcpy(quotation->data, code, code_size); + + return GROWL_BOX(quotation_hdr); +} + +GrowlQuotation *growl_unwrap_quotation(Growl obj) { + if (obj == GROWL_NIL || GROWL_IMM(obj)) + return NULL; + GrowlObjectHeader *hdr = GROWL_UNBOX(obj); + if (hdr->type != GROWL_QUOTATION) + return NULL; + return (GrowlQuotation *)(hdr + 1); +} + +Growl growl_compose(GrowlVM *vm, Growl first, Growl second) { + if (!growl_callable(first)) + return GROWL_NIL; + if (!growl_callable(second)) + return GROWL_NIL; + size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCompose); + GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); + hdr->type = GROWL_COMPOSE; + GrowlCompose *comp = (GrowlCompose *)(hdr + 1); + comp->first = first; + comp->second = second; + return GROWL_BOX(hdr); +} + +GrowlCompose *growl_unwrap_compose(Growl obj) { + if (obj == GROWL_NIL || GROWL_IMM(obj)) + return NULL; + GrowlObjectHeader *hdr = GROWL_UNBOX(obj); + if (hdr->type != GROWL_COMPOSE) + return NULL; + return (GrowlCompose *)(hdr + 1); +} + +Growl growl_curry(GrowlVM *vm, Growl value, Growl callable) { + if (!growl_callable(callable)) + return GROWL_NIL; + size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCurry); + GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); + hdr->type = GROWL_CURRY; + GrowlCurry *comp = (GrowlCurry *)(hdr + 1); + comp->value = value; + comp->callable = callable; + return GROWL_BOX(hdr); +} + +GrowlCurry *growl_unwrap_curry(Growl obj) { + if (obj == GROWL_NIL || GROWL_IMM(obj)) + return NULL; + GrowlObjectHeader *hdr = GROWL_UNBOX(obj); + if (hdr->type != GROWL_CURRY) + return NULL; + return (GrowlCurry *)(hdr + 1); +} diff --git a/next/core/compiler.c b/next/core/compiler.c new file mode 100644 index 0000000..db7f5ca --- /dev/null +++ b/next/core/compiler.c @@ -0,0 +1,2 @@ +#include + diff --git a/next/core/gc.c b/next/core/gc.c new file mode 100644 index 0000000..9e17c1f --- /dev/null +++ b/next/core/gc.c @@ -0,0 +1,169 @@ +// +// Created by lobo on 2/5/26. +// + +#include +#include +#include +#include +#include +#include + +#define ALIGN(n) (((n) + 7) & ~7) + +static int in_from(GrowlVM *vm, void *ptr) { + const uint8_t *x = ptr; + return (x >= vm->from.start && x < vm->from.end); +} + +static Growl copy(GrowlVM *vm, GrowlObjectHeader *hdr) { + assert(in_from(vm, hdr)); + assert(hdr->type != UINT32_MAX); + size_t size = ALIGN(hdr->size); + GrowlObjectHeader *new = (GrowlObjectHeader *)vm->to.free; + vm->to.free += size; + memcpy(new, hdr, size); + hdr->type = UINT32_MAX; + Growl *obj = (Growl *)(hdr + 1); + *obj = (Growl)(new); + return *obj; +} + +static Growl forward(GrowlVM *vm, Growl obj) { + if (obj == 0) + return 0; + if (!in_from(vm, (void *)obj)) + return obj; + + GrowlObjectHeader *hdr = (GrowlObjectHeader *)obj; + if (hdr->type == UINT32_MAX) { + Growl *fwd = (Growl *)(hdr + 1); + return *fwd; + } + return copy(vm, hdr); +} + +GrowlObjectHeader *growl_gc_alloc(GrowlVM *vm, size_t size) { + size = ALIGN(size); + if (vm->from.free + size > vm->from.end) { + growl_gc_collect(vm); + if (vm->from.free + size > vm->from.end) { + fprintf(stderr, "gc: oom (requested %" PRIdPTR " bytes)\n", size); + abort(); + } + } + GrowlObjectHeader *hdr = (GrowlObjectHeader *)vm->from.free; + vm->from.free += size; + hdr->size = size; + return hdr; +} + +GrowlObjectHeader *growl_gc_alloc_tenured(GrowlVM *vm, size_t size) { + size = ALIGN(size); + GrowlObjectHeader *hdr = growl_arena_alloc(&vm->arena, size, 8, 1); + hdr->size = size; + return hdr; +} + +static void scan(GrowlVM *vm, GrowlObjectHeader *hdr) { + switch (hdr->type) { + case GROWL_STRING: + break; + case GROWL_LIST: { + GrowlList *list = (GrowlList *)(hdr + 1); + list->head = forward(vm, list->head); + list->tail = forward(vm, list->tail); + break; + } + case GROWL_TUPLE: { + GrowlTuple *tuple = (GrowlTuple *)(hdr + 1); + for (size_t i = 0; i < tuple->count; ++i) { + tuple->data[i] = forward(vm, tuple->data[i]); + } + break; + } + case GROWL_QUOTATION: { + GrowlQuotation *quot = (GrowlQuotation *)(hdr + 1); + quot->constants = forward(vm, quot->constants); + break; + } + case GROWL_COMPOSE: { + GrowlCompose *comp = (GrowlCompose *)(hdr + 1); + comp->first = forward(vm, comp->first); + comp->second = forward(vm, comp->second); + break; + } + case GROWL_CURRY: { + GrowlCurry *comp = (GrowlCurry *)(hdr + 1); + comp->value = forward(vm, comp->value); + comp->callable = forward(vm, comp->callable); + break; + } + case UINT32_MAX: + fprintf(stderr, "gc: fwd pointer during scan\n"); + abort(); + default: + fprintf(stderr, "gc: junk object type %" PRIu32 "\n", hdr->type); + abort(); + } +} + +static void gc_print_stats(GrowlVM *vm, const char *label) { + size_t used = vm->from.free - vm->from.start; + size_t total = vm->from.end - vm->from.start; + fprintf(stderr, "[%s] used=%zu/%zu bytes (%.1f%%)\n", label, used, total, + (double)used / (double)total * 100.0); +} + +void growl_gc_collect(GrowlVM *vm) { + uint8_t *gc_scan = vm->to.free; + + gc_print_stats(vm, "before GC"); + + for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { + vm->wst[i] = forward(vm, vm->wst[i]); + } + + for (size_t i = 0; i < vm->root_count; ++i) { + *vm->roots[i] = forward(vm, *vm->roots[i]); + } + + uint8_t *arena_scan = vm->arena.start; + while (arena_scan < vm->arena.free) { + GrowlObjectHeader *hdr = (GrowlObjectHeader *)arena_scan; + scan(vm, hdr); + arena_scan += ALIGN(hdr->size); + } + + while (gc_scan < vm->to.free) { + GrowlObjectHeader *hdr = (GrowlObjectHeader *)gc_scan; + scan(vm, hdr); + gc_scan += ALIGN(hdr->size); + } + + GrowlGCArena tmp = vm->from; + vm->from = vm->to; + vm->to = tmp; + vm->to.free = vm->to.start; + vm->scratch.free = vm->scratch.start; + + gc_print_stats(vm, "after GC"); +} + +void growl_gc_root(GrowlVM *vm, Growl *ptr) { + if (vm->root_count >= vm->root_capacity) { + size_t cap = vm->root_capacity == 0 ? 16 : vm->root_capacity * 2; + Growl **data = realloc(vm->roots, cap * sizeof(Growl *)); + if (!data) { + fprintf(stderr, "expanding roots array: oom\n"); + abort(); + } + vm->root_capacity = cap; + vm->roots = data; + } + vm->roots[vm->root_count++] = ptr; +} + +size_t growl_gc_mark(GrowlVM *vm) { return vm->root_count; } + +void growl_gc_reset(GrowlVM *vm, size_t mark) { vm->root_count = mark; } diff --git a/next/core/list.c b/next/core/list.c new file mode 100644 index 0000000..db7f5ca --- /dev/null +++ b/next/core/list.c @@ -0,0 +1,2 @@ +#include + diff --git a/next/core/opcodes.h b/next/core/opcodes.h new file mode 100644 index 0000000..0d3eca1 --- /dev/null +++ b/next/core/opcodes.h @@ -0,0 +1,12 @@ +#ifndef GROWL_OPCODES_H +#define GROWL_OPCODES_H + +enum { + GOP_NOP = 0, + GOP_PUSH_NIL, + GOP_PUSH_CONSTANT, + GOP_CALL, + GOP_RETURN, +}; + +#endif // GROWL_OPCODES_H diff --git a/next/core/sleb128.c b/next/core/sleb128.c new file mode 100644 index 0000000..0f41aad --- /dev/null +++ b/next/core/sleb128.c @@ -0,0 +1,45 @@ +// +// Created by lobo on 2/5/26. +// + +#include "sleb128.h" + +intptr_t growl_sleb128_decode(uint8_t **ptr) { + intptr_t result = 0; + intptr_t shift = 0; + uint8_t byte; + + do { + byte = **ptr; + (*ptr)++; + result |= (intptr_t)(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + + if ((shift < 64) && (byte & 0x40)) { + result |= -(1LL << shift); + } + + return result; +} + +size_t growl_sleb128_peek(const uint8_t *ptr, intptr_t *out) { + intptr_t result = 0, shift = 0; + size_t bytes = 0; + uint8_t byte; + + do { + byte = ptr[bytes]; + bytes++; + result |= (intptr_t)(byte & 0x7f) << shift; + shift += 7; + } while (byte & 0x80); + + if (shift < 64 && byte & 0x40) { + result |= -(1LL << shift); + } + + if (out) + *out = result; + return bytes; +} diff --git a/next/core/sleb128.h b/next/core/sleb128.h new file mode 100644 index 0000000..b866f6d --- /dev/null +++ b/next/core/sleb128.h @@ -0,0 +1,10 @@ +#ifndef GROWL_SLEB128_H +#define GROWL_SLEB128_H + +#include +#include + +intptr_t growl_sleb128_decode(uint8_t **ptr); +size_t growl_sleb128_peek(const uint8_t *ptr, intptr_t *out); + +#endif // GROWL_SLEB128_H diff --git a/next/core/string.c b/next/core/string.c new file mode 100644 index 0000000..41bf42a --- /dev/null +++ b/next/core/string.c @@ -0,0 +1,33 @@ +#include +#include + +Growl growl_make_string(GrowlVM *vm, size_t len) { + size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len; + GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); + hdr->type = GROWL_STRING; + GrowlString *str = (GrowlString *)(hdr + 1); + str->len = len; + memset(str->data, 0, len); + return GROWL_BOX(hdr); +} + +Growl growl_wrap_string(GrowlVM *vm, const char *cstr) { + size_t len = strlen(cstr); + size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len + 1; + GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); + hdr->type = GROWL_STRING; + GrowlString *str = (GrowlString *)(hdr + 1); + str->len = len; + memcpy(str->data, cstr, len); + str->data[len] = 0; + return GROWL_BOX(hdr); +} + +GrowlString *growl_unwrap_string(Growl obj) { + if (obj == 0 || GROWL_IMM(obj)) + return NULL; + GrowlObjectHeader *hdr = GROWL_UNBOX(obj); + if (hdr->type != GROWL_STRING) + return NULL; + return (GrowlString *)(hdr + 1); +} diff --git a/next/core/tuple.c b/next/core/tuple.c new file mode 100644 index 0000000..f38e0b5 --- /dev/null +++ b/next/core/tuple.c @@ -0,0 +1,10 @@ +#include + +GrowlTuple *growl_unwrap_tuple(Growl obj) { + if (obj == 0 || GROWL_IMM(obj)) + return NULL; + GrowlObjectHeader *hdr = GROWL_UNBOX(obj); + if (hdr->type != GROWL_TUPLE) + return NULL; + return (GrowlTuple *)(hdr + 1); +} diff --git a/next/core/vm.c b/next/core/vm.c new file mode 100644 index 0000000..49d94d7 --- /dev/null +++ b/next/core/vm.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +#include "opcodes.h" +#include "sleb128.h" + +#include +#include + +GrowlVM *growl_vm_init(void) { + GrowlVM *mem = malloc(sizeof(GrowlVM)); + if (mem == NULL) { + abort(); + } + + growl_arena_init(&mem->from, GROWL_HEAP_SIZE); + growl_arena_init(&mem->to, GROWL_HEAP_SIZE); + growl_arena_init(&mem->arena, GROWL_ARENA_SIZE); + growl_arena_init(&mem->scratch, GROWL_SCRATCH_SIZE); + + mem->sp = mem->wst; + mem->rsp = mem->rst; + mem->csp = mem->cst; + + for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { + mem->wst[i] = 0; + mem->rst[i] = 0; + } + + mem->roots = NULL; + mem->root_count = 0; + mem->root_capacity = 0; + + return mem; +} + +void growl_vm_free(GrowlVM *vm) { + growl_arena_free(&vm->from); + growl_arena_free(&vm->to); + growl_arena_free(&vm->arena); + growl_arena_free(&vm->scratch); + if (vm->roots != NULL) + free(vm->roots); + free(vm); +} + +__attribute__((format(printf, 2, 3))) static noreturn void +vm_error(GrowlVM *vm, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + fprintf(stderr, "vm: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + longjmp(vm->error, -1); +} + +void growl_push(GrowlVM *vm, Growl obj) { + if (vm->sp >= vm->wst + GROWL_STACK_SIZE) + vm_error(vm, "work stack overflow"); + *vm->sp++ = obj; +} + +Growl growl_pop(GrowlVM *vm) { + if (vm->sp <= vm->wst) + vm_error(vm, "work stack underflow"); + Growl obj = *--vm->sp; + *vm->sp = GROWL_NIL; + return obj; +} + +void growl_rpush(GrowlVM *vm, Growl obj) { + if (vm->rsp >= vm->rst + GROWL_STACK_SIZE) + vm_error(vm, "work stack overflow"); + *vm->rsp++ = obj; +} + +Growl growl_rpop(GrowlVM *vm) { + if (vm->rsp <= vm->rst) + vm_error(vm, "work stack underflow"); + Growl obj = *--vm->rsp; + *vm->rsp = GROWL_NIL; + return obj; +} + +static void push_call(GrowlVM *vm, GrowlQuotation *q, uint8_t *ip) { + if (vm->csp >= vm->cst + GROWL_CALL_STACK_SIZE) + vm_error(vm, "call stack overflow"); + vm->csp->quot = q; + vm->csp->ip = ip; + vm->csp++; +} +static GrowlFrame pop_call(GrowlVM *vm) { + if (vm->csp <= vm->cst) + vm_error(vm, "call stack underflow"); + return *--vm->csp; +} + +int vm_doquot(GrowlVM *vm, GrowlQuotation *quot) { + size_t gc_mark = growl_gc_mark(vm); + int result = setjmp(vm->error); + + if (result != 0) { + growl_gc_reset(vm, gc_mark); + return result; + } + + GrowlTuple *constants = growl_unwrap_tuple(quot->constants); + if (constants != NULL) { + for (size_t i = 0; i < constants->count; ++i) { + growl_gc_root(vm, &constants->data[i]); + } + } + + vm->ip = quot->data; + vm->quotation = quot; + + for (;;) { + uint8_t opcode; + switch (opcode = *vm->ip++) { + case GOP_NOP: + break; + case GOP_PUSH_NIL: + growl_push(vm, GROWL_NIL); + break; + case GOP_PUSH_CONSTANT: { + intptr_t idx = growl_sleb128_decode(&vm->ip); + if (constants != NULL) { + growl_push(vm, constants->data[idx]); + } else { + vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx); + } + break; + case GOP_CALL: { // TODO: compose and curry + Growl obj = growl_pop(vm); + push_call(vm, vm->quotation, vm->ip); + GrowlQuotation *obj_quot = growl_unwrap_quotation(obj); + if (obj_quot == NULL) + vm_error(vm, "attempt to call non-callable"); + vm->quotation = obj_quot; + vm->ip = obj_quot->data; + break; + } + case GOP_RETURN: + if (vm->csp != vm->cst) { + GrowlFrame frame = pop_call(vm); + vm->quotation = frame.quot; + vm->ip = frame.ip; + } else { + goto done; + } + break; + } + default: + vm_error(vm, "unknown opcode %d", opcode); + } + } + +done: + growl_gc_reset(vm, gc_mark); + return 0; +} diff --git a/next/include/growl.h b/next/include/growl.h new file mode 100644 index 0000000..0bda72a --- /dev/null +++ b/next/include/growl.h @@ -0,0 +1,135 @@ +#ifndef GROWL_H +#define GROWL_H + +#include +#include +#include + +typedef uintptr_t Growl; + +#define GROWL_NIL ((Growl)(0)) +#define GROWL_BOX(x) ((Growl)(x)) +#define GROWL_UNBOX(x) ((GrowlObjectHeader *)(x)) +#define GROWL_IMM(x) ((Growl)(x) & (Growl)1) +#define GROWL_NUM(x) (((Growl)((intptr_t)(x) << 1)) | (Growl)1) +#define GROWL_ORD(x) ((intptr_t)(x) >> 1) + +typedef struct GrowlObjectHeader GrowlObjectHeader; +typedef struct GrowlString GrowlString; +typedef struct GrowlList GrowlList; +typedef struct GrowlTuple GrowlTuple; +typedef struct GrowlQuotation GrowlQuotation; +typedef struct GrowlCompose GrowlCompose; +typedef struct GrowlCurry GrowlCurry; +typedef struct GrowlGCArena GrowlGCArena; +typedef struct GrowlFrame GrowlFrame; +typedef struct GrowlVM GrowlVM; + +enum { + GROWL_STRING, + GROWL_LIST, + GROWL_TUPLE, + GROWL_QUOTATION, + GROWL_COMPOSE, + GROWL_CURRY, +}; + +struct GrowlObjectHeader { + size_t size; + uint32_t type; +}; + +struct GrowlString { + size_t len; + char data[]; +}; + +Growl growl_make_string(GrowlVM *vm, size_t len); +Growl growl_wrap_string(GrowlVM *vm, const char *cstr); +GrowlString *growl_unwrap_string(Growl obj); + +struct GrowlList { + Growl head, tail; +}; + +struct GrowlTuple { + size_t count; + Growl data[]; +}; + +GrowlTuple *growl_unwrap_tuple(Growl obj); + +struct GrowlQuotation { + size_t count; + Growl constants; + uint8_t data[]; +}; + +struct GrowlCompose { + Growl first, second; +}; + +struct GrowlCurry { + Growl value, callable; +}; + +int growl_callable(Growl obj); +Growl growl_make_quotation(GrowlVM *vm, const uint8_t *code, size_t code_size, + const Growl *constants, size_t constants_size); +GrowlQuotation *growl_unwrap_quotation(Growl obj); +Growl growl_compose(GrowlVM *vm, Growl first, Growl second); +GrowlCompose *growl_unwrap_compose(Growl obj); +Growl growl_curry(GrowlVM *vm, Growl value, Growl callable); +GrowlCurry *growl_unwrap_curry(Growl obj); + +struct GrowlGCArena { + uint8_t *start, *end; + uint8_t *free; +}; + +void growl_arena_init(GrowlGCArena *arena, size_t size); +void growl_arena_free(GrowlGCArena *arena); +void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align, + size_t count); +#define growl_arena_new(a, t, n) \ + (t *)growl_arena_alloc(a, sizeof(t), _Alignof(t), n) + +#define GROWL_STACK_SIZE 128 +#define GROWL_CALL_STACK_SIZE 64 +#define GROWL_HEAP_SIZE (4 * 1024 * 1024) +#define GROWL_ARENA_SIZE (2 * 1024 * 1024) +#define GROWL_SCRATCH_SIZE (1024 * 1024) + +struct GrowlFrame { + GrowlQuotation *quot; + uint8_t *ip; +}; + +struct GrowlVM { + GrowlGCArena from, to; + GrowlGCArena arena; + GrowlGCArena scratch; + + GrowlQuotation *quotation; + uint8_t *ip; + Growl wst[GROWL_STACK_SIZE], *sp; + Growl rst[GROWL_STACK_SIZE], *rsp; + GrowlFrame cst[GROWL_CALL_STACK_SIZE], *csp; + + Growl **roots; + size_t root_count, root_capacity; + + jmp_buf error; +}; + +GrowlVM *growl_vm_init(void); +void growl_vm_free(GrowlVM *vm); +GrowlObjectHeader *growl_gc_alloc(GrowlVM *vm, size_t size); +GrowlObjectHeader *growl_gc_alloc_tenured(GrowlVM *vm, size_t size); +void growl_gc_collect(GrowlVM *vm); +void growl_gc_root(GrowlVM *vm, Growl *ptr); +size_t growl_gc_mark(GrowlVM *vm); +void growl_gc_reset(GrowlVM *vm, size_t mark); +int vm_doquot(GrowlVM *vm, GrowlQuotation *quot); + +#endif // GROWL_H diff --git a/next/main.c b/next/main.c new file mode 100644 index 0000000..195abf3 --- /dev/null +++ b/next/main.c @@ -0,0 +1,18 @@ +#include "core/opcodes.h" +#include + +static uint8_t code[] = { + GOP_PUSH_NIL, + GOP_RETURN, +}; + +int main(void) { + GrowlVM *vm = growl_vm_init(); + + Growl quot_obj = growl_make_quotation(vm, code, sizeof(code), NULL, 0); + GrowlQuotation *quot = (GrowlQuotation *)(GROWL_UNBOX(quot_obj) + 1); + vm_doquot(vm, quot); + + growl_gc_collect(vm); + growl_vm_free(vm); +}