Compare commits
9 Commits
29dd9abb3f
...
ed84a65757
| Author | SHA1 | Date | |
|---|---|---|---|
|
ed84a65757
|
|||
|
26efd110f1
|
|||
|
dd96f95d77
|
|||
|
70cbb003f2
|
|||
|
665982049d
|
|||
|
195d154474
|
|||
|
26d935e22a
|
|||
|
a4966a057c
|
|||
|
b3ce4fa0ed
|
@@ -36,4 +36,24 @@ public:
|
||||
void execute(context& ctx, const command_info& info) override;
|
||||
};
|
||||
|
||||
class continue_command final : public command {
|
||||
public:
|
||||
void execute(context& ctx, const command_info& info) override;
|
||||
};
|
||||
|
||||
class next_command final : public command {
|
||||
public:
|
||||
void execute(context& ctx, const command_info& info) override;
|
||||
};
|
||||
|
||||
class break_command final : public command {
|
||||
public:
|
||||
void execute(context& ctx, const command_info& info) override;
|
||||
};
|
||||
|
||||
class info_command final : public command {
|
||||
public:
|
||||
void execute(context& ctx, const command_info& info) override;
|
||||
};
|
||||
|
||||
#endif // COMMAND_HPP
|
||||
|
||||
@@ -13,8 +13,14 @@ struct context {
|
||||
furvm::function_h mainFunction;
|
||||
|
||||
bool running = true;
|
||||
bool halt = true;
|
||||
|
||||
void kill() const;
|
||||
void init();
|
||||
|
||||
void run();
|
||||
|
||||
void print_instruction() const;
|
||||
};
|
||||
|
||||
#endif // CONTEXT_HPP
|
||||
|
||||
+138
-2
@@ -1,9 +1,16 @@
|
||||
#include "command.hpp"
|
||||
|
||||
#include "furvm/executor.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/instruction.hpp"
|
||||
#include "furvm/module.hpp"
|
||||
#include "furvm/thing.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
command_info command::parse(std::string_view line) {
|
||||
@@ -50,6 +57,135 @@ void quit_command::execute(context& ctx, const command_info& info) {
|
||||
}
|
||||
|
||||
void run_command::execute(context& ctx, const command_info& info) {
|
||||
ctx.run();
|
||||
std::cout << "Execution finished\n";
|
||||
if (!ctx.executor->done()) {
|
||||
std::cout << "A program is currently running, do you want to kill it? (y/N) ";
|
||||
std::string answer;
|
||||
if (!std::getline(std::cin, answer)) {
|
||||
ctx.running = false;
|
||||
return;
|
||||
}
|
||||
if (answer != "y" && answer != "Y" && answer != "yes") return;
|
||||
ctx.kill();
|
||||
}
|
||||
std::cout << "Running the program\n";
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
void continue_command::execute(context& ctx, const command_info& info) {
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
void next_command::execute(context& ctx, const command_info& info) {
|
||||
if (ctx.executor->done()) {
|
||||
std::cout << "No program's currently running\n";
|
||||
return;
|
||||
}
|
||||
ctx.executor->unsuspend();
|
||||
ctx.executor->step();
|
||||
ctx.print_instruction();
|
||||
}
|
||||
|
||||
static void print_type(const furvm::thing_type& type) {
|
||||
switch (type.type) {
|
||||
case furvm::thing_type::S8: std::cout << "s8"; break;
|
||||
case furvm::thing_type::S16: std::cout << "s16"; break;
|
||||
case furvm::thing_type::S32: std::cout << "s32"; break;
|
||||
case furvm::thing_type::S64: std::cout << "s64"; break;
|
||||
case furvm::thing_type::U8: std::cout << "u8"; break;
|
||||
case furvm::thing_type::U16: std::cout << "u16"; break;
|
||||
case furvm::thing_type::U32: std::cout << "u32"; break;
|
||||
case furvm::thing_type::U64: std::cout << "u64"; break;
|
||||
case furvm::thing_type::Ptr: std::cout << "ptr"; break;
|
||||
case furvm::thing_type::Ref: std::cout << "ref"; break;
|
||||
case furvm::thing_type::Array:
|
||||
std::cout << "array(";
|
||||
print_type(*type.value.array.type);
|
||||
std::cout << ", ";
|
||||
if (type.value.array.size == 0)
|
||||
std::cout << "dynamic";
|
||||
else
|
||||
std::cout << type.value.array.size;
|
||||
std::cout << ")";
|
||||
break;
|
||||
case furvm::thing_type::Count: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_thing(const furvm::thing<>& thing) {
|
||||
std::cout << '(';
|
||||
print_type(thing.type());
|
||||
std::cout << ") ";
|
||||
switch (thing.type().type) {
|
||||
case furvm::thing_type::S8: std::cout << std::to_string(thing.get<furvm::thing_type::s8>()); break;
|
||||
case furvm::thing_type::S16: std::cout << thing.get<furvm::thing_type::s16>(); break;
|
||||
case furvm::thing_type::S32: std::cout << thing.get<furvm::thing_type::s32>(); break;
|
||||
case furvm::thing_type::S64: std::cout << thing.get<furvm::thing_type::s64>(); break;
|
||||
case furvm::thing_type::U8: std::cout << std::to_string(thing.get<furvm::thing_type::u8>()); break;
|
||||
case furvm::thing_type::U16: std::cout << thing.get<furvm::thing_type::u16>(); break;
|
||||
case furvm::thing_type::U32: std::cout << thing.get<furvm::thing_type::u32>(); break;
|
||||
case furvm::thing_type::U64: std::cout << thing.get<furvm::thing_type::u64>(); break;
|
||||
case furvm::thing_type::Ptr: std::cout << thing.get<const void*>(); break;
|
||||
case furvm::thing_type::Array: {
|
||||
if (thing.type().value.array.size == 0) std::cout << thing.length();
|
||||
std::cout << "{ ";
|
||||
for (std::size_t i = 0; i < thing.length(); ++i) {
|
||||
if (i > 0) std::cout << ", ";
|
||||
print_thing(thing.at(i));
|
||||
}
|
||||
std::cout << " }";
|
||||
} break;
|
||||
case furvm::thing_type::Ref:
|
||||
case furvm::thing_type::Count: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void breakpoint_hit(furvm::executor& executor, void* data) {
|
||||
std::cout << "Breakpoint hit\n";
|
||||
context* ctx = reinterpret_cast<context*>(data);
|
||||
ctx->halt = true;
|
||||
|
||||
ctx->print_instruction();
|
||||
}
|
||||
|
||||
void break_command::execute(context& ctx, const command_info& info) {
|
||||
if (info.args.empty()) {
|
||||
std::cerr << "Usage: " << info.commandName << " <function name>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t count = 0;
|
||||
for (const auto& [id, sigPair] : ctx.mod->function_map()) {
|
||||
if (sigPair.first != info.args[0]) continue;
|
||||
const auto& func = ctx.mod->function_at(id);
|
||||
if (func->type() != furvm::function_t::Normal) continue;
|
||||
count += 1;
|
||||
ctx.mod->set_breakpoint(func->position(), furvm::breakpoint{ breakpoint_hit, &ctx });
|
||||
}
|
||||
if (count == 0) {
|
||||
std::cerr << "No function \"" << info.args[0] << "\" found!\n";
|
||||
return;
|
||||
}
|
||||
std::cout << "Breakpoint set in " << count << " places\n";
|
||||
}
|
||||
|
||||
void info_command::execute(context& ctx, const command_info& info) {
|
||||
if (info.args.empty()) {
|
||||
std::cout << "Possible arguments:\n";
|
||||
std::cout << "- variables\n";
|
||||
} else if (info.args[0] == "variables") {
|
||||
if (ctx.executor->frames().empty()) {
|
||||
std::cerr << "Not running\n";
|
||||
return;
|
||||
}
|
||||
std::cout << "Variables:\n";
|
||||
const auto& frame = ctx.executor->top_frame();
|
||||
for (std::size_t i = 0; i < frame.variables.size(); ++i) {
|
||||
// TODO: Add debug information to modules
|
||||
std::cout << "- %" << i << " = ";
|
||||
print_thing(frame.variables[i]);
|
||||
std::cout << '\n';
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Unexpected argument \"" << info.args[0] << "\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
+129
-3
@@ -1,9 +1,135 @@
|
||||
#include "context.hpp"
|
||||
|
||||
void context::run() {
|
||||
if ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done)
|
||||
#include <furvm/executor.hpp>
|
||||
#include <furvm/instruction.hpp>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
void context::kill() const {
|
||||
while (!executor->done())
|
||||
executor->pop_frame();
|
||||
}
|
||||
|
||||
void context::init() {
|
||||
kill();
|
||||
executor->push_frame(mod, *mainFunction);
|
||||
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
|
||||
executor->clear_flags();
|
||||
}
|
||||
|
||||
void context::run() {
|
||||
if (executor->done()) init();
|
||||
executor->unsuspend();
|
||||
|
||||
halt = false;
|
||||
while (!executor->done() && !halt)
|
||||
executor->step();
|
||||
if (executor->done()) {
|
||||
std::cout << "Execution finished\n";
|
||||
halt = true;
|
||||
}
|
||||
}
|
||||
|
||||
void context::print_instruction() const {
|
||||
static const char* s_instrNames[furvm::instruction::Count] = {
|
||||
// NoOperation
|
||||
"nop",
|
||||
// PushS8
|
||||
"push $s8",
|
||||
// PushU8
|
||||
"push $u8",
|
||||
// PushS16
|
||||
"push $s16",
|
||||
// PushU16
|
||||
"push $U16",
|
||||
// PushS32
|
||||
"push $S32",
|
||||
// PushU32
|
||||
"push $U32",
|
||||
// PushConstant
|
||||
"push",
|
||||
// Array
|
||||
"array",
|
||||
// Get
|
||||
"get",
|
||||
// Set
|
||||
"set",
|
||||
// Drop
|
||||
"drop",
|
||||
// Duplicate
|
||||
"dup",
|
||||
// Swap
|
||||
"swap",
|
||||
// Clone
|
||||
"clone",
|
||||
// Reference
|
||||
"ref",
|
||||
// Add
|
||||
"add",
|
||||
// Sub
|
||||
"sub",
|
||||
// Mul
|
||||
"mul",
|
||||
// Div
|
||||
"div",
|
||||
// Mod
|
||||
"mod",
|
||||
// Equals
|
||||
"eq",
|
||||
// NotEquals
|
||||
"ne",
|
||||
// LessThan
|
||||
"lt",
|
||||
// GreaterThan
|
||||
"gt",
|
||||
// LessEqual
|
||||
"le",
|
||||
// GreaterEqual
|
||||
"ge",
|
||||
// Pointerof
|
||||
"pointerof",
|
||||
// Sizeof
|
||||
"sizeof",
|
||||
// Lengthof
|
||||
"lenof",
|
||||
// Load
|
||||
"load",
|
||||
// Store
|
||||
"store",
|
||||
// LoadGlobal
|
||||
"loadg",
|
||||
// StoreGlobal
|
||||
"storeg",
|
||||
// Call
|
||||
"call",
|
||||
// Jump
|
||||
"jmp",
|
||||
// JumpNotZero
|
||||
"jnz",
|
||||
// Return
|
||||
"ret",
|
||||
};
|
||||
|
||||
furvm::instruction instr{};
|
||||
instr.read(mod->bytecode_view().subview(executor->top_frame().position));
|
||||
std::cout << s_instrNames[instr.type];
|
||||
if (instr.arg.type != furvm::instruction_argument_t::None) {
|
||||
std::cout << ' ';
|
||||
switch (instr.arg.type) {
|
||||
case furvm::instruction_argument::S8: std::cout << std::to_string(instr.arg.s8); break;
|
||||
case furvm::instruction_argument::U8: std::cout << std::to_string(instr.arg.u8); break;
|
||||
case furvm::instruction_argument::S16: std::cout << instr.arg.s16; break;
|
||||
case furvm::instruction_argument::U16: std::cout << instr.arg.u16; break;
|
||||
case furvm::instruction_argument::S32: std::cout << instr.arg.s32; break;
|
||||
case furvm::instruction_argument::U32: std::cout << instr.arg.u32; break;
|
||||
case furvm::instruction_argument::None:
|
||||
case furvm::instruction_argument::Count: break;
|
||||
case furvm::instruction_argument::Constant: throw std::runtime_error("unimplemented");
|
||||
case furvm::instruction_argument::Type: std::cout << "$__t" << instr.arg.u32; break;
|
||||
case furvm::instruction_argument::Variable: std::cout << "%" << instr.arg.u16; break;
|
||||
case furvm::instruction_argument::GlobalVariable: std::cout << "%__g" << instr.arg.u16; break;
|
||||
case furvm::instruction_argument::Function: std::cout << "<a function>"; break;
|
||||
case furvm::instruction_argument::Offset: std::cout << std::to_string(instr.arg.s8); break;
|
||||
}
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
+5
-1
@@ -43,8 +43,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string_view, command*> s_commands;
|
||||
s_commands["quit"] = s_commands["q"] = new quit_command();
|
||||
s_commands["exit"] = s_commands["quit"] = s_commands["q"] = new quit_command();
|
||||
s_commands["run"] = s_commands["r"] = new run_command();
|
||||
s_commands["continue"] = s_commands["c"] = new continue_command();
|
||||
s_commands["next"] = s_commands["n"] = new next_command();
|
||||
s_commands["break"] = s_commands["b"] = new break_command();
|
||||
s_commands["info"] = s_commands["i"] = new info_command();
|
||||
|
||||
try {
|
||||
std::ifstream file(argv[1], std::ios::binary | std::ios::in);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
@@ -15,6 +14,8 @@ namespace furvm {
|
||||
enum class executor_flags : std::uint32_t {
|
||||
Suspended = (1 << 0), /**< Execution suspended. */
|
||||
Done = (1 << 1), /**< Execution is finished. */
|
||||
|
||||
JustHit = (1 << 16), /**< Executor just hit a breakpoint. */
|
||||
};
|
||||
|
||||
static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) {
|
||||
@@ -29,13 +30,15 @@ static inline executor_flags operator~(executor_flags flags) {
|
||||
return executor_flags(~static_cast<std::uint32_t>(flags));
|
||||
}
|
||||
|
||||
using executor_callback = std::function<void(executor&)>;
|
||||
|
||||
class executor {
|
||||
friend class context;
|
||||
private:
|
||||
executor(context* context)
|
||||
: m_context(context) {}
|
||||
public:
|
||||
static constexpr executor_flags STATE_FLAGS = executor_flags::JustHit;
|
||||
|
||||
using new_frame_callback = std::function<void(executor&)>;
|
||||
public:
|
||||
/**
|
||||
* @brief Executor frame.
|
||||
@@ -84,6 +87,17 @@ public:
|
||||
* @return The flags.
|
||||
*/
|
||||
executor_flags flags() const { return m_flags; }
|
||||
|
||||
bool done() const { return (m_flags & executor_flags::Done) == executor_flags::Done; }
|
||||
|
||||
bool suspended() const { return (m_flags & executor_flags::Suspended) == executor_flags::Suspended; }
|
||||
|
||||
void unsuspend() { m_flags = m_flags & ~executor_flags::Suspended; }
|
||||
|
||||
void clear_flags() {
|
||||
m_flags = m_flags & STATE_FLAGS;
|
||||
m_flags = m_frames.empty() ? executor_flags::Done : furvm::executor_flags{ 0 };
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Pushes a new frame.
|
||||
@@ -174,13 +188,13 @@ private:
|
||||
private:
|
||||
static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs);
|
||||
private:
|
||||
executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization)
|
||||
executor_flags m_flags = executor_flags::Done;
|
||||
context* m_context;
|
||||
|
||||
std::stack<frame> m_frames;
|
||||
std::stack<thing<>> m_stack;
|
||||
|
||||
executor_callback m_newFrameCb = nullptr;
|
||||
new_frame_callback m_newFrameCb = nullptr;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
@@ -145,6 +145,11 @@ struct mod_type {
|
||||
}
|
||||
};
|
||||
|
||||
struct breakpoint {
|
||||
std::function<void(executor&, void*)> callback;
|
||||
void* data = nullptr;
|
||||
};
|
||||
|
||||
class mod {
|
||||
friend class function;
|
||||
friend class serializer;
|
||||
@@ -379,6 +384,15 @@ public:
|
||||
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||
return m_globalVariables[var];
|
||||
}
|
||||
public:
|
||||
template <typename Fwd, typename = std::enable_if_t<std::is_constructible_v<breakpoint, Fwd>>>
|
||||
void set_breakpoint(bytecode_pos pos, Fwd&& breakpoint) {
|
||||
m_breakpoints[pos] = std::forward<Fwd>(breakpoint);
|
||||
}
|
||||
|
||||
bool has_breakpoint(bytecode_pos pos) const { return m_breakpoints.find(pos) != m_breakpoints.end(); }
|
||||
|
||||
const breakpoint& breakpoint_at(bytecode_pos pos) const { return m_breakpoints.at(pos); }
|
||||
public:
|
||||
/**
|
||||
* @brief Prints the module in a bytecode form to an output stream.
|
||||
@@ -410,6 +424,8 @@ private:
|
||||
std::vector<thing<>> m_globalVariables;
|
||||
|
||||
std::unordered_map<std::string, native_function> m_nativeFunctions;
|
||||
|
||||
std::unordered_map<bytecode_pos, breakpoint> m_breakpoints;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
@@ -104,6 +104,13 @@ void executor::push_frame(const mod_h& mod, function function) {
|
||||
default: throw std::runtime_error("unexpected function type");
|
||||
}
|
||||
if (m_newFrameCb) m_newFrameCb(*this);
|
||||
|
||||
if (!m_frames.empty()) {
|
||||
m_flags = m_flags & ~executor_flags::Done;
|
||||
} else {
|
||||
m_flags = m_flags | executor_flags::Done;
|
||||
}
|
||||
m_flags = m_flags & ~executor_flags::JustHit;
|
||||
}
|
||||
|
||||
struct executor::frame executor::pop_frame() {
|
||||
@@ -117,6 +124,14 @@ struct executor::frame executor::pop_frame() {
|
||||
}
|
||||
if (m_stack.size() != frame.stackBase) throw std::runtime_error("unexhausted stack");
|
||||
if (returnValue.has_value()) push_thing(std::move(returnValue.value()));
|
||||
|
||||
if (!m_frames.empty()) {
|
||||
m_flags = m_flags & ~executor_flags::Done;
|
||||
} else {
|
||||
m_flags = m_flags | executor_flags::Done;
|
||||
}
|
||||
m_flags = m_flags & ~executor_flags::JustHit;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -176,6 +191,14 @@ void executor::step() {
|
||||
|
||||
struct frame& frame = m_frames.top();
|
||||
|
||||
if ((m_flags & executor_flags::JustHit) != executor_flags::JustHit && frame.mod->has_breakpoint(frame.position)) {
|
||||
m_flags = m_flags | executor_flags::Suspended | executor_flags::JustHit;
|
||||
const auto& bp = frame.mod->breakpoint_at(frame.position);
|
||||
bp.callback(*this, bp.data);
|
||||
return;
|
||||
}
|
||||
m_flags = m_flags & ~executor_flags::JustHit;
|
||||
|
||||
instruction instr{};
|
||||
frame.position += instr.read(frame.mod->bytecode_view().subview(frame.position));
|
||||
switch (instr.type) {
|
||||
|
||||
Reference in New Issue
Block a user