chore: flat out the file structure

This commit is contained in:
2026-09-11 18:48:16 +02:00
parent 959d0a7773
commit 3b98f77c08
78 changed files with 76 additions and 40 deletions
+36
View File
@@ -0,0 +1,36 @@
#ifndef FURAS_GEN_HPP
#define FURAS_GEN_HPP
#include "furas/lexer.hpp"
#include "furvm/module.hpp"
#include <string>
namespace furas {
struct generator_error {
enum type {
Success = 0,
Eof = 1,
UnexpectedEof = -1,
UnexpectedToken = -2,
UnknownCharacter = -3,
UnknownType = -4,
} type = Success;
std::string message;
};
class generator {
public:
struct result {
generator_error error;
furvm::mod mod;
};
public:
static result generate(lexer lexer);
};
} // namespace furas
#endif // FURAS_GEN_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURAS_LEXER_HPP
#define FURAS_LEXER_HPP
#include "furas/token.hpp"
#include "furlang/result.hpp"
#include <cstddef>
#include <string_view>
namespace furas {
struct lexer_location {
std::string_view filename;
std::size_t row, col;
};
struct lexer_error {
enum type {
EndOfFile = 0,
UnknownCharacter,
} type;
lexer_location location;
std::string message;
};
using token_r = furlang::result<lexer_error, token>;
class lexer {
public:
lexer(std::string_view filename, std::string_view content)
: m_filename(filename), m_content(content) {}
token_r next_token();
private:
constexpr lexer_location location() const { return { m_filename, m_cursor - m_lineStart, m_column }; }
private:
std::string_view m_filename;
std::string_view m_content;
std::size_t m_cursor = 0;
std::size_t m_lineStart = 0;
std::size_t m_column = 0;
};
} // namespace furas
#endif // FURAS_LEXER_HPP
+98
View File
@@ -0,0 +1,98 @@
#ifndef FURAS_TOKEN_HPP
#define FURAS_TOKEN_HPP
#include <cstdint>
#include <string_view>
namespace furas {
struct token {
enum type {
Identifier = 0, /**< An identifier. */
Signed, /**< A signed integer. */
Unsigned, /**< An unsigned integer. */
// Markers
Monkey, /**< Constant marker (`@`). */
Dolar, /**< Type marker(`$`). */
Sha256, /**< Label marker(`#`). */
Percent, /**< Variable marker(`%`). The more the better. */
EqSign, /**< `=` */
Dot, /**< . */
Colon, /**< `:` */
// Keywords
Func, /**< `func` keyword for defining functions. */
Type, /**< `type` keyword for defining types. */
Native, /**< `native` keyword for native functions. :v: */
Import, /**< `import` keyword for importing functions and types. */
Public, /**< `public` access specifier. */
Private, /**< `private` access specifier. */
Allocate, /**< `allocate` keyword for global variables. */
// Instructions
Push,
Array,
Slice,
Get,
Set,
Drop,
Dup,
Swap,
Clone,
Ref,
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Neq,
Lt,
Gt,
Le,
Ge,
Ptrof,
Sizeof,
Lenof,
Load,
Store,
LoadGlobal,
StoreGlobal,
Call,
Jmp,
Jnz,
Ret,
Count
} type = Count;
union value {
std::nullptr_t null = nullptr;
std::string_view string;
std::int64_t integer;
std::uint64_t uint;
} value;
token(enum type type)
: type(type) {}
token(enum type type, std::string_view string)
: type(type) {
value.string = string;
}
token(std::uint64_t num)
: type(Unsigned) {
value.uint = num;
}
token(std::int64_t num)
: type(Signed) {
value.integer = num;
}
};
} // namespace furas
#endif // FURAS_TOKEN_HPP
+16
View File
@@ -0,0 +1,16 @@
#ifndef FURC_BACK_FURVM_HPP
#define FURC_BACK_FURVM_HPP
#include "furc/middle/ir.hpp"
#include "furvm/module.hpp"
namespace furc {
class furvm_generator final {
public:
static furvm::mod generate(const ir_module& mod);
};
} // namespace furc
#endif // FURC_BACK_FURVM_HPP
+346
View File
@@ -0,0 +1,346 @@
#ifndef FURC_FRONT_AST_HPP
#define FURC_FRONT_AST_HPP
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
namespace furc {
struct comp_stmt_node;
struct if_stmt_node;
struct while_stmt_node;
struct return_stmt_node;
struct var_decl_node;
struct func_decl_node;
struct var_read_expr_node;
struct func_call_expr_node;
struct group_expr_node;
struct binary_op_expr_node;
struct unary_op_expr_node;
struct if_expr_node;
struct int_lit_node;
struct char_lit_node;
struct ast_visitor {
ast_visitor() = default;
virtual ~ast_visitor() = default;
ast_visitor(ast_visitor&&) noexcept = default;
ast_visitor& operator=(ast_visitor&&) noexcept = default;
ast_visitor(const ast_visitor&) = default;
ast_visitor& operator=(const ast_visitor&) = default;
virtual void visit_comp_stmt_node(const comp_stmt_node& node) {}
virtual void visit_if_stmt_node(const if_stmt_node& node) {}
virtual void visit_while_stmt_node(const while_stmt_node& node) {}
virtual void visit_return_stmt_node(const return_stmt_node& node) {}
virtual void visit_var_decl_node(const var_decl_node& node) {}
virtual void visit_func_decl_node(const func_decl_node& node) {}
virtual void visit_var_read_expr_node(const var_read_expr_node& node) {}
virtual void visit_func_call_expr_node(const func_call_expr_node& node) {}
virtual void visit_group_expr_node(const group_expr_node& node) {}
virtual void visit_binary_op_expr_node(const binary_op_expr_node& node) {}
virtual void visit_unary_op_expr_node(const unary_op_expr_node& node) {}
virtual void visit_if_expr_node(const if_expr_node& node) {}
virtual void visit_int_lit_node(const int_lit_node& node) {}
virtual void visit_char_lit_node(const char_lit_node& node) {}
};
struct ast_type {
enum type_e {
Void = 0,
S8,
U8,
S16,
U16,
S32,
U32,
S64,
U64,
} type = Void;
};
class ast_node {
public:
enum category_e {
Statement,
Declaration,
Expression,
Literal,
};
public:
ast_node() = default;
virtual ~ast_node() = default;
ast_node(ast_node&&) noexcept = default;
ast_node& operator=(ast_node&&) noexcept = default;
ast_node(const ast_node&) = delete;
ast_node& operator=(const ast_node&) = delete;
public:
virtual category_e category() const = 0;
virtual void accept(ast_visitor& visitor) const = 0;
};
using ast_node_cat = ast_node::category_e;
class stmt_node : public ast_node {
public:
enum stmt_type_e {
Declaration = 0,
Expression,
Compound,
If,
While,
Return,
};
public:
category_e category() const override { return ast_node_cat::Statement; }
virtual stmt_type_e stmt_type() const = 0;
};
struct comp_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return Compound; }
void accept(ast_visitor& visitor) const override { visitor.visit_comp_stmt_node(*this); }
std::vector<stmt_node*> stmts;
};
class expr_node;
struct if_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return If; }
void accept(ast_visitor& visitor) const override { visitor.visit_if_stmt_node(*this); }
expr_node* cond = nullptr;
stmt_node* thenBranch = nullptr;
stmt_node* elseBranch = nullptr;
};
struct while_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return While; }
void accept(ast_visitor& visitor) const override { visitor.visit_while_stmt_node(*this); }
expr_node* cond = nullptr;
stmt_node* body = nullptr;
};
struct return_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return Return; }
void accept(ast_visitor& visitor) const override { visitor.visit_return_stmt_node(*this); }
expr_node* value = nullptr;
};
class decl_node : public stmt_node {
public:
enum decl_type_e {
Variable,
Function
};
public:
category_e category() const override { return ast_node_cat::Declaration; }
stmt_type_e stmt_type() const override { return Declaration; }
virtual decl_type_e decl_type() const = 0;
};
struct var_decl_node final : public decl_node {
decl_type_e decl_type() const override { return Variable; }
void accept(ast_visitor& visitor) const override { visitor.visit_var_decl_node(*this); }
std::string name;
ast_type type;
expr_node* init = nullptr;
};
struct func_decl_node final : public decl_node {
decl_type_e decl_type() const override { return Function; }
void accept(ast_visitor& visitor) const override { visitor.visit_func_decl_node(*this); }
struct def_s {
comp_stmt_node body;
std::vector<expr_node*> preConds;
std::vector<expr_node*> postConds;
};
std::string name;
ast_type type;
std::vector<var_decl_node> params;
std::optional<def_s> def;
};
class expr_node : public stmt_node {
public:
enum expr_type_e {
Literal,
VarRead,
FunctionCall,
Group,
BinaryOp,
UnaryOp,
If,
};
public:
category_e category() const override { return ast_node_cat::Expression; }
stmt_type_e stmt_type() const override { return Expression; }
virtual expr_type_e expr_type() const = 0;
};
struct var_read_expr_node final : public expr_node {
expr_type_e expr_type() const override { return VarRead; }
void accept(ast_visitor& visitor) const override { visitor.visit_var_read_expr_node(*this); }
std::string name;
var_read_expr_node(std::string&& name)
: name(std::move(name)) {}
};
struct func_call_expr_node final : public expr_node {
expr_type_e expr_type() const override { return FunctionCall; }
void accept(ast_visitor& visitor) const override { visitor.visit_func_call_expr_node(*this); }
expr_node* lhs = nullptr;
std::vector<expr_node*> args;
};
struct group_expr_node final : public expr_node {
expr_type_e expr_type() const override { return Group; }
void accept(ast_visitor& visitor) const override { visitor.visit_group_expr_node(*this); }
expr_node* inner = nullptr;
};
struct binary_op_expr_node final : public expr_node {
enum binary_op_type {
Add = 0,
Sub,
Mul,
Div,
Mod,
Shl,
Shr,
BinAnd,
BinOr,
BinXor,
And,
Or,
Equals,
NotEquals,
LessThan,
LessEquals,
GreaterThan,
GreaterEquals,
};
expr_type_e expr_type() const override { return BinaryOp; }
void accept(ast_visitor& visitor) const override { visitor.visit_binary_op_expr_node(*this); }
expr_node* lhs = nullptr;
expr_node* rhs = nullptr;
binary_op_type type = Add;
};
struct unary_op_expr_node final : public expr_node {
enum unary_op_type {
Positive = 0,
Negative,
PreInc,
PreDec,
PostInc,
PostDec,
BinNot,
Not,
Sizeof,
Pointerof,
Lengthof,
};
expr_type_e expr_type() const override { return UnaryOp; }
void accept(ast_visitor& visitor) const override { visitor.visit_unary_op_expr_node(*this); }
expr_node* lhs = nullptr;
unary_op_type type = Positive;
};
struct if_expr_node final : public expr_node {
expr_type_e expr_type() const override { return If; }
void accept(ast_visitor& visitor) const override { visitor.visit_if_expr_node(*this); }
expr_node* cond = nullptr;
expr_node* thenExpr = nullptr;
expr_node* elseExpr = nullptr;
};
class lit_node : public expr_node {
public:
enum lit_type_e {
Integer,
Char,
};
public:
category_e category() const override { return ast_node_cat::Literal; }
expr_type_e expr_type() const override { return Literal; }
virtual lit_type_e lit_type() const = 0;
};
struct int_lit_node final : public lit_node {
int_lit_node(std::uint64_t value)
: value(value) {}
lit_type_e lit_type() const override { return Integer; }
void accept(ast_visitor& visitor) const override { visitor.visit_int_lit_node(*this); }
std::uint64_t value;
};
struct char_lit_node final : public lit_node {
char_lit_node(char value)
: value(value) {}
lit_type_e lit_type() const override { return Char; }
void accept(ast_visitor& visitor) const override { visitor.visit_char_lit_node(*this); }
char value;
};
struct ast {
std::vector<decl_node*> decls;
};
} // namespace furc
#endif // FURC_FRONT_AST_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURC_FRONT_LEXER_HPP
#define FURC_FRONT_LEXER_HPP
#include "furc/front/token.hpp"
#include <cstddef>
#include <deque>
#include <string_view>
namespace furc {
class lexer {
public:
lexer(std::string_view filepath, std::string_view content)
: m_filepath(filepath), m_content(content) {}
~lexer() = default;
lexer(lexer&&) noexcept = default;
lexer& operator=(lexer&&) noexcept = default;
lexer(const lexer&) = delete;
lexer& operator=(const lexer&) = delete;
public:
token next_token();
token peek_token(std::size_t offset = 0);
private:
token get_token();
void next();
constexpr char get(std::size_t offset = 0) const;
void skip_spaces();
constexpr token::location location() const;
private:
std::string_view m_filepath;
std::string_view m_content;
std::size_t m_cursor = 0;
std::size_t m_row = 0;
std::size_t m_lineStart = 0;
std::deque<token> m_peekToken;
};
} // namespace furc
#endif // FURC_FRONT_LEXER_HPP
+51
View File
@@ -0,0 +1,51 @@
#ifndef FURC_FRONT_PARSER_HPP
#define FURC_FRONT_PARSER_HPP
#include "furc/front/ast.hpp"
#include "furc/front/lexer.hpp"
#include "furlang/arena.hpp"
#include <stdexcept>
namespace furc {
class parser {
public:
parser(lexer&& lexer, furlang::arena& arena)
: m_lexer(std::move(lexer)), m_arena(&arena) {}
~parser() = default;
parser(parser&&) noexcept = default;
parser& operator=(parser&&) noexcept = default;
parser(const parser&) = delete;
parser& operator=(const parser&) = delete;
public:
ast parse();
private:
stmt_node* parse_stmt();
decl_node* parse_decl();
expr_node* parse_expr();
ast_type parse_type();
comp_stmt_node parse_comp();
expr_node* parse_expr_primary();
expr_node* parse_expr_unary();
expr_node* parse_expr_right(expr_node* lhs, std::uint32_t precedence = 15);
private:
template <typename... Types>
token eat_token(Types... types) {
auto token = m_lexer.next_token();
if (((token.type == types) || ...)) return token;
throw std::runtime_error("unexpected token");
}
private:
lexer m_lexer;
furlang::arena* m_arena;
};
} // namespace furc
#endif // FURC_FRONT_PARSER_HPP
+218
View File
@@ -0,0 +1,218 @@
#ifndef FURC_FRONT_TOKEN_HPP
#define FURC_FRONT_TOKEN_HPP
#include <cstddef>
#include <cstdint>
#include <ostream>
#include <string_view>
namespace furc {
struct token {
enum type {
Identifier = 0,
Integer,
String,
Char,
LParen, /**< `(` */
RParen, /**< `)` */
LBrace, /**< `{` */
RBrace, /**< `}` */
LBracket, /**< `[` */
RBracket, /**< `]` */
Semicolon, /**< `;` */
Colon, /**< `:` */
Comma, /**< `,` */
Dot, /**< `.` */
Plus, /**< `+` */
Minus, /**< `-` */
Star, /**< `*` */
Slash, /**< `/` */
Percent, /**< `%` */
DblLT, /**< `<<` */
DblGT, /**< `>>` */
Ampersand, /**< `&` */
Pipe, /**< `|` */
Hat, /**< `^` */
DblAmpersand, /**< `&&` */
DblPipe, /**< `||` */
DblPlus, /**< `++` */
DblMinus, /**< `--` */
Tilde, /**< `~` */
ExMark, /**< `!` */
CatEars, /**< `^^` */
Equals, /**< `=` */
PlusEquals, /**< `+=` */
MinusEquals, /**< `-=` */
StarEquals, /**< `*=` */
SlashEquals, /**< `/=` */
PercentEquals, /**< `%=` */
AmpersandEquals, /**< `&=` */
PipeEquals, /**< `|=` */
HatEquals, /**< `^=` */
DblEquals, /**< `==` */
ExEquals, /**< `!=` */
LessThan, /**< `<` */
LessEquals, /**< `<=` */
GreaterThan, /**< `>` */
GreaterEquals, /**< `>=` */
SlimArrow, /**< `->` */
// My brother just another me
FatArrow, /**< `=>` */
Monkey, /**< `@` */
Sha256, /**< `#` */
Func, /**< `func` */
Return, /**< `return` */
If, /**< `if` */
Else, /**< `else` */
While, /**< `while` */
Public, /**< `public` */
Private, /**< `private` */
Pre, /**< `pre` */
Post, /**< `post` */
Pointerof, /**< `pointerof` */
Sizeof, /**< `sizeof` */
Lengthof, /**< `lengthof` */
S8, /**< `s8` */
U8, /**< `u8` */
S16, /**< `s16` */
U16, /**< `u16` */
S32, /**< `s32` */
U32, /**< `u32` */
S64, /**< `s64` */
U64, /**< `u64` */
// Errors:
UnexpectedCharacter,
UnexpectedEOF,
InvalidInteger,
EndOfFile,
} type;
union value {
std::nullptr_t null = nullptr;
std::uint64_t integer;
std::string_view string;
char character;
} value;
struct location {
std::string_view filepath;
std::size_t row = 0;
std::size_t col = 0;
} loc;
token(location loc, enum type type)
: loc(loc), type(type) {}
token(location loc, std::uint64_t integer)
: loc(loc), type(Integer) {
value.integer = integer;
}
token(location loc, enum type type, std::string_view string)
: loc(loc), type(type) {
value.string = string;
}
token(location loc, enum type type, char character)
: loc(loc), type(type) {
value.character = character;
}
friend std::ostream& operator<<(std::ostream& os, const token& token) {
switch (token.type) {
case token::Identifier: return os << token.value.string;
case token::String: return os << '"' << token.value.string << '"';
case token::Char: return os << '\'' << token.value.character << '\'';
case token::Integer: return os << token.value.integer;
case token::LParen: return os << "(";
case token::RParen: return os << ")";
case token::LBrace: return os << "{";
case token::RBrace: return os << "}";
case token::LBracket: return os << "[";
case token::RBracket: return os << "]";
case token::Semicolon: return os << ";";
case token::Colon: return os << ":";
case token::Comma: return os << ",";
case token::Dot: return os << ".";
case token::Plus: return os << "+";
case token::Minus: return os << "-";
case token::Star: return os << "*";
case token::Slash: return os << "/";
case token::Percent: return os << "%";
case token::DblLT: return os << "<<";
case token::DblGT: return os << ">>";
case token::Ampersand: return os << "&";
case token::Pipe: return os << "|";
case token::Hat: return os << "^";
case token::DblAmpersand: return os << "&&";
case token::DblPipe: return os << "||";
case token::DblPlus: return os << "++";
case token::DblMinus: return os << "--";
case token::Tilde: return os << "~";
case token::ExMark: return os << "!";
case token::CatEars: return os << "^^";
case token::Equals: return os << "=";
case token::PlusEquals: return os << "+=";
case token::MinusEquals: return os << "-=";
case token::StarEquals: return os << "*=";
case token::SlashEquals: return os << "/=";
case token::PercentEquals: return os << "%=";
case token::AmpersandEquals: return os << "&=";
case token::PipeEquals: return os << "|=";
case token::HatEquals: return os << "^=";
case token::DblEquals: return os << "==";
case token::ExEquals: return os << "!=";
case token::LessThan: return os << "<";
case token::LessEquals: return os << "<=";
case token::GreaterThan: return os << ">";
case token::GreaterEquals: return os << ">=";
case token::SlimArrow: return os << "->";
case token::FatArrow: return os << "=>";
case token::Monkey: return os << "@";
case token::Sha256: return os << "#";
case token::Func: return os << "func";
case token::Return: return os << "return";
case token::If: return os << "if";
case token::Else: return os << "else";
case token::While: return os << "while";
case token::Public: return os << "public";
case token::Private: return os << "private";
case token::Pre: return os << "pre";
case token::Post: return os << "post";
case token::Pointerof: return os << "pointerof";
case token::Sizeof: return os << "sizeof";
case token::Lengthof: return os << "lengthof";
case token::S8: return os << "s8";
case token::U8: return os << "u8";
case token::S16: return os << "s16";
case token::U16: return os << "u16";
case token::S32: return os << "s32";
case token::U32: return os << "u32";
case token::S64: return os << "s64";
case token::U64: return os << "u64";
case token::UnexpectedCharacter: return os << "Unexpected character `" << token.value.character << "`";
case token::UnexpectedEOF: return os << "Unexpected End Of File";
case token::InvalidInteger: return os << "Invalid Integer";
case token::EndOfFile: return os << "End Of File";
}
return os;
}
};
using token_t = enum token::type;
} // namespace furc
#endif // FURC_FRONT_TOKEN_HPP
+455
View File
@@ -0,0 +1,455 @@
#ifndef FURC_MIDDLE_IR_HPP
#define FURC_MIDDLE_IR_HPP
#include "furc/front/ast.hpp"
#include "furlang/arena.hpp"
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <optional>
#include <stack>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furc {
struct ir_operand {
enum type_e {
Integer = 0,
Register,
Variable,
Global,
Function,
Block,
BlockPair,
PhiPair,
} type;
union value_u {
std::uint64_t integer;
struct register_s {
std::uint64_t name : 54;
std::uint64_t ver : 10;
register_s() = default;
register_s(std::uint64_t id)
: name((id >> 10) & ((1ULL << 54) - 1)), ver((id >> 0) & ((1 << 10) - 1)) {}
register_s(std::uint64_t name, std::uint64_t ver)
: name(name), ver(ver) {}
operator std::uint64_t() const { return name << 10 | ver; }
} reg;
std::uint16_t variable;
std::uint16_t global;
std::uint64_t function;
std::uint64_t block;
struct block_pair_s {
std::uint64_t first;
std::uint64_t second;
} blockPair;
struct phi_pair_s {
register_s reg;
std::uint64_t block;
} phiPair;
value_u() = default;
value_u(std::uint64_t integer)
: integer(integer) {}
value_u(std::uint16_t variable)
: variable(variable) {}
value_u(register_s reg)
: reg(reg) {}
value_u(std::uint64_t first, std::uint64_t second)
: blockPair({ first, second }) {}
value_u(register_s reg, std::uint64_t block)
: phiPair({ reg, block }) {}
} value;
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_u, Args...>>>
ir_operand(type_e type, Args&&... args)
: type(type), value(std::forward<Args>(args)...) {}
static ir_operand reg(std::uint64_t name, std::uint64_t ver) {
return { Register, value_u::register_s{ name, ver } };
}
bool operator==(const ir_operand& rhs) const {
if (type != rhs.type) return false;
switch (type) {
case Integer: return value.integer == rhs.value.integer;
case Register: return value.reg.name == rhs.value.reg.name && value.reg.ver == rhs.value.reg.ver;
case Variable: return value.variable == rhs.value.variable;
case Global: return value.global == rhs.value.global;
case Function: return value.function == rhs.value.function;
case Block: return value.block == rhs.value.block;
case BlockPair:
return value.blockPair.first == rhs.value.blockPair.first &&
value.blockPair.second == rhs.value.blockPair.second;
case PhiPair:
return value.phiPair.block == rhs.value.phiPair.block &&
value.phiPair.reg.name == rhs.value.phiPair.reg.name &&
value.phiPair.reg.ver == rhs.value.phiPair.reg.ver;
}
throw std::runtime_error("unreachable");
}
};
struct ir_type {
enum type_e {
Void = 0,
S8,
U8,
S16,
U16,
S32,
U32,
S64,
U64,
} type = Void;
};
// TODO: Add data types to instructions (like mov QWORD ... in x86 assembly)
struct ir_instruction {
enum type_e {
Move = 0,
Call,
Branch,
BranchCond,
Return,
Phi,
Add,
Sub,
Mul,
Div,
Mod,
Shl,
Shr,
BinAnd,
BinOr,
BinXor,
And,
Or,
Eq,
NotEq,
LessThan,
LessEq,
GreaterThan,
GreaterEq,
Positive,
Negative,
Increment,
Decrement,
BinNot,
Not,
Sizeof,
Pointerof,
Lenof,
} type;
std::optional<ir_operand> destination;
std::vector<ir_operand> sources;
ir_instruction(type_e type,
std::optional<ir_operand> destination = {},
std::initializer_list<ir_operand> sources = {})
: type(type), destination(destination), sources(sources) {}
static constexpr bool is_terminating(type_e type) {
switch (type) {
case Branch:
case BranchCond:
case Return: return true;
default: return false;
}
}
bool operator==(const ir_instruction& rhs) const {
return type == rhs.type && destination == rhs.destination && sources == rhs.sources;
}
};
struct ir_basic_block {
std::vector<ir_instruction> instructions;
bool is_terminated() const {
return !instructions.empty() && ir_instruction::is_terminating(instructions.back().type);
}
};
struct ir_variable {
ir_variable() = default;
ir_variable(ir_type type)
: type(type) {}
virtual ~ir_variable() = default;
ir_variable(ir_variable&&) noexcept = default;
ir_variable& operator=(ir_variable&&) noexcept = default;
ir_variable(const ir_variable&) = default;
ir_variable& operator=(const ir_variable&) = default;
ir_type type;
virtual ir_operand operand() const = 0;
};
struct ir_module_variable : ir_variable {
ir_module_variable(ir_type type, std::uint16_t name)
: ir_variable(type), name(name) {}
std::uint16_t name;
ir_operand operand() const final { return { ir_operand::Global, name }; }
};
struct ir_function_variable : ir_variable {
ir_function_variable(ir_type type, std::uint64_t name)
: ir_variable(type), name(name) {}
std::uint64_t name;
ir_operand operand() const final { return { ir_operand::Variable, name }; }
};
struct ir_scope {
ir_scope() = default;
virtual ~ir_scope() = default;
ir_scope(ir_scope&&) noexcept = default;
ir_scope& operator=(ir_scope&&) noexcept = default;
ir_scope(const ir_scope&) = default;
ir_scope& operator=(const ir_scope&) = default;
ir_scope* previous = nullptr;
std::unordered_map<std::string, ir_variable*> variables;
const ir_variable* variable(const std::string& name) const {
if (auto it = variables.find(name); it != variables.end()) return it->second;
return (previous != nullptr) ? previous->variable(name) : nullptr;
}
virtual const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) = 0;
};
struct ir_function : ir_scope {
enum type_e {
Normal = 0,
Import,
Native,
} type = Normal;
enum access_e {
Public = 0,
Private,
} access = Public;
std::string name;
std::vector<ir_type> params;
ir_type retType;
std::vector<ir_basic_block> blocks;
std::uint64_t regCount = 0;
std::uint64_t varCount = 0;
const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final {
return variables[name] = arena.allocate<ir_function_variable>(type, varCount++);
}
static ir_function from_name(std::string&& name) {
ir_function func;
func.name = std::move(name);
return func;
}
};
struct ir_module : ir_scope {
std::vector<ir_function*> functions;
furlang::arena arena;
std::uint16_t varCount = 0;
const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final {
return variables[name] = arena.allocate<ir_module_variable>(type, varCount);
}
ir_function* add_function(ir_function&& function) {
return functions.emplace_back(arena.allocate<ir_function>(std::move(function)));
}
};
struct ir_context {
ir_context(ir_function* function)
: function(function) {
if (function->blocks.empty()) new_last();
blockPtr = &function->blocks.front();
}
~ir_context() {
if (blockPtr == nullptr) return;
if (!blockPtr->is_terminated()) {
if (blockIdx + 1 == function->blocks.size()) {
add_instr(ir_instruction::Return);
} else {
add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, blockIdx + 1 });
}
}
}
ir_context(ir_context&& other) noexcept
: function(other.function), blockIdx(other.blockIdx), blockPtr(other.blockPtr) {
other.function = nullptr;
other.blockIdx = 0;
other.blockPtr = nullptr;
}
ir_context& operator=(ir_context&& other) noexcept {
if (this == &other) return *this;
function = other.function;
blockIdx = other.blockIdx;
blockPtr = other.blockPtr;
other.function = nullptr;
other.blockIdx = 0;
other.blockPtr = nullptr;
return *this;
}
ir_context(const ir_context&) = delete;
ir_context& operator=(const ir_context&) = delete;
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<ir_instruction, Args...>>>
ir_instruction& add_instr(Args&&... args) {
auto it = blockPtr->instructions.end();
if (!blockPtr->instructions.empty() && ir_instruction::is_terminating(blockPtr->instructions.back().type)) --it;
it = blockPtr->instructions.emplace(it, std::forward<Args>(args)...);
if (ir_instruction::is_terminating(it->type) && it + 1 != blockPtr->instructions.end())
blockPtr->instructions.pop_back();
return *it;
}
void terminate() { add_instr(ir_instruction::Return); }
void terminate(ir_operand value) {
ir_instruction instr = { ir_instruction::Return };
instr.sources.emplace_back(value);
add_instr(std::move(instr));
}
void terminate(std::uint64_t block) { add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, block }); }
ir_instruction* terminate(ir_operand cond, std::uint64_t thenBranch, std::uint64_t elseBranch) {
return &add_instr(ir_instruction{ ir_instruction::BranchCond,
ir_operand{ ir_operand::BlockPair, thenBranch, elseBranch },
{ cond } });
}
ir_context& new_next() {
if (blockPtr->instructions.empty()) return *this;
auto it = function->blocks.begin() + static_cast<std::ptrdiff_t>(++blockIdx);
if (!blockPtr->is_terminated()) terminate(blockIdx);
blockPtr = &*function->blocks.emplace(it);
return *this;
}
ir_context& new_last() {
blockIdx = function->blocks.size();
blockPtr = &*function->blocks.emplace(function->blocks.end());
return *this;
}
ir_context& go(std::uint64_t block) {
blockIdx = std::min(block, function->blocks.size() - 1);
blockPtr = function->blocks.data() + static_cast<std::ptrdiff_t>(blockIdx);
return *this;
}
ir_context& go_next() { return go(blockIdx + 1); }
ir_context& go_last() { return go(std::min<std::uint64_t>(0, function->blocks.size() - 1)); }
ir_operand last_register() const { return { ir_operand::Register, function->regCount - 1 }; }
ir_operand next_register() const { return { ir_operand::Register, function->regCount++ }; }
static ir_operand block_op(std::uint64_t blockIdx) { return { ir_operand::Block, blockIdx }; }
ir_function* function = nullptr;
std::uint64_t blockIdx = 0;
ir_basic_block* blockPtr = nullptr;
};
class ir_generator final : public ast_visitor {
public:
ir_generator()
: m_initContext(m_module.add_function(ir_function::from_name("module$init"))) {}
void finalize() {
m_module.functions.front()->blocks.emplace_back().instructions.push_back(
ir_instruction{ ir_instruction::Return });
}
ir_module build() {
m_scope = nullptr;
m_context = {};
m_initContext.blockPtr = nullptr;
return std::move(m_module);
}
static ir_module generate(const ast_node& node) {
ir_generator gen;
node.accept(gen);
gen.finalize();
return gen.build();
}
static ir_module generate(const ast& tree) {
ir_generator gen;
for (const auto& node : tree.decls)
node->accept(gen);
gen.finalize();
return gen.build();
}
private:
void visit_comp_stmt_node(const comp_stmt_node& node) override;
void visit_if_stmt_node(const if_stmt_node& node) override;
void visit_while_stmt_node(const while_stmt_node& node) override;
void visit_return_stmt_node(const return_stmt_node& node) override;
void visit_var_decl_node(const var_decl_node& node) override;
void visit_func_decl_node(const func_decl_node& node) override;
void visit_var_read_expr_node(const var_read_expr_node& node) override;
void visit_func_call_expr_node(const func_call_expr_node& node) override;
void visit_group_expr_node(const group_expr_node& node) override;
void visit_binary_op_expr_node(const binary_op_expr_node& node) override;
void visit_unary_op_expr_node(const unary_op_expr_node& node) override;
void visit_if_expr_node(const if_expr_node& node) override;
void visit_int_lit_node(const int_lit_node& node) override;
void visit_char_lit_node(const char_lit_node& node) override;
private:
ir_context& context() { return m_context.top(); }
private:
ir_module m_module;
ir_scope* m_scope = &m_module;
std::stack<ir_context> m_context;
ir_context m_initContext;
};
} // namespace furc
#endif // FURC_MIDDLE_IR_HPP
+177
View File
@@ -0,0 +1,177 @@
/**
* SSA destruction (out-of-SSA phase) for register-based targets based on "Mechanizing Conventional SSA for a
* Verified Destruction with Coalescing" by Delphine Demange and Yon Fernandez de Retana
* (https://dl.acm.org/doi/pdf/10.1145/2892208.2892222 09/11/2026).
*/
#ifndef FURC_MIDDLE_REG_GEN_HPP
#define FURC_MIDDLE_REG_GEN_HPP
#include "furc/middle/ir.hpp"
#include "furc/middle/ssa.hpp"
#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace furc {
class reg_gen {
public:
class disjoint_set {
public:
std::uint64_t find(std::uint64_t var) {
if (m_parents.find(var) == m_parents.end()) m_parents.emplace(var, var);
if (m_parents[var] == var) return var;
return find(m_parents[var]);
}
void unite(std::uint64_t var1, std::uint64_t var2) {
std::uint64_t rep1 = find(var1);
std::uint64_t rep2 = find(var2);
if (rep1 != rep2) m_parents[rep1] = rep2;
}
private:
std::unordered_map<std::uint64_t, std::uint64_t> m_parents;
};
public:
struct block_info {
std::unordered_set<std::uint64_t> defs;
std::unordered_set<std::uint64_t> uses;
std::unordered_set<std::uint64_t> liveIn;
std::unordered_set<std::uint64_t> liveOut;
};
public:
reg_gen(ir_function& func, ssa& ssa) {
std::vector<block_info> lifeBlocks;
live_analysis(lifeBlocks, func.blocks, ssa.cfgBlocks);
remove_interference(func.blocks, lifeBlocks);
merge(func.blocks);
}
public:
static void live_analysis(std::vector<block_info>& lifeBlocks,
const std::vector<ir_basic_block>& irBlocks,
const std::vector<ssa::cfg_block>& cfgBlocks) {
lifeBlocks.resize(irBlocks.size());
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
const auto& irBlock = irBlocks[i];
auto& block = lifeBlocks[i];
for (const auto& instr : irBlock.instructions) {
for (const auto& op : instr.sources) {
if (op.type != ir_operand::Register) continue;
if (block.defs.find(op.value.reg) != block.defs.end()) continue;
block.uses.insert(op.value.reg);
}
if (!instr.destination.has_value() || instr.destination->type != ir_operand::Register) continue;
block.defs.insert(instr.destination->value.reg);
}
}
bool changed = true;
while (changed) {
changed = false;
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
const auto& irBlock = irBlocks[i];
auto& block = lifeBlocks[i];
std::unordered_set<std::uint64_t> newSet;
for (auto succ : cfgBlocks[i].sucs) {
newSet.insert(lifeBlocks[succ].liveIn.begin(), lifeBlocks[succ].liveIn.end());
}
if (newSet != block.liveOut) {
block.liveOut = newSet;
changed = true;
}
newSet.clear();
newSet.insert(block.uses.begin(), block.uses.end());
for (const auto& var : block.liveOut) {
if (block.defs.find(var) != block.defs.end()) continue;
newSet.insert(var);
}
if (newSet != block.liveIn) {
block.liveIn = newSet;
changed = true;
}
}
}
}
static void remove_interference(std::vector<ir_basic_block>& irBlocks, const std::vector<block_info>& lifeBlocks) {
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
auto& irBlock = irBlocks[i];
for (auto it = irBlock.instructions.begin(), end = irBlock.instructions.end();
it != end && it->type == ir_instruction::Phi;
++it) {
assert(it->destination.has_value() && it->destination->type == ir_operand::Register);
const auto& phiDst = it->destination->value.reg;
for (auto& op : it->sources) {
assert(op.type == ir_operand::PhiPair);
const auto& predBlock = lifeBlocks[op.value.phiPair.block];
auto& phiArg = op.value.phiPair.reg;
if (predBlock.liveOut.count(phiArg) == 0 || phiArg == phiDst) continue;
auto oldArg = phiArg;
phiArg.ver = 0; // TODO: Allocate temporary registers
auto& irBlock = irBlocks[op.value.phiPair.block];
assert(!irBlock.instructions.empty());
auto it = irBlock.instructions.end() - 1;
if (ir_instruction::is_terminating(it->type)) --it;
irBlock.instructions.emplace(it,
ir_instruction{ ir_instruction::Move,
ir_operand::reg(phiArg.name, phiArg.ver),
{ ir_operand::reg(oldArg.name, oldArg.ver) } });
}
}
}
}
static void merge(std::vector<ir_basic_block>& irBlocks) {
disjoint_set dj;
for (const auto& block : irBlocks) {
for (const auto& instr : block.instructions) {
if (instr.type != ir_instruction::Phi) break;
assert(instr.destination.has_value() && instr.destination->type == ir_operand::Register);
const auto& phiDst = instr.destination->value.reg;
dj.find(phiDst);
for (const auto& op : instr.sources) {
assert(op.type == ir_operand::PhiPair);
const auto& predBlock = op.value.phiPair.block;
const auto& phiArg = op.value.phiPair.reg;
dj.unite(phiDst, phiArg);
}
}
}
for (auto& block : irBlocks) {
auto it = block.instructions.begin();
while (it != block.instructions.end() && it->type == ir_instruction::Phi) {
it = block.instructions.erase(it);
}
for (; it != block.instructions.end(); ++it) {
for (auto& op : it->sources) {
if (op.type != ir_operand::Register) continue;
op.value.reg = dj.find(op.value.reg);
}
if (!it->destination.has_value() || it->destination->type != ir_operand::Register) continue;
it->destination->value.reg = dj.find(it->destination->value.reg);
}
}
}
};
} // namespace furc
#endif // FURC_MIDDLE_REG_GEN_HPP
+101
View File
@@ -0,0 +1,101 @@
#ifndef FURC_MIDDLE_SSA_HPP
#define FURC_MIDDLE_SSA_HPP
#include "furc/middle/ir.hpp"
#include <cassert>
#include <limits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace furc {
class ssa {
public:
struct cfg_block {
std::unordered_set<std::uint64_t> preds;
std::unordered_set<std::uint64_t> sucs;
};
struct ssa_block {
std::size_t order = 0;
std::uint64_t idom = -1;
std::unordered_set<std::uint64_t> children; // Children of the block in dominator tree
// Dominance Frontiers
std::unordered_set<std::uint64_t> df;
};
struct register_info {
std::unordered_set<std::uint64_t> sites; // Definition Sites
};
public:
ssa(ir_function& func) {
registers.resize(func.regCount);
compute_cfg(func.blocks, cfgBlocks);
collect_registers(func.blocks, registers, globals);
std::vector<std::uint64_t> order;
compute_rpo(cfgBlocks, ssaBlocks, order);
build_dtree(cfgBlocks, ssaBlocks, order);
compute_dfrontiers(cfgBlocks, ssaBlocks);
ssaification(func.blocks, cfgBlocks, ssaBlocks, registers, globals);
rename(func.blocks, func.regCount, cfgBlocks, ssaBlocks, order);
}
public:
static void compute_cfg(const std::vector<ir_basic_block>& irBlocks, std::vector<cfg_block>& cfgBlocks);
static void collect_registers(const std::vector<ir_basic_block>& irBlocks,
std::vector<register_info>& registers,
std::unordered_set<std::uint64_t>& globals);
static void build_dtree(const std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
const std::vector<std::size_t>& order);
static void compute_dfrontiers(const std::vector<cfg_block>& cfgBlocks, std::vector<ssa_block>& ssaBlocks);
static void compute_rpo(std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
std::vector<std::size_t>& order);
static void ssaification(std::vector<ir_basic_block>& irBlocks,
const std::vector<cfg_block>& cfgBlocks,
const std::vector<ssa_block>& ssaBlocks,
const std::vector<register_info>& registers,
const std::unordered_set<std::uint64_t>& globals);
static void rename(std::vector<ir_basic_block>& irBlocks,
std::size_t regCount,
const std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
const std::vector<std::uint64_t>& order);
private:
static void rename_rec(std::vector<std::uint64_t>& counters,
std::vector<std::stack<std::uint64_t>>& stacks,
std::vector<ir_basic_block>& irBlocks,
const std::vector<cfg_block>& cfgBlocks,
const std::vector<ssa_block>& ssaBlocks,
std::size_t blockIdx);
private:
static void rpo_dfs(std::unordered_set<std::size_t>& visited,
std::vector<std::size_t>& order,
std::size_t block,
const std::vector<cfg_block>& blocks);
static std::size_t intersect(std::vector<ssa_block>& m_blocks, std::size_t b1, std::size_t b2);
public:
std::vector<cfg_block> cfgBlocks;
std::vector<ssa_block> ssaBlocks;
std::vector<register_info> registers;
std::unordered_set<std::uint64_t> globals;
};
} // namespace furc
#endif // FURC_MIDDLE_SSA_HPP
+160
View File
@@ -0,0 +1,160 @@
#ifndef FURLANG_ARENA_HPP
#define FURLANG_ARENA_HPP
#include <cstddef>
#include <cstdint>
#include <memory>
#include <type_traits>
namespace furlang {
/**
* @brief An arena (region) allocator implementation.
*/
class arena {
private:
struct region {
using value_type = std::uintptr_t;
static region* create(std::size_t capacity);
region* next;
std::size_t capacity;
std::size_t used;
value_type storage[];
std::size_t free() const { return capacity - used; }
};
public:
/**
* @brief Construct a new arena.
*
* @param minCapacity Minimal capacity of a single region in words.
*/
arena(std::size_t minCapacity = 4 * 1024ULL);
~arena();
/**
* @brief Move constructor
*/
arena(arena&& other) noexcept;
arena(const arena&) = delete;
/**
* @brief Move constructor
*/
arena& operator=(arena&& other) noexcept;
arena& operator=(const arena&) = delete;
public:
/**
* @brief Allocates and default constructs objects.
*
* @tparam T Type of the objects.
* @param count How many objects to allocate.
* @return A pointer to the allocated objects.
*/
template <typename T, typename = std::enable_if_t<std::is_default_constructible_v<T>>>
T* allocate(std::size_t count) {
T* allocated = reinterpret_cast<T*>(allocate(sizeof(T), count));
for (std::size_t i = 0; i < count; ++i) {
new (&allocated[i]) T();
}
return allocated;
}
/**
* @brief Allocates and constructs an object.
*
* @tparam T Type of the object.
* @param args Arguments passed to the object's constructor.
* @return A pointer to the allocated object.
*/
template <typename T, typename... Args, typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
T* allocate(Args&&... args) {
T* allocated = reinterpret_cast<T*>(allocate(sizeof(T), 1));
new (allocated) T(std::forward<Args>(args)...);
return allocated;
}
/**
* @brief Allocates and constructs an object.
*
* @tparam T Type of the object.
* @param args Arguments passed to the object's constructor.
* @return A shared pointer to the allocated object.
*/
template <typename T, typename... Args>
std::shared_ptr<T> allocate_shared(Args&&... args) {
T* allocated = allocate<T>(std::forward<Args>(args)...);
return std::shared_ptr<T>(allocated, [](T* object) { object->~T(); });
}
/**
* @brief Resets the arena.
*
* Resets occupied size of regions. Using previously allocated pointers after calling this function is
* undefined-behaviour.
*/
void reset();
private:
void* allocate(std::size_t size, std::size_t count);
private:
std::size_t m_minCapacity;
region* m_head = nullptr;
region* m_tail = nullptr;
};
template <typename T>
class arena_allocator {
template <typename>
friend class arena_allocator;
public:
using value_type = T;
public:
explicit arena_allocator(arena& arena) noexcept
: m_arena(&arena) {}
template <typename U>
arena_allocator(const arena_allocator<U>& other) noexcept
: m_arena(other.m_arena) {}
template <typename U>
arena_allocator& operator=(const arena_allocator<U>& other) noexcept {
if (this == &other) return *this;
m_arena = other.m_arena;
return *this;
}
template <typename U>
arena_allocator(arena_allocator<U>&& other) noexcept
: m_arena(std::move(other.m_arena)) {}
template <typename U>
arena_allocator& operator=(arena_allocator<U>&& other) noexcept {
if (this == &other) return *this;
m_arena = std::move(other.m_arena);
return *this;
}
public:
[[nodiscard]] T* allocate(std::size_t count = 1) { return m_arena->allocate<T>(count); }
void deallocate(T* ptr, std::size_t count) noexcept {}
public:
template <typename U>
bool operator==(const arena_allocator<U>& other) const noexcept {
return m_arena == other.m_arena;
}
template <typename U>
bool operator!=(const arena_allocator<U>& other) const noexcept {
return m_arena != other.m_arena;
}
private:
arena* m_arena;
};
} // namespace furlang
#endif // FURLANG_ARENA_HPP
+11
View File
@@ -0,0 +1,11 @@
#ifndef FURLANG_HPP
#define FURLANG_HPP
#include "furlang/result.hpp" // IWYU pragma: export
/**
* @brief The common furlang library.
*/
namespace furlang {}
#endif // FURLANG_HPP
+415
View File
@@ -0,0 +1,415 @@
#ifndef FURLANG_RESULT_HPP
#define FURLANG_RESULT_HPP
#include <exception>
#include <optional>
#include <ostream>
#include <type_traits>
#include <utility>
namespace furlang {
/**
* @brief Bad result access exception.
*/
class bad_result_access : public std::exception {
public:
bad_result_access() = default;
~bad_result_access() override = default;
/**
* @brief Move constructor.
*/
bad_result_access(bad_result_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_result_access& operator=(bad_result_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_result_access(const bad_result_access&) = default;
/**
* @brief Copy constructor.
*/
bad_result_access& operator=(const bad_result_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 result access"; }
};
struct error_tag {};
/**
* @brief Result.
*
* Result stores either value or error.
*
* @tparam R Value type.
* @tparam E Error type.
*/
template <typename E, typename R = void>
class result {
public:
using value_type = std::remove_reference_t<R>; /**< Value type. */
using value_reference = value_type&; /**< Value reference type. */
using value_const_reference = const value_type&; /**< Value const reference type. */
using value_pointer = value_type*; /**< Value pointer type. */
using value_const_pointer = const value_type*; /**< Value const pointer type. */
using error_type = std::remove_reference_t<E>; /**< Error type. */
using error_reference = error_type&; /**< Error reference type. */
using error_const_reference = const error_type&; /**< Error const reference type. */
public:
template <typename Other>
result(const result<E, Other>& error)
: result(error_tag{}, error.error()) {}
/**
* @brief Construct a new result.
*
* @param value Value to copy.
*/
result(const value_type& value) { new (&m_value.result) value_type(value); }
/**
* @brief Construct a new result.
*
* @param value Value to move.
*/
result(value_type&& value) { new (&m_value.result) value_type(std::move(value)); }
/**
* @brief Construct a new result.
*
* @param args Variadic arguments to construct the value with.
*/
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
result(Args&&... args) {
new (&m_value.result) value_type(std::forward<Args>(args)...);
}
/**
* @brief Construct a new error result.
*
* @param error Error to copy.
*/
result(error_tag tag, const error_type& error)
: m_error(true) {
new (&m_value.error) error_type(error);
}
/**
* @brief Construct a new error result.
*
* @param error Error to move.
*/
result(error_tag tag, error_type&& error)
: m_error(true) {
new (&m_value.error) error_type(std::move(error));
}
~result() {
if (m_error) {
m_value.error.~error_type();
} else {
m_value.result.~value_type();
}
}
/**
* @brief Move constructor.
*/
result(result&& other) noexcept
: m_error(other.m_error) {
if (m_error) {
new (&m_value.error) error_type(std::move(other.m_value.error));
} else {
new (&m_value.result) value_type(std::move(other.m_value.result));
}
}
/**
* @brief Move constructor.
*/
result& operator=(result&& other) noexcept {
if (this == &other) return *this;
m_error = other.m_error;
if (m_error) {
new (&m_value.error) error_type(std::move(other.m_value.error));
} else {
new (&m_value.result) value_type(std::move(other.m_value.result));
}
return *this;
}
/**
* @brief Copy constructor.
*/
result(const result& other)
: m_error(other.m_error) {
if (m_error) {
new (&m_value.error) error_type(other.m_value.error);
} else {
new (&m_value.result) value_type(other.m_value.result);
}
}
/**
* @brief Copy constructor.
*/
result& operator=(const result& other) {
if (this == &other) return *this;
m_error = other.m_error;
if (m_error) {
new (&m_value.error) error_type(other.m_value.error);
} else {
new (&m_value.result) value_type(other.m_value.result);
}
return *this;
}
public:
template <typename ResultFwd, typename = std::enable_if_t<std::is_constructible_v<R, ResultFwd>>>
static result ok(ResultFwd&& value) {
return { std::forward<ResultFwd>(value) };
}
template <typename ErrorFwd, typename = std::enable_if_t<std::is_constructible_v<E, ErrorFwd>>>
static result error(ErrorFwd&& value) {
return { error_tag{}, std::forward<ErrorFwd>(value) };
}
public:
/**
* @brief Checks if this result contains a value.
*
* @return true if contains a value.
*/
operator bool() const { return !m_error; }
/**
* @brief Checks if this result contains an error.
*
* @return true if contains an error.
*/
bool operator!() const { return m_error; }
/**
* @brief Compares two results for equality.
*
* @param rhs Result to compare against.
* @return true if the results are equal.
*/
bool operator==(const result& rhs) const {
return m_error == rhs.m_error && m_error ? m_value.error == rhs.m_value.error
: m_value.result == rhs.m_value.result;
}
/**
* @brief Compares two results for inequality.
*
* @param rhs Result to compare against.
* @return true if the results are not equal.
*/
bool operator!=(const result& rhs) const { return !this->operator==(rhs); }
/**
* @brief Compares a result with a value for equality.
*
* @param rhs Value to compare against.
* @return true if the values are equal.
*/
bool operator==(const value_type& rhs) const { return !m_error && m_value.result == rhs; }
/**
* @brief Compares a result with a value for inequality.
*
* @param rhs Value to compare against.
* @return true if the values are not equal.
*/
bool operator!=(const value_type& rhs) const { return !this->operator==(rhs); }
/**
* @brief Prints a result to an output stream.
*
* @param os Output stream.
* @param result Result to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const result& result) {
return result.has_value() ? os << result.value() : os << result.error();
}
/**
* @brief Returns a reference to value.
*
* @return Reference to the value.
*/
value_reference operator*() { return value(); }
/**
* @brief Returns a reference to value.
*
* @return Const reference to the value.
*/
value_const_reference operator*() const { return value(); }
/**
* @brief Returns a pointer to value.
*
* @return Pointer to the value.
*/
value_pointer operator->() { return &value(); }
/**
* @brief Returns a pointer to value.
*
* @return Const pointer to the value.
*/
value_const_pointer operator->() const { return &value(); }
public:
/**
* @brief Checks if this result has a value.
*
* @return true if has a value.
*/
bool has_value() const { return !m_error; }
/**
* @brief Checks if this result has an error.
*
* @return true if has an error.
*/
bool has_error() const { return m_error; }
/**
* @brief Returns a reference to value.
*
* @return Reference to the value.
*/
value_reference value() {
if (m_error) throw bad_result_access();
return m_value.result;
}
/**
* @brief Returns a reference to value.
*
* @return Const reference to the value.
*/
value_const_reference value() const {
if (m_error) throw bad_result_access();
return m_value.result;
}
/**
* @brief Returns a reference to error.
*
* @return Reference to the error.
*/
error_reference error() {
if (!m_error) throw bad_result_access();
return m_value.error;
}
/**
* @brief Returns a reference to error.
*
* @return Const reference to the error.
*/
error_const_reference error() const {
if (!m_error) throw bad_result_access();
return m_value.error;
}
public:
/**
* @brief Sets this results value.
*
* @param value Value to copy.
*/
void set_value(const value_type& value) {
if (m_error) m_value.error.~error_type();
new (&m_value.result) value_type(value);
}
/**
* @brief Sets this results value.
*
* @param value Value to move.
*/
void set_value(value_type&& value) {
if (m_error) m_value.error.~error_type();
new (&m_value.result) value_type(std::move(value));
}
/**
* @brief Sets this results error.
*
* @param error Error to copy.
*/
void set_error(const error_type& error) {
if (!m_error) m_value.result.~value_type();
new (&m_value.error) error_type(error);
}
/**
* @brief Sets this results error.
*
* @param error Error to move.
*/
void set_error(error_type&& error) {
if (!m_error) m_value.result.~value_type();
new (&m_value.error) error_type(std::move(error));
}
private:
union value {
value_type result;
error_type error;
value() {}
~value() {}
value(value&&) noexcept {}
value& operator=(value&&) noexcept {}
value(const value&) {}
value& operator=(const value&) {}
} m_value;
bool m_error = false;
};
template <typename E>
class result<E, void> {
public:
using value_type = std::remove_reference_t<E>;
using reference = value_type&;
using const_reference = const value_type&;
public:
result() = default;
result(const value_type& value)
: m_error(true), m_value(value) {}
result(value_type&& value)
: m_error(true), m_value(std::move(value)) {}
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
result(Args&&... args)
: m_error(true), m_value(std::forward<Args>(args)...) {}
public:
bool has_value() const { return !m_error; }
bool has_error() const { return m_error; }
const value_type& error() const { return *m_value; }
private:
std::optional<value_type> m_value;
bool m_error = false;
};
} // namespace furlang
#endif // FURLANG_RESULT_HPP
+58
View File
@@ -0,0 +1,58 @@
#ifndef FURLANG_SERIALIZATION_CODEC_HPP
#define FURLANG_SERIALIZATION_CODEC_HPP
#include "furlang/result.hpp"
#include "furlang/serialization/io.hpp"
#include <type_traits>
#include <utility>
namespace furlang {
namespace serialization {
template <typename Codec, typename T>
using codec_encode_result_t = decltype(std::declval<Codec>().encode(std::declval<writer&>(), std::declval<const T&>()));
template <typename Codec, typename T>
using codec_decode_result_t = decltype(std::declval<Codec>().decode(std::declval<reader&>()));
template <typename Codec, typename T, typename = void>
struct is_codec : std::false_type {};
template <typename Codec, typename T>
struct is_codec<Codec, T, std::void_t<codec_encode_result_t<Codec, T>, codec_decode_result_t<Codec, T>>>
: std::true_type {};
template <typename Codec, typename T>
constexpr bool is_codec_v = is_codec<Codec, T>::value;
template <typename Codec, typename T, typename = std::enable_if_t<is_codec_v<Codec, T>>>
result<error> encode(Codec& codec, writer& writer, const T& value) {
return codec.encode(writer, value);
}
template <typename Codec, typename T, typename = std::enable_if_t<is_codec_v<Codec, T>>>
result<error, T> decode(Codec& codec, reader& reader) {
return codec.decode(reader);
}
template <typename T, typename = void>
class codec;
template <typename T>
struct codec<T, std::enable_if_t<std::is_integral_v<T>>> {
result<error> encode(writer& writer, const T& value) { return writer.write_int(value); }
result<error, T> decode(reader& reader) { return reader.read_int(T{}); }
};
template <>
struct codec<std::string> {
result<error> encode(writer& writer, const std::string& value) { return writer.write_string(value); }
result<error, std::string> decode(reader& reader) { return reader.read_string(); }
};
} // namespace serialization
} // namespace furlang
#endif // FURLANG_SERIALIZATION_CODEC_HPP
+33
View File
@@ -0,0 +1,33 @@
#ifndef FURLANG_SERIALIZATION_ERROR_HPP
#define FURLANG_SERIALIZATION_ERROR_HPP
#include <cstddef>
#include <string>
namespace furlang {
namespace serialization {
enum class error_code {
EndOfFile,
InvalidData,
InvalidTag,
InvalidVersion,
IntegerOverflow,
SizeLimit,
DuplicateId,
UnknownId,
TypeMismatch,
Unsupported,
};
struct error {
error_code code;
std::string message;
std::size_t offset = 0;
};
} // namespace serialization
} // namespace furlang
#endif // FURLANG_SERIALIZATION_ERROR_HPP
+190
View File
@@ -0,0 +1,190 @@
#ifndef FURLANG_SERIALIZATION_IO_HPP
#define FURLANG_SERIALIZATION_IO_HPP
#include "furlang/result.hpp"
#include "furlang/serialization/error.hpp"
#include <cstdint>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
namespace furlang {
namespace serialization {
class writer {
public:
writer() = default;
virtual ~writer() = default;
writer(writer&&) noexcept = default;
writer& operator=(writer&&) noexcept = default;
writer(const writer&) = default;
writer& operator=(const writer&) = default;
public:
virtual result<error> write_s8(std::int8_t value) = 0;
virtual result<error> write_u8(std::uint8_t value) = 0;
virtual result<error> write_s16(std::int16_t value) = 0;
virtual result<error> write_u16(std::uint16_t value) = 0;
virtual result<error> write_s32(std::int32_t value) = 0;
virtual result<error> write_u32(std::uint32_t value) = 0;
virtual result<error> write_s64(std::int64_t value) = 0;
virtual result<error> write_u64(std::uint64_t value) = 0;
result<error> write_int(std::int8_t value) { return write_s8(value); }
result<error> write_int(std::uint8_t value) { return write_u8(value); }
result<error> write_int(std::int16_t value) { return write_s16(value); }
result<error> write_int(std::uint16_t value) { return write_u16(value); }
result<error> write_int(std::int32_t value) { return write_s32(value); }
result<error> write_int(std::uint32_t value) { return write_u32(value); }
result<error> write_int(std::int64_t value) { return write_s64(value); }
result<error> write_int(std::uint64_t value) { return write_u64(value); }
virtual result<error> write_string(const char* string) = 0;
virtual result<error> write_string(std::string_view string) = 0;
virtual result<error> write_string(const std::string& string) = 0;
};
class reader {
public:
reader() = default;
virtual ~reader() = default;
reader(reader&&) noexcept = default;
reader& operator=(reader&&) noexcept = default;
reader(const reader&) = default;
reader& operator=(const reader&) = default;
public:
virtual result<error, std::int8_t> read_s8() = 0;
virtual result<error, std::uint8_t> read_u8() = 0;
virtual result<error, std::int16_t> read_s16() = 0;
virtual result<error, std::uint16_t> read_u16() = 0;
virtual result<error, std::int32_t> read_s32() = 0;
virtual result<error, std::uint32_t> read_u32() = 0;
virtual result<error, std::int64_t> read_s64() = 0;
virtual result<error, std::uint64_t> read_u64() = 0;
result<error, std::int8_t> read_int(std::int8_t) { return read_s8(); }
result<error, std::uint8_t> read_int(std::uint8_t) { return read_u8(); }
result<error, std::int16_t> read_int(std::int16_t) { return read_s16(); }
result<error, std::uint16_t> read_int(std::uint16_t) { return read_u16(); }
result<error, std::int32_t> read_int(std::int32_t) { return read_s32(); }
result<error, std::uint32_t> read_int(std::uint32_t) { return read_u32(); }
result<error, std::int64_t> read_int(std::int64_t) { return read_s64(); }
result<error, std::uint64_t> read_int(std::uint64_t) { return read_u64(); }
virtual result<error, std::string> read_string() = 0;
virtual std::size_t offset() const = 0;
};
enum class endianness {
Little = 0,
Big = 1,
};
class byte_writer : public writer {
public:
byte_writer(endianness endianness = endianness::Big)
: m_endianness(endianness) {}
public:
result<error> write_s8(std::int8_t value) override;
result<error> write_u8(std::uint8_t value) override;
result<error> write_s16(std::int16_t value) override;
result<error> write_u16(std::uint16_t value) override;
result<error> write_s32(std::int32_t value) override;
result<error> write_u32(std::uint32_t value) override;
result<error> write_s64(std::int64_t value) override;
result<error> write_u64(std::uint64_t value) override;
result<error> write_string(const char* string) override;
result<error> write_string(std::string_view string) override;
result<error> write_string(const std::string& string) override;
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
result<error> write_integral_le(T value) {
return write_integral_le(value, std::make_index_sequence<sizeof(T)>{});
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>, std::size_t... I>
result<error> write_integral_le(T value, std::index_sequence<I...>) {
auto usig = static_cast<std::make_unsigned_t<T>>(value);
(m_bytes.push_back(usig >> (I * 8)), ...);
return {};
}
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void write_integral_be(T value) {
write_integral_be(value, std::make_index_sequence<sizeof(T)>{});
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>, std::size_t... I>
void write_integral_be(T value, std::index_sequence<I...>) {
auto usig = static_cast<std::make_unsigned_t<T>>(value);
(m_bytes.push_back(usig >> ((sizeof(T) - 1 - I) * 8)), ...);
}
private:
endianness m_endianness;
std::vector<std::uint8_t> m_bytes;
};
class byte_reader : public reader {
public:
byte_reader(const std::uint8_t* bytes, std::size_t length, endianness endianness = endianness::Big)
: m_endianness(endianness), m_bytes(bytes), m_length(length) {}
public:
result<error, std::int8_t> read_s8() override;
result<error, std::uint8_t> read_u8() override;
result<error, std::int16_t> read_s16() override;
result<error, std::uint16_t> read_u16() override;
result<error, std::int32_t> read_s32() override;
result<error, std::uint32_t> read_u32() override;
result<error, std::int64_t> read_s64() override;
result<error, std::uint64_t> read_u64() override;
result<error, std::string> read_string() override;
std::size_t offset() const override;
private:
result<error, std::uint8_t> read_byte() {
if (m_offset >= m_length)
return result<error, std::uint8_t>::error(error{ error_code::EndOfFile, "", m_offset });
return { m_bytes[m_offset++] };
}
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
result<error, T> read_integral_le() {
using U = std::make_unsigned_t<T>;
U usig = 0;
for (std::size_t i = 0; i < sizeof(T); ++i) {
auto res = read_byte();
if (res.has_error()) return res;
usig |= static_cast<U>(res.value()) << (i * 8);
}
return { static_cast<T>(usig) };
}
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
result<error, T> read_integral_be() {
using U = std::make_unsigned_t<T>;
U usig = 0;
for (std::size_t i = 0; i < sizeof(T); ++i) {
auto res = read_byte();
if (res.has_error()) return res;
usig |= static_cast<U>(res.value()) << ((sizeof(T) - 1 - i) * 8);
}
return { static_cast<T>(usig) };
}
private:
endianness m_endianness;
const std::uint8_t* m_bytes;
std::size_t m_length;
std::size_t m_offset = 0;
};
} // namespace serialization
} // namespace furlang
#endif // FURLANG_SERIALIZATION_IO_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURLANG_UTILITY_HASH_HPP
#define FURLANG_UTILITY_HASH_HPP
#include <cstddef>
#include <functional>
namespace furlang {
namespace utility {
// Source - https://stackoverflow.com/a/27952689
// Posted by Yakk - Adam Nevraumont, modified by community. See post 'Timeline' for change history
// Retrieved 2026-07-07, License - CC BY-SA 4.0
static inline std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
if constexpr (sizeof(std::size_t) >= 8) {
lhs ^= rhs + 0x517cc1b727220a95 + (lhs << 6) + (lhs >> 2);
} else {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
}
return lhs;
}
// Source - https://stackoverflow.com/a/20602159
// Posted by Casey, modified by community. See post 'Timeline' for change history
// Retrieved 2026-07-07, License - CC BY-SA 3.0
template <typename T, typename U, typename FirstHash = std::hash<T>, typename SecondHash = std::hash<U>>
struct pair_hash {
std::size_t operator()(const std::pair<T, U>& pair) const {
return hash_combine(FirstHash()(pair.first), SecondHash()(pair.second));
}
};
template <typename T, typename Hash = std::hash<T>>
struct vector_hash {
std::size_t operator()(const std::vector<T>& vec) const {
std::size_t seed = 0;
for (const auto& element : vec) {
hash_combine(seed, Hash()(element));
}
return seed;
}
};
} // namespace utility
} // namespace furlang
#endif // FURLANG_UTILITY_HASH_HPP
+38
View File
@@ -0,0 +1,38 @@
#ifndef FURLANG_VIEW_HPP
#define FURLANG_VIEW_HPP
#include <algorithm>
#include <cstddef>
#include <limits>
#include <stdexcept>
namespace furlang {
template <typename T>
class view {
public:
constexpr view() noexcept = default;
constexpr view(const T* data, std::size_t size) noexcept
: m_data(data), m_size(size) {}
public:
constexpr view subview(std::size_t offset, std::size_t count = std::numeric_limits<std::size_t>::max()) {
if (count > 0 && offset >= m_size) throw std::runtime_error("offset too large");
return { m_data + offset, std::min(m_size - offset, count) };
}
const T& operator[](std::size_t offset) const {
if (offset >= m_size) throw std::runtime_error("out of bounds");
return m_data[offset];
}
constexpr const T* data() const { return m_data; }
constexpr std::size_t size() const { return m_size; }
private:
const T* m_data = nullptr;
std::size_t m_size = 0;
};
} // namespace furlang
#endif // FURLANG_VIEW_HPP
+31
View File
@@ -0,0 +1,31 @@
#ifndef FURVM_CONSTANT_HPP
#define FURVM_CONSTANT_HPP
#include "furvm/fwd.hpp"
#include <cstdint>
#include <string_view>
namespace furvm {
// TODO: Array constants
struct constant {
enum type_e {
S32 = 0,
U32,
S64,
U64,
String,
} type = S32;
union {
std::int32_t s32;
std::uint32_t u32;
std::int64_t s64;
std::uint64_t u64;
std::string_view string;
};
};
} // namespace furvm
#endif // FURVM_CONSTANT_HPP
+90
View File
@@ -0,0 +1,90 @@
#ifndef FURVM_CONTEXT_HPP
#define FURVM_CONTEXT_HPP
#include "furvm/executor.hpp"
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp"
#include "furvm/module.hpp" // IWYU pragma: keep
#include "furvm/thing.hpp" // IWYU pragma: keep
#include <cstddef>
#include <utility>
#include <vector>
namespace furvm {
class context : public handle_container<mod_h> {
public:
friend class executor;
public:
/**
* @brief Constructs a context.
*/
context() {}
~context() = default;
/**
* @brief Move constructor.
*/
context(context&&) noexcept = default;
/**
* @brief Move constructor.
*/
context& operator=(context&&) noexcept = default;
context(const context&) = delete;
context& operator=(const context&) = delete;
public:
template <typename... Args>
auto& allocate_executor() {
executor executor(this);
return m_executors.emplace_back(std::move(executor));
}
/**
* @brief Returns an executor from the context.
*
* @param args Id of the executor.
* @return A handle to the executor.
*/
template <typename... Args>
auto& executor_at(Args&&... args) {
return m_executors.at(std::forward<Args>(args)...);
}
/**
* @brief Returns an executor from the context.
*
* @param args Id of the executor.
* @return A handle to the executor.
*/
template <typename... Args>
const auto& executor_at(Args&&... args) const {
return m_executors.at(std::forward<Args>(args)...);
}
const std::vector<executor>& executors() const { return m_executors; }
public:
thing_type_store& tt_store() { return m_thingTypeStore; }
public:
template <typename... Args>
thing<> allocate_thing(Args&&... args) {
thing<> thing = { std::forward<Args>(args)... };
m_heap.push_back(thing.raw());
return std::move(thing);
}
private:
handle_container<mod_h> m_modules;
std::vector<executor> m_executors;
class thing_type_store m_thingTypeStore;
// A list of things on the heap
std::vector<std::byte*> m_heap;
};
} // namespace furvm
#endif // FURVM_CONTEXT_HPP
+39
View File
@@ -0,0 +1,39 @@
#ifndef FURVM_DETAIL_HANDLE_HPP
#define FURVM_DETAIL_HANDLE_HPP
#include <functional>
#include <type_traits>
namespace furvm {
namespace detail {
/**
* @brief Default specialization for header_has_refcount type trait.
*/
template <typename Header, typename = void>
struct header_has_refcount : std::false_type {};
/**
* @brief Specialization for header_has_refcount type trait.
*/
template <typename Header>
struct header_has_refcount<Header,
std::void_t<decltype(std::declval<Header&>().acquire()),
decltype(std::declval<Header&>().release()),
decltype(std::declval<Header&>().reference_count())>> : std::true_type {};
/**
* @brief An alias for header_has_refcount's value.
*/
template <typename Header>
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
template <typename Handle, typename IdHash = std::hash<typename Handle::id_type>>
struct handle_hash {
std::size_t operator()(const Handle& handle) const { return IdHash{}(handle.id()); }
};
} // namespace detail
} // namespace furvm
#endif // FURVM_DETAIL_HANDLE_HPP
+176
View File
@@ -0,0 +1,176 @@
#ifndef FURVM_DETAIL_SERIALIZATION_HPP
#define FURVM_DETAIL_SERIALIZATION_HPP
#include <cstdint>
#include <ostream>
#include <string>
namespace furvm {
namespace detail {
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int8_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int16_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int32_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int64_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint8_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint16_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint32_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint64_t value);
/**
* @brief Serializes a string.
*
* @param os Output stream.
* @param value String.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, const std::string& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int8_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int16_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int32_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int64_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint8_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint16_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint32_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint64_t& value);
/**
* @brief Deserializes a string.
*
* @param is Input stream.
* @param value String.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::string& value);
} // namespace detail
} // namespace furvm
#endif // FURVM_DETAIL_SERIALIZATION_HPP
+112
View File
@@ -0,0 +1,112 @@
#ifndef FURVM_EXCEPTIONS_HPP
#define FURVM_EXCEPTIONS_HPP
#include <exception>
namespace furvm {
class bad_thing_access : public std::exception {
public:
bad_thing_access() = default;
~bad_thing_access() override = default;
/**
* @brief Move constructor.
*/
bad_thing_access(bad_thing_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_thing_access& operator=(bad_thing_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_thing_access(const bad_thing_access&) = default;
/**
* @brief Copy constructor.
*/
bad_thing_access& operator=(const bad_thing_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad thing access"; }
};
/**
* @brief Bad constant access exception.
*/
class bad_constant_access : public std::exception {
public:
bad_constant_access() = default;
~bad_constant_access() override = default;
/**
* @brief Move constructor.
*/
bad_constant_access(bad_constant_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_constant_access& operator=(bad_constant_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_constant_access(const bad_constant_access&) = default;
/**
* @brief Copy constructor.
*/
bad_constant_access& operator=(const bad_constant_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad constant access"; }
};
class stack_underflow : public std::exception {
public:
stack_underflow() = default;
~stack_underflow() override = default;
/**
* @brief Move constructor.
*/
stack_underflow(stack_underflow&&) noexcept = default;
/**
* @brief Move constructor.
*/
stack_underflow& operator=(stack_underflow&&) noexcept = default;
/**
* @brief Copy constructor.
*/
stack_underflow(const stack_underflow&) = default;
/**
* @brief Copy constructor.
*/
stack_underflow& operator=(const stack_underflow&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "stack underflow"; }
};
} // namespace furvm
#endif // FURVM_EXCEPTIONS_HPP
+208
View File
@@ -0,0 +1,208 @@
#ifndef FURVM_EXECUTOR_HPP
#define FURVM_EXECUTOR_HPP
#include "furvm/fwd.hpp"
#include "furvm/module.hpp" // IWYU pragma: keep
#include "furvm/stack.hpp"
#include "furvm/thing.hpp" // IWYU pragma: keep
#include <functional>
#include <stack>
#include <vector>
namespace furvm {
enum class executor_flags : std::uint32_t {
Suspended = (1 << 0), /**< Execution suspended. */
Done = (1 << 1), /**< Execution is finished. */
JustHit = (1 << 16), /**< Executor just hit a breakpoint. */
};
static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) {
return executor_flags(static_cast<std::uint32_t>(lhs) | static_cast<std::uint32_t>(rhs));
}
static inline executor_flags operator&(executor_flags lhs, executor_flags rhs) {
return executor_flags(static_cast<std::uint32_t>(lhs) & static_cast<std::uint32_t>(rhs));
}
static inline executor_flags operator~(executor_flags flags) {
return executor_flags(~static_cast<std::uint32_t>(flags));
}
class executor {
friend class context;
private:
executor(context* context)
: m_context(context) {}
public:
static constexpr executor_flags STATE_FLAGS = executor_flags::JustHit;
using new_frame_callback = std::function<void(executor&)>;
using stack_thing = thing<stack_allocator>;
public:
/**
* @brief Executor frame.
*
* Call frame.
*/
struct frame {
mod_h mod; /**< Handle to the frame's module. */
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
thing_type* returnType; /**< Return type. */
std::vector<stack_thing> variables; /**< Frame variables. */
};
public:
~executor() = default;
/**
* @brief Move constructor.
*/
executor(executor&&) noexcept = default;
/**
* @brief Move constructor.
*/
executor& operator=(executor&&) noexcept = default;
/**
* @brief Copy constructor.
*/
executor(const executor&) = default;
/**
* @brief Copy constructor.
*/
executor& operator=(const executor&) = default;
public:
template <typename CallbackFwd>
void set_new_frame_callback(CallbackFwd&& callback) {
m_newFrameCb = std::forward<CallbackFwd>(callback);
}
public:
/**
* @brief Returns flags of this executor.
*
* @return The flags.
*/
executor_flags flags() const { return m_flags; }
bool done() const { return (m_flags & executor_flags::Done) == executor_flags::Done; }
bool suspended() const { return (m_flags & executor_flags::Suspended) == executor_flags::Suspended; }
void unsuspend() { m_flags = m_flags & ~executor_flags::Suspended; }
void clear_flags() {
m_flags = m_flags & STATE_FLAGS;
m_flags = m_frames.empty() ? executor_flags::Done : furvm::executor_flags{ 0 };
}
public:
/**
* @brief Pushes a new frame.
*
* @param mod Handle to the frame's module.
* @param function Frame's function.
*/
void push_frame(const mod_h& mod, function function);
/**
* @brief Pops the top frame.
*
* @return The popped frame.
*/
frame pop_frame();
/**
* @brief Returns the top frame.
*
* @return The frame.
*/
frame top_frame() const;
const std::stack<frame>& frames() const { return m_frames; }
public:
/**
* @brief Pushes a thing onto the stack.
*
* Registers a new thing and pushes its handle onto the stack.
*
* @param thing Thing.
* @return The pushed handle.
*/
stack_thing& push_thing(stack_thing&& thing);
stack_thing& push_thing(const stack_thing& thing);
/**
* @brief Pops a thing from the stack.
*
* @return A handle to the popped thing.
*/
stack_thing pop_thing();
/**
* @brief Returns the top thing on the stack.
*
* @return A handle to the top thing.
*/
stack_thing& top_thing();
const stack_thing& top_thing() const;
const std::vector<stack_thing>& stack() const { return m_stack; }
public:
/**
* @brief Stores a thing in a frame variable.
*
* @param variable Id of the variable in which the handle will be put.
* @param thing Thing handle.
*/
void store_thing(variable_t variable, const stack_thing& thing);
/**
* @brief Stores a thing in a frame variable.
*
* @param variable Id of the variable in which the handle will be put.
* @param thing Thing handle.
*/
void store_thing(variable_t variable, stack_thing&& thing);
/**
* @brief Returns a thing stored in a variable.
*
* @param variable Id of the variable from which the handle will be fetched.
* @return A handle stored in the variable.
*/
stack_thing& load_thing(variable_t variable);
const stack_thing& load_thing(variable_t variable) const;
public:
/**
* @brief Executes next instruction.
*/
void step();
private:
thing_type thing_type_impl(mod_h mod, mod_type type) const;
thing_type* mod_to_thing_type(const mod_h& mod, const mod_type& type) const;
private:
static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs);
private:
executor_flags m_flags = executor_flags::Done;
context* m_context;
furvm::stack<std::byte> m_stackStorage;
std::stack<frame> m_frames;
std::vector<stack_thing> m_stack;
new_frame_callback m_newFrameCb = nullptr;
};
} // namespace furvm
#endif // FURVM_EXECUTOR_HPP
+201
View File
@@ -0,0 +1,201 @@
#ifndef FURVM_FUNCTION_HPP
#define FURVM_FUNCTION_HPP
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp" // IWYU pragma: keep
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace furvm {
enum class function_t : std::uint8_t {
Normal = 0, /**< A normal bytecode function. */
Native, /**< A native function implemented through furvm API. */
Import, /**< A function imported from another module. */
};
/**
* @brief A native function.
*/
using native_function = std::string;
/**
* @brief A function import.
*/
struct import_function {
mod_id mod;
function_id function;
};
/**
* @brief Function signature.
*/
struct function_sig {
std::vector<mod_type_h> params;
std::optional<mod_type_h> returnType;
bool operator==(const function_sig& rhs) const { return params == rhs.params; }
bool operator!=(const function_sig& rhs) const { return !this->operator==(rhs); }
};
class function {
public:
/**
* @brief Constructs a normal function.
*
* @param signature Function's signature.
* @param position Offset in bytecode of the function.
*/
template <typename SigFwd, typename = std::enable_if_t<std::is_constructible_v<function_sig, SigFwd>>>
function(SigFwd&& signature, bytecode_pos position)
: m_type(function_t::Normal), m_signature(std::forward<SigFwd>(signature)), m_value(position) {}
/**
* @brief Constructs a native function.
*
* @param signature Function's signature.
* @param native Native function tag.
*/
template <typename SigFwd,
typename Native,
typename = std::enable_if_t<std::is_constructible_v<native_function, Native> &&
std::is_constructible_v<function_sig, SigFwd>>>
function(SigFwd&& signature, Native&& native)
: m_type(function_t::Native),
m_signature(std::forward<SigFwd>(signature)),
m_value(std::forward<Native>(native)) {}
/**
* @brief Constructs an import function.
*
* @param mod Module's id.
* @param function Function's id.
*/
template <typename ModFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModFwd>>>
function(ModFwd&& mod, function_id function)
: m_type(function_t::Import), m_signature(), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
/**
* @brief Constructs an import function.
*
* @param mod Module.
* @param function Function.
*/
function(const mod_h& mod, const function_h& function);
/**
* @brief Destructs a function.
*/
~function();
/**
* @brief Move constructor.
*/
function(function&&) noexcept;
/**
* @brief Move constructor.
*/
function& operator=(function&&) noexcept;
/**
* @brief Copy constructor.
*/
function(const function&);
/**
* @brief Copy constructor.
*/
function& operator=(const function&);
public:
/**
* @brief Returns a type of this function.
*
* @return The type.
*/
constexpr function_t type() const { return m_type; }
/**
* @brief Returns this function's signature.
*
* @return The signature.
*/
function_sig signature() const { return m_signature; }
public:
/**
* @brief Returns normal function's value.
*
* @return The value.
*/
std::size_t position() const {
if (m_type != function_t::Normal) throw std::runtime_error("function type mismatch");
return m_value.position;
}
/**
* @brief Returns native function's value.
*
* @return The value.
*/
const native_function& native() const {
if (m_type != function_t::Native) throw std::runtime_error("function type mismatch");
return m_value.native;
}
/**
* @brief Returns import function's value.
*
* @return The value.
*/
const import_function& imp() const {
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
return m_value.imp;
}
private:
function_t m_type;
function_sig m_signature;
union value {
std::size_t position = 0;
native_function native;
import_function imp;
value() = default;
value(std::size_t position)
: position(position) {}
template <typename Native, typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>>
value(Native&& native)
: native(std::forward<Native>(native)) {}
value(const import_function& imp)
: imp(imp) {}
~value() {}
value(value&& other) = delete;
value& operator=(value&& other) = delete;
value(const value& other) = delete;
value& operator=(const value& other) = delete;
} m_value;
};
namespace detail {
struct function_sig_hash {
std::size_t operator()(const function_sig& signature) const;
};
} // namespace detail
} // namespace furvm
#endif // FURVM_FUNCTION_HPP
+12
View File
@@ -0,0 +1,12 @@
#ifndef FURVM_HPP
#define FURVM_HPP
#include "furvm/context.hpp" // IWYU pragma: export
#include "furvm/executor.hpp" // IWYU pragma: export
#include "furvm/function.hpp" // IWYU pragma: export
#include "furvm/fwd.hpp" // IWYU pragma: export
#include "furvm/handle.hpp" // IWYU pragma: export
#include "furvm/instruction.hpp" // IWYU pragma: export
#include "furvm/thing.hpp" // IWYU pragma: export
#endif // FURVM_HPP
+218
View File
@@ -0,0 +1,218 @@
#ifndef FURVM_FWD_HPP
#define FURVM_FWD_HPP
#include <cstddef> // IWYU pragma: export
#include <cstdint> // IWYU pragma: export
#include <memory>
#include <string>
/**
* @brief Furlang's virtual machine.
*/
namespace furvm {
/**
* @brief A byte.
*
* There's nothing more to it.
*/
using byte = std::uint8_t;
/**
* @brief An offset into bytecode.
*/
using bytecode_pos = std::uint64_t;
/**
* @brief Handle header with reference count.
*/
template <typename Id>
class refcount_header;
/**
* @brief Generic handle header.
*/
template <typename Id>
class generic_header;
/**
* @brief Generic furvm object handle.
*
* @tparam Value Type of the handle's value.
* @tparam Header Type of the handle's header.
*/
template <typename Value, typename Header>
class handle;
/**
* @brief Container for the handles.
*
* @tparam Handle Type of the container's handle.
*/
template <typename Handle, typename = void>
class handle_container;
// constant.hpp
/**
* @brief Constant index.
*
* An index to the constant in module's constant pool.
*/
using constant_index = std::uint16_t;
/**
* @enum constant_t
* @brief Constant type.
*/
enum class constant_t : std::uint8_t;
/**
* @class constant
* @brief Constant.
*/
class constant;
// instruction.hpp
struct instruction_argument;
/**
* @struct instruction
* @brief Furvm's instruction.
*/
struct instruction;
// function.hpp
/**
* @enum function_t
* @brief Function type.
*/
enum class function_t : std::uint8_t;
/**
* @class function
* @brief Function.
*
* A furvm function.
*/
class function;
/**
* @brief Furvm function's index.
*/
using function_id = std::uint16_t;
/**
* @brief A handle to a furvm function.
*/
using function_h = handle<function, refcount_header<function_id>>;
// module.hpp
struct mod_type;
using mod_type_id = std::uint32_t;
using mod_type_h = handle<mod_type, generic_header<mod_type_id>>;
/**
* @class mod
* @brief Module.
*
* A furvm module. Translation unit of furlang.
*/
class mod;
/**
* @brief An alias to a module shared pointer.
*/
using mod_p = std::shared_ptr<mod>;
/**
* @brief An alias for a module's identifier.
*/
using mod_id = std::string;
/**
* @brief A handle to a furvm module.
*/
using mod_h = handle<mod, refcount_header<mod_id>>;
// thing.hpp
/**
* @class bad_thing_access
* @brief Bad thing access exception.
*/
class bad_thing_access;
using thing_type_id = std::uint32_t;
/**
* @class thing
* @brief Furvm thing.
*
* A stack element. Think of it like of a value in C++ or I guess a class in java.
*/
template <template <typename> typename Allocator = std::allocator>
class thing;
/**
* @brief Furvm thing's index.
*/
using thing_id = std::uint32_t;
// executor.hpp
/**
* @brief A variable index type.
*/
using variable_t = std::uint16_t;
/**
* @enum executor_flags
* @brief Flags of an executor.
*/
enum class executor_flags : std::uint32_t;
/**
* @class executor
* @brief Furvm executor.
*
* Furvm executors are like threads.
*/
class executor;
/**
* @brief Furvm executor's index.
*/
using executor_id = std::uint32_t;
// context.hpp
/**
* @class context
* @brief Context.
*
* A furvm context.
*/
class context;
/**
* @brief An alias to a context shared pointer.
*/
using context_p = std::shared_ptr<context>;
// exceptions.hpp:
/**
* @class stack_underflow
* @brief Stack underflow exception.
*/
class stack_underflow;
} // namespace furvm
#endif // FURVM_FWD_HPP
+448
View File
@@ -0,0 +1,448 @@
#ifndef FURVM_HANDLE_HPP
#define FURVM_HANDLE_HPP
#include "furvm/detail/handle.hpp"
#include "furvm/fwd.hpp"
#include <atomic>
#include <functional>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furvm {
// TODO: Implement generational indexes
template <typename Id>
class refcount_header {
public:
using id_type = Id; /**< Id type. */
using refcount_type = std::uint32_t; /**< Reference count type. */
public:
/**
* @brief Constructs a reference counting header.
*
* @param id Identifier of the handle's value.
* @param refCount Handle's reference count.
* @param onRelease Callback function.
*/
template <typename IdFwd, typename Func>
refcount_header(IdFwd&& id, refcount_type refCount, Func&& onRelease)
: m_id(std::forward<IdFwd>(id)), m_refCount(refCount), m_onRelease(std::forward<Func>(onRelease)) {}
public:
/**
* @brief Returns the header's reference count.
*
* @return The reference count.
*/
refcount_type reference_count() const { return m_refCount; }
/**
* @brief Increments the header's reference count.
*/
void acquire() { ++m_refCount; }
/**
* @brief Decrements the header's reference count.
*
* If the reference count reaches 0, the onRelease callback passed in the constructor will be called.
*/
void release() {
--m_refCount;
if (m_refCount == 0) m_onRelease(m_id);
}
public:
/**
* @brief Returns the header's identifier.
*
* @return The identifier.
*/
id_type id() const { return m_id; }
private:
id_type m_id;
std::atomic<refcount_type> m_refCount;
std::function<void(const id_type&)> m_onRelease;
};
template <typename Id>
class generic_header {
public:
using id_type = Id; /**< Id type. */
public:
/**
* @brief Constructs a generic header.
*
* @param id Identifier of the handle.
*/
generic_header(id_type id)
: m_id(id) {}
public:
/**
* @brief Returns the header's identifier.
*/
id_type id() const { return m_id; }
private:
id_type m_id;
};
template <typename Value, typename Header = refcount_header<std::uint32_t>>
class handle {
public:
using value_type = Value; /** Value type. */
using reference = Value&; /** Reference type. */
using const_reference = const Value&; /** Constant reference type. */
using pointer = Value*; /** Pointer type. */
using const_pointer = const Value*; /** Constant pointer type. */
public:
using id_type = typename Header::id_type; /** Id type of the header. */
using header_type = Header;
public:
using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */
public:
handle() = default;
/**
* @brief Constructs a handle.
*
* @param value A pointer to the header-value pair.
*/
handle(pair_type* value)
: m_value(value) {
if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
}
/**
* @brief Destructs a handle.
*/
~handle() {
if constexpr (detail::header_has_refcount_v<Header>) {
if (m_value != nullptr) m_value->first.release();
}
m_value = nullptr;
}
/**
* @brief Move constructor.
*/
handle(handle&& other) noexcept
: m_value(other.m_value) {
other.m_value = nullptr;
}
/**
* @brief Move constructor.
*/
handle& operator=(handle&& other) noexcept {
if (this == &other) return *this;
m_value = other.m_value;
other.m_value = nullptr;
return *this;
}
/**
* @brief Copy constructor.
*/
handle(const handle& other)
: m_value(other.m_value) {
if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
}
/**
* @brief Copy constructor.
*/
handle& operator=(const handle& other) {
if (this == &other) return *this;
m_value = other.m_value;
if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
return *this;
}
public:
/**
* @brief Returns an identifier of the handle's header.
*
* @return The header's identifier.
*/
id_type id() const { return m_value->first.id(); }
/**
* @brief Returns whether the handle is empty.
*
* @return true if the handle is empty.
*/
bool empty() const { return m_value == nullptr; }
/**
* @brief Returns the handle's header reference count.
*
* @return The reference count.
*/
template <typename U = Header, typename = std::enable_if_t<detail::header_has_refcount_v<U>>>
auto reference_count() const {
return m_value->first.reference_count();
}
/**
* @brief Returns a pointer to the handle's value.
*
* @return The value pointer.
*/
pointer operator->() { return &m_value->second; }
/**
* @brief Returns a pointer to the handle's value.
*
* @return The value pointer.
*/
const_pointer operator->() const { return &m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
reference operator*() { return m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
const_reference operator*() const { return m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
reference value() { return m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
const_reference value() const { return m_value->second; }
public:
/**
* @brief Invalidates the handle without releasing.
*/
void dispatch() { m_value = nullptr; }
public:
bool operator==(const handle& rhs) const { return m_value == rhs.m_value; }
bool operator!=(const handle& rhs) const { return !this->operator==(rhs); }
private:
pair_type* m_value = nullptr;
};
template <typename Handle>
class handle_container<Handle, std::enable_if_t<!std::is_integral_v<typename Handle::id_type>>> {
private:
using pair_type = typename Handle::pair_type; /**< Handle's pair type. */
public:
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>; /**< Handle type. */
using const_value = std::add_const_t<value_type>; /**< Constant handle type. */
using id_type = typename Handle::id_type; /**< Handle's header identifier type. */
public:
handle_container() = default;
~handle_container() = default;
handle_container(handle_container&&) noexcept = default;
handle_container& operator=(handle_container&&) noexcept = default;
handle_container(const handle_container&) = delete;
handle_container& operator=(const handle_container&) = delete;
public:
/**
* @brief Emplaces a new value.
*
* @param id Identifier of the emplaced value.
* @param args Arguments passed to the Handle's value type constructor.
* @return A handle to the emplaced value.
*/
template <typename IdFwd,
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<typename pair_type::second_type, Args...>>>
value_type emplace(IdFwd&& id, Args&&... args) {
id_type idFwd = std::forward<IdFwd>(id);
if (auto it = m_pairs.find(idFwd); it != m_pairs.end()) delete it->second;
auto pair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(idFwd, 0, [&](const id_type& id) { erase(id); }),
std::forward_as_tuple(std::forward<Args>(args)...));
m_pairs.emplace(std::move(idFwd), pair);
return { pair };
}
/**
* @brief Returns a handle to the container's value.
*
* @param id Idenfifier of the value.
* @return The value.
*/
template <typename IdFwd>
value_type at(IdFwd&& id) {
return { m_pairs.at(std::forward<IdFwd>(id)) };
}
/**
* @brief Returns a handle to the container's value.
*
* @param id Idenfifier of the value.
* @return The value.
*/
template <typename IdFwd>
const_value at(IdFwd&& id) const {
return { m_pairs.at(std::forward<IdFwd>(id)) };
}
/**
* @brief Erases a value from the container.
*
* @param id Identifier of the value.
*/
template <typename IdFwd>
void erase(IdFwd&& id) {
auto it = m_pairs.find(std::forward<IdFwd>(id));
if (it == m_pairs.end()) return;
delete it->second;
m_pairs.erase(it);
}
/**
* @brief Checks whether a handle exists inside.
*
* @param id Identifier of the handle.
* @return true if the handle exists insdie of this container.
*/
template <typename IdFwd>
constexpr bool contains(IdFwd&& id) const {
return m_pairs.find(std::forward<IdFwd>(id)) != m_pairs.end();
}
private:
std::unordered_map<id_type, pair_type*> m_pairs;
};
template <typename Handle>
class handle_container<Handle, std::enable_if_t<std::is_integral_v<typename Handle::id_type>>> {
private:
using pair_type = typename Handle::pair_type; /**< Handle's pair type. */
public:
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>; /**< Handle type. */
using const_value = std::add_const_t<value_type>; /**< Constant handle type. */
using id_type = typename Handle::id_type; /**< Handle's header identifier type. */
public:
handle_container() = default;
~handle_container() = default;
handle_container(handle_container&&) noexcept = default;
handle_container& operator=(handle_container&&) noexcept = default;
handle_container(const handle_container&) = delete;
handle_container& operator=(const handle_container&) = delete;
public:
/**
* @brief Emplaces a new value.
*
* @param id Identifier of the emplaced value.
* @param args Arguments passed to the Handle's value type constructor.
* @return A handle to the emplaced value.
*/
template <typename... Args,
typename = std::enable_if_t<std::is_constructible_v<typename pair_type::second_type, Args...>>>
value_type emplace(id_type id, Args&&... args) {
if (id >= m_pairs.size()) {
m_pairs.resize(id + 1, nullptr);
} else if (m_pairs[id] != nullptr) {
delete m_pairs[id];
}
pair_type* newPair = nullptr;
if constexpr (detail::header_has_refcount_v<typename Handle::header_type>) {
newPair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }),
std::forward_as_tuple(std::forward<Args>(args)...));
} else {
newPair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(std::forward<Args>(args)...));
}
m_pairs[id] = newPair;
return { newPair };
}
/**
* @brief Emplaces a new value.
*
* Emplaces a new value with an automatically-assigned identifier.
*
* @param args Arguments passed to the Handle's value type constructor.
* @return A handle to the emplaced value.
*/
template <typename... Args,
typename = std::enable_if_t<std::is_constructible_v<typename Handle::pair_type::second_type, Args...>>>
value_type emplace_back(Args&&... args) {
return emplace(static_cast<id_type>(m_pairs.size()), std::forward<Args>(args)...);
}
/**
* @brief Returns a handle to a value.
*
* @param id Identifier of the value.
* @return The value.
*/
value_type at(id_type id) { return { m_pairs.at(id) }; }
/**
* @brief Returns a handle to a value.
*
* @param id Identifier of the value.
* @return The value.
*/
const_value at(id_type id) const { return { m_pairs.at(id) }; }
/**
* @brief Erases a value from the container.
*
* @param id Identifier of the value.
*/
void erase(id_type id) {
if (id >= m_pairs.size()) return;
delete m_pairs[id];
m_pairs[id] = nullptr;
}
/**
* @brief Checks whether a handle exists inside.
*
* @param id Identifier of the handle.
* @return true if the handle exists insdie of this container.
*/
constexpr bool contains(id_type id) const { return id < m_pairs.size() && m_pairs[id] != nullptr; }
public:
auto begin() { return m_pairs.begin(); }
auto begin() const { return m_pairs.begin(); }
auto cbegin() const { return m_pairs.cbegin(); }
auto end() { return m_pairs.end(); }
auto end() const { return m_pairs.end(); }
auto cend() const { return m_pairs.cend(); }
private:
std::vector<pair_type*> m_pairs;
};
} // namespace furvm
#endif // FURVM_HANDLE_HPP
+104
View File
@@ -0,0 +1,104 @@
#ifndef FURVM_INSTRUCTION_HPP
#define FURVM_INSTRUCTION_HPP
#include "furlang/view.hpp"
#include "furvm/fwd.hpp"
#include <cstddef>
#include <vector>
namespace furvm {
struct instruction_argument {
enum type_e {
None = 0,
S8,
U8,
S16,
U16,
S32,
U32,
Constant,
Type,
Variable,
GlobalVariable,
Function,
Offset,
Count,
} type;
union {
std::int8_t s8;
std::uint8_t u8;
std::int16_t s16;
std::uint16_t u16;
std::int32_t s32;
std::uint32_t u32;
};
static const std::size_t s_sizes[Count];
static const bool s_signedness[Count];
std::size_t size() const { return s_sizes[type]; }
bool is_signed() const { return s_signedness[type]; }
};
using instruction_argument_t = instruction_argument::type_e;
struct instruction {
enum type_e : byte {
NoOperation = 0,
PushS8,
PushU8,
PushS16,
PushU16,
PushS32,
PushU32,
PushConstant,
Array,
Slice,
Get,
Set,
Drop,
Duplicate,
Swap,
Clone,
Reference,
Add,
Sub,
Mul,
Div,
Mod,
Equals,
NotEquals,
LessThan,
GreaterThan,
LessEqual,
GreaterEqual,
Pointerof,
Sizeof,
Lengthof,
Load,
Store,
LoadGlobal,
StoreGlobal,
Call,
Jump,
JumpNotZero,
Return,
Count,
} type;
instruction_argument arg;
static const instruction_argument_t s_arguments[Count];
std::size_t read(furlang::view<std::uint8_t> in);
std::size_t write(std::vector<std::uint8_t>& out) const;
};
using instruction_t = instruction::type_e;
} // namespace furvm
#endif // FURVM_INSTRUCTION_HPP
+452
View File
@@ -0,0 +1,452 @@
#ifndef FURVM_MODULE_HPP
#define FURVM_MODULE_HPP
#include "furlang/utility/hash.hpp"
#include "furlang/view.hpp"
#include "furvm/constant.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp"
#include "furvm/thing.hpp"
#include <functional>
#include <istream>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furvm {
struct mod_type {
struct array_value {
mod_type_id typeId;
std::size_t size;
};
struct slice_value {
mod_type_id typeId;
};
struct import_value {
mod_id modId;
mod_type_id typeId;
};
enum type {
S8 = 0,
S16,
S32,
S64,
U8,
U16,
U32,
U64,
Ptr,
Ref,
Array,
Slice,
Import,
Count,
} type;
union value {
std::nullptr_t null = nullptr;
mod_type_id typeRef;
array_value array;
slice_value slice;
import_value imprt;
value() = default;
value(mod_type_id id)
: typeRef(id) {}
value(mod_type_id id, std::size_t size)
: array({}) {
array.typeId = id;
array.size = size;
}
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
value(ModIdFwd&& modId, mod_type_id typeId)
: imprt({}) {
imprt.modId = std::forward<ModIdFwd>(modId);
imprt.typeId = typeId;
}
~value() {}
value(value&& other) = delete;
value& operator=(value&& other) = delete;
value(const value& other) = delete;
value& operator=(const value& other) = delete;
} value;
mod_type(enum type type)
: type(type) {}
mod_type(enum type type, mod_type_id typeRef)
: type(type), value(typeRef) {}
mod_type(mod_type_id id, std::size_t size)
: type(Array), value(id, size) {}
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
mod_type(ModIdFwd&& modId, mod_type_id typeId)
: type(Import), value(std::forward<ModIdFwd>(modId), typeId) {}
~mod_type() {
switch (type) {
case Array: value.array.~array_value(); break;
case Slice: value.slice.~slice_value(); break;
case Import: value.imprt.~import_value(); break;
default: break;
}
}
mod_type(mod_type&& other) noexcept
: type(other.type) {
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break;
default: break;
}
other.type = Count;
}
mod_type& operator=(mod_type&& other) noexcept {
if (this == &other) return *this;
type = other.type;
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break;
default: break;
}
other.type = Count;
return *this;
}
mod_type(const mod_type& other)
: type(other.type) {
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(other.value.imprt); break;
default: break;
}
}
mod_type& operator=(const mod_type& other) {
if (this == &other) return *this;
type = other.type;
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(other.value.imprt); break;
default: break;
}
return *this;
}
};
struct breakpoint {
std::function<void(executor&, void*)> callback;
void* data = nullptr;
};
class mod {
friend class function;
friend class serializer;
public:
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
static constexpr char MAGIC[4] = { 'F', 'u', 'r', 'M' }; /** Furvm module file magic. */
using native_function = std::function<void(executor&)>;
public:
/**
* @brief Constructs a module.
*
* @param name Name of the module.
* @param args Arguments forwarded to bytecode's constructor.
*/
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<bytecode_t, Args...>>>
mod(Args&&... args)
: m_bytecode(std::forward<Args>(args)...) {}
~mod() = default;
/**
* @brief Move constructor.
*/
mod(mod&&) = default;
/**
* @brief Move constructor.
*/
mod& operator=(mod&&) = default;
mod(const mod&) = delete;
mod& operator=(const mod&) = delete;
public:
/**
* @brief Returns a byte from bytecode of this module.
*
* @param offset An offset of the byte.
* @return The byte.
*/
byte byte_at(std::size_t offset) const { return m_bytecode.at(offset); }
/**
* @brief Returns the module's bytecode.
*
* @return A reference to the bytecode.
*/
constexpr bytecode_t& bytecode() { return m_bytecode; }
/**
* @brief Returns the module's bytecode.
*
* @return A constant reference to the bytecode.
*/
furlang::view<std::uint8_t> bytecode_view() const { return { m_bytecode.data(), m_bytecode.size() }; }
public:
/**
* @brief Emplaces a function in the module's function container.
*
* Emplaces the function in module's function container and name to function map and public functions map.
*
* @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function.
*/
template <typename... Args>
function_h emplace_function(Args&&... args) {
function_h function;
if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
}
return std::move(function);
}
/**
* @brief Emplaces a function in the module's function container.
*
* Emplaces the function in module's function container and name to function map.
*
* @param name Name of the function.
* @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function.
*/
template <typename NameFwd,
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
function_h emplace_function(NameFwd&& name, Args&&... args) {
function_h function;
if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
}
auto pair = std::make_pair(std::forward<NameFwd>(name), function->signature());
m_functionMap[function.id()] = pair;
m_functionSigs[std::move(pair)] = function.id();
return std::move(function);
}
/**
* @brief Returns a function from the module.
*
* @param id Identifier of the function.
* @return A handle to the function.
*/
auto function_at(function_id id) { return m_functions.at(id); }
/**
* @brief Returns a function from the module.
*
* @param id Identifier of the function.
* @return A handle to the function.
*/
auto function_at(function_id id) const { return m_functions.at(id); }
/**
* @brief Returns a function from the module.
*
* @param name Name of the function.
* @return A handle to the function.
*/
template <typename NameFwd,
typename SigFwd,
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd> &&
std::is_constructible_v<function_sig, SigFwd>>>
auto function_at(NameFwd&& name, SigFwd&& signature) {
return function_at(
m_functionSigs.at(std::make_pair<>(std::forward<NameFwd>(name), std::forward<SigFwd>(signature))));
}
/**
* @brief Erases a function from the module's function container.
*
* @param id Identifier of the function.
*/
void erase_function(function_id id) {
m_functions.erase(id);
if (auto it = m_functionMap.find(id); it != m_functionMap.end()) {
m_functionSigs.erase(it->second);
m_functionMap.erase(it);
}
}
const handle_container<function_h>& functions() const { return m_functions; }
const auto& function_map() const { return m_functionMap; }
public:
template <typename NameFwd, typename Func>
void set_native_function(NameFwd&& name, Func&& func) {
m_nativeFunctions.emplace(std::forward<NameFwd>(name), std::forward<Func>(func));
}
template <typename NameFwd>
native_function get_native_function(NameFwd&& name) const {
return m_nativeFunctions.at(std::forward<NameFwd>(name));
}
public:
/**
* @brief Emplaces a type in the context.
*
* @param args Arguments forwarded to the type constructor.
* @return The emplaced type.
*/
template <typename... Args>
auto emplace_type(Args&&... args) {
if constexpr (std::is_constructible_v<mod_type, Args...>) {
return m_types.emplace_back(std::forward<Args>(args)...);
} else {
return m_types.emplace(std::forward<Args>(args)...);
}
}
/**
* @brief Returns a type from the context.
*
* @param args type's id.
* @return A handle to the type.
*/
template <typename... Args>
auto type_at(Args&&... args) {
return m_types.at(std::forward<Args>(args)...);
}
/**
* @brief Returns a type from the context.
*
* @param args type's id.
* @return A handle to the type.
*/
template <typename... Args>
auto type_at(Args&&... args) const {
return m_types.at(std::forward<Args>(args)...);
}
/**
* @brief Erases a type from the context.
*
* @param args type's id.
*/
template <typename... Args>
void erase_type(Args&&... args) {
m_types.erase(std::forward<Args>(args)...);
}
const handle_container<mod_type_h>& types() const { return m_types; }
public:
void set_global_variable_count(std::uint16_t count) {
m_globalVariables.resize(count);
m_globalVariables.shrink_to_fit();
}
std::uint16_t get_global_variable_count() const { return static_cast<std::uint16_t>(m_globalVariables.size()); }
void store_global_variable(std::uint16_t var, thing<>&& thing) {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
m_globalVariables.emplace(m_globalVariables.cbegin() + var, std::move(thing));
}
void store_global_variable(std::uint16_t var, const thing<>& thing) {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
m_globalVariables.emplace(m_globalVariables.cbegin() + var, thing);
}
thing<>& load_global_variable(std::uint16_t var) {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
return m_globalVariables[var];
}
const thing<>& load_global_variable(std::uint16_t var) const {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
return m_globalVariables[var];
}
public:
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<constant, Args...>>>
void emplace_constant(Args&&... args) {
m_constants.emplace_back(std::forward<Args>(args)...);
}
const constant& constant_at(constant_index index) const { return m_constants.at(index); }
public:
template <typename Fwd, typename = std::enable_if_t<std::is_constructible_v<breakpoint, Fwd>>>
void set_breakpoint(bytecode_pos pos, Fwd&& breakpoint) {
m_breakpoints[pos] = std::forward<Fwd>(breakpoint);
}
bool has_breakpoint(bytecode_pos pos) const { return m_breakpoints.find(pos) != m_breakpoints.end(); }
const breakpoint& breakpoint_at(bytecode_pos pos) const { return m_breakpoints.at(pos); }
public:
/**
* @brief Prints the module in a bytecode form to an output stream.
*
* @param os Output stream.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os) const;
/**
* @brief Loads a module in a bytecode form from an input stream.
*
* @param is Input stream.
* @return The loaded module.
*/
static mod load(std::istream& is);
private:
bytecode_t m_bytecode;
using pair_type = std::pair<std::string, function_sig>;
using pair_hash =
furlang::utility::pair_hash<std::string, function_sig, std::hash<std::string>, detail::function_sig_hash>;
std::unordered_map<pair_type, function_id, pair_hash> m_functionSigs;
std::unordered_map<function_id, pair_type> m_functionMap;
handle_container<function_h> m_functions;
handle_container<mod_type_h> m_types;
std::vector<thing<>> m_globalVariables;
std::vector<constant> m_constants;
std::unordered_map<std::string, native_function> m_nativeFunctions;
std::unordered_map<bytecode_pos, breakpoint> m_breakpoints;
};
} // namespace furvm
#endif // FURVM_MODULE_HPP
+56
View File
@@ -0,0 +1,56 @@
#ifndef FURVM_STACK_HPP
#define FURVM_STACK_HPP
#include <cstddef>
#include <new>
#include <stack>
namespace furvm {
template <typename T>
struct stack {
stack(std::size_t capacity = (1024ULL * 1024ULL) / sizeof(T))
: begin(new T[capacity]()), cursor(begin), capacity(capacity) {}
T* begin;
T* cursor;
std::size_t capacity;
std::stack<T*> frames;
void push_frame() { frames.push(cursor); }
void pop_frame() {
cursor = frames.top();
frames.pop();
}
};
template <typename T>
class stack_allocator {
public:
stack_allocator() = default;
stack_allocator(stack<T>& stack)
: m_ref(&stack) {}
template <typename U>
constexpr stack_allocator(const stack_allocator<U>& other) noexcept
: m_ref(other.m_ref) {}
public:
T* allocate(std::size_t n) {
if (m_ref == nullptr) throw std::bad_alloc();
if (m_ref->capacity - (m_ref->cursor - m_ref->begin) < n) throw std::bad_alloc();
T* ptr = m_ref->cursor;
m_ref->cursor += n;
return ptr;
}
void deallocate(T* ptr, std::size_t n) {}
private:
stack<T>* m_ref = nullptr;
};
} // namespace furvm
#endif // FURVM_STACK_HPP
File diff suppressed because it is too large Load Diff
+226
View File
@@ -0,0 +1,226 @@
#ifndef FURVM_TYPES_HPP
#define FURVM_TYPES_HPP
#include "furvm/fwd.hpp"
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace furvm {
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
struct thing_type {
struct array_value {
thing_type* type;
std::size_t size;
};
struct slice_value {
thing_type* type;
};
enum type { // NOLINT
S8 = 0,
S16,
S32,
S64,
U8,
U16,
U32,
U64,
String,
Ptr,
Ref,
Array,
Slice,
Count,
} type = Count;
union value {
std::nullptr_t null = nullptr;
thing_type* typeRef;
array_value array;
slice_value slice;
value() = default;
value(thing_type* type)
: typeRef(type) {}
value(thing_type* type, std::size_t size)
: array({}) {
array.type = type;
array.size = size;
}
} value;
static constexpr thing_type_id INVALID_ID = std::numeric_limits<thing_type_id>::max();
thing_type_id id = INVALID_ID;
bool operator==(const thing_type& other) const {
if (type != other.type) return false;
switch (type) {
case S8:
case S16:
case S32:
case S64:
case U8:
case U16:
case U32:
case U64:
case String: return true;
case Ptr:
case Ref: return *value.typeRef == *other.value.typeRef;
case Array: return *value.array.type == *other.value.array.type && value.array.size == other.value.array.size;
case Slice: return *value.slice.type == *other.value.slice.type;
case Count: break;
}
return false;
}
bool operator!=(const thing_type& other) const { return !this->operator==(other); }
static bool is_primitive(enum type type) {
switch (type) {
case S8:
case S16:
case S32:
case S64:
case U8:
case U16:
case U32:
case U64: return true;
case String:
case Ptr:
case Ref:
case Array:
case Slice: return false;
case Count: break;
}
throw std::runtime_error("unreachable");
}
static std::size_t primitive_size(enum type type) {
switch (type) {
case thing_type::S8: return sizeof(s8);
case thing_type::S16: return sizeof(s16);
case thing_type::S32: return sizeof(s32);
case thing_type::S64: return sizeof(s64);
case thing_type::U8: return sizeof(u8);
case thing_type::U16: return sizeof(u16);
case thing_type::U32: return sizeof(u32);
case thing_type::U64: return sizeof(u64);
case thing_type::String:
case Ptr:
case Ref:
case Array:
case Slice: return 0;
case Count: break;
}
throw std::runtime_error("unreachable");
}
};
namespace detail {
template <typename T, typename = void>
struct overrides_thing_type_matching : std::false_type {};
template <typename T>
struct overrides_thing_type_matching<T, std::void_t<decltype(T::matches(std::declval<const thing_type&>()))>>
: std::is_same<decltype(T::matches(std::declval<const thing_type&>())), bool> {};
template <typename T>
struct thing_traits {
bool operator()(const thing_type& type) const {
if constexpr (overrides_thing_type_matching<T>::value) {
return T::matches(type);
} else {
return false;
}
}
};
template <>
struct thing_traits<s8> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S8; }
};
template <>
struct thing_traits<u8> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U8; }
};
template <>
struct thing_traits<s16> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S16; }
};
template <>
struct thing_traits<u16> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U16; }
};
template <>
struct thing_traits<s32> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S32; }
};
template <>
struct thing_traits<u32> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U32; }
};
template <>
struct thing_traits<s64> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S64; }
};
template <>
struct thing_traits<u64> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U64; }
};
template <typename Inner>
struct thing_traits<Inner*> {
bool operator()(const thing_type& type) const {
return (type.type == thing_type::Ptr || type.type == thing_type::Ref) &&
thing_traits<Inner>{}(*type.value.typeRef);
}
};
template <typename T, typename Thing, typename = void>
struct cassignable_to_thing : std::false_type {};
template <typename T, typename Thing>
struct cassignable_to_thing<T,
Thing,
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Thing, typename = void>
struct massignable_to_thing : std::false_type {};
template <typename T, typename Thing>
struct massignable_to_thing<T,
Thing,
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<T&&>()))>>
: std::true_type {};
} // namespace detail
} // namespace furvm
#endif // FURVM_TYPES_HPP