@@ -106,6 +106,55 @@ private:
|
||||
region* m_tail = nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class arena_allocator {
|
||||
template <typename>
|
||||
friend class arena_allocator;
|
||||
public:
|
||||
using value_type = T;
|
||||
public:
|
||||
explicit arena_allocator(arena& arena) noexcept
|
||||
: m_arena(&arena) {}
|
||||
|
||||
template <typename U>
|
||||
arena_allocator(const arena_allocator<U>& other) noexcept
|
||||
: m_arena(other.m_arena) {}
|
||||
|
||||
template <typename U>
|
||||
arena_allocator& operator=(const arena_allocator<U>& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_arena = other.m_arena;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
arena_allocator(arena_allocator<U>&& other) noexcept
|
||||
: m_arena(std::move(other.m_arena)) {}
|
||||
|
||||
template <typename U>
|
||||
arena_allocator& operator=(arena_allocator<U>&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_arena = std::move(other.m_arena);
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
[[nodiscard]] T* allocate(std::size_t count = 1) { return m_arena->allocate<T>(count); }
|
||||
|
||||
void deallocate(T* ptr, std::size_t count) noexcept {}
|
||||
public:
|
||||
template <typename U>
|
||||
bool operator==(const arena_allocator<U>& other) const noexcept {
|
||||
return m_arena == other.m_arena;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
bool operator!=(const arena_allocator<U>& other) const noexcept {
|
||||
return m_arena != other.m_arena;
|
||||
}
|
||||
private:
|
||||
arena* m_arena;
|
||||
};
|
||||
|
||||
} // namespace furlang
|
||||
|
||||
#endif // FURLANG_ARENA_HPP
|
||||
@@ -4,24 +4,18 @@
|
||||
#include "furlang/arena.hpp"
|
||||
#include "furvm/executor.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/module.hpp"
|
||||
#include "furvm/thing.hpp"
|
||||
#include "furvm/handle.hpp"
|
||||
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
class context {
|
||||
public:
|
||||
friend class executor;
|
||||
friend class thing;
|
||||
public:
|
||||
context();
|
||||
~context() = default;
|
||||
@@ -39,105 +33,77 @@ public:
|
||||
context(const context&) = delete;
|
||||
context& operator=(const context&) = delete;
|
||||
public:
|
||||
/**
|
||||
* @brief Emplaces a new module in this context.
|
||||
*
|
||||
* @param args Arguments to forward to module's constructor.
|
||||
* @return A handle to the module.
|
||||
*/
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<mod, Args...>>>
|
||||
mod_h emplace_module(Args&&... args) {
|
||||
mod_p mod = std::make_shared<class mod>(std::forward<Args>(args)...);
|
||||
std::string name = mod->name();
|
||||
return { name, m_modules[name] = std::move(mod) };
|
||||
template <typename... Args>
|
||||
auto emplace_module(Args&&... args) {
|
||||
return m_modules.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases a module from this context.
|
||||
*
|
||||
* @param name Name of the module to erase.
|
||||
* @return A shared pointer to the erased module.
|
||||
*/
|
||||
template <typename Name>
|
||||
mod_p erase_module(Name&& name) {
|
||||
return std::move(m_modules.erase(std::forward<Name>(name))->second);
|
||||
template <typename... Args>
|
||||
auto module_at(Args&&... args) {
|
||||
return m_modules.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a module of this context.
|
||||
*
|
||||
* @param name Name of the module.
|
||||
* @return The module.
|
||||
*/
|
||||
template <typename Name>
|
||||
constexpr mod_p& module_at(Name&& name) {
|
||||
return m_modules.at(std::forward<Name>(name));
|
||||
template <typename... Args>
|
||||
auto module_at(Args&&... args) const {
|
||||
return m_modules.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a module of this context.
|
||||
*
|
||||
* @param name Name of the module.
|
||||
* @return The module.
|
||||
*/
|
||||
template <typename Name>
|
||||
constexpr const mod_p& module_at(Name&& name) const {
|
||||
return m_modules.at(std::forward<Name>(name));
|
||||
template <typename... Args>
|
||||
auto erase_module(Args&&... args) {
|
||||
return m_modules.erase(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns how many does this context have modules.
|
||||
*
|
||||
* @return The module count.
|
||||
*/
|
||||
constexpr size_t module_count() const { return m_modules.size(); }
|
||||
public:
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<executor, Args...>>>
|
||||
executor_h emplace_executor(Args&&... args) {
|
||||
executor executor(std::forward<Args>(args)...);
|
||||
executor_id id = m_executors.size();
|
||||
m_executors.emplace_back(std::move(executor));
|
||||
return { id, m_executors[id] };
|
||||
template <typename... Args>
|
||||
auto emplace_executor(Args&&... args) {
|
||||
return m_executors.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto executor_at(Args&&... args) {
|
||||
return m_executors.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto executor_at(Args&&... args) const {
|
||||
return m_executors.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto erase_executor(Args&&... args) {
|
||||
return m_executors.erase(std::forward<Args>(args)...);
|
||||
}
|
||||
public:
|
||||
template <typename... Args>
|
||||
auto emplace_thing(Args&&... args) {
|
||||
return m_things.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto thing_at(Args&&... args) {
|
||||
return m_things.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto thing_at(Args&&... args) const {
|
||||
return m_things.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto erase_thing(Args&&... args) {
|
||||
return m_things.erase(std::forward<Args>(args)...);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Removes unreferenced things from the thing list.
|
||||
*/
|
||||
void collect();
|
||||
public:
|
||||
thing_h emplace_thing(thing_t type) {
|
||||
thing_id id = m_things.size();
|
||||
if (!m_deadThings.empty()) {
|
||||
id = m_deadThings.front();
|
||||
m_deadThings.pop();
|
||||
}
|
||||
|
||||
size_t size = thing_type_size(type);
|
||||
void* data = nullptr;
|
||||
for (auto it = m_deadThingData.begin(); it != m_deadThingData.end(); ++it) {
|
||||
thing_t curType{};
|
||||
std::memcpy(&curType, static_cast<char*>(*it) - sizeof(curType), sizeof(curType));
|
||||
if (thing_type_size(curType) != size) continue;
|
||||
data = *it;
|
||||
m_deadThingData.erase(it);
|
||||
break;
|
||||
}
|
||||
if (data == nullptr) data = m_thingArena.allocate<char>(sizeof(thing_t) + size);
|
||||
memcpy(data, &type, sizeof(type));
|
||||
data = static_cast<char*>(data) + sizeof(type);
|
||||
|
||||
thing_p thing = std::make_shared<class thing>(type, data);
|
||||
return { id, m_things.emplace_back(std::move(thing)) };
|
||||
}
|
||||
private:
|
||||
std::unordered_map<std::string, mod_p> m_modules;
|
||||
handle_container<mod_h> m_modules;
|
||||
handle_container<thing_h> m_things;
|
||||
handle_container<executor_h> m_executors;
|
||||
|
||||
std::vector<thing_p> m_things;
|
||||
std::vector<executor> m_executors;
|
||||
|
||||
std::queue<thing_id> m_deadThings;
|
||||
std::vector<void*> m_deadThingData;
|
||||
furlang::arena m_thingArena;
|
||||
furlang::arena m_thingArena;
|
||||
thing_allocator<std::byte> m_thingAllocator;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef FURVM_DETAIL_HANDLE_HPP
|
||||
#define FURVM_DETAIL_HANDLE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace furvm {
|
||||
namespace detail {
|
||||
|
||||
template <typename Header, typename = void>
|
||||
struct header_has_refcount : std::false_type {};
|
||||
|
||||
template <typename Header>
|
||||
struct header_has_refcount<Header,
|
||||
std::void_t<decltype(std::declval<Header&>().acquire()),
|
||||
decltype(std::declval<Header&>().release()),
|
||||
decltype(std::declval<Header&>().reference_count())>> : std::true_type {};
|
||||
|
||||
template <typename Header>
|
||||
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_DETAIL_HANDLE_HPP
|
||||
@@ -2,8 +2,11 @@
|
||||
#define FURVM_EXECUTOR_HPP
|
||||
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
@@ -99,19 +102,17 @@ public:
|
||||
*/
|
||||
frame frame() const;
|
||||
public:
|
||||
/**
|
||||
* @brief Pushes a thing onto the stack.
|
||||
*
|
||||
* @param thing The thing to push.
|
||||
*/
|
||||
void push_thing(const thing_h& thing);
|
||||
template <typename HandleFwd>
|
||||
void push_thing(HandleFwd&& handle) {
|
||||
m_stack.emplace(std::forward<HandleFwd>(handle));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pushes a thing onto the stack.
|
||||
*
|
||||
* @param thing The thing to push.
|
||||
*/
|
||||
void push_thing(thing_h&& thing);
|
||||
thing_h push_thing(class thing<>&& thing);
|
||||
|
||||
/**
|
||||
* @brief Pops a thing from the stack.
|
||||
|
||||
+28
-18
@@ -1,8 +1,6 @@
|
||||
#ifndef FURVM_FWD_HPP
|
||||
#define FURVM_FWD_HPP
|
||||
|
||||
#include "furvm/handle.hpp"
|
||||
|
||||
#include <cstddef> // IWYU pragma: export
|
||||
#include <cstdint> // IWYU pragma: export
|
||||
#include <memory>
|
||||
@@ -25,6 +23,24 @@ using byte = std::uint8_t;
|
||||
*/
|
||||
using bytecode_pos = std::uint64_t;
|
||||
|
||||
/**
|
||||
* @brief Handle header with reference count.
|
||||
*/
|
||||
template <typename Id>
|
||||
class refcount_header;
|
||||
|
||||
/**
|
||||
* @brief Generic handle header.
|
||||
*/
|
||||
template <typename Id>
|
||||
class generic_header;
|
||||
|
||||
/**
|
||||
* @brief Handle.
|
||||
*/
|
||||
template <typename Value, typename Header>
|
||||
class handle;
|
||||
|
||||
// constant.hpp
|
||||
|
||||
/**
|
||||
@@ -76,11 +92,6 @@ enum class function_t : std::uint8_t;
|
||||
*/
|
||||
class function;
|
||||
|
||||
/**
|
||||
* @brief An alias to a function shared pointer.
|
||||
*/
|
||||
using function_p = std::shared_ptr<function>;
|
||||
|
||||
/**
|
||||
* @brief Furvm function's index.
|
||||
*/
|
||||
@@ -89,7 +100,7 @@ using function_id = std::uint16_t;
|
||||
/**
|
||||
* @brief A handle to a furvm function.
|
||||
*/
|
||||
using function_h = handle<function_id, function>;
|
||||
using function_h = handle<function, refcount_header<function_id>>;
|
||||
|
||||
// module.hpp
|
||||
|
||||
@@ -114,7 +125,7 @@ using mod_id = std::string;
|
||||
/**
|
||||
* @brief A handle to a furvm module.
|
||||
*/
|
||||
using mod_h = handle<mod_id, mod_p>;
|
||||
using mod_h = handle<mod, refcount_header<mod_id>>;
|
||||
|
||||
// thing.hpp
|
||||
|
||||
@@ -130,28 +141,27 @@ enum class thing_t : std::uint8_t;
|
||||
*/
|
||||
class bad_thing_access;
|
||||
|
||||
template <typename T>
|
||||
class thing_allocator;
|
||||
|
||||
/**
|
||||
* @class thing
|
||||
* @brief Furvm thing.
|
||||
*
|
||||
* A stack element. Think of it like of a value in C++ or I guess a class in java.
|
||||
*/
|
||||
template <template <typename> typename Allocator = thing_allocator>
|
||||
class thing;
|
||||
|
||||
/**
|
||||
* @brief An alias to a thing shared pointer.
|
||||
*/
|
||||
using thing_p = std::shared_ptr<thing>;
|
||||
|
||||
/**
|
||||
* @brief Furvm thing's index.
|
||||
*/
|
||||
using thing_id = std::uint32_t;
|
||||
|
||||
/**
|
||||
* @brief A handle to a furvm's thing.
|
||||
* @brief A handle to a thing.
|
||||
*/
|
||||
using thing_h = handle<thing_id, thing_p>;
|
||||
using thing_h = handle<thing<thing_allocator>, refcount_header<thing_id>>;
|
||||
|
||||
// executor.hpp
|
||||
|
||||
@@ -185,9 +195,9 @@ using executor_p = std::shared_ptr<executor>;
|
||||
using executor_id = std::uint32_t;
|
||||
|
||||
/**
|
||||
* @brief A handle to a furvm's executor.
|
||||
* @brief A handle to an executor.
|
||||
*/
|
||||
using executor_h = handle<executor_id, executor>;
|
||||
using executor_h = handle<executor, refcount_header<executor_id>>;
|
||||
|
||||
// context.hpp
|
||||
|
||||
|
||||
+217
-98
@@ -1,118 +1,237 @@
|
||||
#ifndef FURVM_HANDLE_HPP
|
||||
#define FURVM_HANDLE_HPP
|
||||
|
||||
#include <limits>
|
||||
#include "furvm/detail/handle.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
namespace detail {
|
||||
template <typename Handle, typename = void>
|
||||
class handle_container;
|
||||
|
||||
template <typename Id, typename = void>
|
||||
struct dead_id {
|
||||
static constexpr Id value = {};
|
||||
// TODO: Implement generational indexes
|
||||
|
||||
template <typename Id>
|
||||
class refcount_header {
|
||||
public:
|
||||
using id_type = Id;
|
||||
using refcount_type = std::uint32_t;
|
||||
public:
|
||||
template <typename IdFwd>
|
||||
refcount_header(IdFwd&& id, refcount_type refCount)
|
||||
: m_id(std::forward<IdFwd>(id)), m_refCount(refCount) {}
|
||||
public:
|
||||
refcount_type reference_count() const { return m_refCount; }
|
||||
|
||||
void acquire() { ++m_refCount; }
|
||||
|
||||
void release() { --m_refCount; }
|
||||
public:
|
||||
id_type id() const { return m_id; }
|
||||
private:
|
||||
id_type m_id;
|
||||
std::atomic<refcount_type> m_refCount;
|
||||
};
|
||||
|
||||
template <typename Id>
|
||||
struct dead_id<Id, std::enable_if_t<std::is_integral_v<Id>>> {
|
||||
static constexpr Id value = std::numeric_limits<Id>::max();
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Id, typename T>
|
||||
class handle {
|
||||
class generic_header {
|
||||
public:
|
||||
using id_type = Id;
|
||||
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a new handle.
|
||||
*
|
||||
* @param id Id of the handle.
|
||||
* @param value Value of the handle.
|
||||
*/
|
||||
template <typename IdF, typename Value>
|
||||
handle(IdF&& id, Value&& value)
|
||||
: m_id(std::forward<IdF>(id)), m_value(std::forward<Value>(value)) {}
|
||||
|
||||
handle()
|
||||
: m_id(detail::dead_id<Id>::value), m_value() {}
|
||||
generic_header(id_type id)
|
||||
: m_id(id) {}
|
||||
public:
|
||||
handle& operator=(value_type&& value) noexcept {
|
||||
m_value = std::move(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
handle& operator=(const value_type& value) noexcept {
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the handle's id.
|
||||
*
|
||||
* @return The id.
|
||||
*/
|
||||
constexpr Id id() const { return m_id; }
|
||||
|
||||
/**
|
||||
* @brief Returns the handle's pointer.
|
||||
*
|
||||
* @return The pointer.
|
||||
*/
|
||||
reference value() { return m_value; }
|
||||
|
||||
/**
|
||||
* @brief Returns the handle's pointer.
|
||||
*
|
||||
* @return The pointer.
|
||||
*/
|
||||
const_reference value() const { return m_value; }
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the handle's id.
|
||||
*
|
||||
* @return The id.
|
||||
*/
|
||||
explicit operator Id() const { return m_id; }
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a reference to the handle's value.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
reference operator*() { return m_value; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the handle's value.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
const_reference operator*() const { return m_value; }
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the handle's value.
|
||||
*
|
||||
* @return The value pointer.
|
||||
*/
|
||||
pointer operator->() { return &m_value; }
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the handle's value.
|
||||
*
|
||||
* @return The value pointer.
|
||||
*/
|
||||
const_pointer operator->() const { return &m_value; }
|
||||
id_type id() const { return m_id; }
|
||||
private:
|
||||
id_type m_id;
|
||||
value_type m_value;
|
||||
id_type m_id;
|
||||
};
|
||||
|
||||
template <typename Value, typename Header = refcount_header<std::uint32_t>>
|
||||
class handle {
|
||||
public:
|
||||
using value_type = Value;
|
||||
using reference = Value&;
|
||||
using const_reference = const Value&;
|
||||
using pointer = Value*;
|
||||
using const_pointer = const Value*;
|
||||
public:
|
||||
using id_type = typename Header::id_type;
|
||||
public:
|
||||
using pair_type = std::pair<Header, Value>;
|
||||
public:
|
||||
handle() = default;
|
||||
|
||||
handle(pair_type* value)
|
||||
: m_value(value) {
|
||||
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||
m_value->first.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
~handle() {
|
||||
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||
if (m_value != nullptr) m_value->first.release();
|
||||
}
|
||||
m_value = nullptr;
|
||||
}
|
||||
|
||||
handle(handle&& other) noexcept
|
||||
: m_value(other.m_value) {
|
||||
other.m_value = nullptr;
|
||||
}
|
||||
|
||||
handle& operator=(handle&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_value = other.m_value;
|
||||
other.m_value = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
handle(const handle& other)
|
||||
: m_value(other.m_value) {
|
||||
m_value->first.acquire();
|
||||
}
|
||||
|
||||
handle& operator=(const handle& other) {
|
||||
if (this == &other) return *this;
|
||||
m_value = other.m_value;
|
||||
m_value->first.acquire();
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
id_type id() const { return m_value->first.id(); }
|
||||
|
||||
bool empty() const { return m_value == nullptr; }
|
||||
|
||||
template <typename ReferenceCount = typename Header::refcount_type>
|
||||
ReferenceCount reference_count() const {
|
||||
return m_value->first.reference_count();
|
||||
}
|
||||
|
||||
pointer operator->() { return &m_value->second; }
|
||||
const_pointer operator->() const { return &m_value->second; }
|
||||
|
||||
reference operator*() { return m_value->second; }
|
||||
const_reference operator*() const { return m_value->second; }
|
||||
|
||||
reference value() { return m_value->second; }
|
||||
const_reference value() const { return m_value->second; }
|
||||
private:
|
||||
pair_type* m_value = nullptr;
|
||||
};
|
||||
|
||||
template <typename Handle>
|
||||
class handle_container<Handle, std::enable_if_t<!std::is_integral_v<typename Handle::id_type>>> {
|
||||
private:
|
||||
using pair_type = typename Handle::pair_type;
|
||||
public:
|
||||
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>;
|
||||
using const_value = std::add_const_t<value_type>;
|
||||
|
||||
using id_type = typename Handle::id_type;
|
||||
public:
|
||||
handle_container() = default;
|
||||
~handle_container() = default;
|
||||
|
||||
handle_container(handle_container&&) noexcept = default;
|
||||
handle_container& operator=(handle_container&&) noexcept = default;
|
||||
|
||||
handle_container(const handle_container&) = delete;
|
||||
handle_container& operator=(const handle_container&) = delete;
|
||||
public:
|
||||
template <typename IdFwd,
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<std::is_constructible_v<typename pair_type::second_type, Args...>>>
|
||||
value_type emplace(IdFwd&& id, Args&&... args) {
|
||||
id_type idFwd = std::forward<IdFwd>(id);
|
||||
if (auto it = m_pairs.find(idFwd); it != m_pairs.end()) delete it->second;
|
||||
auto pair = new pair_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(idFwd, 0),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
m_pairs.emplace(std::move(idFwd), pair);
|
||||
return { pair };
|
||||
}
|
||||
|
||||
template <typename IdFwd>
|
||||
value_type at(IdFwd&& id) {
|
||||
return { m_pairs.at(std::forward<IdFwd>(id)) };
|
||||
}
|
||||
|
||||
template <typename IdFwd>
|
||||
const_value at(IdFwd&& id) const {
|
||||
return { m_pairs.at(std::forward<IdFwd>(id)) };
|
||||
}
|
||||
|
||||
template <typename IdFwd>
|
||||
void erase(IdFwd&& id) {
|
||||
auto it = m_pairs.find(std::forward<IdFwd>(id));
|
||||
if (it == m_pairs.end()) return;
|
||||
delete it->second;
|
||||
m_pairs.erase(it);
|
||||
}
|
||||
private:
|
||||
std::unordered_map<id_type, pair_type*> m_pairs;
|
||||
};
|
||||
|
||||
template <typename Handle>
|
||||
class handle_container<Handle, std::enable_if_t<std::is_integral_v<typename Handle::id_type>>> {
|
||||
private:
|
||||
using pair_type = typename Handle::pair_type;
|
||||
public:
|
||||
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>;
|
||||
using const_value = std::add_const_t<value_type>;
|
||||
|
||||
using id_type = typename Handle::id_type;
|
||||
public:
|
||||
handle_container() = default;
|
||||
~handle_container() = default;
|
||||
|
||||
handle_container(handle_container&&) noexcept = default;
|
||||
handle_container& operator=(handle_container&&) noexcept = default;
|
||||
|
||||
handle_container(const handle_container&) = delete;
|
||||
handle_container& operator=(const handle_container&) = delete;
|
||||
public:
|
||||
template <typename... Args,
|
||||
typename = std::enable_if_t<std::is_constructible_v<typename pair_type::second_type, Args...>>>
|
||||
value_type emplace(id_type id, Args&&... args) {
|
||||
if (id >= m_pairs.size()) {
|
||||
m_pairs.resize(id + 1, nullptr);
|
||||
} else if (m_pairs[id] != nullptr) {
|
||||
delete m_pairs[id];
|
||||
}
|
||||
|
||||
pair_type* newPair = new pair_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(id, 0),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
|
||||
m_pairs[id] = newPair;
|
||||
return { newPair };
|
||||
}
|
||||
|
||||
template <typename... Args,
|
||||
typename = std::enable_if_t<std::is_constructible_v<typename Handle::pair_type::second_type, Args...>>>
|
||||
value_type emplace_back(Args&&... args) {
|
||||
return emplace(static_cast<id_type>(m_pairs.size()), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
value_type at(id_type id) { return { m_pairs.at(id) }; }
|
||||
const_value at(id_type id) const { return { m_pairs.at(id) }; }
|
||||
|
||||
void erase(id_type id) {
|
||||
if (id >= m_pairs.size()) return;
|
||||
delete m_pairs[id];
|
||||
m_pairs[id] = nullptr;
|
||||
}
|
||||
private:
|
||||
std::vector<pair_type*> m_pairs;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -24,21 +24,9 @@ public:
|
||||
* @param name Name of this module.
|
||||
* @param args Arguments to forward to bytecode's constructor.
|
||||
*/
|
||||
template <typename Name,
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<std::is_constructible_v<bytecode_t, Args...>>>
|
||||
mod(Name&& name, Args&&... args)
|
||||
: m_name(std::forward<Name>(name)), m_bytecode(std::forward<Args>(args)...) {}
|
||||
|
||||
/**
|
||||
* @brief Construct a new module.
|
||||
*
|
||||
* @param name C-string name of this module.
|
||||
* @param args Arguments to forward to bytecode's constructor.
|
||||
*/
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<bytecode_t, Args...>>>
|
||||
mod(const char* name, Args&&... args)
|
||||
: m_name(name), m_bytecode(std::forward<Args>(args)...) {}
|
||||
mod(Args&&... args)
|
||||
: m_bytecode(std::forward<Args>(args)...) {}
|
||||
|
||||
~mod() = default;
|
||||
|
||||
@@ -55,13 +43,6 @@ public:
|
||||
mod(const mod&) = delete;
|
||||
mod& operator=(const mod&) = delete;
|
||||
public:
|
||||
/**
|
||||
* @brief Returns this module's name.
|
||||
*
|
||||
* @return The name.
|
||||
*/
|
||||
constexpr const std::string& name() const { return m_name; }
|
||||
|
||||
/**
|
||||
* @brief Returns a byte from bytecode of this module.
|
||||
*
|
||||
@@ -84,41 +65,37 @@ public:
|
||||
*/
|
||||
constexpr const bytecode_t& bytecode() const { return m_bytecode; }
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a function from this module.
|
||||
*
|
||||
* @param name Name of the function.
|
||||
* @return The function.
|
||||
*/
|
||||
template <typename Name>
|
||||
function_h function_at(Name&& name) const {
|
||||
if (auto it = m_functionMap.find(std::forward<Name>(name)); it != m_functionMap.end()) return it->second;
|
||||
throw std::runtime_error("invalid function");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a function from this module.
|
||||
*
|
||||
* @param id Id of the function.
|
||||
* @return The function.
|
||||
*/
|
||||
function_h function_at(function_id id) const { return { id, m_functions.at(id) }; }
|
||||
|
||||
template <typename... Args>
|
||||
function_h emplace_function(Args&&... args) {
|
||||
function function(std::forward<Args>(args)...);
|
||||
std::string name = function.name();
|
||||
function_id id = m_functions.size();
|
||||
function_h handle = { id, m_functions.emplace_back(std::move(function)) };
|
||||
m_functionMap.emplace(std::move(name), handle);
|
||||
return handle;
|
||||
function_h function = m_functions.emplace_back(std::forward<Args>(args)...);
|
||||
m_functionMap[function->name()] = function.id();
|
||||
return std::move(function);
|
||||
}
|
||||
private:
|
||||
std::string m_name;
|
||||
bytecode_t m_bytecode;
|
||||
|
||||
std::unordered_map<std::string, function_h> m_functionMap;
|
||||
std::vector<function> m_functions;
|
||||
template <typename FunctionHandle>
|
||||
void push_back(FunctionHandle&& handle) {
|
||||
m_functions.emplace_back(std::forward<FunctionHandle>(handle));
|
||||
}
|
||||
|
||||
auto function_at(function_id id) { return m_functions.at(id); }
|
||||
auto function_at(function_id id) const { return m_functions.at(id); }
|
||||
|
||||
template <typename NameFwd>
|
||||
auto function_at(NameFwd&& name) {
|
||||
return function_at(m_functionMap.at(std::forward<NameFwd>(name)));
|
||||
}
|
||||
|
||||
template <typename NameFwd>
|
||||
auto function_at(NameFwd&& name) const {
|
||||
return function_at(m_functionMap.at(std::forward<NameFwd>(name)));
|
||||
}
|
||||
|
||||
void erase_function(function_id id) { m_functions.erase(id); }
|
||||
private:
|
||||
bytecode_t m_bytecode;
|
||||
|
||||
std::unordered_map<std::string, function_id> m_functionMap;
|
||||
handle_container<function_h> m_functions;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
+170
-43
@@ -1,8 +1,15 @@
|
||||
#ifndef FURVM_THING_HPP
|
||||
#define FURVM_THING_HPP
|
||||
|
||||
#include "furlang/arena.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
enum class thing_t : std::uint8_t {
|
||||
@@ -15,7 +22,12 @@ enum class thing_t : std::uint8_t {
|
||||
* @param type Type of the thing.
|
||||
* @return The data size of the thing.
|
||||
*/
|
||||
std::size_t thing_type_size(thing_t type);
|
||||
static inline std::size_t thing_type_size(thing_t type) {
|
||||
switch (type) {
|
||||
case thing_t::Int32: return sizeof(std::int32_t);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class bad_thing_access : public std::exception {
|
||||
public:
|
||||
@@ -50,28 +62,40 @@ public:
|
||||
const char* what() const noexcept override { return "bad thing access"; }
|
||||
};
|
||||
|
||||
template <template <typename> typename Allocator>
|
||||
class thing final {
|
||||
friend class executor;
|
||||
public:
|
||||
using nref_t = std::size_t; /**< Type of reference count. */
|
||||
|
||||
static constexpr thing_id GENERATION_SIZE = 12; /**< Bit size of generation part in thing_handle. */
|
||||
using allocator_type = Allocator<std::byte>;
|
||||
public:
|
||||
thing(thing_t type);
|
||||
thing(thing_t type, const allocator_type& allocator = {})
|
||||
: m_type(type), m_allocator(allocator) {
|
||||
m_data = m_allocator.allocate(thing_type_size(type));
|
||||
}
|
||||
|
||||
thing(thing_t type, void* data);
|
||||
|
||||
~thing();
|
||||
~thing() { m_allocator.deallocate(m_data, thing_type_size(thing_t::Int32)); }
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
thing(thing&&) noexcept;
|
||||
thing(thing&& other) noexcept
|
||||
: m_type(other.m_type), m_data(other.m_data), m_allocator(std::move(other.m_allocator)) {
|
||||
other.m_type = {};
|
||||
other.m_data = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
thing& operator=(thing&&) noexcept;
|
||||
thing& operator=(thing&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_type = other.m_type;
|
||||
m_data = other.m_data;
|
||||
m_allocator = std::move(other.m_allocator);
|
||||
other.m_type = {};
|
||||
other.m_data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
thing(const thing&) = delete;
|
||||
thing& operator=(const thing&) = delete;
|
||||
@@ -82,55 +106,158 @@ public:
|
||||
* @param thing Thing to clone.
|
||||
* @return Shared pointer to a clone of the thing.
|
||||
*/
|
||||
static thing_h clone(const context_p& context, const thing_h& thing);
|
||||
thing clone() const {
|
||||
class thing res(m_type, m_allocator);
|
||||
switch (m_type) {
|
||||
default: {
|
||||
std::memcpy(res.m_data, m_data, thing_type_size(m_type));
|
||||
} break;
|
||||
}
|
||||
return std::move(res);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns an int32 value from this thing.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
std::int32_t& int32();
|
||||
std::int32_t& int32() {
|
||||
if (m_type != thing_t::Int32) throw bad_thing_access();
|
||||
return *reinterpret_cast<std::int32_t*>(m_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an int32 value from this thing.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
const std::int32_t& int32() const;
|
||||
const std::int32_t& int32() const {
|
||||
if (m_type != thing_t::Int32) throw bad_thing_access();
|
||||
return *reinterpret_cast<std::int32_t*>(m_data);
|
||||
}
|
||||
public:
|
||||
thing_h add(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h sub(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h mul(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h div(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h mod(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h equals(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h not_equals(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h less_than(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h greater_than(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h less_equals(const context_p& context, const thing_h& rhs) const;
|
||||
thing_h greater_equals(const context_p& context, const thing_h& rhs) const;
|
||||
public:
|
||||
/**
|
||||
* @brief Increments reference count of this thing by one.
|
||||
*/
|
||||
void add_reference() { ++m_refCount; }
|
||||
thing add(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() + rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrements reference count of this thing by one.
|
||||
*/
|
||||
void remove_reference() { --m_refCount; }
|
||||
thing sub(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() - rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns reference count of this thing.
|
||||
*
|
||||
* @return The reference count.
|
||||
*/
|
||||
constexpr nref_t reference_count() const { return m_refCount; }
|
||||
thing mul(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() * rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing div(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() / rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing mod(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() % rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing equals(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() == rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing not_equals(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() != rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing less_than(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() < rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing greater_than(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() > rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing less_equals(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() <= rhs.int32();
|
||||
return res;
|
||||
}
|
||||
|
||||
thing greater_equals(const thing& rhs) const {
|
||||
thing res(m_type, m_allocator);
|
||||
res.int32() = int32() >= rhs.int32();
|
||||
return res;
|
||||
}
|
||||
private:
|
||||
thing_t m_type;
|
||||
bool m_ownData = true;
|
||||
nref_t m_refCount = 0;
|
||||
void* m_data = nullptr;
|
||||
thing_t m_type{};
|
||||
std::byte* m_data = nullptr;
|
||||
|
||||
allocator_type m_allocator;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class thing_allocator {
|
||||
template <typename>
|
||||
friend class thing_allocator;
|
||||
public:
|
||||
using value_type = T;
|
||||
public:
|
||||
explicit thing_allocator(furlang::arena& arena) noexcept
|
||||
: m_arena(&arena) {}
|
||||
|
||||
template <typename U>
|
||||
thing_allocator(const thing_allocator<U>& other) noexcept
|
||||
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
|
||||
|
||||
template <typename U>
|
||||
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_arena = other.m_arena;
|
||||
m_deadThings = other.m_deadThings;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
thing_allocator(thing_allocator<U>&& other) noexcept
|
||||
: m_arena(std::move(other.m_arena)) {}
|
||||
|
||||
template <typename U>
|
||||
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_arena = std::move(other.m_arena);
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
[[nodiscard]] T* allocate(std::size_t count = 1) { return m_arena->allocate<T>(count); }
|
||||
|
||||
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings.emplace_back(ptr, count); }
|
||||
public:
|
||||
template <typename U>
|
||||
bool operator==(const thing_allocator<U>& other) const noexcept {
|
||||
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
bool operator!=(const thing_allocator<U>& other) const noexcept {
|
||||
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
|
||||
}
|
||||
private:
|
||||
furlang::arena* m_arena;
|
||||
|
||||
std::vector<std::pair<T*, std::size_t>> m_deadThings;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
|
||||
namespace furvm {
|
||||
|
||||
context::context() {}
|
||||
context::context()
|
||||
: m_thingAllocator(m_thingArena) {}
|
||||
|
||||
void context::collect() {
|
||||
for (auto& ref : m_things) {
|
||||
if (ref->reference_count() != 0) continue;
|
||||
ref = nullptr;
|
||||
}
|
||||
}
|
||||
void context::collect() {}
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
+26
-43
@@ -28,21 +28,14 @@ struct executor::frame executor::frame() const {
|
||||
return m_frames.top();
|
||||
}
|
||||
|
||||
void executor::push_thing(const thing_h& thing) {
|
||||
(*thing)->add_reference();
|
||||
m_stack.push(thing);
|
||||
}
|
||||
|
||||
void executor::push_thing(thing_h&& thing) {
|
||||
(*thing)->add_reference();
|
||||
m_stack.push(std::move(thing));
|
||||
thing_h executor::push_thing(::furvm::thing<>&& thing) {
|
||||
return m_stack.emplace(m_context->emplace_thing(std::move(thing)));
|
||||
}
|
||||
|
||||
thing_h executor::pop_thing() {
|
||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||
thing_h top = std::move(m_stack.top());
|
||||
m_stack.pop();
|
||||
(*top)->remove_reference();
|
||||
return top;
|
||||
}
|
||||
|
||||
@@ -54,20 +47,12 @@ thing_h executor::thing() const {
|
||||
void executor::store_thing(variable_t variable, const thing_h& thing) {
|
||||
auto& frame = m_frames.top();
|
||||
frame.variables.resize(variable + 1);
|
||||
if (*frame.variables[variable] != nullptr) {
|
||||
(*frame.variables[variable])->remove_reference();
|
||||
}
|
||||
frame.variables[variable] = thing;
|
||||
(*thing)->add_reference();
|
||||
}
|
||||
|
||||
void executor::store_thing(variable_t variable, thing_h&& thing) {
|
||||
auto& frame = m_frames.top();
|
||||
frame.variables.resize(variable + 1);
|
||||
if ((*frame.variables[variable]) != nullptr) {
|
||||
(*frame.variables[variable])->remove_reference();
|
||||
}
|
||||
(*thing)->add_reference();
|
||||
frame.variables[variable] = std::move(thing);
|
||||
}
|
||||
|
||||
@@ -81,13 +66,11 @@ void executor::step() {
|
||||
|
||||
struct frame& frame = m_frames.top();
|
||||
|
||||
instruction_t instr = static_cast<instruction_t>((*frame.mod)->byte(frame.position++));
|
||||
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte(frame.position++));
|
||||
switch (instr) {
|
||||
case instruction_t::NoOperation: break;
|
||||
case instruction_t::PushB2I: {
|
||||
auto thing = m_context->emplace_thing(thing_t::Int32);
|
||||
(*thing)->int32() = (*frame.mod)->byte(frame.position++);
|
||||
push_thing(std::move(thing));
|
||||
push_thing({ thing_t::Int32, m_context->m_thingAllocator })->int32() = frame.mod->byte(frame.position++);
|
||||
} break;
|
||||
case instruction_t::Drop: {
|
||||
pop_thing();
|
||||
@@ -96,81 +79,81 @@ void executor::step() {
|
||||
push_thing(thing());
|
||||
} break;
|
||||
case instruction_t::Clone: {
|
||||
push_thing(std::move(thing::clone(m_context, thing())));
|
||||
push_thing(std::move(thing()->clone()));
|
||||
} break;
|
||||
case instruction_t::Add: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->add(m_context, rhs));
|
||||
push_thing(lhs->add(*rhs));
|
||||
} break;
|
||||
case instruction_t::Sub: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->sub(m_context, rhs));
|
||||
push_thing(lhs->sub(*rhs));
|
||||
} break;
|
||||
case instruction_t::Mul: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->mul(m_context, rhs));
|
||||
push_thing(lhs->mul(*rhs));
|
||||
} break;
|
||||
case instruction_t::Div: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->div(m_context, rhs));
|
||||
push_thing(lhs->div(*rhs));
|
||||
} break;
|
||||
case instruction_t::Mod: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->mod(m_context, rhs));
|
||||
push_thing(lhs->mod(*rhs));
|
||||
} break;
|
||||
case instruction_t::Equals: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->equals(m_context, rhs));
|
||||
push_thing(lhs->equals(*rhs));
|
||||
} break;
|
||||
case instruction_t::NotEquals: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->not_equals(m_context, rhs));
|
||||
push_thing(lhs->not_equals(*rhs));
|
||||
} break;
|
||||
case instruction_t::LessThan: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->less_than(m_context, rhs));
|
||||
push_thing(lhs->less_than(*rhs));
|
||||
} break;
|
||||
case instruction_t::GreaterThan: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->greater_than(m_context, rhs));
|
||||
push_thing(lhs->greater_than(*rhs));
|
||||
} break;
|
||||
case instruction_t::LessEqual: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->less_equals(m_context, rhs));
|
||||
push_thing(lhs->less_equals(*rhs));
|
||||
} break;
|
||||
case instruction_t::GreaterEqual: {
|
||||
auto rhs = pop_thing();
|
||||
auto lhs = pop_thing();
|
||||
push_thing((*lhs)->greater_equals(m_context, rhs));
|
||||
push_thing(lhs->greater_equals(*rhs));
|
||||
} break;
|
||||
case instruction_t::Load: {
|
||||
variable_t variable = static_cast<std::uint16_t>((*frame.mod)->byte(frame.position)) |
|
||||
(static_cast<std::uint16_t>((*frame.mod)->byte(frame.position + 1)) << 8);
|
||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
|
||||
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
|
||||
frame.position += 2;
|
||||
push_thing(load_thing(variable));
|
||||
} break;
|
||||
case instruction_t::Store: {
|
||||
variable_t variable = static_cast<std::uint16_t>((*frame.mod)->byte(frame.position)) |
|
||||
(static_cast<std::uint16_t>((*frame.mod)->byte(frame.position + 1)) << 8);
|
||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
|
||||
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
|
||||
frame.position += 2;
|
||||
store_thing(variable, std::move(pop_thing()));
|
||||
} break;
|
||||
case instruction_t::Call: {
|
||||
function_id funcId = static_cast<std::uint16_t>((*frame.mod)->byte(frame.position)) |
|
||||
(static_cast<std::uint16_t>((*frame.mod)->byte(frame.position + 1)) << 8);
|
||||
function_id funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
|
||||
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
|
||||
frame.position += 2;
|
||||
|
||||
const function_h& function = (*frame.mod)->function_at(funcId);
|
||||
const function_h& function = frame.mod->function_at(funcId);
|
||||
switch (function->type()) {
|
||||
case function_t::Normal: push_frame(frame.mod, function); break;
|
||||
case function_t::Native: function->native()(*this); break;
|
||||
@@ -182,12 +165,12 @@ void executor::step() {
|
||||
}
|
||||
} break;
|
||||
case instruction_t::Jump: {
|
||||
frame.position += (*frame.mod)->byte(frame.position++);
|
||||
frame.position += frame.mod->byte(frame.position++);
|
||||
} break;
|
||||
case instruction_t::JumpNotZero: {
|
||||
byte offset = (*frame.mod)->byte(frame.position++);
|
||||
byte offset = frame.mod->byte(frame.position++);
|
||||
auto cond = pop_thing();
|
||||
if ((*cond)->int32() != 0) frame.position += offset;
|
||||
if (cond->int32() != 0) frame.position += offset;
|
||||
} break;
|
||||
case instruction_t::Return: {
|
||||
pop_frame();
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/instruction.hpp"
|
||||
#include "furvm/module.hpp"
|
||||
#include "furvm/serializer.hpp"
|
||||
#include "furvm/thing.hpp"
|
||||
|
||||
@@ -26,7 +27,7 @@ int main(void) {
|
||||
auto context = std::make_shared<furvm::context>();
|
||||
|
||||
furvm::mod_h mainModule = context->emplace_module("main", s_bytecode.begin(), s_bytecode.end());
|
||||
furvm::function_h mainFunc = (*mainModule)->emplace_function("main", 0);
|
||||
furvm::function_h mainFunc = mainModule->emplace_function("main", 0);
|
||||
|
||||
furvm::executor_h executor = context->emplace_executor(context);
|
||||
executor->push_frame(mainModule, mainFunc);
|
||||
|
||||
+24
-25
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
@@ -68,32 +67,32 @@ static inline void read_u8(std::istream& is, std::uint8_t& value) {
|
||||
}
|
||||
|
||||
bool serializer::serialize_module(std::ostream& os, const mod_p& mod) {
|
||||
os.write(reinterpret_cast<const char*>(MODULE_MAGIC), sizeof(MODULE_MAGIC));
|
||||
write_u32(os, VERSION);
|
||||
// os.write(reinterpret_cast<const char*>(MODULE_MAGIC), sizeof(MODULE_MAGIC));
|
||||
// write_u32(os, VERSION);
|
||||
|
||||
write_u32(os, mod->m_functions.size());
|
||||
for (function_id id = 0; id < static_cast<function_id>(mod->m_functions.size()); ++id) {
|
||||
const auto& func = mod->function_at(id);
|
||||
write_u16(os, id);
|
||||
write_u8(os, static_cast<std::uint8_t>(func->type()));
|
||||
switch (func->type()) {
|
||||
case function_t::Normal: {
|
||||
write_u64(os, func->position());
|
||||
} break;
|
||||
case function_t::Native: {
|
||||
// TODO: Replace with a reference to the function's name from constant pool
|
||||
throw std::runtime_error("cannot serialize native functions yet");
|
||||
} break;
|
||||
case function_t::Import: {
|
||||
// TODO: Replace with a reference to the module's and function's name from constant pool
|
||||
throw std::runtime_error("cannot serialize import functions yet");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
// write_u32(os, mod->m_functions.size());
|
||||
// for (function_id id = 0; id < static_cast<function_id>(mod->m_functions.size()); ++id) {
|
||||
// const auto& func = mod->function_at(id);
|
||||
// write_u16(os, id);
|
||||
// write_u8(os, static_cast<std::uint8_t>(func->type()));
|
||||
// switch (func->type()) {
|
||||
// case function_t::Normal: {
|
||||
// write_u64(os, func->position());
|
||||
// } break;
|
||||
// case function_t::Native: {
|
||||
// // TODO: Replace with a reference to the function's name from constant pool
|
||||
// throw std::runtime_error("cannot serialize native functions yet");
|
||||
// } break;
|
||||
// case function_t::Import: {
|
||||
// // TODO: Replace with a reference to the module's and function's name from constant pool
|
||||
// throw std::runtime_error("cannot serialize import functions yet");
|
||||
// } break;
|
||||
// }
|
||||
// }
|
||||
|
||||
write_u64(os, mod->m_bytecode.size());
|
||||
os.write(reinterpret_cast<const char*>(mod->m_bytecode.data()),
|
||||
static_cast<std::streamsize>(mod->m_bytecode.size()));
|
||||
// write_u64(os, mod->m_bytecode.size());
|
||||
// os.write(reinterpret_cast<const char*>(mod->m_bytecode.data()),
|
||||
// static_cast<std::streamsize>(mod->m_bytecode.size()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
// NOLINTBEGIN(cppcoreguidelines-no-malloc)
|
||||
|
||||
#include "furvm/thing.hpp"
|
||||
|
||||
#include "furvm/context.hpp" // IWYU pragma: keep
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
std::size_t thing_type_size(thing_t type) {
|
||||
switch (type) {
|
||||
case thing_t::Int32: return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
thing::thing(thing_t type)
|
||||
: m_type(type) {
|
||||
std::size_t size = thing_type_size(type);
|
||||
byte* data = new byte[size];
|
||||
if (data == nullptr) throw std::runtime_error("failed to allocate data for new thing");
|
||||
std::memcpy(data, &type, sizeof(type));
|
||||
m_data = data + sizeof(type);
|
||||
|
||||
switch (m_type) {
|
||||
// Primitives are zero-initialized.
|
||||
default:
|
||||
case thing_t::Int32: std::memset(m_data, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
thing::thing(thing_t type, void* data)
|
||||
: m_type(type), m_ownData(false) {
|
||||
std::size_t size = thing_type_size(type);
|
||||
if (data == nullptr) throw std::runtime_error("failed to allocate data for new thing");
|
||||
std::memcpy(data, &type, sizeof(type));
|
||||
m_data = static_cast<char*>(data) + sizeof(type);
|
||||
|
||||
switch (m_type) {
|
||||
// Primitives are zero-initialized.
|
||||
default:
|
||||
case thing_t::Int32: std::memset(m_data, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
thing::~thing() {
|
||||
switch (m_type) {
|
||||
// Primitives are not destructed.
|
||||
default:
|
||||
case thing_t::Int32: break;
|
||||
}
|
||||
|
||||
if (m_ownData) delete[] static_cast<char*>(m_data);
|
||||
}
|
||||
|
||||
thing::thing(thing&& other) noexcept
|
||||
: m_type(other.m_type), m_refCount(other.m_refCount), m_data(other.m_data) {
|
||||
other.m_type = {};
|
||||
other.m_ownData = false;
|
||||
other.m_refCount = {};
|
||||
other.m_data = nullptr;
|
||||
}
|
||||
|
||||
thing& thing::operator=(thing&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_type = other.m_type;
|
||||
m_refCount = other.m_refCount;
|
||||
m_data = other.m_data;
|
||||
|
||||
other.m_type = {};
|
||||
other.m_refCount = {};
|
||||
other.m_data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static constexpr std::uint16_t thing_type_pair(thing_t lhs, thing_t rhs) {
|
||||
return (static_cast<std::uint16_t>(lhs) << 8) | static_cast<std::uint16_t>(rhs);
|
||||
}
|
||||
|
||||
thing_h thing::clone(const context_p& context, const thing_h& thing) {
|
||||
auto th = context->emplace_thing((*thing)->m_type);
|
||||
switch ((*thing)->m_type) {
|
||||
// Primitives
|
||||
default: {
|
||||
memcpy((*th)->m_data, (*thing)->m_data, thing_type_size((*thing)->m_type));
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(th);
|
||||
}
|
||||
|
||||
std::int32_t& thing::int32() {
|
||||
if (m_type != thing_t::Int32) throw bad_thing_access();
|
||||
return *static_cast<std::int32_t*>(m_data);
|
||||
}
|
||||
|
||||
const std::int32_t& thing::int32() const {
|
||||
if (m_type != thing_t::Int32) throw bad_thing_access();
|
||||
return *static_cast<std::int32_t*>(m_data);
|
||||
}
|
||||
|
||||
thing_h thing::add(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = int32() + (*rhs)->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::sub(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = int32() - (*rhs)->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::mul(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = int32() * (*rhs)->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::div(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = int32() / (*rhs)->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::mod(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = int32() % (*rhs)->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::equals(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = static_cast<std::int32_t>(int32() == (*rhs)->int32());
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::not_equals(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = static_cast<std::int32_t>(int32() != (*rhs)->int32());
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::less_than(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = static_cast<std::int32_t>(int32() < (*rhs)->int32());
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::greater_than(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = static_cast<std::int32_t>(int32() > (*rhs)->int32());
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::less_equals(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = static_cast<std::int32_t>(int32() <= (*rhs)->int32());
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
thing_h thing::greater_equals(const context_p& context, const thing_h& rhs) const {
|
||||
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
|
||||
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||
thing_h res = context->emplace_thing(thing_t::Int32);
|
||||
(*res)->int32() = static_cast<std::int32_t>(int32() >= (*rhs)->int32());
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-no-malloc)
|
||||
Reference in New Issue
Block a user