roza/lib/VM.cpp

258 lines
6.7 KiB
C++

#include "VM.hpp"
#include "lib/opcodes.hpp"
namespace roza
{
/*explicit*/ VM::VM(StatusLog& log)
: m_log { log }
{
}
/*virtual*/ VM::~VM()
{
}
void VM::exec(std::shared_ptr<Program> program)
{
m_last_program = program;
while (m_pc < program->size())
{
switch (program->opcode(m_pc))
{
case OP_BRF: {
auto value = program->value(pop());
if (!value->as_bool())
{
m_pc = *program->param(m_pc);
}
else
{
m_pc++;
}
} break;
case OP_BR: {
m_pc = *program->param(m_pc);
} break;
case OP_LOAD_GLOBAL: {
int addr = *program->param(m_pc);
auto value = m_globals[addr];
push(program->push_value(value));
m_pc++;
} break;
case OP_STORE_GLOBAL: {
auto value = program->value(pop());
int addr = *program->param(m_pc);
m_globals[addr] = value;
m_pc++;
} break;
case OP_ASSERT: {
auto value = program->value(pop());
if (!value->as_bool())
{
m_log.fatal(value->loc(), "assertion failed");
}
m_pc++;
} break;
case OP_PUSH_CONST: {
param_t param = *program->param(m_pc);
push(param);
m_pc++;
} break;
case OP_EQ: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->equals(*rhs),
lhs->loc());
});
} break;
case OP_ILT: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() < rhs->as_int(),
lhs->loc());
});
} break;
case OP_IGT: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() > rhs->as_int(),
lhs->loc());
});
} break;
case OP_IMP: {
apply_binop(program, [](auto lhs, auto rhs){
bool a = lhs->as_bool();
bool b = rhs->as_bool();
return std::make_shared<Value>(!a || b,
lhs->loc());
});
} break;
case OP_AND: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_bool() && rhs->as_bool(),
lhs->loc());
});
} break;
case OP_OR: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_bool() || rhs->as_bool(),
lhs->loc());
});
} break;
case OP_NOT: {
apply_unop(program, [](auto lhs){
return std::make_shared<Value>(!lhs->as_bool(),
lhs->loc());
});
} break;
case OP_IADD: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() + rhs->as_int(),
lhs->loc());
});
} break;
case OP_ISUB: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() - rhs->as_int(),
lhs->loc());
});
} break;
case OP_IMUL: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() * rhs->as_int(),
lhs->loc());
});
} break;
case OP_IDIV: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() / rhs->as_int(),
lhs->loc());
});
} break;
case OP_IMOD: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() % rhs->as_int(),
lhs->loc());
});
} break;
case OP_IPOW: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(static_cast<int>(std::pow(lhs->as_int(),
rhs->as_int())),
lhs->loc());
});
} break;
case OP_IUADD: {
apply_unop(program, [](auto lhs){
return std::make_shared<Value>(lhs->as_int(),
lhs->loc());
});
} break;
case OP_IUSUB: {
apply_unop(program, [](auto lhs){
return std::make_shared<Value>(-lhs->as_int(),
lhs->loc());
});
} break;
case OP_POP: {
pop();
m_pc++;
} break;
default:
m_log.fatal(SrcLoc {},
std::string()
+ "cannot execute opcode '"
+ OpcodeStr[program->opcode(m_pc)]
+ "'"); break;
}
}
}
std::string VM::string() const
{
std::stringstream ss;
for (size_t i=0; i<m_stack.size(); i++)
{
ss << i << "\t";
if (m_last_program && m_last_program->has_value(m_stack[i]))
{
ss << m_stack[i]
<< "\t("
<< m_last_program->value(m_stack[i])->string()
<< ")"
<< std::endl;
}
else
{
ss << m_stack[i] << std::endl;
}
}
return ss.str();
}
void VM::push(param_t param)
{
m_stack.push_back(param);
}
param_t VM::pop()
{
assert(m_stack.size() > 0);
param_t res = m_stack.back();
m_stack.pop_back();
return res;
}
void VM::apply_binop(std::shared_ptr<Program> program, binop_t op)
{
auto rhs = program->value(pop());
auto lhs = program->value(pop());
auto val = op(lhs, rhs);
push(program->push_value(val));
m_pc++;
}
void VM::apply_unop(std::shared_ptr<Program> program, unop_t op)
{
auto lhs = program->value(pop());
auto val = op(lhs);
push(program->push_value(val));
m_pc++;
}
}