Newer
Older
Tardis / lang / vm.c
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 John Watts and the LuminaSensum contributors

#include "vm.h"
#include "error.h"
#include "object.h"
#include <stddef.h>
#include <stdlib.h>

struct vm_state {
	Object *stack;
	int stack_base;
	int stack_next;
	int stack_size;
	Object *tail_obj;
	const char *tail_name;
};

VmState vm_create(void) {
	struct vm_state *state = malloc(sizeof(struct vm_state));
	abort_if(state, state == NULL, "vm_create: unable to allocate state");
	state->stack_size = 16;
	state->stack_base = 0;
	state->stack_next = 0;
	state->tail_obj = object_none();
	state->tail_name = NULL;
	state->stack = malloc(sizeof(Object) * state->stack_size);
	abort_if(state, state->stack == NULL,
		"vm_create: unable to allocate stack");
	return (VmState)state;
}

void vm_destroy(VmState *state) {
	struct vm_state **priv = (struct vm_state **)state;
	free((*priv)->stack);
	free(*priv);
}

void vm_stack_set(VmState state, int index, Object obj) {
	struct vm_state *priv = (struct vm_state *)state;
	int offset = priv->stack_base + index;
	abort_if(state, index < 0, "vm_stack_set: negative value");
	abort_if(state, offset >= priv->stack_next,
		"vm_stack_set: stack overflow");
	Object *stack_pos = &priv->stack[offset];
	object_drop(state, stack_pos);
	*stack_pos = obj;
}

Object vm_stack_get(VmState state, int index) {
	struct vm_state *priv = (struct vm_state *)state;
	int offset = priv->stack_base + index;
	abort_if(state, index < 0, "vm_stack_get: negative value");
	abort_if(state, offset >= priv->stack_next,
		"vm_stack_get: stack overflow");
	Object obj = priv->stack[offset];
	object_hold(state, obj);
	return obj;
}

void vm_stack_push(VmState state, Object obj) {
	struct vm_state *priv = (struct vm_state *)state;
	// stack_next starts at 0, stack_size starts at 1
	// Convert stack_size to a max stack offset to compensate
	int max_stack = priv->stack_size - 1;
	// Check if stack_next is outside the stack before
	// writing to it
	abort_if(state, priv->stack_next > max_stack,
		"vm_stack_push: stack overflow");
	priv->stack[priv->stack_next++] = obj;
}

Object vm_stack_pop(VmState state) {
	struct vm_state *priv = (struct vm_state *)state;
	abort_if(state, priv->stack_next <= priv->stack_base,
		"vm_stack_pop: stack base underflow");
	Object obj = priv->stack[--priv->stack_next];
	priv->stack[priv->stack_next] = object_none();
	return obj;
}

void vm_stack_drop(VmState state, int count) {
	abort_if(state, count < 1, "vm_stack_drop: invalid drop count");
	while (count--) {
		Object obj = vm_stack_pop(state);
		object_drop(state, &obj);
	}
}

int vm_stack_depth(VmState state) {
	struct vm_state *priv = (struct vm_state *)state;
	return priv->stack_next - priv->stack_base;
}

void vm_call(VmState state, Object obj, const char *name, int arg_count) {
	struct vm_state *priv = (struct vm_state *)state;
	int old_base = priv->stack_base;
	priv->stack_base = priv->stack_next - arg_count;
	object_hold(state, obj);
	do {
		dispatch_call(priv, obj, name);
		object_drop(state, &obj);
		obj = priv->tail_obj;
		name = priv->tail_name;
		priv->tail_obj = object_none();
		priv->tail_name = NULL;
	} while (obj != object_none());
	priv->stack_base = old_base;
}

void vm_tailcall(VmState state, Object obj, const char *name) {
	struct vm_state *priv = (struct vm_state *)state;
	object_hold(state, obj);
	priv->tail_obj = obj;
	priv->tail_name = name;
}