Files
furlang/furvm/src/executor.cpp
T

142 lines
4.3 KiB
C++

#include "furvm/executor.hpp"
#include "furvm/context.hpp" // IWYU pragma: keep
#include "furvm/exceptions.hpp"
#include "furvm/function.hpp" // IWYU pragma: keep
#include "furvm/instruction.hpp"
#include "furvm/thing.hpp"
#include <stdexcept>
namespace furvm {
executor::executor(egzekutor, executor_handle id, const context_p& context)
: m_id(id), m_context(context) {}
executor::~executor() {
m_context->m_executors[m_id] = nullptr;
}
executor_p executor::create(const context_p& context) {
auto ex = std::make_shared<executor>(egzekutor{}, context->m_executors.size(), context);
context->m_executors.push_back(ex);
return std::move(ex);
}
void executor::push_frame(const function_p& function) {
if (function->type() != function_t::Normal) return;
m_frames.emplace((struct executor::frame){ function->mod(), function->position(), m_stack.size() });
}
struct executor::frame executor::pop_frame() {
if (m_frames.empty()) throw stack_underflow();
struct executor::frame frame = m_frames.top();
m_frames.pop();
return frame;
}
struct executor::frame executor::frame() const {
return m_frames.top();
}
void executor::push_thing(const thing_p& thing) {
thing->add_reference();
m_stack.push(thing);
}
void executor::push_thing(thing_p&& thing) {
thing->add_reference();
m_stack.push(std::move(thing));
}
thing_p executor::pop_thing() {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
thing_p top = std::move(m_stack.top());
m_stack.pop();
top->remove_reference();
return top;
}
thing_p executor::thing() const {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
return m_stack.top();
}
void executor::step() {
if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return;
struct frame& frame = m_frames.top();
instruction_t instr = static_cast<instruction_t>(frame.mod->byte(frame.position++));
switch (instr) {
case instruction_t::NoOperation: break;
case instruction_t::PushB2I: {
auto thing = thing::create(m_context, thing_t::Int32);
thing->int32() = frame.mod->byte(frame.position++);
push_thing(std::move(thing));
} break;
case instruction_t::Drop: {
pop_thing();
} break;
case instruction_t::Duplicate: {
push_thing(thing());
} break;
case instruction_t::Clone: {
push_thing(std::move(thing::clone(thing())));
} break;
case instruction_t::Add: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs + rhs);
} break;
case instruction_t::Sub: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs - rhs);
} break;
case instruction_t::Mul: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs * rhs);
} break;
case instruction_t::Div: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs / rhs);
} break;
case instruction_t::Mod: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs % rhs);
} break;
case instruction_t::Call: {
function_handle funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
const function_p& function = frame.mod->function_at(funcId);
switch (function->type()) {
case function_t::Normal: push_frame(function); break;
case function_t::Native: function->native()(*this); break;
case function_t::Import: {
const mod_p& impMod = m_context->m_modules.at(function->imported_module());
push_frame(impMod->function_at(function->imported_function()));
} break;
}
} break;
case instruction_t::Return: {
pop_frame();
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
} break;
case instruction_t::ReturnValue: {
auto value = pop_thing();
pop_frame();
push_thing(std::move(value));
} break;
case instruction_t::PushConstant: throw std::runtime_error("unimplemented");
default: throw std::runtime_error("unknown instruction");
}
}
} // namespace furvm