This commit is contained in:
Andre Wagner
2026-05-03 09:53:23 -05:00
parent eee26cc767
commit 62d36f68b8
18 changed files with 423 additions and 310 deletions

7
.idea/ctestState.xml generated
View File

@@ -2,9 +2,10 @@
<project version="4"> <project version="4">
<component name="CidrCTestProjectState"> <component name="CidrCTestProjectState">
<ctestState> <ctestState>
<lineNumber testId="path:CMakeLists.txt test:tyche_as_test" value="108" /> <lineNumber testId="path:CMakeLists.txt test:tyche_as_test" value="116" />
<lineNumber testId="path:CMakeLists.txt test:tyche_bytecode_test" value="100" /> <lineNumber testId="path:CMakeLists.txt test:tyche_bytearray_test" value="102" />
<lineNumber testId="path:CMakeLists.txt test:tyche_vm_test" value="104" /> <lineNumber testId="path:CMakeLists.txt test:tyche_bytecode_test" value="106" />
<lineNumber testId="path:CMakeLists.txt test:tyche_vm_test" value="110" />
</ctestState> </ctestState>
</component> </component>
</project> </project>

View File

@@ -60,32 +60,34 @@ FetchContent_MakeAvailable(googletest)
add_library(lib${PROJECT_NAME} SHARED add_library(lib${PROJECT_NAME} SHARED
src/common/overloaded.hh src/common/overloaded.hh
src/common/bytearray.hh src/bytearray/bytearray.hh
src/common/bytearray.cc src/bytearray/bytearray.cc
src/bytecode/bytecode.cc src/bytearray/bytearraybuilder.hh
src/bytecode/bytecode.hh src/bytearray/bytearraybuilder.cc
src/bytecode/bytecodeprototype.hh #src/bytecode/bytecode.cc
src/bytecode/constant.hh #src/bytecode/bytecode.hh
src/bytecode/bc_exceptions.hh #src/bytecode/bytecodeprototype.hh
src/assembler/lexer.cc #src/bytecode/constant.hh
src/assembler/lexer.hh #src/bytecode/bc_exceptions.hh
src/assembler/assembler.cc #src/assembler/lexer.cc
src/assembler/assembler.hh #src/assembler/lexer.hh
src/assembler/as_exceptions.hh #src/assembler/assembler.cc
src/instructions/instruction.hh #src/assembler/assembler.hh
src/instructions/instruction.cc #src/assembler/as_exceptions.hh
src/vm/code.cc #src/instructions/instruction.hh
src/vm/code.hh #src/instructions/instruction.cc
src/vm/value.cc #src/vm/code.cc
src/vm/value.hh #src/vm/code.hh
src/vm/stack.cc #src/vm/value.cc
src/vm/stack.hh #src/vm/value.hh
src/vm/vm_exceptions.hh #src/vm/stack.cc
src/vm/vm.cc #src/vm/stack.hh
src/vm/vm.hh #src/vm/vm_exceptions.hh
src/vm/expr.cc #src/vm/vm.cc
src/vm/expr.hh #src/vm/vm.hh
src/vm/location.hh #src/vm/expr.cc
#src/vm/expr.hh
#src/vm/location.hh
) )
target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings}) target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
@@ -96,6 +98,10 @@ target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
enable_testing() 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) add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc)
target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main) target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main)
add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test) 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) target_link_libraries(${PROJECT_NAME}-vm-test lib${PROJECT_NAME} gtest_main)
add_test(NAME tyche_vm_test COMMAND ${PROJECT_NAME}-vm-test) 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) target_link_libraries(${PROJECT_NAME}-as-test lib${PROJECT_NAME} gtest_main)
add_test(NAME tyche_as_test COMMAND ${PROJECT_NAME}-as-test) add_test(NAME tyche_as_test COMMAND ${PROJECT_NAME}-as-test)

View File

@@ -10,7 +10,7 @@ using namespace std::string_literals;
namespace tyche::as { namespace tyche::as {
ByteArray Assembler::assemble() StaticByteArray Assembler::assemble()
{ {
bc::BytecodePrototype bp; bc::BytecodePrototype bp;

View File

@@ -5,7 +5,7 @@
#include <string> #include <string>
#include "lexer.hh" #include "lexer.hh"
#include "../common/bytearray.hh" #include "../bytearray/bytearray.hh"
#include "../bytecode/bytecodeprototype.hh" #include "../bytecode/bytecodeprototype.hh"
namespace tyche::as { namespace tyche::as {
@@ -14,7 +14,7 @@ class Assembler {
public: public:
explicit Assembler(std::string source) : lexer_(std::move(source) + "\n") {} explicit Assembler(std::string source) : lexer_(std::move(source) + "\n") {}
[[nodiscard]] ByteArray assemble(); [[nodiscard]] StaticByteArray assemble();
private: private:
Lexer lexer_; Lexer lexer_;

View File

@@ -47,7 +47,7 @@ TEST(Assember, Assembler)
bp.functions.at(1).code.append_byte((uint8_t) Instruction::PushInt16); 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_int16(5000);
bp.functions.at(1).code.append_byte((uint8_t) Instruction::Return); 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"( std::string src = R"(
.const .const
@@ -64,7 +64,7 @@ TEST(Assember, Assembler)
ret ret
)"; )";
ByteArray actual = Assembler(src).assemble(); StaticByteArray actual = Assembler(src).assemble();
ASSERT_EQ(expected, actual); ASSERT_EQ(expected, actual);
} }

View 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";
}
}

View 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

View 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_);
}
}

View 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
View 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();
}

View File

@@ -5,29 +5,29 @@
namespace tyche::bc { namespace tyche::bc {
Bytecode::Bytecode(ByteArray ba) Bytecode::Bytecode(StaticByteArray const* ba)
: byte_array_(std::move(ba)) : byte_array_(ba)
{ {
// check file size // 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)"); throw BytecodeParsingError("Invalid bytecode format (file too short)");
// check magic number and version // 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)"); 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"); throw BytecodeParsingError("Unexpected bytecode format version");
// load cache // load cache
cache_.constants_idx_addr = byte_array_.get_uint32(TOC_START); cache_.constants_idx_addr = byte_array_->get_uint32(TOC_START);
cache_.n_constants = byte_array_.get_uint16(TOC_START + 4); 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_.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_.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)); 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)); uint32_t code_start = byte_array_->get_uint32(TOC_START + (3 * TOC_RECORD_SZ));
for (uint32_t i = 0; i < cache_.n_functions; ++i) { 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_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_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 ConstantValue Bytecode::get_constant(uint32_t idx) const
{ {
uint32_t constant_idx = byte_array_.get_uint32(cache_.constants_idx_addr + (idx * CONST_RECORD_SZ)); 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)) { switch ((ConstantType) byte_array_->get_byte(cache_.constants_start_addr + constant_idx)) {
case CONST_TYPE_FLOAT: 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: case CONST_TYPE_STRING:
return byte_array_.get_string_ptr(cache_.constants_start_addr + constant_idx + 1).first; return byte_array_->get_string_ptr(cache_.constants_start_addr + constant_idx + 1).first;
default: default:
throw BytecodeParsingError("Invalid bytecode format (invalid constant type)"); 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); uint32_t idx = cache_.functions_idx_addr + (function_id * FUNCTION_RECORD_SZ);
return { return {
.n_params = byte_array_.get_uint16(idx + 4), .n_params = byte_array_->get_uint16(idx + 4),
.locals = byte_array_.get_uint16(idx + 6), .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 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 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 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 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 // header section
ByteArray header; ByteArrayBuilder header;
header.set_uint32(0, MAGIC_NUMBER); header.set_uint32(0, MAGIC_NUMBER);
header.set_byte(4, BYTECODE_VERSION); header.set_byte(4, BYTECODE_VERSION);
// constants // constants
ByteArray constant_indexes; ByteArrayBuilder constant_indexes;
ByteArray raw_constants; ByteArrayBuilder raw_constants;
uint32_t idx = 0; uint32_t idx = 0;
for (auto const& constant: bp.constants) { for (auto const& constant: bp.constants) {
@@ -116,8 +116,8 @@ ByteArray Bytecode::generate(BytecodePrototype const& bp)
} }
// functions // functions
ByteArray functions_indexes; ByteArrayBuilder functions_indexes;
ByteArray raw_code; ByteArrayBuilder raw_code;
uint32_t idx_idx = 0, code_idx = 0; uint32_t idx_idx = 0, code_idx = 0;
for (auto const& f: bp.functions) { 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_constant_start = function_idx_start + functions_indexes.size();
uint32_t raw_code_start = raw_constant_start + raw_constants.size(); uint32_t raw_code_start = raw_constant_start + raw_constants.size();
ByteArray toc; ByteArrayBuilder toc;
if (!bp.constants.empty()) { 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, CONST_IDX_START);
toc.set_uint32(SEC_CONST_IDX * TOC_RECORD_SZ + 4, constant_indexes.size() / CONST_RECORD_SZ); 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 // assemble bytecode
// //
ByteArray ba; ByteArrayBuilder ba;
ba.set_bytearray(0, header); ba.set_bytearray(0, header);
ba.set_bytearray(TOC_START, toc); ba.set_bytearray(TOC_START, toc);
ba.set_bytearray(CONST_IDX_START, constant_indexes); ba.set_bytearray(CONST_IDX_START, constant_indexes);
ba.set_bytearray(function_idx_start, functions_indexes); ba.set_bytearray(function_idx_start, functions_indexes);
ba.set_bytearray(raw_constant_start, raw_constants); ba.set_bytearray(raw_constant_start, raw_constants);
ba.set_bytearray(raw_code_start, raw_code); ba.set_bytearray(raw_code_start, raw_code);
return ba; return ba.build();
} }
} }

View File

@@ -1,7 +1,7 @@
#ifndef TYCHE_BYTECODE_HH #ifndef TYCHE_BYTECODE_HH
#define TYCHE_BYTECODE_HH #define TYCHE_BYTECODE_HH
#include "../common/bytearray.hh" #include "../bytearray/bytearray.hh"
#include "bytecodeprototype.hh" #include "bytecodeprototype.hh"
#include "constant.hh" #include "constant.hh"
@@ -9,8 +9,7 @@ namespace tyche::bc {
class Bytecode { class Bytecode {
public: public:
Bytecode() = default; explicit Bytecode(StaticByteArray const* ba);
explicit Bytecode(ByteArray ba);
[[nodiscard]] uint32_t n_constants() const; [[nodiscard]] uint32_t n_constants() const;
[[nodiscard]] uint32_t n_functions() const; [[nodiscard]] uint32_t n_functions() const;
@@ -28,10 +27,10 @@ public:
// TODO - debugging info // TODO - debugging info
[[nodiscard]] static ByteArray generate(BytecodePrototype const& bp); [[nodiscard]] static StaticByteArray generate(BytecodePrototype const& bp);
private: private:
ByteArray byte_array_; // the actual data StaticByteArray const* byte_array_; // the actual data
static constexpr uint8_t BYTECODE_VERSION = 1; static constexpr uint8_t BYTECODE_VERSION = 1;
static constexpr uint32_t MAGIC_NUMBER = 0x74b3c138; static constexpr uint32_t MAGIC_NUMBER = 0x74b3c138;

View File

@@ -5,7 +5,7 @@
#include <string> #include <string>
#include <variant> #include <variant>
#include <vector> #include <vector>
#include "../common/bytearray.hh" #include "../bytearray/bytearraybuilder.hh"
namespace tyche::bc { namespace tyche::bc {
@@ -13,9 +13,9 @@ struct BytecodePrototype {
struct Function { struct Function {
uint16_t n_pars; uint16_t n_pars;
uint16_t n_locals; uint16_t n_locals;
ByteArray code {}; 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>; using ConstantValue = std::variant<float, std::string>;

View File

@@ -3,43 +3,46 @@
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include "../common/bytearray.hh" #include "../bytearray/bytearray.hh"
#include "../bytearray/bytearraybuilder.hh"
#include "bytecodeprototype.hh" #include "bytecodeprototype.hh"
#include "bytecode.hh" #include "bytecode.hh"
using namespace tyche; using namespace tyche;
using namespace tyche::bc; using namespace tyche::bc;
TEST(ByteArray, ByteArray) TEST(StaticByteArray, StaticByteArray)
{ {
auto test = [](std::function<void(ByteArray&)> const& f, std::vector<uint8_t> const& expected) { auto test = [](std::function<void(ByteArrayBuilder&)> const& f, std::vector<uint8_t> const& expected) {
ByteArray ba; ByteArrayBuilder ba;
f(ba); f(ba);
ASSERT_EQ(ba.data().size(), expected.size()); ASSERT_EQ(ba.data().size(), expected.size());
ASSERT_EQ(std::memcmp(ba.data().data(), expected.data(), ba.data().size()), 0); 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) TESTX(ba.set_byte(1, 0xab), 0x00, 0xab)
ByteArray ba; ByteArrayBuilder ba;
ba.set_byte(1, 0xab); ASSERT_EQ(ba.get_byte(1), 0xab); { 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); { auto b = ba.set_int8(1, 12).build(); ASSERT_EQ(b.get_int8(1), 12); }
ba.set_int8(1, -12); ASSERT_EQ(ba.get_int8(1), -12); { auto b = ba.set_int8(1, -12).build(); ASSERT_EQ(b.get_int8(1), -12); }
ba.set_int16(1, 5000); ASSERT_EQ(ba.get_int16(1), 5000); { auto b = ba.set_int16(1, 5000).build(); ASSERT_EQ(b.get_int16(1), 5000); }
ba.set_int32(1, 5000300); ASSERT_EQ(ba.get_int32(1), 5000300); { auto b = ba.set_int32(1, 5000300).build(); ASSERT_EQ(b.get_int32(1), 5000300); }
ba.set_int32(1, -5000300); ASSERT_EQ(ba.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); { auto b = ba.set_float(1, 3.14).build(); ASSERT_FLOAT_EQ(b.get_float(1), 3.14); }
ba.set_float(1, -3.14); ASSERT_FLOAT_EQ(ba.get_float(1), -3.14); { auto b = ba.set_float(1, -3.14).build(); ASSERT_FLOAT_EQ(b.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, -5000300.1324).build(); ASSERT_FLOAT_EQ(b.get_float(1), -5000300.1324); }
ba.set_string(1, "Hello world!"); {
auto str = ba.get_string_ptr(1); auto b = ba.set_string(1, "Hello world!").build();
auto str = b.get_string_ptr(1);
EXPECT_STREQ(str.first, "Hello world!"); EXPECT_STREQ(str.first, "Hello world!");
ASSERT_EQ(str.second, 13); ASSERT_EQ(str.second, 13);
}
#undef TESTX #undef TESTX
} }
@@ -76,7 +79,7 @@ TEST(Bytecode, Constants)
CONST_TYPE_STRING, 'H', 'E', 'L', 'L', 'O', 0x00 CONST_TYPE_STRING, 'H', 'E', 'L', 'L', 'O', 0x00
}; };
ByteArray ba = Bytecode::generate(bp); StaticByteArray ba = Bytecode::generate(bp);
ASSERT_EQ(ba.data(), expected); ASSERT_EQ(ba.data(), expected);
} }
@@ -115,7 +118,7 @@ TEST(Bytecode, Code)
0x68, 42, 0x42, 0x68, 42, 0x42,
}; };
ByteArray ba = Bytecode::generate(bp); StaticByteArray ba = Bytecode::generate(bp);
ASSERT_EQ(ba.data(), expected); ASSERT_EQ(ba.data(), expected);
} }
@@ -135,12 +138,12 @@ TEST(Bytecode, Parsing)
auto& ff = bp.functions.emplace_back(2, 1); auto& ff = bp.functions.emplace_back(2, 1);
ff.code.append_byte(0x42); ff.code.append_byte(0x42);
ByteArray ba = Bytecode::generate(bp); StaticByteArray ba = Bytecode::generate(bp);
// print(ba.data()); // print(ba.data());
// read bytecode // read bytecode
Bytecode bc(std::move(ba)); Bytecode bc(&ba);
ASSERT_EQ(bc.n_constants(), 2); ASSERT_EQ(bc.n_constants(), 2);
ASSERT_EQ(bc.n_functions(), 2); ASSERT_EQ(bc.n_functions(), 2);

View File

@@ -1,149 +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<const char*, size_t> ByteArray::get_string_ptr(uint32_t addr) const
{
return { (const char *) &data_.at(addr), strlen((const char *) &data_.at(addr)) + 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";
}
}

View File

@@ -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<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==(ByteArray const& lhs, ByteArray const& rhs) { return lhs.data_ == rhs.data_; }
private:
std::vector<uint8_t> data_ {};
};
}
#endif //TYCHE_BYTEARRAY_HH

View File

@@ -4,9 +4,9 @@
namespace tyche::vm { 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 // TODO - adjust function calls, constants
bytecode_ = std::move(bc); bytecode_ = std::move(bc);

View File

@@ -17,7 +17,7 @@ struct Operation
class Code { class Code {
public: public:
FunctionId import_bytecode(ByteArray incoming); FunctionId import_bytecode(StaticByteArray const* incoming);
[[nodiscard]] std::string disassemble() const; [[nodiscard]] std::string disassemble() const;