roza/lib/vm.c

212 lines
4.1 KiB
C

#include "vm.h"
#include "lib/commons.h"
#include "lib/mod.h"
#include "lib/type.h"
#include "lib/tysy.h"
void vm_init(vm_t* vm, tysy_t* tysy, err_t* err)
{
assert(vm);
vm->pc = 0;
vm->bp = 0;
vm->sp = 0;
vm->tysy = tysy;
vm->err = err;
}
void vm_free(vm_t* vm)
{
assert(vm);
}
void vm_push_value(vm_t* vm, mod_t* mod, value_t* value)
{
assert(vm);
assert(value);
param_t param = mod_push_new_value(mod, value);
vm_push(vm, param);
}
void vm_push(vm_t* vm, param_t param)
{
assert(vm);
assert(vm->sp + 1 < RZ_STACK_LIMIT);
vm->stack[vm->sp] = param;
vm->sp++;
}
param_t vm_pop(vm_t* vm)
{
assert(vm);
assert(vm->sp > 0);
param_t param = vm->stack[vm->sp - 1];
vm->sp--;
return param;
}
size_t vm_stack_str(vm_t* vm, char* buffer, size_t size)
{
assert(vm);
assert(buffer);
size_t sz = 0;
for (size_t i=0; i<vm->sp; i++)
{
sz += snprintf(buffer + sz, size - sz, "| %d |\n", vm->stack[vm->sp - 1 - i]);
}
return sz;
}
int vm_exec_mod(vm_t* vm, mod_t* mod)
{
assert(vm);
vm->pc = 0;
while (vm->pc < mod->program.size)
{
int status = vm_exec_instr(vm,
mod,
mod->program.ops[vm->pc],
mod->program.params[vm->pc]);
if (status != 0)
{
return status;
}
}
return 0;
}
int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param)
{
assert(vm);
switch (op)
{
case OP_PUSH: {
if (vm->sp + 1 >= RZ_STACK_LIMIT)
{
fprintf(stderr, "Stack overflow\n");
abort();
}
vm->stack[vm->sp] = param;
vm->sp++;
vm->pc++;
} break;
case OP_ASSERT: {
param_t top = vm_pop(vm);
value_t* value = mod->values.data[top];
if (value->type->kind != TYPE_BOOL)
{
vm_ensure_type(vm, value, TYPE_BOOL);
vm->pc++;
break;
}
if (!value->value.bool)
{
fprintf(stderr, "\33[31mASSERT\33[0m[:%d] assertion failed\n",
value->line);
return -1;
}
vm->pc++;
} break;
case OP_NE:
case OP_EQ: {
int l = vm_pop(vm);
int r = vm_pop(vm);
assert(l != -1);
assert(r != -1);
value_t* rhs = mod->values.data[l];
value_t* lhs = mod->values.data[r];
int same_type = lhs->type->kind == rhs->type->kind;
int eq = value_eq(lhs, rhs);
value_t* res = tysy_new_bool(vm->tysy,
(op == OP_EQ
? (same_type && eq)
: (!same_type || !eq)),
rhs->line);
vm_push_value(vm, mod, res);
vm->pc++;
} break;
case OP_LT:
case OP_LE:
case OP_GT:
case OP_GE: {
int r = vm_pop(vm);
int l = vm_pop(vm);
value_t* lhs = mod->values.data[l];
value_t* rhs = mod->values.data[r];
vm_ensure_type(vm, lhs, TYPE_NUM);
vm_ensure_type(vm, rhs, TYPE_NUM);
int val = 0;
switch (op)
{
case OP_LT: val = lhs->value.num < rhs->value.num; break;
case OP_LE: val = lhs->value.num <= rhs->value.num; break;
case OP_GT: val = lhs->value.num > rhs->value.num; break;
case OP_GE: val = lhs->value.num >= rhs->value.num; break;
default: break;
}
value_t* res = tysy_new_bool(vm->tysy, val, lhs->line);
vm_push_value(vm, mod, res);
vm->pc++;
} break;
default: {
fprintf(stderr, "Cannot execute unknown opcode '%s'.\n",
OpcodeStr[op]);
abort();
};
}
return 0;
}
void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want)
{
assert(vm);
assert(value);
TypeKind got = value->type->kind;
int line = value->line;
if (got != want)
{
char msg[RZ_STR_LIMIT];
snprintf(msg, RZ_STR_LIMIT,
"type mismatch: %s expected, got %s.",
TypeKindStr[want] + strlen("TYPE_"),
TypeKindStr[got] + strlen("TYPE_"));
err_fatal(vm->err, msg, line);
}
}