Files
furlang/furvm/src/executor.cpp
T
2026-07-05 13:31:15 +02:00

206 lines
6.8 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/fwd.hpp"
#include "furvm/instruction.hpp"
#include "furvm/thing.hpp"
#include <cstdint>
#include <stdexcept>
#include <vector>
namespace furvm {
void executor::push_frame(const mod_h& mod, function function) {
mod_h modInst = mod;
while (function.type() == function_t::Import) {
modInst = m_context->at(function.imp().mod);
function = *modInst->function_at(function.imp().function);
}
std::vector<thing_h> args;
args.reserve(function.param_count());
for (size_t i = 0; i < function.param_count(); ++i)
args.push_back(pop_thing());
switch (function.type()) {
case function_t::Normal: {
m_frames.emplace((struct executor::frame){ mod, function.position(), m_stack.size(), std::move(args) });
} break;
case function_t::Native: {
m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), std::move(args) });
modInst->get_native_function(function.native())(*this);
m_frames.pop();
} break;
default: throw std::runtime_error("unexpected function type");
}
}
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();
}
thing_h executor::push_thing(::furvm::thing<>&& thing) {
return m_stack.emplace(m_context->emplace_thing(std::move(thing)));
}
thing_h executor::pop_thing() {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
thing_h top = std::move(m_stack.top());
m_stack.pop();
return top;
}
thing_h executor::thing() const {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
return m_stack.top();
}
void executor::store_thing(variable_t variable, const thing_h& thing) {
auto& frame = m_frames.top();
if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
frame.variables[variable] = thing;
}
void executor::store_thing(variable_t variable, thing_h&& thing) {
auto& frame = m_frames.top();
if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
frame.variables[variable] = std::move(thing);
}
thing_h executor::load_thing(variable_t variable) const {
const auto& frame = m_frames.top();
return frame.variables[variable];
}
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: {
push_thing({ *m_context->at("core")->type_at(2), m_context, m_context->thing_alloc() })->get<int_t>() =
frame.mod->byte(frame.position++);
} 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()));
} break;
case instruction_t::Reference: {
auto thing = pop_thing();
push_thing(std::move(thing->reference()));
} break;
case instruction_t::Add: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->add(*rhs));
} break;
case instruction_t::Sub: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->sub(*rhs));
} break;
case instruction_t::Mul: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->mul(*rhs));
} break;
case instruction_t::Div: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->div(*rhs));
} break;
case instruction_t::Mod: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->mod(*rhs));
} break;
case instruction_t::Equals: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->equals(*rhs));
} break;
case instruction_t::NotEquals: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->not_equals(*rhs));
} break;
case instruction_t::LessThan: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->less_than(*rhs));
} break;
case instruction_t::GreaterThan: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->greater_than(*rhs));
} break;
case instruction_t::LessEqual: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->less_equals(*rhs));
} break;
case instruction_t::GreaterEqual: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs->greater_equals(*rhs));
} break;
case instruction_t::Load: {
variable_t variable = 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;
push_thing(load_thing(variable));
} break;
case instruction_t::Store: {
variable_t variable = 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;
store_thing(variable, std::move(pop_thing()));
} break;
case instruction_t::Call: {
function_id 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;
push_frame(frame.mod, *frame.mod->function_at(funcId));
} break;
case instruction_t::Jump: {
frame.position += ((std::int8_t)frame.mod->byte(frame.position)) + 1;
} break;
case instruction_t::JumpNotZero: {
byte offset = frame.mod->byte(frame.position++);
auto cond = pop_thing();
if (cond->get<int_t>() != 0) frame.position += (std::int8_t)offset;
} 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