Compare commits
6 Commits
cfefca49ee
...
9bf4da4fd9
| Author | SHA1 | Date | |
|---|---|---|---|
|
9bf4da4fd9
|
|||
|
6c7d16631d
|
|||
|
24a9530abc
|
|||
|
0354ec1e79
|
|||
|
ddb60ca93a
|
|||
|
3aaed55485
|
+24
-14
@@ -107,6 +107,11 @@ static void print_type(const furvm::thing_type& type) {
|
||||
std::cout << type.value.array.size;
|
||||
std::cout << ")";
|
||||
break;
|
||||
case furvm::thing_type::Slice:
|
||||
std::cout << "slice(";
|
||||
print_type(*type.value.slice.type);
|
||||
std::cout << ")";
|
||||
break;
|
||||
case furvm::thing_type::Count: break;
|
||||
}
|
||||
}
|
||||
@@ -116,21 +121,26 @@ static void print_thing(const furvm::thing<>& thing) {
|
||||
print_type(thing.type());
|
||||
std::cout << ") ";
|
||||
switch (thing.type().type) {
|
||||
case furvm::thing_type::S8: std::cout << std::to_string(thing.get<furvm::thing_type::s8>()); break;
|
||||
case furvm::thing_type::S16: std::cout << thing.get<furvm::thing_type::s16>(); break;
|
||||
case furvm::thing_type::S32: std::cout << thing.get<furvm::thing_type::s32>(); break;
|
||||
case furvm::thing_type::S64: std::cout << thing.get<furvm::thing_type::s64>(); break;
|
||||
case furvm::thing_type::U8: std::cout << std::to_string(thing.get<furvm::thing_type::u8>()); break;
|
||||
case furvm::thing_type::U16: std::cout << thing.get<furvm::thing_type::u16>(); break;
|
||||
case furvm::thing_type::U32: std::cout << thing.get<furvm::thing_type::u32>(); break;
|
||||
case furvm::thing_type::U64: std::cout << thing.get<furvm::thing_type::u64>(); break;
|
||||
case furvm::thing_type::S8: std::cout << std::to_string(thing.get<furvm::s8>()); break;
|
||||
case furvm::thing_type::S16: std::cout << thing.get<furvm::s16>(); break;
|
||||
case furvm::thing_type::S32: std::cout << thing.get<furvm::s32>(); break;
|
||||
case furvm::thing_type::S64: std::cout << thing.get<furvm::s64>(); break;
|
||||
case furvm::thing_type::U8: std::cout << std::to_string(thing.get<furvm::u8>()); break;
|
||||
case furvm::thing_type::U16: std::cout << thing.get<furvm::u16>(); break;
|
||||
case furvm::thing_type::U32: std::cout << thing.get<furvm::u32>(); break;
|
||||
case furvm::thing_type::U64: std::cout << thing.get<furvm::u64>(); break;
|
||||
case furvm::thing_type::Ptr: std::cout << thing.get<const void*>(); break;
|
||||
case furvm::thing_type::Array: {
|
||||
if (thing.type().value.array.size == 0) std::cout << thing.length();
|
||||
std::cout << "{ ";
|
||||
for (std::size_t i = 0; i < thing.length(); ++i) {
|
||||
if (i > 0) std::cout << ", ";
|
||||
print_thing(thing.at(i));
|
||||
case furvm::thing_type::Array:
|
||||
case furvm::thing_type::Slice: {
|
||||
if (thing.length() == 0) {
|
||||
std::cout << "{}";
|
||||
break;
|
||||
}
|
||||
|
||||
std::cout << '(' << thing.length() << ") { ";
|
||||
for (const auto& el : thing) {
|
||||
print_thing(el);
|
||||
std::cout << ", ";
|
||||
}
|
||||
std::cout << " }";
|
||||
} break;
|
||||
|
||||
@@ -49,6 +49,8 @@ void context::print_instruction() const {
|
||||
"push",
|
||||
// Array
|
||||
"array",
|
||||
// Slice
|
||||
"slice",
|
||||
// Get
|
||||
"get",
|
||||
// Set
|
||||
|
||||
+9
-9
@@ -15,17 +15,17 @@ static void print_thing(const furvm::thing<>& thing) {
|
||||
using namespace furvm;
|
||||
|
||||
switch (thing.type().type) {
|
||||
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
|
||||
case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break;
|
||||
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;
|
||||
case thing_type::S64: std::cout << thing.get<thing_type::s64>(); break;
|
||||
case thing_type::U8: std::cout << thing.get<thing_type::u8>(); break;
|
||||
case thing_type::U16: std::cout << thing.get<thing_type::u16>(); break;
|
||||
case thing_type::U32: std::cout << thing.get<thing_type::u32>(); break;
|
||||
case thing_type::U64: std::cout << thing.get<thing_type::u64>(); break;
|
||||
case thing_type::S8: std::cout << thing.cast_to<s16>(); break;
|
||||
case thing_type::S16: std::cout << thing.get<s16>(); break;
|
||||
case thing_type::S32: std::cout << thing.get<s32>(); break;
|
||||
case thing_type::S64: std::cout << thing.get<s64>(); break;
|
||||
case thing_type::U8: std::cout << thing.get<u8>(); break;
|
||||
case thing_type::U16: std::cout << thing.get<u16>(); break;
|
||||
case thing_type::U32: std::cout << thing.get<u32>(); break;
|
||||
case thing_type::U64: std::cout << thing.get<u64>(); break;
|
||||
case thing_type::Array:
|
||||
std::cout << "{ ";
|
||||
for (thing_type::u64 i = 0; i < thing.length(); ++i) {
|
||||
for (u64 i = 0; i < thing.length(); ++i) {
|
||||
if (i > 0) std::cout << ", ";
|
||||
print_thing(thing.at(i));
|
||||
}
|
||||
|
||||
+306
-196
@@ -5,13 +5,14 @@
|
||||
#include "furlang/utility/hash.hpp"
|
||||
#include "furvm/exceptions.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/types.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
@@ -20,124 +21,6 @@
|
||||
|
||||
namespace furvm {
|
||||
|
||||
struct thing_type {
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
struct array_value {
|
||||
thing_type* type;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
struct slice_value {
|
||||
thing_type* type;
|
||||
};
|
||||
|
||||
enum type { // NOLINT
|
||||
S8 = 0,
|
||||
S16,
|
||||
S32,
|
||||
S64,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
Ptr,
|
||||
Ref,
|
||||
Array,
|
||||
Slice,
|
||||
|
||||
Count,
|
||||
} type = Count;
|
||||
union value {
|
||||
std::nullptr_t null = nullptr;
|
||||
thing_type* typeRef;
|
||||
array_value array;
|
||||
slice_value slice;
|
||||
|
||||
value() = default;
|
||||
|
||||
value(thing_type* type)
|
||||
: typeRef(type) {}
|
||||
|
||||
value(thing_type* type, std::size_t size)
|
||||
: array({}) {
|
||||
array.type = type;
|
||||
array.size = size;
|
||||
}
|
||||
} value;
|
||||
|
||||
static constexpr thing_type_id INVALID_ID = std::numeric_limits<thing_type_id>::max();
|
||||
|
||||
thing_type_id id = INVALID_ID;
|
||||
|
||||
bool operator==(const thing_type& other) const {
|
||||
if (type != other.type) return false;
|
||||
switch (type) {
|
||||
case S8:
|
||||
case S16:
|
||||
case S32:
|
||||
case S64:
|
||||
case U8:
|
||||
case U16:
|
||||
case U32:
|
||||
case U64: return true;
|
||||
case Ptr:
|
||||
case Ref: return *value.typeRef == *other.value.typeRef;
|
||||
case Array: return *value.array.type == *other.value.array.type && value.array.size == other.value.array.size;
|
||||
case Slice: return *value.slice.type == *other.value.slice.type;
|
||||
case Count: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const thing_type& other) const { return !this->operator==(other); }
|
||||
|
||||
static bool is_primitive(enum type type) {
|
||||
switch (type) {
|
||||
case S8:
|
||||
case S16:
|
||||
case S32:
|
||||
case S64:
|
||||
case U8:
|
||||
case U16:
|
||||
case U32:
|
||||
case U64: return true;
|
||||
case Ptr:
|
||||
case Ref:
|
||||
case Array:
|
||||
case Slice: return false;
|
||||
case Count: break;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
|
||||
static std::size_t primitive_size(enum type type) {
|
||||
switch (type) {
|
||||
case thing_type::S8: return sizeof(s8);
|
||||
case thing_type::S16: return sizeof(s16);
|
||||
case thing_type::S32: return sizeof(s32);
|
||||
case thing_type::S64: return sizeof(s64);
|
||||
case thing_type::U8: return sizeof(u8);
|
||||
case thing_type::U16: return sizeof(u16);
|
||||
case thing_type::U32: return sizeof(u32);
|
||||
case thing_type::U64: return sizeof(u64);
|
||||
case Ptr:
|
||||
case Ref:
|
||||
case Array:
|
||||
case Slice: return 0;
|
||||
case Count: break;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct thing_type_hash {
|
||||
@@ -151,7 +34,8 @@ struct thing_type_hash {
|
||||
case thing_type::U8:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64: return seed;
|
||||
case thing_type::U64:
|
||||
case thing_type::String: return seed;
|
||||
case thing_type::Ptr:
|
||||
case thing_type::Ref: return furlang::utility::hash_combine(seed, thing_type_hash{}(*type.value.typeRef));
|
||||
case thing_type::Array:
|
||||
@@ -210,19 +94,130 @@ class thing final {
|
||||
public:
|
||||
using allocator_type = Allocator<std::byte>; /**< Allocator type. */
|
||||
public:
|
||||
struct string {
|
||||
std::size_t size;
|
||||
u8* data;
|
||||
|
||||
static bool matches(const thing_type& type) { return type.type == thing_type::String; }
|
||||
};
|
||||
|
||||
struct dynamic_array {
|
||||
std::size_t size;
|
||||
std::byte* data;
|
||||
|
||||
static bool matches(const thing_type& type) {
|
||||
return type.type == thing_type::Array && type.value.array.size == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct slice {
|
||||
std::size_t length;
|
||||
std::byte* data;
|
||||
std::size_t length;
|
||||
std::byte* data;
|
||||
|
||||
static bool matches(const thing_type& type) { return type.type == thing_type::Slice; }
|
||||
};
|
||||
|
||||
struct header {
|
||||
thing_type type;
|
||||
};
|
||||
private:
|
||||
template <bool Const>
|
||||
class generic_iterator {
|
||||
using owner_type = std::conditional_t<Const, const thing, thing>;
|
||||
using data_type = std::byte;
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = thing;
|
||||
using pointer = std::conditional_t<Const, const value_type*, value_type*>;
|
||||
using reference = std::conditional_t<Const, const value_type&, value_type&>;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
public:
|
||||
generic_iterator() = default;
|
||||
|
||||
generic_iterator(owner_type* owner, data_type* ptr)
|
||||
: m_owner(owner), m_ptr(ptr) {
|
||||
fill();
|
||||
}
|
||||
public:
|
||||
reference operator*() { return m_thing; }
|
||||
|
||||
value_type operator*() const {
|
||||
thing thing = { m_thing.type() };
|
||||
thing.assign(m_thing);
|
||||
return thing;
|
||||
}
|
||||
|
||||
reference operator[](difference_type n) { return *(*this + n); }
|
||||
|
||||
pointer operator->() { return &m_thing; }
|
||||
|
||||
generic_iterator& operator+=(difference_type n) {
|
||||
m_ptr += n * step_size();
|
||||
fill();
|
||||
return *this;
|
||||
}
|
||||
|
||||
generic_iterator& operator-=(difference_type n) {
|
||||
m_ptr -= n * step_size();
|
||||
fill();
|
||||
return *this;
|
||||
}
|
||||
|
||||
generic_iterator operator+(difference_type n) const { return { m_owner, m_ptr + (n * step_size()) }; }
|
||||
|
||||
friend generic_iterator operator+(difference_type n, const generic_iterator& it) {
|
||||
return { it.m_owner, it.m_ptr + (n * it.step_size()) };
|
||||
}
|
||||
|
||||
generic_iterator operator-(difference_type n) const { return { m_owner, m_ptr - (n * step_size()) }; }
|
||||
|
||||
difference_type operator-(const generic_iterator& other) const { return (m_ptr - other.m_ptr) / step_size(); }
|
||||
|
||||
generic_iterator& operator++() {
|
||||
m_ptr += step_size();
|
||||
fill();
|
||||
return *this;
|
||||
}
|
||||
|
||||
generic_iterator operator++(int) { return { m_owner, m_ptr + step_size() }; }
|
||||
|
||||
generic_iterator& operator--() {
|
||||
m_ptr -= step_size();
|
||||
fill();
|
||||
return *this;
|
||||
}
|
||||
|
||||
generic_iterator operator--(int) { return { m_owner, m_ptr - step_size() }; }
|
||||
|
||||
bool operator==(const generic_iterator& other) const {
|
||||
return m_owner == other.m_owner && m_ptr == other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator!=(const generic_iterator& other) const {
|
||||
return m_owner != other.m_owner || m_ptr != other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator<(const generic_iterator& other) const { return other.m_ptr < m_ptr; }
|
||||
bool operator>(const generic_iterator& other) const { return other.m_ptr > m_ptr; }
|
||||
bool operator<=(const generic_iterator& other) const { return other.m_ptr <= m_ptr; }
|
||||
bool operator>=(const generic_iterator& other) const { return other.m_ptr >= m_ptr; }
|
||||
private:
|
||||
difference_type step_size() const { return thing::compute_size_na(m_owner->inner_type()); }
|
||||
|
||||
void fill() {
|
||||
m_thing.m_reference = true;
|
||||
m_thing.m_data = m_ptr;
|
||||
m_thing.m_type = &m_owner->inner_type();
|
||||
m_thing.m_size = compute_size_na(m_owner->inner_type());
|
||||
}
|
||||
private:
|
||||
owner_type* m_owner = nullptr;
|
||||
data_type* m_ptr = nullptr;
|
||||
thing m_thing;
|
||||
};
|
||||
|
||||
using iterator = generic_iterator<false>;
|
||||
using const_iterator = generic_iterator<true>;
|
||||
public:
|
||||
thing(const allocator_type& allocator = {})
|
||||
: m_allocator(allocator) {}
|
||||
@@ -430,7 +425,7 @@ public:
|
||||
*/
|
||||
template <typename T>
|
||||
T& get() {
|
||||
if (compute_size_na(*m_type) != sizeof(T)) throw bad_thing_access();
|
||||
if (!detail::thing_traits<T>{}(*m_type)) throw bad_thing_access();
|
||||
return *std::launder(reinterpret_cast<T*>(m_data));
|
||||
}
|
||||
|
||||
@@ -441,9 +436,15 @@ public:
|
||||
*/
|
||||
template <typename T>
|
||||
const T& get() const {
|
||||
if (compute_size_na(*m_type) != sizeof(T)) throw bad_thing_access();
|
||||
if (!detail::thing_traits<T>{}(*m_type)) throw bad_thing_access();
|
||||
return *std::launder(reinterpret_cast<const T*>(m_data));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set(T&& newValue) {
|
||||
if (!detail::thing_traits<T>{}(*m_type)) throw bad_thing_access();
|
||||
*std::launder(reinterpret_cast<T*>(m_data)) = std::forward<T>(newValue);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a sum of two things.
|
||||
@@ -538,40 +539,60 @@ public:
|
||||
*
|
||||
* @return The integer value.
|
||||
*/
|
||||
thing_type::s64 integer() const {
|
||||
s64 integer() const {
|
||||
switch (type().type) {
|
||||
case thing_type::S8: return get<thing_type::s8>();
|
||||
case thing_type::S16: return get<thing_type::s16>();
|
||||
case thing_type::S32: return get<thing_type::s32>();
|
||||
case thing_type::S64: return get<thing_type::s64>();
|
||||
case thing_type::U8: return get<thing_type::u8>();
|
||||
case thing_type::U16: return get<thing_type::u16>();
|
||||
case thing_type::U32: return get<thing_type::u32>();
|
||||
case thing_type::U64: return get<thing_type::u64>();
|
||||
case thing_type::S8: return get<s8>();
|
||||
case thing_type::S16: return get<s16>();
|
||||
case thing_type::S32: return get<s32>();
|
||||
case thing_type::S64: return get<s64>();
|
||||
case thing_type::U8: return get<u8>();
|
||||
case thing_type::U16: return get<u16>();
|
||||
case thing_type::U32: return get<u32>();
|
||||
case thing_type::U64: return get<u64>();
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
void resize(thing_type::u64 newSize) {
|
||||
if (!is(thing_type::Array)) throw bad_thing_access();
|
||||
if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
|
||||
void resize(u64 newSize) {
|
||||
switch (type().type) {
|
||||
case thing_type::Array: {
|
||||
if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
|
||||
|
||||
auto& array = get<dynamic_array>();
|
||||
if (newSize < 0 || newSize == array.size) return;
|
||||
std::size_t innerSize = compute_size_na(*type().value.array.type);
|
||||
std::byte* newData = new std::byte[innerSize * newSize];
|
||||
std::memcpy(newData, array.data, innerSize * std::min(static_cast<thing_type::u64>(array.size), newSize));
|
||||
array.size = newSize;
|
||||
delete[] array.data;
|
||||
array.data = newData;
|
||||
auto& array = get<dynamic_array>();
|
||||
if (newSize < 0 || newSize == array.size) return;
|
||||
std::size_t innerSize = compute_size_na(*type().value.array.type);
|
||||
std::byte* newData = new std::byte[innerSize * newSize];
|
||||
std::memcpy(newData, array.data, innerSize * std::min(static_cast<u64>(array.size), newSize));
|
||||
array.size = newSize;
|
||||
delete[] array.data;
|
||||
array.data = newData;
|
||||
} break;
|
||||
case thing_type::String: {
|
||||
auto& string = get<struct string>();
|
||||
if (newSize < 0 || newSize == string.size) return;
|
||||
u8* newData = new u8[newSize];
|
||||
std::memcpy(newData, string.data, std::min(static_cast<u64>(string.size), newSize));
|
||||
string.size = newSize;
|
||||
delete[] string.data;
|
||||
string.data = newData;
|
||||
} break;
|
||||
default: throw bad_thing_access();
|
||||
}
|
||||
}
|
||||
|
||||
thing at(thing_type::u64 index) const {
|
||||
thing at(u64 index) const {
|
||||
thing ref = { m_allocator };
|
||||
ref.m_reference = true;
|
||||
ref.m_size = compute_size_na(*(ref.m_type = &inner_type()));
|
||||
|
||||
switch (type().type) {
|
||||
case thing_type::String: {
|
||||
auto& string = get<struct string>();
|
||||
if (index < 0 || index >= string.size) throw std::out_of_range("index out of range");
|
||||
|
||||
ref.m_data = reinterpret_cast<std::byte*>(string.data + index);
|
||||
return ref;
|
||||
}
|
||||
case thing_type::Array: {
|
||||
if (type().value.array.size == 0) {
|
||||
auto& array = get<dynamic_array>();
|
||||
@@ -586,7 +607,7 @@ public:
|
||||
return ref;
|
||||
}
|
||||
case thing_type::Slice: {
|
||||
const auto& slice = get<struct slice>();
|
||||
const auto& slice = get<struct slice>();
|
||||
if (index < 0 || index >= slice.length) throw std::out_of_range("index out of range");
|
||||
ref.m_data = slice.data + (index * ref.m_size);
|
||||
return ref;
|
||||
@@ -595,7 +616,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
thing slice(thing_type::u64 begin, thing_type::u64 len) const {
|
||||
thing slice(u64 begin, u64 len) const {
|
||||
auto& inner = inner_type();
|
||||
|
||||
thing_type sliceType;
|
||||
@@ -609,22 +630,26 @@ public:
|
||||
len = std::min(len, length() - begin);
|
||||
|
||||
switch (type().type) {
|
||||
case thing_type::String: {
|
||||
data.data = reinterpret_cast<std::byte*>(get<struct string>().data);
|
||||
} break;
|
||||
case thing_type::Array: {
|
||||
data.data = ((type().value.array.size != 0) ? m_data : get<dynamic_array>().data) ;
|
||||
data.data = ((type().value.array.size != 0) ? m_data : get<dynamic_array>().data);
|
||||
} break;
|
||||
case thing_type::Slice: {
|
||||
data.data = get<struct slice>().data ;
|
||||
data.data = get<struct slice>().data;
|
||||
} break;
|
||||
default: throw bad_thing_access();
|
||||
}
|
||||
|
||||
data.data += (compute_size_na(inner) * begin);
|
||||
data.length = len;
|
||||
return slice;
|
||||
data.data += (compute_size_na(inner) * begin);
|
||||
data.length = len;
|
||||
return slice;
|
||||
}
|
||||
|
||||
thing_type::u64 length() const {
|
||||
u64 length() const {
|
||||
switch (type().type) {
|
||||
case thing_type::String: return get<struct string>().size;
|
||||
case thing_type::Array:
|
||||
return type().value.array.size == 0 ? get<dynamic_array>().size : type().value.array.size;
|
||||
case thing_type::Slice: return get<struct slice>().length;
|
||||
@@ -655,6 +680,7 @@ public:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
|
||||
case thing_type::String:
|
||||
case thing_type::Ptr:
|
||||
case thing_type::Ref:
|
||||
case thing_type::Array:
|
||||
@@ -663,6 +689,71 @@ public:
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
|
||||
void assign(const thing& rhs) {
|
||||
if (type() != rhs.type()) throw std::runtime_error("thing type mismatch");
|
||||
// TODO: Move this to another function
|
||||
switch (type().type) {
|
||||
case thing_type::S8:
|
||||
case thing_type::S16:
|
||||
case thing_type::S32:
|
||||
case thing_type::S64:
|
||||
case thing_type::U8:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
|
||||
case thing_type::Ptr:
|
||||
case thing_type::Ref:
|
||||
case thing_type::Array:
|
||||
case thing_type::Slice: throw std::runtime_error("unimplemented");
|
||||
case thing_type::Count: break;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assign(const T& value) {
|
||||
if constexpr (detail::cassignable_to_thing<T, thing>::value) {
|
||||
detail::thing_traits<T>::assign_to(*this, value);
|
||||
} else {
|
||||
get<T>() = value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assign(T&& value) { // NOLINT
|
||||
if constexpr (detail::massignable_to_thing<T, thing>::value) {
|
||||
detail::thing_traits<T>::assign_to(*this, std::move(value)); // NOLINT
|
||||
} else {
|
||||
get<T>() = std::move(value); // NOLINT
|
||||
}
|
||||
}
|
||||
public:
|
||||
iterator begin() {
|
||||
switch (type().type) {
|
||||
case thing_type::String: return { this, reinterpret_cast<std::byte*>(get<struct string>().data) };
|
||||
case thing_type::Array: return { this, (type().value.array.size == 0) ? get<dynamic_array>().data : m_data };
|
||||
case thing_type::Slice: return { this, get<struct slice>().data };
|
||||
default: throw bad_thing_access();
|
||||
}
|
||||
}
|
||||
|
||||
iterator end() { return begin() + length(); }
|
||||
|
||||
const_iterator cbegin() const {
|
||||
switch (type().type) {
|
||||
case thing_type::String: return { this, reinterpret_cast<std::byte*>(get<struct string>().data) };
|
||||
case thing_type::Array: return { this, (type().value.array.size == 0) ? get<dynamic_array>().data : m_data };
|
||||
case thing_type::Slice: return { this, get<struct slice>().data };
|
||||
default: throw bad_thing_access();
|
||||
}
|
||||
}
|
||||
|
||||
const_iterator cend() const { return cbegin() + length(); }
|
||||
|
||||
const_iterator begin() const { return cbegin(); }
|
||||
|
||||
const_iterator end() const { return cend(); }
|
||||
private:
|
||||
static void copy_list(const thing_type& arrayType, void* dst, const void* src) {
|
||||
if (arrayType.type != thing_type::Array || arrayType.value.array.type == nullptr)
|
||||
@@ -697,6 +788,7 @@ private:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64:
|
||||
case thing_type::String:
|
||||
case thing_type::Ptr:
|
||||
case thing_type::Ref:
|
||||
case thing_type::Slice: std::memcpy(dst, src, size * elementSize); return;
|
||||
@@ -714,14 +806,15 @@ private:
|
||||
private:
|
||||
static std::size_t compute_size_na(const thing_type& type) {
|
||||
switch (type.type) {
|
||||
case thing_type::S8: return sizeof(thing_type::s8);
|
||||
case thing_type::S16: return sizeof(thing_type::s16);
|
||||
case thing_type::S32: return sizeof(thing_type::s32);
|
||||
case thing_type::S64: return sizeof(thing_type::s64);
|
||||
case thing_type::U8: return sizeof(thing_type::u8);
|
||||
case thing_type::U16: return sizeof(thing_type::u16);
|
||||
case thing_type::U32: return sizeof(thing_type::u32);
|
||||
case thing_type::U64: return sizeof(thing_type::u64);
|
||||
case thing_type::S8: return sizeof(s8);
|
||||
case thing_type::S16: return sizeof(s16);
|
||||
case thing_type::S32: return sizeof(s32);
|
||||
case thing_type::S64: return sizeof(s64);
|
||||
case thing_type::U8: return sizeof(u8);
|
||||
case thing_type::U16: return sizeof(u16);
|
||||
case thing_type::U32: return sizeof(u32);
|
||||
case thing_type::U64: return sizeof(u64);
|
||||
case thing_type::String: return sizeof(string);
|
||||
case thing_type::Ptr: return sizeof(void*);
|
||||
case thing_type::Array:
|
||||
return type.value.array.size == 0 ? sizeof(dynamic_array)
|
||||
@@ -740,14 +833,14 @@ private:
|
||||
template <typename Func>
|
||||
decltype(auto) visit_primitive(Func&& func) const {
|
||||
switch (type().type) {
|
||||
case thing_type::S8: return std::forward<Func>(func)(get<thing_type::s8>());
|
||||
case thing_type::S16: return std::forward<Func>(func)(get<thing_type::s16>());
|
||||
case thing_type::S32: return std::forward<Func>(func)(get<thing_type::s32>());
|
||||
case thing_type::S64: return std::forward<Func>(func)(get<thing_type::s64>());
|
||||
case thing_type::U8: return std::forward<Func>(func)(get<thing_type::u8>());
|
||||
case thing_type::U16: return std::forward<Func>(func)(get<thing_type::u16>());
|
||||
case thing_type::U32: return std::forward<Func>(func)(get<thing_type::u32>());
|
||||
case thing_type::U64: return std::forward<Func>(func)(get<thing_type::u64>());
|
||||
case thing_type::S8: return std::forward<Func>(func)(get<s8>());
|
||||
case thing_type::S16: return std::forward<Func>(func)(get<s16>());
|
||||
case thing_type::S32: return std::forward<Func>(func)(get<s32>());
|
||||
case thing_type::S64: return std::forward<Func>(func)(get<s64>());
|
||||
case thing_type::U8: return std::forward<Func>(func)(get<u8>());
|
||||
case thing_type::U16: return std::forward<Func>(func)(get<u16>());
|
||||
case thing_type::U32: return std::forward<Func>(func)(get<u32>());
|
||||
case thing_type::U64: return std::forward<Func>(func)(get<u64>());
|
||||
default: throw bad_thing_access();
|
||||
}
|
||||
}
|
||||
@@ -834,30 +927,15 @@ private:
|
||||
|
||||
thing res = { thing_type{ resultType }, m_allocator };
|
||||
switch (resultType) {
|
||||
case thing_type::S8:
|
||||
res.get<thing_type::s8>() = Op{}(cast_to<thing_type::s8>(), rhs.cast_to<thing_type::s8>());
|
||||
return res;
|
||||
case thing_type::S16:
|
||||
res.get<thing_type::s16>() = Op{}(cast_to<thing_type::s16>(), rhs.cast_to<thing_type::s16>());
|
||||
return res;
|
||||
case thing_type::S32:
|
||||
res.get<thing_type::s32>() = Op{}(cast_to<thing_type::s32>(), rhs.cast_to<thing_type::s32>());
|
||||
return res;
|
||||
case thing_type::S64:
|
||||
res.get<thing_type::s64>() = Op{}(cast_to<thing_type::s64>(), rhs.cast_to<thing_type::s64>());
|
||||
return res;
|
||||
case thing_type::U8:
|
||||
res.get<thing_type::u8>() = Op{}(cast_to<thing_type::u8>(), rhs.cast_to<thing_type::u8>());
|
||||
return res;
|
||||
case thing_type::U16:
|
||||
res.get<thing_type::u16>() = Op{}(cast_to<thing_type::u16>(), rhs.cast_to<thing_type::u16>());
|
||||
return res;
|
||||
case thing_type::U32:
|
||||
res.get<thing_type::u32>() = Op{}(cast_to<thing_type::u32>(), rhs.cast_to<thing_type::u32>());
|
||||
return res;
|
||||
case thing_type::U64:
|
||||
res.get<thing_type::u64>() = Op{}(cast_to<thing_type::u64>(), rhs.cast_to<thing_type::u64>());
|
||||
return res;
|
||||
case thing_type::S8: res.get<s8>() = Op{}(cast_to<s8>(), rhs.cast_to<s8>()); return res;
|
||||
case thing_type::S16: res.get<s16>() = Op{}(cast_to<s16>(), rhs.cast_to<s16>()); return res;
|
||||
case thing_type::S32: res.get<s32>() = Op{}(cast_to<s32>(), rhs.cast_to<s32>()); return res;
|
||||
case thing_type::S64: res.get<s64>() = Op{}(cast_to<s64>(), rhs.cast_to<s64>()); return res;
|
||||
case thing_type::U8: res.get<u8>() = Op{}(cast_to<u8>(), rhs.cast_to<u8>()); return res;
|
||||
case thing_type::U16: res.get<u16>() = Op{}(cast_to<u16>(), rhs.cast_to<u16>()); return res;
|
||||
case thing_type::U32: res.get<u32>() = Op{}(cast_to<u32>(), rhs.cast_to<u32>()); return res;
|
||||
case thing_type::U64: res.get<u64>() = Op{}(cast_to<u64>(), rhs.cast_to<u64>()); return res;
|
||||
case thing_type::String:
|
||||
case thing_type::Ptr: // TODO: Pointer arithmetics
|
||||
case thing_type::Ref:
|
||||
case thing_type::Array:
|
||||
@@ -889,6 +967,10 @@ private:
|
||||
|
||||
thing_type& inner_type() const {
|
||||
switch (type().type) {
|
||||
case thing_type::String: {
|
||||
static thing_type s_inner = { thing_type::U8 };
|
||||
return s_inner;
|
||||
}
|
||||
case thing_type::Array: return *type().value.array.type;
|
||||
case thing_type::Slice: return *type().value.slice.type;
|
||||
default: throw bad_thing_access();
|
||||
@@ -898,13 +980,41 @@ private:
|
||||
// A flag indicating whether the thing instance owns the data, or not.
|
||||
bool m_reference = false;
|
||||
|
||||
thing_type* m_type = nullptr;
|
||||
std::size_t m_size = 0;
|
||||
std::byte* m_data = nullptr;
|
||||
const thing_type* m_type = nullptr;
|
||||
std::size_t m_size = 0;
|
||||
std::byte* m_data = nullptr;
|
||||
|
||||
allocator_type m_allocator;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <>
|
||||
struct thing_traits<std::string_view> {
|
||||
template <template <typename...> class Allocator>
|
||||
static void assign_to(thing<Allocator>& thing, const std::string_view& value) {
|
||||
using Thing = furvm::thing<Allocator>;
|
||||
|
||||
auto& string = thing.template get<typename Thing::string>();
|
||||
string.data = new furvm::u8[string.size = value.length()];
|
||||
std::memcpy(string.data, value.data(), value.length());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<std::string> {
|
||||
template <template <typename...> class Allocator>
|
||||
static void assign_to(thing<Allocator>& thing, const std::string& value) {
|
||||
using Thing = furvm::thing<Allocator>;
|
||||
|
||||
auto& string = thing.template get<typename Thing::string>();
|
||||
string.data = new furvm::u8[string.size = value.length()];
|
||||
std::memcpy(string.data, value.data(), value.length());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_THING_HPP
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#ifndef FURVM_TYPES_HPP
|
||||
#define FURVM_TYPES_HPP
|
||||
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
struct thing_type {
|
||||
struct array_value {
|
||||
thing_type* type;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
struct slice_value {
|
||||
thing_type* type;
|
||||
};
|
||||
|
||||
enum type { // NOLINT
|
||||
S8 = 0,
|
||||
S16,
|
||||
S32,
|
||||
S64,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
String,
|
||||
Ptr,
|
||||
Ref,
|
||||
Array,
|
||||
Slice,
|
||||
|
||||
Count,
|
||||
} type = Count;
|
||||
union value {
|
||||
std::nullptr_t null = nullptr;
|
||||
thing_type* typeRef;
|
||||
array_value array;
|
||||
slice_value slice;
|
||||
|
||||
value() = default;
|
||||
|
||||
value(thing_type* type)
|
||||
: typeRef(type) {}
|
||||
|
||||
value(thing_type* type, std::size_t size)
|
||||
: array({}) {
|
||||
array.type = type;
|
||||
array.size = size;
|
||||
}
|
||||
} value;
|
||||
|
||||
static constexpr thing_type_id INVALID_ID = std::numeric_limits<thing_type_id>::max();
|
||||
|
||||
thing_type_id id = INVALID_ID;
|
||||
|
||||
bool operator==(const thing_type& other) const {
|
||||
if (type != other.type) return false;
|
||||
switch (type) {
|
||||
case S8:
|
||||
case S16:
|
||||
case S32:
|
||||
case S64:
|
||||
case U8:
|
||||
case U16:
|
||||
case U32:
|
||||
case U64:
|
||||
case String: return true;
|
||||
case Ptr:
|
||||
case Ref: return *value.typeRef == *other.value.typeRef;
|
||||
case Array: return *value.array.type == *other.value.array.type && value.array.size == other.value.array.size;
|
||||
case Slice: return *value.slice.type == *other.value.slice.type;
|
||||
case Count: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const thing_type& other) const { return !this->operator==(other); }
|
||||
|
||||
static bool is_primitive(enum type type) {
|
||||
switch (type) {
|
||||
case S8:
|
||||
case S16:
|
||||
case S32:
|
||||
case S64:
|
||||
case U8:
|
||||
case U16:
|
||||
case U32:
|
||||
case U64: return true;
|
||||
case String:
|
||||
case Ptr:
|
||||
case Ref:
|
||||
case Array:
|
||||
case Slice: return false;
|
||||
case Count: break;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
|
||||
static std::size_t primitive_size(enum type type) {
|
||||
switch (type) {
|
||||
case thing_type::S8: return sizeof(s8);
|
||||
case thing_type::S16: return sizeof(s16);
|
||||
case thing_type::S32: return sizeof(s32);
|
||||
case thing_type::S64: return sizeof(s64);
|
||||
case thing_type::U8: return sizeof(u8);
|
||||
case thing_type::U16: return sizeof(u16);
|
||||
case thing_type::U32: return sizeof(u32);
|
||||
case thing_type::U64: return sizeof(u64);
|
||||
case thing_type::String:
|
||||
case Ptr:
|
||||
case Ref:
|
||||
case Array:
|
||||
case Slice: return 0;
|
||||
case Count: break;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct overrides_thing_type_matching : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct overrides_thing_type_matching<T, std::void_t<decltype(T::matches(std::declval<const thing_type&>()))>>
|
||||
: std::is_same<decltype(T::matches(std::declval<const thing_type&>())), bool> {};
|
||||
|
||||
template <typename T>
|
||||
struct thing_traits {
|
||||
bool operator()(const thing_type& type) const {
|
||||
if constexpr (overrides_thing_type_matching<T>::value) {
|
||||
return T::matches(type);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<s8> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::S8; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<u8> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::U8; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<s16> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::S16; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<u16> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::U16; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<s32> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::S32; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<u32> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::U32; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<s64> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::S64; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct thing_traits<u64> {
|
||||
bool operator()(const thing_type& type) const { return type.type == thing_type::U64; }
|
||||
};
|
||||
|
||||
template <typename Inner>
|
||||
struct thing_traits<Inner*> {
|
||||
bool operator()(const thing_type& type) const {
|
||||
return (type.type == thing_type::Ptr || type.type == thing_type::Ref) &&
|
||||
thing_traits<Inner>{}(*type.value.typeRef);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Thing, typename = void>
|
||||
struct cassignable_to_thing : std::false_type {};
|
||||
|
||||
template <typename T, typename Thing>
|
||||
struct cassignable_to_thing<T,
|
||||
Thing,
|
||||
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<const T&>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T, typename Thing, typename = void>
|
||||
struct massignable_to_thing : std::false_type {};
|
||||
|
||||
template <typename T, typename Thing>
|
||||
struct massignable_to_thing<T,
|
||||
Thing,
|
||||
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<T&&>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_TYPES_HPP
|
||||
+20
-24
@@ -31,7 +31,8 @@ thing_type executor::thing_type_impl(mod_h mod, mod_type type) const {
|
||||
case thing_type::U8:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64: return { static_cast<enum thing_type::type>(type.type) };
|
||||
case thing_type::U64:
|
||||
case thing_type::String: return { static_cast<enum thing_type::type>(type.type) };
|
||||
case thing_type::Ptr: return { thing_type::Ptr, mod_to_thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
||||
case thing_type::Ref: return { thing_type::Ref, mod_to_thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
||||
case thing_type::Array: {
|
||||
@@ -60,7 +61,8 @@ bool executor::compare_thing_types(const thing_type& lhs, const thing_type& rhs)
|
||||
case thing_type::U8:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64: return true;
|
||||
case thing_type::U64:
|
||||
case thing_type::String: return true;
|
||||
case thing_type::Ptr:
|
||||
case thing_type::Ref: return compare_thing_types(*lhs.value.typeRef, *rhs.value.typeRef);
|
||||
case thing_type::Array:
|
||||
@@ -211,44 +213,41 @@ void executor::step() {
|
||||
switch (instr.type) {
|
||||
case instruction_t::NoOperation: break;
|
||||
case instruction_t::PushS8: {
|
||||
push_thing({ (struct thing_type){ thing_type::S8 }, { m_stackStorage } }).get<thing_type::s8>() = instr.arg.s8;
|
||||
push_thing({ (struct thing_type){ thing_type::S8 }, { m_stackStorage } }).get<s8>() = instr.arg.s8;
|
||||
} break;
|
||||
case instruction_t::PushU8: {
|
||||
push_thing({ (struct thing_type){ thing_type::U8 }, { m_stackStorage } }).get<thing_type::u8>() = instr.arg.u8;
|
||||
push_thing({ (struct thing_type){ thing_type::U8 }, { m_stackStorage } }).get<u8>() = instr.arg.u8;
|
||||
} break;
|
||||
case instruction_t::PushS16: {
|
||||
push_thing({ (struct thing_type){ thing_type::S16 }, { m_stackStorage } }).get<thing_type::s16>() =
|
||||
instr.arg.s16;
|
||||
push_thing({ (struct thing_type){ thing_type::S16 }, { m_stackStorage } }).get<s16>() = instr.arg.s16;
|
||||
} break;
|
||||
case instruction_t::PushU16: {
|
||||
push_thing({ (struct thing_type){ thing_type::U16 }, { m_stackStorage } }).get<thing_type::u16>() =
|
||||
instr.arg.u16;
|
||||
push_thing({ (struct thing_type){ thing_type::U16 }, { m_stackStorage } }).get<u16>() = instr.arg.u16;
|
||||
} break;
|
||||
case instruction_t::PushS32: {
|
||||
push_thing({ (struct thing_type){ thing_type::S32 }, { m_stackStorage } }).get<thing_type::s32>() =
|
||||
instr.arg.s8; // NOLINT
|
||||
push_thing({ (struct thing_type){ thing_type::S32 }, { m_stackStorage } }).get<s32>() = instr.arg.s8; // NOLINT
|
||||
} break;
|
||||
case instruction_t::PushU32: {
|
||||
push_thing({ (struct thing_type){ thing_type::U32 }, { m_stackStorage } }).get<thing_type::u32>() =
|
||||
static_cast<thing_type::u32>(instr.arg.u8);
|
||||
push_thing({ (struct thing_type){ thing_type::U32 }, { m_stackStorage } }).get<u32>() =
|
||||
static_cast<u32>(instr.arg.u8);
|
||||
} break;
|
||||
case instruction_t::PushConstant: {
|
||||
auto constant = frame.mod->constant_at(instr.arg.u16);
|
||||
switch (constant.type) {
|
||||
case constant::S32:
|
||||
push_thing({ (struct thing_type){ thing_type::S32 }, { m_stackStorage } }).get<thing_type::s32>() =
|
||||
push_thing({ (struct thing_type){ thing_type::S32 }, { m_stackStorage } }).get<s32>() =
|
||||
constant.s32; // NOLINT
|
||||
break;
|
||||
case constant::U32:
|
||||
push_thing({ (struct thing_type){ thing_type::U32 }, { m_stackStorage } }).get<thing_type::u32>() =
|
||||
push_thing({ (struct thing_type){ thing_type::U32 }, { m_stackStorage } }).get<u32>() =
|
||||
constant.u32; // NOLINT
|
||||
break;
|
||||
case constant::S64:
|
||||
push_thing({ (struct thing_type){ thing_type::S64 }, { m_stackStorage } }).get<thing_type::s64>() =
|
||||
push_thing({ (struct thing_type){ thing_type::S64 }, { m_stackStorage } }).get<s64>() =
|
||||
constant.s64; // NOLINT
|
||||
break;
|
||||
case constant::U64:
|
||||
push_thing({ (struct thing_type){ thing_type::U64 }, { m_stackStorage } }).get<thing_type::u64>() =
|
||||
push_thing({ (struct thing_type){ thing_type::U64 }, { m_stackStorage } }).get<u64>() =
|
||||
constant.u64; // NOLINT
|
||||
break;
|
||||
case constant::String: throw std::runtime_error("unimplemented");
|
||||
@@ -375,23 +374,20 @@ void executor::step() {
|
||||
case thing_type::U8:
|
||||
case thing_type::U16:
|
||||
case thing_type::U32:
|
||||
case thing_type::U64:
|
||||
size.get<thing_type::u64>() = static_cast<thing_type::u64>(thing_type::primitive_size(thing.type().type));
|
||||
break;
|
||||
case thing_type::Ptr: size.get<thing_type::u64>() = static_cast<thing_type::u64>(sizeof(void*)); break;
|
||||
case thing_type::U64: size.get<u64>() = static_cast<u64>(thing_type::primitive_size(thing.type().type)); break;
|
||||
case thing_type::Ptr: size.get<u64>() = static_cast<u64>(sizeof(void*)); break;
|
||||
case thing_type::Array:
|
||||
/* TODO: Return actual memory size of the array
|
||||
* By the memory size I mean the length times sizeof single element.
|
||||
*/
|
||||
size.get<thing_type::u64>() = thing.length();
|
||||
size.get<u64>() = thing.length();
|
||||
break;
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
} break;
|
||||
case instruction_t::Lengthof: {
|
||||
auto thing = pop_thing();
|
||||
push_thing({ (struct thing_type){ thing_type::U64 }, { m_stackStorage } }).get<thing_type::u64>() =
|
||||
thing.length();
|
||||
auto thing = pop_thing();
|
||||
push_thing({ (struct thing_type){ thing_type::U64 }, { m_stackStorage } }).get<u64>() = thing.length();
|
||||
} break;
|
||||
case instruction_t::Load: {
|
||||
push_thing(std::move(stack_thing::make_reference(load_thing(instr.arg.u16), { m_stackStorage })));
|
||||
|
||||
+10
-10
@@ -14,17 +14,17 @@ static void print_thing(const furvm::thing<>& thing) {
|
||||
using namespace furvm;
|
||||
|
||||
switch (thing.type().type) {
|
||||
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
|
||||
case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break;
|
||||
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;
|
||||
case thing_type::S64: std::cout << thing.get<thing_type::s64>(); break;
|
||||
case thing_type::U8: std::cout << thing.get<thing_type::u8>(); break;
|
||||
case thing_type::U16: std::cout << thing.get<thing_type::u16>(); break;
|
||||
case thing_type::U32: std::cout << thing.get<thing_type::u32>(); break;
|
||||
case thing_type::U64: std::cout << thing.get<thing_type::u64>(); break;
|
||||
case thing_type::S8: std::cout << thing.cast_to<s16>(); break;
|
||||
case thing_type::S16: std::cout << thing.get<s16>(); break;
|
||||
case thing_type::S32: std::cout << thing.get<s32>(); break;
|
||||
case thing_type::S64: std::cout << thing.get<s64>(); break;
|
||||
case thing_type::U8: std::cout << thing.get<u8>(); break;
|
||||
case thing_type::U16: std::cout << thing.get<u16>(); break;
|
||||
case thing_type::U32: std::cout << thing.get<u32>(); break;
|
||||
case thing_type::U64: std::cout << thing.get<u64>(); break;
|
||||
case thing_type::Array:
|
||||
std::cout << "{ ";
|
||||
for (thing_type::u64 i = 0; i < thing.length(); ++i) {
|
||||
for (u64 i = 0; i < thing.length(); ++i) {
|
||||
if (i > 0) std::cout << ", ";
|
||||
print_thing(thing.at(i));
|
||||
}
|
||||
@@ -32,7 +32,7 @@ static void print_thing(const furvm::thing<>& thing) {
|
||||
break;
|
||||
case thing_type::Slice:
|
||||
std::cout << "Slice [" << thing.length() << "] { ";
|
||||
for (thing_type::u64 i = 0; i < thing.length(); ++i) {
|
||||
for (u64 i = 0; i < thing.length(); ++i) {
|
||||
if (i > 0) std::cout << ", ";
|
||||
print_thing(thing.at(i));
|
||||
}
|
||||
|
||||
+66
-13
@@ -1,3 +1,4 @@
|
||||
#include "furvm/exceptions.hpp"
|
||||
#include "furvm/furvm.hpp"
|
||||
#include "furvm/thing.hpp"
|
||||
|
||||
@@ -10,28 +11,28 @@ namespace {
|
||||
|
||||
TEST(ThingOps, Add) {
|
||||
furvm::thing lhs{ furvm::thing_type{ furvm::thing_type::U32 } };
|
||||
lhs.get<furvm::thing_type::u32>() = 6;
|
||||
lhs.get<furvm::u32>() = 6;
|
||||
furvm::thing rhs{ furvm::thing_type{ furvm::thing_type::U32 } };
|
||||
rhs.get<furvm::thing_type::u32>() = 7;
|
||||
rhs.get<furvm::u32>() = 7;
|
||||
|
||||
auto res = lhs.add(rhs);
|
||||
ASSERT_EQ(res.type().type, furvm::thing_type::U32);
|
||||
EXPECT_EQ(lhs.get<furvm::thing_type::u32>(), 6);
|
||||
EXPECT_EQ(rhs.get<furvm::thing_type::u32>(), 7);
|
||||
EXPECT_EQ(res.get<furvm::thing_type::u32>(), 6 + 7);
|
||||
EXPECT_EQ(lhs.get<furvm::u32>(), 6);
|
||||
EXPECT_EQ(rhs.get<furvm::u32>(), 7);
|
||||
EXPECT_EQ(res.get<furvm::u32>(), 6 + 7);
|
||||
}
|
||||
|
||||
TEST(ThingOps, Array) {
|
||||
furvm::thing_type innerType = { furvm::thing_type::U32 };
|
||||
|
||||
static constexpr std::size_t LENGTH = 10;
|
||||
static constexpr std::array<furvm::thing_type::u32, LENGTH> values = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 7 };
|
||||
static constexpr std::size_t LENGTH = 10;
|
||||
static constexpr std::array<furvm::u32, LENGTH> values = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 7 };
|
||||
|
||||
furvm::thing array{ furvm::thing_type{ furvm::thing_type::Array, { &innerType, LENGTH } } };
|
||||
EXPECT_EQ(array.length(), LENGTH);
|
||||
for (std::size_t i = 0; i < LENGTH; ++i) {
|
||||
auto el = array.at(i);
|
||||
el.get<furvm::thing_type::u32>() = values[i];
|
||||
auto el = array.at(i);
|
||||
el.get<furvm::u32>() = values[i];
|
||||
EXPECT_EQ(array.at(i).integer(), values[i]);
|
||||
}
|
||||
}
|
||||
@@ -39,19 +40,71 @@ TEST(ThingOps, Array) {
|
||||
TEST(ThingOps, Slice) {
|
||||
furvm::thing_type innerType = { furvm::thing_type::U32 };
|
||||
|
||||
static constexpr std::size_t LENGTH = 10;
|
||||
static constexpr std::array<furvm::thing_type::u32, LENGTH> values = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 7 };
|
||||
static constexpr std::size_t LENGTH = 10;
|
||||
static constexpr std::array<furvm::u32, LENGTH> values = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 7 };
|
||||
|
||||
furvm::thing array{ furvm::thing_type{ furvm::thing_type::Array, { &innerType, LENGTH } } };
|
||||
EXPECT_EQ(array.length(), LENGTH);
|
||||
furvm::thing slice = array.slice(0, LENGTH);
|
||||
EXPECT_EQ(slice.length(), LENGTH);
|
||||
for (std::size_t i = 0; i < LENGTH; ++i) {
|
||||
auto el = slice.at(i);
|
||||
el.get<furvm::thing_type::u32>() = values[i];
|
||||
auto el = slice.at(i);
|
||||
el.get<furvm::u32>() = values[i];
|
||||
EXPECT_EQ(array.at(i).integer(), values[i]);
|
||||
EXPECT_EQ(slice.at(i).integer(), array.at(i).integer());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ThingOps, String) {
|
||||
static constexpr std::string_view STRING = "femboje na topie";
|
||||
static constexpr auto LENGTH = STRING.length();
|
||||
|
||||
furvm::thing string{ furvm::thing_type{ furvm::thing_type::String } };
|
||||
string.assign(STRING);
|
||||
EXPECT_EQ(string.length(), LENGTH);
|
||||
furvm::thing slice = string.slice(0, LENGTH);
|
||||
EXPECT_EQ(slice.length(), LENGTH);
|
||||
for (std::size_t i = 0; i < LENGTH; ++i) {
|
||||
auto el = slice.at(i);
|
||||
EXPECT_EQ(slice.at(i).get<furvm::u8>(), STRING[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ThingOps, Iterators) {
|
||||
furvm::thing_type innerType = { furvm::thing_type::U32 };
|
||||
|
||||
static constexpr std::size_t LENGTH = 10;
|
||||
static constexpr std::array<furvm::u32, LENGTH> values = { 0, 1, 2, 3, 4, 5, 6, 7, 6, 7 };
|
||||
|
||||
furvm::thing array{ furvm::thing_type{ furvm::thing_type::Array, { &innerType, LENGTH } } };
|
||||
EXPECT_EQ(array.length(), LENGTH);
|
||||
for (std::size_t i = 0; i < LENGTH; ++i) {
|
||||
auto el = array.at(i);
|
||||
el.get<furvm::u32>() = values[i];
|
||||
EXPECT_EQ(array.at(i).integer(), values[i]);
|
||||
}
|
||||
|
||||
std::size_t itCount = 0;
|
||||
for (auto it = array.begin(); it != array.end(); ++it, ++itCount) {
|
||||
auto idx = it - array.begin();
|
||||
ASSERT_EQ(idx, itCount);
|
||||
ASSERT_LT(idx, LENGTH);
|
||||
EXPECT_EQ(it->integer(), values[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ThingOps, Access) {
|
||||
furvm::thing thing{ furvm::thing_type{ furvm::thing_type::U8 } };
|
||||
EXPECT_NO_THROW(thing.get<furvm::u8>());
|
||||
EXPECT_THROW(thing.get<furvm::s8>(), furvm::bad_thing_access);
|
||||
EXPECT_THROW(thing.get<furvm::s32>(), furvm::bad_thing_access);
|
||||
EXPECT_THROW(thing.get<void*>(), furvm::bad_thing_access);
|
||||
|
||||
EXPECT_NO_THROW(thing.assign<furvm::u8>(67));
|
||||
EXPECT_THROW(thing.assign<furvm::s32>(1337), furvm::bad_thing_access);
|
||||
EXPECT_EQ(thing.get<furvm::u8>(), 67);
|
||||
EXPECT_NO_THROW(thing.set<furvm::u8>(67)); // He talkin' 'bout sum 6-7 while I want sixty ni-
|
||||
EXPECT_THROW(thing.set<furvm::s32>(1337), furvm::bad_thing_access);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user