#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) { m_last_program = program; while (m_pc < program->size()) { switch (program->opcode(m_pc)) { 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(lhs->equals(*rhs), lhs->loc()); }); } break; case OP_ILT: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_int() < rhs->as_int(), lhs->loc()); }); } break; case OP_IGT: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(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(!a || b, lhs->loc()); }); } break; case OP_AND: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_bool() && rhs->as_bool(), lhs->loc()); }); } break; case OP_OR: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_bool() || rhs->as_bool(), lhs->loc()); }); } break; case OP_NOT: { apply_unop(program, [](auto lhs){ return std::make_shared(!lhs->as_bool(), lhs->loc()); }); } break; case OP_IADD: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_int() + rhs->as_int(), lhs->loc()); }); } break; case OP_ISUB: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_int() - rhs->as_int(), lhs->loc()); }); } break; case OP_IMUL: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_int() * rhs->as_int(), lhs->loc()); }); } break; case OP_IDIV: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_int() / rhs->as_int(), lhs->loc()); }); } break; case OP_IMOD: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(lhs->as_int() % rhs->as_int(), lhs->loc()); }); } break; case OP_IPOW: { apply_binop(program, [](auto lhs, auto rhs){ return std::make_shared(static_cast(std::pow(lhs->as_int(), rhs->as_int())), lhs->loc()); }); } break; case OP_IUADD: { apply_unop(program, [](auto lhs){ return std::make_shared(lhs->as_int(), lhs->loc()); }); } break; case OP_IUSUB: { apply_unop(program, [](auto lhs){ return std::make_shared(-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; ihas_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, 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, unop_t op) { auto lhs = program->value(pop()); auto val = op(lhs); push(program->push_value(val)); m_pc++; } }