@@ -1,6 +1,7 @@
|
||||
#ifndef FURVM_DETAIL_HANDLE_HPP
|
||||
#define FURVM_DETAIL_HANDLE_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace furvm {
|
||||
@@ -27,6 +28,11 @@ struct header_has_refcount<Header,
|
||||
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
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef FURVM_FUNCTION_HPP
|
||||
#define FURVM_FUNCTION_HPP
|
||||
|
||||
#include "furlang/utility/hash.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp" // IWYU pragma: keep
|
||||
|
||||
@@ -9,6 +10,7 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
@@ -31,26 +33,43 @@ struct import_function {
|
||||
function_id function;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function signature.
|
||||
*/
|
||||
struct function_sig {
|
||||
std::vector<type_h> params;
|
||||
|
||||
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 paramCount Paremeter count.
|
||||
* @param signature Function's signature.
|
||||
* @param position Offset in bytecode of the function.
|
||||
*/
|
||||
function(std::uint32_t paramCount, bytecode_pos position)
|
||||
: m_type(function_t::Normal), m_paramCount(paramCount), m_value(position) {}
|
||||
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 paramCount Paremeter count.
|
||||
* @param signature Function's signature.
|
||||
* @param native Native function tag.
|
||||
*/
|
||||
template <typename Native, typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>>
|
||||
function(std::uint32_t paramCount, Native&& native)
|
||||
: m_type(function_t::Native), m_paramCount(paramCount), m_value(std::forward<Native>(native)) {}
|
||||
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.
|
||||
@@ -60,7 +79,7 @@ public:
|
||||
*/
|
||||
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_paramCount(0), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
|
||||
: m_type(function_t::Import), m_signature(), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an import function.
|
||||
@@ -103,11 +122,11 @@ public:
|
||||
constexpr function_t type() const { return m_type; }
|
||||
|
||||
/**
|
||||
* @brief Returns this function's parameter count.
|
||||
* @brief Returns this function's signature.
|
||||
*
|
||||
* @return The parameter count.
|
||||
* @return The signature.
|
||||
*/
|
||||
constexpr std::uint32_t param_count() const { return m_paramCount; }
|
||||
function_sig signature() const { return m_signature; }
|
||||
public:
|
||||
/**
|
||||
* @brief Returns normal function's value.
|
||||
@@ -139,8 +158,8 @@ public:
|
||||
return m_value.imp;
|
||||
}
|
||||
private:
|
||||
function_t m_type;
|
||||
std::uint32_t m_paramCount;
|
||||
function_t m_type;
|
||||
function_sig m_signature;
|
||||
|
||||
union value {
|
||||
std::size_t position = 0;
|
||||
@@ -168,6 +187,16 @@ private:
|
||||
} m_value;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct function_sig_hash {
|
||||
std::size_t operator()(const function_sig& signature) const {
|
||||
return furlang::utility::vector_hash<type_h, detail::handle_hash<type_h>>{}(signature.params);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace furvm
|
||||
|
||||
#endif // FURVM_FUNCTION_HPP
|
||||
|
||||
@@ -237,6 +237,10 @@ 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;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef FURVM_MODULE_HPP
|
||||
#define FURVM_MODULE_HPP
|
||||
|
||||
#include "furlang/utility/hash.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/handle.hpp"
|
||||
@@ -90,7 +91,6 @@ public:
|
||||
} else {
|
||||
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
|
||||
}
|
||||
m_functionMap[function.id()] = "";
|
||||
return std::move(function);
|
||||
}
|
||||
|
||||
@@ -99,22 +99,23 @@ public:
|
||||
*
|
||||
* 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_named(NameFwd&& name, Args&&... args) {
|
||||
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)...));
|
||||
}
|
||||
std::string nameInst = std::forward<NameFwd>(name);
|
||||
m_functionNames[nameInst] = function.id();
|
||||
m_functionMap[function.id()] = nameInst;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -140,20 +141,13 @@ public:
|
||||
* @param name Name of the function.
|
||||
* @return A handle to the function.
|
||||
*/
|
||||
template <typename NameFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
|
||||
auto function_at(NameFwd&& name) {
|
||||
return function_at(m_functionNames.at(std::forward<NameFwd>(name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a function from the module.
|
||||
*
|
||||
* @param name Name of the function.
|
||||
* @return A handle to the function.
|
||||
*/
|
||||
template <typename NameFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
|
||||
auto function_at(NameFwd&& name) const {
|
||||
return function_at(m_functionNames.at(std::forward<NameFwd>(name)));
|
||||
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))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +155,13 @@ public:
|
||||
*
|
||||
* @param id Identifier of the function.
|
||||
*/
|
||||
void erase_function(function_id id) { m_functions.erase(id); }
|
||||
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);
|
||||
}
|
||||
}
|
||||
public:
|
||||
template <typename NameFwd, typename Func>
|
||||
void set_native_function(NameFwd&& name, Func&& func) {
|
||||
@@ -238,9 +238,12 @@ public:
|
||||
private:
|
||||
bytecode_t m_bytecode;
|
||||
|
||||
std::unordered_map<std::string, function_id> m_functionNames;
|
||||
std::unordered_map<function_id, std::string> m_functionMap;
|
||||
handle_container<function_h> m_functions;
|
||||
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<type_h> m_types;
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ void executor::push_frame(const mod_h& mod, function function) {
|
||||
}
|
||||
|
||||
std::vector<thing_h> args;
|
||||
args.reserve(function.param_count());
|
||||
for (size_t i = 0; i < function.param_count(); ++i)
|
||||
args.reserve(function.signature().params.size());
|
||||
while (args.size() < args.capacity())
|
||||
args.push_back(pop_thing());
|
||||
|
||||
switch (function.type()) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace furvm {
|
||||
|
||||
function::function(const mod_h& mod, const function_h& function)
|
||||
: m_type(function_t::Import), m_paramCount(0), m_value(import_function{ mod.id(), function.id() }) {}
|
||||
: m_type(function_t::Import), m_value(import_function{ mod.id(), function.id() }) {}
|
||||
|
||||
function::~function() {
|
||||
switch (m_type) {
|
||||
@@ -23,7 +23,7 @@ function::~function() {
|
||||
}
|
||||
|
||||
function::function(function&& other) noexcept
|
||||
: m_type(other.m_type), m_paramCount(other.m_paramCount) {
|
||||
: m_type(other.m_type), m_signature(std::move(other.m_signature)) {
|
||||
switch (m_type) {
|
||||
case function_t::Normal: {
|
||||
m_value.position = other.m_value.position;
|
||||
@@ -42,8 +42,8 @@ function::function(function&& other) noexcept
|
||||
function& function::operator=(function&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_type = other.m_type;
|
||||
m_paramCount = other.m_paramCount;
|
||||
m_type = other.m_type;
|
||||
m_signature = other.m_signature;
|
||||
switch (m_type) {
|
||||
case function_t::Normal: {
|
||||
m_value.position = other.m_value.position;
|
||||
@@ -62,7 +62,7 @@ function& function::operator=(function&& other) noexcept {
|
||||
}
|
||||
|
||||
function::function(const function& other)
|
||||
: m_type(other.m_type), m_paramCount(other.m_paramCount) {
|
||||
: m_type(other.m_type), m_signature(other.m_signature) {
|
||||
switch (m_type) {
|
||||
case function_t::Normal: {
|
||||
m_value.position = other.m_value.position;
|
||||
@@ -80,8 +80,8 @@ function::function(const function& other)
|
||||
function& function::operator=(const function& other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_type = other.m_type;
|
||||
m_paramCount = other.m_paramCount;
|
||||
m_type = other.m_type;
|
||||
m_signature = other.m_signature;
|
||||
switch (m_type) {
|
||||
case function_t::Normal: {
|
||||
m_value.position = other.m_value.position;
|
||||
|
||||
+4
-4
@@ -58,10 +58,10 @@ int main(int argc, char** argv) {
|
||||
static_cast<furvm::byte>(furvm::instruction_t::Return),
|
||||
};
|
||||
|
||||
furvm::mod_h mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode));
|
||||
mod->emplace_type(std::make_shared<furvm::type>(context->at("core")->type_at(2), 10)).dispatch();
|
||||
auto mainFunc = mod->emplace_function_named("main", 0, 0);
|
||||
mod->emplace_function(1, "print").dispatch();
|
||||
furvm::mod_h mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode));
|
||||
furvm::type_h intType = mod->emplace_type(std::make_shared<furvm::type>(context->at("core")->type_at(2), 10));
|
||||
auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0);
|
||||
mod->emplace_function(furvm::function_sig{ { intType } }, "print").dispatch();
|
||||
#endif
|
||||
mod->set_native_function("print", [](furvm::executor& executor) { print_thing(*executor.load_thing(0)); });
|
||||
|
||||
|
||||
+88
-67
@@ -1,6 +1,7 @@
|
||||
#include "furvm/module.hpp"
|
||||
|
||||
#include "furvm/detail/serialization.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
#include "furvm/type.hpp"
|
||||
|
||||
@@ -17,34 +18,6 @@ std::ostream& mod::serialize(std::ostream& os) const {
|
||||
os.write(MAGIC, sizeof(MAGIC));
|
||||
detail::serialize(os, std::uint32_t(0)); // version
|
||||
|
||||
function_id funcCount = m_functions.cend() - m_functions.cbegin();
|
||||
detail::serialize(os, funcCount);
|
||||
for (function_id id = 0; id < funcCount; ++id) {
|
||||
if (!m_functions.contains(id)) {
|
||||
detail::serialize(os, "");
|
||||
detail::serialize(os, 0);
|
||||
detail::serialize(os, std::uint8_t(0xFF)); // null function
|
||||
continue;
|
||||
}
|
||||
|
||||
function_h func = m_functions.at(id);
|
||||
detail::serialize(os, m_functionMap.at(func.id()));
|
||||
detail::serialize(os, func->param_count());
|
||||
detail::serialize(os, std::uint8_t(func->type()));
|
||||
switch (func->type()) {
|
||||
case function_t::Normal: {
|
||||
detail::serialize(os, func->position());
|
||||
} break;
|
||||
case function_t::Native: {
|
||||
detail::serialize(os, func->native());
|
||||
} break;
|
||||
case function_t::Import: {
|
||||
detail::serialize(os, func->imp().mod);
|
||||
detail::serialize(os, func->imp().function);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
type_id typeCount = type_id(m_types.cend() - m_types.cbegin());
|
||||
detail::serialize(os, typeCount);
|
||||
for (type_id id = 0; id < typeCount; ++id) {
|
||||
@@ -67,6 +40,45 @@ std::ostream& mod::serialize(std::ostream& os) const {
|
||||
}
|
||||
}
|
||||
|
||||
function_id funcCount = m_functions.cend() - m_functions.cbegin();
|
||||
detail::serialize(os, funcCount);
|
||||
for (function_id id = 0; id < funcCount; ++id) {
|
||||
if (!m_functions.contains(id)) {
|
||||
detail::serialize(os, "");
|
||||
detail::serialize(os, 0);
|
||||
detail::serialize(os, std::uint8_t(0xFF)); // null function
|
||||
continue;
|
||||
}
|
||||
|
||||
// Function name
|
||||
function_h func = m_functions.at(id);
|
||||
if (auto it = m_functionMap.find(func.id()); it != m_functionMap.end()) {
|
||||
detail::serialize(os, it->second.first);
|
||||
} else {
|
||||
detail::serialize(os, "");
|
||||
}
|
||||
|
||||
// Function signature
|
||||
detail::serialize(os, func->signature().params.size());
|
||||
for (std::uint32_t i = 0; i < func->signature().params.size(); ++i)
|
||||
detail::serialize(os, func->signature().params[i].id());
|
||||
|
||||
// Function body/value
|
||||
detail::serialize(os, std::uint8_t(func->type()));
|
||||
switch (func->type()) {
|
||||
case function_t::Normal: {
|
||||
detail::serialize(os, func->position());
|
||||
} break;
|
||||
case function_t::Native: {
|
||||
detail::serialize(os, func->native());
|
||||
} break;
|
||||
case function_t::Import: {
|
||||
detail::serialize(os, func->imp().mod);
|
||||
detail::serialize(os, func->imp().function);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
detail::serialize(os, std::uint64_t(m_bytecode.size()));
|
||||
return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size()));
|
||||
}
|
||||
@@ -81,45 +93,6 @@ mod mod::load(std::istream& is) {
|
||||
|
||||
mod mod;
|
||||
|
||||
function_id functionCount = 0;
|
||||
detail::load(is, functionCount);
|
||||
for (function_id id = 0; id < functionCount; ++id) {
|
||||
std::string name;
|
||||
detail::load(is, name);
|
||||
std::uint32_t paramCount = 0;
|
||||
detail::load(is, paramCount);
|
||||
std::uint8_t type = 0;
|
||||
detail::load(is, type);
|
||||
if (type == 0xFF) continue;
|
||||
|
||||
switch ((function_t)type) {
|
||||
case function_t::Normal: {
|
||||
decltype(std::declval<function>().position()) offset = 0;
|
||||
detail::load(is, offset);
|
||||
if (name.empty())
|
||||
mod.emplace_function_named(std::move(name), id, paramCount, offset).dispatch();
|
||||
else
|
||||
mod.emplace_function(id, paramCount, offset).dispatch();
|
||||
} break;
|
||||
case function_t::Native: {
|
||||
std::string native;
|
||||
detail::load(is, native);
|
||||
if (name.empty())
|
||||
mod.emplace_function_named(std::move(name), id, paramCount, std::move(native)).dispatch();
|
||||
else
|
||||
mod.emplace_function(id, paramCount, std::move(native)).dispatch();
|
||||
} break;
|
||||
case function_t::Import: {
|
||||
std::string modName;
|
||||
detail::load(is, modName);
|
||||
function_id function = 0;
|
||||
detail::load(is, function);
|
||||
mod.emplace_function(id, std::move(modName), function).dispatch();
|
||||
} break;
|
||||
default: throw std::runtime_error("unknown function type");
|
||||
}
|
||||
}
|
||||
|
||||
type_id typeCount = 0;
|
||||
detail::load(is, typeCount);
|
||||
for (type_id id = 0; id < typeCount; ++id) {
|
||||
@@ -151,6 +124,54 @@ mod mod::load(std::istream& is) {
|
||||
}
|
||||
}
|
||||
|
||||
function_id functionCount = 0;
|
||||
detail::load(is, functionCount);
|
||||
for (function_id id = 0; id < functionCount; ++id) {
|
||||
std::string name;
|
||||
detail::load(is, name);
|
||||
|
||||
function_sig signature;
|
||||
std::uint32_t paramCount = 0;
|
||||
detail::load(is, paramCount);
|
||||
signature.params.reserve(paramCount);
|
||||
while (signature.params.size() < paramCount) {
|
||||
thing_id param{ 0 };
|
||||
detail::load(is, param);
|
||||
signature.params.emplace_back(mod.type_at(param));
|
||||
}
|
||||
|
||||
std::uint8_t type = 0;
|
||||
detail::load(is, type);
|
||||
if (type == 0xFF) continue;
|
||||
|
||||
switch ((function_t)type) {
|
||||
case function_t::Normal: {
|
||||
decltype(std::declval<function>().position()) offset = 0;
|
||||
detail::load(is, offset);
|
||||
if (name.empty())
|
||||
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
|
||||
else
|
||||
mod.emplace_function(id, std::move(signature), offset).dispatch();
|
||||
} break;
|
||||
case function_t::Native: {
|
||||
std::string native;
|
||||
detail::load(is, native);
|
||||
if (name.empty())
|
||||
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
|
||||
else
|
||||
mod.emplace_function(id, std::move(signature), std::move(native)).dispatch();
|
||||
} break;
|
||||
case function_t::Import: {
|
||||
std::string modName;
|
||||
detail::load(is, modName);
|
||||
function_id function = 0;
|
||||
detail::load(is, function);
|
||||
mod.emplace_function(id, std::move(modName), function).dispatch();
|
||||
} break;
|
||||
default: throw std::runtime_error("unknown function type");
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t bytecodeLength = 0;
|
||||
detail::load(is, bytecodeLength);
|
||||
mod.bytecode().resize(bytecodeLength);
|
||||
|
||||
Reference in New Issue
Block a user