VM basics #5
@@ -59,15 +59,15 @@ FetchContent_MakeAvailable(googletest)
|
||||
#
|
||||
|
||||
add_library(lib${PROJECT_NAME} SHARED
|
||||
src/bytecode/bytearray.hh
|
||||
src/bytecode/bytearray.cc
|
||||
src/common/overloaded.hh
|
||||
src/common/bytearray.hh
|
||||
src/common/bytearray.cc
|
||||
src/bytecode/bytecode.cc
|
||||
src/bytecode/bytecode.hh
|
||||
src/bytecode/bytecodeprototype.hh
|
||||
src/common/overloaded.hh
|
||||
src/bytecode/constant.hh
|
||||
src/vm/code.cc
|
||||
src/vm/code.hh
|
||||
src/bytecode/constant.hh
|
||||
src/vm/instruction.hh
|
||||
src/vm/instruction.cc
|
||||
src/vm/value.cc
|
||||
@@ -75,6 +75,11 @@ add_library(lib${PROJECT_NAME} SHARED
|
||||
src/vm/stack.cc
|
||||
src/vm/stack.hh
|
||||
src/vm/vm_exceptions.hh
|
||||
src/vm/vm.cc
|
||||
src/vm/vm.hh
|
||||
src/vm/expr.cc
|
||||
src/vm/expr.hh
|
||||
src/vm/location.hh
|
||||
)
|
||||
|
||||
target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
||||
|
||||
9
TODO.md
9
TODO.md
@@ -25,9 +25,12 @@ After some additional development:
|
||||
- [x] Output bytecode format
|
||||
- [x] Value object
|
||||
- [x] Stack object
|
||||
- [ ] External interface
|
||||
- [ ] Code execution (except functions)
|
||||
- [ ] Functions
|
||||
- [x] External interface
|
||||
- [x] Code execution (except functions)
|
||||
- [x] Functions
|
||||
- [x] Print stack
|
||||
|
||||
- [ ] Assembler
|
||||
|
||||
After some additional development:
|
||||
- [ ] Bytecode loader
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "bytecode.hh"
|
||||
#include "../common/overloaded.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::bc {
|
||||
|
||||
Bytecode::Bytecode(ByteArray ba)
|
||||
: byte_array_(std::move(ba))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef TYCHE_BYTECODE_HH
|
||||
#define TYCHE_BYTECODE_HH
|
||||
|
||||
#include "bytearray.hh"
|
||||
#include "../common/bytearray.hh"
|
||||
#include "bytecodeprototype.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::bc {
|
||||
|
||||
class Bytecode {
|
||||
public:
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include "constant.hh"
|
||||
#include "bytearray.hh"
|
||||
#include "../common/bytearray.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::bc {
|
||||
|
||||
struct BytecodePrototype {
|
||||
struct Function {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::bc {
|
||||
|
||||
using ConstantValue = std::variant<float, std::string>;
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "bytearray.hh"
|
||||
#include "../common/bytearray.hh"
|
||||
#include "bytecodeprototype.hh"
|
||||
#include "bytecode.hh"
|
||||
|
||||
using namespace tyche;
|
||||
using namespace tyche::bc;
|
||||
|
||||
TEST(ByteArray, ByteArray)
|
||||
{
|
||||
|
||||
@@ -2,14 +2,51 @@
|
||||
#include "../common/overloaded.hh"
|
||||
#include "instruction.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
void Code::import_bytecode(ByteArray incoming)
|
||||
FunctionId Code::import_bytecode(ByteArray incoming)
|
||||
{
|
||||
Bytecode bc(std::move(incoming));
|
||||
bc::Bytecode bc(std::move(incoming));
|
||||
// TODO - adjust function calls, constants
|
||||
|
||||
bytecode_ = std::move(bc);
|
||||
|
||||
return 0; // TODO
|
||||
}
|
||||
|
||||
Operation Code::operation(Location const& location) const
|
||||
{
|
||||
Instruction inst = (Instruction) bytecode_.get_code_byte(location.function_id, location.pc);
|
||||
OperandType opet = instruction_operand_type(inst);
|
||||
|
||||
switch (opet) {
|
||||
case OperandType::NoOperand:
|
||||
return {
|
||||
.instruction = inst,
|
||||
.operator_ = 0,
|
||||
.next_location = { .function_id = location.function_id, .pc = location.pc + 1 },
|
||||
};
|
||||
case OperandType::Int8:
|
||||
return {
|
||||
.instruction = inst,
|
||||
.operator_ = bytecode_.get_code_int8(location.function_id, location.pc + 1),
|
||||
.next_location = { .function_id = location.function_id, .pc = location.pc + 2 },
|
||||
};
|
||||
case OperandType::Int16:
|
||||
return {
|
||||
.instruction = inst,
|
||||
.operator_ = bytecode_.get_code_int16(location.function_id, location.pc + 1),
|
||||
.next_location = { .function_id = location.function_id, .pc = location.pc + 3 },
|
||||
};
|
||||
case OperandType::Int32:
|
||||
return {
|
||||
.instruction = inst,
|
||||
.operator_ = bytecode_.get_code_int32(location.function_id, location.pc + 1),
|
||||
.next_location = { .function_id = location.function_id, .pc = location.pc + 5 },
|
||||
};
|
||||
}
|
||||
|
||||
throw std::logic_error("Should not get here");
|
||||
}
|
||||
|
||||
std::string Code::disassemble() const
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
#ifndef TYCHE_CODE_HH
|
||||
#define TYCHE_CODE_HH
|
||||
|
||||
#include "instruction.hh"
|
||||
#include "location.hh"
|
||||
#include "value.hh"
|
||||
#include "../bytecode/bytecode.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
struct Operation
|
||||
{
|
||||
Instruction instruction;
|
||||
int32_t operator_;
|
||||
Location next_location;
|
||||
};
|
||||
|
||||
class Code {
|
||||
public:
|
||||
void import_bytecode(ByteArray incoming);
|
||||
FunctionId import_bytecode(ByteArray incoming);
|
||||
|
||||
[[nodiscard]] std::string disassemble() const;
|
||||
|
||||
[[nodiscard]] Operation operation(Location const& location) const;
|
||||
|
||||
private:
|
||||
Bytecode bytecode_;
|
||||
bc::Bytecode bytecode_;
|
||||
};
|
||||
|
||||
} // tyche
|
||||
|
||||
18
src/vm/expr.cc
Normal file
18
src/vm/expr.cc
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "expr.hh"
|
||||
|
||||
#include "vm_exceptions.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
Value binary_operation(Value const& a, Value const& b, BinaryOperationType op)
|
||||
{
|
||||
// TODO - this is temporary code
|
||||
|
||||
if (a.type() == Type::Integer && b.type() == Type::Integer && op == BinaryOperationType::Sum) {
|
||||
return Value::CreateInteger(a.as_integer() + b.as_integer());
|
||||
} else {
|
||||
throw VMInvalidOperation(op, a.type(), b.type());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
src/vm/expr.hh
Normal file
12
src/vm/expr.hh
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef TYCHE_EXPR_HH
|
||||
#define TYCHE_EXPR_HH
|
||||
#include "value.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
enum class BinaryOperationType { Sum };
|
||||
|
||||
Value binary_operation(Value const& a, Value const& b, BinaryOperationType op);
|
||||
|
||||
}
|
||||
#endif //TYCHE_EXPR_HH
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "instruction.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper)
|
||||
{
|
||||
@@ -98,30 +98,48 @@ std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper)
|
||||
out = "???";
|
||||
}
|
||||
|
||||
if ((uint8_t) inst < 0xa0)
|
||||
OperandType operands = instruction_operand_type(inst);
|
||||
|
||||
if (operands == OperandType::NoOperand)
|
||||
return { out, 1 };
|
||||
|
||||
out += " " + std::to_string(oper);
|
||||
if ((uint8_t) inst >= 0xe0)
|
||||
if (operands == OperandType::Int32)
|
||||
return { out, 5 };
|
||||
else if ((uint8_t) inst >= 0xc0)
|
||||
if (operands == OperandType::Int16)
|
||||
return { out, 3 };
|
||||
else
|
||||
|
||||
return { out, 2 };
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> debug_instruction(Bytecode const& bt, uint32_t function_id, uint32_t addr)
|
||||
std::pair<std::string, size_t> debug_instruction(bc::Bytecode const& bt, uint32_t function_id, uint32_t addr)
|
||||
{
|
||||
auto inst = (Instruction) bt.get_code_byte(function_id, addr);
|
||||
|
||||
if ((uint8_t) inst >= 0xe0)
|
||||
return debug_instruction(inst, bt.get_code_int32(function_id, addr + 1));
|
||||
else if ((uint8_t) inst >= 0xc0)
|
||||
return debug_instruction(inst, bt.get_code_int16(function_id, addr + 1));
|
||||
else if ((uint8_t) inst >= 0xa0)
|
||||
return debug_instruction(inst, bt.get_code_int8(function_id, addr + 1));
|
||||
|
||||
switch (instruction_operand_type(inst)) {
|
||||
case OperandType::NoOperand:
|
||||
return debug_instruction(inst);
|
||||
case OperandType::Int8:
|
||||
return debug_instruction(inst, bt.get_code_int8(function_id, addr + 1));
|
||||
case OperandType::Int16:
|
||||
return debug_instruction(inst, bt.get_code_int16(function_id, addr + 1));
|
||||
case OperandType::Int32:
|
||||
return debug_instruction(inst, bt.get_code_int32(function_id, addr + 1));
|
||||
default:
|
||||
}
|
||||
|
||||
return { "???", 1 };
|
||||
}
|
||||
|
||||
OperandType instruction_operand_type(Instruction inst)
|
||||
{
|
||||
if ((uint8_t) inst >= 0xe0)
|
||||
return OperandType::Int32;
|
||||
if ((uint8_t) inst >= 0xc0)
|
||||
return OperandType::Int16;
|
||||
if ((uint8_t) inst >= 0xa0)
|
||||
return OperandType::Int8;
|
||||
return OperandType::NoOperand;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "../bytecode/bytecode.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
enum class Instruction : uint8_t {
|
||||
|
||||
@@ -96,7 +96,10 @@ enum class Instruction : uint8_t {
|
||||
};
|
||||
|
||||
std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper=0);
|
||||
std::pair<std::string, size_t> debug_instruction(Bytecode const& bt, uint32_t function_id, uint32_t addr);
|
||||
std::pair<std::string, size_t> debug_instruction(bc::Bytecode const& bt, uint32_t function_id, uint32_t addr);
|
||||
|
||||
enum class OperandType { NoOperand, Int8, Int16, Int32 };
|
||||
OperandType instruction_operand_type(Instruction instruction);
|
||||
|
||||
}
|
||||
|
||||
|
||||
16
src/vm/location.hh
Normal file
16
src/vm/location.hh
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef TYCHE_LOCATION_HH
|
||||
#define TYCHE_LOCATION_HH
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
struct Location
|
||||
{
|
||||
uint32_t function_id;
|
||||
uint32_t pc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TYCHE_LOCATION_HH
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "vm_exceptions.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
Stack::Stack()
|
||||
{
|
||||
@@ -16,7 +16,7 @@ void Stack::push(Value const& value)
|
||||
|
||||
Value Stack::pop()
|
||||
{
|
||||
if (stack_.size() <= fps_.size())
|
||||
if (stack_.size() <= fps_.top())
|
||||
throw VMStackUnderflow();
|
||||
|
||||
Value v = stack_.back();
|
||||
@@ -58,4 +58,15 @@ void Stack::pop_fp()
|
||||
fps_.pop();
|
||||
}
|
||||
|
||||
std::string Stack::debug() const
|
||||
{
|
||||
if (stack_.empty())
|
||||
return "empty";
|
||||
|
||||
std::string out;
|
||||
for (size_t i = 0; i < stack_.size(); ++i)
|
||||
out += "[" + stack_.at(i).to_string() + "] ";
|
||||
return out;
|
||||
}
|
||||
|
||||
} // tyche
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "value.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
class Stack {
|
||||
public:
|
||||
@@ -21,6 +21,10 @@ public:
|
||||
void push_fp();
|
||||
void pop_fp();
|
||||
|
||||
[[nodiscard]] size_t fp_level() const { return fps_.size(); }
|
||||
|
||||
[[nodiscard]] std::string debug() const;
|
||||
|
||||
private:
|
||||
std::vector<Value> stack_;
|
||||
std::stack<size_t> fps_;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "../bytecode/bytecodeprototype.hh"
|
||||
#include "../bytecode/bytearray.hh"
|
||||
#include "../common/bytearray.hh"
|
||||
#include "../bytecode/bytecode.hh"
|
||||
#include "code.hh"
|
||||
#include "stack.hh"
|
||||
#include "vm.hh"
|
||||
|
||||
using namespace tyche;
|
||||
using namespace tyche::bc;
|
||||
using namespace tyche::vm;
|
||||
|
||||
TEST(Code, ImportSingleAndDebug)
|
||||
{
|
||||
@@ -68,6 +71,27 @@ TEST(Stack, FramePointer)
|
||||
ASSERT_EQ(stack.at(-2).as_integer(), 10);
|
||||
}
|
||||
|
||||
TEST(VM, BasicCode)
|
||||
{
|
||||
// code (2+3)
|
||||
BytecodePrototype bp;
|
||||
bp.functions.emplace_back(0, 0);
|
||||
bp.functions.at(0).code.append_byte((uint8_t) Instruction::PushInt8);
|
||||
bp.functions.at(0).code.append_int8(2);
|
||||
bp.functions.at(0).code.append_byte((uint8_t) Instruction::PushInt8);
|
||||
bp.functions.at(0).code.append_int8(3);
|
||||
bp.functions.at(0).code.append_byte((uint8_t) Instruction::Sum);
|
||||
bp.functions.at(0).code.append_byte((uint8_t) Instruction::Return);
|
||||
ByteArray ba = Bytecode::generate(bp);
|
||||
|
||||
VM vm;
|
||||
vm.load_bytecode(std::move(ba));
|
||||
vm.call(0);
|
||||
|
||||
int32_t result = vm.to_integer(-1);
|
||||
ASSERT_EQ(result, 5);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "../common/overloaded.hh"
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
Type Value::type() const
|
||||
{
|
||||
@@ -11,6 +11,18 @@ Type Value::type() const
|
||||
[](int32_t) { return Type::Integer; },
|
||||
[](float) { return Type::Float; },
|
||||
[](std::string const&) { return Type::String; },
|
||||
[](Function const&) { return Type::Function; },
|
||||
}, value_);
|
||||
}
|
||||
|
||||
std::string Value::to_string() const
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[](std::monostate) { return std::string("nil"); },
|
||||
[](int32_t i) { return std::to_string(i); },
|
||||
[](float f) { return std::to_string(f); },
|
||||
[](std::string const& s) { return s; },
|
||||
[](Function const& f) { return "@" + std::to_string(f.f_id); }
|
||||
}, value_);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,19 @@
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace tyche {
|
||||
namespace tyche::vm {
|
||||
|
||||
using FunctionId = uint32_t;
|
||||
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
Nil = 0, Integer, Float, String, Array, Table, Function, NativePointer,
|
||||
};
|
||||
|
||||
|
||||
class Value {
|
||||
struct Function { FunctionId f_id; };
|
||||
|
||||
public:
|
||||
Value() : value_(std::monostate()) {}
|
||||
|
||||
@@ -19,15 +24,19 @@ public:
|
||||
static Value CreateInteger(int32_t v) { return Value(v); }
|
||||
static Value CreateFloat(float f) { return Value(f); }
|
||||
static Value CreateString(std::string const& str) { return Value(str); }
|
||||
static Value CreateFunctionId(FunctionId f_id) { return Value(Function { f_id }); }
|
||||
|
||||
[[nodiscard]] Type type() const;
|
||||
|
||||
[[nodiscard]] int32_t as_integer() const { return std::get<int32_t>(value_); }
|
||||
[[nodiscard]] float as_float() const { return std::get<float>(value_); }
|
||||
[[nodiscard]] std::string as_string() const { return std::get<std::string>(value_); }
|
||||
[[nodiscard]] FunctionId as_function_id() const { return std::get<Function>(value_).f_id; }
|
||||
|
||||
[[nodiscard]] std::string to_string() const;
|
||||
|
||||
private:
|
||||
using Internal = std::variant<std::monostate, int32_t, float, std::string>;
|
||||
using Internal = std::variant<std::monostate, int32_t, float, std::string, Function>;
|
||||
Internal value_;
|
||||
|
||||
explicit Value(Internal const& internal) : value_(internal) {}
|
||||
|
||||
79
src/vm/vm.cc
Normal file
79
src/vm/vm.cc
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "vm.hh"
|
||||
|
||||
#include "vm_exceptions.hh"
|
||||
#include "expr.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
void VM::load_bytecode(ByteArray const& ba)
|
||||
{
|
||||
FunctionId f_id = code_.import_bytecode(ba);
|
||||
stack_.push(Value::CreateFunctionId(f_id));
|
||||
}
|
||||
|
||||
void VM::call(size_t n_params)
|
||||
{
|
||||
// TODO - parameters
|
||||
|
||||
Value f = stack_.pop();
|
||||
if (f.type() != Type::Function)
|
||||
throw VMTypeError(Type::Function, f.type());
|
||||
|
||||
loc_.emplace(f.as_function_id(), 0);
|
||||
stack_.push_fp();
|
||||
run_until_return();
|
||||
// stack_.pop_fp();
|
||||
loc_.pop();
|
||||
}
|
||||
|
||||
int32_t VM::to_integer(int index) const
|
||||
{
|
||||
Value i = stack_.at(index);
|
||||
assert_type(i, Type::Integer);
|
||||
return i.as_integer();
|
||||
}
|
||||
|
||||
void VM::push_integer(int32_t value)
|
||||
{
|
||||
stack_.push(Value::CreateInteger(value));
|
||||
}
|
||||
|
||||
void VM::run_until_return()
|
||||
{
|
||||
size_t level = stack_.fp_level();
|
||||
|
||||
while (stack_.fp_level() >= level)
|
||||
step();
|
||||
}
|
||||
|
||||
void VM::step()
|
||||
{
|
||||
Operation op = code_.operation(loc_.top());
|
||||
switch (op.instruction) {
|
||||
case Instruction::PushInt8:
|
||||
case Instruction::PushInt16:
|
||||
case Instruction::PushInt32:
|
||||
push_integer(op.operator_);
|
||||
break;
|
||||
case Instruction::Sum:
|
||||
stack_.push(binary_operation(stack_.pop(), stack_.pop(), BinaryOperationType::Sum));
|
||||
break;
|
||||
case Instruction::Return: {
|
||||
Value v = stack_.pop();
|
||||
stack_.pop_fp();
|
||||
stack_.push(v);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw VMInvalidOpcode((uint8_t) op.instruction);
|
||||
}
|
||||
loc_.top() = op.next_location;
|
||||
}
|
||||
|
||||
void VM::assert_type(Value const& val, Type type)
|
||||
{
|
||||
if (val.type() != type)
|
||||
throw VMTypeError(type, val.type());
|
||||
}
|
||||
|
||||
} // tyche
|
||||
35
src/vm/vm.hh
Normal file
35
src/vm/vm.hh
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef TYCHE_VM_HH
|
||||
#define TYCHE_VM_HH
|
||||
|
||||
#include "code.hh"
|
||||
#include "location.hh"
|
||||
#include "stack.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
class VM {
|
||||
public:
|
||||
void load_bytecode(ByteArray const& ba);
|
||||
|
||||
void call(size_t n_params);
|
||||
|
||||
[[nodiscard]] int32_t to_integer(int index) const;
|
||||
|
||||
void push_integer(int32_t value);
|
||||
|
||||
[[nodiscard]] std::string debug_stack() const { return stack_.debug(); }
|
||||
|
||||
private:
|
||||
void run_until_return();
|
||||
void step();
|
||||
|
||||
static void assert_type(Value const& val, Type type);
|
||||
|
||||
Stack stack_;
|
||||
Code code_;
|
||||
std::stack<Location> loc_;
|
||||
};
|
||||
|
||||
} // tyche
|
||||
|
||||
#endif //TYCHE_VM_HH
|
||||
@@ -4,7 +4,9 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace tyche {
|
||||
#include "expr.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
class VMRuntimeError : public std::runtime_error
|
||||
{
|
||||
@@ -24,6 +26,24 @@ public:
|
||||
explicit VMStackOutOfRange() : VMRuntimeError("Item does not exist in stack") {}
|
||||
};
|
||||
|
||||
class VMTypeError : public VMRuntimeError
|
||||
{
|
||||
public:
|
||||
explicit VMTypeError(Type expected, Type found) : VMRuntimeError("Type error") {} // TODO - print types
|
||||
};
|
||||
|
||||
class VMInvalidOpcode : public VMRuntimeError
|
||||
{
|
||||
public:
|
||||
explicit VMInvalidOpcode(uint8_t opcode) : VMRuntimeError("Invalid opcode " + std::to_string(opcode)) {}
|
||||
};
|
||||
|
||||
class VMInvalidOperation : public VMRuntimeError
|
||||
{
|
||||
public:
|
||||
explicit VMInvalidOperation(BinaryOperationType op, Type type1, Type type2) : VMRuntimeError("Invalid binary operation") {} // TODO - print types
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TYCHE_VM_EXCEPTIONS_HH
|
||||
|
||||
Reference in New Issue
Block a user