Compare commits
4 Commits
master
...
rethink-me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62d36f68b8 | ||
|
|
eee26cc767 | ||
|
|
223570478e | ||
|
|
ff23e14122 |
11
.idea/ctestState.xml
generated
Normal file
11
.idea/ctestState.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CidrCTestProjectState">
|
||||
<ctestState>
|
||||
<lineNumber testId="path:CMakeLists.txt test:tyche_as_test" value="116" />
|
||||
<lineNumber testId="path:CMakeLists.txt test:tyche_bytearray_test" value="102" />
|
||||
<lineNumber testId="path:CMakeLists.txt test:tyche_bytecode_test" value="106" />
|
||||
<lineNumber testId="path:CMakeLists.txt test:tyche_vm_test" value="110" />
|
||||
</ctestState>
|
||||
</component>
|
||||
</project>
|
||||
@@ -60,32 +60,34 @@ FetchContent_MakeAvailable(googletest)
|
||||
|
||||
add_library(lib${PROJECT_NAME} SHARED
|
||||
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/bytecode/constant.hh
|
||||
src/vm/code.cc
|
||||
src/vm/code.hh
|
||||
src/vm/instruction.hh
|
||||
src/vm/instruction.cc
|
||||
src/vm/value.cc
|
||||
src/vm/value.hh
|
||||
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
|
||||
src/assembler/lexer.cc
|
||||
src/assembler/lexer.hh
|
||||
src/assembler/assembler.cc
|
||||
src/assembler/assembler.hh
|
||||
src/assembler/as_exceptions.hh
|
||||
src/bytecode/bc_exceptions.hh
|
||||
src/bytearray/bytearray.hh
|
||||
src/bytearray/bytearray.cc
|
||||
src/bytearray/bytearraybuilder.hh
|
||||
src/bytearray/bytearraybuilder.cc
|
||||
#src/bytecode/bytecode.cc
|
||||
#src/bytecode/bytecode.hh
|
||||
#src/bytecode/bytecodeprototype.hh
|
||||
#src/bytecode/constant.hh
|
||||
#src/bytecode/bc_exceptions.hh
|
||||
#src/assembler/lexer.cc
|
||||
#src/assembler/lexer.hh
|
||||
#src/assembler/assembler.cc
|
||||
#src/assembler/assembler.hh
|
||||
#src/assembler/as_exceptions.hh
|
||||
#src/instructions/instruction.hh
|
||||
#src/instructions/instruction.cc
|
||||
#src/vm/code.cc
|
||||
#src/vm/code.hh
|
||||
#src/vm/value.cc
|
||||
#src/vm/value.hh
|
||||
#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})
|
||||
@@ -96,6 +98,10 @@ target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(${PROJECT_NAME}-bytearray-test src/bytearray/tests.cc)
|
||||
target_link_libraries(${PROJECT_NAME}-bytearray-test lib${PROJECT_NAME} gtest_main)
|
||||
add_test(NAME tyche_bytearray_test COMMAND ${PROJECT_NAME}-bytearray-test)
|
||||
|
||||
add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc)
|
||||
target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main)
|
||||
add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test)
|
||||
@@ -104,7 +110,9 @@ add_executable(${PROJECT_NAME}-vm-test src/vm/tests.cc)
|
||||
target_link_libraries(${PROJECT_NAME}-vm-test lib${PROJECT_NAME} gtest_main)
|
||||
add_test(NAME tyche_vm_test COMMAND ${PROJECT_NAME}-vm-test)
|
||||
|
||||
add_executable(${PROJECT_NAME}-as-test src/assembler/tests.cc)
|
||||
add_executable(${PROJECT_NAME}-as-test src/assembler/tests.cc
|
||||
src/bytearray/bytearraybuilder.cc
|
||||
src/bytearray/bytearraybuilder.hh)
|
||||
target_link_libraries(${PROJECT_NAME}-as-test lib${PROJECT_NAME} gtest_main)
|
||||
add_test(NAME tyche_as_test COMMAND ${PROJECT_NAME}-as-test)
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
#include "as_exceptions.hh"
|
||||
#include "../bytecode/bytecode.hh"
|
||||
#include "../vm/instruction.hh"
|
||||
#include "../instructions/instruction.hh"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace tyche::as {
|
||||
|
||||
ByteArray Assembler::assemble()
|
||||
StaticByteArray Assembler::assemble()
|
||||
{
|
||||
bc::BytecodePrototype bp;
|
||||
|
||||
@@ -61,16 +61,16 @@ ByteArray Assembler::assemble()
|
||||
tt = lexer_.ingest();
|
||||
}
|
||||
|
||||
auto oinst = vm::translate_instruction(instruction, oper);
|
||||
auto oinst = translate_instruction(instruction, oper);
|
||||
if (!oinst)
|
||||
throw AssemblyError("Invalid or misused instruction '" + instruction + "'", tt.line, tt.column);
|
||||
|
||||
bp.functions.at(function_id).code.append_byte((uint8_t) *oinst);
|
||||
switch (vm::instruction_operand_type(*oinst)) {
|
||||
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::Int32: bp.functions.at(function_id).code.append_int32(*oper); break;
|
||||
case vm::OperandType::NoOperand: default: break;
|
||||
switch (instruction_operand_type(*oinst)) {
|
||||
case OperandType::Int8: bp.functions.at(function_id).code.append_int8((int8_t) *oper); break;
|
||||
case OperandType::Int16: bp.functions.at(function_id).code.append_int16((int16_t) *oper); break;
|
||||
case OperandType::Int32: bp.functions.at(function_id).code.append_int32(*oper); break;
|
||||
case OperandType::NoOperand: default: break;
|
||||
}
|
||||
|
||||
if (tt.type != TokenType::Enter)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "lexer.hh"
|
||||
#include "../common/bytearray.hh"
|
||||
#include "../bytearray/bytearray.hh"
|
||||
#include "../bytecode/bytecodeprototype.hh"
|
||||
|
||||
namespace tyche::as {
|
||||
@@ -14,7 +14,7 @@ class Assembler {
|
||||
public:
|
||||
explicit Assembler(std::string source) : lexer_(std::move(source) + "\n") {}
|
||||
|
||||
[[nodiscard]] ByteArray assemble();
|
||||
[[nodiscard]] StaticByteArray assemble();
|
||||
|
||||
private:
|
||||
Lexer lexer_;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "lexer.hh"
|
||||
|
||||
#include <iostream>
|
||||
using namespace std::string_literals;
|
||||
|
||||
#include "as_exceptions.hh"
|
||||
|
||||
namespace tyche::as {
|
||||
@@ -119,4 +122,21 @@ void Lexer::ingest_next_token()
|
||||
current_token_ = { .type = type, .token = value, .line = current_line, .column = pos_ - current_line_pos };
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Token const& t)
|
||||
{
|
||||
switch (t.type) {
|
||||
case TokenType::BOF: os << "BOF"s; break;
|
||||
case TokenType::Directive: os << "Directive ("s << std::get<std::string>(t.token) << ")"s;
|
||||
case TokenType::Instruction: os << "Instruction ("s << std::get<std::string>(t.token) << ")"s;
|
||||
case TokenType::Integer: os << "Integer ("s << std::to_string(std::get<int>(t.token)) << ")"s;
|
||||
case TokenType::Float: os << "Float ("s << std::to_string(std::get<float>(t.token)) << ")"s;
|
||||
case TokenType::String: os << "String ("s << std::get<std::string>(t.token) << ")"s;
|
||||
case TokenType::Enter: os << "Enter"s;
|
||||
case TokenType::Colon: os << "Colon"s;
|
||||
case TokenType::EOF_: os << "EOF"s;
|
||||
default: os << "???"s;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
} // tyche
|
||||
|
||||
@@ -13,6 +13,7 @@ enum class TokenType {
|
||||
|
||||
using TokenValue = std::variant<std::monostate, int, float, std::string>;
|
||||
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
TokenValue token = std::monostate();
|
||||
@@ -21,6 +22,7 @@ struct Token {
|
||||
|
||||
friend bool operator==(Token const& lhs, Token const& rhs) { return std::tie(lhs.type, lhs.token) == std::tie(rhs.type, rhs.token); }
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, Token const& t);
|
||||
|
||||
std::string token_type_name(TokenType type);
|
||||
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
|
||||
#include "../bytecode/bytecodeprototype.hh"
|
||||
#include "../bytecode/bytecode.hh"
|
||||
#include "../vm/instruction.hh"
|
||||
#include "../instructions/instruction.hh"
|
||||
|
||||
using namespace tyche;
|
||||
using namespace tyche::as;
|
||||
using namespace tyche::bc;
|
||||
using namespace tyche::vm;
|
||||
|
||||
TEST(Lexer, Lexer)
|
||||
{
|
||||
@@ -48,7 +47,7 @@ TEST(Assember, Assembler)
|
||||
bp.functions.at(1).code.append_byte((uint8_t) Instruction::PushInt16);
|
||||
bp.functions.at(1).code.append_int16(5000);
|
||||
bp.functions.at(1).code.append_byte((uint8_t) Instruction::Return);
|
||||
ByteArray expected = Bytecode::generate(bp);
|
||||
StaticByteArray expected = Bytecode::generate(bp);
|
||||
|
||||
std::string src = R"(
|
||||
.const
|
||||
@@ -65,7 +64,7 @@ TEST(Assember, Assembler)
|
||||
ret
|
||||
)";
|
||||
|
||||
ByteArray actual = Assembler(src).assemble();
|
||||
StaticByteArray actual = Assembler(src).assemble();
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
|
||||
81
src/bytearray/bytearray.cc
Normal file
81
src/bytearray/bytearray.cc
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "bytearray.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace tyche {
|
||||
|
||||
uint8_t StaticByteArray::get_byte(uint32_t addr) const
|
||||
{
|
||||
return data_.at(addr);
|
||||
}
|
||||
|
||||
uint16_t StaticByteArray::get_uint16(uint32_t addr) const
|
||||
{
|
||||
return (uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8;
|
||||
}
|
||||
|
||||
uint32_t StaticByteArray::get_uint32(uint32_t addr) const
|
||||
{
|
||||
return (uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8
|
||||
| (uint32_t) get_byte(addr+2) << 16
|
||||
| (uint32_t) get_byte(addr+3) << 24;
|
||||
}
|
||||
|
||||
int8_t StaticByteArray::get_int8(uint32_t addr) const
|
||||
{
|
||||
return std::bit_cast<int8_t>(get_byte(addr));
|
||||
}
|
||||
|
||||
int16_t StaticByteArray::get_int16(uint32_t addr) const
|
||||
{
|
||||
return (uint16_t) get_byte(addr)
|
||||
| (uint16_t) get_byte(addr+1) << 8;
|
||||
}
|
||||
|
||||
int32_t StaticByteArray::get_int32(uint32_t addr) const
|
||||
{
|
||||
return std::bit_cast<int32_t>((uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8
|
||||
| (uint32_t) get_byte(addr+2) << 16
|
||||
| (uint32_t) get_byte(addr+3) << 24);
|
||||
}
|
||||
|
||||
float StaticByteArray::get_float(uint32_t addr) const
|
||||
{
|
||||
uint32_t bits = (uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8
|
||||
| (uint32_t) get_byte(addr+2) << 16
|
||||
| (uint32_t) get_byte(addr+3) << 24;
|
||||
float value;
|
||||
std::memcpy(&value, &bits, 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::pair<const char*, size_t> StaticByteArray::get_string_ptr(uint32_t addr) const
|
||||
{
|
||||
return { (const char *) &data_.at(addr), strlen((const char *) &data_.at(addr)) + 1 };
|
||||
}
|
||||
|
||||
std::string StaticByteArray::hexdump() const
|
||||
{
|
||||
auto to_hex = [](uint32_t value, size_t n_chars) -> std::string {
|
||||
char buf[15];
|
||||
snprintf(buf, sizeof buf, "%0*X", (int) n_chars, value);
|
||||
return { buf };
|
||||
};
|
||||
|
||||
std::string out;
|
||||
for (size_t i = 0; i < data_.size(); ++i) {
|
||||
if (i % 16 == 0)
|
||||
out += to_hex(i, 4) + " | ";
|
||||
out += to_hex(data_.at(i), 2) + " ";
|
||||
if (i % 16 == 15)
|
||||
out += "\n";
|
||||
}
|
||||
return out + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
44
src/bytearray/bytearray.hh
Normal file
44
src/bytearray/bytearray.hh
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef TYCHE_BYTEARRAY_HH
|
||||
#define TYCHE_BYTEARRAY_HH
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tyche {
|
||||
|
||||
class StaticByteArray {
|
||||
public:
|
||||
explicit StaticByteArray(std::vector<uint8_t> const& data) : data_(data) {}
|
||||
|
||||
explicit StaticByteArray(StaticByteArray const& ba) : data_(ba.data()) {}
|
||||
|
||||
// not assignable or moveable
|
||||
StaticByteArray(StaticByteArray&&) = delete;
|
||||
StaticByteArray& operator=(StaticByteArray const&) = delete;
|
||||
StaticByteArray& operator=(StaticByteArray&&) = delete;
|
||||
|
||||
[[nodiscard]] uint8_t get_byte(uint32_t addr) const;
|
||||
[[nodiscard]] uint16_t get_uint16(uint32_t addr) const;
|
||||
[[nodiscard]] uint32_t get_uint32(uint32_t addr) const;
|
||||
[[nodiscard]] int8_t get_int8(uint32_t addr) const;
|
||||
[[nodiscard]] int16_t get_int16(uint32_t addr) const;
|
||||
[[nodiscard]] int32_t get_int32(uint32_t addr) const;
|
||||
[[nodiscard]] float get_float(uint32_t addr) const;
|
||||
[[nodiscard]] std::pair<const char*, size_t> get_string_ptr(uint32_t addr) const;
|
||||
|
||||
[[nodiscard]] std::vector<uint8_t> const& data() const { return data_; }
|
||||
[[nodiscard]] size_t size() const { return data_.size(); }
|
||||
|
||||
[[nodiscard]] std::string hexdump() const;
|
||||
|
||||
friend bool operator==(StaticByteArray const& lhs, StaticByteArray const& rhs) { return lhs.data_ == rhs.data_; }
|
||||
|
||||
private:
|
||||
const std::vector<uint8_t> data_ {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TYCHE_BYTEARRAY_HH
|
||||
88
src/bytearray/bytearraybuilder.cc
Normal file
88
src/bytearray/bytearraybuilder.cc
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "bytearraybuilder.hh"
|
||||
|
||||
namespace tyche {
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_byte(uint32_t addr, uint8_t byte)
|
||||
{
|
||||
if (data_.size() < (addr + 1))
|
||||
data_.resize(addr + 1, 0);
|
||||
data_.at(addr) = byte;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_int8(uint32_t addr, int8_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_int16(uint32_t addr, int16_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_int32(uint32_t addr, int32_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
set_byte(addr+2, (uint8_t) (value >> 16));
|
||||
set_byte(addr+3, (uint8_t) (value >> 24));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_uint16(uint32_t addr, uint16_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_uint32(uint32_t addr, uint32_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
set_byte(addr+2, (uint8_t) (value >> 16));
|
||||
set_byte(addr+3, (uint8_t) (value >> 24));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_float(uint32_t addr, float value)
|
||||
{
|
||||
uint32_t bits;
|
||||
std::memcpy(&bits, &value, 4);
|
||||
set_byte(addr, (uint8_t) (bits));
|
||||
set_byte(addr+1, (uint8_t) (bits >> 8));
|
||||
set_byte(addr+2, (uint8_t) (bits >> 16));
|
||||
set_byte(addr+3, (uint8_t) (bits >> 24));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_string(uint32_t addr, std::string const& str)
|
||||
{
|
||||
for (uint8_t c: str)
|
||||
set_byte(addr++, c);
|
||||
set_byte(addr, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::set_bytearray(uint32_t addr, ByteArrayBuilder const& bytearray)
|
||||
{
|
||||
for (uint8_t byte: bytearray.data_)
|
||||
set_byte(addr++, byte);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArrayBuilder& ByteArrayBuilder::append_bytearray(ByteArrayBuilder const& bytearray)
|
||||
{
|
||||
data_.insert(data_.end(), bytearray.data_.begin(), bytearray.data_.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
StaticByteArray ByteArrayBuilder::build() const
|
||||
{
|
||||
return StaticByteArray(data_);
|
||||
}
|
||||
|
||||
}
|
||||
45
src/bytearray/bytearraybuilder.hh
Normal file
45
src/bytearray/bytearraybuilder.hh
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef TYCHE_BYTEARRAYBUILDER_HH
|
||||
#define TYCHE_BYTEARRAYBUILDER_HH
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "bytearray.hh"
|
||||
|
||||
namespace tyche {
|
||||
|
||||
class ByteArrayBuilder {
|
||||
public:
|
||||
ByteArrayBuilder& set_byte(uint32_t addr, uint8_t byte);
|
||||
ByteArrayBuilder& set_uint16(uint32_t addr, uint16_t value);
|
||||
ByteArrayBuilder& set_uint32(uint32_t addr, uint32_t value);
|
||||
ByteArrayBuilder& set_int8(uint32_t addr, int8_t value);
|
||||
ByteArrayBuilder& set_int16(uint32_t addr, int16_t value);
|
||||
ByteArrayBuilder& set_int32(uint32_t addr, int32_t value);
|
||||
ByteArrayBuilder& set_float(uint32_t addr, float value);
|
||||
ByteArrayBuilder& set_string(uint32_t addr, std::string const& str);
|
||||
ByteArrayBuilder& set_bytearray(uint32_t addr, ByteArrayBuilder const& bytearray);
|
||||
|
||||
ByteArrayBuilder& append_byte(uint8_t byte) { set_byte(data_.size(), byte); return *this; }
|
||||
ByteArrayBuilder& append_uint16(uint16_t value) { set_uint16(data_.size(), value); return *this; }
|
||||
ByteArrayBuilder& append_uint32(uint32_t value) { set_uint32(data_.size(), value); return *this; }
|
||||
ByteArrayBuilder& append_int8(int8_t value) { set_int8(data_.size(), value); return *this; }
|
||||
ByteArrayBuilder& append_int16(int16_t value) { set_int16(data_.size(), value); return *this; }
|
||||
ByteArrayBuilder& append_int32(int32_t value) { set_int32(data_.size(), value); return *this; }
|
||||
ByteArrayBuilder& append_float(float value) { set_float(data_.size(), value); return *this; }
|
||||
ByteArrayBuilder& append_string(std::string const& str) { set_string(data_.size(), str); return *this; }
|
||||
ByteArrayBuilder& append_bytearray(ByteArrayBuilder const& bytearray);
|
||||
|
||||
[[nodiscard]] std::vector<uint8_t> const& data() const { return data_; }
|
||||
[[nodiscard]] size_t size() const { return data_.size(); }
|
||||
|
||||
[[nodiscard]] StaticByteArray build() const;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data_ {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TYCHE_BYTEARRAYBUILDER_HH
|
||||
51
src/bytearray/tests.cc
Normal file
51
src/bytearray/tests.cc
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "bytearray.hh"
|
||||
#include "bytearraybuilder.hh"
|
||||
|
||||
using namespace tyche;
|
||||
|
||||
TEST(StaticByteArray, StaticByteArray)
|
||||
{
|
||||
auto test = [](std::function<void(ByteArrayBuilder&)> const& f, std::vector<uint8_t> const& expected) {
|
||||
ByteArrayBuilder ba;
|
||||
f(ba);
|
||||
ASSERT_EQ(ba.data().size(), expected.size());
|
||||
ASSERT_EQ(std::memcmp(ba.data().data(), expected.data(), ba.data().size()), 0);
|
||||
};
|
||||
|
||||
#define TESTX(a, ...) test([](ByteArrayBuilder& ba) { a; }, std::vector<uint8_t>({ __VA_ARGS__ }));
|
||||
|
||||
TESTX(ba.set_byte(1, 0xab), 0x00, 0xab)
|
||||
|
||||
ByteArrayBuilder ba;
|
||||
{ auto b = ba.set_byte(1, 0xab).build(); ASSERT_EQ(b.get_byte(1), 0xab); }
|
||||
|
||||
{ auto b = ba.set_int8(1, 12).build(); ASSERT_EQ(b.get_int8(1), 12); }
|
||||
{ auto b = ba.set_int8(1, -12).build(); ASSERT_EQ(b.get_int8(1), -12); }
|
||||
{ auto b = ba.set_int16(1, 5000).build(); ASSERT_EQ(b.get_int16(1), 5000); }
|
||||
{ auto b = ba.set_int32(1, 5000300).build(); ASSERT_EQ(b.get_int32(1), 5000300); }
|
||||
{ auto b = ba.set_int32(1, -5000300).build(); ASSERT_EQ(b.get_int32(1), -5000300); }
|
||||
|
||||
{ auto b = ba.set_float(1, 3.14).build(); ASSERT_FLOAT_EQ(b.get_float(1), 3.14); }
|
||||
{ auto b = ba.set_float(1, -3.14).build(); ASSERT_FLOAT_EQ(b.get_float(1), -3.14); }
|
||||
{ auto b = ba.set_float(1, -5000300.1324).build(); ASSERT_FLOAT_EQ(b.get_float(1), -5000300.1324); }
|
||||
|
||||
{
|
||||
auto b = ba.set_string(1, "Hello world!").build();
|
||||
auto str = b.get_string_ptr(1);
|
||||
EXPECT_STREQ(str.first, "Hello world!");
|
||||
ASSERT_EQ(str.second, 13);
|
||||
}
|
||||
|
||||
#undef TESTX
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -5,29 +5,29 @@
|
||||
|
||||
namespace tyche::bc {
|
||||
|
||||
Bytecode::Bytecode(ByteArray ba)
|
||||
: byte_array_(std::move(ba))
|
||||
Bytecode::Bytecode(StaticByteArray const* ba)
|
||||
: byte_array_(ba)
|
||||
{
|
||||
// check file size
|
||||
if (byte_array_.size() < (TOC_START + TOC_SZ))
|
||||
if (byte_array_->size() < (TOC_START + TOC_SZ))
|
||||
throw BytecodeParsingError("Invalid bytecode format (file too short)");
|
||||
|
||||
// check magic number and version
|
||||
if (byte_array_.get_uint32(0) != MAGIC_NUMBER)
|
||||
if (byte_array_->get_uint32(0) != MAGIC_NUMBER)
|
||||
throw BytecodeParsingError("Invalid bytecode format (magic number not matching)");
|
||||
if (byte_array_.get_uint32(4) != BYTECODE_VERSION)
|
||||
if (byte_array_->get_uint32(4) != BYTECODE_VERSION)
|
||||
throw BytecodeParsingError("Unexpected bytecode format version");
|
||||
|
||||
// load cache
|
||||
cache_.constants_idx_addr = byte_array_.get_uint32(TOC_START);
|
||||
cache_.n_constants = byte_array_.get_uint16(TOC_START + 4);
|
||||
cache_.functions_idx_addr = byte_array_.get_uint32(TOC_START + (1 * TOC_RECORD_SZ));
|
||||
cache_.n_functions = byte_array_.get_uint16(TOC_START + (1 * TOC_RECORD_SZ) + 4);
|
||||
cache_.constants_start_addr = byte_array_.get_uint32(TOC_START + (2 * TOC_RECORD_SZ));
|
||||
uint32_t code_start = byte_array_.get_uint32(TOC_START + (3 * TOC_RECORD_SZ));
|
||||
cache_.constants_idx_addr = byte_array_->get_uint32(TOC_START);
|
||||
cache_.n_constants = byte_array_->get_uint16(TOC_START + 4);
|
||||
cache_.functions_idx_addr = byte_array_->get_uint32(TOC_START + (1 * TOC_RECORD_SZ));
|
||||
cache_.n_functions = byte_array_->get_uint16(TOC_START + (1 * TOC_RECORD_SZ) + 4);
|
||||
cache_.constants_start_addr = byte_array_->get_uint32(TOC_START + (2 * TOC_RECORD_SZ));
|
||||
uint32_t code_start = byte_array_->get_uint32(TOC_START + (3 * TOC_RECORD_SZ));
|
||||
for (uint32_t i = 0; i < cache_.n_functions; ++i) {
|
||||
cache_.function_addr.emplace_back(code_start + byte_array_.get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ)));
|
||||
cache_.function_sz.emplace_back(byte_array_.get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ) + 8));
|
||||
cache_.function_addr.emplace_back(code_start + byte_array_->get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ)));
|
||||
cache_.function_sz.emplace_back(byte_array_->get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ) + 8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@ uint32_t Bytecode::n_functions() const
|
||||
|
||||
ConstantValue Bytecode::get_constant(uint32_t idx) const
|
||||
{
|
||||
uint32_t constant_idx = byte_array_.get_uint32(cache_.constants_idx_addr + (idx * CONST_RECORD_SZ));
|
||||
switch ((ConstantType) byte_array_.get_byte(cache_.constants_start_addr + constant_idx)) {
|
||||
uint32_t constant_idx = byte_array_->get_uint32(cache_.constants_idx_addr + (idx * CONST_RECORD_SZ));
|
||||
switch ((ConstantType) byte_array_->get_byte(cache_.constants_start_addr + constant_idx)) {
|
||||
case CONST_TYPE_FLOAT:
|
||||
return byte_array_.get_float(cache_.constants_start_addr + constant_idx + 1);
|
||||
return byte_array_->get_float(cache_.constants_start_addr + constant_idx + 1);
|
||||
case CONST_TYPE_STRING:
|
||||
return byte_array_.get_string(cache_.constants_start_addr + constant_idx + 1).first;
|
||||
return byte_array_->get_string_ptr(cache_.constants_start_addr + constant_idx + 1).first;
|
||||
default:
|
||||
throw BytecodeParsingError("Invalid bytecode format (invalid constant type)");
|
||||
}
|
||||
@@ -58,8 +58,8 @@ Bytecode::FunctionDef Bytecode::get_function_def(uint32_t function_id) const
|
||||
{
|
||||
uint32_t idx = cache_.functions_idx_addr + (function_id * FUNCTION_RECORD_SZ);
|
||||
return {
|
||||
.n_params = byte_array_.get_uint16(idx + 4),
|
||||
.locals = byte_array_.get_uint16(idx + 6),
|
||||
.n_params = byte_array_->get_uint16(idx + 4),
|
||||
.locals = byte_array_->get_uint16(idx + 6),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,34 +70,34 @@ uint32_t Bytecode::get_function_sz(uint32_t function_id) const
|
||||
|
||||
uint8_t Bytecode::get_code_byte(uint32_t function_id, uint32_t idx) const
|
||||
{
|
||||
return byte_array_.get_byte(cache_.function_addr.at(function_id) + idx);
|
||||
return byte_array_->get_byte(cache_.function_addr.at(function_id) + idx);
|
||||
}
|
||||
|
||||
int8_t Bytecode::get_code_int8(uint32_t function_id, uint32_t idx) const
|
||||
{
|
||||
return byte_array_.get_int8(cache_.function_addr.at(function_id) + idx);
|
||||
return byte_array_->get_int8(cache_.function_addr.at(function_id) + idx);
|
||||
}
|
||||
|
||||
int16_t Bytecode::get_code_int16(uint32_t function_id, uint32_t idx) const
|
||||
{
|
||||
return byte_array_.get_int16(cache_.function_addr.at(function_id) + idx);
|
||||
return byte_array_->get_int16(cache_.function_addr.at(function_id) + idx);
|
||||
}
|
||||
|
||||
int32_t Bytecode::get_code_int32(uint32_t function_id, uint32_t idx) const
|
||||
{
|
||||
return byte_array_.get_int32(cache_.function_addr.at(function_id) + idx);
|
||||
return byte_array_->get_int32(cache_.function_addr.at(function_id) + idx);
|
||||
}
|
||||
|
||||
ByteArray Bytecode::generate(BytecodePrototype const& bp)
|
||||
StaticByteArray Bytecode::generate(BytecodePrototype const& bp)
|
||||
{
|
||||
// header section
|
||||
ByteArray header;
|
||||
ByteArrayBuilder header;
|
||||
header.set_uint32(0, MAGIC_NUMBER);
|
||||
header.set_byte(4, BYTECODE_VERSION);
|
||||
|
||||
// constants
|
||||
ByteArray constant_indexes;
|
||||
ByteArray raw_constants;
|
||||
ByteArrayBuilder constant_indexes;
|
||||
ByteArrayBuilder raw_constants;
|
||||
|
||||
uint32_t idx = 0;
|
||||
for (auto const& constant: bp.constants) {
|
||||
@@ -116,8 +116,8 @@ ByteArray Bytecode::generate(BytecodePrototype const& bp)
|
||||
}
|
||||
|
||||
// functions
|
||||
ByteArray functions_indexes;
|
||||
ByteArray raw_code;
|
||||
ByteArrayBuilder functions_indexes;
|
||||
ByteArrayBuilder raw_code;
|
||||
|
||||
uint32_t idx_idx = 0, code_idx = 0;
|
||||
for (auto const& f: bp.functions) {
|
||||
@@ -135,7 +135,7 @@ ByteArray Bytecode::generate(BytecodePrototype const& bp)
|
||||
uint32_t raw_constant_start = function_idx_start + functions_indexes.size();
|
||||
uint32_t raw_code_start = raw_constant_start + raw_constants.size();
|
||||
|
||||
ByteArray toc;
|
||||
ByteArrayBuilder toc;
|
||||
if (!bp.constants.empty()) {
|
||||
toc.set_uint32(SEC_CONST_IDX * TOC_RECORD_SZ, CONST_IDX_START);
|
||||
toc.set_uint32(SEC_CONST_IDX * TOC_RECORD_SZ + 4, constant_indexes.size() / CONST_RECORD_SZ);
|
||||
@@ -153,14 +153,14 @@ ByteArray Bytecode::generate(BytecodePrototype const& bp)
|
||||
// assemble bytecode
|
||||
//
|
||||
|
||||
ByteArray ba;
|
||||
ByteArrayBuilder ba;
|
||||
ba.set_bytearray(0, header);
|
||||
ba.set_bytearray(TOC_START, toc);
|
||||
ba.set_bytearray(CONST_IDX_START, constant_indexes);
|
||||
ba.set_bytearray(function_idx_start, functions_indexes);
|
||||
ba.set_bytearray(raw_constant_start, raw_constants);
|
||||
ba.set_bytearray(raw_code_start, raw_code);
|
||||
return ba;
|
||||
return ba.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
#ifndef TYCHE_BYTECODE_HH
|
||||
#define TYCHE_BYTECODE_HH
|
||||
|
||||
#include "../common/bytearray.hh"
|
||||
#include "../bytearray/bytearray.hh"
|
||||
#include "bytecodeprototype.hh"
|
||||
#include "constant.hh"
|
||||
|
||||
namespace tyche::bc {
|
||||
|
||||
class Bytecode {
|
||||
public:
|
||||
Bytecode() = default;
|
||||
explicit Bytecode(ByteArray ba);
|
||||
explicit Bytecode(StaticByteArray const* ba);
|
||||
|
||||
[[nodiscard]] uint32_t n_constants() const;
|
||||
[[nodiscard]] uint32_t n_functions() const;
|
||||
@@ -27,10 +27,10 @@ public:
|
||||
|
||||
// TODO - debugging info
|
||||
|
||||
[[nodiscard]] static ByteArray generate(BytecodePrototype const& bp);
|
||||
[[nodiscard]] static StaticByteArray generate(BytecodePrototype const& bp);
|
||||
|
||||
private:
|
||||
ByteArray byte_array_; // the actual data
|
||||
StaticByteArray const* byte_array_; // the actual data
|
||||
|
||||
static constexpr uint8_t BYTECODE_VERSION = 1;
|
||||
static constexpr uint32_t MAGIC_NUMBER = 0x74b3c138;
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include "constant.hh"
|
||||
#include "../common/bytearray.hh"
|
||||
#include "../bytearray/bytearraybuilder.hh"
|
||||
|
||||
namespace tyche::bc {
|
||||
|
||||
struct BytecodePrototype {
|
||||
struct Function {
|
||||
uint16_t n_pars;
|
||||
uint16_t n_locals;
|
||||
ByteArray code {};
|
||||
uint16_t n_pars;
|
||||
uint16_t n_locals;
|
||||
ByteArrayBuilder code {};
|
||||
|
||||
Function(uint16_t n_pars_, uint16_t n_locals_) : n_pars(n_pars_), n_locals(n_locals_), code(ByteArray {}) {}
|
||||
Function(uint16_t n_pars_, uint16_t n_locals_) : n_pars(n_pars_), n_locals(n_locals_), code(ByteArrayBuilder {}) {}
|
||||
};
|
||||
|
||||
using ConstantValue = std::variant<float, std::string>;
|
||||
std::vector<ConstantValue> constants {};
|
||||
std::vector<Function> functions {};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace tyche::bc {
|
||||
|
||||
using ConstantValue = std::variant<float, std::string>;
|
||||
using ConstantValue = std::variant<float, const char*>;
|
||||
|
||||
enum ConstantType : uint8_t { CONST_TYPE_FLOAT = 1, CONST_TYPE_STRING = 2 };
|
||||
|
||||
|
||||
@@ -3,40 +3,46 @@
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "../common/bytearray.hh"
|
||||
#include "../bytearray/bytearray.hh"
|
||||
#include "../bytearray/bytearraybuilder.hh"
|
||||
#include "bytecodeprototype.hh"
|
||||
#include "bytecode.hh"
|
||||
|
||||
using namespace tyche;
|
||||
using namespace tyche::bc;
|
||||
|
||||
TEST(ByteArray, ByteArray)
|
||||
TEST(StaticByteArray, StaticByteArray)
|
||||
{
|
||||
auto test = [](std::function<void(ByteArray&)> const& f, std::vector<uint8_t> const& expected) {
|
||||
ByteArray ba;
|
||||
auto test = [](std::function<void(ByteArrayBuilder&)> const& f, std::vector<uint8_t> const& expected) {
|
||||
ByteArrayBuilder ba;
|
||||
f(ba);
|
||||
ASSERT_EQ(ba.data().size(), expected.size());
|
||||
ASSERT_EQ(std::memcmp(ba.data().data(), expected.data(), ba.data().size()), 0);
|
||||
};
|
||||
|
||||
#define TESTX(a, ...) test([](ByteArray& ba) { a; }, std::vector<uint8_t>({ __VA_ARGS__ }));
|
||||
#define TESTX(a, ...) test([](ByteArrayBuilder& ba) { a; }, std::vector<uint8_t>({ __VA_ARGS__ }));
|
||||
|
||||
TESTX(ba.set_byte(1, 0xab), 0x00, 0xab)
|
||||
|
||||
ByteArray ba;
|
||||
ba.set_byte(1, 0xab); ASSERT_EQ(ba.get_byte(1), 0xab);
|
||||
ByteArrayBuilder ba;
|
||||
{ auto b = ba.set_byte(1, 0xab).build(); ASSERT_EQ(b.get_byte(1), 0xab); }
|
||||
|
||||
ba.set_int8(1, 12); ASSERT_EQ(ba.get_int8(1), 12);
|
||||
ba.set_int8(1, -12); ASSERT_EQ(ba.get_int8(1), -12);
|
||||
ba.set_int16(1, 5000); ASSERT_EQ(ba.get_int16(1), 5000);
|
||||
ba.set_int32(1, 5000300); ASSERT_EQ(ba.get_int32(1), 5000300);
|
||||
ba.set_int32(1, -5000300); ASSERT_EQ(ba.get_int32(1), -5000300);
|
||||
{ auto b = ba.set_int8(1, 12).build(); ASSERT_EQ(b.get_int8(1), 12); }
|
||||
{ auto b = ba.set_int8(1, -12).build(); ASSERT_EQ(b.get_int8(1), -12); }
|
||||
{ auto b = ba.set_int16(1, 5000).build(); ASSERT_EQ(b.get_int16(1), 5000); }
|
||||
{ auto b = ba.set_int32(1, 5000300).build(); ASSERT_EQ(b.get_int32(1), 5000300); }
|
||||
{ auto b = ba.set_int32(1, -5000300).build(); ASSERT_EQ(b.get_int32(1), -5000300); }
|
||||
|
||||
ba.set_float(1, 3.14); ASSERT_FLOAT_EQ(ba.get_float(1), 3.14);
|
||||
ba.set_float(1, -3.14); ASSERT_FLOAT_EQ(ba.get_float(1), -3.14);
|
||||
ba.set_float(1, -5000300.1324); ASSERT_FLOAT_EQ(ba.get_float(1), -5000300.1324);
|
||||
{ auto b = ba.set_float(1, 3.14).build(); ASSERT_FLOAT_EQ(b.get_float(1), 3.14); }
|
||||
{ auto b = ba.set_float(1, -3.14).build(); ASSERT_FLOAT_EQ(b.get_float(1), -3.14); }
|
||||
{ auto b = ba.set_float(1, -5000300.1324).build(); ASSERT_FLOAT_EQ(b.get_float(1), -5000300.1324); }
|
||||
|
||||
ba.set_string(1, "Hello world!"); ASSERT_EQ(ba.get_string(1), std::make_pair("Hello world!", 13));
|
||||
{
|
||||
auto b = ba.set_string(1, "Hello world!").build();
|
||||
auto str = b.get_string_ptr(1);
|
||||
EXPECT_STREQ(str.first, "Hello world!");
|
||||
ASSERT_EQ(str.second, 13);
|
||||
}
|
||||
|
||||
#undef TESTX
|
||||
}
|
||||
@@ -73,7 +79,7 @@ TEST(Bytecode, Constants)
|
||||
CONST_TYPE_STRING, 'H', 'E', 'L', 'L', 'O', 0x00
|
||||
};
|
||||
|
||||
ByteArray ba = Bytecode::generate(bp);
|
||||
StaticByteArray ba = Bytecode::generate(bp);
|
||||
ASSERT_EQ(ba.data(), expected);
|
||||
}
|
||||
|
||||
@@ -112,7 +118,7 @@ TEST(Bytecode, Code)
|
||||
0x68, 42, 0x42,
|
||||
};
|
||||
|
||||
ByteArray ba = Bytecode::generate(bp);
|
||||
StaticByteArray ba = Bytecode::generate(bp);
|
||||
ASSERT_EQ(ba.data(), expected);
|
||||
}
|
||||
|
||||
@@ -132,12 +138,12 @@ TEST(Bytecode, Parsing)
|
||||
auto& ff = bp.functions.emplace_back(2, 1);
|
||||
ff.code.append_byte(0x42);
|
||||
|
||||
ByteArray ba = Bytecode::generate(bp);
|
||||
StaticByteArray ba = Bytecode::generate(bp);
|
||||
// print(ba.data());
|
||||
|
||||
// read bytecode
|
||||
|
||||
Bytecode bc(std::move(ba));
|
||||
Bytecode bc(&ba);
|
||||
|
||||
ASSERT_EQ(bc.n_constants(), 2);
|
||||
ASSERT_EQ(bc.n_functions(), 2);
|
||||
@@ -145,7 +151,7 @@ TEST(Bytecode, Parsing)
|
||||
ASSERT_EQ(bc.get_function_sz(1), 1);
|
||||
|
||||
ASSERT_FLOAT_EQ(std::get<float>(bc.get_constant(0)), 3.14f);
|
||||
ASSERT_EQ(std::get<std::string>(bc.get_constant(1)), "HELLO");
|
||||
EXPECT_STREQ(std::get<const char*>(bc.get_constant(1)), "HELLO");
|
||||
|
||||
Bytecode::FunctionDef f1 = bc.get_function_def(0);
|
||||
ASSERT_EQ(f1.n_params, 0);
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
#include "bytearray.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace tyche {
|
||||
|
||||
void ByteArray::set_byte(uint32_t addr, uint8_t byte)
|
||||
{
|
||||
if (data_.size() < (addr + 1))
|
||||
data_.resize(addr + 1, 0);
|
||||
data_.at(addr) = byte;
|
||||
}
|
||||
|
||||
void ByteArray::set_int8(uint32_t addr, int8_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) value);
|
||||
}
|
||||
|
||||
void ByteArray::set_int16(uint32_t addr, int16_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
}
|
||||
|
||||
void ByteArray::set_int32(uint32_t addr, int32_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
set_byte(addr+2, (uint8_t) (value >> 16));
|
||||
set_byte(addr+3, (uint8_t) (value >> 24));
|
||||
}
|
||||
|
||||
void ByteArray::set_uint16(uint32_t addr, uint16_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
}
|
||||
|
||||
void ByteArray::set_uint32(uint32_t addr, uint32_t value)
|
||||
{
|
||||
set_byte(addr, (uint8_t) (value));
|
||||
set_byte(addr+1, (uint8_t) (value >> 8));
|
||||
set_byte(addr+2, (uint8_t) (value >> 16));
|
||||
set_byte(addr+3, (uint8_t) (value >> 24));
|
||||
}
|
||||
|
||||
void ByteArray::set_float(uint32_t addr, float value)
|
||||
{
|
||||
uint32_t bits;
|
||||
std::memcpy(&bits, &value, 4);
|
||||
set_byte(addr, (uint8_t) (bits));
|
||||
set_byte(addr+1, (uint8_t) (bits >> 8));
|
||||
set_byte(addr+2, (uint8_t) (bits >> 16));
|
||||
set_byte(addr+3, (uint8_t) (bits >> 24));
|
||||
}
|
||||
|
||||
void ByteArray::set_string(uint32_t addr, std::string const& str)
|
||||
{
|
||||
for (uint8_t c: str)
|
||||
set_byte(addr++, c);
|
||||
set_byte(addr, 0);
|
||||
}
|
||||
|
||||
void ByteArray::set_bytearray(uint32_t addr, ByteArray const& bytearray)
|
||||
{
|
||||
for (uint8_t byte: bytearray.data())
|
||||
set_byte(addr++, byte);
|
||||
}
|
||||
|
||||
uint8_t ByteArray::get_byte(uint32_t addr) const
|
||||
{
|
||||
return data_.at(addr);
|
||||
}
|
||||
|
||||
uint16_t ByteArray::get_uint16(uint32_t addr) const
|
||||
{
|
||||
return (uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8;
|
||||
}
|
||||
|
||||
uint32_t ByteArray::get_uint32(uint32_t addr) const
|
||||
{
|
||||
return (uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8
|
||||
| (uint32_t) get_byte(addr+2) << 16
|
||||
| (uint32_t) get_byte(addr+3) << 24;
|
||||
}
|
||||
|
||||
int8_t ByteArray::get_int8(uint32_t addr) const
|
||||
{
|
||||
return std::bit_cast<int8_t>(get_byte(addr));
|
||||
}
|
||||
|
||||
int16_t ByteArray::get_int16(uint32_t addr) const
|
||||
{
|
||||
return (uint16_t) get_byte(addr)
|
||||
| (uint16_t) get_byte(addr+1) << 8;
|
||||
}
|
||||
|
||||
int32_t ByteArray::get_int32(uint32_t addr) const
|
||||
{
|
||||
return std::bit_cast<int32_t>((uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8
|
||||
| (uint32_t) get_byte(addr+2) << 16
|
||||
| (uint32_t) get_byte(addr+3) << 24);
|
||||
}
|
||||
|
||||
float ByteArray::get_float(uint32_t addr) const
|
||||
{
|
||||
uint32_t bits = (uint32_t) get_byte(addr)
|
||||
| (uint32_t) get_byte(addr+1) << 8
|
||||
| (uint32_t) get_byte(addr+2) << 16
|
||||
| (uint32_t) get_byte(addr+3) << 24;
|
||||
float value;
|
||||
std::memcpy(&value, &bits, 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> ByteArray::get_string(uint32_t addr) const
|
||||
{
|
||||
std::string data;
|
||||
while (char c = (char) get_byte(addr++))
|
||||
data += c;
|
||||
return { data, data.size() + 1 };
|
||||
}
|
||||
|
||||
void ByteArray::append_bytearray(ByteArray const& bytearray)
|
||||
{
|
||||
data_.insert(data_.end(), bytearray.data().begin(), bytearray.data().end());
|
||||
}
|
||||
|
||||
std::string ByteArray::hexdump() const
|
||||
{
|
||||
auto to_hex = [](uint32_t value, size_t n_chars) -> std::string {
|
||||
char buf[15];
|
||||
snprintf(buf, sizeof buf, "%0*X", (int) n_chars, value);
|
||||
return { buf };
|
||||
};
|
||||
|
||||
std::string out;
|
||||
for (size_t i = 0; i < data_.size(); ++i) {
|
||||
if (i % 16 == 0)
|
||||
out += to_hex(i, 4) + " | ";
|
||||
out += to_hex(data_.at(i), 2) + " ";
|
||||
if (i % 16 == 15)
|
||||
out += "\n";
|
||||
}
|
||||
return out + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#ifndef TYCHE_BYTEARRAY_HH
|
||||
#define TYCHE_BYTEARRAY_HH
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tyche {
|
||||
|
||||
class ByteArray {
|
||||
public:
|
||||
ByteArray() = default;
|
||||
explicit ByteArray(std::vector<uint8_t> data) : data_(std::move(data)) {}
|
||||
|
||||
void set_byte(uint32_t addr, uint8_t byte);
|
||||
void set_uint16(uint32_t addr, uint16_t value);
|
||||
void set_uint32(uint32_t addr, uint32_t value);
|
||||
void set_int8(uint32_t addr, int8_t value);
|
||||
void set_int16(uint32_t addr, int16_t value);
|
||||
void set_int32(uint32_t addr, int32_t value);
|
||||
void set_float(uint32_t addr, float value);
|
||||
void set_string(uint32_t addr, std::string const& str);
|
||||
void set_bytearray(uint32_t addr, ByteArray const& bytearray);
|
||||
|
||||
void append_byte(uint8_t byte) { set_byte(data_.size(), byte); }
|
||||
void append_uint16(uint16_t value) { set_uint16(data_.size(), value); }
|
||||
void append_uint32(uint32_t value) { set_uint32(data_.size(), value); }
|
||||
void append_int8(int8_t value) { set_int8(data_.size(), value); }
|
||||
void append_int16(int16_t value) { set_int16(data_.size(), value); }
|
||||
void append_int32(int32_t value) { set_int32(data_.size(), value); }
|
||||
void append_float(float value) { set_float(data_.size(), value); }
|
||||
void append_string(std::string const& str) { set_string(data_.size(), str); }
|
||||
void append_bytearray(ByteArray const& bytearray);
|
||||
|
||||
[[nodiscard]] uint8_t get_byte(uint32_t addr) const;
|
||||
[[nodiscard]] uint16_t get_uint16(uint32_t addr) const;
|
||||
[[nodiscard]] uint32_t get_uint32(uint32_t addr) const;
|
||||
[[nodiscard]] int8_t get_int8(uint32_t addr) const;
|
||||
[[nodiscard]] int16_t get_int16(uint32_t addr) const;
|
||||
[[nodiscard]] int32_t get_int32(uint32_t addr) const;
|
||||
[[nodiscard]] float get_float(uint32_t addr) const;
|
||||
[[nodiscard]] std::pair<std::string, size_t> get_string(uint32_t addr) const;
|
||||
|
||||
[[nodiscard]] std::vector<uint8_t> const& data() const { return data_; }
|
||||
[[nodiscard]] size_t size() const { return data_.size(); }
|
||||
|
||||
[[nodiscard]] std::string hexdump() const;
|
||||
|
||||
friend bool operator==(ByteArray const& lhs, ByteArray const& rhs) { return lhs.data_ == rhs.data_; }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data_ {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TYCHE_BYTEARRAY_HH
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace tyche::vm {
|
||||
namespace tyche {
|
||||
|
||||
const std::unordered_map<std::string, Instruction> instruction_names = {
|
||||
{ "pushi", Instruction::PushInt8 },
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "../bytecode/bytecode.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
namespace tyche {
|
||||
|
||||
constexpr uint8_t OPCODE_NEXT_SIZE = 0x20;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "code.hh"
|
||||
#include "../common/overloaded.hh"
|
||||
#include "instruction.hh"
|
||||
#include "../instructions/instruction.hh"
|
||||
|
||||
namespace tyche::vm {
|
||||
|
||||
FunctionId Code::import_bytecode(ByteArray incoming)
|
||||
FunctionId Code::import_bytecode(StaticByteArray const* incoming)
|
||||
{
|
||||
bc::Bytecode bc(std::move(incoming));
|
||||
bc::Bytecode bc(incoming);
|
||||
// TODO - adjust function calls, constants
|
||||
|
||||
bytecode_ = std::move(bc);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef TYCHE_CODE_HH
|
||||
#define TYCHE_CODE_HH
|
||||
|
||||
#include "instruction.hh"
|
||||
#include "../instructions/instruction.hh"
|
||||
#include "location.hh"
|
||||
#include "value.hh"
|
||||
#include "../bytecode/bytecode.hh"
|
||||
@@ -17,7 +17,7 @@ struct Operation
|
||||
|
||||
class Code {
|
||||
public:
|
||||
FunctionId import_bytecode(ByteArray incoming);
|
||||
FunctionId import_bytecode(StaticByteArray const* incoming);
|
||||
|
||||
[[nodiscard]] std::string disassemble() const;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ static int init_ = []() {
|
||||
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(Sum, String, String) { return Value::createString(std::string(a.as_string_ptr()) + std::string(b.as_string_ptr())); };
|
||||
|
||||
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()); };
|
||||
@@ -61,13 +61,13 @@ static int init_ = []() {
|
||||
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(Equality, String, String) { return Value::createIntegerFromBool(strcmp(a.as_string_ptr(), b.as_string_ptr()) == 0); };
|
||||
|
||||
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(Inequality, String, String) { return Value::createIntegerFromBool(strcmp(a.as_string_ptr(), b.as_string_ptr()) != 0); };
|
||||
|
||||
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()); };
|
||||
|
||||
@@ -110,7 +110,7 @@ TEST(VM, StackOperations)
|
||||
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");
|
||||
EXPECT_STREQ(run("pushc 1").to_string_ptr(-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);
|
||||
}
|
||||
@@ -224,7 +224,7 @@ TEST(VM, FloatFloatOperations)
|
||||
|
||||
TEST(VM, StringString)
|
||||
{
|
||||
ASSERT_EQ(run(R"(
|
||||
EXPECT_STREQ(run(R"(
|
||||
.const
|
||||
0: "Hello"
|
||||
1: "World"
|
||||
@@ -233,7 +233,7 @@ TEST(VM, StringString)
|
||||
pushc 1
|
||||
sum
|
||||
ret
|
||||
)").to_string(-1), "HelloWorld");
|
||||
)").to_string_ptr(-1), "HelloWorld");
|
||||
|
||||
ASSERT_EQ(run(R"(
|
||||
.const
|
||||
|
||||
@@ -41,4 +41,13 @@ std::string Value::to_string() const
|
||||
}, value_);
|
||||
}
|
||||
|
||||
const char* Value::as_string_ptr() const
|
||||
{
|
||||
if (auto s = std::get_if<std::string>(&value_))
|
||||
return s->c_str();
|
||||
if (auto s = std::get_if<const char*>(&value_))
|
||||
return *s;
|
||||
throw std::logic_error("Shouldn't get here");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ 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 createStringFromConstant(const char* str) { return Value(str); }
|
||||
static Value createFunctionId(FunctionId f_id) { return Value(Function { f_id }); }
|
||||
|
||||
static Value createFalse() { return createInteger(0); }
|
||||
@@ -36,13 +37,13 @@ public:
|
||||
|
||||
[[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]] const char* as_string_ptr() const;
|
||||
[[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, Function>;
|
||||
using Internal = std::variant<std::monostate, int32_t, float, std::string, const char*, Function>;
|
||||
Internal value_;
|
||||
|
||||
explicit Value(Internal const& internal) : value_(internal) {}
|
||||
|
||||
12
src/vm/vm.cc
12
src/vm/vm.cc
@@ -49,11 +49,11 @@ float VM::to_float(int index) const
|
||||
throw VMTypeError(Type::Float, f.type());
|
||||
}
|
||||
|
||||
std::string VM::to_string(int index) const
|
||||
const char* VM::to_string_ptr(int index) const
|
||||
{
|
||||
Value i = stack_.at(index);
|
||||
assert_type(i, Type::String);
|
||||
return i.as_string();
|
||||
Value s = stack_.at(index);
|
||||
assert_type(s, Type::String);
|
||||
return s.as_string_ptr();
|
||||
}
|
||||
|
||||
VM& VM::push_nil()
|
||||
@@ -110,8 +110,8 @@ void VM::step()
|
||||
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 if (auto s = std::get_if<const char*>(&cnst))
|
||||
stack_.push(Value::createStringFromConstant(*s));
|
||||
else
|
||||
throw std::logic_error("Shouldn't get here");
|
||||
break;
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
|
||||
[[nodiscard]] int32_t to_integer(int index) const;
|
||||
[[nodiscard]] float to_float(int index) const;
|
||||
[[nodiscard]] std::string to_string(int index) const;
|
||||
[[nodiscard]] const char* to_string_ptr(int index) const;
|
||||
|
||||
[[nodiscard]] std::string debug_stack() const { return stack_.debug(); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user