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

#define DEBUG_MSG 0 // Change to 1 for debug logging

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

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

VmState vm_create(void) {
	struct vm_state *state = calloc(sizeof(struct vm_state), 1);
	if (state == NULL)
		abort_print("vm_create: unable to allocate state");
	state->stack_base = 0;
	state->stack_next = 0;
	state->tail_obj = object_none();
	state->tail_name = NULL;
	state->stack = object_list_create(state, 16);
	state->stack_trace = stacktrace_create(32);
	return (VmState)state;
}

void vm_destroy(VmState *state) {
	object_drop(*state, &(*state)->stack);
	free((*state)->stack_trace);
	free(*state);
}

void vm_stack_set(VmState state, int index, Object obj) {
	int offset = state->stack_base + index;
	vm_abort_if(state, index < 0, "vm_stack_set: negative value");
	vm_abort_if(state, offset >= state->stack_next,
		"vm_stack_set: stack overflow");
	object_list_set(state, state->stack, offset, obj);
}

Object vm_stack_get(VmState state, int index) {
	int offset = state->stack_base + index;
	vm_abort_if(state, index < 0, "vm_stack_get: negative value");
	vm_abort_if(state, offset >= state->stack_next,
		"vm_stack_get: stack overflow");
	Object obj = object_list_get(state, state->stack, offset);
	return obj;
}

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

Object vm_stack_pop(VmState state) {
	vm_abort_if(state, state->stack_next <= state->stack_base,
		"vm_stack_pop: stack base underflow");
	Object obj = object_list_get(state, state->stack, --state->stack_next);
	object_list_set(state, state->stack, state->stack_next, object_none());
	return obj;
}

void vm_stack_drop(VmState state, int count) {
	vm_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) {
	return state->stack_next - state->stack_base;
}

void vm_call(VmState state, Object obj, const char *name, int arg_count) {
	int old_base = state->stack_base;
	state->stack_base = state->stack_next - arg_count;
	object_hold(state, obj);
	do {
		stacktrace_push(state->stack_trace, name);
		debug_msg("Calling %s\n", name);
		dispatch_call(state, obj, name);
		debug_msg("Exiting %s\n", name);
		stacktrace_pop(state->stack_trace);
		object_drop(state, &obj);
		obj = state->tail_obj;
		name = state->tail_name;
		state->tail_obj = object_none();
		state->tail_name = NULL;
	} while (name != NULL);
	state->stack_base = old_base;
}

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

__attribute__((noreturn)) void vm_abort_msg(VmState state, const char *msg) {
	stacktrace_print(state->stack_trace);
	abort_print(msg);
}

void vm_abort_if(VmState state, int value, const char *msg) {
	if (value)
		vm_abort_msg(state, msg);
}