start work on new interpreter
This commit is contained in:
parent
7efa99d064
commit
2ac2f85512
15 changed files with 780 additions and 30 deletions
26
next/core/arena.c
Normal file
26
next/core/arena.c
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include <growl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
97
next/core/callable.c
Normal file
97
next/core/callable.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include <growl.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
2
next/core/compiler.c
Normal file
2
next/core/compiler.c
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include <growl.h>
|
||||
|
||||
169
next/core/gc.c
Normal file
169
next/core/gc.c
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// Created by lobo on 2/5/26.
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <growl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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; }
|
||||
2
next/core/list.c
Normal file
2
next/core/list.c
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include <growl.h>
|
||||
|
||||
12
next/core/opcodes.h
Normal file
12
next/core/opcodes.h
Normal file
|
|
@ -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
|
||||
45
next/core/sleb128.c
Normal file
45
next/core/sleb128.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
10
next/core/sleb128.h
Normal file
10
next/core/sleb128.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef GROWL_SLEB128_H
|
||||
#define GROWL_SLEB128_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
intptr_t growl_sleb128_decode(uint8_t **ptr);
|
||||
size_t growl_sleb128_peek(const uint8_t *ptr, intptr_t *out);
|
||||
|
||||
#endif // GROWL_SLEB128_H
|
||||
33
next/core/string.c
Normal file
33
next/core/string.c
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include <growl.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
10
next/core/tuple.c
Normal file
10
next/core/tuple.c
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include <growl.h>
|
||||
|
||||
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);
|
||||
}
|
||||
164
next/core/vm.c
Normal file
164
next/core/vm.c
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#include <growl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdnoreturn.h>
|
||||
|
||||
#include "opcodes.h"
|
||||
#include "sleb128.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue