Compare commits
37 Commits
eaa74fd573
...
c1b65838b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
c1b65838b0
|
|||
|
f0fe6b4179
|
|||
|
3f6d0adef6
|
|||
|
6ca5fcc995
|
|||
|
ba39f5ab27
|
|||
|
5472b77ea0
|
|||
|
00ec0b8f52
|
|||
|
9021ae0089
|
|||
|
4d935ef75f
|
|||
|
0534359503
|
|||
|
efe028a54f
|
|||
|
1e19d83d83
|
|||
|
8afeabfd69
|
|||
|
a4fd939cd2
|
|||
|
26d77bdc02
|
|||
|
d86f38d166
|
|||
|
874d1f14cb
|
|||
|
48c6da1134
|
|||
|
464b24eeed
|
|||
|
e7e5f36380
|
|||
|
bfdbbb0d5f
|
|||
|
daf16c18e1
|
|||
|
6824015728
|
|||
|
a077457636
|
|||
|
5ace40b6aa
|
|||
|
a869f71f3a
|
|||
|
16b6b9549c
|
|||
|
c6ec18aeb8
|
|||
|
4895489511
|
|||
|
5407f8cda7
|
|||
|
2030b66c7d
|
|||
|
0304911516
|
|||
|
6481c9c71d
|
|||
|
6605b3264f
|
|||
|
66fdd1dc9c
|
|||
|
2f9328d85a
|
|||
|
4a3ce6040c
|
@@ -13,10 +13,8 @@ if [ -z "$FILES" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for file in $FILES; do
|
git diff -U0 --cached | \
|
||||||
clang-format -i "$file"
|
python3 <(curl -s https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang/tools/clang-format/clang-format-diff.py) -p1 -i
|
||||||
git add "$file"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Running clang-tidy..."
|
echo "Running clang-tidy..."
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -8,7 +8,11 @@ find_package(GTest REQUIRED)
|
|||||||
|
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_subdirectory(furlang)
|
add_subdirectory(furlang)
|
||||||
|
add_subdirectory(furvm)
|
||||||
add_subdirectory(furc)
|
add_subdirectory(furc)
|
||||||
|
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
@@ -17,7 +21,7 @@ if(DOXYGEN_FOUND)
|
|||||||
set(DOXYGEN_HIDE_UNDOC_NAMESPACES NO)
|
set(DOXYGEN_HIDE_UNDOC_NAMESPACES NO)
|
||||||
set(DOXYGEN_HIDE_UNDOC_RELATIONS NO)
|
set(DOXYGEN_HIDE_UNDOC_RELATIONS NO)
|
||||||
set(DOXYGEN_FILE_PATTERNS "*.hpp")
|
set(DOXYGEN_FILE_PATTERNS "*.hpp")
|
||||||
doxygen_add_docs(doxygen_doc ${CMAKE_SOURCE_DIR}/furc/ ${CMAKE_SOURCE_DIR}/furlang/
|
doxygen_add_docs(doxygen_doc ${CMAKE_SOURCE_DIR}/furlang/ ${CMAKE_SOURCE_DIR}/furvm/ ${CMAKE_SOURCE_DIR}/furc/
|
||||||
ALL WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
ALL WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
COMMENT "Generating API documentation with Doxygen")
|
COMMENT "Generating API documentation with Doxygen")
|
||||||
endif()
|
endif()
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
file(GLOB_RECURSE FURVM_SRCS "src/**.cpp")
|
||||||
|
file(GLOB_RECURSE FURVM_HDRS "include/**.hpp")
|
||||||
|
add_library(libfurvm ${FURVM_SRCS} ${FURVM_HDRS})
|
||||||
|
target_include_directories(libfurvm PUBLIC include/)
|
||||||
|
target_compile_definitions(libfurvm PRIVATE LIBFURVM)
|
||||||
|
set_target_properties(libfurvm PROPERTIES PREFIX "")
|
||||||
|
target_link_libraries(libfurvm PUBLIC furlang)
|
||||||
|
add_executable(furvm src/main.cpp)
|
||||||
|
target_link_libraries(furvm PRIVATE libfurvm)
|
||||||
|
|
||||||
|
include(GoogleTest)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE FURVM_TESTS "test/**.cpp")
|
||||||
|
add_executable(furvm_tests ${FURVM_TESTS})
|
||||||
|
target_link_libraries(furvm_tests PRIVATE libfurvm GTest::gtest_main)
|
||||||
|
gtest_discover_tests(furvm_tests)
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
#ifndef FURVM_CONSTANT_HPP
|
||||||
|
#define FURVM_CONSTANT_HPP
|
||||||
|
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class constant_t : std::uint8_t {
|
||||||
|
String, /**< String constant. */
|
||||||
|
};
|
||||||
|
|
||||||
|
class constant {
|
||||||
|
public:
|
||||||
|
using string_type = std::string_view; /**< String constant type. */
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new string constant.
|
||||||
|
*
|
||||||
|
* @param string String.
|
||||||
|
*/
|
||||||
|
constant(string_type string)
|
||||||
|
: m_type(constant_t::String), m_value(string) {}
|
||||||
|
|
||||||
|
~constant() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
constant(constant&&) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
constant& operator=(constant&&) = default;
|
||||||
|
|
||||||
|
constant(const constant&) = delete;
|
||||||
|
constant& operator=(const constant&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns this constant's type.
|
||||||
|
* @see constant_t
|
||||||
|
*
|
||||||
|
* @return The constant type.
|
||||||
|
*/
|
||||||
|
constexpr constant_t type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this constant's string value.
|
||||||
|
* @throws bad_constant_access if this constant's type is not constant_t::String.
|
||||||
|
*
|
||||||
|
* @return The string value.
|
||||||
|
*/
|
||||||
|
constexpr string_type string() const {
|
||||||
|
require_type(constant_t::String);
|
||||||
|
return m_value.string;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void require_type(constant_t type) const {
|
||||||
|
if (m_type != type) throw bad_constant_access();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
constant_t m_type{};
|
||||||
|
union value {
|
||||||
|
string_type string;
|
||||||
|
|
||||||
|
value(string_type sv)
|
||||||
|
: string(sv) {}
|
||||||
|
} m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_CONSTANT_HPP
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#ifndef FURVM_CONTEXT_HPP
|
||||||
|
#define FURVM_CONTEXT_HPP
|
||||||
|
|
||||||
|
#include "furlang/arena.hpp"
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
#include "furvm/module.hpp"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
class context {
|
||||||
|
public:
|
||||||
|
friend class executor;
|
||||||
|
friend class thing;
|
||||||
|
public:
|
||||||
|
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:
|
||||||
|
/**
|
||||||
|
* @brief Adds a module to this context.
|
||||||
|
*
|
||||||
|
* @param args Arguments to forward to module's constructor.
|
||||||
|
* @return An index to the emplaced module.
|
||||||
|
*/
|
||||||
|
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<mod, module_handle, Args...>>>
|
||||||
|
constexpr const auto& emplace(Args&&... args) {
|
||||||
|
module_handle id = static_cast<module_handle>(m_modules.size());
|
||||||
|
return m_modules.emplace_back(std::make_unique<mod>(id, std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Erases a module from this context.
|
||||||
|
*
|
||||||
|
* @param index Index to the module.
|
||||||
|
* @return Old value.
|
||||||
|
*/
|
||||||
|
mod_p erase(module_handle index) {
|
||||||
|
if (index >= m_modules.size()) return nullptr;
|
||||||
|
return std::move(m_modules[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a module of this context.
|
||||||
|
*
|
||||||
|
* @param index Position of the module.
|
||||||
|
* @return The module.
|
||||||
|
*/
|
||||||
|
constexpr mod_p& operator[](module_handle index) { return m_modules[index]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a module of this context.
|
||||||
|
*
|
||||||
|
* @param index Position of the module.
|
||||||
|
* @return The module.
|
||||||
|
*/
|
||||||
|
constexpr const mod_p& operator[](module_handle index) const { return m_modules[index]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a module of this context.
|
||||||
|
*
|
||||||
|
* @param index Position of the module.
|
||||||
|
* @return The module.
|
||||||
|
*/
|
||||||
|
constexpr mod_p& at(module_handle index) { return m_modules.at(index); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a module of this context.
|
||||||
|
*
|
||||||
|
* @param index Position of the module.
|
||||||
|
* @return The module.
|
||||||
|
*/
|
||||||
|
constexpr const mod_p& at(module_handle index) const { return m_modules.at(index); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns how many does this context have modules.
|
||||||
|
*
|
||||||
|
* @return The module count.
|
||||||
|
*/
|
||||||
|
constexpr size_t size() const { return m_modules.size(); }
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Removes unreferenced things from the thing list.
|
||||||
|
*/
|
||||||
|
void collect();
|
||||||
|
private:
|
||||||
|
std::vector<mod_p> m_modules;
|
||||||
|
std::vector<thing_p> m_things;
|
||||||
|
std::vector<executor_p> m_executors;
|
||||||
|
|
||||||
|
std::queue<thing_handle> m_deadThings;
|
||||||
|
std::vector<void*> m_deadThingData;
|
||||||
|
furlang::arena m_thingArena;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_CONTEXT_HPP
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef FURVM_EXCEPTIONS_HPP
|
||||||
|
#define FURVM_EXCEPTIONS_HPP
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
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,158 @@
|
|||||||
|
#ifndef FURVM_EXECUTOR_HPP
|
||||||
|
#define FURVM_EXECUTOR_HPP
|
||||||
|
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
enum class executor_flags : std::uint32_t {
|
||||||
|
Suspended = (1 << 0), /**< Execution suspended. */
|
||||||
|
Done = (1 << 1), /**< Execution is finished. */
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief A private token for the private constructor.
|
||||||
|
*
|
||||||
|
* Also `egzekutor` in Polish translates to `executor` I think.
|
||||||
|
*/
|
||||||
|
struct egzekutor {
|
||||||
|
explicit egzekutor() = default;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Executor frame.
|
||||||
|
*
|
||||||
|
* Call frame.
|
||||||
|
*/
|
||||||
|
struct frame {
|
||||||
|
mod_p mod; /**< Shared pointer to a module with the bytecode. */
|
||||||
|
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
|
||||||
|
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Private constructor.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
executor(egzekutor, executor_handle id, const context_p& context);
|
||||||
|
|
||||||
|
~executor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
executor(executor&&) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
executor& operator=(executor&&) noexcept = default;
|
||||||
|
|
||||||
|
executor(const executor&) = delete;
|
||||||
|
executor& operator=(const executor&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns a new executor.
|
||||||
|
*
|
||||||
|
* @param context Furvm context.
|
||||||
|
* @return Shared pointer to the new executor.
|
||||||
|
*/
|
||||||
|
static executor_p create(const context_p& context);
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns an id of this executor.
|
||||||
|
*
|
||||||
|
* @return The id.
|
||||||
|
*/
|
||||||
|
executor_handle id() const { return m_id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns flags of this executor.
|
||||||
|
*
|
||||||
|
* @return The flags.
|
||||||
|
*/
|
||||||
|
executor_flags flags() const { return m_flags; }
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Pushes a new frame.
|
||||||
|
*
|
||||||
|
* @param function Function.
|
||||||
|
*/
|
||||||
|
void push_frame(const function_p& function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pops the top frame.
|
||||||
|
*
|
||||||
|
* @return The popped frame.
|
||||||
|
*/
|
||||||
|
frame pop_frame();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the top frame.
|
||||||
|
*
|
||||||
|
* @return The frame.
|
||||||
|
*/
|
||||||
|
frame frame() const;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Pushes a thing onto the stack.
|
||||||
|
*
|
||||||
|
* @param thing The thing to push.
|
||||||
|
*/
|
||||||
|
void push_thing(const thing_p& thing);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes a thing onto the stack.
|
||||||
|
*
|
||||||
|
* @param thing The thing to push.
|
||||||
|
*/
|
||||||
|
void push_thing(thing_p&& thing);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pops a thing from the stack.
|
||||||
|
*
|
||||||
|
* @return The popped thing.
|
||||||
|
*/
|
||||||
|
thing_p pop_thing();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the top thing on the stack.
|
||||||
|
*
|
||||||
|
* @return The thing.
|
||||||
|
*/
|
||||||
|
thing_p thing() const;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Executes next instruction.
|
||||||
|
*/
|
||||||
|
void step();
|
||||||
|
private:
|
||||||
|
executor_handle m_id;
|
||||||
|
executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization)
|
||||||
|
context_p m_context;
|
||||||
|
|
||||||
|
std::stack<struct frame> m_frames;
|
||||||
|
std::stack<thing_p> m_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_EXECUTOR_HPP
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
#ifndef FURVM_FUNCTION_HPP
|
||||||
|
#define FURVM_FUNCTION_HPP
|
||||||
|
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
using native_function = std::function<void(executor&)>;
|
||||||
|
|
||||||
|
class function {
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief A private token for the private constructor.
|
||||||
|
*
|
||||||
|
* Also `funkcja` in Polish translates to `function` from what I heard.
|
||||||
|
*/
|
||||||
|
struct funkcja {
|
||||||
|
explicit funkcja() = default;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Private constructor.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param position
|
||||||
|
* @param mod
|
||||||
|
*/
|
||||||
|
function(funkcja, function_handle id, std::size_t position, const mod_p& mod);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Private constructor.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param native
|
||||||
|
* @param mod
|
||||||
|
*/
|
||||||
|
function(funkcja, function_handle id, const native_function& native, const mod_p& mod);
|
||||||
|
|
||||||
|
~function() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
function(function&&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
function& operator=(function&&) noexcept;
|
||||||
|
|
||||||
|
function(const function&) = delete;
|
||||||
|
function& operator=(const function&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns a new function.
|
||||||
|
*
|
||||||
|
* @param mod Module.
|
||||||
|
* @param args Arguments to pass to the function constructor.
|
||||||
|
* @return The new function.
|
||||||
|
*/
|
||||||
|
template <typename... Args,
|
||||||
|
typename = std::enable_if_t<std::is_constructible_v<function, funkcja, function_handle, Args..., const mod_p&>>>
|
||||||
|
static function_p create(const mod_p& mod, Args&&... args) {
|
||||||
|
function_handle id = mod->m_functions.size();
|
||||||
|
|
||||||
|
auto func = std::make_shared<function>(funkcja{}, id, std::forward<Args>(args)..., mod);
|
||||||
|
mod->m_functions.emplace(mod->m_functions.begin() + id, func);
|
||||||
|
return std::move(func);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns an id of this function.
|
||||||
|
*
|
||||||
|
* @return The id.
|
||||||
|
*/
|
||||||
|
constexpr function_handle id() const { return m_id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a type of this function.
|
||||||
|
*
|
||||||
|
* @return The type.
|
||||||
|
*/
|
||||||
|
constexpr function_t type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a parent module of this function.
|
||||||
|
*
|
||||||
|
* @return A shared pointer to the module.
|
||||||
|
*/
|
||||||
|
const mod_p& mod() const { return m_module; }
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns a value for normal function.
|
||||||
|
*
|
||||||
|
* @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 a value for native function.
|
||||||
|
*
|
||||||
|
* @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 a module of imported function.
|
||||||
|
*
|
||||||
|
* @return A handle to the module.
|
||||||
|
*/
|
||||||
|
module_handle imported_module() const {
|
||||||
|
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
|
||||||
|
return m_value.imp.mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a module of imported function.
|
||||||
|
*
|
||||||
|
* @return A handle to the module.
|
||||||
|
*/
|
||||||
|
function_handle imported_function() const {
|
||||||
|
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
|
||||||
|
return m_value.imp.function;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
function_handle m_id;
|
||||||
|
function_t m_type;
|
||||||
|
mod_p m_module;
|
||||||
|
|
||||||
|
union value {
|
||||||
|
std::size_t position = 0;
|
||||||
|
native_function native;
|
||||||
|
struct {
|
||||||
|
module_handle mod;
|
||||||
|
function_handle function;
|
||||||
|
} imp;
|
||||||
|
|
||||||
|
value() = default;
|
||||||
|
|
||||||
|
value(std::size_t position)
|
||||||
|
: position(position) {}
|
||||||
|
|
||||||
|
value(const native_function& native)
|
||||||
|
: native(native) {}
|
||||||
|
|
||||||
|
value(module_handle mod, function_handle function)
|
||||||
|
: imp({ mod, function }) {}
|
||||||
|
|
||||||
|
~value() {}
|
||||||
|
|
||||||
|
value(value&& other) = delete;
|
||||||
|
value& operator=(value&& other) = delete;
|
||||||
|
value(const value& other) = delete;
|
||||||
|
value& operator=(const value& other) = delete;
|
||||||
|
} m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_FUNCTION_HPP
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#ifndef FURVM_FWD_HPP
|
||||||
|
#define FURVM_FWD_HPP
|
||||||
|
|
||||||
|
#include <cstddef> // IWYU pragma: export
|
||||||
|
#include <cstdint> // IWYU pragma: export
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Furlang's virtual machine.
|
||||||
|
*/
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A byte.
|
||||||
|
*
|
||||||
|
* There's nothing more to it.
|
||||||
|
*/
|
||||||
|
using byte = std::uint8_t;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum instruction_t
|
||||||
|
* @brief Furvm's instruction type.
|
||||||
|
*/
|
||||||
|
enum class instruction_t : byte;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 An alias to a function shared pointer.
|
||||||
|
*/
|
||||||
|
using function_p = std::shared_ptr<function>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Furvm function's index.
|
||||||
|
*/
|
||||||
|
using function_handle = std::uint16_t;
|
||||||
|
|
||||||
|
// module.hpp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 Furvm module's index.
|
||||||
|
*/
|
||||||
|
using module_handle = std::uint32_t;
|
||||||
|
|
||||||
|
// thing.hpp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum thing_t
|
||||||
|
* @brief Thing type.
|
||||||
|
*/
|
||||||
|
enum class thing_t : std::uint8_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class bad_thing_access
|
||||||
|
* @brief Bad thing access exception.
|
||||||
|
*/
|
||||||
|
class bad_thing_access;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class thing
|
||||||
|
* @brief Furvm thing.
|
||||||
|
*
|
||||||
|
* A stack element. Think of it like of a value in C++ or I guess a class in java.
|
||||||
|
*/
|
||||||
|
class thing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An alias to a thing shared pointer.
|
||||||
|
*/
|
||||||
|
using thing_p = std::shared_ptr<thing>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Furvm thing's index.
|
||||||
|
*/
|
||||||
|
using thing_handle = std::uint32_t;
|
||||||
|
|
||||||
|
// executor.hpp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 An alias to a executor shared pointer.
|
||||||
|
*/
|
||||||
|
using executor_p = std::shared_ptr<executor>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Furvm executor's index.
|
||||||
|
*/
|
||||||
|
using executor_handle = 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,99 @@
|
|||||||
|
#ifndef FURVM_INSTRUCTION_HPP
|
||||||
|
#define FURVM_INSTRUCTION_HPP
|
||||||
|
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
enum class instruction_t : byte {
|
||||||
|
/**
|
||||||
|
* @brief No operation.
|
||||||
|
*/
|
||||||
|
NoOperation = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an integer from a byte onto the stack.
|
||||||
|
*
|
||||||
|
* Pushes an integer constructed from a next byte onto the stack.
|
||||||
|
*/
|
||||||
|
PushB2I,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes a constant onto the stack.
|
||||||
|
*
|
||||||
|
* Pushes a constant from the constant pool denoted by two next bytes in little-endian onto the stack.
|
||||||
|
*/
|
||||||
|
PushConstant,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pops top element from the stack.
|
||||||
|
*/
|
||||||
|
Drop,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Duplicates top element on the stack.
|
||||||
|
*/
|
||||||
|
Duplicate,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clones top element on the stack.
|
||||||
|
*/
|
||||||
|
Clone,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds two things together on the stack.
|
||||||
|
*/
|
||||||
|
Add,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subtracts two things together on the stack.
|
||||||
|
*/
|
||||||
|
Sub,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiplies two things together on the stack.
|
||||||
|
*/
|
||||||
|
Mul,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Divides two things together on the stack.
|
||||||
|
*/
|
||||||
|
Div,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modulos two things together on the stack.
|
||||||
|
*/
|
||||||
|
Mod,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calls a function.
|
||||||
|
*
|
||||||
|
* Calls a function denoted by next two bytes in little-endian from current frame's module.
|
||||||
|
*/
|
||||||
|
Call,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pops the current call frame.
|
||||||
|
*/
|
||||||
|
Return,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pops the current call frame and pushes the first element from the previous stack onto the new stack.
|
||||||
|
*/
|
||||||
|
ReturnValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct instruction {
|
||||||
|
instruction_t type; /**< Instruction type. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Instruction value.
|
||||||
|
*/
|
||||||
|
union value {
|
||||||
|
constant_index constant; /**< Constant instruction argument. */
|
||||||
|
} value; /**< Instruction value. */
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_INSTRUCTION_HPP
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#ifndef FURVM_MODULE_HPP
|
||||||
|
#define FURVM_MODULE_HPP
|
||||||
|
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
class mod {
|
||||||
|
friend class function;
|
||||||
|
public:
|
||||||
|
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new module.
|
||||||
|
*
|
||||||
|
* @param id Id 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(module_handle id, Args&&... args)
|
||||||
|
: m_id(id), 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 an id of this module.
|
||||||
|
*
|
||||||
|
* @return The id.
|
||||||
|
*/
|
||||||
|
constexpr module_handle id() const { return m_id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a byte from bytecode of this module.
|
||||||
|
*
|
||||||
|
* @param offset An offset of the byte.
|
||||||
|
* @return The byte.
|
||||||
|
*/
|
||||||
|
constexpr byte byte(std::size_t offset) const { return m_bytecode.at(offset); }
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns a function from this module.
|
||||||
|
*
|
||||||
|
* @param id Id of the function.
|
||||||
|
* @return The function.
|
||||||
|
*/
|
||||||
|
constexpr const function_p& function_at(function_handle id) const { return m_functions.at(id); }
|
||||||
|
private:
|
||||||
|
module_handle m_id;
|
||||||
|
bytecode_t m_bytecode;
|
||||||
|
std::vector<function_p> m_functions;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_MODULE_HPP
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
#ifndef FURVM_THING_HPP
|
||||||
|
#define FURVM_THING_HPP
|
||||||
|
|
||||||
|
#include "furvm/context.hpp" // IWYU pragma: keep
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
enum class thing_t : std::uint8_t {
|
||||||
|
Int32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns data size of a thing.
|
||||||
|
*
|
||||||
|
* @param type Type of the thing.
|
||||||
|
* @return The data size of the thing.
|
||||||
|
*/
|
||||||
|
std::size_t thing_type_size(thing_t type);
|
||||||
|
|
||||||
|
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"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class thing final {
|
||||||
|
friend class executor;
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief A private token for the private constructor.
|
||||||
|
*
|
||||||
|
* Also `rzecz` in Polish translates to `thing` I think.
|
||||||
|
*/
|
||||||
|
struct rzecz {
|
||||||
|
explicit rzecz() = default;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
using nref_t = std::size_t; /**< Type of reference count. */
|
||||||
|
|
||||||
|
static constexpr thing_handle GENERATION_SIZE = 12; /**< Bit size of generation part in thing_handle. */
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Private constructor.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param type
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
thing(rzecz, thing_handle id, thing_t type, const context_p& context);
|
||||||
|
|
||||||
|
~thing();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
thing(thing&&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
thing& operator=(thing&&) noexcept;
|
||||||
|
|
||||||
|
thing(const thing&) = delete;
|
||||||
|
thing& operator=(const thing&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Adds two things together.
|
||||||
|
*
|
||||||
|
* @param lhs Left-hand-side thing.
|
||||||
|
* @param rhs Right-hand-side thing.
|
||||||
|
* @return Shared pointer to result thing.
|
||||||
|
*/
|
||||||
|
friend thing_p operator+(const thing_p& lhs, const thing_p& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subtracts two things together.
|
||||||
|
*
|
||||||
|
* @param lhs Left-hand-side thing.
|
||||||
|
* @param rhs Right-hand-side thing.
|
||||||
|
* @return Shared pointer to result thing.
|
||||||
|
*/
|
||||||
|
friend thing_p operator-(const thing_p& lhs, const thing_p& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiplies two things together.
|
||||||
|
*
|
||||||
|
* @param lhs Left-hand-side thing.
|
||||||
|
* @param rhs Right-hand-side thing.
|
||||||
|
* @return Shared pointer to result thing.
|
||||||
|
*/
|
||||||
|
friend thing_p operator*(const thing_p& lhs, const thing_p& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Divides two things together.
|
||||||
|
*
|
||||||
|
* @param lhs Left-hand-side thing.
|
||||||
|
* @param rhs Right-hand-side thing.
|
||||||
|
* @return Shared pointer to result thing.
|
||||||
|
*/
|
||||||
|
friend thing_p operator/(const thing_p& lhs, const thing_p& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modulos two things together.
|
||||||
|
*
|
||||||
|
* @param lhs Left-hand-side thing.
|
||||||
|
* @param rhs Right-hand-side thing.
|
||||||
|
* @return Shared pointer to result thing.
|
||||||
|
*/
|
||||||
|
friend thing_p operator%(const thing_p& lhs, const thing_p& rhs);
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns a new thing.
|
||||||
|
*
|
||||||
|
* @param context Furvm context.
|
||||||
|
* @param args Arguments to forward to the thing constructor.
|
||||||
|
* @return Shared pointer to the new thing.
|
||||||
|
*/
|
||||||
|
template <typename... Args,
|
||||||
|
typename = std::enable_if_t<std::is_constructible_v<thing, rzecz, thing_handle, Args..., const context_p&>>>
|
||||||
|
static thing_p create(const context_p& context, Args&&... args) {
|
||||||
|
thing_handle id = context->m_things.size();
|
||||||
|
if (!context->m_deadThings.empty()) {
|
||||||
|
id = context->m_deadThings.front();
|
||||||
|
context->m_deadThings.pop();
|
||||||
|
id += 1 << GENERATION_SIZE;
|
||||||
|
}
|
||||||
|
thing_handle idx = id & ((1ULL << ((sizeof(id) * 8) - GENERATION_SIZE)) - 1);
|
||||||
|
|
||||||
|
auto th = std::make_shared<thing>(rzecz{}, id, std::forward<Args>(args)..., context);
|
||||||
|
context->m_things.emplace(context->m_things.begin() + idx, th);
|
||||||
|
return std::move(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a clone of the thing.
|
||||||
|
*
|
||||||
|
* @param thing Thing to clone.
|
||||||
|
* @return Shared pointer to a clone of the thing.
|
||||||
|
*/
|
||||||
|
static thing_p clone(const thing_p& thing);
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns an int32 value from this thing.
|
||||||
|
*
|
||||||
|
* @return The value.
|
||||||
|
*/
|
||||||
|
std::int32_t& int32();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an int32 value from this thing.
|
||||||
|
*
|
||||||
|
* @return The value.
|
||||||
|
*/
|
||||||
|
const std::int32_t& int32() const;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Increments reference count of this thing by one.
|
||||||
|
*/
|
||||||
|
void add_reference() { ++m_refCount; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decrements reference count of this thing by one.
|
||||||
|
*/
|
||||||
|
void remove_reference() { --m_refCount; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns reference count of this thing.
|
||||||
|
*
|
||||||
|
* @return The reference count.
|
||||||
|
*/
|
||||||
|
constexpr nref_t reference_count() const { return m_refCount; }
|
||||||
|
private:
|
||||||
|
thing_handle m_id;
|
||||||
|
thing_t m_type;
|
||||||
|
context_p m_context;
|
||||||
|
|
||||||
|
nref_t m_refCount = 0;
|
||||||
|
void* m_data = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_THING_HPP
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "furvm/context.hpp"
|
||||||
|
|
||||||
|
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
context::context() {}
|
||||||
|
|
||||||
|
void context::collect() {
|
||||||
|
for (auto& ref : m_things) {
|
||||||
|
if (ref->reference_count() != 0) continue;
|
||||||
|
ref = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
#include "furvm/executor.hpp"
|
||||||
|
|
||||||
|
#include "furvm/context.hpp" // IWYU pragma: keep
|
||||||
|
#include "furvm/exceptions.hpp"
|
||||||
|
#include "furvm/function.hpp" // IWYU pragma: keep
|
||||||
|
#include "furvm/instruction.hpp"
|
||||||
|
#include "furvm/thing.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
executor::executor(egzekutor, executor_handle id, const context_p& context)
|
||||||
|
: m_id(id), m_context(context) {}
|
||||||
|
|
||||||
|
executor::~executor() {
|
||||||
|
m_context->m_executors[m_id] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
executor_p executor::create(const context_p& context) {
|
||||||
|
auto ex = std::make_shared<executor>(egzekutor{}, context->m_executors.size(), context);
|
||||||
|
context->m_executors.push_back(ex);
|
||||||
|
return std::move(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::push_frame(const function_p& function) {
|
||||||
|
if (function->type() != function_t::Normal) return;
|
||||||
|
m_frames.emplace((struct executor::frame){ function->mod(), function->position(), m_stack.size() });
|
||||||
|
}
|
||||||
|
|
||||||
|
struct executor::frame executor::pop_frame() {
|
||||||
|
if (m_frames.empty()) throw stack_underflow();
|
||||||
|
struct executor::frame frame = m_frames.top();
|
||||||
|
m_frames.pop();
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct executor::frame executor::frame() const {
|
||||||
|
return m_frames.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::push_thing(const thing_p& thing) {
|
||||||
|
thing->add_reference();
|
||||||
|
m_stack.push(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::push_thing(thing_p&& thing) {
|
||||||
|
thing->add_reference();
|
||||||
|
m_stack.push(std::move(thing));
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p executor::pop_thing() {
|
||||||
|
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||||
|
thing_p top = std::move(m_stack.top());
|
||||||
|
m_stack.pop();
|
||||||
|
top->remove_reference();
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p executor::thing() const {
|
||||||
|
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||||
|
return m_stack.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::step() {
|
||||||
|
if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return;
|
||||||
|
|
||||||
|
struct frame& frame = m_frames.top();
|
||||||
|
|
||||||
|
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 = thing::create(m_context, thing_t::Int32);
|
||||||
|
thing->int32() = frame.mod->byte(frame.position++);
|
||||||
|
push_thing(std::move(thing));
|
||||||
|
} break;
|
||||||
|
case instruction_t::Drop: {
|
||||||
|
pop_thing();
|
||||||
|
} break;
|
||||||
|
case instruction_t::Duplicate: {
|
||||||
|
push_thing(thing());
|
||||||
|
} break;
|
||||||
|
case instruction_t::Clone: {
|
||||||
|
push_thing(std::move(thing::clone(thing())));
|
||||||
|
} break;
|
||||||
|
case instruction_t::Add: {
|
||||||
|
auto rhs = pop_thing();
|
||||||
|
auto lhs = pop_thing();
|
||||||
|
push_thing(lhs + rhs);
|
||||||
|
} break;
|
||||||
|
case instruction_t::Sub: {
|
||||||
|
auto rhs = pop_thing();
|
||||||
|
auto lhs = pop_thing();
|
||||||
|
push_thing(lhs - rhs);
|
||||||
|
} break;
|
||||||
|
case instruction_t::Mul: {
|
||||||
|
auto rhs = pop_thing();
|
||||||
|
auto lhs = pop_thing();
|
||||||
|
push_thing(lhs * rhs);
|
||||||
|
} break;
|
||||||
|
case instruction_t::Div: {
|
||||||
|
auto rhs = pop_thing();
|
||||||
|
auto lhs = pop_thing();
|
||||||
|
push_thing(lhs / rhs);
|
||||||
|
} break;
|
||||||
|
case instruction_t::Mod: {
|
||||||
|
auto rhs = pop_thing();
|
||||||
|
auto lhs = pop_thing();
|
||||||
|
push_thing(lhs % rhs);
|
||||||
|
} break;
|
||||||
|
case instruction_t::Call: {
|
||||||
|
function_handle 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_p& function = frame.mod->function_at(funcId);
|
||||||
|
switch (function->type()) {
|
||||||
|
case function_t::Normal: push_frame(function); break;
|
||||||
|
case function_t::Native: function->native()(*this); break;
|
||||||
|
case function_t::Import: {
|
||||||
|
const mod_p& impMod = m_context->m_modules.at(function->imported_module());
|
||||||
|
push_frame(impMod->function_at(function->imported_function()));
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case instruction_t::Return: {
|
||||||
|
pop_frame();
|
||||||
|
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
|
||||||
|
} break;
|
||||||
|
case instruction_t::ReturnValue: {
|
||||||
|
auto value = pop_thing();
|
||||||
|
pop_frame();
|
||||||
|
push_thing(std::move(value));
|
||||||
|
} break;
|
||||||
|
case instruction_t::PushConstant: throw std::runtime_error("unimplemented");
|
||||||
|
default: throw std::runtime_error("unknown instruction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#include "furvm/function.hpp"
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
function::function(funkcja, function_handle id, std::size_t position, const mod_p& mod)
|
||||||
|
: m_id(id), m_type(function_t::Normal), m_module(mod), m_value(position) {}
|
||||||
|
|
||||||
|
function::function(funkcja, function_handle id, const native_function& native, const mod_p& mod)
|
||||||
|
: m_id(id), m_type(function_t::Native), m_module(mod), m_value(native) {}
|
||||||
|
|
||||||
|
function::function(function&& other) noexcept
|
||||||
|
: m_id(other.m_id), m_type(other.m_type), m_module(std::move(other.m_module)) {
|
||||||
|
switch (m_type) {
|
||||||
|
case function_t::Normal: {
|
||||||
|
m_value.position = other.m_value.position;
|
||||||
|
} break;
|
||||||
|
case function_t::Native: {
|
||||||
|
new (&m_value.native) native_function(std::move(other.m_value.native));
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
other.m_value.position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function& function::operator=(function&& other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
|
||||||
|
m_id = other.m_id;
|
||||||
|
m_type = other.m_type;
|
||||||
|
m_module = std::move(other.m_module);
|
||||||
|
switch (m_type) {
|
||||||
|
case function_t::Normal: {
|
||||||
|
m_value.position = other.m_value.position;
|
||||||
|
} break;
|
||||||
|
case function_t::Native: {
|
||||||
|
new (&m_value.native) native_function(std::move(other.m_value.native));
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
other.m_value.position = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef LIBFURVM
|
||||||
|
|
||||||
|
#include "furvm/context.hpp"
|
||||||
|
#include "furvm/executor.hpp"
|
||||||
|
#include "furvm/function.hpp"
|
||||||
|
#include "furvm/instruction.hpp"
|
||||||
|
#include "furvm/thing.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
static constexpr std::array<furvm::byte, 8> s_bytecode = {
|
||||||
|
furvm::byte(furvm::instruction_t::PushB2I),
|
||||||
|
67,
|
||||||
|
furvm::byte(furvm::instruction_t::Clone),
|
||||||
|
furvm::byte(furvm::instruction_t::Add),
|
||||||
|
furvm::byte(furvm::instruction_t::Call),
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
furvm::byte(furvm::instruction_t::Return),
|
||||||
|
};
|
||||||
|
|
||||||
|
void print(furvm::executor& exec) {
|
||||||
|
std::cout << exec.pop_thing()->int32() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
auto context = std::make_shared<furvm::context>();
|
||||||
|
|
||||||
|
auto mainModule = context->emplace(s_bytecode.begin(), s_bytecode.end());
|
||||||
|
|
||||||
|
auto mainFunction = furvm::function::create(mainModule, 0);
|
||||||
|
|
||||||
|
auto printFunction = furvm::function::create(mainModule, print);
|
||||||
|
|
||||||
|
auto executor = furvm::executor::create(context);
|
||||||
|
executor->push_frame(mainFunction);
|
||||||
|
|
||||||
|
static constexpr std::size_t FPC = 3; // Frames per collection
|
||||||
|
|
||||||
|
std::size_t count = 0;
|
||||||
|
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
|
||||||
|
executor->step();
|
||||||
|
if ((++count % FPC) == 0) context->collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LUBFURVM
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#include "furvm/module.hpp"
|
||||||
|
|
||||||
|
namespace furvm {}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
// 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(rzecz, thing_handle id, thing_t type, const context_p& context)
|
||||||
|
: m_id(id), m_type(type), m_context(context) {
|
||||||
|
std::size_t size = thing_type_size(type);
|
||||||
|
std::byte* data = nullptr;
|
||||||
|
if (!m_context->m_deadThingData.empty()) {
|
||||||
|
thing_t itType{};
|
||||||
|
for (auto it = m_context->m_deadThingData.rbegin(); it != m_context->m_deadThingData.rend(); ++it) {
|
||||||
|
std::memcpy(&itType, static_cast<std::byte*>(*it) - sizeof(itType), sizeof(itType));
|
||||||
|
if (size == thing_type_size(itType)) {
|
||||||
|
data = static_cast<std::byte*>(*it);
|
||||||
|
m_context->m_deadThingData.erase(std::next(it).base());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data == nullptr) data = m_context->m_thingArena.allocate<std::byte>(sizeof(type) + 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() {
|
||||||
|
switch (m_type) {
|
||||||
|
// Primitives are not destructed.
|
||||||
|
default:
|
||||||
|
case thing_t::Int32: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_context->m_deadThingData.push_back(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
thing::thing(thing&& other) noexcept
|
||||||
|
: m_id(other.m_id),
|
||||||
|
m_type(other.m_type),
|
||||||
|
m_context(std::move(other.m_context)),
|
||||||
|
m_refCount(other.m_refCount),
|
||||||
|
m_data(other.m_data) {
|
||||||
|
other.m_id = {};
|
||||||
|
other.m_type = {};
|
||||||
|
other.m_refCount = {};
|
||||||
|
other.m_data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
thing& thing::operator=(thing&& other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
m_id = other.m_id;
|
||||||
|
m_type = other.m_type;
|
||||||
|
m_context = std::move(other.m_context);
|
||||||
|
m_refCount = other.m_refCount;
|
||||||
|
m_data = other.m_data;
|
||||||
|
|
||||||
|
other.m_id = {};
|
||||||
|
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_p operator+(const thing_p& lhs, const thing_p& rhs) {
|
||||||
|
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
|
||||||
|
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||||
|
auto res = thing::create(lhs->m_context, thing_t::Int32);
|
||||||
|
res->int32() = lhs->int32() + rhs->int32();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
default: throw std::runtime_error("unexpected operator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p operator-(const thing_p& lhs, const thing_p& rhs) {
|
||||||
|
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
|
||||||
|
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||||
|
auto res = thing::create(lhs->m_context, thing_t::Int32);
|
||||||
|
res->int32() = lhs->int32() - rhs->int32();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
default: throw std::runtime_error("unexpected operator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p operator*(const thing_p& lhs, const thing_p& rhs) {
|
||||||
|
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
|
||||||
|
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||||
|
auto res = thing::create(lhs->m_context, thing_t::Int32);
|
||||||
|
res->int32() = lhs->int32() * rhs->int32();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
default: throw std::runtime_error("unexpected operator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p operator/(const thing_p& lhs, const thing_p& rhs) {
|
||||||
|
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
|
||||||
|
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||||
|
auto res = thing::create(lhs->m_context, thing_t::Int32);
|
||||||
|
res->int32() = lhs->int32() / rhs->int32();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
default: throw std::runtime_error("unexpected operator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p operator%(const thing_p& lhs, const thing_p& rhs) {
|
||||||
|
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
|
||||||
|
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
|
||||||
|
auto res = thing::create(lhs->m_context, thing_t::Int32);
|
||||||
|
res->int32() = lhs->int32() % rhs->int32();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
default: throw std::runtime_error("unexpected operator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_p thing::clone(const thing_p& thing) {
|
||||||
|
thing_handle id = thing->m_context->m_things.size();
|
||||||
|
if (!thing->m_context->m_deadThings.empty()) {
|
||||||
|
id = thing->m_context->m_deadThings.front();
|
||||||
|
thing->m_context->m_deadThings.pop();
|
||||||
|
id += 1 << GENERATION_SIZE;
|
||||||
|
}
|
||||||
|
thing_handle idx = id & ((1ULL << ((sizeof(id) * 8) - GENERATION_SIZE)) - 1);
|
||||||
|
|
||||||
|
auto th = std::make_shared<class thing>(rzecz{}, id, thing->m_type, thing->m_context);
|
||||||
|
switch (thing->m_type) {
|
||||||
|
// Primitives
|
||||||
|
default: {
|
||||||
|
memcpy(th->m_data, thing->m_data, thing_type_size(thing->m_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing->m_context->m_things.emplace(thing->m_context->m_things.begin() + idx, th);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
// NOLINTEND(cppcoreguidelines-no-malloc)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#include "gtest/gtest.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
namespace {}
|
||||||
Reference in New Issue
Block a user