Compare commits
5 Commits
9021ae0089
...
3f6d0adef6
| Author | SHA1 | Date | |
|---|---|---|---|
|
3f6d0adef6
|
|||
|
6ca5fcc995
|
|||
|
ba39f5ab27
|
|||
|
5472b77ea0
|
|||
|
00ec0b8f52
|
@@ -8,6 +8,9 @@ find_package(GTest REQUIRED)
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_subdirectory(furlang)
|
||||
add_subdirectory(furvm)
|
||||
add_subdirectory(furc)
|
||||
|
||||
@@ -94,10 +94,9 @@ public:
|
||||
/**
|
||||
* @brief Pushes a new frame.
|
||||
*
|
||||
* @param mod A shared pointer to a furvm module.
|
||||
* @param position Position in the module's bytecode.
|
||||
* @param function Function.
|
||||
*/
|
||||
void push_frame(const mod_p& mod, std::size_t position);
|
||||
void push_frame(const function_p& function);
|
||||
|
||||
/**
|
||||
* @brief Pops the top frame.
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#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. */
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
private:
|
||||
function_handle m_id;
|
||||
function_t m_type;
|
||||
mod_p m_module;
|
||||
|
||||
union value {
|
||||
std::size_t position = 0;
|
||||
native_function native;
|
||||
|
||||
value() = default;
|
||||
|
||||
value(std::size_t position)
|
||||
: position(position) {}
|
||||
|
||||
value(const native_function& native)
|
||||
: native(native) {}
|
||||
|
||||
~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
|
||||
@@ -10,6 +10,11 @@
|
||||
*/
|
||||
namespace furvm {
|
||||
|
||||
/**
|
||||
* @brief A byte.
|
||||
*
|
||||
* There's nothing more to it.
|
||||
*/
|
||||
using byte = std::uint8_t;
|
||||
|
||||
// constant.hpp
|
||||
@@ -47,6 +52,32 @@ enum class instruction_t : byte;
|
||||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,6 +65,13 @@ enum class instruction_t : byte {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace furvm {
|
||||
|
||||
class mod {
|
||||
friend class function;
|
||||
public:
|
||||
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
|
||||
public:
|
||||
@@ -50,9 +51,18 @@ public:
|
||||
* @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
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "furvm/context.hpp" // IWYU pragma: keep
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace furvm {
|
||||
|
||||
enum class thing_t : std::uint8_t {
|
||||
@@ -93,7 +91,6 @@ public:
|
||||
thing(const thing&) = delete;
|
||||
thing& operator=(const thing&) = delete;
|
||||
public:
|
||||
#define x(lType, rType) ((std::uint16_t)((((std::uint8_t)(lType)) << 8) | ((std::uint8_t)(rType))))
|
||||
/**
|
||||
* @brief Adds two things together.
|
||||
*
|
||||
@@ -101,16 +98,7 @@ public:
|
||||
* @param rhs Right-hand-side thing.
|
||||
* @return Shared pointer to result thing.
|
||||
*/
|
||||
friend thing_p operator+(const thing_p& lhs, const thing_p& rhs) {
|
||||
switch (x(lhs->m_type, rhs->m_type)) {
|
||||
case x(thing_t::Int32, thing_t::Int32): {
|
||||
auto res = create(lhs->m_context, thing_t::Int32);
|
||||
res->int32() = lhs->int32() + rhs->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unexpected operator");
|
||||
}
|
||||
}
|
||||
friend thing_p operator+(const thing_p& lhs, const thing_p& rhs);
|
||||
|
||||
/**
|
||||
* @brief Subtracts two things together.
|
||||
@@ -119,16 +107,7 @@ public:
|
||||
* @param rhs Right-hand-side thing.
|
||||
* @return Shared pointer to result thing.
|
||||
*/
|
||||
friend thing_p operator-(const thing_p& lhs, const thing_p& rhs) {
|
||||
switch (x(lhs->m_type, rhs->m_type)) {
|
||||
case x(thing_t::Int32, thing_t::Int32): {
|
||||
auto res = create(lhs->m_context, thing_t::Int32);
|
||||
res->int32() = lhs->int32() - rhs->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unexpected operator");
|
||||
}
|
||||
}
|
||||
friend thing_p operator-(const thing_p& lhs, const thing_p& rhs);
|
||||
|
||||
/**
|
||||
* @brief Multiplies two things together.
|
||||
@@ -137,16 +116,7 @@ public:
|
||||
* @param rhs Right-hand-side thing.
|
||||
* @return Shared pointer to result thing.
|
||||
*/
|
||||
friend thing_p operator*(const thing_p& lhs, const thing_p& rhs) {
|
||||
switch (x(lhs->m_type, rhs->m_type)) {
|
||||
case x(thing_t::Int32, thing_t::Int32): {
|
||||
auto res = create(lhs->m_context, thing_t::Int32);
|
||||
res->int32() = lhs->int32() * rhs->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unexpected operator");
|
||||
}
|
||||
}
|
||||
friend thing_p operator*(const thing_p& lhs, const thing_p& rhs);
|
||||
|
||||
/**
|
||||
* @brief Divides two things together.
|
||||
@@ -155,16 +125,7 @@ public:
|
||||
* @param rhs Right-hand-side thing.
|
||||
* @return Shared pointer to result thing.
|
||||
*/
|
||||
friend thing_p operator/(const thing_p& lhs, const thing_p& rhs) {
|
||||
switch (x(lhs->m_type, rhs->m_type)) {
|
||||
case x(thing_t::Int32, thing_t::Int32): {
|
||||
auto res = create(lhs->m_context, thing_t::Int32);
|
||||
res->int32() = lhs->int32() / rhs->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unexpected operator");
|
||||
}
|
||||
}
|
||||
friend thing_p operator/(const thing_p& lhs, const thing_p& rhs);
|
||||
|
||||
/**
|
||||
* @brief Modulos two things together.
|
||||
@@ -173,17 +134,7 @@ public:
|
||||
* @param rhs Right-hand-side thing.
|
||||
* @return Shared pointer to result thing.
|
||||
*/
|
||||
friend thing_p operator%(const thing_p& lhs, const thing_p& rhs) {
|
||||
switch (x(lhs->m_type, rhs->m_type)) {
|
||||
case x(thing_t::Int32, thing_t::Int32): {
|
||||
auto res = create(lhs->m_context, thing_t::Int32);
|
||||
res->int32() = lhs->int32() % rhs->int32();
|
||||
return res;
|
||||
}
|
||||
default: throw std::runtime_error("unexpected operator");
|
||||
}
|
||||
}
|
||||
#undef x
|
||||
friend thing_p operator%(const thing_p& lhs, const thing_p& rhs);
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a new thing.
|
||||
|
||||
+15
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
#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"
|
||||
|
||||
@@ -22,8 +23,9 @@ executor_p executor::create(const context_p& context) {
|
||||
return std::move(ex);
|
||||
}
|
||||
|
||||
void executor::push_frame(const mod_p& mod, std::size_t position) {
|
||||
m_frames.emplace((struct executor::frame){ mod, position, m_stack.size() });
|
||||
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() {
|
||||
@@ -107,6 +109,17 @@ void executor::step() {
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
case instruction_t::Return: {
|
||||
pop_frame();
|
||||
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
|
||||
|
||||
@@ -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
|
||||
+16
-3
@@ -2,28 +2,41 @@
|
||||
|
||||
#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, 6> s_bytecode = {
|
||||
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::Drop),
|
||||
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(mainModule, 0);
|
||||
executor->push_frame(mainFunction);
|
||||
|
||||
static constexpr std::size_t FPC = 3; // Frames per collection
|
||||
|
||||
|
||||
@@ -82,6 +82,65 @@ thing& thing::operator=(thing&& other) noexcept {
|
||||
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()) {
|
||||
|
||||
Reference in New Issue
Block a user