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