191 lines
3.7 KiB
C
191 lines
3.7 KiB
C
#include "vm.h"
|
|
#include "lib/commons.h"
|
|
#include "lib/mod.h"
|
|
#include "lib/type.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)
|
|
{
|
|
char msg[RZ_STR_LIMIT];
|
|
snprintf(msg, RZ_STR_LIMIT,
|
|
"type mismatch: BOOL expected, got %s.",
|
|
TypeKindStr[value->type->kind] + strlen("TYPE_"));
|
|
|
|
err_fatal(vm->err, msg, value->line);
|
|
|
|
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 type = lhs->type->kind;
|
|
|
|
int eq = 0;
|
|
|
|
if (same_type)
|
|
{
|
|
if (type == TYPE_NUM)
|
|
{
|
|
eq = lhs->value.num == rhs->value.num;
|
|
}
|
|
else if (type == TYPE_BOOL)
|
|
{
|
|
eq = lhs->value.bool == rhs->value.bool;
|
|
}
|
|
else if (type == TYPE_STR)
|
|
{
|
|
eq = strcmp(lhs->value.str, rhs->value.str) == 0;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Cannot test equality on unknown type '%s'.\n",
|
|
TypeKindStr[type]);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
default: {
|
|
fprintf(stderr, "Cannot execute unknown opcode '%s'.\n",
|
|
OpcodeStr[op]);
|
|
abort();
|
|
};
|
|
}
|
|
|
|
return 0;
|
|
}
|