4 Commits

Author SHA1 Message Date
Andre Wagner
62d36f68b8 . 2026-05-03 09:53:23 -05:00
Andre Wagner
eee26cc767 . 2026-05-02 21:13:31 -05:00
Andre Wagner
223570478e . 2026-05-02 21:12:12 -05:00
Andre Wagner
ff23e14122 . 2026-05-02 20:54:26 -05:00
29 changed files with 493 additions and 338 deletions

11
.idea/ctestState.xml generated Normal file
View 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>

View File

@@ -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)

View File

@@ -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)

View File

@@ -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_;

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

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

View File

@@ -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;

View File

@@ -5,8 +5,7 @@
#include <string>
#include <variant>
#include <vector>
#include "constant.hh"
#include "../common/bytearray.hh"
#include "../bytearray/bytearraybuilder.hh"
namespace tyche::bc {
@@ -14,11 +13,12 @@ struct BytecodePrototype {
struct Function {
uint16_t n_pars;
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>;
std::vector<ConstantValue> constants {};
std::vector<Function> functions {};

View File

@@ -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 };

View File

@@ -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);

View File

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

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<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

View File

@@ -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 },

View File

@@ -8,7 +8,7 @@
#include "../bytecode/bytecode.hh"
namespace tyche::vm {
namespace tyche {
constexpr uint8_t OPCODE_NEXT_SIZE = 0x20;

View File

@@ -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);

View File

@@ -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;

View File

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

View File

@@ -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

View File

@@ -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");
}
}

View File

@@ -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) {}

View File

@@ -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;

View File

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