chore: flat out the file structure
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
#ifndef FURVM_CONSTANT_HPP
|
||||
#define FURVM_CONSTANT_HPP
|
||||
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
// TODO: Array constants
|
||||
struct constant {
|
||||
enum type_e {
|
||||
S32 = 0,
|
||||
U32,
|
||||
S64,
|
||||
U64,
|
||||
String,
|
||||
} type = S32;
|
||||
union {
|
||||
std::int32_t s32;
|
||||
std::uint32_t u32;
|
||||
std::int64_t s64;
|
||||
std::uint64_t u64;
|
||||
std::string_view string;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_CONSTANT_HPP
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef FURVM_CONTEXT_HPP
|
||||
#define FURVM_CONTEXT_HPP
|
||||
|
||||
#include "furvm/executor.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp"
|
||||
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
class context : public handle_container<mod_h> {
|
||||
public:
|
||||
friend class executor;
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a context.
|
||||
*/
|
||||
context() {}
|
||||
|
||||
~context() = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
context(context&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
context& operator=(context&&) noexcept = default;
|
||||
|
||||
context(const context&) = delete;
|
||||
context& operator=(const context&) = delete;
|
||||
public:
|
||||
template <typename... Args>
|
||||
auto& allocate_executor() {
|
||||
executor executor(this);
|
||||
return m_executors.emplace_back(std::move(executor));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an executor from the context.
|
||||
*
|
||||
* @param args Id of the executor.
|
||||
* @return A handle to the executor.
|
||||
*/
|
||||
template <typename... Args>
|
||||
auto& executor_at(Args&&... args) {
|
||||
return m_executors.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an executor from the context.
|
||||
*
|
||||
* @param args Id of the executor.
|
||||
* @return A handle to the executor.
|
||||
*/
|
||||
template <typename... Args>
|
||||
const auto& executor_at(Args&&... args) const {
|
||||
return m_executors.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
const std::vector<executor>& executors() const { return m_executors; }
|
||||
public:
|
||||
thing_type_store& tt_store() { return m_thingTypeStore; }
|
||||
public:
|
||||
template <typename... Args>
|
||||
thing<> allocate_thing(Args&&... args) {
|
||||
thing<> thing = { std::forward<Args>(args)... };
|
||||
m_heap.push_back(thing.raw());
|
||||
return std::move(thing);
|
||||
}
|
||||
private:
|
||||
handle_container<mod_h> m_modules;
|
||||
std::vector<executor> m_executors;
|
||||
|
||||
class thing_type_store m_thingTypeStore;
|
||||
|
||||
// A list of things on the heap
|
||||
std::vector<std::byte*> m_heap;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_CONTEXT_HPP
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef FURVM_DETAIL_HANDLE_HPP
|
||||
#define FURVM_DETAIL_HANDLE_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace furvm {
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* @brief Default specialization for header_has_refcount type trait.
|
||||
*/
|
||||
template <typename Header, typename = void>
|
||||
struct header_has_refcount : std::false_type {};
|
||||
|
||||
/**
|
||||
* @brief Specialization for header_has_refcount type trait.
|
||||
*/
|
||||
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 {};
|
||||
|
||||
/**
|
||||
* @brief An alias for header_has_refcount's value.
|
||||
*/
|
||||
template <typename Header>
|
||||
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
|
||||
|
||||
template <typename Handle, typename IdHash = std::hash<typename Handle::id_type>>
|
||||
struct handle_hash {
|
||||
std::size_t operator()(const Handle& handle) const { return IdHash{}(handle.id()); }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_DETAIL_HANDLE_HPP
|
||||
@@ -0,0 +1,176 @@
|
||||
#ifndef FURVM_DETAIL_SERIALIZATION_HPP
|
||||
#define FURVM_DETAIL_SERIALIZATION_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace furvm {
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::int8_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::int16_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::int32_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::int64_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes an integer.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value Integer.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, std::uint64_t value);
|
||||
|
||||
/**
|
||||
* @brief Serializes a string.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param value String.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os, const std::string& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::int8_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::int16_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::int32_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::int64_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::uint8_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::uint16_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::uint32_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes an integer.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value Integer.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::uint64_t& value);
|
||||
|
||||
/**
|
||||
* @brief Deserializes a string.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @param value String.
|
||||
* @return The input stream.
|
||||
*/
|
||||
std::istream& load(std::istream& is, std::string& value);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_DETAIL_SERIALIZATION_HPP
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef FURVM_EXCEPTIONS_HPP
|
||||
#define FURVM_EXCEPTIONS_HPP
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
class bad_thing_access : public std::exception {
|
||||
public:
|
||||
bad_thing_access() = default;
|
||||
~bad_thing_access() override = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
bad_thing_access(bad_thing_access&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
bad_thing_access& operator=(bad_thing_access&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
bad_thing_access(const bad_thing_access&) = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
bad_thing_access& operator=(const bad_thing_access&) = default;
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a C-style string describing the cause of the error.
|
||||
*
|
||||
* @return The cause of the error.
|
||||
*/
|
||||
const char* what() const noexcept override { return "bad thing access"; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Bad constant access exception.
|
||||
*/
|
||||
class bad_constant_access : public std::exception {
|
||||
public:
|
||||
bad_constant_access() = default;
|
||||
~bad_constant_access() override = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
bad_constant_access(bad_constant_access&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
bad_constant_access& operator=(bad_constant_access&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
bad_constant_access(const bad_constant_access&) = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
bad_constant_access& operator=(const bad_constant_access&) = default;
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a C-style string describing the cause of the error.
|
||||
*
|
||||
* @return The cause of the error.
|
||||
*/
|
||||
const char* what() const noexcept override { return "bad constant access"; }
|
||||
};
|
||||
|
||||
class stack_underflow : public std::exception {
|
||||
public:
|
||||
stack_underflow() = default;
|
||||
~stack_underflow() override = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
stack_underflow(stack_underflow&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
stack_underflow& operator=(stack_underflow&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
stack_underflow(const stack_underflow&) = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
stack_underflow& operator=(const stack_underflow&) = default;
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a C-style string describing the cause of the error.
|
||||
*
|
||||
* @return The cause of the error.
|
||||
*/
|
||||
const char* what() const noexcept override { return "stack underflow"; }
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_EXCEPTIONS_HPP
|
||||
@@ -0,0 +1,208 @@
|
||||
#ifndef FURVM_EXECUTOR_HPP
|
||||
#define FURVM_EXECUTOR_HPP
|
||||
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||
#include "furvm/stack.hpp"
|
||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
enum class executor_flags : std::uint32_t {
|
||||
Suspended = (1 << 0), /**< Execution suspended. */
|
||||
Done = (1 << 1), /**< Execution is finished. */
|
||||
|
||||
JustHit = (1 << 16), /**< Executor just hit a breakpoint. */
|
||||
};
|
||||
|
||||
static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) {
|
||||
return executor_flags(static_cast<std::uint32_t>(lhs) | static_cast<std::uint32_t>(rhs));
|
||||
}
|
||||
|
||||
static inline executor_flags operator&(executor_flags lhs, executor_flags rhs) {
|
||||
return executor_flags(static_cast<std::uint32_t>(lhs) & static_cast<std::uint32_t>(rhs));
|
||||
}
|
||||
|
||||
static inline executor_flags operator~(executor_flags flags) {
|
||||
return executor_flags(~static_cast<std::uint32_t>(flags));
|
||||
}
|
||||
|
||||
class executor {
|
||||
friend class context;
|
||||
private:
|
||||
executor(context* context)
|
||||
: m_context(context) {}
|
||||
public:
|
||||
static constexpr executor_flags STATE_FLAGS = executor_flags::JustHit;
|
||||
|
||||
using new_frame_callback = std::function<void(executor&)>;
|
||||
|
||||
using stack_thing = thing<stack_allocator>;
|
||||
public:
|
||||
/**
|
||||
* @brief Executor frame.
|
||||
*
|
||||
* Call frame.
|
||||
*/
|
||||
struct frame {
|
||||
mod_h mod; /**< Handle to the frame's module. */
|
||||
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
|
||||
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
|
||||
|
||||
thing_type* returnType; /**< Return type. */
|
||||
std::vector<stack_thing> variables; /**< Frame variables. */
|
||||
};
|
||||
public:
|
||||
~executor() = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
executor(executor&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
executor& operator=(executor&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
executor(const executor&) = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
executor& operator=(const executor&) = default;
|
||||
public:
|
||||
template <typename CallbackFwd>
|
||||
void set_new_frame_callback(CallbackFwd&& callback) {
|
||||
m_newFrameCb = std::forward<CallbackFwd>(callback);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns flags of this executor.
|
||||
*
|
||||
* @return The flags.
|
||||
*/
|
||||
executor_flags flags() const { return m_flags; }
|
||||
|
||||
bool done() const { return (m_flags & executor_flags::Done) == executor_flags::Done; }
|
||||
|
||||
bool suspended() const { return (m_flags & executor_flags::Suspended) == executor_flags::Suspended; }
|
||||
|
||||
void unsuspend() { m_flags = m_flags & ~executor_flags::Suspended; }
|
||||
|
||||
void clear_flags() {
|
||||
m_flags = m_flags & STATE_FLAGS;
|
||||
m_flags = m_frames.empty() ? executor_flags::Done : furvm::executor_flags{ 0 };
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Pushes a new frame.
|
||||
*
|
||||
* @param mod Handle to the frame's module.
|
||||
* @param function Frame's function.
|
||||
*/
|
||||
void push_frame(const mod_h& mod, function function);
|
||||
|
||||
/**
|
||||
* @brief Pops the top frame.
|
||||
*
|
||||
* @return The popped frame.
|
||||
*/
|
||||
frame pop_frame();
|
||||
|
||||
/**
|
||||
* @brief Returns the top frame.
|
||||
*
|
||||
* @return The frame.
|
||||
*/
|
||||
frame top_frame() const;
|
||||
|
||||
const std::stack<frame>& frames() const { return m_frames; }
|
||||
public:
|
||||
/**
|
||||
* @brief Pushes a thing onto the stack.
|
||||
*
|
||||
* Registers a new thing and pushes its handle onto the stack.
|
||||
*
|
||||
* @param thing Thing.
|
||||
* @return The pushed handle.
|
||||
*/
|
||||
stack_thing& push_thing(stack_thing&& thing);
|
||||
|
||||
stack_thing& push_thing(const stack_thing& thing);
|
||||
|
||||
/**
|
||||
* @brief Pops a thing from the stack.
|
||||
*
|
||||
* @return A handle to the popped thing.
|
||||
*/
|
||||
stack_thing pop_thing();
|
||||
|
||||
/**
|
||||
* @brief Returns the top thing on the stack.
|
||||
*
|
||||
* @return A handle to the top thing.
|
||||
*/
|
||||
stack_thing& top_thing();
|
||||
|
||||
const stack_thing& top_thing() const;
|
||||
|
||||
const std::vector<stack_thing>& stack() const { return m_stack; }
|
||||
public:
|
||||
/**
|
||||
* @brief Stores a thing in a frame variable.
|
||||
*
|
||||
* @param variable Id of the variable in which the handle will be put.
|
||||
* @param thing Thing handle.
|
||||
*/
|
||||
void store_thing(variable_t variable, const stack_thing& thing);
|
||||
|
||||
/**
|
||||
* @brief Stores a thing in a frame variable.
|
||||
*
|
||||
* @param variable Id of the variable in which the handle will be put.
|
||||
* @param thing Thing handle.
|
||||
*/
|
||||
void store_thing(variable_t variable, stack_thing&& thing);
|
||||
|
||||
/**
|
||||
* @brief Returns a thing stored in a variable.
|
||||
*
|
||||
* @param variable Id of the variable from which the handle will be fetched.
|
||||
* @return A handle stored in the variable.
|
||||
*/
|
||||
stack_thing& load_thing(variable_t variable);
|
||||
|
||||
const stack_thing& load_thing(variable_t variable) const;
|
||||
public:
|
||||
/**
|
||||
* @brief Executes next instruction.
|
||||
*/
|
||||
void step();
|
||||
private:
|
||||
thing_type thing_type_impl(mod_h mod, mod_type type) const;
|
||||
|
||||
thing_type* mod_to_thing_type(const mod_h& mod, const mod_type& type) const;
|
||||
private:
|
||||
static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs);
|
||||
private:
|
||||
executor_flags m_flags = executor_flags::Done;
|
||||
context* m_context;
|
||||
furvm::stack<std::byte> m_stackStorage;
|
||||
|
||||
std::stack<frame> m_frames;
|
||||
std::vector<stack_thing> m_stack;
|
||||
|
||||
new_frame_callback m_newFrameCb = nullptr;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_EXECUTOR_HPP
|
||||
@@ -0,0 +1,201 @@
|
||||
#ifndef FURVM_FUNCTION_HPP
|
||||
#define FURVM_FUNCTION_HPP
|
||||
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp" // IWYU pragma: keep
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
enum class function_t : std::uint8_t {
|
||||
Normal = 0, /**< A normal bytecode function. */
|
||||
Native, /**< A native function implemented through furvm API. */
|
||||
Import, /**< A function imported from another module. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A native function.
|
||||
*/
|
||||
using native_function = std::string;
|
||||
|
||||
/**
|
||||
* @brief A function import.
|
||||
*/
|
||||
struct import_function {
|
||||
mod_id mod;
|
||||
function_id function;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function signature.
|
||||
*/
|
||||
struct function_sig {
|
||||
std::vector<mod_type_h> params;
|
||||
std::optional<mod_type_h> returnType;
|
||||
|
||||
bool operator==(const function_sig& rhs) const { return params == rhs.params; }
|
||||
|
||||
bool operator!=(const function_sig& rhs) const { return !this->operator==(rhs); }
|
||||
};
|
||||
|
||||
class function {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a normal function.
|
||||
*
|
||||
* @param signature Function's signature.
|
||||
* @param position Offset in bytecode of the function.
|
||||
*/
|
||||
template <typename SigFwd, typename = std::enable_if_t<std::is_constructible_v<function_sig, SigFwd>>>
|
||||
function(SigFwd&& signature, bytecode_pos position)
|
||||
: m_type(function_t::Normal), m_signature(std::forward<SigFwd>(signature)), m_value(position) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a native function.
|
||||
*
|
||||
* @param signature Function's signature.
|
||||
* @param native Native function tag.
|
||||
*/
|
||||
template <typename SigFwd,
|
||||
typename Native,
|
||||
typename = std::enable_if_t<std::is_constructible_v<native_function, Native> &&
|
||||
std::is_constructible_v<function_sig, SigFwd>>>
|
||||
function(SigFwd&& signature, Native&& native)
|
||||
: m_type(function_t::Native),
|
||||
m_signature(std::forward<SigFwd>(signature)),
|
||||
m_value(std::forward<Native>(native)) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an import function.
|
||||
*
|
||||
* @param mod Module's id.
|
||||
* @param function Function's id.
|
||||
*/
|
||||
template <typename ModFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModFwd>>>
|
||||
function(ModFwd&& mod, function_id function)
|
||||
: m_type(function_t::Import), m_signature(), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an import function.
|
||||
*
|
||||
* @param mod Module.
|
||||
* @param function Function.
|
||||
*/
|
||||
function(const mod_h& mod, const function_h& function);
|
||||
|
||||
/**
|
||||
* @brief Destructs a function.
|
||||
*/
|
||||
~function();
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
function(function&&) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
function& operator=(function&&) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
function(const function&);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
function& operator=(const function&);
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a type of this function.
|
||||
*
|
||||
* @return The type.
|
||||
*/
|
||||
constexpr function_t type() const { return m_type; }
|
||||
|
||||
/**
|
||||
* @brief Returns this function's signature.
|
||||
*
|
||||
* @return The signature.
|
||||
*/
|
||||
function_sig signature() const { return m_signature; }
|
||||
public:
|
||||
/**
|
||||
* @brief Returns normal function's value.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
std::size_t position() const {
|
||||
if (m_type != function_t::Normal) throw std::runtime_error("function type mismatch");
|
||||
return m_value.position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns native function's value.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
const native_function& native() const {
|
||||
if (m_type != function_t::Native) throw std::runtime_error("function type mismatch");
|
||||
return m_value.native;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns import function's value.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
const import_function& imp() const {
|
||||
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
|
||||
return m_value.imp;
|
||||
}
|
||||
private:
|
||||
function_t m_type;
|
||||
function_sig m_signature;
|
||||
|
||||
union value {
|
||||
std::size_t position = 0;
|
||||
native_function native;
|
||||
import_function imp;
|
||||
|
||||
value() = default;
|
||||
|
||||
value(std::size_t position)
|
||||
: position(position) {}
|
||||
|
||||
template <typename Native, typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>>
|
||||
value(Native&& native)
|
||||
: native(std::forward<Native>(native)) {}
|
||||
|
||||
value(const import_function& imp)
|
||||
: imp(imp) {}
|
||||
|
||||
~value() {}
|
||||
|
||||
value(value&& other) = delete;
|
||||
value& operator=(value&& other) = delete;
|
||||
value(const value& other) = delete;
|
||||
value& operator=(const value& other) = delete;
|
||||
} m_value;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct function_sig_hash {
|
||||
std::size_t operator()(const function_sig& signature) const;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_FUNCTION_HPP
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef FURVM_HPP
|
||||
#define FURVM_HPP
|
||||
|
||||
#include "furvm/context.hpp" // IWYU pragma: export
|
||||
#include "furvm/executor.hpp" // IWYU pragma: export
|
||||
#include "furvm/function.hpp" // IWYU pragma: export
|
||||
#include "furvm/fwd.hpp" // IWYU pragma: export
|
||||
#include "furvm/handle.hpp" // IWYU pragma: export
|
||||
#include "furvm/instruction.hpp" // IWYU pragma: export
|
||||
#include "furvm/thing.hpp" // IWYU pragma: export
|
||||
|
||||
#endif // FURVM_HPP
|
||||
@@ -0,0 +1,218 @@
|
||||
#ifndef FURVM_FWD_HPP
|
||||
#define FURVM_FWD_HPP
|
||||
|
||||
#include <cstddef> // IWYU pragma: export
|
||||
#include <cstdint> // IWYU pragma: export
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Furlang's virtual machine.
|
||||
*/
|
||||
namespace furvm {
|
||||
|
||||
/**
|
||||
* @brief A byte.
|
||||
*
|
||||
* There's nothing more to it.
|
||||
*/
|
||||
using byte = std::uint8_t;
|
||||
|
||||
/**
|
||||
* @brief An offset into bytecode.
|
||||
*/
|
||||
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 Generic furvm object handle.
|
||||
*
|
||||
* @tparam Value Type of the handle's value.
|
||||
* @tparam Header Type of the handle's header.
|
||||
*/
|
||||
template <typename Value, typename Header>
|
||||
class handle;
|
||||
|
||||
/**
|
||||
* @brief Container for the handles.
|
||||
*
|
||||
* @tparam Handle Type of the container's handle.
|
||||
*/
|
||||
template <typename Handle, typename = void>
|
||||
class handle_container;
|
||||
|
||||
// constant.hpp
|
||||
|
||||
/**
|
||||
* @brief Constant index.
|
||||
*
|
||||
* An index to the constant in module's constant pool.
|
||||
*/
|
||||
using constant_index = std::uint16_t;
|
||||
|
||||
/**
|
||||
* @enum constant_t
|
||||
* @brief Constant type.
|
||||
*/
|
||||
enum class constant_t : std::uint8_t;
|
||||
|
||||
/**
|
||||
* @class constant
|
||||
* @brief Constant.
|
||||
*/
|
||||
class constant;
|
||||
|
||||
// instruction.hpp
|
||||
|
||||
struct instruction_argument;
|
||||
|
||||
/**
|
||||
* @struct instruction
|
||||
* @brief Furvm's instruction.
|
||||
*/
|
||||
struct instruction;
|
||||
|
||||
// function.hpp
|
||||
|
||||
/**
|
||||
* @enum function_t
|
||||
* @brief Function type.
|
||||
*/
|
||||
enum class function_t : std::uint8_t;
|
||||
|
||||
/**
|
||||
* @class function
|
||||
* @brief Function.
|
||||
*
|
||||
* A furvm function.
|
||||
*/
|
||||
class function;
|
||||
|
||||
/**
|
||||
* @brief Furvm function's index.
|
||||
*/
|
||||
using function_id = std::uint16_t;
|
||||
|
||||
/**
|
||||
* @brief A handle to a furvm function.
|
||||
*/
|
||||
using function_h = handle<function, refcount_header<function_id>>;
|
||||
|
||||
// module.hpp
|
||||
|
||||
struct mod_type;
|
||||
|
||||
using mod_type_id = std::uint32_t;
|
||||
|
||||
using mod_type_h = handle<mod_type, generic_header<mod_type_id>>;
|
||||
|
||||
/**
|
||||
* @class mod
|
||||
* @brief Module.
|
||||
*
|
||||
* A furvm module. Translation unit of furlang.
|
||||
*/
|
||||
class mod;
|
||||
|
||||
/**
|
||||
* @brief An alias to a module shared pointer.
|
||||
*/
|
||||
using mod_p = std::shared_ptr<mod>;
|
||||
|
||||
/**
|
||||
* @brief An alias for a module's identifier.
|
||||
*/
|
||||
using mod_id = std::string;
|
||||
|
||||
/**
|
||||
* @brief A handle to a furvm module.
|
||||
*/
|
||||
using mod_h = handle<mod, refcount_header<mod_id>>;
|
||||
|
||||
// thing.hpp
|
||||
|
||||
/**
|
||||
* @class bad_thing_access
|
||||
* @brief Bad thing access exception.
|
||||
*/
|
||||
class bad_thing_access;
|
||||
|
||||
using thing_type_id = std::uint32_t;
|
||||
|
||||
/**
|
||||
* @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 = std::allocator>
|
||||
class thing;
|
||||
|
||||
/**
|
||||
* @brief Furvm thing's index.
|
||||
*/
|
||||
using thing_id = std::uint32_t;
|
||||
|
||||
// executor.hpp
|
||||
|
||||
/**
|
||||
* @brief A variable index type.
|
||||
*/
|
||||
using variable_t = std::uint16_t;
|
||||
|
||||
/**
|
||||
* @enum executor_flags
|
||||
* @brief Flags of an executor.
|
||||
*/
|
||||
enum class executor_flags : std::uint32_t;
|
||||
|
||||
/**
|
||||
* @class executor
|
||||
* @brief Furvm executor.
|
||||
*
|
||||
* Furvm executors are like threads.
|
||||
*/
|
||||
class executor;
|
||||
|
||||
/**
|
||||
* @brief Furvm executor's index.
|
||||
*/
|
||||
using executor_id = std::uint32_t;
|
||||
|
||||
// context.hpp
|
||||
|
||||
/**
|
||||
* @class context
|
||||
* @brief Context.
|
||||
*
|
||||
* A furvm context.
|
||||
*/
|
||||
class context;
|
||||
|
||||
/**
|
||||
* @brief An alias to a context shared pointer.
|
||||
*/
|
||||
using context_p = std::shared_ptr<context>;
|
||||
|
||||
// exceptions.hpp:
|
||||
|
||||
/**
|
||||
* @class stack_underflow
|
||||
* @brief Stack underflow exception.
|
||||
*/
|
||||
class stack_underflow;
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_FWD_HPP
|
||||
@@ -0,0 +1,448 @@
|
||||
#ifndef FURVM_HANDLE_HPP
|
||||
#define FURVM_HANDLE_HPP
|
||||
|
||||
#include "furvm/detail/handle.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
// TODO: Implement generational indexes
|
||||
|
||||
template <typename Id>
|
||||
class refcount_header {
|
||||
public:
|
||||
using id_type = Id; /**< Id type. */
|
||||
using refcount_type = std::uint32_t; /**< Reference count type. */
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a reference counting header.
|
||||
*
|
||||
* @param id Identifier of the handle's value.
|
||||
* @param refCount Handle's reference count.
|
||||
* @param onRelease Callback function.
|
||||
*/
|
||||
template <typename IdFwd, typename Func>
|
||||
refcount_header(IdFwd&& id, refcount_type refCount, Func&& onRelease)
|
||||
: m_id(std::forward<IdFwd>(id)), m_refCount(refCount), m_onRelease(std::forward<Func>(onRelease)) {}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the header's reference count.
|
||||
*
|
||||
* @return The reference count.
|
||||
*/
|
||||
refcount_type reference_count() const { return m_refCount; }
|
||||
|
||||
/**
|
||||
* @brief Increments the header's reference count.
|
||||
*/
|
||||
void acquire() { ++m_refCount; }
|
||||
|
||||
/**
|
||||
* @brief Decrements the header's reference count.
|
||||
*
|
||||
* If the reference count reaches 0, the onRelease callback passed in the constructor will be called.
|
||||
*/
|
||||
void release() {
|
||||
--m_refCount;
|
||||
if (m_refCount == 0) m_onRelease(m_id);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the header's identifier.
|
||||
*
|
||||
* @return The identifier.
|
||||
*/
|
||||
id_type id() const { return m_id; }
|
||||
private:
|
||||
id_type m_id;
|
||||
std::atomic<refcount_type> m_refCount;
|
||||
std::function<void(const id_type&)> m_onRelease;
|
||||
};
|
||||
|
||||
template <typename Id>
|
||||
class generic_header {
|
||||
public:
|
||||
using id_type = Id; /**< Id type. */
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a generic header.
|
||||
*
|
||||
* @param id Identifier of the handle.
|
||||
*/
|
||||
generic_header(id_type id)
|
||||
: m_id(id) {}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the header's identifier.
|
||||
*/
|
||||
id_type id() const { return m_id; }
|
||||
private:
|
||||
id_type m_id;
|
||||
};
|
||||
|
||||
template <typename Value, typename Header = refcount_header<std::uint32_t>>
|
||||
class handle {
|
||||
public:
|
||||
using value_type = Value; /** Value type. */
|
||||
using reference = Value&; /** Reference type. */
|
||||
using const_reference = const Value&; /** Constant reference type. */
|
||||
using pointer = Value*; /** Pointer type. */
|
||||
using const_pointer = const Value*; /** Constant pointer type. */
|
||||
public:
|
||||
using id_type = typename Header::id_type; /** Id type of the header. */
|
||||
|
||||
using header_type = Header;
|
||||
public:
|
||||
using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */
|
||||
public:
|
||||
handle() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a handle.
|
||||
*
|
||||
* @param value A pointer to the header-value pair.
|
||||
*/
|
||||
handle(pair_type* value)
|
||||
: m_value(value) {
|
||||
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||
m_value->first.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructs a handle.
|
||||
*/
|
||||
~handle() {
|
||||
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||
if (m_value != nullptr) m_value->first.release();
|
||||
}
|
||||
m_value = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
handle(handle&& other) noexcept
|
||||
: m_value(other.m_value) {
|
||||
other.m_value = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
handle& operator=(handle&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
m_value = other.m_value;
|
||||
other.m_value = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
handle(const handle& other)
|
||||
: m_value(other.m_value) {
|
||||
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||
m_value->first.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
handle& operator=(const handle& other) {
|
||||
if (this == &other) return *this;
|
||||
m_value = other.m_value;
|
||||
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||
m_value->first.acquire();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns an identifier of the handle's header.
|
||||
*
|
||||
* @return The header's identifier.
|
||||
*/
|
||||
id_type id() const { return m_value->first.id(); }
|
||||
|
||||
/**
|
||||
* @brief Returns whether the handle is empty.
|
||||
*
|
||||
* @return true if the handle is empty.
|
||||
*/
|
||||
bool empty() const { return m_value == nullptr; }
|
||||
|
||||
/**
|
||||
* @brief Returns the handle's header reference count.
|
||||
*
|
||||
* @return The reference count.
|
||||
*/
|
||||
template <typename U = Header, typename = std::enable_if_t<detail::header_has_refcount_v<U>>>
|
||||
auto reference_count() const {
|
||||
return m_value->first.reference_count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the handle's value.
|
||||
*
|
||||
* @return The value pointer.
|
||||
*/
|
||||
pointer operator->() { return &m_value->second; }
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the handle's value.
|
||||
*
|
||||
* @return The value pointer.
|
||||
*/
|
||||
const_pointer operator->() const { return &m_value->second; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the handle's value.
|
||||
*
|
||||
* @return The value reference.
|
||||
*/
|
||||
reference operator*() { return m_value->second; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the handle's value.
|
||||
*
|
||||
* @return The value reference.
|
||||
*/
|
||||
const_reference operator*() const { return m_value->second; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the handle's value.
|
||||
*
|
||||
* @return The value reference.
|
||||
*/
|
||||
reference value() { return m_value->second; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the handle's value.
|
||||
*
|
||||
* @return The value reference.
|
||||
*/
|
||||
const_reference value() const { return m_value->second; }
|
||||
public:
|
||||
/**
|
||||
* @brief Invalidates the handle without releasing.
|
||||
*/
|
||||
void dispatch() { m_value = nullptr; }
|
||||
public:
|
||||
bool operator==(const handle& rhs) const { return m_value == rhs.m_value; }
|
||||
|
||||
bool operator!=(const handle& rhs) const { return !this->operator==(rhs); }
|
||||
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; /**< Handle's pair type. */
|
||||
public:
|
||||
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>; /**< Handle type. */
|
||||
using const_value = std::add_const_t<value_type>; /**< Constant handle type. */
|
||||
|
||||
using id_type = typename Handle::id_type; /**< Handle's header identifier 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:
|
||||
/**
|
||||
* @brief Emplaces a new value.
|
||||
*
|
||||
* @param id Identifier of the emplaced value.
|
||||
* @param args Arguments passed to the Handle's value type constructor.
|
||||
* @return A handle to the emplaced value.
|
||||
*/
|
||||
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, [&](const id_type& id) { erase(id); }),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
m_pairs.emplace(std::move(idFwd), pair);
|
||||
return { pair };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to the container's value.
|
||||
*
|
||||
* @param id Idenfifier of the value.
|
||||
* @return The value.
|
||||
*/
|
||||
template <typename IdFwd>
|
||||
value_type at(IdFwd&& id) {
|
||||
return { m_pairs.at(std::forward<IdFwd>(id)) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to the container's value.
|
||||
*
|
||||
* @param id Idenfifier of the value.
|
||||
* @return The value.
|
||||
*/
|
||||
template <typename IdFwd>
|
||||
const_value at(IdFwd&& id) const {
|
||||
return { m_pairs.at(std::forward<IdFwd>(id)) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases a value from the container.
|
||||
*
|
||||
* @param id Identifier of the value.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a handle exists inside.
|
||||
*
|
||||
* @param id Identifier of the handle.
|
||||
* @return true if the handle exists insdie of this container.
|
||||
*/
|
||||
template <typename IdFwd>
|
||||
constexpr bool contains(IdFwd&& id) const {
|
||||
return m_pairs.find(std::forward<IdFwd>(id)) != m_pairs.end();
|
||||
}
|
||||
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; /**< Handle's pair type. */
|
||||
public:
|
||||
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>; /**< Handle type. */
|
||||
using const_value = std::add_const_t<value_type>; /**< Constant handle type. */
|
||||
|
||||
using id_type = typename Handle::id_type; /**< Handle's header identifier 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:
|
||||
/**
|
||||
* @brief Emplaces a new value.
|
||||
*
|
||||
* @param id Identifier of the emplaced value.
|
||||
* @param args Arguments passed to the Handle's value type constructor.
|
||||
* @return A handle to the emplaced value.
|
||||
*/
|
||||
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 = nullptr;
|
||||
if constexpr (detail::header_has_refcount_v<typename Handle::header_type>) {
|
||||
newPair = new pair_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
} else {
|
||||
newPair = new pair_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(id),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
m_pairs[id] = newPair;
|
||||
return { newPair };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emplaces a new value.
|
||||
*
|
||||
* Emplaces a new value with an automatically-assigned identifier.
|
||||
*
|
||||
* @param args Arguments passed to the Handle's value type constructor.
|
||||
* @return A handle to the emplaced value.
|
||||
*/
|
||||
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)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a value.
|
||||
*
|
||||
* @param id Identifier of the value.
|
||||
* @return The value.
|
||||
*/
|
||||
value_type at(id_type id) { return { m_pairs.at(id) }; }
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a value.
|
||||
*
|
||||
* @param id Identifier of the value.
|
||||
* @return The value.
|
||||
*/
|
||||
const_value at(id_type id) const { return { m_pairs.at(id) }; }
|
||||
|
||||
/**
|
||||
* @brief Erases a value from the container.
|
||||
*
|
||||
* @param id Identifier of the value.
|
||||
*/
|
||||
void erase(id_type id) {
|
||||
if (id >= m_pairs.size()) return;
|
||||
delete m_pairs[id];
|
||||
m_pairs[id] = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a handle exists inside.
|
||||
*
|
||||
* @param id Identifier of the handle.
|
||||
* @return true if the handle exists insdie of this container.
|
||||
*/
|
||||
constexpr bool contains(id_type id) const { return id < m_pairs.size() && m_pairs[id] != nullptr; }
|
||||
public:
|
||||
auto begin() { return m_pairs.begin(); }
|
||||
auto begin() const { return m_pairs.begin(); }
|
||||
auto cbegin() const { return m_pairs.cbegin(); }
|
||||
|
||||
auto end() { return m_pairs.end(); }
|
||||
auto end() const { return m_pairs.end(); }
|
||||
auto cend() const { return m_pairs.cend(); }
|
||||
private:
|
||||
std::vector<pair_type*> m_pairs;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_HANDLE_HPP
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifndef FURVM_INSTRUCTION_HPP
|
||||
#define FURVM_INSTRUCTION_HPP
|
||||
|
||||
#include "furlang/view.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
struct instruction_argument {
|
||||
enum type_e {
|
||||
None = 0,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
Constant,
|
||||
Type,
|
||||
Variable,
|
||||
GlobalVariable,
|
||||
Function,
|
||||
Offset,
|
||||
|
||||
Count,
|
||||
} type;
|
||||
union {
|
||||
std::int8_t s8;
|
||||
std::uint8_t u8;
|
||||
std::int16_t s16;
|
||||
std::uint16_t u16;
|
||||
std::int32_t s32;
|
||||
std::uint32_t u32;
|
||||
};
|
||||
|
||||
static const std::size_t s_sizes[Count];
|
||||
static const bool s_signedness[Count];
|
||||
|
||||
std::size_t size() const { return s_sizes[type]; }
|
||||
bool is_signed() const { return s_signedness[type]; }
|
||||
};
|
||||
|
||||
using instruction_argument_t = instruction_argument::type_e;
|
||||
|
||||
struct instruction {
|
||||
enum type_e : byte {
|
||||
NoOperation = 0,
|
||||
PushS8,
|
||||
PushU8,
|
||||
PushS16,
|
||||
PushU16,
|
||||
PushS32,
|
||||
PushU32,
|
||||
PushConstant,
|
||||
Array,
|
||||
Slice,
|
||||
Get,
|
||||
Set,
|
||||
Drop,
|
||||
Duplicate,
|
||||
Swap,
|
||||
Clone,
|
||||
Reference,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
Equals,
|
||||
NotEquals,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessEqual,
|
||||
GreaterEqual,
|
||||
Pointerof,
|
||||
Sizeof,
|
||||
Lengthof,
|
||||
Load,
|
||||
Store,
|
||||
LoadGlobal,
|
||||
StoreGlobal,
|
||||
Call,
|
||||
Jump,
|
||||
JumpNotZero,
|
||||
Return,
|
||||
|
||||
Count,
|
||||
} type;
|
||||
instruction_argument arg;
|
||||
|
||||
static const instruction_argument_t s_arguments[Count];
|
||||
|
||||
std::size_t read(furlang::view<std::uint8_t> in);
|
||||
std::size_t write(std::vector<std::uint8_t>& out) const;
|
||||
};
|
||||
|
||||
using instruction_t = instruction::type_e;
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_INSTRUCTION_HPP
|
||||
@@ -0,0 +1,452 @@
|
||||
#ifndef FURVM_MODULE_HPP
|
||||
#define FURVM_MODULE_HPP
|
||||
|
||||
#include "furlang/utility/hash.hpp"
|
||||
#include "furlang/view.hpp"
|
||||
#include "furvm/constant.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp"
|
||||
#include "furvm/thing.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
struct mod_type {
|
||||
struct array_value {
|
||||
mod_type_id typeId;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
struct slice_value {
|
||||
mod_type_id typeId;
|
||||
};
|
||||
|
||||
struct import_value {
|
||||
mod_id modId;
|
||||
mod_type_id typeId;
|
||||
};
|
||||
|
||||
enum type {
|
||||
S8 = 0,
|
||||
S16,
|
||||
S32,
|
||||
S64,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
Ptr,
|
||||
Ref,
|
||||
Array,
|
||||
Slice,
|
||||
|
||||
Import,
|
||||
Count,
|
||||
} type;
|
||||
union value {
|
||||
std::nullptr_t null = nullptr;
|
||||
mod_type_id typeRef;
|
||||
array_value array;
|
||||
slice_value slice;
|
||||
import_value imprt;
|
||||
|
||||
value() = default;
|
||||
|
||||
value(mod_type_id id)
|
||||
: typeRef(id) {}
|
||||
|
||||
value(mod_type_id id, std::size_t size)
|
||||
: array({}) {
|
||||
array.typeId = id;
|
||||
array.size = size;
|
||||
}
|
||||
|
||||
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
|
||||
value(ModIdFwd&& modId, mod_type_id typeId)
|
||||
: imprt({}) {
|
||||
imprt.modId = std::forward<ModIdFwd>(modId);
|
||||
imprt.typeId = typeId;
|
||||
}
|
||||
|
||||
~value() {}
|
||||
|
||||
value(value&& other) = delete;
|
||||
value& operator=(value&& other) = delete;
|
||||
value(const value& other) = delete;
|
||||
value& operator=(const value& other) = delete;
|
||||
} value;
|
||||
|
||||
mod_type(enum type type)
|
||||
: type(type) {}
|
||||
|
||||
mod_type(enum type type, mod_type_id typeRef)
|
||||
: type(type), value(typeRef) {}
|
||||
|
||||
mod_type(mod_type_id id, std::size_t size)
|
||||
: type(Array), value(id, size) {}
|
||||
|
||||
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
|
||||
mod_type(ModIdFwd&& modId, mod_type_id typeId)
|
||||
: type(Import), value(std::forward<ModIdFwd>(modId), typeId) {}
|
||||
|
||||
~mod_type() {
|
||||
switch (type) {
|
||||
case Array: value.array.~array_value(); break;
|
||||
case Slice: value.slice.~slice_value(); break;
|
||||
case Import: value.imprt.~import_value(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
mod_type(mod_type&& other) noexcept
|
||||
: type(other.type) {
|
||||
switch (type) {
|
||||
case Array: new (&value.array) array_value(other.value.array); break;
|
||||
case Slice: new (&value.slice) slice_value(other.value.slice); break;
|
||||
case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break;
|
||||
default: break;
|
||||
}
|
||||
other.type = Count;
|
||||
}
|
||||
|
||||
mod_type& operator=(mod_type&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
type = other.type;
|
||||
switch (type) {
|
||||
case Array: new (&value.array) array_value(other.value.array); break;
|
||||
case Slice: new (&value.slice) slice_value(other.value.slice); break;
|
||||
case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break;
|
||||
default: break;
|
||||
}
|
||||
other.type = Count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
mod_type(const mod_type& other)
|
||||
: type(other.type) {
|
||||
switch (type) {
|
||||
case Array: new (&value.array) array_value(other.value.array); break;
|
||||
case Slice: new (&value.slice) slice_value(other.value.slice); break;
|
||||
case Import: new (&value.imprt) import_value(other.value.imprt); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
mod_type& operator=(const mod_type& other) {
|
||||
if (this == &other) return *this;
|
||||
type = other.type;
|
||||
switch (type) {
|
||||
case Array: new (&value.array) array_value(other.value.array); break;
|
||||
case Slice: new (&value.slice) slice_value(other.value.slice); break;
|
||||
case Import: new (&value.imprt) import_value(other.value.imprt); break;
|
||||
default: break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct breakpoint {
|
||||
std::function<void(executor&, void*)> callback;
|
||||
void* data = nullptr;
|
||||
};
|
||||
|
||||
class mod {
|
||||
friend class function;
|
||||
friend class serializer;
|
||||
public:
|
||||
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
|
||||
|
||||
static constexpr char MAGIC[4] = { 'F', 'u', 'r', 'M' }; /** Furvm module file magic. */
|
||||
|
||||
using native_function = std::function<void(executor&)>;
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a module.
|
||||
*
|
||||
* @param name Name of the module.
|
||||
* @param args Arguments forwarded to bytecode's constructor.
|
||||
*/
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<bytecode_t, Args...>>>
|
||||
mod(Args&&... args)
|
||||
: m_bytecode(std::forward<Args>(args)...) {}
|
||||
|
||||
~mod() = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
mod(mod&&) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
mod& operator=(mod&&) = default;
|
||||
|
||||
mod(const mod&) = delete;
|
||||
mod& operator=(const mod&) = delete;
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a byte from bytecode of this module.
|
||||
*
|
||||
* @param offset An offset of the byte.
|
||||
* @return The byte.
|
||||
*/
|
||||
byte byte_at(std::size_t offset) const { return m_bytecode.at(offset); }
|
||||
|
||||
/**
|
||||
* @brief Returns the module's bytecode.
|
||||
*
|
||||
* @return A reference to the bytecode.
|
||||
*/
|
||||
constexpr bytecode_t& bytecode() { return m_bytecode; }
|
||||
|
||||
/**
|
||||
* @brief Returns the module's bytecode.
|
||||
*
|
||||
* @return A constant reference to the bytecode.
|
||||
*/
|
||||
furlang::view<std::uint8_t> bytecode_view() const { return { m_bytecode.data(), m_bytecode.size() }; }
|
||||
public:
|
||||
/**
|
||||
* @brief Emplaces a function in the module's function container.
|
||||
*
|
||||
* Emplaces the function in module's function container and name to function map and public functions map.
|
||||
*
|
||||
* @param args Arguments forwarded into the container's emplace_back function.
|
||||
* @return A handle to the emplaced function.
|
||||
*/
|
||||
template <typename... Args>
|
||||
function_h emplace_function(Args&&... args) {
|
||||
function_h function;
|
||||
if constexpr (std::is_constructible_v<class function, Args...>) {
|
||||
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
|
||||
} else {
|
||||
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
|
||||
}
|
||||
return std::move(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emplaces a function in the module's function container.
|
||||
*
|
||||
* Emplaces the function in module's function container and name to function map.
|
||||
*
|
||||
* @param name Name of the function.
|
||||
* @param args Arguments forwarded into the container's emplace_back function.
|
||||
* @return A handle to the emplaced function.
|
||||
*/
|
||||
template <typename NameFwd,
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
|
||||
function_h emplace_function(NameFwd&& name, Args&&... args) {
|
||||
function_h function;
|
||||
if constexpr (std::is_constructible_v<class function, Args...>) {
|
||||
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
|
||||
} else {
|
||||
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
|
||||
}
|
||||
auto pair = std::make_pair(std::forward<NameFwd>(name), function->signature());
|
||||
m_functionMap[function.id()] = pair;
|
||||
m_functionSigs[std::move(pair)] = function.id();
|
||||
return std::move(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a function from the module.
|
||||
*
|
||||
* @param id Identifier of the function.
|
||||
* @return A handle to the function.
|
||||
*/
|
||||
auto function_at(function_id id) { return m_functions.at(id); }
|
||||
|
||||
/**
|
||||
* @brief Returns a function from the module.
|
||||
*
|
||||
* @param id Identifier of the function.
|
||||
* @return A handle to the function.
|
||||
*/
|
||||
auto function_at(function_id id) const { return m_functions.at(id); }
|
||||
|
||||
/**
|
||||
* @brief Returns a function from the module.
|
||||
*
|
||||
* @param name Name of the function.
|
||||
* @return A handle to the function.
|
||||
*/
|
||||
template <typename NameFwd,
|
||||
typename SigFwd,
|
||||
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd> &&
|
||||
std::is_constructible_v<function_sig, SigFwd>>>
|
||||
auto function_at(NameFwd&& name, SigFwd&& signature) {
|
||||
return function_at(
|
||||
m_functionSigs.at(std::make_pair<>(std::forward<NameFwd>(name), std::forward<SigFwd>(signature))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases a function from the module's function container.
|
||||
*
|
||||
* @param id Identifier of the function.
|
||||
*/
|
||||
void erase_function(function_id id) {
|
||||
m_functions.erase(id);
|
||||
if (auto it = m_functionMap.find(id); it != m_functionMap.end()) {
|
||||
m_functionSigs.erase(it->second);
|
||||
m_functionMap.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
const handle_container<function_h>& functions() const { return m_functions; }
|
||||
|
||||
const auto& function_map() const { return m_functionMap; }
|
||||
public:
|
||||
template <typename NameFwd, typename Func>
|
||||
void set_native_function(NameFwd&& name, Func&& func) {
|
||||
m_nativeFunctions.emplace(std::forward<NameFwd>(name), std::forward<Func>(func));
|
||||
}
|
||||
|
||||
template <typename NameFwd>
|
||||
native_function get_native_function(NameFwd&& name) const {
|
||||
return m_nativeFunctions.at(std::forward<NameFwd>(name));
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* @brief Emplaces a type in the context.
|
||||
*
|
||||
* @param args Arguments forwarded to the type constructor.
|
||||
* @return The emplaced type.
|
||||
*/
|
||||
template <typename... Args>
|
||||
auto emplace_type(Args&&... args) {
|
||||
if constexpr (std::is_constructible_v<mod_type, Args...>) {
|
||||
return m_types.emplace_back(std::forward<Args>(args)...);
|
||||
} else {
|
||||
return m_types.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a type from the context.
|
||||
*
|
||||
* @param args type's id.
|
||||
* @return A handle to the type.
|
||||
*/
|
||||
template <typename... Args>
|
||||
auto type_at(Args&&... args) {
|
||||
return m_types.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a type from the context.
|
||||
*
|
||||
* @param args type's id.
|
||||
* @return A handle to the type.
|
||||
*/
|
||||
template <typename... Args>
|
||||
auto type_at(Args&&... args) const {
|
||||
return m_types.at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases a type from the context.
|
||||
*
|
||||
* @param args type's id.
|
||||
*/
|
||||
template <typename... Args>
|
||||
void erase_type(Args&&... args) {
|
||||
m_types.erase(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
const handle_container<mod_type_h>& types() const { return m_types; }
|
||||
public:
|
||||
void set_global_variable_count(std::uint16_t count) {
|
||||
m_globalVariables.resize(count);
|
||||
m_globalVariables.shrink_to_fit();
|
||||
}
|
||||
|
||||
std::uint16_t get_global_variable_count() const { return static_cast<std::uint16_t>(m_globalVariables.size()); }
|
||||
|
||||
void store_global_variable(std::uint16_t var, thing<>&& thing) {
|
||||
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||
m_globalVariables.emplace(m_globalVariables.cbegin() + var, std::move(thing));
|
||||
}
|
||||
|
||||
void store_global_variable(std::uint16_t var, const thing<>& thing) {
|
||||
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||
m_globalVariables.emplace(m_globalVariables.cbegin() + var, thing);
|
||||
}
|
||||
|
||||
thing<>& load_global_variable(std::uint16_t var) {
|
||||
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||
return m_globalVariables[var];
|
||||
}
|
||||
|
||||
const thing<>& load_global_variable(std::uint16_t var) const {
|
||||
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||
return m_globalVariables[var];
|
||||
}
|
||||
public:
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<constant, Args...>>>
|
||||
void emplace_constant(Args&&... args) {
|
||||
m_constants.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
const constant& constant_at(constant_index index) const { return m_constants.at(index); }
|
||||
public:
|
||||
template <typename Fwd, typename = std::enable_if_t<std::is_constructible_v<breakpoint, Fwd>>>
|
||||
void set_breakpoint(bytecode_pos pos, Fwd&& breakpoint) {
|
||||
m_breakpoints[pos] = std::forward<Fwd>(breakpoint);
|
||||
}
|
||||
|
||||
bool has_breakpoint(bytecode_pos pos) const { return m_breakpoints.find(pos) != m_breakpoints.end(); }
|
||||
|
||||
const breakpoint& breakpoint_at(bytecode_pos pos) const { return m_breakpoints.at(pos); }
|
||||
public:
|
||||
/**
|
||||
* @brief Prints the module in a bytecode form to an output stream.
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @return The output stream.
|
||||
*/
|
||||
std::ostream& serialize(std::ostream& os) const;
|
||||
|
||||
/**
|
||||
* @brief Loads a module in a bytecode form from an input stream.
|
||||
*
|
||||
* @param is Input stream.
|
||||
* @return The loaded module.
|
||||
*/
|
||||
static mod load(std::istream& is);
|
||||
private:
|
||||
bytecode_t m_bytecode;
|
||||
|
||||
using pair_type = std::pair<std::string, function_sig>;
|
||||
using pair_hash =
|
||||
furlang::utility::pair_hash<std::string, function_sig, std::hash<std::string>, detail::function_sig_hash>;
|
||||
std::unordered_map<pair_type, function_id, pair_hash> m_functionSigs;
|
||||
std::unordered_map<function_id, pair_type> m_functionMap;
|
||||
handle_container<function_h> m_functions;
|
||||
|
||||
handle_container<mod_type_h> m_types;
|
||||
|
||||
std::vector<thing<>> m_globalVariables;
|
||||
|
||||
std::vector<constant> m_constants;
|
||||
|
||||
std::unordered_map<std::string, native_function> m_nativeFunctions;
|
||||
|
||||
std::unordered_map<bytecode_pos, breakpoint> m_breakpoints;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_MODULE_HPP
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef FURVM_STACK_HPP
|
||||
#define FURVM_STACK_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <stack>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
template <typename T>
|
||||
struct stack {
|
||||
stack(std::size_t capacity = (1024ULL * 1024ULL) / sizeof(T))
|
||||
: begin(new T[capacity]()), cursor(begin), capacity(capacity) {}
|
||||
|
||||
T* begin;
|
||||
T* cursor;
|
||||
std::size_t capacity;
|
||||
std::stack<T*> frames;
|
||||
|
||||
void push_frame() { frames.push(cursor); }
|
||||
|
||||
void pop_frame() {
|
||||
cursor = frames.top();
|
||||
frames.pop();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class stack_allocator {
|
||||
public:
|
||||
stack_allocator() = default;
|
||||
|
||||
stack_allocator(stack<T>& stack)
|
||||
: m_ref(&stack) {}
|
||||
|
||||
template <typename U>
|
||||
constexpr stack_allocator(const stack_allocator<U>& other) noexcept
|
||||
: m_ref(other.m_ref) {}
|
||||
public:
|
||||
T* allocate(std::size_t n) {
|
||||
if (m_ref == nullptr) throw std::bad_alloc();
|
||||
if (m_ref->capacity - (m_ref->cursor - m_ref->begin) < n) throw std::bad_alloc();
|
||||
|
||||
T* ptr = m_ref->cursor;
|
||||
m_ref->cursor += n;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(T* ptr, std::size_t n) {}
|
||||
private:
|
||||
stack<T>* m_ref = nullptr;
|
||||
};
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_STACK_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
Reference in New Issue
Block a user