.
This commit is contained in:
8
TODO.md
8
TODO.md
@@ -31,11 +31,11 @@ After some additional development:
|
||||
- [x] Print stack
|
||||
- [x] Assembler
|
||||
- [ ] VM execution
|
||||
- [ ] Expressions
|
||||
- [x] Integer
|
||||
- [ ] Float
|
||||
- [ ] String
|
||||
- [ ] Stack operations (nil, integer, float, string, function)
|
||||
- [x] Integer
|
||||
- [x] Float
|
||||
- [x] String
|
||||
- [ ] Expressions
|
||||
- [x] Integer
|
||||
- [ ] Float
|
||||
- [ ] String
|
||||
|
||||
@@ -23,6 +23,8 @@ public:
|
||||
|
||||
[[nodiscard]] Operation operation(Location const& location) const;
|
||||
|
||||
[[nodiscard]] bc::Bytecode const& bytecode() const { return bytecode_; }
|
||||
|
||||
private:
|
||||
bc::Bytecode bytecode_;
|
||||
};
|
||||
|
||||
@@ -19,15 +19,15 @@ static int init_ = []() {
|
||||
|
||||
#define BIN_OP(op, t1, t2) binary_ops[(size_t) BinaryOperationType::op][(size_t) Type::t1][(size_t) Type::t2] = [](Value const& b, Value const& a)
|
||||
|
||||
BIN_OP(Sum, Integer, Integer) { return Value::CreateInteger(a.as_integer() + b.as_integer()); };
|
||||
BIN_OP(Sum, Integer, Integer) { return Value::createInteger(a.as_integer() + b.as_integer()); };
|
||||
|
||||
BIN_OP(Subtraction, Integer, Integer) { return Value::CreateInteger(a.as_integer() - b.as_integer()); };
|
||||
BIN_OP(Subtraction, Integer, Integer) { return Value::createInteger(a.as_integer() - b.as_integer()); };
|
||||
|
||||
BIN_OP(Multiplication, Integer, Integer) { return Value::CreateInteger(a.as_integer() * b.as_integer()); };
|
||||
BIN_OP(Multiplication, Integer, Integer) { return Value::createInteger(a.as_integer() * b.as_integer()); };
|
||||
|
||||
BIN_OP(Division, Integer, Integer) { return Value::CreateFloat((float) a.as_integer() / (float) b.as_integer()); };
|
||||
BIN_OP(Division, Integer, Integer) { return Value::createFloat((float) a.as_integer() / (float) b.as_integer()); };
|
||||
|
||||
BIN_OP(IntegerDivision, Integer, Integer) { return Value::CreateInteger(a.as_integer() / b.as_integer()); };
|
||||
BIN_OP(IntegerDivision, Integer, Integer) { return Value::createInteger(a.as_integer() / b.as_integer()); };
|
||||
|
||||
BIN_OP(Equality, Integer, Integer) { return Value::CreateIntegerFromBool(a.as_integer() == b.as_integer()); };
|
||||
|
||||
@@ -41,19 +41,19 @@ static int init_ = []() {
|
||||
|
||||
BIN_OP(GreaterThanOrEquals, Integer, Integer) { return Value::CreateIntegerFromBool(a.as_integer() >= b.as_integer()); };
|
||||
|
||||
BIN_OP(BitwiseAnd, Integer, Integer) { return Value::CreateInteger(a.as_integer() & b.as_integer()); };
|
||||
BIN_OP(BitwiseAnd, Integer, Integer) { return Value::createInteger(a.as_integer() & b.as_integer()); };
|
||||
|
||||
BIN_OP(BitwiseOr, Integer, Integer) { return Value::CreateInteger(a.as_integer() | b.as_integer()); };
|
||||
BIN_OP(BitwiseOr, Integer, Integer) { return Value::createInteger(a.as_integer() | b.as_integer()); };
|
||||
|
||||
BIN_OP(BitwiseXor, Integer, Integer) { return Value::CreateInteger(a.as_integer() ^ b.as_integer()); };
|
||||
BIN_OP(BitwiseXor, Integer, Integer) { return Value::createInteger(a.as_integer() ^ b.as_integer()); };
|
||||
|
||||
BIN_OP(Power, Integer, Integer) { return Value::CreateInteger((int32_t) powl(a.as_integer(), b.as_integer())); };
|
||||
BIN_OP(Power, Integer, Integer) { return Value::createInteger((int32_t) powl(a.as_integer(), b.as_integer())); };
|
||||
|
||||
BIN_OP(ShiftLeft, Integer, Integer) { return Value::CreateInteger(a.as_integer() << b.as_integer()); };
|
||||
BIN_OP(ShiftLeft, Integer, Integer) { return Value::createInteger(a.as_integer() << b.as_integer()); };
|
||||
|
||||
BIN_OP(ShiftRight, Integer, Integer) { return Value::CreateInteger(a.as_integer() >> b.as_integer()); };
|
||||
BIN_OP(ShiftRight, Integer, Integer) { return Value::createInteger(a.as_integer() >> b.as_integer()); };
|
||||
|
||||
BIN_OP(Modulo, Integer, Integer) { return Value::CreateInteger(a.as_integer() % b.as_integer()); };
|
||||
BIN_OP(Modulo, Integer, Integer) { return Value::createInteger(a.as_integer() % b.as_integer()); };
|
||||
|
||||
#undef BIN_OP
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const std::unordered_map<std::string, Instruction> instruction_names = {
|
||||
{ "pushc", Instruction::PushConstant8 },
|
||||
{ "pushz", Instruction::PushZero },
|
||||
{ "pusht", Instruction::PushTrue },
|
||||
{ "pushf", Instruction::PushFunction8 },
|
||||
{ "newa", Instruction::NewArray },
|
||||
{ "newt", Instruction::NewTable },
|
||||
{ "pop", Instruction::Pop },
|
||||
@@ -76,6 +77,11 @@ std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper)
|
||||
case Instruction::PushConstant32:
|
||||
out = "pushc";
|
||||
break;
|
||||
case Instruction::PushFunction8:
|
||||
case Instruction::PushFunction16:
|
||||
case Instruction::PushFunction32:
|
||||
out = "pushf";
|
||||
break;
|
||||
case Instruction::PushZero: out = "pushz"; break;
|
||||
case Instruction::PushTrue: out = "pusht"; break;
|
||||
case Instruction::NewArray: out = "newa"; break;
|
||||
|
||||
@@ -21,6 +21,9 @@ enum class Instruction : uint8_t {
|
||||
PushConstant8 = 0xa1,
|
||||
PushConstant16 = 0xc1,
|
||||
PushConstant32 = 0xe1,
|
||||
PushFunction8 = 0xa2,
|
||||
PushFunction16 = 0xc2,
|
||||
PushFunction32 = 0xe2,
|
||||
PushZero = 0x00,
|
||||
PushTrue = 0x01,
|
||||
NewArray = 0x02,
|
||||
|
||||
@@ -24,6 +24,14 @@ Value Stack::pop()
|
||||
return v;
|
||||
}
|
||||
|
||||
Value Stack::peek() const
|
||||
{
|
||||
if (stack_.size() <= fps_.top())
|
||||
throw VMStackUnderflow();
|
||||
|
||||
return stack_.back();
|
||||
}
|
||||
|
||||
Value Stack::at(int pos) const
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
|
||||
void push(Value const& value);
|
||||
Value pop();
|
||||
[[nodiscard]] Value peek() const;
|
||||
|
||||
[[nodiscard]] Value at(int pos) const;
|
||||
[[nodiscard]] size_t size() const;
|
||||
|
||||
@@ -11,6 +11,17 @@ using namespace tyche;
|
||||
using namespace tyche::bc;
|
||||
using namespace tyche::vm;
|
||||
|
||||
static VM run(std::string oper) {
|
||||
return VM().load_bytecode(as::Assembler(std::format(R"(
|
||||
.const
|
||||
0: 3.14
|
||||
1: "Hello world"
|
||||
.func 0
|
||||
{}
|
||||
ret
|
||||
)", oper)).assemble()).call(0);
|
||||
}
|
||||
|
||||
TEST(Code, ImportSingleAndDebug)
|
||||
{
|
||||
BytecodePrototype bp;
|
||||
@@ -35,9 +46,9 @@ TEST(Code, ImportSingleAndDebug)
|
||||
TEST(Stack, PushPullGet)
|
||||
{
|
||||
Stack stack;
|
||||
stack.push(Value::CreateInteger(10));
|
||||
stack.push(Value::CreateInteger(20));
|
||||
stack.push(Value::CreateInteger(30));
|
||||
stack.push(Value::createInteger(10));
|
||||
stack.push(Value::createInteger(20));
|
||||
stack.push(Value::createInteger(30));
|
||||
|
||||
ASSERT_EQ(stack.size(), 3);
|
||||
ASSERT_EQ(stack.at(0).as_integer(), 10);
|
||||
@@ -49,12 +60,12 @@ TEST(Stack, PushPullGet)
|
||||
TEST(Stack, FramePointer)
|
||||
{
|
||||
Stack stack;
|
||||
stack.push(Value::CreateInteger(10));
|
||||
stack.push(Value::CreateInteger(20));
|
||||
stack.push(Value::createInteger(10));
|
||||
stack.push(Value::createInteger(20));
|
||||
stack.push_fp();
|
||||
stack.push(Value::CreateInteger(30));
|
||||
stack.push(Value::CreateInteger(40));
|
||||
stack.push(Value::CreateInteger(50));
|
||||
stack.push(Value::createInteger(30));
|
||||
stack.push(Value::createInteger(40));
|
||||
stack.push(Value::createInteger(50));
|
||||
|
||||
ASSERT_EQ(stack.size(), 3);
|
||||
ASSERT_EQ(stack.at(0).as_integer(), 30);
|
||||
@@ -94,20 +105,14 @@ TEST(VM, BasicCode)
|
||||
|
||||
TEST(VM, StackOperations)
|
||||
{
|
||||
auto run = [](std::string oper) {
|
||||
return VM().load_bytecode(as::Assembler(std::format(R"(
|
||||
.const
|
||||
0: 3.14
|
||||
1: "Hello world"
|
||||
.func 0
|
||||
{}
|
||||
ret
|
||||
)", oper)).assemble()).call(0);
|
||||
};
|
||||
|
||||
ASSERT_EQ(run("pushi 5000").to_integer(-1), 5000);
|
||||
ASSERT_EQ(run("pushi -5000").to_integer(-1), -5000);
|
||||
ASSERT_FLOAT_EQ(run("pushi 5000").to_float(-1), 5000.f);
|
||||
ASSERT_FLOAT_EQ(run("pushc 0").to_float(-1), 3.14f);
|
||||
ASSERT_EQ(run("pushc 0").to_integer(-1), 3);
|
||||
ASSERT_EQ(run("pushc 1").to_string(-1), "Hello world");
|
||||
ASSERT_TRUE(run("pushf 0").is_function(-1));
|
||||
ASSERT_EQ(run("pushi 2\n pushi 3\n pop").to_integer(-1), 2);
|
||||
}
|
||||
|
||||
TEST(VM, IntegerIntegerOperations)
|
||||
@@ -144,7 +149,7 @@ TEST(VM, IntegerIntegerOperations)
|
||||
ASSERT_EQ(test_op(30, 2, "shr"), 7);
|
||||
ASSERT_EQ(test_op(8, 3, "mod"), 2);
|
||||
|
||||
// TODO - div
|
||||
ASSERT_FLOAT_EQ(run("pushi 3\n pushi 2\n div").to_float(-1), 1.5f);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
||||
@@ -22,13 +22,13 @@ class Value {
|
||||
public:
|
||||
Value() : value_(std::monostate()) {}
|
||||
|
||||
static Value CreateNil() { return Value(std::monostate()); }
|
||||
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 }); }
|
||||
static Value createNil() { return Value(std::monostate()); }
|
||||
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 }); }
|
||||
|
||||
static Value CreateIntegerFromBool(bool b) { return CreateInteger(b ? 1 : 0); }
|
||||
static Value CreateIntegerFromBool(bool b) { return createInteger(b ? 1 : 0); }
|
||||
|
||||
[[nodiscard]] Type type() const;
|
||||
|
||||
|
||||
66
src/vm/vm.cc
66
src/vm/vm.cc
@@ -8,7 +8,7 @@ namespace tyche::vm {
|
||||
VM& VM::load_bytecode(ByteArray const& ba)
|
||||
{
|
||||
FunctionId f_id = code_.import_bytecode(ba);
|
||||
stack_.push(Value::CreateFunctionId(f_id));
|
||||
stack_.push(Value::createFunctionId(f_id));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -32,26 +32,51 @@ VM& VM::call(size_t n_params)
|
||||
int32_t VM::to_integer(int index) const
|
||||
{
|
||||
Value i = stack_.at(index);
|
||||
assert_type(i, Type::Integer);
|
||||
return i.as_integer();
|
||||
if (i.type() == Type::Integer)
|
||||
return i.as_integer();
|
||||
if (i.type() == Type::Float)
|
||||
return (int32_t) i.as_float();
|
||||
throw VMTypeError(Type::Integer, i.type());
|
||||
}
|
||||
|
||||
float VM::to_float(int index) const
|
||||
{
|
||||
Value f = stack_.at(index);
|
||||
if (f.type() == Type::Float)
|
||||
return f.as_float();
|
||||
if (f.type() == Type::Integer)
|
||||
return (float) f.as_integer();
|
||||
throw VMTypeError(Type::Float, f.type());
|
||||
}
|
||||
|
||||
std::string VM::to_string(int index) const
|
||||
{
|
||||
Value i = stack_.at(index);
|
||||
assert_type(i, Type::Float);
|
||||
return i.as_float();
|
||||
assert_type(i, Type::String);
|
||||
return i.as_string();
|
||||
}
|
||||
|
||||
VM& VM::push_nil()
|
||||
{
|
||||
stack_.push(Value::createNil());
|
||||
return *this;
|
||||
}
|
||||
|
||||
VM& VM::push_integer(int32_t value)
|
||||
{
|
||||
stack_.push(Value::CreateInteger(value));
|
||||
stack_.push(Value::createInteger(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
VM& VM::push_float(float value)
|
||||
{
|
||||
stack_.push(Value::CreateFloat(value));
|
||||
stack_.push(Value::createFloat(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
VM& VM::push_string(std::string const& str)
|
||||
{
|
||||
stack_.push(Value::createString(str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -79,6 +104,33 @@ void VM::step()
|
||||
push_integer(op.operator_);
|
||||
break;
|
||||
|
||||
case Instruction::PushConstant8:
|
||||
case Instruction::PushConstant16:
|
||||
case Instruction::PushConstant32: {
|
||||
auto cnst = code_.bytecode().get_constant(op.operator_);
|
||||
if (auto f = std::get_if<float>(&cnst))
|
||||
push_float(*f);
|
||||
else if (auto s = std::get_if<std::string>(&cnst))
|
||||
push_string(*s);
|
||||
else
|
||||
throw std::logic_error("Shouldn't get here");
|
||||
break;
|
||||
}
|
||||
|
||||
case Instruction::PushFunction8:
|
||||
case Instruction::PushFunction16:
|
||||
case Instruction::PushFunction32:
|
||||
stack_.push(Value::createFunctionId(op.operator_));
|
||||
break;
|
||||
|
||||
case Instruction::Pop:
|
||||
stack_.pop();
|
||||
break;
|
||||
|
||||
case Instruction::Duplicate:
|
||||
stack_.push(stack_.peek());
|
||||
break;
|
||||
|
||||
//
|
||||
// logical/arithmetic
|
||||
//
|
||||
|
||||
20
src/vm/vm.hh
20
src/vm/vm.hh
@@ -13,13 +13,27 @@ public:
|
||||
|
||||
VM& call(size_t n_params);
|
||||
|
||||
[[nodiscard]] int32_t to_integer(int index) const;
|
||||
[[nodiscard]] float to_float(int index) const;
|
||||
[[nodiscard]] bool is_nil(int index) const { return stack_.at(index).type() == Type::Nil; }
|
||||
[[nodiscard]] bool is_integer(int index) const { return stack_.at(index).type() == Type::Integer; }
|
||||
[[nodiscard]] bool is_float(int index) const { return stack_.at(index).type() == Type::Float; }
|
||||
[[nodiscard]] bool is_string(int index) const { return stack_.at(index).type() == Type::String; }
|
||||
[[nodiscard]] bool is_array(int index) const { return stack_.at(index).type() == Type::Array; }
|
||||
[[nodiscard]] bool is_table(int index) const { return stack_.at(index).type() == Type::Table; }
|
||||
[[nodiscard]] bool is_function(int index) const { return stack_.at(index).type() == Type::Function; }
|
||||
[[nodiscard]] bool is_native_pointer(int index) const { return stack_.at(index).type() == Type::NativePointer; }
|
||||
|
||||
[[nodiscard]] size_t stack_sz() const { return stack_.size(); }
|
||||
|
||||
VM& push_nil();
|
||||
VM& push_integer(int32_t value);
|
||||
VM& push_float(float value);
|
||||
VM& push_string(std::string const& string);
|
||||
|
||||
[[nodiscard]] std::string debug_stack() const { return stack_.debug(); }
|
||||
[[nodiscard]] int32_t to_integer(int index) const;
|
||||
[[nodiscard]] float to_float(int index) const;
|
||||
[[nodiscard]] std::string to_string(int index) const;
|
||||
|
||||
[[nodiscard]] std::string debug_stack() const { return stack_.debug(); }
|
||||
|
||||
private:
|
||||
void run_until_return();
|
||||
|
||||
Reference in New Issue
Block a user