2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,3 +32,5 @@
|
|||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
cmake-build-*/
|
||||||
|
build/
|
||||||
|
|||||||
33
TODO.md
33
TODO.md
@@ -19,7 +19,7 @@ After some additional development:
|
|||||||
|
|
||||||
## VM
|
## VM
|
||||||
|
|
||||||
- [ ] VM
|
- [x] VM
|
||||||
- [x] Code
|
- [x] Code
|
||||||
- [x] Simple bytecode loader
|
- [x] Simple bytecode loader
|
||||||
- [x] Output bytecode format
|
- [x] Output bytecode format
|
||||||
@@ -29,10 +29,37 @@ After some additional development:
|
|||||||
- [x] Code execution (except functions)
|
- [x] Code execution (except functions)
|
||||||
- [x] Functions
|
- [x] Functions
|
||||||
- [x] Print stack
|
- [x] Print stack
|
||||||
|
- [x] Assembler
|
||||||
- [ ] Assembler
|
- [ ] VM execution
|
||||||
|
- [x] Stack operations (nil, integer, float, string, function)
|
||||||
|
- [x] Integer
|
||||||
|
- [x] Float
|
||||||
|
- [x] String
|
||||||
|
- [x] Expressions
|
||||||
|
- [x] Integer
|
||||||
|
- [x] Float
|
||||||
|
- [x] String
|
||||||
|
- [ ] Local/global variables
|
||||||
|
- [ ] Functions
|
||||||
|
- [ ] Constants
|
||||||
|
- [ ] Other operations
|
||||||
|
- [ ] Arrays
|
||||||
|
- [ ] Iteration
|
||||||
|
- [ ] Expressions
|
||||||
|
- [ ] Tables
|
||||||
|
- [ ] Iteration
|
||||||
|
- [ ] Metatables
|
||||||
|
- [ ] Expressions
|
||||||
|
- [ ] Control flow
|
||||||
|
- [ ] Compilation
|
||||||
|
- [ ] Error handling
|
||||||
|
- [ ] C++ API
|
||||||
|
- [ ] Run native code on VM
|
||||||
|
- [ ] Run tyche code from C++
|
||||||
|
- [ ] C API
|
||||||
|
|
||||||
After some additional development:
|
After some additional development:
|
||||||
- [ ] Bytecode loader
|
- [ ] Bytecode loader
|
||||||
- Combine multiple chunks
|
- Combine multiple chunks
|
||||||
- Resolve function ids, constant ids, etc
|
- Resolve function ids, constant ids, etc
|
||||||
|
- [ ] Upvalues
|
||||||
18
doc/OPCODES
18
doc/OPCODES
@@ -37,7 +37,7 @@ Local variables:
|
|||||||
|
|
||||||
Function operations:
|
Function operations:
|
||||||
a7 c7 e7 call [n_pars] Enter function on stack toplevel (passing n next stack values as parameters)
|
a7 c7 e7 call [n_pars] Enter function on stack toplevel (passing n next stack values as parameters)
|
||||||
10 ret Leave a function (return value in stack)
|
10 ret Leave a function (return value in stack)
|
||||||
11 retn Leave a function (return nil)
|
11 retn Leave a function (return nil)
|
||||||
|
|
||||||
Table and array operations:
|
Table and array operations:
|
||||||
@@ -65,17 +65,21 @@ Logical/arithmetic:
|
|||||||
2b and Bitwise AND
|
2b and Bitwise AND
|
||||||
2c or Bitwise OR
|
2c or Bitwise OR
|
||||||
2d xor Bitwise XOR
|
2d xor Bitwise XOR
|
||||||
|
2e pow Power
|
||||||
|
2f shl Shift left
|
||||||
|
30 shr Shift right
|
||||||
|
31 mod Modulo
|
||||||
|
|
||||||
Other value operations:
|
Other value operations:
|
||||||
30 len Get table, array or string size
|
40 len Get table, array or string size
|
||||||
31 type Get type from value at the top of the stack
|
41 type Get type from value at the top of the stack
|
||||||
b0 cast [type] Cast type to another type
|
b0 cast [type] Cast type to another type
|
||||||
32 ver Return VM version
|
42 ver Return VM version
|
||||||
|
|
||||||
External code:
|
External code:
|
||||||
38 cmpl Compile code to assembly
|
48 cmpl Compile code to assembly
|
||||||
39 asmbl Assemble code to bytecode format
|
49 asmbl Assemble code to bytecode format
|
||||||
3a load Load bytecode as function (will place function on stack)
|
4a load Load bytecode as function (will place function on stack)
|
||||||
|
|
||||||
Control flow:
|
Control flow:
|
||||||
a8 c8 e8 bz [pc] Branch if zero
|
a8 c8 e8 bz [pc] Branch if zero
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ ByteArray Assembler::assemble()
|
|||||||
case vm::OperandType::Int8: bp.functions.at(function_id).code.append_int8((int8_t) *oper); break;
|
case vm::OperandType::Int8: bp.functions.at(function_id).code.append_int8((int8_t) *oper); break;
|
||||||
case vm::OperandType::Int16: bp.functions.at(function_id).code.append_int16((int16_t) *oper); break;
|
case vm::OperandType::Int16: bp.functions.at(function_id).code.append_int16((int16_t) *oper); break;
|
||||||
case vm::OperandType::Int32: bp.functions.at(function_id).code.append_int32(*oper); break;
|
case vm::OperandType::Int32: bp.functions.at(function_id).code.append_int32(*oper); break;
|
||||||
case vm::OperandType::NoOperand: default:
|
case vm::OperandType::NoOperand: default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tt.type != TokenType::Enter)
|
if (tt.type != TokenType::Enter)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace tyche::as {
|
|||||||
|
|
||||||
class Assembler {
|
class Assembler {
|
||||||
public:
|
public:
|
||||||
explicit Assembler(std::string source) : lexer_(std::move(source)) {}
|
explicit Assembler(std::string source) : lexer_(std::move(source) + "\n") {}
|
||||||
|
|
||||||
[[nodiscard]] ByteArray assemble();
|
[[nodiscard]] ByteArray assemble();
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ std::string ByteArray::hexdump() const
|
|||||||
{
|
{
|
||||||
auto to_hex = [](uint32_t value, size_t n_chars) -> std::string {
|
auto to_hex = [](uint32_t value, size_t n_chars) -> std::string {
|
||||||
char buf[15];
|
char buf[15];
|
||||||
snprintf(buf, sizeof buf, (std::string("%0") + std::to_string(n_chars) + "X").c_str(), value);
|
snprintf(buf, sizeof buf, "%0*X", (int) n_chars, value);
|
||||||
return { buf };
|
return { buf };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ Operation Code::operation(Location const& location) const
|
|||||||
.operator_ = bytecode_.get_code_int32(location.function_id, location.pc + 1),
|
.operator_ = bytecode_.get_code_int32(location.function_id, location.pc + 1),
|
||||||
.next_location = { .function_id = location.function_id, .pc = location.pc + 5 },
|
.next_location = { .function_id = location.function_id, .pc = location.pc + 5 },
|
||||||
};
|
};
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::logic_error("Should not get here");
|
throw std::logic_error("Should not get here");
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] Operation operation(Location const& location) const;
|
[[nodiscard]] Operation operation(Location const& location) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bc::Bytecode const& bytecode() const { return bytecode_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bc::Bytecode bytecode_;
|
bc::Bytecode bytecode_;
|
||||||
};
|
};
|
||||||
|
|||||||
110
src/vm/expr.cc
110
src/vm/expr.cc
@@ -1,18 +1,114 @@
|
|||||||
#include "expr.hh"
|
#include "expr.hh"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "vm_exceptions.hh"
|
#include "vm_exceptions.hh"
|
||||||
|
|
||||||
namespace tyche::vm {
|
namespace tyche::vm {
|
||||||
|
|
||||||
|
std::function<Value(Value const&, Value const&)> binary_ops[(size_t) BinaryOperationType::COUNT][(size_t) Type::COUNT][(size_t) Type::COUNT];
|
||||||
|
|
||||||
|
static int init_ = []() {
|
||||||
|
// every combination, except when explicit, return type error
|
||||||
|
for (size_t i = 0; i < (size_t) BinaryOperationType::COUNT; ++i) {
|
||||||
|
for (size_t j = 0; j < (size_t) Type::COUNT; ++j) {
|
||||||
|
for (size_t k = 0; k < (size_t) Type::COUNT; ++k) {
|
||||||
|
binary_ops[i][j][k] = [&i](Value const& a, Value const& b) -> Value {
|
||||||
|
throw VMInvalidOperation((BinaryOperationType) i, a.type(), b.type());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// every equality/inequality, by default, return inequal
|
||||||
|
for (size_t j = 0; j < (size_t) Type::COUNT; ++j) {
|
||||||
|
for (size_t k = 0; k < (size_t) Type::COUNT; ++k) {
|
||||||
|
binary_ops[(size_t) BinaryOperationType::Equality][j][k] = [](Value const&, Value const&) { return Value::createFalse(); };
|
||||||
|
binary_ops[(size_t) BinaryOperationType::Inequality][j][k] = [](Value const&, Value const&) { return Value::createTrue(); };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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, Float) { return Value::createFloat((float) a.as_integer() + b.as_float()); };
|
||||||
|
BIN_OP(Sum, Float, Integer) { return Value::createFloat(a.as_float() + (float) b.as_integer()); };
|
||||||
|
BIN_OP(Sum, Float, Float) { return Value::createFloat(a.as_float() + b.as_float()); };
|
||||||
|
BIN_OP(Sum, String, String) { return Value::createString(a.as_string() + b.as_string()); };
|
||||||
|
|
||||||
|
BIN_OP(Subtraction, Integer, Integer) { return Value::createInteger(a.as_integer() - b.as_integer()); };
|
||||||
|
BIN_OP(Subtraction, Integer, Float) { return Value::createFloat((float) a.as_integer() - b.as_float()); };
|
||||||
|
BIN_OP(Subtraction, Float, Integer) { return Value::createFloat(a.as_float() - (float) b.as_integer()); };
|
||||||
|
BIN_OP(Subtraction, Float, Float) { return Value::createFloat(a.as_float() - b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(Multiplication, Integer, Integer) { return Value::createInteger(a.as_integer() * b.as_integer()); };
|
||||||
|
BIN_OP(Multiplication, Integer, Float) { return Value::createFloat((float) a.as_integer() * b.as_float()); };
|
||||||
|
BIN_OP(Multiplication, Float, Integer) { return Value::createFloat(a.as_float() * (float) b.as_integer()); };
|
||||||
|
BIN_OP(Multiplication, Float, Float) { return Value::createFloat(a.as_float() * b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(Division, Integer, Integer) { return Value::createFloat((float) a.as_integer() / (float) b.as_integer()); };
|
||||||
|
BIN_OP(Division, Integer, Float) { return Value::createFloat((float) a.as_integer() / b.as_float()); };
|
||||||
|
BIN_OP(Division, Float, Integer) { return Value::createFloat(a.as_float() / (float) b.as_integer()); };
|
||||||
|
BIN_OP(Division, Float, Float) { return Value::createFloat(a.as_float() / b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(IntegerDivision, Integer, Integer) { return Value::createInteger(a.as_integer() / b.as_integer()); };
|
||||||
|
BIN_OP(IntegerDivision, Integer, Float) { return Value::createInteger(a.as_integer() / (int32_t) b.as_float()); };
|
||||||
|
BIN_OP(IntegerDivision, Float, Integer) { return Value::createInteger((int32_t) a.as_float() / b.as_integer()); };
|
||||||
|
BIN_OP(IntegerDivision, Float, Float) { return Value::createInteger((int32_t) a.as_float() / (int32_t) b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(Equality, Integer, Integer) { return Value::createIntegerFromBool(a.as_integer() == b.as_integer()); };
|
||||||
|
BIN_OP(Equality, Integer, Float) { return Value::createIntegerFromBool(std::abs((float) a.as_integer() - b.as_float()) < FLOAT_EPSILON); };
|
||||||
|
BIN_OP(Equality, Float, Integer) { return Value::createIntegerFromBool(std::abs(a.as_float() - (float) b.as_integer()) < FLOAT_EPSILON); };
|
||||||
|
BIN_OP(Equality, Float, Float) { return Value::createIntegerFromBool(std::abs(a.as_float() - b.as_float()) < FLOAT_EPSILON); };
|
||||||
|
BIN_OP(Equality, String, String) { return Value::createIntegerFromBool(a.as_string() == b.as_string()); };
|
||||||
|
|
||||||
|
BIN_OP(Inequality, Integer, Integer) { return Value::createIntegerFromBool(a.as_integer() != b.as_integer()); };
|
||||||
|
BIN_OP(Inequality, Integer, Float) { return Value::createIntegerFromBool(std::abs((float) a.as_integer() - b.as_float()) >= FLOAT_EPSILON); };
|
||||||
|
BIN_OP(Inequality, Float, Integer) { return Value::createIntegerFromBool(std::abs(a.as_float() - (float) b.as_integer()) >= FLOAT_EPSILON); };
|
||||||
|
BIN_OP(Inequality, Float, Float) { return Value::createIntegerFromBool(std::abs(a.as_float() - b.as_float()) >= FLOAT_EPSILON); };
|
||||||
|
BIN_OP(Inequality, String, String) { return Value::createIntegerFromBool(a.as_string() != b.as_string()); };
|
||||||
|
|
||||||
|
BIN_OP(LessThan, Integer, Integer) { return Value::createIntegerFromBool(a.as_integer() < b.as_integer()); };
|
||||||
|
BIN_OP(LessThan, Integer, Float) { return Value::createIntegerFromBool((float) a.as_integer() < b.as_float()); };
|
||||||
|
BIN_OP(LessThan, Float, Integer) { return Value::createIntegerFromBool(a.as_float() < (float) b.as_integer()); };
|
||||||
|
BIN_OP(LessThan, Float, Float) { return Value::createIntegerFromBool(a.as_float() < b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(LessThanOrEquals, Integer, Integer) { return Value::createIntegerFromBool(a.as_integer() <= b.as_integer()); };
|
||||||
|
BIN_OP(LessThanOrEquals, Integer, Float) { return Value::createIntegerFromBool((float) a.as_integer() <= b.as_float()); };
|
||||||
|
BIN_OP(LessThanOrEquals, Float, Integer) { return Value::createIntegerFromBool(a.as_float() <= (float) b.as_integer()); };
|
||||||
|
BIN_OP(LessThanOrEquals, Float, Float) { return Value::createIntegerFromBool(a.as_float() <= b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(GreaterThan, Integer, Integer) { return Value::createIntegerFromBool(a.as_integer() > b.as_integer()); };
|
||||||
|
BIN_OP(GreaterThan, Integer, Float) { return Value::createIntegerFromBool((float) a.as_integer() > b.as_float()); };
|
||||||
|
BIN_OP(GreaterThan, Float, Integer) { return Value::createIntegerFromBool(a.as_float() > (float) b.as_integer()); };
|
||||||
|
BIN_OP(GreaterThan, Float, Float) { return Value::createIntegerFromBool(a.as_float() > b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(GreaterThanOrEquals, Integer, Integer) { return Value::createIntegerFromBool(a.as_integer() >= b.as_integer()); };
|
||||||
|
BIN_OP(GreaterThanOrEquals, Integer, Float) { return Value::createIntegerFromBool((float) a.as_integer() >= b.as_float()); };
|
||||||
|
BIN_OP(GreaterThanOrEquals, Float, Integer) { return Value::createIntegerFromBool(a.as_float() >= (float) b.as_integer()); };
|
||||||
|
BIN_OP(GreaterThanOrEquals, Float, Float) { return Value::createIntegerFromBool(a.as_float() >= b.as_float()); };
|
||||||
|
|
||||||
|
BIN_OP(Power, Integer, Integer) { return Value::createInteger((int32_t) powl(a.as_integer(), b.as_integer())); };
|
||||||
|
BIN_OP(Power, Integer, Float) { return Value::createFloat(powf((float) a.as_integer(), b.as_float())); };
|
||||||
|
BIN_OP(Power, Float, Integer) { return Value::createFloat(powf(a.as_float(), (float) b.as_integer())); };
|
||||||
|
BIN_OP(Power, Float, Float) { return Value::createFloat(powf(a.as_float(), b.as_float())); };
|
||||||
|
|
||||||
|
BIN_OP(Modulo, 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(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(BitwiseXor, Integer, Integer) { return Value::createInteger(a.as_integer() ^ b.as_integer()); };
|
||||||
|
|
||||||
|
#undef BIN_OP
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}();
|
||||||
|
|
||||||
Value binary_operation(Value const& a, Value const& b, BinaryOperationType op)
|
Value binary_operation(Value const& a, Value const& b, BinaryOperationType op)
|
||||||
{
|
{
|
||||||
// TODO - this is temporary code
|
return binary_ops[(size_t) op][(size_t) b.type()][(size_t) a.type()](a, b);
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,16 @@
|
|||||||
|
|
||||||
namespace tyche::vm {
|
namespace tyche::vm {
|
||||||
|
|
||||||
enum class BinaryOperationType { Sum };
|
enum class BinaryOperationType
|
||||||
|
{
|
||||||
|
Sum, Subtraction, Multiplication, Division, IntegerDivision,
|
||||||
|
Equality, Inequality, LessThan, LessThanOrEquals,
|
||||||
|
GreaterThan, GreaterThanOrEquals, Power, Modulo,
|
||||||
|
BitwiseAnd, BitwiseOr, BitwiseXor, ShiftLeft, ShiftRight,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr float FLOAT_EPSILON = 0.000001f;
|
||||||
|
|
||||||
Value binary_operation(Value const& a, Value const& b, BinaryOperationType op);
|
Value binary_operation(Value const& a, Value const& b, BinaryOperationType op);
|
||||||
|
|
||||||
|
|||||||
@@ -5,54 +5,59 @@
|
|||||||
|
|
||||||
namespace tyche::vm {
|
namespace tyche::vm {
|
||||||
|
|
||||||
const std::unordered_map<std::string, vm::Instruction> instruction_names = {
|
const std::unordered_map<std::string, Instruction> instruction_names = {
|
||||||
{ "pushi", vm::Instruction::PushInt8 },
|
{ "pushi", Instruction::PushInt8 },
|
||||||
{ "pushc", vm::Instruction::PushConstant8 },
|
{ "pushc", Instruction::PushConstant8 },
|
||||||
{ "pushz", vm::Instruction::PushZero },
|
{ "pushz", Instruction::PushZero },
|
||||||
{ "pusht", vm::Instruction::PushTrue },
|
{ "pusht", Instruction::PushTrue },
|
||||||
{ "newa", vm::Instruction::NewArray },
|
{ "pushf", Instruction::PushFunction8 },
|
||||||
{ "newt", vm::Instruction::NewTable },
|
{ "newa", Instruction::NewArray },
|
||||||
{ "pop", vm::Instruction::Pop },
|
{ "newt", Instruction::NewTable },
|
||||||
{ "dup", vm::Instruction::Duplicate },
|
{ "pop", Instruction::Pop },
|
||||||
{ "setl", vm::Instruction::SetLocal8 },
|
{ "dup", Instruction::Duplicate },
|
||||||
{ "getl", vm::Instruction::GetLocal8 },
|
{ "setl", Instruction::SetLocal8 },
|
||||||
{ "setg", vm::Instruction::SetGlobal8 },
|
{ "getl", Instruction::GetLocal8 },
|
||||||
{ "getl", vm::Instruction::GetGlobal8 },
|
{ "setg", Instruction::SetGlobal8 },
|
||||||
{ "call8", vm::Instruction::Call8 },
|
{ "getl", Instruction::GetGlobal8 },
|
||||||
{ "ret", vm::Instruction::Return },
|
{ "call8", Instruction::Call8 },
|
||||||
{ "retn", vm::Instruction::ReturnNil },
|
{ "ret", Instruction::Return },
|
||||||
{ "getkv", vm::Instruction::GetKeyValue },
|
{ "retn", Instruction::ReturnNil },
|
||||||
{ "setkv", vm::Instruction::SetKeyValue },
|
{ "getkv", Instruction::GetKeyValue },
|
||||||
{ "geta", vm::Instruction::GetArrayItem },
|
{ "setkv", Instruction::SetKeyValue },
|
||||||
{ "seta", vm::Instruction::SetArrayItem },
|
{ "geta", Instruction::GetArrayItem },
|
||||||
{ "appnd", vm::Instruction::Append },
|
{ "seta", Instruction::SetArrayItem },
|
||||||
{ "next", vm::Instruction::Next },
|
{ "appnd", Instruction::Append },
|
||||||
{ "smt", vm::Instruction::SetMetatable },
|
{ "next", Instruction::Next },
|
||||||
{ "mt", vm::Instruction::GetMetatable },
|
{ "smt", Instruction::SetMetatable },
|
||||||
{ "sum", vm::Instruction::Sum },
|
{ "mt", Instruction::GetMetatable },
|
||||||
{ "sub", vm::Instruction::Subtract },
|
{ "sum", Instruction::Sum },
|
||||||
{ "mul", vm::Instruction::Multiply },
|
{ "sub", Instruction::Subtract },
|
||||||
{ "div", vm::Instruction::Divide },
|
{ "mul", Instruction::Multiply },
|
||||||
{ "idiv", vm::Instruction::DivideInt },
|
{ "div", Instruction::Divide },
|
||||||
{ "eq", vm::Instruction::Equals },
|
{ "idiv", Instruction::DivideInt },
|
||||||
{ "neq", vm::Instruction::NotEquals },
|
{ "eq", Instruction::Equals },
|
||||||
{ "lt", vm::Instruction::LessThan },
|
{ "neq", Instruction::NotEquals },
|
||||||
{ "lte", vm::Instruction::LessThanEq },
|
{ "lt", Instruction::LessThan },
|
||||||
{ "gt", vm::Instruction::GreaterThan },
|
{ "lte", Instruction::LessThanEq },
|
||||||
{ "gte", vm::Instruction::GreaterThanEq },
|
{ "gt", Instruction::GreaterThan },
|
||||||
{ "and", vm::Instruction::And },
|
{ "gte", Instruction::GreaterThanEq },
|
||||||
{ "or", vm::Instruction::Or },
|
{ "and", Instruction::And },
|
||||||
{ "xor", vm::Instruction::Xor },
|
{ "or", Instruction::Or },
|
||||||
{ "len", vm::Instruction::Len },
|
{ "xor", Instruction::Xor },
|
||||||
{ "type", vm::Instruction::Type },
|
{ "pow", Instruction::Power },
|
||||||
{ "cast", vm::Instruction::Cast },
|
{ "shl", Instruction::ShiftLeft },
|
||||||
{ "ver", vm::Instruction::Version },
|
{ "shr", Instruction::ShiftRight },
|
||||||
{ "bz", vm::Instruction::BranchIfZero8 },
|
{ "mod", Instruction::Modulo },
|
||||||
{ "bnz", vm::Instruction::BranchIfNotZero8 },
|
{ "len", Instruction::Len },
|
||||||
{ "jmp", vm::Instruction::Jump8 },
|
{ "type", Instruction::Type },
|
||||||
{ "cmpl", vm::Instruction::Compile },
|
{ "cast", Instruction::Cast },
|
||||||
{ "asmbl", vm::Instruction::Assemble },
|
{ "ver", Instruction::Version },
|
||||||
{ "load", vm::Instruction::Load },
|
{ "bz", Instruction::BranchIfZero8 },
|
||||||
|
{ "bnz", Instruction::BranchIfNotZero8 },
|
||||||
|
{ "jmp", Instruction::Jump8 },
|
||||||
|
{ "cmpl", Instruction::Compile },
|
||||||
|
{ "asmbl", Instruction::Assemble },
|
||||||
|
{ "load", Instruction::Load },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +77,11 @@ std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper)
|
|||||||
case Instruction::PushConstant32:
|
case Instruction::PushConstant32:
|
||||||
out = "pushc";
|
out = "pushc";
|
||||||
break;
|
break;
|
||||||
|
case Instruction::PushFunction8:
|
||||||
|
case Instruction::PushFunction16:
|
||||||
|
case Instruction::PushFunction32:
|
||||||
|
out = "pushf";
|
||||||
|
break;
|
||||||
case Instruction::PushZero: out = "pushz"; break;
|
case Instruction::PushZero: out = "pushz"; break;
|
||||||
case Instruction::PushTrue: out = "pusht"; break;
|
case Instruction::PushTrue: out = "pusht"; break;
|
||||||
case Instruction::NewArray: out = "newa"; break;
|
case Instruction::NewArray: out = "newa"; break;
|
||||||
@@ -127,6 +137,10 @@ std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper)
|
|||||||
case Instruction::And: out = "and"; break;
|
case Instruction::And: out = "and"; break;
|
||||||
case Instruction::Or: out = "or"; break;
|
case Instruction::Or: out = "or"; break;
|
||||||
case Instruction::Xor: out = "xor"; break;
|
case Instruction::Xor: out = "xor"; break;
|
||||||
|
case Instruction::Power: out = "pow"; break;
|
||||||
|
case Instruction::ShiftLeft: out = "shl"; break;
|
||||||
|
case Instruction::ShiftRight: out = "shr"; break;
|
||||||
|
case Instruction::Modulo: out = "mod"; break;
|
||||||
case Instruction::Len: out = "len"; break;
|
case Instruction::Len: out = "len"; break;
|
||||||
case Instruction::Type: out = "type"; break;
|
case Instruction::Type: out = "type"; break;
|
||||||
case Instruction::Cast: out = "cast"; break;
|
case Instruction::Cast: out = "cast"; break;
|
||||||
@@ -181,6 +195,7 @@ std::pair<std::string, size_t> debug_instruction(bc::Bytecode const& bt, uint32_
|
|||||||
case OperandType::Int32:
|
case OperandType::Int32:
|
||||||
return debug_instruction(inst, bt.get_code_int32(function_id, addr + 1));
|
return debug_instruction(inst, bt.get_code_int32(function_id, addr + 1));
|
||||||
default:
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { "???", 1 };
|
return { "???", 1 };
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ enum class Instruction : uint8_t {
|
|||||||
PushConstant8 = 0xa1,
|
PushConstant8 = 0xa1,
|
||||||
PushConstant16 = 0xc1,
|
PushConstant16 = 0xc1,
|
||||||
PushConstant32 = 0xe1,
|
PushConstant32 = 0xe1,
|
||||||
|
PushFunction8 = 0xa2,
|
||||||
|
PushFunction16 = 0xc2,
|
||||||
|
PushFunction32 = 0xe2,
|
||||||
PushZero = 0x00,
|
PushZero = 0x00,
|
||||||
PushTrue = 0x01,
|
PushTrue = 0x01,
|
||||||
NewArray = 0x02,
|
NewArray = 0x02,
|
||||||
@@ -74,12 +77,16 @@ enum class Instruction : uint8_t {
|
|||||||
And = 0x2b,
|
And = 0x2b,
|
||||||
Or = 0x2c,
|
Or = 0x2c,
|
||||||
Xor = 0x2d,
|
Xor = 0x2d,
|
||||||
|
Power = 0x2e,
|
||||||
|
ShiftLeft = 0x2f,
|
||||||
|
ShiftRight = 0x30,
|
||||||
|
Modulo = 0x31,
|
||||||
|
|
||||||
// other value operations
|
// other value operations
|
||||||
Len = 0x30,
|
Len = 0x40,
|
||||||
Type = 0x31,
|
Type = 0x41,
|
||||||
Cast = 0x32,
|
Cast = 0x42,
|
||||||
Version = 0x33,
|
Version = 0x43,
|
||||||
|
|
||||||
// control flow
|
// control flow
|
||||||
BranchIfZero8 = 0xa8,
|
BranchIfZero8 = 0xa8,
|
||||||
@@ -93,9 +100,9 @@ enum class Instruction : uint8_t {
|
|||||||
Jump32 = 0xea,
|
Jump32 = 0xea,
|
||||||
|
|
||||||
// external code
|
// external code
|
||||||
Compile = 0x38,
|
Compile = 0x48,
|
||||||
Assemble = 0x39,
|
Assemble = 0x49,
|
||||||
Load = 0x3a,
|
Load = 0x4a,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper=0);
|
std::pair<std::string, size_t> debug_instruction(Instruction inst, int oper=0);
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ Value Stack::pop()
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value Stack::peek() const
|
||||||
|
{
|
||||||
|
if (stack_.size() <= fps_.top())
|
||||||
|
throw VMStackUnderflow();
|
||||||
|
|
||||||
|
return stack_.back();
|
||||||
|
}
|
||||||
|
|
||||||
Value Stack::at(int pos) const
|
Value Stack::at(int pos) const
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public:
|
|||||||
|
|
||||||
void push(Value const& value);
|
void push(Value const& value);
|
||||||
Value pop();
|
Value pop();
|
||||||
|
[[nodiscard]] Value peek() const;
|
||||||
|
|
||||||
[[nodiscard]] Value at(int pos) const;
|
[[nodiscard]] Value at(int pos) const;
|
||||||
[[nodiscard]] size_t size() const;
|
[[nodiscard]] size_t size() const;
|
||||||
|
|||||||
194
src/vm/tests.cc
194
src/vm/tests.cc
@@ -1,8 +1,8 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "../bytecode/bytecodeprototype.hh"
|
#include "../bytecode/bytecodeprototype.hh"
|
||||||
#include "../common/bytearray.hh"
|
|
||||||
#include "../bytecode/bytecode.hh"
|
#include "../bytecode/bytecode.hh"
|
||||||
|
#include "../assembler/assembler.hh"
|
||||||
#include "code.hh"
|
#include "code.hh"
|
||||||
#include "stack.hh"
|
#include "stack.hh"
|
||||||
#include "vm.hh"
|
#include "vm.hh"
|
||||||
@@ -11,6 +11,17 @@ using namespace tyche;
|
|||||||
using namespace tyche::bc;
|
using namespace tyche::bc;
|
||||||
using namespace tyche::vm;
|
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)
|
TEST(Code, ImportSingleAndDebug)
|
||||||
{
|
{
|
||||||
BytecodePrototype bp;
|
BytecodePrototype bp;
|
||||||
@@ -35,9 +46,9 @@ TEST(Code, ImportSingleAndDebug)
|
|||||||
TEST(Stack, PushPullGet)
|
TEST(Stack, PushPullGet)
|
||||||
{
|
{
|
||||||
Stack stack;
|
Stack stack;
|
||||||
stack.push(Value::CreateInteger(10));
|
stack.push(Value::createInteger(10));
|
||||||
stack.push(Value::CreateInteger(20));
|
stack.push(Value::createInteger(20));
|
||||||
stack.push(Value::CreateInteger(30));
|
stack.push(Value::createInteger(30));
|
||||||
|
|
||||||
ASSERT_EQ(stack.size(), 3);
|
ASSERT_EQ(stack.size(), 3);
|
||||||
ASSERT_EQ(stack.at(0).as_integer(), 10);
|
ASSERT_EQ(stack.at(0).as_integer(), 10);
|
||||||
@@ -49,12 +60,12 @@ TEST(Stack, PushPullGet)
|
|||||||
TEST(Stack, FramePointer)
|
TEST(Stack, FramePointer)
|
||||||
{
|
{
|
||||||
Stack stack;
|
Stack stack;
|
||||||
stack.push(Value::CreateInteger(10));
|
stack.push(Value::createInteger(10));
|
||||||
stack.push(Value::CreateInteger(20));
|
stack.push(Value::createInteger(20));
|
||||||
stack.push_fp();
|
stack.push_fp();
|
||||||
stack.push(Value::CreateInteger(30));
|
stack.push(Value::createInteger(30));
|
||||||
stack.push(Value::CreateInteger(40));
|
stack.push(Value::createInteger(40));
|
||||||
stack.push(Value::CreateInteger(50));
|
stack.push(Value::createInteger(50));
|
||||||
|
|
||||||
ASSERT_EQ(stack.size(), 3);
|
ASSERT_EQ(stack.size(), 3);
|
||||||
ASSERT_EQ(stack.at(0).as_integer(), 30);
|
ASSERT_EQ(stack.at(0).as_integer(), 30);
|
||||||
@@ -92,6 +103,171 @@ TEST(VM, BasicCode)
|
|||||||
ASSERT_EQ(result, 5);
|
ASSERT_EQ(result, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(VM, StackOperations)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
auto test_op = [](int32_t op1, int32_t op2, std::string oper) {
|
||||||
|
return VM().load_bytecode(as::Assembler(std::format(R"(
|
||||||
|
.func 0
|
||||||
|
pushi {}
|
||||||
|
pushi {}
|
||||||
|
{}
|
||||||
|
ret
|
||||||
|
)", op1, op2, oper)).assemble()).call(0).to_integer(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_EQ(test_op(2, 3, "sum"), 5);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "sub"), -1);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "mul"), 6);
|
||||||
|
ASSERT_EQ(test_op(20, 3, "idiv"), 6);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "eq"), 0);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "neq"), 1);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "lt"), 1);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "lte"), 1);
|
||||||
|
ASSERT_EQ(test_op(3, 3, "lte"), 1);
|
||||||
|
ASSERT_EQ(test_op(4, 3, "lte"), 0);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "gt"), 0);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "gte"), 0);
|
||||||
|
ASSERT_EQ(test_op(3, 3, "gte"), 1);
|
||||||
|
ASSERT_EQ(test_op(4, 3, "gte"), 1);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "and"), 2);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "or"), 3);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "xor"), 1);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "pow"), 8);
|
||||||
|
ASSERT_EQ(test_op(2, 3, "shl"), 16);
|
||||||
|
ASSERT_EQ(test_op(30, 2, "shr"), 7);
|
||||||
|
ASSERT_EQ(test_op(8, 3, "mod"), 2);
|
||||||
|
|
||||||
|
ASSERT_FLOAT_EQ(run("pushi 3\n pushi 2\n div").to_float(-1), 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VM, IntegerFloatOperations)
|
||||||
|
{
|
||||||
|
auto test_op = [](int op1, std::string const& op2, std::string oper) -> VM {
|
||||||
|
return VM().load_bytecode(as::Assembler(std::format(R"(
|
||||||
|
.const
|
||||||
|
0: {}
|
||||||
|
.func 0
|
||||||
|
pushi {}
|
||||||
|
pushc 0
|
||||||
|
{}
|
||||||
|
ret
|
||||||
|
)", op2, op1, oper)).assemble()).call(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_FLOAT_EQ(test_op(2, "3.5", "sum").to_float(-1), 5.5f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op(2, "3.5", "sub").to_float(-1), -1.5f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op(2, "3.5", "mul").to_float(-1), 7.f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op(20, "3.5", "idiv").to_integer(-1), 6);
|
||||||
|
ASSERT_FLOAT_EQ(test_op(20, "3.5", "div").to_float(-1), 5.7142859);
|
||||||
|
ASSERT_FLOAT_EQ(test_op(3, "3.5", "eq").to_integer(-1), 0);
|
||||||
|
ASSERT_FLOAT_EQ(test_op(3, "3.0", "eq").to_integer(-1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VM, FloatIntegerOperations)
|
||||||
|
{
|
||||||
|
auto test_op = [](std::string const& op1, int op2, std::string oper) -> VM {
|
||||||
|
return VM().load_bytecode(as::Assembler(std::format(R"(
|
||||||
|
.const
|
||||||
|
0: {}
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushi {}
|
||||||
|
{}
|
||||||
|
ret
|
||||||
|
)", op1, op2, oper)).assemble()).call(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", 2, "sum").to_float(-1), 5.5f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", 2, "sub").to_float(-1), 1.5f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", 2, "mul").to_float(-1), 7.f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", 2, "idiv").to_integer(-1), 1);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", 2, "div").to_float(-1), 1.75f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", 3, "eq").to_integer(-1), 0);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.0", 3, "eq").to_integer(-1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VM, FloatFloatOperations)
|
||||||
|
{
|
||||||
|
auto test_op = [](std::string const& op1, std::string const& op2, std::string oper) -> VM {
|
||||||
|
return VM().load_bytecode(as::Assembler(std::format(R"(
|
||||||
|
.const
|
||||||
|
0: {}
|
||||||
|
1: {}
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
{}
|
||||||
|
ret
|
||||||
|
)", op1, op2, oper)).assemble()).call(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", "2.2", "sum").to_float(-1), 5.7f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", "2.2", "sub").to_float(-1), 1.3f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", "2.2", "mul").to_float(-1), 7.7f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.5", "2.2", "idiv").to_integer(-1), 1);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("4.5", "2.5", "div").to_float(-1), 1.8f);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.2005", "3.2", "eq").to_integer(-1), 0);
|
||||||
|
ASSERT_FLOAT_EQ(test_op("3.2", "3.2", "eq").to_integer(-1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VM, StringString)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(run(R"(
|
||||||
|
.const
|
||||||
|
0: "Hello"
|
||||||
|
1: "World"
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
sum
|
||||||
|
ret
|
||||||
|
)").to_string(-1), "HelloWorld");
|
||||||
|
|
||||||
|
ASSERT_EQ(run(R"(
|
||||||
|
.const
|
||||||
|
0: "Hello"
|
||||||
|
1: "World"
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
eq
|
||||||
|
ret
|
||||||
|
)").to_integer(-1), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(run(R"(
|
||||||
|
.const
|
||||||
|
0: "Hello"
|
||||||
|
1: "Hello"
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushc 1
|
||||||
|
eq
|
||||||
|
ret
|
||||||
|
)").to_integer(-1), 1);
|
||||||
|
|
||||||
|
ASSERT_EQ(run(R"(
|
||||||
|
.const
|
||||||
|
0: "Hello"
|
||||||
|
.func 0
|
||||||
|
pushc 0
|
||||||
|
pushi 1
|
||||||
|
eq
|
||||||
|
ret
|
||||||
|
)").to_integer(-1), 0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
|||||||
@@ -4,6 +4,21 @@
|
|||||||
|
|
||||||
namespace tyche::vm {
|
namespace tyche::vm {
|
||||||
|
|
||||||
|
std::string type_name(Type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case Type::Nil: return "nil";
|
||||||
|
case Type::Integer: return "integer";
|
||||||
|
case Type::Float: return "float";
|
||||||
|
case Type::String: return "string";
|
||||||
|
case Type::Array: return "array";
|
||||||
|
case Type::Table: return "table";
|
||||||
|
case Type::Function: return "function";
|
||||||
|
case Type::NativePointer: return "native pointer";
|
||||||
|
case Type::COUNT: default: return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Type Value::type() const
|
Type Value::type() const
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef TYCHE_VALUE_HH
|
#ifndef TYCHE_VALUE_HH
|
||||||
#define TYCHE_VALUE_HH
|
#define TYCHE_VALUE_HH
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
@@ -10,9 +11,10 @@ using FunctionId = uint32_t;
|
|||||||
|
|
||||||
enum class Type : uint8_t
|
enum class Type : uint8_t
|
||||||
{
|
{
|
||||||
Nil = 0, Integer, Float, String, Array, Table, Function, NativePointer,
|
Nil = 0, Integer, Float, String, Array, Table, Function, NativePointer, COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string type_name(Type type);
|
||||||
|
|
||||||
class Value {
|
class Value {
|
||||||
struct Function { FunctionId f_id; };
|
struct Function { FunctionId f_id; };
|
||||||
@@ -20,11 +22,15 @@ class Value {
|
|||||||
public:
|
public:
|
||||||
Value() : value_(std::monostate()) {}
|
Value() : value_(std::monostate()) {}
|
||||||
|
|
||||||
static Value CreateNil() { return Value(std::monostate()); }
|
static Value createNil() { return Value(std::monostate()); }
|
||||||
static Value CreateInteger(int32_t v) { return Value(v); }
|
static Value createInteger(int32_t v) { return Value(v); }
|
||||||
static Value CreateFloat(float f) { return Value(f); }
|
static Value createFloat(float f) { return Value(f); }
|
||||||
static Value CreateString(std::string const& str) { return Value(str); }
|
static Value createString(std::string const& str) { return Value(str); }
|
||||||
static Value CreateFunctionId(FunctionId f_id) { return Value(Function { f_id }); }
|
static Value createFunctionId(FunctionId f_id) { return Value(Function { f_id }); }
|
||||||
|
|
||||||
|
static Value createFalse() { return createInteger(0); }
|
||||||
|
static Value createTrue() { return createInteger(1); }
|
||||||
|
static Value createIntegerFromBool(bool b) { return createInteger(b ? 1 : 0); }
|
||||||
|
|
||||||
[[nodiscard]] Type type() const;
|
[[nodiscard]] Type type() const;
|
||||||
|
|
||||||
|
|||||||
120
src/vm/vm.cc
120
src/vm/vm.cc
@@ -5,13 +5,14 @@
|
|||||||
|
|
||||||
namespace tyche::vm {
|
namespace tyche::vm {
|
||||||
|
|
||||||
void VM::load_bytecode(ByteArray const& ba)
|
VM& VM::load_bytecode(ByteArray const& ba)
|
||||||
{
|
{
|
||||||
FunctionId f_id = code_.import_bytecode(ba);
|
FunctionId f_id = code_.import_bytecode(ba);
|
||||||
stack_.push(Value::CreateFunctionId(f_id));
|
stack_.push(Value::createFunctionId(f_id));
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::call(size_t n_params)
|
VM& VM::call(size_t n_params)
|
||||||
{
|
{
|
||||||
// TODO - parameters
|
// TODO - parameters
|
||||||
|
|
||||||
@@ -24,18 +25,59 @@ void VM::call(size_t n_params)
|
|||||||
run_until_return();
|
run_until_return();
|
||||||
// stack_.pop_fp();
|
// stack_.pop_fp();
|
||||||
loc_.pop();
|
loc_.pop();
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t VM::to_integer(int index) const
|
int32_t VM::to_integer(int index) const
|
||||||
{
|
{
|
||||||
Value i = stack_.at(index);
|
Value i = stack_.at(index);
|
||||||
assert_type(i, Type::Integer);
|
if (i.type() == Type::Integer)
|
||||||
return i.as_integer();
|
return i.as_integer();
|
||||||
|
if (i.type() == Type::Float)
|
||||||
|
return (int32_t) i.as_float();
|
||||||
|
throw VMTypeError(Type::Integer, i.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::push_integer(int32_t value)
|
float VM::to_float(int index) const
|
||||||
{
|
{
|
||||||
stack_.push(Value::CreateInteger(value));
|
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::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));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM& VM::push_float(float value)
|
||||||
|
{
|
||||||
|
stack_.push(Value::createFloat(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM& VM::push_string(std::string const& str)
|
||||||
|
{
|
||||||
|
stack_.push(Value::createString(str));
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::run_until_return()
|
void VM::run_until_return()
|
||||||
@@ -48,16 +90,76 @@ void VM::run_until_return()
|
|||||||
|
|
||||||
void VM::step()
|
void VM::step()
|
||||||
{
|
{
|
||||||
|
|
||||||
Operation op = code_.operation(loc_.top());
|
Operation op = code_.operation(loc_.top());
|
||||||
switch (op.instruction) {
|
switch (op.instruction) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// stack management
|
||||||
|
//
|
||||||
|
|
||||||
case Instruction::PushInt8:
|
case Instruction::PushInt8:
|
||||||
case Instruction::PushInt16:
|
case Instruction::PushInt16:
|
||||||
case Instruction::PushInt32:
|
case Instruction::PushInt32:
|
||||||
push_integer(op.operator_);
|
push_integer(op.operator_);
|
||||||
break;
|
break;
|
||||||
case Instruction::Sum:
|
|
||||||
stack_.push(binary_operation(stack_.pop(), stack_.pop(), BinaryOperationType::Sum));
|
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;
|
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
|
||||||
|
//
|
||||||
|
|
||||||
|
#define BIN_OP(op) { Value a = stack_.pop(); Value b = stack_.pop(); stack_.push(binary_operation(a, b, BinaryOperationType::op)); }
|
||||||
|
case Instruction::Sum: BIN_OP(Sum) break;
|
||||||
|
case Instruction::Subtract: BIN_OP(Subtraction) break;
|
||||||
|
case Instruction::Multiply: BIN_OP(Multiplication) break;
|
||||||
|
case Instruction::Divide: BIN_OP(Division) break;
|
||||||
|
case Instruction::DivideInt: BIN_OP(IntegerDivision) break;
|
||||||
|
case Instruction::Equals: BIN_OP(Equality) break;
|
||||||
|
case Instruction::NotEquals: BIN_OP(Inequality) break;
|
||||||
|
case Instruction::LessThan: BIN_OP(LessThan) break;
|
||||||
|
case Instruction::LessThanEq: BIN_OP(LessThanOrEquals) break;
|
||||||
|
case Instruction::GreaterThan: BIN_OP(GreaterThan) break;
|
||||||
|
case Instruction::GreaterThanEq: BIN_OP(GreaterThanOrEquals) break;
|
||||||
|
case Instruction::And: BIN_OP(BitwiseAnd) break;
|
||||||
|
case Instruction::Or: BIN_OP(BitwiseOr) break;
|
||||||
|
case Instruction::Xor: BIN_OP(BitwiseXor) break;
|
||||||
|
case Instruction::Power: BIN_OP(Power) break;
|
||||||
|
case Instruction::ShiftLeft: BIN_OP(ShiftLeft) break;
|
||||||
|
case Instruction::ShiftRight: BIN_OP(ShiftRight) break;
|
||||||
|
case Instruction::Modulo: BIN_OP(Modulo) break;
|
||||||
|
#undef BIN_OP
|
||||||
|
|
||||||
|
//
|
||||||
|
// function operations
|
||||||
|
//
|
||||||
|
|
||||||
case Instruction::Return: {
|
case Instruction::Return: {
|
||||||
Value v = stack_.pop();
|
Value v = stack_.pop();
|
||||||
stack_.pop_fp();
|
stack_.pop_fp();
|
||||||
|
|||||||
26
src/vm/vm.hh
26
src/vm/vm.hh
@@ -9,15 +9,31 @@ namespace tyche::vm {
|
|||||||
|
|
||||||
class VM {
|
class VM {
|
||||||
public:
|
public:
|
||||||
void load_bytecode(ByteArray const& ba);
|
VM& load_bytecode(ByteArray const& ba);
|
||||||
|
|
||||||
void call(size_t n_params);
|
VM& call(size_t n_params);
|
||||||
|
|
||||||
[[nodiscard]] int32_t to_integer(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; }
|
||||||
|
|
||||||
void push_integer(int32_t value);
|
[[nodiscard]] size_t stack_sz() const { return stack_.size(); }
|
||||||
|
|
||||||
[[nodiscard]] std::string debug_stack() const { return stack_.debug(); }
|
VM& push_nil();
|
||||||
|
VM& push_integer(int32_t value);
|
||||||
|
VM& push_float(float value);
|
||||||
|
VM& push_string(std::string const& string);
|
||||||
|
|
||||||
|
[[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:
|
private:
|
||||||
void run_until_return();
|
void run_until_return();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public:
|
|||||||
class VMTypeError : public VMRuntimeError
|
class VMTypeError : public VMRuntimeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VMTypeError(Type expected, Type found) : VMRuntimeError("Type error") {} // TODO - print types
|
explicit VMTypeError(Type expected, Type found) : VMRuntimeError("Type error (expected " + type_name(expected) + ", found " + type_name(found) + ")") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class VMInvalidOpcode : public VMRuntimeError
|
class VMInvalidOpcode : public VMRuntimeError
|
||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
class VMInvalidOperation : public VMRuntimeError
|
class VMInvalidOperation : public VMRuntimeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VMInvalidOperation(BinaryOperationType op, Type type1, Type type2) : VMRuntimeError("Invalid binary operation") {} // TODO - print types
|
explicit VMInvalidOperation(BinaryOperationType op, Type type1, Type type2) : VMRuntimeError("Invalid binary operation for types " + type_name(type1) + " and " + type_name(type2)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user