2023-08-30 18:06:26 +00:00
|
|
|
#include "VM.hpp"
|
2023-08-30 22:31:19 +00:00
|
|
|
#include "lib/opcodes.hpp"
|
2023-08-30 18:06:26 +00:00
|
|
|
|
|
|
|
namespace roza
|
|
|
|
{
|
|
|
|
/*explicit*/ VM::VM(StatusLog& log)
|
|
|
|
: m_log { log }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*virtual*/ VM::~VM()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void VM::exec(std::shared_ptr<Program> program)
|
|
|
|
{
|
2023-08-30 22:31:19 +00:00
|
|
|
m_last_program = program;
|
2023-08-30 18:06:26 +00:00
|
|
|
while (m_pc < program->size())
|
|
|
|
{
|
|
|
|
switch (program->opcode(m_pc))
|
|
|
|
{
|
2023-08-31 12:41:43 +00:00
|
|
|
case OP_ASSERT: {
|
|
|
|
auto value = program->value(pop());
|
|
|
|
|
|
|
|
if (!value->as_bool())
|
|
|
|
{
|
|
|
|
m_log.fatal(value->loc(), "assertion failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pc++;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2023-08-30 18:06:26 +00:00
|
|
|
case OP_PUSH_CONST: {
|
|
|
|
param_t param = *program->param(m_pc);
|
|
|
|
push(param);
|
|
|
|
m_pc++;
|
|
|
|
} break;
|
|
|
|
|
2023-08-31 09:37:13 +00:00
|
|
|
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;
|
|
|
|
|
2023-08-31 09:07:03 +00:00
|
|
|
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;
|
|
|
|
|
2023-08-30 22:31:19 +00:00
|
|
|
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){
|
2023-08-31 09:07:03 +00:00
|
|
|
return std::make_shared<Value>(static_cast<int>(std::pow(lhs->as_int(),
|
|
|
|
rhs->as_int())),
|
2023-08-30 22:31:19 +00:00
|
|
|
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;
|
|
|
|
|
2023-08-30 18:06:26 +00:00
|
|
|
case OP_POP: {
|
|
|
|
pop();
|
|
|
|
m_pc++;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
m_log.fatal(SrcLoc {},
|
|
|
|
std::string()
|
|
|
|
+ "cannot execute opcode '"
|
|
|
|
+ OpcodeStr[program->opcode(m_pc)]
|
|
|
|
+ "'"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-30 22:31:19 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2023-08-30 18:06:26 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-08-30 22:31:19 +00:00
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
2023-08-30 18:06:26 +00:00
|
|
|
}
|