#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 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(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(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(frame.mod->byte(frame.position)) | (static_cast(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; } } 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