Compare commits
38 Commits
6ec4710eaa
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
549af109b9
|
|||
|
6dce7016ce
|
|||
|
a1e43576cc
|
|||
|
9f950e45e7
|
|||
|
dfaa3081cb
|
|||
|
46e92deed5
|
|||
|
42e71080a2
|
|||
|
03448d865e
|
|||
|
cb845ca625
|
|||
|
798287ef47
|
|||
|
466d7f003b
|
|||
|
ff3fce53f5
|
|||
|
9b280fab22
|
|||
|
e1b75ccc8e
|
|||
|
cd715ae779
|
|||
|
8d3859ce70
|
|||
|
3c0588e8db
|
|||
|
abbc1714c1
|
|||
|
b7da2f3d32
|
|||
|
d49b4f5bc7
|
|||
|
7de645d323
|
|||
|
2519560451
|
|||
|
7c74850aff
|
|||
|
81a439b2ad
|
|||
|
b913fc0c8b
|
|||
|
ee9eadeea8
|
|||
|
642d57622a
|
|||
|
9690ac3617
|
|||
|
f89f643870
|
|||
|
100c542fe1
|
|||
|
cf284592fd
|
|||
|
f5ebc767ab
|
|||
|
484e16963f
|
|||
|
575054b75a
|
|||
|
a7ab214ce3
|
|||
|
e087c11008
|
|||
|
2164ab0d97
|
|||
|
11b05b8c18
|
+3
-1
@@ -31,7 +31,9 @@ Checks: >
|
|||||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
|
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-bugprone-forward-declaration-namespace
|
-cppcoreguidelines-use-enum-class,
|
||||||
|
-bugprone-forward-declaration-namespace,
|
||||||
|
-bugprone-tagged-union-member-count
|
||||||
|
|
||||||
WarningsAsErrors: "*"
|
WarningsAsErrors: "*"
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
add_subdirectory(furlang)
|
add_subdirectory(furlang)
|
||||||
add_subdirectory(furvm)
|
add_subdirectory(furvm)
|
||||||
add_subdirectory(furc)
|
add_subdirectory(furc)
|
||||||
|
add_subdirectory(furas)
|
||||||
|
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
|
set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
file(GLOB_RECURSE FURAS_SRCS "src/**.cpp")
|
||||||
|
file(GLOB_RECURSE FURAS_HDRS "include/**.hpp")
|
||||||
|
add_executable(furas ${FURAS_SRCS} ${FURAS_HDRS})
|
||||||
|
target_include_directories(furas PUBLIC include/)
|
||||||
|
target_link_libraries(furas PRIVATE furlang libfurvm)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
type arr = array $s8 10
|
||||||
|
|
||||||
|
func println $arr = native println
|
||||||
|
|
||||||
|
public func main = #main
|
||||||
|
main:
|
||||||
|
array $arr
|
||||||
|
dup
|
||||||
|
store %0
|
||||||
|
|
||||||
|
lenof
|
||||||
|
store %1
|
||||||
|
|
||||||
|
push $s32 0
|
||||||
|
loop:
|
||||||
|
dup
|
||||||
|
load %1
|
||||||
|
ge
|
||||||
|
jnz #end
|
||||||
|
body:
|
||||||
|
dup
|
||||||
|
load %0
|
||||||
|
swap
|
||||||
|
push $s8 69
|
||||||
|
set
|
||||||
|
|
||||||
|
push $s32 1
|
||||||
|
add
|
||||||
|
|
||||||
|
jmp #loop
|
||||||
|
end:
|
||||||
|
drop
|
||||||
|
load %0
|
||||||
|
call $arr println
|
||||||
|
ret
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
type arr = array $s8 10
|
||||||
|
|
||||||
|
func println $arr = native println
|
||||||
|
|
||||||
|
public func main = #main
|
||||||
|
main:
|
||||||
|
array $arr
|
||||||
|
store %0
|
||||||
|
|
||||||
|
push $s32 2
|
||||||
|
store %1
|
||||||
|
|
||||||
|
push $s32 0
|
||||||
|
loop:
|
||||||
|
dup
|
||||||
|
load %1
|
||||||
|
ge
|
||||||
|
jnz #end
|
||||||
|
body:
|
||||||
|
dup
|
||||||
|
load %0
|
||||||
|
swap
|
||||||
|
push $s8 69
|
||||||
|
set
|
||||||
|
|
||||||
|
push $s32 1
|
||||||
|
add
|
||||||
|
|
||||||
|
jmp #loop
|
||||||
|
end:
|
||||||
|
drop
|
||||||
|
load %0
|
||||||
|
call $arr println
|
||||||
|
ret
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
func println $u32 = native println
|
||||||
|
|
||||||
|
public func main = #main
|
||||||
|
main:
|
||||||
|
push $u32 0
|
||||||
|
store %0
|
||||||
|
loop_header:
|
||||||
|
load %0
|
||||||
|
push $u32 10
|
||||||
|
ge
|
||||||
|
jnz #loop_end
|
||||||
|
loop_body:
|
||||||
|
load %0
|
||||||
|
call $u32 println
|
||||||
|
|
||||||
|
load %0
|
||||||
|
push $u32 1
|
||||||
|
add
|
||||||
|
store %0
|
||||||
|
|
||||||
|
jmp #loop_header
|
||||||
|
loop_end:
|
||||||
|
ret
|
||||||
@@ -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<token, lexer_error>;
|
||||||
|
|
||||||
|
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,94 @@
|
|||||||
|
#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. */
|
||||||
|
|
||||||
|
// Instructions
|
||||||
|
Push,
|
||||||
|
Array,
|
||||||
|
Get,
|
||||||
|
Set,
|
||||||
|
Drop,
|
||||||
|
Dup,
|
||||||
|
Swap,
|
||||||
|
Clone,
|
||||||
|
Ref,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
Le,
|
||||||
|
Ge,
|
||||||
|
Ptrof,
|
||||||
|
Sizeof,
|
||||||
|
Lenof,
|
||||||
|
Load,
|
||||||
|
Store,
|
||||||
|
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,558 @@
|
|||||||
|
#include "furas/gen.hpp"
|
||||||
|
|
||||||
|
#include "furas/token.hpp"
|
||||||
|
#include "furvm/function.hpp"
|
||||||
|
#include "furvm/fwd.hpp"
|
||||||
|
#include "furvm/instruction.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace furas {
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct instruction {
|
||||||
|
furvm::instruction_t fur{};
|
||||||
|
enum arg_type {
|
||||||
|
None = 0,
|
||||||
|
Type,
|
||||||
|
Constant,
|
||||||
|
Variable,
|
||||||
|
Function,
|
||||||
|
Label,
|
||||||
|
} arg = None;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTBEGIN
|
||||||
|
std::unordered_map<enum token::type, instruction> instructions = {
|
||||||
|
{ token::Array, { furvm::instruction_t::Array, instruction::Type } },
|
||||||
|
{ token::Get, { furvm::instruction_t::Get } },
|
||||||
|
{ token::Set, { furvm::instruction_t::Set } },
|
||||||
|
{ token::Drop, { furvm::instruction_t::Drop } },
|
||||||
|
{ token::Dup, { furvm::instruction_t::Duplicate } },
|
||||||
|
{ token::Swap, { furvm::instruction_t::Swap } },
|
||||||
|
{ token::Clone, { furvm::instruction_t::Clone } },
|
||||||
|
{ token::Ref, { furvm::instruction_t::Reference } },
|
||||||
|
{ token::Add, { furvm::instruction_t::Add } },
|
||||||
|
{ token::Sub, { furvm::instruction_t::Sub } },
|
||||||
|
{ token::Mul, { furvm::instruction_t::Mul } },
|
||||||
|
{ token::Div, { furvm::instruction_t::Div } },
|
||||||
|
{ token::Mod, { furvm::instruction_t::Mod } },
|
||||||
|
{ token::Eq, { furvm::instruction_t::Equals } },
|
||||||
|
{ token::Neq, { furvm::instruction_t::NotEquals } },
|
||||||
|
{ token::Lt, { furvm::instruction_t::LessThan } },
|
||||||
|
{ token::Gt, { furvm::instruction_t::GreaterThan } },
|
||||||
|
{ token::Le, { furvm::instruction_t::LessEqual } },
|
||||||
|
{ token::Ge, { furvm::instruction_t::GreaterEqual } },
|
||||||
|
{ token::Ptrof, { furvm::instruction_t::Pointerof } },
|
||||||
|
{ token::Sizeof, { furvm::instruction_t::Sizeof } },
|
||||||
|
{ token::Lenof, { furvm::instruction_t::Lengthof } },
|
||||||
|
{ token::Load, { furvm::instruction_t::Load, instruction::Variable } },
|
||||||
|
{ token::Store, { furvm::instruction_t::Store, instruction::Variable } },
|
||||||
|
{ token::Call, { furvm::instruction_t::Call, instruction::Function } },
|
||||||
|
{ token::Jmp, { furvm::instruction_t::Jump, instruction::Label } },
|
||||||
|
{ token::Jnz, { furvm::instruction_t::JumpNotZero, instruction::Label } },
|
||||||
|
{ token::Ret, { furvm::instruction_t::Return } },
|
||||||
|
};
|
||||||
|
// NOLINTEND
|
||||||
|
|
||||||
|
const char* token_type(enum token::type type) {
|
||||||
|
switch (type) {
|
||||||
|
case token::Identifier: return "identifier";
|
||||||
|
case token::Signed: return "signed";
|
||||||
|
case token::Unsigned: return "unsigned";
|
||||||
|
case token::Monkey: return "'@'";
|
||||||
|
case token::Dolar: return "'$'";
|
||||||
|
case token::Sha256: return "'#'";
|
||||||
|
case token::Percent: return "'%'";
|
||||||
|
case token::EqSign: return "'='";
|
||||||
|
case token::Dot: return "'.'";
|
||||||
|
case token::Colon: return "':'";
|
||||||
|
case token::Func: return "func";
|
||||||
|
case token::Type: return "type";
|
||||||
|
case token::Native: return "native";
|
||||||
|
case token::Import: return "import";
|
||||||
|
case token::Public: return "public";
|
||||||
|
case token::Private: return "private";
|
||||||
|
case token::Push: return "push";
|
||||||
|
case token::Array: return "array";
|
||||||
|
case token::Get: return "get";
|
||||||
|
case token::Set: return "set";
|
||||||
|
case token::Drop: return "drop";
|
||||||
|
case token::Dup: return "dup";
|
||||||
|
case token::Swap: return "swap";
|
||||||
|
case token::Clone: return "clone";
|
||||||
|
case token::Ref: return "ref";
|
||||||
|
case token::Add: return "add";
|
||||||
|
case token::Sub: return "sub";
|
||||||
|
case token::Mul: return "mul";
|
||||||
|
case token::Div: return "div";
|
||||||
|
case token::Mod: return "mod";
|
||||||
|
case token::Eq: return "eq";
|
||||||
|
case token::Neq: return "neq";
|
||||||
|
case token::Lt: return "lt";
|
||||||
|
case token::Gt: return "gt";
|
||||||
|
case token::Le: return "le";
|
||||||
|
case token::Ge: return "ge";
|
||||||
|
case token::Ptrof: return "ptrof";
|
||||||
|
case token::Sizeof: return "sizeof";
|
||||||
|
case token::Lenof: return "lenof";
|
||||||
|
case token::Load: return "load";
|
||||||
|
case token::Store: return "store";
|
||||||
|
case token::Call: return "call";
|
||||||
|
case token::Jmp: return "jmp";
|
||||||
|
case token::Jnz: return "jnz";
|
||||||
|
case token::Ret: return "ret";
|
||||||
|
case token::Count: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mod_context {
|
||||||
|
struct label_context {
|
||||||
|
struct function_info {
|
||||||
|
std::string name;
|
||||||
|
furvm::function_sig signature;
|
||||||
|
bool pub;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::size_t INVALID = std::numeric_limits<std::size_t>::max();
|
||||||
|
|
||||||
|
std::size_t offset = INVALID;
|
||||||
|
std::vector<function_info> functions;
|
||||||
|
std::vector<std::size_t> unknowns;
|
||||||
|
};
|
||||||
|
|
||||||
|
furvm::mod mod;
|
||||||
|
std::unordered_map<std::pair<std::string, furvm::function_sig>,
|
||||||
|
furvm::function_h,
|
||||||
|
furlang::utility::
|
||||||
|
pair_hash<std::string, furvm::function_sig, std::hash<std::string>, furvm::detail::function_sig_hash>>
|
||||||
|
functions;
|
||||||
|
std::unordered_map<std::string, furvm::mod_type_h> types;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, label_context> labels;
|
||||||
|
|
||||||
|
struct token_result {
|
||||||
|
generator_error error;
|
||||||
|
token token;
|
||||||
|
|
||||||
|
struct token* operator->() { return &token; }
|
||||||
|
struct token& operator*() { return token; }
|
||||||
|
|
||||||
|
bool operator!() const { return error.type != generator_error::Success; }
|
||||||
|
};
|
||||||
|
|
||||||
|
mod_context() {
|
||||||
|
types.emplace("s8", mod.emplace_type(furvm::mod_type::S8));
|
||||||
|
types.emplace("u8", mod.emplace_type(furvm::mod_type::U8));
|
||||||
|
types.emplace("s16", mod.emplace_type(furvm::mod_type::S16));
|
||||||
|
types.emplace("u16", mod.emplace_type(furvm::mod_type::U16));
|
||||||
|
types.emplace("s32", mod.emplace_type(furvm::mod_type::S32));
|
||||||
|
types.emplace("u32", mod.emplace_type(furvm::mod_type::U32));
|
||||||
|
types.emplace("s64", mod.emplace_type(furvm::mod_type::S64));
|
||||||
|
types.emplace("u64", mod.emplace_type(furvm::mod_type::U64));
|
||||||
|
}
|
||||||
|
|
||||||
|
static token_result next_token(lexer& lexer) {
|
||||||
|
auto token = lexer.next_token();
|
||||||
|
if (token.has_error()) {
|
||||||
|
switch (token.error().type) {
|
||||||
|
case lexer_error::EndOfFile:
|
||||||
|
return { { generator_error::UnexpectedEof, "Unexpected end of file" }, { token::Count } };
|
||||||
|
case lexer_error::UnknownCharacter:
|
||||||
|
return { { generator_error::UnknownCharacter, token.error().message }, { token::Count } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { { generator_error::Success }, *token };
|
||||||
|
}
|
||||||
|
|
||||||
|
static token_result eat_token(lexer& lexer, enum token::type type) {
|
||||||
|
auto token = next_token(lexer);
|
||||||
|
if (!token) return token;
|
||||||
|
if (token.token.type != type) {
|
||||||
|
return { { generator_error::UnexpectedToken,
|
||||||
|
"Expected "s + token_type(type) + ", but got " + token_type(token->type) },
|
||||||
|
{ token::Count } };
|
||||||
|
}
|
||||||
|
return { { generator_error::Success }, token.token };
|
||||||
|
}
|
||||||
|
|
||||||
|
generator_error generate(lexer& lexer) {
|
||||||
|
auto result = next_token(lexer);
|
||||||
|
if (result.error.type == generator_error::UnexpectedEof) return { generator_error::Eof };
|
||||||
|
if (!result) return result.error;
|
||||||
|
switch (result->type) {
|
||||||
|
case token::Identifier: {
|
||||||
|
std::string labelName = std::string(result->value.string);
|
||||||
|
|
||||||
|
result = eat_token(lexer, token::Colon);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
auto& label = labels[labelName];
|
||||||
|
if (label.offset != label_context::INVALID)
|
||||||
|
return { generator_error::UnexpectedToken, "Label "s + labelName + " already exists" };
|
||||||
|
label.offset = mod.bytecode().size();
|
||||||
|
|
||||||
|
for (auto& func : label.functions) {
|
||||||
|
furvm::function_h handle =
|
||||||
|
(func.pub ? mod.emplace_function(func.name, std::move(func.signature), label.offset)
|
||||||
|
: mod.emplace_function(std::move(func.signature), label.offset));
|
||||||
|
|
||||||
|
functions.emplace(std::make_pair(std::move(func.name), std::move(func.signature)), handle);
|
||||||
|
|
||||||
|
handle.dispatch();
|
||||||
|
}
|
||||||
|
for (auto unknown : label.unknowns) {
|
||||||
|
std::ptrdiff_t jmpOff = static_cast<std::ptrdiff_t>(label.offset - unknown);
|
||||||
|
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
||||||
|
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||||
|
assert(false); // TODO: Further jumps are not implemented
|
||||||
|
}
|
||||||
|
mod.bytecode()[unknown - 1] = jmpOff;
|
||||||
|
}
|
||||||
|
label.functions = {};
|
||||||
|
label.unknowns = {};
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Func:
|
||||||
|
case token::Type:
|
||||||
|
case token::Public:
|
||||||
|
case token::Private: {
|
||||||
|
bool pub = false;
|
||||||
|
if (result->type == token::Public) {
|
||||||
|
pub = true;
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return { result.error };
|
||||||
|
} else if (result->type == token::Private) {
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return { result.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->type == token::Func) {
|
||||||
|
auto nameRes = next_token(lexer);
|
||||||
|
if (!nameRes) return nameRes.error;
|
||||||
|
|
||||||
|
furvm::function_sig signature;
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
while (result->type == token::Dolar) {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (auto it = types.find(std::string(result->value.string)); it != types.end()) {
|
||||||
|
signature.params.push_back(it->second);
|
||||||
|
} else {
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
}
|
||||||
|
if (result->type != token::EqSign) return result.error;
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (result->type == token::Dolar) {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (auto it = types.find(std::string(result->value.string)); it != types.end()) {
|
||||||
|
signature.returnType = it->second;
|
||||||
|
} else {
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (result->type) {
|
||||||
|
case token::Sha256: {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
std::string name = std::string(nameRes->value.string);
|
||||||
|
|
||||||
|
std::string labelName = std::string(result->value.string);
|
||||||
|
std::size_t offset = 0;
|
||||||
|
if (auto it = labels.find(labelName); it != labels.end()) {
|
||||||
|
offset = it->second.offset;
|
||||||
|
} else {
|
||||||
|
labels[labelName].functions.emplace_back(
|
||||||
|
label_context::function_info{ std::move(name), std::move(signature), pub });
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
furvm::function_h handle =
|
||||||
|
(pub ? mod.emplace_function(name, signature, offset) : mod.emplace_function(signature, offset));
|
||||||
|
|
||||||
|
functions.emplace(std::make_pair(name, std::move(signature)), handle);
|
||||||
|
|
||||||
|
handle.dispatch();
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
case token::Native: {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
std::string nativeName = std::string(result->value.string);
|
||||||
|
|
||||||
|
std::string name = std::string(nameRes->value.string);
|
||||||
|
|
||||||
|
furvm::function_h handle = (pub ? mod.emplace_function(name, signature, std::move(nativeName))
|
||||||
|
: mod.emplace_function(signature, std::move(nativeName)));
|
||||||
|
|
||||||
|
functions.emplace(std::make_pair(name, std::move(signature)), handle);
|
||||||
|
|
||||||
|
handle.dispatch();
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
case token::Import: {
|
||||||
|
throw std::runtime_error("unimplemented");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { generator_error::UnexpectedToken,
|
||||||
|
"Unexpected token "s + token_type(result->type) +
|
||||||
|
", expected label name, `native`, or `import`" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->type == token::Type) {
|
||||||
|
auto nameRes = eat_token(lexer, token::Identifier);
|
||||||
|
if (!nameRes) return nameRes.error;
|
||||||
|
|
||||||
|
result = eat_token(lexer, token::EqSign);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
result = next_token(lexer);
|
||||||
|
if (!result) return result.error;
|
||||||
|
switch (result->type) {
|
||||||
|
case token::Import: {
|
||||||
|
auto typeNameRes = eat_token(lexer, token::Identifier);
|
||||||
|
if (!typeNameRes) return typeNameRes.error;
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
case token::Array: {
|
||||||
|
result = eat_token(lexer, token::Dolar);
|
||||||
|
if (!result) return result.error;
|
||||||
|
|
||||||
|
auto typeNameRes = eat_token(lexer, token::Identifier);
|
||||||
|
if (!typeNameRes) return typeNameRes.error;
|
||||||
|
|
||||||
|
auto size = eat_token(lexer, token::Unsigned);
|
||||||
|
if (!size) return size.error;
|
||||||
|
|
||||||
|
furvm::mod_type_id innerId = 0;
|
||||||
|
if (auto it = types.find(std::string(typeNameRes->value.string)); it != types.end()) {
|
||||||
|
innerId = it->second.id();
|
||||||
|
} else {
|
||||||
|
return { generator_error::UnknownType,
|
||||||
|
"Unknown type "s + std::string(typeNameRes->value.string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = mod.emplace_type(innerId, size->value.uint);
|
||||||
|
types.emplace(std::string(nameRes->value.string), type);
|
||||||
|
type.dispatch();
|
||||||
|
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { generator_error::UnexpectedToken,
|
||||||
|
"Unexpected token "s + token_type(result->type) +
|
||||||
|
", expected either type, `import`, or `array`" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { generator_error::UnexpectedToken,
|
||||||
|
"Unexpected token "s + token_type(result->type) + ", expected either `func` or `type`" };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Push: {
|
||||||
|
auto result = eat_token(lexer, token::Dolar);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto typeName = eat_token(lexer, token::Identifier);
|
||||||
|
if (!typeName) return result.error;
|
||||||
|
auto it = types.find(std::string(typeName->value.string));
|
||||||
|
if (it == types.end())
|
||||||
|
return { generator_error::UnexpectedToken, "Unknown type "s + std::string(typeName->value.string) };
|
||||||
|
// TODO: Add support for signed integers
|
||||||
|
auto value = eat_token(lexer, token::Unsigned);
|
||||||
|
if (!value) return result.error;
|
||||||
|
auto type = it->second;
|
||||||
|
switch (type->type) {
|
||||||
|
case furvm::mod_type::S8: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS8));
|
||||||
|
mod.bytecode().push_back(value->value.uint & 0xFF);
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::U8: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU8));
|
||||||
|
mod.bytecode().push_back(value->value.uint & 0xFF);
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::S16: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS16));
|
||||||
|
mod.bytecode().push_back(value->value.uint & 0xFF);
|
||||||
|
mod.bytecode().push_back((value->value.uint >> 8) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::U16: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU16));
|
||||||
|
mod.bytecode().push_back(value->value.uint & 0xFF);
|
||||||
|
mod.bytecode().push_back((value->value.uint >> 8) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::S32: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS32));
|
||||||
|
mod.bytecode().push_back(value->value.uint & 0xFF);
|
||||||
|
} break;
|
||||||
|
case furvm::mod_type::U32: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU32));
|
||||||
|
mod.bytecode().push_back(value->value.uint & 0xFF);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
return { generator_error::UnexpectedToken, "Unexpected type "s + std::string(typeName->value.string) };
|
||||||
|
}
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Array:
|
||||||
|
case token::Get:
|
||||||
|
case token::Set:
|
||||||
|
case token::Drop:
|
||||||
|
case token::Dup:
|
||||||
|
case token::Swap:
|
||||||
|
case token::Clone:
|
||||||
|
case token::Ref:
|
||||||
|
case token::Add:
|
||||||
|
case token::Sub:
|
||||||
|
case token::Mul:
|
||||||
|
case token::Div:
|
||||||
|
case token::Mod:
|
||||||
|
case token::Eq:
|
||||||
|
case token::Neq:
|
||||||
|
case token::Lt:
|
||||||
|
case token::Gt:
|
||||||
|
case token::Le:
|
||||||
|
case token::Ge:
|
||||||
|
case token::Ptrof:
|
||||||
|
case token::Sizeof:
|
||||||
|
case token::Lenof:
|
||||||
|
case token::Load:
|
||||||
|
case token::Store:
|
||||||
|
case token::Call:
|
||||||
|
case token::Jmp:
|
||||||
|
case token::Jnz:
|
||||||
|
case token::Ret: {
|
||||||
|
auto it = instructions.find(result->type);
|
||||||
|
assert(it != instructions.end());
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(it->second.fur));
|
||||||
|
switch (it->second.arg) {
|
||||||
|
case instruction::None: break;
|
||||||
|
case instruction::Type: {
|
||||||
|
result = eat_token(lexer, token::Dolar);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto type = types.find(std::string(result->value.string));
|
||||||
|
if (type == types.end())
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
auto id = type->second.id();
|
||||||
|
mod.bytecode().push_back((id >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 8) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 16) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 24) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case instruction::Constant: {
|
||||||
|
assert(false); // TODO: Unimplemented
|
||||||
|
} break;
|
||||||
|
case instruction::Variable: {
|
||||||
|
result = eat_token(lexer, token::Percent);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Unsigned);
|
||||||
|
if (!result) return result.error;
|
||||||
|
std::uint16_t var = result->value.uint;
|
||||||
|
mod.bytecode().push_back((var >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((var >> 8) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case instruction::Function: {
|
||||||
|
furvm::function_sig signature;
|
||||||
|
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
||||||
|
result->type == token::Dolar) {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto type = types.find(std::string(result->value.string));
|
||||||
|
if (type == types.end())
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
signature.params.push_back(type->second);
|
||||||
|
}
|
||||||
|
if (!result || result->type != token::Identifier) return result.error;
|
||||||
|
std::string name(result->value.string);
|
||||||
|
|
||||||
|
auto func = functions.find(std::make_pair(name, signature));
|
||||||
|
if (func == functions.end())
|
||||||
|
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
|
||||||
|
auto id = func->second.id();
|
||||||
|
mod.bytecode().push_back((id >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((id >> 8) & 0xFF);
|
||||||
|
} break;
|
||||||
|
case instruction::Label: {
|
||||||
|
result = eat_token(lexer, token::Sha256);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
auto& label = labels[std::string(result->value.string)];
|
||||||
|
auto offset = label.offset;
|
||||||
|
if (offset == label_context::INVALID) {
|
||||||
|
label.unknowns.push_back(mod.bytecode().size() + 1);
|
||||||
|
mod.bytecode().push_back(0);
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
std::ptrdiff_t jmpOff = static_cast<std::ptrdiff_t>(offset - mod.bytecode().size() - 1);
|
||||||
|
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
|
||||||
|
jmpOff > std::numeric_limits<std::int8_t>::max()) {
|
||||||
|
assert(false); // TODO: Further jumps are not implemented
|
||||||
|
}
|
||||||
|
mod.bytecode().push_back(jmpOff);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
|
case token::Signed:
|
||||||
|
case token::Unsigned:
|
||||||
|
case token::Colon:
|
||||||
|
case token::Monkey:
|
||||||
|
case token::Dolar:
|
||||||
|
case token::Sha256:
|
||||||
|
case token::Percent:
|
||||||
|
case token::EqSign:
|
||||||
|
case token::Dot:
|
||||||
|
case token::Native:
|
||||||
|
case token::Import:
|
||||||
|
case token::Count: break;
|
||||||
|
}
|
||||||
|
return { generator_error::UnexpectedToken, "Unexpected token "s + token_type(result->type) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
generator::result generator::generate(lexer lexer) {
|
||||||
|
mod_context context;
|
||||||
|
generator_error error;
|
||||||
|
do {
|
||||||
|
error = context.generate(lexer);
|
||||||
|
} while (error.type == generator_error::Success);
|
||||||
|
if (error.type == generator_error::Eof) return { { generator_error::Success }, std::move(context.mod) };
|
||||||
|
return { error };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furas
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "furas/lexer.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace furas {
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
token_r lexer::next_token() {
|
||||||
|
while (m_cursor < m_content.size() && std::isspace(m_content[m_cursor]) != 0) {
|
||||||
|
if (m_content[m_cursor] == '\n') {
|
||||||
|
m_lineStart = m_cursor + 1;
|
||||||
|
++m_column;
|
||||||
|
}
|
||||||
|
++m_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add support for single-line comments
|
||||||
|
// TODO: Add support for multi-line comments
|
||||||
|
|
||||||
|
if (m_cursor >= m_content.size()) return token_r{ lexer_error{ lexer_error::EndOfFile, location() } };
|
||||||
|
|
||||||
|
// TODO: Add support for negative integers (I am positive thanks to stasiu :v:)
|
||||||
|
// TODO: Add support for hexadecimal and binary numeric literals
|
||||||
|
if (std::isdigit(m_content[m_cursor]) != 0) {
|
||||||
|
std::uint64_t num = 0;
|
||||||
|
while (m_cursor < m_content.size() && std::isdigit(m_content[m_cursor]) != 0) {
|
||||||
|
num *= 10;
|
||||||
|
num += m_content[m_cursor++] - '0';
|
||||||
|
}
|
||||||
|
return { num };
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<std::string_view, enum token::type> s_tokens = {
|
||||||
|
{ "func", token::Func },
|
||||||
|
{ "type", token::Type },
|
||||||
|
{ "native", token::Native },
|
||||||
|
{ "import", token::Import },
|
||||||
|
{ "public", token::Public },
|
||||||
|
{ "private", token::Private },
|
||||||
|
|
||||||
|
{ "push", token::Push },
|
||||||
|
{ "array", token::Array },
|
||||||
|
{ "get", token::Get },
|
||||||
|
{ "set", token::Set },
|
||||||
|
{ "drop", token::Drop },
|
||||||
|
{ "dup", token::Dup },
|
||||||
|
{ "swap", token::Swap },
|
||||||
|
{ "clone", token::Clone },
|
||||||
|
{ "ref", token::Ref },
|
||||||
|
{ "add", token::Add },
|
||||||
|
{ "sub", token::Sub },
|
||||||
|
{ "mul", token::Mul },
|
||||||
|
{ "div", token::Div },
|
||||||
|
{ "mod", token::Mod },
|
||||||
|
{ "eq", token::Eq },
|
||||||
|
{ "neq", token::Neq },
|
||||||
|
{ "lt", token::Lt },
|
||||||
|
{ "gt", token::Gt },
|
||||||
|
{ "le", token::Le },
|
||||||
|
{ "ge", token::Ge },
|
||||||
|
{ "ptrof", token::Ptrof },
|
||||||
|
{ "sizeof", token::Sizeof },
|
||||||
|
{ "lenof", token::Lenof },
|
||||||
|
{ "load", token::Load },
|
||||||
|
{ "store", token::Store },
|
||||||
|
{ "call", token::Call },
|
||||||
|
{ "jmp", token::Jmp },
|
||||||
|
{ "jnz", token::Jnz },
|
||||||
|
{ "ret", token::Ret },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std::isalnum(m_content[m_cursor]) != 0) {
|
||||||
|
std::size_t begin = m_cursor++;
|
||||||
|
while (m_cursor < m_content.size() && (std::isalnum(m_content[m_cursor]) != 0 || m_content[m_cursor] == '_'))
|
||||||
|
++m_cursor;
|
||||||
|
std::string_view str = m_content.substr(begin, m_cursor - begin);
|
||||||
|
if (auto it = s_tokens.find(str); it != s_tokens.end()) return { it->second };
|
||||||
|
return { token::Identifier, str };
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_content[m_cursor]) {
|
||||||
|
case '@': ++m_cursor; return { token::Monkey };
|
||||||
|
case '$': ++m_cursor; return { token::Dolar };
|
||||||
|
case '#': ++m_cursor; return { token::Sha256 };
|
||||||
|
case '%': ++m_cursor; return { token::Percent };
|
||||||
|
case '=': ++m_cursor; return { token::EqSign };
|
||||||
|
case '.': ++m_cursor; return { token::Dot };
|
||||||
|
case ':': ++m_cursor; return { token::Colon };
|
||||||
|
default:
|
||||||
|
return token_r{
|
||||||
|
lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furas
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#include "furas/gen.hpp"
|
||||||
|
#include "furas/lexer.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <furvm/module.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { // NOLINT
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "feed me more arguments daddy >_<\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
std::cerr << "too much O_O\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* filepath = argv[1];
|
||||||
|
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate | std::ios::in);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "file won't open >~<\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string content;
|
||||||
|
content.resize(file.tellg());
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(content.data(), static_cast<std::streamsize>(content.size()));
|
||||||
|
|
||||||
|
auto result = furas::generator::generate(furas::lexer(filepath, content));
|
||||||
|
if (result.error.type != furas::generator_error::Success) {
|
||||||
|
std::cerr << result.error.message << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.mod.serialize(std::cout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -113,6 +113,8 @@ enum class unaryop_expression_node_t {
|
|||||||
PostfixIncrement, /**< Postfix increment */
|
PostfixIncrement, /**< Postfix increment */
|
||||||
PrefixDecrement, /**< Prefix decrement */
|
PrefixDecrement, /**< Prefix decrement */
|
||||||
PostfixDecrement, /**< Postfix decrement */
|
PostfixDecrement, /**< Postfix decrement */
|
||||||
|
Pointerof, /**< Pointerof */
|
||||||
|
Sizeof, /**< Sizeof */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ enum class keyword_token {
|
|||||||
Native, /**< `native` */
|
Native, /**< `native` */
|
||||||
Public, /**< `public` */
|
Public, /**< `public` */
|
||||||
Private, /**< `private` */
|
Private, /**< `private` */
|
||||||
|
Pointerof, /**< `pointerof` */
|
||||||
|
Sizeof, /**< `sizeof` */
|
||||||
|
|
||||||
Int32, /**< `int32` */
|
Int32, /**< `int32` */
|
||||||
};
|
};
|
||||||
@@ -173,6 +175,8 @@ static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword)
|
|||||||
case keyword_token::Native: return os << "native";
|
case keyword_token::Native: return os << "native";
|
||||||
case keyword_token::Public: return os << "public";
|
case keyword_token::Public: return os << "public";
|
||||||
case keyword_token::Private: return os << "private";
|
case keyword_token::Private: return os << "private";
|
||||||
|
case keyword_token::Pointerof: return os << "pointerof";
|
||||||
|
case keyword_token::Sizeof: return os << "sizeof";
|
||||||
case keyword_token::Int32: return os << "int32";
|
case keyword_token::Int32: return os << "int32";
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
@@ -190,6 +194,8 @@ static inline std::string operator+(const std::string& str, keyword_token keywor
|
|||||||
case keyword_token::Native: return str + "native";
|
case keyword_token::Native: return str + "native";
|
||||||
case keyword_token::Public: return str + "public";
|
case keyword_token::Public: return str + "public";
|
||||||
case keyword_token::Private: return str + "private";
|
case keyword_token::Private: return str + "private";
|
||||||
|
case keyword_token::Pointerof: return str + "pointerof";
|
||||||
|
case keyword_token::Sizeof: return str + "sizeof";
|
||||||
case keyword_token::Int32: return str + "int32";
|
case keyword_token::Int32: return str + "int32";
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ std::ostream& operator<<(std::ostream& os, unaryop_expression_node_t type) {
|
|||||||
case unaryop_expression_node_t::PostfixIncrement: return os << "++";
|
case unaryop_expression_node_t::PostfixIncrement: return os << "++";
|
||||||
case unaryop_expression_node_t::PrefixDecrement:
|
case unaryop_expression_node_t::PrefixDecrement:
|
||||||
case unaryop_expression_node_t::PostfixDecrement: return os << "--";
|
case unaryop_expression_node_t::PostfixDecrement: return os << "--";
|
||||||
|
case unaryop_expression_node_t::Pointerof: return os << "pointerof";
|
||||||
|
case unaryop_expression_node_t::Sizeof: return os << "sizeof";
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
@@ -54,6 +56,8 @@ std::ostream& unary_op_expression_node::print(std::ostream& os) const {
|
|||||||
case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')';
|
case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')';
|
||||||
case unaryop_expression_node_t::PostfixIncrement:
|
case unaryop_expression_node_t::PostfixIncrement:
|
||||||
case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')';
|
case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')';
|
||||||
|
case unaryop_expression_node_t::Pointerof: return os << "pointerof " << *m_node;
|
||||||
|
case unaryop_expression_node_t::Sizeof: return os << "sizeof " << *m_node;
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-26
@@ -21,12 +21,14 @@ furvm::mod furvm_generator::generate(furlang::ir::mod& mod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) {
|
void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) {
|
||||||
|
furvm::function_sig signature; // TODO: Complete
|
||||||
|
|
||||||
switch (function.type()) {
|
switch (function.type()) {
|
||||||
case furlang::ir::function_t::Normal: {
|
case furlang::ir::function_t::Normal: {
|
||||||
if (function.access() == furlang::ir::function_access_t::Public)
|
if (function.access() == furlang::ir::function_access_t::Public)
|
||||||
mod.emplace_function(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
|
mod.emplace_function(function.name(), std::move(signature), mod.bytecode().size()).dispatch();
|
||||||
else
|
else
|
||||||
mod.emplace_function_private(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
|
mod.emplace_function(std::move(signature), mod.bytecode().size()).dispatch();
|
||||||
|
|
||||||
function_context ctx;
|
function_context ctx;
|
||||||
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
|
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
|
||||||
@@ -49,28 +51,34 @@ void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::func
|
|||||||
} break;
|
} break;
|
||||||
case furlang::ir::function_t::Native: {
|
case furlang::ir::function_t::Native: {
|
||||||
if (function.access() == furlang::ir::function_access_t::Public)
|
if (function.access() == furlang::ir::function_access_t::Public)
|
||||||
mod.emplace_function(function.name(), function.param_count(), function.name()).dispatch();
|
mod.emplace_function(function.name(), std::move(signature), function.name()).dispatch();
|
||||||
else
|
else
|
||||||
mod.emplace_function_private(function.name(), function.param_count(), function.name()).dispatch();
|
mod.emplace_function(std::move(signature), function.name()).dispatch();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline furvm::instruction_t binary_op_type(furlang::ir::binary_op_instruction_t type) {
|
static inline furvm::instruction_t op_type(furlang::ir::instruction_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case furlang::ir::binary_op_instruction_t::Add: return furvm::instruction_t::Add;
|
// Unary
|
||||||
case furlang::ir::binary_op_instruction_t::Sub: return furvm::instruction_t::Sub;
|
case furlang::ir::instruction_t::Pointerof: return furvm::instruction_t::Pointerof;
|
||||||
case furlang::ir::binary_op_instruction_t::Mul: return furvm::instruction_t::Mul;
|
case furlang::ir::instruction_t::Sizeof: return furvm::instruction_t::Sizeof;
|
||||||
case furlang::ir::binary_op_instruction_t::Div: return furvm::instruction_t::Div;
|
|
||||||
case furlang::ir::binary_op_instruction_t::Mod: return furvm::instruction_t::Mod;
|
// Binary
|
||||||
case furlang::ir::binary_op_instruction_t::Eq: return furvm::instruction_t::Equals;
|
case furlang::ir::instruction_t::Add: return furvm::instruction_t::Add;
|
||||||
case furlang::ir::binary_op_instruction_t::NotEq: return furvm::instruction_t::NotEquals;
|
case furlang::ir::instruction_t::Sub: return furvm::instruction_t::Sub;
|
||||||
case furlang::ir::binary_op_instruction_t::LessThan: return furvm::instruction_t::LessThan;
|
case furlang::ir::instruction_t::Mul: return furvm::instruction_t::Mul;
|
||||||
case furlang::ir::binary_op_instruction_t::GreaterThan: return furvm::instruction_t::GreaterThan;
|
case furlang::ir::instruction_t::Div: return furvm::instruction_t::Div;
|
||||||
case furlang::ir::binary_op_instruction_t::LessEq: return furvm::instruction_t::LessEqual;
|
case furlang::ir::instruction_t::Mod: return furvm::instruction_t::Mod;
|
||||||
case furlang::ir::binary_op_instruction_t::GreaterEq: return furvm::instruction_t::GreaterEqual;
|
case furlang::ir::instruction_t::Eq: return furvm::instruction_t::Equals;
|
||||||
|
case furlang::ir::instruction_t::NotEq: return furvm::instruction_t::NotEquals;
|
||||||
|
case furlang::ir::instruction_t::LessThan: return furvm::instruction_t::LessThan;
|
||||||
|
case furlang::ir::instruction_t::GreaterThan: return furvm::instruction_t::GreaterThan;
|
||||||
|
case furlang::ir::instruction_t::LessEq: return furvm::instruction_t::LessEqual;
|
||||||
|
case furlang::ir::instruction_t::GreaterEq: return furvm::instruction_t::GreaterEqual;
|
||||||
|
|
||||||
|
default: throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
throw std::runtime_error("unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void furvm_generator::generate_instruction(furvm::mod& mod,
|
void furvm_generator::generate_instruction(furvm::mod& mod,
|
||||||
@@ -90,9 +98,31 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
|
|||||||
mod.bytecode().push_back((var >> 8) & 0xFF);
|
mod.bytecode().push_back((var >> 8) & 0xFF);
|
||||||
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
|
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
|
||||||
} break;
|
} break;
|
||||||
case furlang::ir::instruction_t::BinaryOp: {
|
case furlang::ir::instruction_t::Add:
|
||||||
const auto& op = dynamic_cast<const furlang::ir::binary_op_instruction&>(instr);
|
case furlang::ir::instruction_t::Sub:
|
||||||
mod.bytecode().push_back(static_cast<furvm::byte>(binary_op_type(op.op_type())));
|
case furlang::ir::instruction_t::Mul:
|
||||||
|
case furlang::ir::instruction_t::Div:
|
||||||
|
case furlang::ir::instruction_t::Mod:
|
||||||
|
case furlang::ir::instruction_t::Eq:
|
||||||
|
case furlang::ir::instruction_t::NotEq:
|
||||||
|
case furlang::ir::instruction_t::LessThan:
|
||||||
|
case furlang::ir::instruction_t::GreaterThan:
|
||||||
|
case furlang::ir::instruction_t::LessEq:
|
||||||
|
case furlang::ir::instruction_t::GreaterEq: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(op_type(instr.type())));
|
||||||
|
|
||||||
|
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
|
||||||
|
ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
|
||||||
|
}
|
||||||
|
auto var = ctx.variables[instr.destination().reg()];
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Store));
|
||||||
|
mod.bytecode().push_back((var >> 0) & 0xFF);
|
||||||
|
mod.bytecode().push_back((var >> 8) & 0xFF);
|
||||||
|
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
|
||||||
|
} break;
|
||||||
|
case furlang::ir::instruction_t::Pointerof:
|
||||||
|
case furlang::ir::instruction_t::Sizeof: {
|
||||||
|
mod.bytecode().push_back(static_cast<furvm::byte>(op_type(instr.type())));
|
||||||
|
|
||||||
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
|
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
|
||||||
ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
|
ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
|
||||||
@@ -113,17 +143,13 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
|
|||||||
generate_jump(mod, ctx, branch.else_block(), false);
|
generate_jump(mod, ctx, branch.else_block(), false);
|
||||||
} break;
|
} break;
|
||||||
case furlang::ir::instruction_t::Return: {
|
case furlang::ir::instruction_t::Return: {
|
||||||
if (instr.sources().empty()) {
|
|
||||||
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Return));
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Return));
|
||||||
} else {
|
|
||||||
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::ReturnValue));
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
case furlang::ir::instruction_t::Call: {
|
case furlang::ir::instruction_t::Call: {
|
||||||
const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr);
|
const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr);
|
||||||
|
|
||||||
// TODO: Implement a queue for unknown functions
|
// TODO: Implement a queue for unknown functions
|
||||||
furvm::function_id func = mod.get_function_id(call.name());
|
furvm::function_id func = mod.function_at(call.name(), furvm::function_sig{}).id(); // TODO: Complete
|
||||||
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call));
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call));
|
||||||
mod.bytecode().push_back((func >> 0) & 0xFF);
|
mod.bytecode().push_back((func >> 0) & 0xFF);
|
||||||
mod.bytecode().push_back((func >> 8) & 0xFF);
|
mod.bytecode().push_back((func >> 8) & 0xFF);
|
||||||
@@ -144,7 +170,7 @@ void furvm_generator::generate_operand(furvm::mod& mod, function_context& ctx, c
|
|||||||
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
|
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
|
||||||
} break;
|
} break;
|
||||||
case furlang::ir::operand_t::Integer: {
|
case furlang::ir::operand_t::Integer: {
|
||||||
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushB2I));
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS32));
|
||||||
mod.bytecode().push_back(operand.integer());
|
mod.bytecode().push_back(operand.integer());
|
||||||
} break;
|
} break;
|
||||||
case furlang::ir::operand_t::Variable:
|
case furlang::ir::operand_t::Variable:
|
||||||
|
|||||||
@@ -132,23 +132,36 @@ void ir_generator::visit(const ast::var_read_expression_node& node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ir_generator::visit(const ast::unary_op_expression_node& node) {
|
static inline furlang::ir::instruction_t unary_op_instruction_t(ast::unaryop_expression_node_t type) {
|
||||||
throw std::runtime_error("unimplemented");
|
switch (type) {
|
||||||
|
case ast::unaryop_expression_node_t::Pointerof: return furlang::ir::instruction_t::Pointerof;
|
||||||
|
case ast::unaryop_expression_node_t::Sizeof: return furlang::ir::instruction_t::Sizeof;
|
||||||
|
default: throw std::runtime_error("unimplemented");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) {
|
void ir_generator::visit(const ast::unary_op_expression_node& node) {
|
||||||
|
node.get_node()->accept(*this);
|
||||||
|
ir_register src = m_registerCounter - 1;
|
||||||
|
ir_register dst = m_registerCounter++;
|
||||||
|
push<furlang::ir::unary_instruction>(unary_op_instruction_t(node.type()),
|
||||||
|
ir::operand::new_reg(src),
|
||||||
|
ir::operand::new_reg(dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline furlang::ir::instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ast::binop_expression_node_t::Add: return furlang::ir::binary_op_instruction_t::Add;
|
case ast::binop_expression_node_t::Add: return furlang::ir::instruction_t::Add;
|
||||||
case ast::binop_expression_node_t::Sub: return furlang::ir::binary_op_instruction_t::Sub;
|
case ast::binop_expression_node_t::Sub: return furlang::ir::instruction_t::Sub;
|
||||||
case ast::binop_expression_node_t::Mul: return furlang::ir::binary_op_instruction_t::Mul;
|
case ast::binop_expression_node_t::Mul: return furlang::ir::instruction_t::Mul;
|
||||||
case ast::binop_expression_node_t::Div: return furlang::ir::binary_op_instruction_t::Div;
|
case ast::binop_expression_node_t::Div: return furlang::ir::instruction_t::Div;
|
||||||
case ast::binop_expression_node_t::Mod: return furlang::ir::binary_op_instruction_t::Mod;
|
case ast::binop_expression_node_t::Mod: return furlang::ir::instruction_t::Mod;
|
||||||
case ast::binop_expression_node_t::Equal: return furlang::ir::binary_op_instruction_t::Eq;
|
case ast::binop_expression_node_t::Equal: return furlang::ir::instruction_t::Eq;
|
||||||
case ast::binop_expression_node_t::NotEqual: return furlang::ir::binary_op_instruction_t::NotEq;
|
case ast::binop_expression_node_t::NotEqual: return furlang::ir::instruction_t::NotEq;
|
||||||
case ast::binop_expression_node_t::LessThan: return furlang::ir::binary_op_instruction_t::LessThan;
|
case ast::binop_expression_node_t::LessThan: return furlang::ir::instruction_t::LessThan;
|
||||||
case ast::binop_expression_node_t::GreaterThan: return furlang::ir::binary_op_instruction_t::GreaterThan;
|
case ast::binop_expression_node_t::GreaterThan: return furlang::ir::instruction_t::GreaterThan;
|
||||||
case ast::binop_expression_node_t::LessEqual: return furlang::ir::binary_op_instruction_t::LessEq;
|
case ast::binop_expression_node_t::LessEqual: return furlang::ir::instruction_t::LessEq;
|
||||||
case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::binary_op_instruction_t::GreaterEq;
|
case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::instruction_t::GreaterEq;
|
||||||
case ast::binop_expression_node_t::None:
|
case ast::binop_expression_node_t::None:
|
||||||
default: throw std::runtime_error("unreachable");
|
default: throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
@@ -160,7 +173,7 @@ void ir_generator::visit(const ast::binary_op_expression_node& node) {
|
|||||||
node.rhs()->accept(*this);
|
node.rhs()->accept(*this);
|
||||||
ir_register rhs = m_registerCounter - 1;
|
ir_register rhs = m_registerCounter - 1;
|
||||||
ir_register dst = m_registerCounter++;
|
ir_register dst = m_registerCounter++;
|
||||||
push<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()),
|
push<furlang::ir::binary_instruction>(binary_op_instruction_t(node.type()),
|
||||||
ir::operand::new_reg(lhs),
|
ir::operand::new_reg(lhs),
|
||||||
ir::operand::new_reg(rhs),
|
ir::operand::new_reg(rhs),
|
||||||
ir::operand::new_reg(dst));
|
ir::operand::new_reg(dst));
|
||||||
@@ -176,7 +189,7 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
|
|||||||
|
|
||||||
auto compound = node.compound();
|
auto compound = node.compound();
|
||||||
if (compound != ast::binop_expression_node_t::None) {
|
if (compound != ast::binop_expression_node_t::None) {
|
||||||
push<ir::binary_op_instruction>(binary_op_instruction_t(compound),
|
push<ir::binary_instruction>(binary_op_instruction_t(compound),
|
||||||
ir::operand::new_reg(reg),
|
ir::operand::new_reg(reg),
|
||||||
ir::operand::new_reg(rhs),
|
ir::operand::new_reg(rhs),
|
||||||
ir::operand::new_reg(reg));
|
ir::operand::new_reg(reg));
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ token_r lexer::next_token() {
|
|||||||
{ "native", keyword_token::Native },
|
{ "native", keyword_token::Native },
|
||||||
{ "public", keyword_token::Public },
|
{ "public", keyword_token::Public },
|
||||||
{ "private", keyword_token::Private },
|
{ "private", keyword_token::Private },
|
||||||
|
{ "pointerof", keyword_token::Pointerof },
|
||||||
|
{ "sizeof", keyword_token::Sizeof },
|
||||||
{ "int32", keyword_token::Int32 },
|
{ "int32", keyword_token::Int32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+27
-10
@@ -9,6 +9,7 @@
|
|||||||
#include "furc/front/token.hpp"
|
#include "furc/front/token.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -313,27 +314,43 @@ struct unaryop_info {
|
|||||||
std::uint32_t precedence;
|
std::uint32_t precedence;
|
||||||
};
|
};
|
||||||
|
|
||||||
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) {
|
static inline std::optional<unaryop_info> get_unaryop_info(const token_r& token) {
|
||||||
static std::unordered_map<token_t, unaryop_info> s_prefixes = {
|
static std::unordered_map<token_t, unaryop_info> s_prefixes = {
|
||||||
{ token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 3 } },
|
{ token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 2 } },
|
||||||
{ token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 3 } },
|
{ token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 2 } },
|
||||||
{ token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } },
|
{ token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } },
|
||||||
{ token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } },
|
{ token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } },
|
||||||
};
|
};
|
||||||
|
static std::unordered_map<keyword_token, unaryop_info> s_keywords = {
|
||||||
|
{ keyword_token::Pointerof, unaryop_info{ ast::unaryop_expression_node_t::Pointerof, 2 } },
|
||||||
|
{ keyword_token::Sizeof, unaryop_info{ ast::unaryop_expression_node_t::Sizeof, 2 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (token->type == token_t::Keyword) {
|
||||||
|
auto it = s_keywords.find(token->value.keyword);
|
||||||
|
if (it == s_keywords.end()) return {};
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = s_prefixes.find(token->type);
|
||||||
|
if (it == s_prefixes.end()) return {};
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) {
|
||||||
std::shared_ptr<ast::unary_op_expression_node> result;
|
std::shared_ptr<ast::unary_op_expression_node> result;
|
||||||
while (true) {
|
while (true) {
|
||||||
auto it = s_prefixes.find(peek_token()->type);
|
auto opt = get_unaryop_info(peek_token());
|
||||||
if (it == s_prefixes.end()) break;
|
if (!opt.has_value()) break;
|
||||||
auto current = it->second;
|
unaryop_info current = opt.value();
|
||||||
if (current.precedence >= precedence) break;
|
if (current.precedence >= precedence) break;
|
||||||
auto token = next_token();
|
auto token = next_token();
|
||||||
|
|
||||||
ast::expression_node_p expression;
|
ast::expression_node_p expression;
|
||||||
|
|
||||||
auto nextIt = s_prefixes.find(peek_token()->type);
|
opt = get_unaryop_info(peek_token());
|
||||||
if (nextIt != s_prefixes.end()) {
|
if (opt.has_value()) {
|
||||||
auto next = nextIt->second;
|
auto next = opt.value();
|
||||||
auto expr = parse_expression_unary(current.precedence + 1);
|
auto expr = parse_expression_unary(current.precedence + 1);
|
||||||
if (expr.has_error()) {
|
if (expr.has_error()) {
|
||||||
return ast::expression_node_r(ast::error{ expr.error().location });
|
return ast::expression_node_r(ast::error{ expr.error().location });
|
||||||
@@ -464,7 +481,7 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
|
|||||||
} else if (current.type == rhsop_info_t::FuncCall && peek_token().has_value()) {
|
} else if (current.type == rhsop_info_t::FuncCall && peek_token().has_value()) {
|
||||||
if (peek_token()->type != token_t::RParen) {
|
if (peek_token()->type != token_t::RParen) {
|
||||||
while (true) {
|
while (true) {
|
||||||
auto expr = parse_expression_unary(current.precedence + 1);
|
auto expr = parse_expression_unary(16);
|
||||||
if (expr.has_error()) {
|
if (expr.has_error()) {
|
||||||
return ast::expression_node_r(ast::error{ expr.error().location });
|
return ast::expression_node_r(ast::error{ expr.error().location });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -427,7 +428,17 @@ static void sccp_stage(function_context& ctx) {
|
|||||||
case furlang::ir::instruction_t::Assign: {
|
case furlang::ir::instruction_t::Assign: {
|
||||||
newLat = sccp_stage_get_lattice(latticeValues, *instr->sources().front());
|
newLat = sccp_stage_get_lattice(latticeValues, *instr->sources().front());
|
||||||
} break;
|
} break;
|
||||||
case furlang::ir::instruction_t::BinaryOp: {
|
case furlang::ir::instruction_t::Add:
|
||||||
|
case furlang::ir::instruction_t::Sub:
|
||||||
|
case furlang::ir::instruction_t::Mul:
|
||||||
|
case furlang::ir::instruction_t::Div:
|
||||||
|
case furlang::ir::instruction_t::Mod:
|
||||||
|
case furlang::ir::instruction_t::Eq:
|
||||||
|
case furlang::ir::instruction_t::NotEq:
|
||||||
|
case furlang::ir::instruction_t::LessThan:
|
||||||
|
case furlang::ir::instruction_t::GreaterThan:
|
||||||
|
case furlang::ir::instruction_t::LessEq:
|
||||||
|
case furlang::ir::instruction_t::GreaterEq: {
|
||||||
lattice lhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[0]);
|
lattice lhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[0]);
|
||||||
lattice rhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[1]);
|
lattice rhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[1]);
|
||||||
|
|
||||||
@@ -435,40 +446,31 @@ static void sccp_stage(function_context& ctx) {
|
|||||||
newLat.type = lattice::Bottom;
|
newLat.type = lattice::Bottom;
|
||||||
} else if (lhs.type == lattice::Constant && rhs.type == lattice::Constant) {
|
} else if (lhs.type == lattice::Constant && rhs.type == lattice::Constant) {
|
||||||
newLat.type = lattice::Constant;
|
newLat.type = lattice::Constant;
|
||||||
switch (dynamic_cast<const furlang::ir::binary_op_instruction&>(*instr).op_type()) {
|
switch (instr->type()) {
|
||||||
case furlang::ir::binary_op_instruction_t::Add:
|
case furlang::ir::instruction_t::Add: newLat.constant = lhs.constant + rhs.constant; break;
|
||||||
newLat.constant = lhs.constant + rhs.constant;
|
case furlang::ir::instruction_t::Sub: newLat.constant = lhs.constant - rhs.constant; break;
|
||||||
break;
|
case furlang::ir::instruction_t::Mul: newLat.constant = lhs.constant * rhs.constant; break;
|
||||||
case furlang::ir::binary_op_instruction_t::Sub:
|
case furlang::ir::instruction_t::Div: newLat.constant = lhs.constant / rhs.constant; break;
|
||||||
newLat.constant = lhs.constant - rhs.constant;
|
case furlang::ir::instruction_t::Mod: newLat.constant = lhs.constant % rhs.constant; break;
|
||||||
break;
|
case furlang::ir::instruction_t::Eq:
|
||||||
case furlang::ir::binary_op_instruction_t::Mul:
|
|
||||||
newLat.constant = lhs.constant * rhs.constant;
|
|
||||||
break;
|
|
||||||
case furlang::ir::binary_op_instruction_t::Div:
|
|
||||||
newLat.constant = lhs.constant / rhs.constant;
|
|
||||||
break;
|
|
||||||
case furlang::ir::binary_op_instruction_t::Mod:
|
|
||||||
newLat.constant = lhs.constant % rhs.constant;
|
|
||||||
break;
|
|
||||||
case furlang::ir::binary_op_instruction_t::Eq:
|
|
||||||
newLat.constant = (lhs.constant == rhs.constant) ? 1 : 0;
|
newLat.constant = (lhs.constant == rhs.constant) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case furlang::ir::binary_op_instruction_t::NotEq:
|
case furlang::ir::instruction_t::NotEq:
|
||||||
newLat.constant = (lhs.constant != rhs.constant) ? 1 : 0;
|
newLat.constant = (lhs.constant != rhs.constant) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case furlang::ir::binary_op_instruction_t::LessThan:
|
case furlang::ir::instruction_t::LessThan:
|
||||||
newLat.constant = (lhs.constant < rhs.constant) ? 1 : 0;
|
newLat.constant = (lhs.constant < rhs.constant) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case furlang::ir::binary_op_instruction_t::GreaterThan:
|
case furlang::ir::instruction_t::GreaterThan:
|
||||||
newLat.constant = (lhs.constant > rhs.constant) ? 1 : 0;
|
newLat.constant = (lhs.constant > rhs.constant) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case furlang::ir::binary_op_instruction_t::LessEq:
|
case furlang::ir::instruction_t::LessEq:
|
||||||
newLat.constant = (lhs.constant <= rhs.constant) ? 1 : 0;
|
newLat.constant = (lhs.constant <= rhs.constant) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case furlang::ir::binary_op_instruction_t::GreaterEq:
|
case furlang::ir::instruction_t::GreaterEq:
|
||||||
newLat.constant = (lhs.constant >= rhs.constant) ? 1 : 0;
|
newLat.constant = (lhs.constant >= rhs.constant) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
|
default: throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
+3
-3
@@ -23,7 +23,7 @@ int main(void) {
|
|||||||
while (x < y) {
|
while (x < y) {
|
||||||
x = x + z;
|
x = x + z;
|
||||||
}
|
}
|
||||||
print(x);
|
print(sizeof x);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
furlang::arena arena{};
|
furlang::arena arena{};
|
||||||
@@ -61,7 +61,7 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto context = std::make_shared<furvm::context>();
|
auto context = std::make_shared<furvm::context>();
|
||||||
auto furvmMod = context->emplace_module("main", furc::back::furvm_generator::generate(mod));
|
auto furvmMod = context->emplace("main", furc::back::furvm_generator::generate(mod));
|
||||||
|
|
||||||
std::ofstream file("./a.fmod", std::ios::binary);
|
std::ofstream file("./a.fmod", std::ios::binary);
|
||||||
furvmMod->serialize(file);
|
furvmMod->serialize(file);
|
||||||
@@ -71,7 +71,7 @@ int main(void) {
|
|||||||
[](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; });
|
[](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; });
|
||||||
|
|
||||||
furvm::executor_h executor = context->emplace_executor(context);
|
furvm::executor_h executor = context->emplace_executor(context);
|
||||||
executor->push_frame(furvmMod, *furvmMod->function_at("main"));
|
executor->push_frame(furvmMod, *furvmMod->function_at("main", furvm::function_sig{}));
|
||||||
|
|
||||||
std::cout << "--- Interpreting:\n";
|
std::cout << "--- Interpreting:\n";
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,19 @@ namespace ir {
|
|||||||
enum class instruction_t {
|
enum class instruction_t {
|
||||||
Alloca, /**< Unused */
|
Alloca, /**< Unused */
|
||||||
Assign, /**< Assign */
|
Assign, /**< Assign */
|
||||||
BinaryOp, /**< Binary operation */
|
Add, /**< Addition */
|
||||||
|
Sub, /**< Subtraction */
|
||||||
|
Mul, /**< Multiplication */
|
||||||
|
Div, /**< Division */
|
||||||
|
Mod, /**< Modulo */
|
||||||
|
Eq, /**< Equal */
|
||||||
|
NotEq, /**< Not equal */
|
||||||
|
LessThan, /**< Less than */
|
||||||
|
GreaterThan, /**< Greater than */
|
||||||
|
LessEq, /**< Less or equal */
|
||||||
|
GreaterEq, /**< Greater or equal */
|
||||||
|
Pointerof, /**< Pointerof */
|
||||||
|
Sizeof, /**< Sizeof */
|
||||||
Call, /**< Call */
|
Call, /**< Call */
|
||||||
Branch, /**< Branch */
|
Branch, /**< Branch */
|
||||||
BranchCond, /**< Conditional branch */
|
BranchCond, /**< Conditional branch */
|
||||||
@@ -28,6 +40,31 @@ enum class instruction_t {
|
|||||||
Phi, /**< Phi function */
|
Phi, /**< Phi function */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline std::ostream& operator<<(std::ostream& os, instruction_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case instruction_t::Alloca: return os << "alloca";
|
||||||
|
case instruction_t::Assign: return os << "assign";
|
||||||
|
case instruction_t::Add: return os << "add";
|
||||||
|
case instruction_t::Sub: return os << "sub";
|
||||||
|
case instruction_t::Mul: return os << "mul";
|
||||||
|
case instruction_t::Div: return os << "div";
|
||||||
|
case instruction_t::Mod: return os << "mod";
|
||||||
|
case instruction_t::Eq: return os << "eq";
|
||||||
|
case instruction_t::NotEq: return os << "notEq";
|
||||||
|
case instruction_t::LessThan: return os << "lessThan";
|
||||||
|
case instruction_t::GreaterThan: return os << "greaterThan";
|
||||||
|
case instruction_t::LessEq: return os << "lessEq";
|
||||||
|
case instruction_t::GreaterEq: return os << "greaterEq";
|
||||||
|
case instruction_t::Pointerof: return os << "pointerof";
|
||||||
|
case instruction_t::Sizeof: return os << "sizeof";
|
||||||
|
case instruction_t::Call: return os << "call";
|
||||||
|
case instruction_t::Branch: return os << "branch";
|
||||||
|
case instruction_t::BranchCond: return os << "branchCond";
|
||||||
|
case instruction_t::Return: return os << "return";
|
||||||
|
case instruction_t::Phi: return os << "phi";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks if an instruction type exits.
|
* @brief Checks if an instruction type exits.
|
||||||
*
|
*
|
||||||
@@ -241,78 +278,112 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Binary operation instruction type
|
* @brief Generic unary instruction
|
||||||
*/
|
*/
|
||||||
enum class binary_op_instruction_t {
|
class unary_instruction final : public instruction {
|
||||||
Add, /**< Addition */
|
public:
|
||||||
Sub, /**< Subtraction */
|
/**
|
||||||
Mul, /**< Multiplication */
|
* @brief Construct a new unary instruction.
|
||||||
Div, /**< Division */
|
*
|
||||||
Mod, /**< Modulo */
|
* @param type Instruction type.
|
||||||
|
* @param src Source operand.
|
||||||
|
* @param dst Destination operand.
|
||||||
|
*/
|
||||||
|
unary_instruction(instruction_t type, operand&& src, operand&& dst)
|
||||||
|
: m_type(type), m_src(std::move(src)), m_dst(std::move(dst)) {}
|
||||||
|
|
||||||
Eq, /**< Equal */
|
~unary_instruction() override = default;
|
||||||
NotEq, /**< Not equal */
|
|
||||||
LessThan, /**< Less than */
|
|
||||||
GreaterThan, /**< Greater than */
|
|
||||||
LessEq, /**< Less or equal */
|
|
||||||
GreaterEq, /**< Greater or equal */
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline std::ostream& operator<<(std::ostream& os, binary_op_instruction_t type) {
|
|
||||||
switch (type) {
|
|
||||||
case binary_op_instruction_t::Add: return os << '+';
|
|
||||||
case binary_op_instruction_t::Sub: return os << '-';
|
|
||||||
case binary_op_instruction_t::Mul: return os << '*';
|
|
||||||
case binary_op_instruction_t::Div: return os << '/';
|
|
||||||
case binary_op_instruction_t::Mod: return os << '%';
|
|
||||||
case binary_op_instruction_t::Eq: return os << "==";
|
|
||||||
case binary_op_instruction_t::NotEq: return os << "!=";
|
|
||||||
case binary_op_instruction_t::LessThan: return os << '<';
|
|
||||||
case binary_op_instruction_t::GreaterThan: return os << '>';
|
|
||||||
case binary_op_instruction_t::LessEq: return os << "<=";
|
|
||||||
case binary_op_instruction_t::GreaterEq: return os << ">=";
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Binary operation instruction
|
* @brief Move constructor
|
||||||
*/
|
*/
|
||||||
class binary_op_instruction final : public instruction {
|
unary_instruction(unary_instruction&&) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor
|
||||||
|
*/
|
||||||
|
unary_instruction& operator=(unary_instruction&&) = default;
|
||||||
|
|
||||||
|
unary_instruction(const unary_instruction&) = delete;
|
||||||
|
|
||||||
|
unary_instruction& operator=(const unary_instruction&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's type.
|
||||||
|
*
|
||||||
|
* @return The instruction type.
|
||||||
|
*/
|
||||||
|
instruction_t type() const override { return m_type; }
|
||||||
|
|
||||||
|
bool has_destination() const override { return true; }
|
||||||
|
|
||||||
|
operand& destination() override { return m_dst; }
|
||||||
|
|
||||||
|
const operand& destination() const override { return m_dst; }
|
||||||
|
|
||||||
|
std::vector<operand*> sources() override { return { &m_src }; }
|
||||||
|
|
||||||
|
std::vector<const operand*> sources() const override { return { &m_src }; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's source operand.
|
||||||
|
*
|
||||||
|
* @return The operand.
|
||||||
|
*/
|
||||||
|
const operand& src() const { return m_src; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's destination operand.
|
||||||
|
*
|
||||||
|
* @return The operand.
|
||||||
|
*/
|
||||||
|
const operand& dst() const { return m_dst; }
|
||||||
|
private:
|
||||||
|
instruction_t m_type;
|
||||||
|
operand m_src;
|
||||||
|
operand m_dst;
|
||||||
|
protected:
|
||||||
|
std::ostream& print(std::ostream& os) const override { return os << m_dst << " = " << m_type << ' ' << m_src; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generic binary instruction
|
||||||
|
*/
|
||||||
|
class binary_instruction final : public instruction {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new binary operation instruction.
|
* @brief Construct a new binary operation instruction.
|
||||||
*
|
*
|
||||||
* @param type Operation type.
|
* @param type Instruction type.
|
||||||
* @param lhs Left-hand-side operand.
|
* @param lhs Left-hand-side operand.
|
||||||
* @param rhs Right-hand-side operand.
|
* @param rhs Right-hand-side operand.
|
||||||
* @param dst Destination operand.
|
* @param dst Destination operand.
|
||||||
*/
|
*/
|
||||||
binary_op_instruction(binary_op_instruction_t type, operand&& lhs, operand&& rhs, operand&& dst)
|
binary_instruction(instruction_t type, operand&& lhs, operand&& rhs, operand&& dst)
|
||||||
: m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)), m_dst(std::move(dst)) {}
|
: m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)), m_dst(std::move(dst)) {}
|
||||||
|
|
||||||
~binary_op_instruction() override = default;
|
~binary_instruction() override = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor
|
* @brief Move constructor
|
||||||
*/
|
*/
|
||||||
binary_op_instruction(binary_op_instruction&&) = default;
|
binary_instruction(binary_instruction&&) = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor
|
* @brief Move constructor
|
||||||
*/
|
*/
|
||||||
binary_op_instruction& operator=(binary_op_instruction&&) = default;
|
binary_instruction& operator=(binary_instruction&&) = default;
|
||||||
|
|
||||||
binary_op_instruction(const binary_op_instruction&) = delete;
|
binary_instruction(const binary_instruction&) = delete;
|
||||||
|
|
||||||
binary_op_instruction& operator=(const binary_op_instruction&) = delete;
|
binary_instruction& operator=(const binary_instruction&) = delete;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Returns this instruction's type.
|
* @brief Returns this instruction's type.
|
||||||
*
|
*
|
||||||
* @return instruction_t::BinaryOp.
|
* @return instruction_t::BinaryOp.
|
||||||
*/
|
*/
|
||||||
instruction_t type() const override { return instruction_t::BinaryOp; }
|
instruction_t type() const override { return m_type; }
|
||||||
|
|
||||||
bool has_destination() const override { return true; }
|
bool has_destination() const override { return true; }
|
||||||
|
|
||||||
@@ -324,13 +395,6 @@ public:
|
|||||||
|
|
||||||
std::vector<const operand*> sources() const override { return { &m_lhs, &m_rhs }; }
|
std::vector<const operand*> sources() const override { return { &m_lhs, &m_rhs }; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this instruction's operation type.
|
|
||||||
*
|
|
||||||
* @return The operation type.
|
|
||||||
*/
|
|
||||||
binary_op_instruction_t op_type() const { return m_type; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this instruction's left-hand-side operand.
|
* @brief Returns this instruction's left-hand-side operand.
|
||||||
*
|
*
|
||||||
@@ -352,13 +416,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
const operand& dst() const { return m_dst; }
|
const operand& dst() const { return m_dst; }
|
||||||
private:
|
private:
|
||||||
binary_op_instruction_t m_type;
|
instruction_t m_type;
|
||||||
operand m_lhs /**< Left-hand-side operand */;
|
operand m_lhs /**< Left-hand-side operand */;
|
||||||
operand m_rhs /**< Right-hand-side operand */;
|
operand m_rhs /**< Right-hand-side operand */;
|
||||||
operand m_dst /**< Destination operand */;
|
operand m_dst /**< Destination operand */;
|
||||||
protected:
|
protected:
|
||||||
std::ostream& print(std::ostream& os) const override {
|
std::ostream& print(std::ostream& os) const override {
|
||||||
return os << "binop(" << m_type << ") " << m_lhs << ", " << m_rhs << ", " << m_dst;
|
return os << m_dst << " = " << m_lhs << ' ' << m_type << ' ' << m_rhs;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -617,7 +681,7 @@ private:
|
|||||||
std::vector<std::pair<operand, block_index>> m_labels;
|
std::vector<std::pair<operand, block_index>> m_labels;
|
||||||
protected:
|
protected:
|
||||||
std::ostream& print(std::ostream& os) const override {
|
std::ostream& print(std::ostream& os) const override {
|
||||||
os << "phi " << m_dst << " =";
|
os << m_dst << " = phi";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (const auto& pair : m_labels) {
|
for (const auto& pair : m_labels) {
|
||||||
if (!first) os << ',';
|
if (!first) os << ',';
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -7,20 +7,22 @@
|
|||||||
#include "furvm/handle.hpp"
|
#include "furvm/handle.hpp"
|
||||||
#include "furvm/module.hpp" // IWYU pragma: keep
|
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||||
|
#include "furvm/thing_allocator.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
class context {
|
class context : public handle_container<mod_h> {
|
||||||
public:
|
public:
|
||||||
friend class executor;
|
friend class executor;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructs a context.
|
* @brief Constructs a context.
|
||||||
*/
|
*/
|
||||||
context();
|
context()
|
||||||
|
: m_thingAllocator(m_thingArena) {}
|
||||||
|
|
||||||
~context() = default;
|
~context() = default;
|
||||||
|
|
||||||
@@ -36,49 +38,6 @@ public:
|
|||||||
|
|
||||||
context(const context&) = delete;
|
context(const context&) = delete;
|
||||||
context& operator=(const context&) = delete;
|
context& operator=(const context&) = delete;
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Emplaces a module in the context.
|
|
||||||
*
|
|
||||||
* @param args Arguments forwarded to the module constructor.
|
|
||||||
* @return The emplaced module.
|
|
||||||
*/
|
|
||||||
template <typename... Args>
|
|
||||||
auto emplace_module(Args&&... args) {
|
|
||||||
return m_modules.emplace(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a module from the context.
|
|
||||||
*
|
|
||||||
* @param args Module's id.
|
|
||||||
* @return A handle to the module.
|
|
||||||
*/
|
|
||||||
template <typename... Args>
|
|
||||||
auto module_at(Args&&... args) {
|
|
||||||
return m_modules.at(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a module from the context.
|
|
||||||
*
|
|
||||||
* @param args Module's id.
|
|
||||||
* @return A handle to the module.
|
|
||||||
*/
|
|
||||||
template <typename... Args>
|
|
||||||
auto module_at(Args&&... args) const {
|
|
||||||
return m_modules.at(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Erases a module from the context.
|
|
||||||
*
|
|
||||||
* @param args Module's id.
|
|
||||||
*/
|
|
||||||
template <typename... Args>
|
|
||||||
void erase_module(Args&&... args) {
|
|
||||||
m_modules.erase(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Emplaces an executor in the context.
|
* @brief Emplaces an executor in the context.
|
||||||
@@ -166,6 +125,8 @@ public:
|
|||||||
* @return The thing allocator.
|
* @return The thing allocator.
|
||||||
*/
|
*/
|
||||||
thing_allocator<std::byte> thing_alloc() const { return m_thingAllocator; }
|
thing_allocator<std::byte> thing_alloc() const { return m_thingAllocator; }
|
||||||
|
|
||||||
|
thing_type_store& thing_type_store() { return m_thingTypeStore; }
|
||||||
private:
|
private:
|
||||||
handle_container<mod_h> m_modules;
|
handle_container<mod_h> m_modules;
|
||||||
handle_container<thing_h> m_things;
|
handle_container<thing_h> m_things;
|
||||||
@@ -173,6 +134,7 @@ private:
|
|||||||
|
|
||||||
furlang::arena m_thingArena;
|
furlang::arena m_thingArena;
|
||||||
thing_allocator<std::byte> m_thingAllocator;
|
thing_allocator<std::byte> m_thingAllocator;
|
||||||
|
class thing_type_store m_thingTypeStore;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef FURVM_DETAIL_HANDLE_HPP
|
#ifndef FURVM_DETAIL_HANDLE_HPP
|
||||||
#define FURVM_DETAIL_HANDLE_HPP
|
#define FURVM_DETAIL_HANDLE_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
@@ -27,6 +28,11 @@ struct header_has_refcount<Header,
|
|||||||
template <typename Header>
|
template <typename Header>
|
||||||
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
|
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 detail
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ namespace detail {
|
|||||||
* @return The output stream.
|
* @return The output stream.
|
||||||
*/
|
*/
|
||||||
std::ostream& serialize(std::ostream& os, std::int8_t value);
|
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);
|
std::ostream& serialize(std::ostream& os, std::int16_t value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +34,14 @@ std::ostream& serialize(std::ostream& os, std::int16_t value);
|
|||||||
* @return The output stream.
|
* @return The output stream.
|
||||||
*/
|
*/
|
||||||
std::ostream& serialize(std::ostream& os, std::int32_t value);
|
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);
|
std::ostream& serialize(std::ostream& os, std::int64_t value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,6 +89,87 @@ std::ostream& serialize(std::ostream& os, std::uint64_t value);
|
|||||||
*/
|
*/
|
||||||
std::ostream& serialize(std::ostream& os, const std::string& value);
|
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 detail
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "furvm/module.hpp" // IWYU pragma: keep
|
#include "furvm/module.hpp" // IWYU pragma: keep
|
||||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
#include "furvm/thing.hpp" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -40,6 +41,7 @@ public:
|
|||||||
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
|
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
|
||||||
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
|
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
|
||||||
|
|
||||||
|
thing_type* returnType; /**< Return type. */
|
||||||
std::vector<thing_h> variables; /**< Frame variables. */
|
std::vector<thing_h> variables; /**< Frame variables. */
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
@@ -164,6 +166,10 @@ public:
|
|||||||
* @brief Executes next instruction.
|
* @brief Executes next instruction.
|
||||||
*/
|
*/
|
||||||
void step();
|
void step();
|
||||||
|
private:
|
||||||
|
thing_type thing_type_impl(mod_h mod, mod_type type) const;
|
||||||
|
|
||||||
|
thing_type* thing_type(const mod_h& mod, const mod_type& type) const;
|
||||||
private:
|
private:
|
||||||
executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization)
|
executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization)
|
||||||
context_p m_context;
|
context_p m_context;
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
#ifndef FURVM_FUNCTION_HPP
|
#ifndef FURVM_FUNCTION_HPP
|
||||||
#define FURVM_FUNCTION_HPP
|
#define FURVM_FUNCTION_HPP
|
||||||
|
|
||||||
|
#include "furlang/utility/hash.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
#include "furvm/handle.hpp" // IWYU pragma: keep
|
#include "furvm/handle.hpp" // IWYU pragma: keep
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
@@ -31,40 +34,60 @@ struct import_function {
|
|||||||
function_id function;
|
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 {
|
class function {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructs a normal function.
|
* @brief Constructs a normal function.
|
||||||
*
|
*
|
||||||
* @param name Name of the function.
|
* @param signature Function's signature.
|
||||||
* @param paramCount Paremeter count.
|
|
||||||
* @param position Offset in bytecode of the function.
|
* @param position Offset in bytecode of the function.
|
||||||
*/
|
*/
|
||||||
template <typename Name>
|
template <typename SigFwd, typename = std::enable_if_t<std::is_constructible_v<function_sig, SigFwd>>>
|
||||||
function(Name&& name, std::uint32_t paramCount, bytecode_pos position)
|
function(SigFwd&& signature, bytecode_pos position)
|
||||||
: m_name(std::forward<Name>(name)), m_type(function_t::Normal), m_paramCount(paramCount), m_value(position) {}
|
: m_type(function_t::Normal), m_signature(std::forward<SigFwd>(signature)), m_value(position) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructs a native function.
|
* @brief Constructs a native function.
|
||||||
*
|
*
|
||||||
* @param name Name of the function.
|
* @param signature Function's signature.
|
||||||
* @param paramCount Paremeter count.
|
|
||||||
* @param native Native function tag.
|
* @param native Native function tag.
|
||||||
*/
|
*/
|
||||||
template <typename Name,
|
template <typename SigFwd,
|
||||||
typename Native,
|
typename Native,
|
||||||
typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>>
|
typename = std::enable_if_t<std::is_constructible_v<native_function, Native> &&
|
||||||
function(Name&& name, std::uint32_t paramCount, Native&& native)
|
std::is_constructible_v<function_sig, SigFwd>>>
|
||||||
: m_name(std::forward<Name>(name)),
|
function(SigFwd&& signature, Native&& native)
|
||||||
m_type(function_t::Native),
|
: m_type(function_t::Native),
|
||||||
m_paramCount(paramCount),
|
m_signature(std::forward<SigFwd>(signature)),
|
||||||
m_value(std::forward<Native>(native)) {}
|
m_value(std::forward<Native>(native)) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructs an import function.
|
* @brief Constructs an import function.
|
||||||
*
|
*
|
||||||
* @param paramCount Parameter count.
|
* @param mod Module's id.
|
||||||
* @param imp Import function.
|
* @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);
|
function(const mod_h& mod, const function_h& function);
|
||||||
|
|
||||||
@@ -93,13 +116,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
function& operator=(const function&);
|
function& operator=(const function&);
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* @brief Returns a name of this function.
|
|
||||||
*
|
|
||||||
* @return The name.
|
|
||||||
*/
|
|
||||||
constexpr const std::string& name() const { return m_name; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a type of this function.
|
* @brief Returns a type of this function.
|
||||||
*
|
*
|
||||||
@@ -108,11 +124,11 @@ public:
|
|||||||
constexpr function_t type() const { return m_type; }
|
constexpr function_t type() const { return m_type; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this function's parameter count.
|
* @brief Returns this function's signature.
|
||||||
*
|
*
|
||||||
* @return The parameter count.
|
* @return The signature.
|
||||||
*/
|
*/
|
||||||
constexpr std::uint32_t param_count() const { return m_paramCount; }
|
function_sig signature() const { return m_signature; }
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Returns normal function's value.
|
* @brief Returns normal function's value.
|
||||||
@@ -144,9 +160,8 @@ public:
|
|||||||
return m_value.imp;
|
return m_value.imp;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
|
||||||
function_t m_type;
|
function_t m_type;
|
||||||
std::uint32_t m_paramCount;
|
function_sig m_signature;
|
||||||
|
|
||||||
union value {
|
union value {
|
||||||
std::size_t position = 0;
|
std::size_t position = 0;
|
||||||
@@ -174,6 +189,14 @@ private:
|
|||||||
} m_value;
|
} m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct function_sig_hash {
|
||||||
|
std::size_t operator()(const function_sig& signature) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|
||||||
#endif // FURVM_FUNCTION_HPP
|
#endif // FURVM_FUNCTION_HPP
|
||||||
|
|||||||
+12
-16
@@ -115,6 +115,12 @@ using function_h = handle<function, refcount_header<function_id>>;
|
|||||||
|
|
||||||
// module.hpp
|
// 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
|
* @class mod
|
||||||
* @brief Module.
|
* @brief Module.
|
||||||
@@ -138,30 +144,20 @@ using mod_id = std::string;
|
|||||||
*/
|
*/
|
||||||
using mod_h = handle<mod, refcount_header<mod_id>>;
|
using mod_h = handle<mod, refcount_header<mod_id>>;
|
||||||
|
|
||||||
|
// thing_allocator.hpp
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class thing_allocator;
|
||||||
|
|
||||||
// thing.hpp
|
// thing.hpp
|
||||||
|
|
||||||
/**
|
|
||||||
* @enum type_t
|
|
||||||
* @brief Type of the thing's type.
|
|
||||||
*/
|
|
||||||
enum class type_t : std::uint32_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct type
|
|
||||||
* @brief Thing type.
|
|
||||||
*/
|
|
||||||
struct type;
|
|
||||||
|
|
||||||
using type_p = std::shared_ptr<type>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class bad_thing_access
|
* @class bad_thing_access
|
||||||
* @brief Bad thing access exception.
|
* @brief Bad thing access exception.
|
||||||
*/
|
*/
|
||||||
class bad_thing_access;
|
class bad_thing_access;
|
||||||
|
|
||||||
template <typename T>
|
using thing_type_id = std::uint32_t;
|
||||||
class thing_allocator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class thing
|
* @class thing
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ public:
|
|||||||
using const_pointer = const Value*; /** Constant pointer type. */
|
using const_pointer = const Value*; /** Constant pointer type. */
|
||||||
public:
|
public:
|
||||||
using id_type = typename Header::id_type; /** Id type of the header. */
|
using id_type = typename Header::id_type; /** Id type of the header. */
|
||||||
|
|
||||||
|
using header_type = Header;
|
||||||
public:
|
public:
|
||||||
using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */
|
using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */
|
||||||
public:
|
public:
|
||||||
@@ -148,8 +150,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
handle(const handle& other)
|
handle(const handle& other)
|
||||||
: m_value(other.m_value) {
|
: m_value(other.m_value) {
|
||||||
|
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||||
m_value->first.acquire();
|
m_value->first.acquire();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy constructor.
|
* @brief Copy constructor.
|
||||||
@@ -157,7 +161,9 @@ public:
|
|||||||
handle& operator=(const handle& other) {
|
handle& operator=(const handle& other) {
|
||||||
if (this == &other) return *this;
|
if (this == &other) return *this;
|
||||||
m_value = other.m_value;
|
m_value = other.m_value;
|
||||||
|
if constexpr (detail::header_has_refcount_v<Header>) {
|
||||||
m_value->first.acquire();
|
m_value->first.acquire();
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@@ -180,8 +186,8 @@ public:
|
|||||||
*
|
*
|
||||||
* @return The reference count.
|
* @return The reference count.
|
||||||
*/
|
*/
|
||||||
template <typename ReferenceCount = typename Header::refcount_type>
|
template <typename U = Header, typename = std::enable_if_t<detail::header_has_refcount_v<U>>>
|
||||||
ReferenceCount reference_count() const {
|
auto reference_count() const {
|
||||||
return m_value->first.reference_count();
|
return m_value->first.reference_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +237,10 @@ public:
|
|||||||
* @brief Invalidates the handle without releasing.
|
* @brief Invalidates the handle without releasing.
|
||||||
*/
|
*/
|
||||||
void dispatch() { m_value = nullptr; }
|
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:
|
private:
|
||||||
pair_type* m_value = nullptr;
|
pair_type* m_value = nullptr;
|
||||||
};
|
};
|
||||||
@@ -308,6 +318,17 @@ public:
|
|||||||
delete it->second;
|
delete it->second;
|
||||||
m_pairs.erase(it);
|
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:
|
private:
|
||||||
std::unordered_map<id_type, pair_type*> m_pairs;
|
std::unordered_map<id_type, pair_type*> m_pairs;
|
||||||
};
|
};
|
||||||
@@ -347,9 +368,16 @@ public:
|
|||||||
delete m_pairs[id];
|
delete m_pairs[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
pair_type* newPair = new pair_type(std::piecewise_construct,
|
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(id, 0, [&](const id_type& id) { erase(id); }),
|
||||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
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;
|
m_pairs[id] = newPair;
|
||||||
return { newPair };
|
return { newPair };
|
||||||
@@ -395,6 +423,14 @@ public:
|
|||||||
delete m_pairs[id];
|
delete m_pairs[id];
|
||||||
m_pairs[id] = nullptr;
|
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:
|
public:
|
||||||
auto begin() { return m_pairs.begin(); }
|
auto begin() { return m_pairs.begin(); }
|
||||||
auto begin() const { return m_pairs.begin(); }
|
auto begin() const { return m_pairs.begin(); }
|
||||||
|
|||||||
@@ -12,11 +12,34 @@ enum class instruction_t : byte {
|
|||||||
NoOperation = 0,
|
NoOperation = 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pushes an integer from a byte onto the stack.
|
* @brief Pushes an s8 integer from a byte onto the stack.
|
||||||
*
|
|
||||||
* Pushes an integer constructed from a next byte onto the stack.
|
|
||||||
*/
|
*/
|
||||||
PushB2I,
|
PushS8,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an u8 integer from a byte onto the stack.
|
||||||
|
*/
|
||||||
|
PushU8,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an s16 integer from two byte onto the stack.
|
||||||
|
*/
|
||||||
|
PushS16,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an u16 integer from two byte onto the stack.
|
||||||
|
*/
|
||||||
|
PushU16,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an s32 integer from a byte onto the stack.
|
||||||
|
*/
|
||||||
|
PushS32,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an u32 integer from a byte onto the stack.
|
||||||
|
*/
|
||||||
|
PushU32,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pushes a constant onto the stack.
|
* @brief Pushes a constant onto the stack.
|
||||||
@@ -25,6 +48,24 @@ enum class instruction_t : byte {
|
|||||||
*/
|
*/
|
||||||
PushConstant,
|
PushConstant,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes a new array onto the stack.
|
||||||
|
*
|
||||||
|
* Type is the next 4 bytes in little-endian.
|
||||||
|
* If the type is dynamic the array's size will be popped off of the stack.
|
||||||
|
*/
|
||||||
|
Array,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes an element from an array onto the stack.
|
||||||
|
*/
|
||||||
|
Get,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets an array element.
|
||||||
|
*/
|
||||||
|
Set,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pops top element from the stack.
|
* @brief Pops top element from the stack.
|
||||||
*/
|
*/
|
||||||
@@ -35,6 +76,11 @@ enum class instruction_t : byte {
|
|||||||
*/
|
*/
|
||||||
Duplicate,
|
Duplicate,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swaps two top elements of the stack.
|
||||||
|
*/
|
||||||
|
Swap,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clones top element on the stack.
|
* @brief Clones top element on the stack.
|
||||||
*/
|
*/
|
||||||
@@ -102,6 +148,21 @@ enum class instruction_t : byte {
|
|||||||
*/
|
*/
|
||||||
GreaterEqual,
|
GreaterEqual,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes a pointer of popped-off thing onto the stack.
|
||||||
|
*/
|
||||||
|
Pointerof,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes a size of popped-off thing onto the stack.
|
||||||
|
*/
|
||||||
|
Sizeof,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes a length of popped-off thing onto the stack.
|
||||||
|
*/
|
||||||
|
Lengthof,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pushes a variable onto the stack.
|
* @brief Pushes a variable onto the stack.
|
||||||
*
|
*
|
||||||
@@ -142,11 +203,6 @@ enum class instruction_t : byte {
|
|||||||
* @brief Pops the current call frame.
|
* @brief Pops the current call frame.
|
||||||
*/
|
*/
|
||||||
Return,
|
Return,
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Pops the current call frame and pushes the first element from the previous stack onto the new stack.
|
|
||||||
*/
|
|
||||||
ReturnValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction {
|
struct instruction {
|
||||||
|
|||||||
+222
-55
@@ -1,19 +1,145 @@
|
|||||||
#ifndef FURVM_MODULE_HPP
|
#ifndef FURVM_MODULE_HPP
|
||||||
#define FURVM_MODULE_HPP
|
#define FURVM_MODULE_HPP
|
||||||
|
|
||||||
|
#include "furlang/utility/hash.hpp"
|
||||||
#include "furvm/function.hpp"
|
#include "furvm/function.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
#include "furvm/handle.hpp"
|
#include "furvm/handle.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <istream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
|
struct mod_type {
|
||||||
|
struct array {
|
||||||
|
mod_type_id typeId;
|
||||||
|
std::size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct imprt {
|
||||||
|
mod_id modId;
|
||||||
|
mod_type_id typeId;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum type {
|
||||||
|
S8 = 0,
|
||||||
|
S16,
|
||||||
|
S32,
|
||||||
|
S64,
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
Ptr,
|
||||||
|
Ref,
|
||||||
|
Array,
|
||||||
|
|
||||||
|
Import,
|
||||||
|
Count,
|
||||||
|
} type;
|
||||||
|
union value {
|
||||||
|
std::nullptr_t null = nullptr;
|
||||||
|
mod_type_id typeRef;
|
||||||
|
array array;
|
||||||
|
imprt 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(); break;
|
||||||
|
case Import: value.imprt.~imprt(); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_type(mod_type&& other) noexcept
|
||||||
|
: type(other.type) {
|
||||||
|
switch (type) {
|
||||||
|
case Array: new (&value.array) array(other.value.array); break;
|
||||||
|
case Import: new (&value.imprt) imprt(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(other.value.array); break;
|
||||||
|
case Import: new (&value.imprt) imprt(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(other.value.array); break;
|
||||||
|
case Import: new (&value.imprt) imprt(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(other.value.array); break;
|
||||||
|
case Import: new (&value.imprt) imprt(other.value.imprt); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class mod {
|
class mod {
|
||||||
friend class function;
|
friend class function;
|
||||||
friend class serializer;
|
friend class serializer;
|
||||||
@@ -81,9 +207,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
function_h emplace_function(Args&&... args) {
|
function_h emplace_function(Args&&... args) {
|
||||||
function_h function = m_functions.emplace_back(std::forward<Args>(args)...);
|
function_h function;
|
||||||
m_functionMap[function->name()] = function.id();
|
if constexpr (std::is_constructible_v<class function, Args...>) {
|
||||||
m_publicFunctions[function->name()] = function.id();
|
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);
|
return std::move(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,36 +221,26 @@ public:
|
|||||||
*
|
*
|
||||||
* Emplaces the function in module's function container and name to function map.
|
* 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.
|
* @param args Arguments forwarded into the container's emplace_back function.
|
||||||
* @return A handle to the emplaced function.
|
* @return A handle to the emplaced function.
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename NameFwd,
|
||||||
function_h emplace_function_private(Args&&... args) {
|
typename... Args,
|
||||||
function_h function = m_functions.emplace_back(std::forward<Args>(args)...);
|
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
|
||||||
m_functionMap[function->name()] = function.id();
|
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);
|
return std::move(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Inserts a function in the module's function container.
|
|
||||||
*
|
|
||||||
* @param function Function to insert.
|
|
||||||
*/
|
|
||||||
template <typename Function>
|
|
||||||
auto push_back(Function&& function) {
|
|
||||||
return emplace_function(std::forward<Function>(function));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Inserts a function in the module's function container.
|
|
||||||
*
|
|
||||||
* @param function Function to insert.
|
|
||||||
*/
|
|
||||||
template <typename Function>
|
|
||||||
auto push_back_private(Function&& function) {
|
|
||||||
return emplace_function_private(std::forward<Function>(function));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a function from the module.
|
* @brief Returns a function from the module.
|
||||||
*
|
*
|
||||||
@@ -144,31 +263,13 @@ public:
|
|||||||
* @param name Name of the function.
|
* @param name Name of the function.
|
||||||
* @return A handle to the function.
|
* @return A handle to the function.
|
||||||
*/
|
*/
|
||||||
template <typename NameFwd>
|
template <typename NameFwd,
|
||||||
auto function_at(NameFwd&& name) {
|
typename SigFwd,
|
||||||
return function_at(m_publicFunctions.at(std::forward<NameFwd>(name)));
|
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(
|
||||||
* @brief Returns a function from the module.
|
m_functionSigs.at(std::make_pair<>(std::forward<NameFwd>(name), std::forward<SigFwd>(signature))));
|
||||||
*
|
|
||||||
* @param name Name of the function.
|
|
||||||
* @return A handle to the function.
|
|
||||||
*/
|
|
||||||
template <typename NameFwd>
|
|
||||||
auto function_at(NameFwd&& name) const {
|
|
||||||
return function_at(m_publicFunctions.at(std::forward<NameFwd>(name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns an id of a function from the module.
|
|
||||||
*
|
|
||||||
* @param name Name of the function.
|
|
||||||
* @return An id of the function.
|
|
||||||
*/
|
|
||||||
template <typename NameFwd>
|
|
||||||
auto get_function_id(NameFwd&& name) {
|
|
||||||
return m_functionMap.at(std::forward<NameFwd>(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,7 +277,13 @@ public:
|
|||||||
*
|
*
|
||||||
* @param id Identifier of the function.
|
* @param id Identifier of the function.
|
||||||
*/
|
*/
|
||||||
void erase_function(function_id id) { m_functions.erase(id); }
|
void erase_function(function_id id) {
|
||||||
|
m_functions.erase(id);
|
||||||
|
if (auto it = m_functionMap.find(id); it != m_functionMap.end()) {
|
||||||
|
m_functionSigs.erase(it->second);
|
||||||
|
m_functionMap.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
template <typename NameFwd, typename Func>
|
template <typename NameFwd, typename Func>
|
||||||
void set_native_function(NameFwd&& name, Func&& func) {
|
void set_native_function(NameFwd&& name, Func&& func) {
|
||||||
@@ -187,6 +294,53 @@ public:
|
|||||||
native_function get_native_function(NameFwd&& name) const {
|
native_function get_native_function(NameFwd&& name) const {
|
||||||
return m_nativeFunctions.at(std::forward<NameFwd>(name));
|
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)...);
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Prints the module in a bytecode form to an output stream.
|
* @brief Prints the module in a bytecode form to an output stream.
|
||||||
@@ -195,13 +349,26 @@ public:
|
|||||||
* @return The output stream.
|
* @return The output stream.
|
||||||
*/
|
*/
|
||||||
std::ostream& serialize(std::ostream& os) const;
|
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:
|
private:
|
||||||
bytecode_t m_bytecode;
|
bytecode_t m_bytecode;
|
||||||
|
|
||||||
std::unordered_map<std::string, function_id> m_functionMap;
|
using pair_type = std::pair<std::string, function_sig>;
|
||||||
std::unordered_map<std::string, function_id> m_publicFunctions;
|
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<function_h> m_functions;
|
||||||
|
|
||||||
|
handle_container<mod_type_h> m_types;
|
||||||
|
|
||||||
std::unordered_map<std::string, native_function> m_nativeFunctions;
|
std::unordered_map<std::string, native_function> m_nativeFunctions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+468
-298
@@ -2,143 +2,208 @@
|
|||||||
#define FURVM_THING_HPP
|
#define FURVM_THING_HPP
|
||||||
|
|
||||||
#include "furlang/arena.hpp"
|
#include "furlang/arena.hpp"
|
||||||
|
#include "furlang/utility/hash.hpp"
|
||||||
#include "furvm/exceptions.hpp"
|
#include "furvm/exceptions.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
|
#include "furvm/thing_allocator.hpp" // IWYU pragma: keep
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <limits>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
enum class type_t : std::uint32_t {
|
struct thing_type {
|
||||||
Primitive = 0,
|
using s8 = std::int8_t;
|
||||||
Reference,
|
using s16 = std::int16_t;
|
||||||
List,
|
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 array {
|
||||||
|
thing_type* type;
|
||||||
|
std::size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
using primitive_type = std::uint64_t;
|
enum type { // NOLINT
|
||||||
using reference_type = std::shared_ptr<type>;
|
S8 = 0,
|
||||||
using list_type = std::shared_ptr<type>;
|
S16,
|
||||||
|
S32,
|
||||||
|
S64,
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
Ptr,
|
||||||
|
Ref,
|
||||||
|
Array,
|
||||||
|
|
||||||
struct type {
|
Count,
|
||||||
type_t t;
|
} type;
|
||||||
union {
|
union value {
|
||||||
primitive_type primitive;
|
std::nullptr_t null = nullptr;
|
||||||
reference_type reference;
|
thing_type* typeRef;
|
||||||
list_type list;
|
array array;
|
||||||
};
|
|
||||||
|
|
||||||
type(type_t type)
|
value() = default;
|
||||||
: t(type), primitive(0) {}
|
|
||||||
|
|
||||||
type(primitive_type primitive)
|
value(thing_type* type)
|
||||||
: t(type_t::Primitive), primitive(primitive) {}
|
: typeRef(type) {}
|
||||||
|
|
||||||
type(const reference_type& reference)
|
value(thing_type* type, std::size_t size)
|
||||||
: t(type_t::Reference), reference(reference) {}
|
: array({}) {
|
||||||
|
array.type = type;
|
||||||
|
array.size = size;
|
||||||
|
}
|
||||||
|
} value;
|
||||||
|
|
||||||
static type make_list(const list_type& list) {
|
static constexpr thing_type_id INVALID_ID = std::numeric_limits<thing_type_id>::max();
|
||||||
type type(type_t::List);
|
|
||||||
new (&type.list) list_type(list);
|
thing_type_id id = INVALID_ID;
|
||||||
return type;
|
|
||||||
|
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: 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 Count: break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
~type() {
|
bool operator!=(const thing_type& other) const { return !this->operator==(other); }
|
||||||
switch (t) {
|
|
||||||
case type_t::Reference: reference.~reference_type(); break;
|
static bool is_primitive(enum type type) {
|
||||||
case type_t::List: list.~list_type(); break;
|
switch (type) {
|
||||||
default: break;
|
case S8:
|
||||||
|
case S16:
|
||||||
|
case S32:
|
||||||
|
case S64:
|
||||||
|
case U8:
|
||||||
|
case U16:
|
||||||
|
case U32:
|
||||||
|
case U64: return true;
|
||||||
|
case Ptr:
|
||||||
|
case Ref:
|
||||||
|
case Array: return false;
|
||||||
|
case Count: break;
|
||||||
}
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
type(type&& other) noexcept
|
static std::size_t primitive_size(enum type type) {
|
||||||
: t(other.t) {
|
switch (type) {
|
||||||
switch (t) {
|
case thing_type::S8: return sizeof(s8);
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
case thing_type::S16: return sizeof(s16);
|
||||||
case type_t::Reference: reference = std::move(other.reference); break;
|
case thing_type::S32: return sizeof(s32);
|
||||||
case type_t::List: list = std::move(other.list); break;
|
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 Ptr:
|
||||||
|
case Ref:
|
||||||
|
case Array: return 0;
|
||||||
|
case Count: break;
|
||||||
}
|
}
|
||||||
}
|
throw std::runtime_error("unreachable");
|
||||||
|
|
||||||
type& operator=(type&& other) noexcept {
|
|
||||||
if (this == &other) return *this;
|
|
||||||
t = other.t;
|
|
||||||
switch (t) {
|
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
|
||||||
case type_t::Reference: reference = std::move(other.reference); break;
|
|
||||||
case type_t::List: list = std::move(other.list); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
type(const type& other)
|
|
||||||
: t(other.t) {
|
|
||||||
switch (t) {
|
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
|
||||||
case type_t::Reference: reference = other.reference; break;
|
|
||||||
case type_t::List: list = other.list; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type& operator=(const type& other) {
|
|
||||||
if (this == &other) return *this;
|
|
||||||
t = other.t;
|
|
||||||
switch (t) {
|
|
||||||
case type_t::Primitive: primitive = other.primitive; break;
|
|
||||||
case type_t::Reference: reference = other.reference; break;
|
|
||||||
case type_t::List: list = other.list; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using byte_t = std::int8_t; /**< A 1-byte integer. */
|
namespace detail {
|
||||||
using short_t = std::int16_t; /**< A 2-byte integer. */
|
|
||||||
using int_t = std::int32_t; /**< A 4-byte integer. */
|
|
||||||
using long_t = std::int64_t; /**< An 8-byte integer. */
|
|
||||||
|
|
||||||
using reference_t = std::byte*;
|
struct thing_type_hash {
|
||||||
|
std::size_t operator()(const thing_type& type) const {
|
||||||
struct list_t {
|
std::size_t seed = std::hash<decltype(type.type)>{}(type.type);
|
||||||
long_t size;
|
switch (type.type) {
|
||||||
std::byte* data;
|
case thing_type::S8:
|
||||||
|
case thing_type::S16:
|
||||||
|
case thing_type::S32:
|
||||||
|
case thing_type::S64:
|
||||||
|
case thing_type::U8:
|
||||||
|
case thing_type::U16:
|
||||||
|
case thing_type::U32:
|
||||||
|
case thing_type::U64: return seed;
|
||||||
|
case thing_type::Ptr:
|
||||||
|
case thing_type::Ref: return furlang::utility::hash_combine(seed, thing_type_hash{}(*type.value.typeRef));
|
||||||
|
case thing_type::Array:
|
||||||
|
seed = furlang::utility::hash_combine(seed, thing_type_hash{}(*type.value.array.type));
|
||||||
|
seed = furlang::utility::hash_combine(seed,
|
||||||
|
std::hash<decltype(type.value.array.size)>{}(type.value.array.size));
|
||||||
|
return seed;
|
||||||
|
case thing_type::Count: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
} // namespace detail
|
||||||
* @brief A 1-byte integer thing type.
|
|
||||||
*/
|
|
||||||
inline static type byteType = { sizeof(byte_t) }; // NOLINT
|
|
||||||
|
|
||||||
/**
|
class thing_type_store {
|
||||||
* @brief A 2-byte integer thing type.
|
public:
|
||||||
*/
|
thing_type* insert(thing_type& type) {
|
||||||
inline static type shortType = { sizeof(short_t) }; // NOLINT
|
if (auto it = m_typeMap.find(type); it != m_typeMap.end()) return m_map[type.id = it->second];
|
||||||
|
if (type.id == thing_type::INVALID_ID) type.id = m_counter++;
|
||||||
|
|
||||||
/**
|
thing_type* ptr = m_arena.allocate<thing_type>(type);
|
||||||
* @brief A 4-byte integer thing type.
|
m_map[type.id] = ptr;
|
||||||
*/
|
m_typeMap[type] = type.id;
|
||||||
inline static type intType = { sizeof(int_t) }; // NOLINT
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
thing_type* insert(const thing_type& type) {
|
||||||
* @brief An 8-byte integer thing type.
|
if (auto it = m_typeMap.find(type); it != m_typeMap.end()) return m_map[it->second];
|
||||||
*/
|
|
||||||
inline static type longType = { sizeof(long_t) }; // NOLINT
|
thing_type_id id = m_counter++;
|
||||||
|
thing_type* ptr = m_arena.allocate<thing_type>(type);
|
||||||
|
m_map[id] = ptr;
|
||||||
|
m_typeMap[type] = id;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_type* at(thing_type_id id) const {
|
||||||
|
if (auto it = m_map.find(id); it != m_map.end()) return it->second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
furlang::arena m_arena;
|
||||||
|
std::unordered_map<thing_type_id, thing_type*> m_map;
|
||||||
|
std::unordered_map<thing_type, thing_type_id, detail::thing_type_hash> m_typeMap;
|
||||||
|
thing_type_id m_counter = 0;
|
||||||
|
};
|
||||||
|
|
||||||
template <template <typename> typename Allocator>
|
template <template <typename> typename Allocator>
|
||||||
class thing final {
|
class thing final {
|
||||||
friend class executor;
|
friend class executor;
|
||||||
public:
|
public:
|
||||||
using allocator_type = Allocator<std::byte>; /**< Allocator type. */
|
using allocator_type = Allocator<std::byte>; /**< Allocator type. */
|
||||||
|
public:
|
||||||
|
union array {
|
||||||
|
std::byte flat[];
|
||||||
|
struct {
|
||||||
|
std::size_t size;
|
||||||
|
std::byte* data;
|
||||||
|
} dynamic;
|
||||||
|
};
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructs a thing.
|
* @brief Constructs a thing.
|
||||||
@@ -146,17 +211,9 @@ public:
|
|||||||
* @param type Thing type.
|
* @param type Thing type.
|
||||||
* @param allocator Allocator for the thing's data.
|
* @param allocator Allocator for the thing's data.
|
||||||
*/
|
*/
|
||||||
thing(const type& type, const allocator_type& allocator = {})
|
thing(const thing_type& type, const allocator_type& allocator = {})
|
||||||
: thing(std::make_shared<class type>(type), allocator) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructs a thing.
|
|
||||||
*
|
|
||||||
* @param type Thing type.
|
|
||||||
* @param allocator Allocator for the thing's data.
|
|
||||||
*/
|
|
||||||
thing(const type_p& type, const allocator_type& allocator = {})
|
|
||||||
: m_type(type), m_size(compute_size(type)), m_allocator(allocator) {
|
: m_type(type), m_size(compute_size(type)), m_allocator(allocator) {
|
||||||
|
if (m_type.type == thing_type::Ref) return;
|
||||||
// TODO: Account for alignment
|
// TODO: Account for alignment
|
||||||
m_data = m_allocator.allocate(m_size);
|
m_data = m_allocator.allocate(m_size);
|
||||||
std::memset(m_data, 0, m_size);
|
std::memset(m_data, 0, m_size);
|
||||||
@@ -166,18 +223,15 @@ public:
|
|||||||
* @brief Destructs a thing.
|
* @brief Destructs a thing.
|
||||||
*/
|
*/
|
||||||
~thing() {
|
~thing() {
|
||||||
if (m_data != nullptr && m_size > 0) m_allocator.deallocate(m_data, m_size);
|
if (m_type.type != thing_type::Ref && m_data != nullptr && m_size > 0) m_allocator.deallocate(m_data, m_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor.
|
* @brief Move constructor.
|
||||||
*/
|
*/
|
||||||
thing(thing&& other) noexcept
|
thing(thing&& other) noexcept
|
||||||
: m_type(std::move(other.m_type)),
|
: m_type(other.m_type), m_data(other.m_data), m_size(other.m_size), m_allocator(std::move(other.m_allocator)) {
|
||||||
m_data(other.m_data),
|
other.m_type.type = thing_type::Count;
|
||||||
m_size(other.m_size),
|
|
||||||
m_allocator(std::move(other.m_allocator)) {
|
|
||||||
other.m_type = {};
|
|
||||||
other.m_data = nullptr;
|
other.m_data = nullptr;
|
||||||
other.m_size = 0;
|
other.m_size = 0;
|
||||||
}
|
}
|
||||||
@@ -190,7 +244,7 @@ public:
|
|||||||
m_type = other.m_type;
|
m_type = other.m_type;
|
||||||
m_data = other.m_data;
|
m_data = other.m_data;
|
||||||
m_allocator = std::move(other.m_allocator);
|
m_allocator = std::move(other.m_allocator);
|
||||||
other.m_type = {};
|
other.m_type.type = thing_type::Count;
|
||||||
other.m_data = nullptr;
|
other.m_data = nullptr;
|
||||||
other.m_size = 0;
|
other.m_size = 0;
|
||||||
return *this;
|
return *this;
|
||||||
@@ -206,16 +260,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
thing clone() const {
|
thing clone() const {
|
||||||
thing res(m_type, m_allocator);
|
thing res(m_type, m_allocator);
|
||||||
switch (m_type->t) {
|
switch (m_type.type) {
|
||||||
case type_t::Primitive:
|
case thing_type::S8:
|
||||||
case type_t::Reference: {
|
case thing_type::S16:
|
||||||
std::memcpy(res.m_data, m_data, m_size);
|
case thing_type::S32:
|
||||||
} break;
|
case thing_type::S64:
|
||||||
case type_t::List: {
|
case thing_type::U8:
|
||||||
copy_list(m_type->list, res.get<list_t>(), get<list_t>());
|
case thing_type::U16:
|
||||||
} break;
|
case thing_type::U32:
|
||||||
|
case thing_type::U64:
|
||||||
|
case thing_type::Ptr: std::memcpy(res.m_data, m_data, m_size); return std::move(res);
|
||||||
|
case thing_type::Array: copy_list(m_type, res.get<array>(), get<array>()); return std::move(res);
|
||||||
|
case thing_type::Ref: throw std::runtime_error("cannot clone references");
|
||||||
|
case thing_type::Count: break;
|
||||||
}
|
}
|
||||||
return std::move(res);
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -223,7 +282,26 @@ public:
|
|||||||
*
|
*
|
||||||
* @return The type.
|
* @return The type.
|
||||||
*/
|
*/
|
||||||
constexpr auto type() const { return *m_type; }
|
constexpr thing_type type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the thing's true type.
|
||||||
|
*
|
||||||
|
* If the thing is a reference, returns the referenced type.
|
||||||
|
*
|
||||||
|
* @return The true type.
|
||||||
|
*/
|
||||||
|
constexpr thing_type true_type() const { return (m_type.type == thing_type::Ref) ? *m_type.value.typeRef : m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the thing is of a specified type.
|
||||||
|
*
|
||||||
|
* Compares the true type.
|
||||||
|
*
|
||||||
|
* @param type Type to compare.
|
||||||
|
* @return true if the types match.
|
||||||
|
*/
|
||||||
|
constexpr bool is(enum thing_type::type type) const { return true_type().type == type; }
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Returns a raw data pointer.
|
* @brief Returns a raw data pointer.
|
||||||
@@ -246,8 +324,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& get() {
|
T& get() {
|
||||||
std::size_t size = m_size > 0 ? m_size : compute_size_na(m_type);
|
if (compute_size_na(m_type) != sizeof(T)) throw bad_thing_access();
|
||||||
if (size != sizeof(T)) throw bad_thing_access();
|
|
||||||
return *std::launder(reinterpret_cast<T*>(m_data));
|
return *std::launder(reinterpret_cast<T*>(m_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,8 +335,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& get() const {
|
const T& get() const {
|
||||||
std::size_t size = m_size > 0 ? m_size : compute_size_na(m_type);
|
if (compute_size_na(m_type) != sizeof(T)) throw bad_thing_access();
|
||||||
if (size != sizeof(T)) throw bad_thing_access();
|
|
||||||
return *std::launder(reinterpret_cast<const T*>(m_data));
|
return *std::launder(reinterpret_cast<const T*>(m_data));
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@@ -356,220 +432,314 @@ public:
|
|||||||
*
|
*
|
||||||
* @return The integer value.
|
* @return The integer value.
|
||||||
*/
|
*/
|
||||||
long_t integer() const {
|
thing_type::s64 integer() const {
|
||||||
if (m_type->t != type_t::Primitive) throw bad_thing_access();
|
switch (true_type().type) {
|
||||||
switch (m_type->primitive) {
|
case thing_type::S8: return get<thing_type::s8>();
|
||||||
case sizeof(byte_t): return get<byte_t>();
|
case thing_type::S16: return get<thing_type::s16>();
|
||||||
case sizeof(short_t): return get<short_t>();
|
case thing_type::S32: return get<thing_type::s32>();
|
||||||
case sizeof(int_t): return get<int_t>();
|
case thing_type::S64: return get<thing_type::s64>();
|
||||||
case sizeof(long_t): return get<long_t>();
|
case thing_type::U8: return get<thing_type::u8>();
|
||||||
|
case thing_type::U16: return get<thing_type::u16>();
|
||||||
|
case thing_type::U32: return get<thing_type::u32>();
|
||||||
|
case thing_type::U64: return get<thing_type::u64>();
|
||||||
default: throw std::runtime_error("unreachable");
|
default: throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thing reference() const {
|
void resize(thing_type::u64 newSize) {
|
||||||
thing res = { std::make_shared<class type>(m_type), m_allocator };
|
if (!is(thing_type::Array)) throw bad_thing_access();
|
||||||
res.get<reference_t>() = m_data;
|
if (true_type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
|
||||||
return std::move(res);
|
|
||||||
|
auto& array = get<union array>();
|
||||||
|
if (newSize < 0 || newSize == array.dynamic.size) return;
|
||||||
|
std::size_t innerSize = compute_size_na(*true_type().value.array.type);
|
||||||
|
std::byte* newData = new std::byte[innerSize * newSize];
|
||||||
|
std::memcpy(newData,
|
||||||
|
array.dynamic.data,
|
||||||
|
innerSize * std::min(static_cast<thing_type::u64>(array.dynamic.size), newSize));
|
||||||
|
array.dynamic.size = newSize;
|
||||||
|
delete[] array.dynamic.data;
|
||||||
|
array.dynamic.data = newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
thing resolve() const {
|
thing at(thing_type::u64 index) const {
|
||||||
thing rsv = { m_type, m_data, m_allocator };
|
if (!is(thing_type::Array)) throw bad_thing_access();
|
||||||
while (rsv.type().t == type_t::Reference)
|
|
||||||
rsv = { rsv.m_type->reference, rsv.get<reference_t>(), std::move(rsv.m_allocator) };
|
std::size_t elementSize = compute_size_na(*m_type.value.array.type);
|
||||||
return rsv;
|
if (m_type.value.array.size == 0) {
|
||||||
|
auto& array = get<union array>();
|
||||||
|
if (index < 0 || index >= array.dynamic.size) throw std::out_of_range("index out of range");
|
||||||
|
thing ref = { { thing_type::Ref, m_type.value.array.type }, m_allocator };
|
||||||
|
ref.m_data = array.dynamic.data + (index * elementSize);
|
||||||
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize(long_t newSize) {
|
std::byte* data = reinterpret_cast<array*>(m_data)->flat;
|
||||||
if (m_type->t != type_t::List) throw bad_thing_access();
|
if (index < 0 || index >= m_type.value.array.size) throw std::out_of_range("index out of range");
|
||||||
auto& list = get<list_t>();
|
thing ref = { { thing_type::Ref, m_type.value.array.type }, m_allocator };
|
||||||
if (newSize < 0 || newSize == list.size) return;
|
ref.m_data = data + (index * elementSize);
|
||||||
std::byte* newData = new std::byte[compute_size_na(m_type->list) * newSize];
|
return ref;
|
||||||
std::memcpy(newData, list.data, compute_size_na(m_type->list) * std::min(list.size, newSize));
|
|
||||||
list.size = newSize;
|
|
||||||
delete[] list.data;
|
|
||||||
list.data = newData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thing at(long_t index) const {
|
thing_type::u64 length() const {
|
||||||
if (m_type->t != type_t::List) throw bad_thing_access();
|
if (!is(thing_type::Array)) throw bad_thing_access();
|
||||||
auto& list = get<list_t>();
|
return true_type().value.array.size == 0 ? get<array>().dynamic.size : true_type().value.array.size;
|
||||||
if (index < 0 || index >= list.size) throw std::out_of_range("index out of range");
|
}
|
||||||
thing res = { m_type->list, m_allocator };
|
|
||||||
res.get<reference_t>() = list.data; // TODO: Account for padding, alignment and stuff
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
return std::move(res);
|
T cast_to() const {
|
||||||
|
return visit_primitive([](auto value) { return static_cast<T>(value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Changes reference thing's referenced thing.
|
||||||
|
*
|
||||||
|
* Yes.
|
||||||
|
*
|
||||||
|
* @param thing Thing.
|
||||||
|
*/
|
||||||
|
void reference(const thing& thing) {
|
||||||
|
if (m_type.type != thing_type::Ref || *m_type.value.typeRef != thing.type()) throw bad_thing_access();
|
||||||
|
m_data = thing.m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Self-explainatory.
|
||||||
|
*
|
||||||
|
* TODO: Document
|
||||||
|
*/
|
||||||
|
void assign(thing&& thing) {
|
||||||
|
class thing rhs = std::move(thing);
|
||||||
|
if (true_type() != rhs.true_type()) throw std::runtime_error("thing type mismatch");
|
||||||
|
// TODO: Move this to another function
|
||||||
|
switch (true_type().type) {
|
||||||
|
case thing_type::S8:
|
||||||
|
case thing_type::S16:
|
||||||
|
case thing_type::S32:
|
||||||
|
case thing_type::S64:
|
||||||
|
case thing_type::U8:
|
||||||
|
case thing_type::U16:
|
||||||
|
case thing_type::U32:
|
||||||
|
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
|
||||||
|
case thing_type::Ptr:
|
||||||
|
case thing_type::Ref:
|
||||||
|
case thing_type::Array: throw std::runtime_error("unimplemented");
|
||||||
|
case thing_type::Count: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
static void copy_list(const type_p& innerType, list_t& dst, const list_t& src) {
|
static void copy_list(const thing_type& arrayType, array& dst, const array& src) {
|
||||||
dst.size = src.size;
|
if (arrayType.type != thing_type::Array || arrayType.value.array.type == nullptr)
|
||||||
if (dst.size <= 0) {
|
throw std::runtime_error("invalid type");
|
||||||
dst.data = nullptr;
|
|
||||||
|
const auto& innerType = *arrayType.value.array.type;
|
||||||
|
std::size_t elementSize = compute_size_na(innerType);
|
||||||
|
|
||||||
|
std::byte* data = nullptr;
|
||||||
|
const std::byte* srcData = nullptr;
|
||||||
|
std::size_t size = 0;
|
||||||
|
if (arrayType.value.array.size == 0) {
|
||||||
|
size = dst.dynamic.size = src.dynamic.size;
|
||||||
|
if (dst.dynamic.size < 0) {
|
||||||
|
dst.dynamic.data = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (innerType == nullptr) throw std::runtime_error("inner type should not be null!");
|
|
||||||
|
|
||||||
std::size_t size = compute_size_na(innerType) * dst.size;
|
srcData = src.dynamic.data;
|
||||||
dst.data = new std::byte[size];
|
data = dst.dynamic.data = new std::byte[dst.dynamic.size];
|
||||||
switch (innerType->t) {
|
} else {
|
||||||
case type_t::Primitive:
|
data = dst.flat;
|
||||||
case type_t::Reference: {
|
srcData = src.flat;
|
||||||
std::memcpy(dst.data, src.data, size);
|
size = arrayType.value.array.size;
|
||||||
} break;
|
}
|
||||||
case type_t::List: {
|
|
||||||
|
switch (innerType.type) {
|
||||||
|
case thing_type::S8:
|
||||||
|
case thing_type::S16:
|
||||||
|
case thing_type::S32:
|
||||||
|
case thing_type::S64:
|
||||||
|
case thing_type::U8:
|
||||||
|
case thing_type::U16:
|
||||||
|
case thing_type::U32:
|
||||||
|
case thing_type::U64:
|
||||||
|
case thing_type::Ptr:
|
||||||
|
case thing_type::Ref: std::memcpy(dst.flat, src.flat, size); return;
|
||||||
|
case thing_type::Array:
|
||||||
for (std::size_t i = 0; i < size; ++i) {
|
for (std::size_t i = 0; i < size; ++i) {
|
||||||
copy_list(innerType->list,
|
copy_list(*innerType.value.array.type,
|
||||||
*std::launder(reinterpret_cast<list_t*>(dst.data)),
|
*std::launder(reinterpret_cast<array*>(data + (i * elementSize))),
|
||||||
*std::launder(reinterpret_cast<list_t*>(src.data)));
|
*std::launder(reinterpret_cast<const array*>(srcData + (i * elementSize))));
|
||||||
}
|
}
|
||||||
} break;
|
return;
|
||||||
|
case thing_type::Count: break;
|
||||||
}
|
}
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
static std::size_t compute_size_na(const type_p& type) {
|
static std::size_t compute_size_na(const thing_type& type) {
|
||||||
switch (type->t) {
|
switch (type.type) {
|
||||||
case type_t::Primitive: return type->primitive;
|
case thing_type::S8: return sizeof(thing_type::s8);
|
||||||
case type_t::Reference: return sizeof(reference_t);
|
case thing_type::S16: return sizeof(thing_type::s16);
|
||||||
case type_t::List: return sizeof(list_t);
|
case thing_type::S32: return sizeof(thing_type::s32);
|
||||||
|
case thing_type::S64: return sizeof(thing_type::s64);
|
||||||
|
case thing_type::U8: return sizeof(thing_type::u8);
|
||||||
|
case thing_type::U16: return sizeof(thing_type::u16);
|
||||||
|
case thing_type::U32: return sizeof(thing_type::u32);
|
||||||
|
case thing_type::U64: return sizeof(thing_type::u64);
|
||||||
|
case thing_type::Ptr: return sizeof(void*);
|
||||||
|
case thing_type::Ref: return compute_size_na(*type.value.typeRef);
|
||||||
|
case thing_type::Array:
|
||||||
|
return type.value.array.size == 0 ? sizeof(array)
|
||||||
|
: compute_size_na(*type.value.array.type) * type.value.array.size;
|
||||||
|
case thing_type::Count: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("unreachable");
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Align to 4 bytes
|
// NOTE: Align to 4 bytes
|
||||||
static std::size_t compute_size(const type_p& type) { return (compute_size_na(type) + 3) & ~3; }
|
static std::size_t compute_size(const thing_type& type) { return (compute_size_na(type) + 3) & ~3; }
|
||||||
private:
|
|
||||||
thing(const type_p& type, std::byte* data, const allocator_type& allocator = {})
|
|
||||||
: m_type(type), m_size(0), m_data(data), m_allocator(allocator) {}
|
|
||||||
private:
|
private:
|
||||||
|
template <typename Func>
|
||||||
|
decltype(auto) visit_primitive(Func&& func) const {
|
||||||
|
switch (true_type().type) {
|
||||||
|
case thing_type::S8: return std::forward<Func>(func)(get<thing_type::s8>());
|
||||||
|
case thing_type::S16: return std::forward<Func>(func)(get<thing_type::s16>());
|
||||||
|
case thing_type::S32: return std::forward<Func>(func)(get<thing_type::s32>());
|
||||||
|
case thing_type::S64: return std::forward<Func>(func)(get<thing_type::s64>());
|
||||||
|
case thing_type::U8: return std::forward<Func>(func)(get<thing_type::u8>());
|
||||||
|
case thing_type::U16: return std::forward<Func>(func)(get<thing_type::u16>());
|
||||||
|
case thing_type::U32: return std::forward<Func>(func)(get<thing_type::u32>());
|
||||||
|
case thing_type::U64: return std::forward<Func>(func)(get<thing_type::u64>());
|
||||||
|
default: throw bad_thing_access();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Op>
|
template <typename Op>
|
||||||
thing binary_op(const thing& rhs, const Op& op) const {
|
thing binary_op(const thing& rhs, const Op& op) const {
|
||||||
thing lhsRsv = resolve();
|
if (thing_type::is_primitive(true_type().type) && thing_type::is_primitive(true_type().type)) {
|
||||||
thing rhsRsv = rhs.resolve();
|
static constexpr enum thing_type::type promotions[8 * 8] = {
|
||||||
|
// S8
|
||||||
|
thing_type::S8,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::U64,
|
||||||
|
// S16
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::U64,
|
||||||
|
// S32
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::U64,
|
||||||
|
// S64
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::U64,
|
||||||
|
// U8
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::U8,
|
||||||
|
thing_type::U16,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::U64,
|
||||||
|
// U16
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S16,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::U16,
|
||||||
|
thing_type::U16,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::U64,
|
||||||
|
// U32
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::S32,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::S64,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::U32,
|
||||||
|
thing_type::U64,
|
||||||
|
// U64
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
thing_type::U64,
|
||||||
|
};
|
||||||
|
|
||||||
if (lhsRsv.type().t == type_t::Primitive && rhsRsv.type().t == type_t::Primitive) {
|
enum thing_type::type resultType = promotions[true_type().type + (rhs.true_type().type * 8)];
|
||||||
std::size_t size = std::max(lhsRsv.type().primitive, rhsRsv.type().primitive);
|
|
||||||
|
|
||||||
long_t result = op(lhsRsv.integer(), rhsRsv.integer());
|
thing res = { thing_type{ resultType }, m_allocator };
|
||||||
|
switch (resultType) {
|
||||||
thing res({ size }, m_allocator);
|
case thing_type::S8:
|
||||||
switch (size) {
|
res.get<thing_type::s8>() = Op{}(cast_to<thing_type::s8>(), rhs.cast_to<thing_type::s8>());
|
||||||
case sizeof(byte_t): res.get<byte_t>() = result; break;
|
return res;
|
||||||
case sizeof(short_t): res.get<short_t>() = result; break;
|
case thing_type::S16:
|
||||||
case sizeof(int_t): res.get<int_t>() = result; break;
|
res.get<thing_type::s16>() = Op{}(cast_to<thing_type::s16>(), rhs.cast_to<thing_type::s16>());
|
||||||
case sizeof(long_t): res.get<long_t>() = result; break;
|
return res;
|
||||||
default: throw std::runtime_error("unreachable");
|
case thing_type::S32:
|
||||||
|
res.get<thing_type::s32>() = Op{}(cast_to<thing_type::s32>(), rhs.cast_to<thing_type::s32>());
|
||||||
|
return res;
|
||||||
|
case thing_type::S64:
|
||||||
|
res.get<thing_type::s64>() = Op{}(cast_to<thing_type::s64>(), rhs.cast_to<thing_type::s64>());
|
||||||
|
return res;
|
||||||
|
case thing_type::U8:
|
||||||
|
res.get<thing_type::u8>() = Op{}(cast_to<thing_type::u8>(), rhs.cast_to<thing_type::u8>());
|
||||||
|
return res;
|
||||||
|
case thing_type::U16:
|
||||||
|
res.get<thing_type::u16>() = Op{}(cast_to<thing_type::u16>(), rhs.cast_to<thing_type::u16>());
|
||||||
|
return res;
|
||||||
|
case thing_type::U32:
|
||||||
|
res.get<thing_type::u32>() = Op{}(cast_to<thing_type::u32>(), rhs.cast_to<thing_type::u32>());
|
||||||
|
return res;
|
||||||
|
case thing_type::U64:
|
||||||
|
res.get<thing_type::u64>() = Op{}(cast_to<thing_type::u64>(), rhs.cast_to<thing_type::u64>());
|
||||||
|
return res;
|
||||||
|
case thing_type::Ptr: // TODO: Pointer arithmetics
|
||||||
|
case thing_type::Ref:
|
||||||
|
case thing_type::Array:
|
||||||
|
case thing_type::Count: break;
|
||||||
}
|
}
|
||||||
return std::move(res);
|
throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("unexpected operation");
|
throw std::runtime_error("unexpected operation");
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
type_p m_type;
|
thing_type m_type;
|
||||||
std::size_t m_size;
|
std::size_t m_size;
|
||||||
std::byte* m_data;
|
std::byte* m_data;
|
||||||
|
|
||||||
allocator_type m_allocator;
|
allocator_type m_allocator;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class thing_allocator {
|
|
||||||
template <typename>
|
|
||||||
friend class thing_allocator;
|
|
||||||
|
|
||||||
using dead_things = std::vector<std::pair<T*, std::size_t>>;
|
|
||||||
public:
|
|
||||||
using value_type = T; /**< Value type. */
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructs a thing allocator.
|
|
||||||
*
|
|
||||||
* @param arena Base arena allocator.
|
|
||||||
*/
|
|
||||||
explicit thing_allocator(furlang::arena& arena) noexcept
|
|
||||||
: m_arena(&arena), m_deadThings(std::make_shared<dead_things>()) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator(thing_allocator<U>&& other) noexcept
|
|
||||||
: m_arena(std::move(other.m_arena)), m_deadThings(std::move(other.m_deadThings)) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
|
|
||||||
if (this == &other) return *this;
|
|
||||||
m_arena = std::move(other.m_arena);
|
|
||||||
m_deadThings = std::move(other.m_deadThings);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copy constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator(const thing_allocator<U>& other) noexcept
|
|
||||||
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copy constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
|
|
||||||
if (this == &other) return *this;
|
|
||||||
m_arena = other.m_arena;
|
|
||||||
m_deadThings = other.m_deadThings;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Returns a free chunk of memory.
|
|
||||||
*
|
|
||||||
* @param count Count of the things that must fit inside the chunk.
|
|
||||||
* @return The chunk.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] T* allocate(std::size_t count = 1) {
|
|
||||||
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
|
|
||||||
if (it->second != count) continue;
|
|
||||||
m_deadThings->erase(it);
|
|
||||||
return it->first;
|
|
||||||
}
|
|
||||||
return m_arena->allocate<T>(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Recycles the pointer.
|
|
||||||
*/
|
|
||||||
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings->emplace_back(ptr, count); }
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Compares two thing allocators for equality.
|
|
||||||
*
|
|
||||||
* @return true if the two things are equal.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
bool operator==(const thing_allocator<U>& other) const noexcept {
|
|
||||||
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares two thing allocators for inequality.
|
|
||||||
*
|
|
||||||
* @return true if the two things are not equal.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
bool operator!=(const thing_allocator<U>& other) const noexcept {
|
|
||||||
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
furlang::arena* m_arena;
|
|
||||||
|
|
||||||
std::shared_ptr<dead_things> m_deadThings;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|
||||||
#endif // FURVM_THING_HPP
|
#endif // FURVM_THING_HPP
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
#ifndef FURVM_THING_ALLOCATOR_HPP
|
||||||
|
#define FURVM_THING_ALLOCATOR_HPP
|
||||||
|
|
||||||
|
#include "furlang/arena.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace furvm {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class thing_allocator {
|
||||||
|
template <typename>
|
||||||
|
friend class thing_allocator;
|
||||||
|
|
||||||
|
using dead_things = std::vector<std::pair<T*, std::size_t>>;
|
||||||
|
public:
|
||||||
|
using value_type = T; /**< Value type. */
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a thing allocator.
|
||||||
|
*
|
||||||
|
* @param arena Base arena allocator.
|
||||||
|
*/
|
||||||
|
explicit thing_allocator(furlang::arena& arena) noexcept
|
||||||
|
: m_arena(&arena), m_deadThings(std::make_shared<dead_things>()) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
thing_allocator(thing_allocator<U>&& other) noexcept
|
||||||
|
: m_arena(std::move(other.m_arena)), m_deadThings(std::move(other.m_deadThings)) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
m_arena = std::move(other.m_arena);
|
||||||
|
m_deadThings = std::move(other.m_deadThings);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructor.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
thing_allocator(const thing_allocator<U>& other) noexcept
|
||||||
|
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructor.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
m_arena = other.m_arena;
|
||||||
|
m_deadThings = other.m_deadThings;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns a free chunk of memory.
|
||||||
|
*
|
||||||
|
* @param count Count of the things that must fit inside the chunk.
|
||||||
|
* @return The chunk.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] T* allocate(std::size_t count = 1) {
|
||||||
|
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
|
||||||
|
if (it->second != count) continue;
|
||||||
|
T* data = it->first;
|
||||||
|
m_deadThings->erase(it);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return m_arena->allocate<T>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Recycles the pointer.
|
||||||
|
*/
|
||||||
|
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings->emplace_back(ptr, count); }
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Compares two thing allocators for equality.
|
||||||
|
*
|
||||||
|
* @return true if the two things are equal.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
bool operator==(const thing_allocator<U>& other) const noexcept {
|
||||||
|
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two thing allocators for inequality.
|
||||||
|
*
|
||||||
|
* @return true if the two things are not equal.
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
bool operator!=(const thing_allocator<U>& other) const noexcept {
|
||||||
|
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
furlang::arena* m_arena;
|
||||||
|
|
||||||
|
std::shared_ptr<dead_things> m_deadThings;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furvm
|
||||||
|
|
||||||
|
#endif // FURVM_THING_ALLOCATOR_HPP
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include "furvm/context.hpp"
|
|
||||||
|
|
||||||
#include "furvm/thing.hpp" // IWYU pragma: keep
|
|
||||||
|
|
||||||
namespace furvm {
|
|
||||||
|
|
||||||
context::context()
|
|
||||||
: m_thingAllocator(m_thingArena) {}
|
|
||||||
|
|
||||||
} // namespace furvm
|
|
||||||
@@ -1,49 +1,127 @@
|
|||||||
#include "furvm/detail/serialization.hpp"
|
#include "furvm/detail/serialization.hpp"
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
namespace furvm::detail {
|
namespace furvm::detail {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T, std::size_t... I>
|
||||||
|
void serialize_integral_impl(std::ostream& os, std::make_unsigned_t<T> u_value, std::index_sequence<I...>) {
|
||||||
|
((os << static_cast<char>((u_value >> (I * 8)) & 0xFF)), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t... I>
|
||||||
|
void load_integral_impl(const char* bs, std::make_unsigned_t<T>& u_value, std::index_sequence<I...>) {
|
||||||
|
using UnsignedT = std::make_unsigned_t<T>;
|
||||||
|
((u_value |= (static_cast<UnsignedT>(static_cast<std::uint8_t>(bs[I])) << (I * 8))), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::ostream& serialize_integral(std::ostream& os, T value) {
|
||||||
|
static_assert(std::is_integral_v<T>, "Type must be an integral type.");
|
||||||
|
using UnsignedT = std::make_unsigned_t<T>;
|
||||||
|
|
||||||
|
serialize_integral_impl<T>(os, static_cast<UnsignedT>(value), std::make_index_sequence<sizeof(T)>{});
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::istream& load_integral(std::istream& is, T& value) {
|
||||||
|
static_assert(std::is_integral_v<T>, "Type must be an integral type.");
|
||||||
|
|
||||||
|
char bs[sizeof(T)];
|
||||||
|
if (!is.read(bs, sizeof(T))) {
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
using UnsignedT = std::make_unsigned_t<T>;
|
||||||
|
UnsignedT uVal = 0;
|
||||||
|
|
||||||
|
load_integral_impl<T>(bs, uVal, std::make_index_sequence<sizeof(T)>{});
|
||||||
|
|
||||||
|
value = static_cast<T>(uVal);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::int8_t value) {
|
std::ostream& serialize(std::ostream& os, std::int8_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF);
|
return serialize_integral(os, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::int16_t value) {
|
std::ostream& serialize(std::ostream& os, std::int16_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF);
|
return serialize_integral(os, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::int32_t value) {
|
std::ostream& serialize(std::ostream& os, std::int32_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF)
|
return serialize_integral(os, value);
|
||||||
<< (char)((value >> 24ULL) & 0xFF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::int64_t value) {
|
std::ostream& serialize(std::ostream& os, std::int64_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF)
|
return serialize_integral(os, value);
|
||||||
<< (char)((value >> 24ULL) & 0xFF) << (char)((value >> 32ULL) & 0xFF) << (char)((value >> 40ULL) & 0xFF)
|
|
||||||
<< (char)((value >> 48ULL) & 0xFF) << (char)((value >> 56ULL) & 0xFF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::uint8_t value) {
|
std::ostream& serialize(std::ostream& os, std::uint8_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF);
|
return serialize_integral(os, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::uint16_t value) {
|
std::ostream& serialize(std::ostream& os, std::uint16_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF);
|
return serialize_integral(os, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::uint32_t value) {
|
std::ostream& serialize(std::ostream& os, std::uint32_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF)
|
return serialize_integral(os, value);
|
||||||
<< (char)((value >> 24ULL) & 0xFF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, std::uint64_t value) {
|
std::ostream& serialize(std::ostream& os, std::uint64_t value) {
|
||||||
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF)
|
return serialize_integral(os, value);
|
||||||
<< (char)((value >> 24ULL) & 0xFF) << (char)((value >> 32ULL) & 0xFF) << (char)((value >> 40ULL) & 0xFF)
|
|
||||||
<< (char)((value >> 48ULL) & 0xFF) << (char)((value >> 56ULL) & 0xFF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& serialize(std::ostream& os, const std::string& value) {
|
std::ostream& serialize(std::ostream& os, const std::string& value) {
|
||||||
return serialize(os, std::uint16_t(value.size())).write(value.data(), static_cast<std::streamsize>(value.size()));
|
return serialize(os, static_cast<std::uint16_t>(value.size()))
|
||||||
|
.write(value.data(), static_cast<std::streamsize>(value.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::int8_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::int16_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::int32_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::int64_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::uint8_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::uint16_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::uint32_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::uint64_t& value) {
|
||||||
|
return load_integral(is, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& load(std::istream& is, std::string& value) {
|
||||||
|
std::uint16_t length = 0;
|
||||||
|
load(is, length);
|
||||||
|
|
||||||
|
value.resize(length);
|
||||||
|
return is.read(value.data(), static_cast<std::streamsize>(length));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace furvm::detail
|
} // namespace furvm::detail
|
||||||
|
|||||||
+159
-20
@@ -7,32 +7,72 @@
|
|||||||
#include "furvm/instruction.hpp"
|
#include "furvm/instruction.hpp"
|
||||||
#include "furvm/thing.hpp"
|
#include "furvm/thing.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
|
thing_type executor::thing_type_impl(mod_h mod, mod_type type) const {
|
||||||
|
while (type.type == mod_type::Import) {
|
||||||
|
auto imprt = std::move(type.value.imprt);
|
||||||
|
mod = m_context->at(imprt.modId);
|
||||||
|
type = *mod->type_at(imprt.typeId);
|
||||||
|
}
|
||||||
|
switch (static_cast<enum thing_type::type>(type.type)) {
|
||||||
|
case thing_type::S8:
|
||||||
|
case thing_type::S16:
|
||||||
|
case thing_type::S32:
|
||||||
|
case thing_type::S64:
|
||||||
|
case thing_type::U8:
|
||||||
|
case thing_type::U16:
|
||||||
|
case thing_type::U32:
|
||||||
|
case thing_type::U64: return { static_cast<enum thing_type::type>(type.type) };
|
||||||
|
case thing_type::Ptr: return { thing_type::Ptr, thing_type(mod, *mod->type_at(type.value.typeRef)) };
|
||||||
|
case thing_type::Array: {
|
||||||
|
return { static_cast<enum thing_type::type>(type.type),
|
||||||
|
{ thing_type(mod, *mod->type_at(type.value.array.typeId)), type.value.array.size } };
|
||||||
|
}
|
||||||
|
default: throw std::runtime_error("invalid thing type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thing_type* executor::thing_type(const mod_h& mod, const mod_type& type) const {
|
||||||
|
struct thing_type thingType = thing_type_impl(mod, type);
|
||||||
|
return m_context->thing_type_store().insert(thingType);
|
||||||
|
}
|
||||||
|
|
||||||
void executor::push_frame(const mod_h& mod, function function) {
|
void executor::push_frame(const mod_h& mod, function function) {
|
||||||
mod_h modInst = mod;
|
mod_h modInst = mod;
|
||||||
while (function.type() == function_t::Import) {
|
while (function.type() == function_t::Import) {
|
||||||
modInst = m_context->module_at(function.imp().mod);
|
modInst = m_context->at(function.imp().mod);
|
||||||
function = *modInst->function_at(function.imp().function);
|
function = *modInst->function_at(function.imp().function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto signature = function.signature();
|
||||||
std::vector<thing_h> args;
|
std::vector<thing_h> args;
|
||||||
args.reserve(function.param_count());
|
args.reserve(signature.params.size());
|
||||||
for (size_t i = 0; i < function.param_count(); ++i)
|
for (const auto& param : signature.params) {
|
||||||
args.push_back(pop_thing());
|
auto arg = pop_thing();
|
||||||
|
if (arg->type() != *thing_type(mod, *param)) throw std::runtime_error("function argument type mismatch");
|
||||||
|
args.push_back(std::move(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thing_type* returnType = nullptr;
|
||||||
|
if (function.signature().returnType.has_value())
|
||||||
|
returnType = thing_type(mod, *function.signature().returnType.value()); // NOLINT
|
||||||
|
|
||||||
switch (function.type()) {
|
switch (function.type()) {
|
||||||
case function_t::Normal: {
|
case function_t::Normal: {
|
||||||
m_frames.emplace((struct executor::frame){ mod, function.position(), m_stack.size(), std::move(args) });
|
m_frames.emplace(
|
||||||
|
(struct executor::frame){ mod, function.position(), m_stack.size(), returnType, std::move(args) });
|
||||||
} break;
|
} break;
|
||||||
case function_t::Native: {
|
case function_t::Native: {
|
||||||
m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), std::move(args) });
|
m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), returnType, std::move(args) });
|
||||||
modInst->get_native_function(function.name())(*this);
|
modInst->get_native_function(function.native())(*this);
|
||||||
m_frames.pop();
|
pop_frame();
|
||||||
} break;
|
} break;
|
||||||
default: throw std::runtime_error("unexpected function type");
|
default: throw std::runtime_error("unexpected function type");
|
||||||
}
|
}
|
||||||
@@ -42,6 +82,10 @@ struct executor::frame executor::pop_frame() {
|
|||||||
if (m_frames.empty()) throw stack_underflow();
|
if (m_frames.empty()) throw stack_underflow();
|
||||||
struct executor::frame frame = m_frames.top();
|
struct executor::frame frame = m_frames.top();
|
||||||
m_frames.pop();
|
m_frames.pop();
|
||||||
|
thing_h returnValue;
|
||||||
|
if (frame.returnType != nullptr) returnValue = pop_thing();
|
||||||
|
if (m_stack.size() != frame.stackBase) throw std::runtime_error("unexhausted stack");
|
||||||
|
if (frame.returnType != nullptr) push_thing(std::move(returnValue));
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +101,7 @@ thing_h executor::pop_thing() {
|
|||||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||||
thing_h top = std::move(m_stack.top());
|
thing_h top = std::move(m_stack.top());
|
||||||
m_stack.pop();
|
m_stack.pop();
|
||||||
return top;
|
return std::move(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
thing_h executor::thing() const {
|
thing_h executor::thing() const {
|
||||||
@@ -79,7 +123,7 @@ void executor::store_thing(variable_t variable, thing_h&& thing) {
|
|||||||
|
|
||||||
thing_h executor::load_thing(variable_t variable) const {
|
thing_h executor::load_thing(variable_t variable) const {
|
||||||
const auto& frame = m_frames.top();
|
const auto& frame = m_frames.top();
|
||||||
return frame.variables[variable];
|
return { frame.variables[variable] };
|
||||||
}
|
}
|
||||||
|
|
||||||
void executor::step() {
|
void executor::step() {
|
||||||
@@ -90,8 +134,63 @@ void executor::step() {
|
|||||||
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte(frame.position++));
|
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte(frame.position++));
|
||||||
switch (instr) {
|
switch (instr) {
|
||||||
case instruction_t::NoOperation: break;
|
case instruction_t::NoOperation: break;
|
||||||
case instruction_t::PushB2I: {
|
case instruction_t::PushS8: {
|
||||||
push_thing({ intType, m_context->thing_alloc() })->get<int_t>() = frame.mod->byte(frame.position++);
|
push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() })->get<thing_type::s8>() =
|
||||||
|
static_cast<thing_type::s8>(frame.mod->byte(frame.position++));
|
||||||
|
} break;
|
||||||
|
case instruction_t::PushU8: {
|
||||||
|
push_thing({ (struct thing_type){ thing_type::U8 }, m_context->thing_alloc() })->get<thing_type::u8>() =
|
||||||
|
static_cast<thing_type::u8>(frame.mod->byte(frame.position++));
|
||||||
|
} break;
|
||||||
|
case instruction_t::PushS16: {
|
||||||
|
thing_type::u16 value = frame.mod->byte(frame.position++);
|
||||||
|
value |= static_cast<thing_type::u16>(frame.mod->byte(frame.position++) << 8);
|
||||||
|
push_thing({ (struct thing_type){ thing_type::S16 }, m_context->thing_alloc() })->get<thing_type::s16>() =
|
||||||
|
static_cast<thing_type::s16>(value);
|
||||||
|
} break;
|
||||||
|
case instruction_t::PushU16: {
|
||||||
|
thing_type::u16 value = frame.mod->byte(frame.position++);
|
||||||
|
value |= static_cast<thing_type::u16>(frame.mod->byte(frame.position++) << 8);
|
||||||
|
push_thing({ (struct thing_type){ thing_type::U16 }, m_context->thing_alloc() })->get<thing_type::u16>() =
|
||||||
|
value;
|
||||||
|
} break;
|
||||||
|
case instruction_t::PushS32: {
|
||||||
|
push_thing({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() })->get<thing_type::s32>() =
|
||||||
|
static_cast<thing_type::s32>(frame.mod->byte(frame.position++));
|
||||||
|
} break;
|
||||||
|
case instruction_t::PushU32: {
|
||||||
|
push_thing({ (struct thing_type){ thing_type::U32 }, m_context->thing_alloc() })->get<thing_type::u32>() =
|
||||||
|
static_cast<thing_type::u32>(frame.mod->byte(frame.position++));
|
||||||
|
} break;
|
||||||
|
case instruction_t::Array: {
|
||||||
|
mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte(frame.position)) |
|
||||||
|
(static_cast<mod_type_id>(frame.mod->byte(frame.position + 1)) << 8) |
|
||||||
|
(static_cast<mod_type_id>(frame.mod->byte(frame.position + 2)) << 16) |
|
||||||
|
(static_cast<mod_type_id>(frame.mod->byte(frame.position + 3)) << 24);
|
||||||
|
frame.position += 4;
|
||||||
|
|
||||||
|
const auto& type = *thing_type(frame.mod, *frame.mod->type_at(typeId));
|
||||||
|
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
|
||||||
|
throw std::runtime_error("invalid array type");
|
||||||
|
|
||||||
|
auto array = push_thing({ type, m_context->thing_alloc() });
|
||||||
|
|
||||||
|
if (type.value.array.size == 0) {
|
||||||
|
auto sizeThing = pop_thing();
|
||||||
|
std::int64_t size = sizeThing->integer();
|
||||||
|
array->resize(size);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case instruction_t::Get: {
|
||||||
|
auto index = pop_thing();
|
||||||
|
auto array = pop_thing();
|
||||||
|
push_thing(array->at(index->integer()));
|
||||||
|
} break;
|
||||||
|
case instruction_t::Set: {
|
||||||
|
auto element = pop_thing();
|
||||||
|
auto index = pop_thing();
|
||||||
|
auto array = pop_thing();
|
||||||
|
array->at(index->integer()).assign(std::move(element->clone()));
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Drop: {
|
case instruction_t::Drop: {
|
||||||
pop_thing();
|
pop_thing();
|
||||||
@@ -99,12 +198,20 @@ void executor::step() {
|
|||||||
case instruction_t::Duplicate: {
|
case instruction_t::Duplicate: {
|
||||||
push_thing(thing());
|
push_thing(thing());
|
||||||
} break;
|
} break;
|
||||||
|
case instruction_t::Swap: {
|
||||||
|
auto thing1 = pop_thing();
|
||||||
|
auto thing2 = pop_thing();
|
||||||
|
push_thing(std::move(thing1));
|
||||||
|
push_thing(std::move(thing2));
|
||||||
|
} break;
|
||||||
case instruction_t::Clone: {
|
case instruction_t::Clone: {
|
||||||
push_thing(std::move(thing()->clone()));
|
push_thing(std::move(thing()->clone()));
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Reference: {
|
case instruction_t::Reference: {
|
||||||
auto thing = pop_thing();
|
auto thing = pop_thing();
|
||||||
push_thing(std::move(thing->reference()));
|
push_thing({ (struct thing_type){ thing_type::Ref, m_context->thing_type_store().insert(thing->type()) },
|
||||||
|
m_context->thing_alloc() })
|
||||||
|
->reference(*thing);
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Add: {
|
case instruction_t::Add: {
|
||||||
auto rhs = pop_thing();
|
auto rhs = pop_thing();
|
||||||
@@ -161,6 +268,42 @@ void executor::step() {
|
|||||||
auto lhs = pop_thing();
|
auto lhs = pop_thing();
|
||||||
push_thing(lhs->greater_equals(*rhs));
|
push_thing(lhs->greater_equals(*rhs));
|
||||||
} break;
|
} break;
|
||||||
|
case instruction_t::Pointerof: {
|
||||||
|
auto thing = pop_thing();
|
||||||
|
auto ptr =
|
||||||
|
push_thing({ (struct thing_type){ thing_type::Ptr, m_context->thing_type_store().at(thing->type().id) },
|
||||||
|
m_context->thing_alloc() });
|
||||||
|
ptr->get<void*>() = thing->raw();
|
||||||
|
} break;
|
||||||
|
case instruction_t::Sizeof: {
|
||||||
|
auto thing = pop_thing();
|
||||||
|
auto size = push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() });
|
||||||
|
switch (thing->type().type) {
|
||||||
|
case thing_type::S8:
|
||||||
|
case thing_type::S16:
|
||||||
|
case thing_type::S32:
|
||||||
|
case thing_type::S64:
|
||||||
|
case thing_type::U8:
|
||||||
|
case thing_type::U16:
|
||||||
|
case thing_type::U32:
|
||||||
|
case thing_type::U64:
|
||||||
|
size->get<thing_type::u64>() = static_cast<thing_type::u64>(thing_type::primitive_size(thing->type().type));
|
||||||
|
break;
|
||||||
|
case thing_type::Ptr: size->get<thing_type::u64>() = static_cast<thing_type::u64>(sizeof(void*)); break;
|
||||||
|
case thing_type::Array:
|
||||||
|
/* TODO: Return actual memory size of the array
|
||||||
|
* By the memory size I mean the length times sizeof single element.
|
||||||
|
*/
|
||||||
|
size->get<thing_type::u64>() = thing->length();
|
||||||
|
break;
|
||||||
|
default: throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case instruction_t::Lengthof: {
|
||||||
|
auto thing = pop_thing();
|
||||||
|
auto length = push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() });
|
||||||
|
length->get<thing_type::u64>() = thing->length();
|
||||||
|
} break;
|
||||||
case instruction_t::Load: {
|
case instruction_t::Load: {
|
||||||
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
|
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
|
||||||
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
|
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
|
||||||
@@ -180,22 +323,18 @@ void executor::step() {
|
|||||||
push_frame(frame.mod, *frame.mod->function_at(funcId));
|
push_frame(frame.mod, *frame.mod->function_at(funcId));
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Jump: {
|
case instruction_t::Jump: {
|
||||||
frame.position += ((std::int8_t)frame.mod->byte(frame.position)) + 1;
|
std::int8_t offset = static_cast<std::int8_t>(frame.mod->byte(frame.position++));
|
||||||
|
frame.position += offset;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::JumpNotZero: {
|
case instruction_t::JumpNotZero: {
|
||||||
byte offset = frame.mod->byte(frame.position++);
|
byte offset = frame.mod->byte(frame.position++);
|
||||||
auto cond = pop_thing();
|
auto cond = pop_thing();
|
||||||
if (cond->get<int_t>() != 0) frame.position += (std::int8_t)offset;
|
if (cond->integer() != 0) frame.position += (std::int8_t)offset;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Return: {
|
case instruction_t::Return: {
|
||||||
pop_frame();
|
pop_frame();
|
||||||
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
|
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::ReturnValue: {
|
|
||||||
auto value = pop_thing();
|
|
||||||
pop_frame();
|
|
||||||
push_thing(std::move(value));
|
|
||||||
} break;
|
|
||||||
case instruction_t::PushConstant: throw std::runtime_error("unimplemented");
|
case instruction_t::PushConstant: throw std::runtime_error("unimplemented");
|
||||||
default: throw std::runtime_error("unknown instruction");
|
default: throw std::runtime_error("unknown instruction");
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-7
@@ -7,7 +7,7 @@
|
|||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
function::function(const mod_h& mod, const function_h& function)
|
function::function(const mod_h& mod, const function_h& function)
|
||||||
: m_type(function_t::Import), m_paramCount(0), m_value(import_function{ mod.id(), function.id() }) {}
|
: m_type(function_t::Import), m_value(import_function{ mod.id(), function.id() }) {}
|
||||||
|
|
||||||
function::~function() {
|
function::~function() {
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
@@ -23,7 +23,7 @@ function::~function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function::function(function&& other) noexcept
|
function::function(function&& other) noexcept
|
||||||
: m_name(std::move(other.m_name)), m_type(other.m_type), m_paramCount(other.m_paramCount) {
|
: m_type(other.m_type), m_signature(std::move(other.m_signature)) {
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case function_t::Normal: {
|
case function_t::Normal: {
|
||||||
m_value.position = other.m_value.position;
|
m_value.position = other.m_value.position;
|
||||||
@@ -42,9 +42,8 @@ function::function(function&& other) noexcept
|
|||||||
function& function::operator=(function&& other) noexcept {
|
function& function::operator=(function&& other) noexcept {
|
||||||
if (this == &other) return *this;
|
if (this == &other) return *this;
|
||||||
|
|
||||||
m_name = std::move(other.m_name);
|
|
||||||
m_type = other.m_type;
|
m_type = other.m_type;
|
||||||
m_paramCount = other.m_paramCount;
|
m_signature = other.m_signature;
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case function_t::Normal: {
|
case function_t::Normal: {
|
||||||
m_value.position = other.m_value.position;
|
m_value.position = other.m_value.position;
|
||||||
@@ -63,7 +62,7 @@ function& function::operator=(function&& other) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function::function(const function& other)
|
function::function(const function& other)
|
||||||
: m_name(other.m_name), m_type(other.m_type), m_paramCount(other.m_paramCount) {
|
: m_type(other.m_type), m_signature(other.m_signature) {
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case function_t::Normal: {
|
case function_t::Normal: {
|
||||||
m_value.position = other.m_value.position;
|
m_value.position = other.m_value.position;
|
||||||
@@ -81,9 +80,8 @@ function::function(const function& other)
|
|||||||
function& function::operator=(const function& other) {
|
function& function::operator=(const function& other) {
|
||||||
if (this == &other) return *this;
|
if (this == &other) return *this;
|
||||||
|
|
||||||
m_name = other.m_name;
|
|
||||||
m_type = other.m_type;
|
m_type = other.m_type;
|
||||||
m_paramCount = other.m_paramCount;
|
m_signature = other.m_signature;
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case function_t::Normal: {
|
case function_t::Normal: {
|
||||||
m_value.position = other.m_value.position;
|
m_value.position = other.m_value.position;
|
||||||
@@ -100,4 +98,12 @@ function& function::operator=(const function& other) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
std::size_t function_sig_hash::operator()(const function_sig& signature) const {
|
||||||
|
return furlang::utility::vector_hash<mod_type_h, detail::handle_hash<mod_type_h>>{}(signature.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|||||||
+68
-20
@@ -1,5 +1,6 @@
|
|||||||
#ifndef LIBFURVM
|
#ifndef LIBFURVM
|
||||||
|
|
||||||
|
#include "furvm/detail/serialization.hpp"
|
||||||
#include "furvm/furvm.hpp"
|
#include "furvm/furvm.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -7,33 +8,80 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
static constexpr std::array<furvm::byte, 9> s_bytecode = {
|
static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) {
|
||||||
furvm::byte(furvm::instruction_t::PushB2I),
|
using namespace furvm;
|
||||||
67,
|
|
||||||
furvm::byte(furvm::instruction_t::Duplicate),
|
|
||||||
furvm::byte(furvm::instruction_t::Reference),
|
|
||||||
furvm::byte(furvm::instruction_t::Add),
|
|
||||||
furvm::byte(furvm::instruction_t::Call),
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
furvm::byte(furvm::instruction_t::Return),
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(void) {
|
switch (thing.true_type().type) {
|
||||||
|
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
|
||||||
|
case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break;
|
||||||
|
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;
|
||||||
|
case thing_type::S64: std::cout << thing.get<thing_type::s64>(); break;
|
||||||
|
case thing_type::U8: std::cout << thing.get<thing_type::u8>(); break;
|
||||||
|
case thing_type::U16: std::cout << thing.get<thing_type::u16>(); break;
|
||||||
|
case thing_type::U32: std::cout << thing.get<thing_type::u32>(); break;
|
||||||
|
case thing_type::U64: std::cout << thing.get<thing_type::u64>(); break;
|
||||||
|
case thing_type::Array:
|
||||||
|
std::cout << "{ ";
|
||||||
|
for (thing_type::u64 i = 0; i < thing.length(); ++i) {
|
||||||
|
if (i > 0) std::cout << ", ";
|
||||||
|
print_thing(thing.at(i));
|
||||||
|
}
|
||||||
|
std::cout << " }";
|
||||||
|
break;
|
||||||
|
default: std::cerr << "{Type not recognized}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
auto context = std::make_shared<furvm::context>();
|
auto context = std::make_shared<furvm::context>();
|
||||||
|
|
||||||
furvm::mod_h furlangModule = context->emplace_module("furlang");
|
#if 1 // NOLINT
|
||||||
furvm::function_h printFunc = furlangModule->emplace_function("print", 1, "print");
|
if (argc != 2) {
|
||||||
furlangModule->set_native_function("print",
|
std::cerr << "Usage: " << argv[0] << " <mod.fmod>\n";
|
||||||
[](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; });
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
furvm::mod_h mainModule = context->emplace_module("main", s_bytecode.begin(), s_bytecode.end());
|
std::ifstream file(argv[1]);
|
||||||
furvm::function_h mainFunc = mainModule->emplace_function("main", 0, 0);
|
if (!file.is_open()) {
|
||||||
mainModule->emplace_function(furlangModule, printFunc).dispatch();
|
std::cerr << "Failed to open " << argv[1] << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
furvm::mod_h mod = context->emplace("main", std::move(furvm::mod::load(file)));
|
||||||
|
auto mainFunc = mod->function_at("main", furvm::function_sig{});
|
||||||
|
#else
|
||||||
|
static const furvm::byte s_bytecode[] = {
|
||||||
|
static_cast<furvm::byte>(furvm::instruction_t::Array),
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
static_cast<furvm::byte>(furvm::instruction_t::Reference),
|
||||||
|
static_cast<furvm::byte>(furvm::instruction_t::Lengthof),
|
||||||
|
static_cast<furvm::byte>(furvm::instruction_t::Call),
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
static_cast<furvm::byte>(furvm::instruction_t::Drop),
|
||||||
|
static_cast<furvm::byte>(furvm::instruction_t::Return),
|
||||||
|
};
|
||||||
|
|
||||||
|
auto mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode));
|
||||||
|
auto charType = mod->emplace_type(furvm::mod_type::S8);
|
||||||
|
auto arrayType = mod->emplace_type(charType.id(), 10);
|
||||||
|
auto u64Type = mod->emplace_type(furvm::mod_type::U64);
|
||||||
|
auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0);
|
||||||
|
mod->emplace_function(furvm::function_sig{ { u64Type }, u64Type }, "print").dispatch();
|
||||||
|
#endif
|
||||||
|
mod->set_native_function("println", [](furvm::executor& executor) {
|
||||||
|
auto arg = executor.load_thing(0);
|
||||||
|
print_thing(*arg);
|
||||||
|
std::cout << '\n';
|
||||||
|
});
|
||||||
|
|
||||||
furvm::executor_h executor = context->emplace_executor(context);
|
furvm::executor_h executor = context->emplace_executor(context);
|
||||||
executor->push_frame(mainModule, *mainFunc);
|
executor->push_frame(mod, *mainFunc);
|
||||||
|
|
||||||
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
|
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
|
||||||
executor->step();
|
executor->step();
|
||||||
|
|||||||
+175
-7
@@ -1,27 +1,86 @@
|
|||||||
#include "furvm/module.hpp"
|
#include "furvm/module.hpp"
|
||||||
|
|
||||||
#include "furvm/detail/serialization.hpp"
|
#include "furvm/detail/serialization.hpp"
|
||||||
|
#include "furvm/function.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace furvm {
|
namespace furvm {
|
||||||
|
|
||||||
std::ostream& mod::serialize(std::ostream& os) const {
|
std::ostream& mod::serialize(std::ostream& os) const {
|
||||||
os << MAGIC;
|
os.write(MAGIC, sizeof(MAGIC));
|
||||||
detail::serialize(os, std::uint32_t(0)); // version
|
detail::serialize(os, std::uint32_t(0)); // version
|
||||||
|
|
||||||
detail::serialize(os, function_id(m_functionMap.size()));
|
mod_type_id typeCount = mod_type_id(m_types.cend() - m_types.cbegin());
|
||||||
for (auto* pair : m_functions) {
|
detail::serialize(os, typeCount);
|
||||||
function_h func = { pair };
|
for (mod_type_id id = 0; id < typeCount; ++id) {
|
||||||
bool isPublic = m_publicFunctions.find(func->name()) != m_publicFunctions.end();
|
if (!m_types.contains(id)) {
|
||||||
detail::serialize(os, isPublic ? func->name() : ""); // private functions have empty names
|
detail::serialize(os, std::uint8_t(0xFF)); // null type
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = *m_types.at(id);
|
||||||
|
detail::serialize(os, static_cast<std::uint8_t>(type.type));
|
||||||
|
switch (type.type) {
|
||||||
|
case mod_type::S8:
|
||||||
|
case mod_type::S16:
|
||||||
|
case mod_type::S32:
|
||||||
|
case mod_type::S64:
|
||||||
|
case mod_type::U8:
|
||||||
|
case mod_type::U16:
|
||||||
|
case mod_type::U32:
|
||||||
|
case mod_type::U64: break;
|
||||||
|
case mod_type::Ptr:
|
||||||
|
case mod_type::Ref: detail::serialize(os, type.value.typeRef); break;
|
||||||
|
case mod_type::Array: {
|
||||||
|
detail::serialize(os, type.value.array.typeId);
|
||||||
|
detail::serialize(os, type.value.array.size);
|
||||||
|
} break;
|
||||||
|
case mod_type::Import: {
|
||||||
|
detail::serialize(os, type.value.imprt.modId);
|
||||||
|
detail::serialize(os, type.value.imprt.typeId);
|
||||||
|
} break;
|
||||||
|
case mod_type::Count: throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function_id funcCount = m_functions.cend() - m_functions.cbegin();
|
||||||
|
detail::serialize(os, funcCount);
|
||||||
|
for (function_id id = 0; id < funcCount; ++id) {
|
||||||
|
if (!m_functions.contains(id)) {
|
||||||
|
detail::serialize(os, "");
|
||||||
|
detail::serialize(os, 0);
|
||||||
|
detail::serialize(os, std::uint8_t(0xFF)); // null function
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function name
|
||||||
|
function_h func = m_functions.at(id);
|
||||||
|
if (auto it = m_functionMap.find(func.id()); it != m_functionMap.end()) {
|
||||||
|
detail::serialize(os, it->second.first);
|
||||||
|
} else {
|
||||||
|
detail::serialize(os, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function signature
|
||||||
|
detail::serialize(os, static_cast<std::uint32_t>(func->signature().params.size()));
|
||||||
|
for (std::uint32_t i = 0; i < func->signature().params.size(); ++i)
|
||||||
|
detail::serialize(os, func->signature().params[i].id());
|
||||||
|
|
||||||
|
// Function body/value
|
||||||
detail::serialize(os, std::uint8_t(func->type()));
|
detail::serialize(os, std::uint8_t(func->type()));
|
||||||
switch (func->type()) {
|
switch (func->type()) {
|
||||||
case function_t::Normal: {
|
case function_t::Normal: {
|
||||||
detail::serialize(os, func->position());
|
detail::serialize(os, func->position());
|
||||||
} break;
|
} break;
|
||||||
case function_t::Native: break;
|
case function_t::Native: {
|
||||||
|
detail::serialize(os, func->native());
|
||||||
|
} break;
|
||||||
case function_t::Import: {
|
case function_t::Import: {
|
||||||
detail::serialize(os, func->imp().mod);
|
detail::serialize(os, func->imp().mod);
|
||||||
detail::serialize(os, func->imp().function);
|
detail::serialize(os, func->imp().function);
|
||||||
@@ -32,4 +91,113 @@ std::ostream& mod::serialize(std::ostream& os) const {
|
|||||||
detail::serialize(os, std::uint64_t(m_bytecode.size()));
|
detail::serialize(os, std::uint64_t(m_bytecode.size()));
|
||||||
return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size()));
|
return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod mod::load(std::istream& is) {
|
||||||
|
std::uint32_t magic = 0;
|
||||||
|
detail::load(is, magic);
|
||||||
|
std::uint32_t version = 0;
|
||||||
|
detail::load(is, version);
|
||||||
|
if (std::memcmp(&magic, MAGIC, sizeof(MAGIC)) != 0) throw std::runtime_error("invalid magic");
|
||||||
|
if (version != 0) throw std::runtime_error("unsupported version");
|
||||||
|
|
||||||
|
mod mod;
|
||||||
|
|
||||||
|
mod_type_id typeCount = 0;
|
||||||
|
detail::load(is, typeCount);
|
||||||
|
for (mod_type_id id = 0; id < typeCount; ++id) {
|
||||||
|
std::uint8_t type = 0;
|
||||||
|
detail::load(is, type);
|
||||||
|
if (type == 0xFF) continue;
|
||||||
|
|
||||||
|
switch ((enum mod_type::type)type) {
|
||||||
|
case mod_type::S8:
|
||||||
|
case mod_type::S16:
|
||||||
|
case mod_type::S32:
|
||||||
|
case mod_type::S64:
|
||||||
|
case mod_type::U8:
|
||||||
|
case mod_type::U16:
|
||||||
|
case mod_type::U32:
|
||||||
|
case mod_type::U64: {
|
||||||
|
mod_type theType = { (enum mod_type::type)type };
|
||||||
|
mod.emplace_type(id, theType).dispatch();
|
||||||
|
} break;
|
||||||
|
case mod_type::Ptr: {
|
||||||
|
mod_type_id typeId = 0;
|
||||||
|
detail::load(is, typeId);
|
||||||
|
mod.emplace_type(id, typeId).dispatch();
|
||||||
|
} break;
|
||||||
|
case mod_type::Array: {
|
||||||
|
mod_type_id typeId = 0;
|
||||||
|
detail::load(is, typeId);
|
||||||
|
std::size_t size = 0;
|
||||||
|
detail::load(is, size);
|
||||||
|
mod.emplace_type(id, typeId, size).dispatch();
|
||||||
|
} break;
|
||||||
|
case mod_type::Import: {
|
||||||
|
std::string modName;
|
||||||
|
detail::load(is, modName);
|
||||||
|
mod_type_id typeId = 0;
|
||||||
|
detail::load(is, typeId);
|
||||||
|
mod.emplace_type(id, std::move(modName), typeId).dispatch();
|
||||||
|
} break;
|
||||||
|
default: throw std::runtime_error("unknown type type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function_id functionCount = 0;
|
||||||
|
detail::load(is, functionCount);
|
||||||
|
for (function_id id = 0; id < functionCount; ++id) {
|
||||||
|
std::string name;
|
||||||
|
detail::load(is, name);
|
||||||
|
|
||||||
|
function_sig signature;
|
||||||
|
std::uint32_t paramCount = 0;
|
||||||
|
detail::load(is, paramCount);
|
||||||
|
signature.params.reserve(paramCount);
|
||||||
|
while (signature.params.size() < paramCount) {
|
||||||
|
thing_id param{ 0 };
|
||||||
|
detail::load(is, param);
|
||||||
|
signature.params.emplace_back(mod.type_at(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t type = 0;
|
||||||
|
detail::load(is, type);
|
||||||
|
if (type == 0xFF) continue;
|
||||||
|
|
||||||
|
switch ((function_t)type) {
|
||||||
|
case function_t::Normal: {
|
||||||
|
decltype(std::declval<function>().position()) offset = 0;
|
||||||
|
detail::load(is, offset);
|
||||||
|
if (name.empty())
|
||||||
|
mod.emplace_function(id, std::move(signature), offset).dispatch();
|
||||||
|
else
|
||||||
|
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
|
||||||
|
} break;
|
||||||
|
case function_t::Native: {
|
||||||
|
std::string native;
|
||||||
|
detail::load(is, native);
|
||||||
|
if (name.empty())
|
||||||
|
mod.emplace_function(id, std::move(signature), std::move(native)).dispatch();
|
||||||
|
else
|
||||||
|
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
|
||||||
|
} break;
|
||||||
|
case function_t::Import: {
|
||||||
|
std::string modName;
|
||||||
|
detail::load(is, modName);
|
||||||
|
function_id function = 0;
|
||||||
|
detail::load(is, function);
|
||||||
|
mod.emplace_function(id, std::move(modName), function).dispatch();
|
||||||
|
} break;
|
||||||
|
default: throw std::runtime_error("unknown function type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t bytecodeLength = 0;
|
||||||
|
detail::load(is, bytecodeLength);
|
||||||
|
mod.bytecode().resize(bytecodeLength);
|
||||||
|
is.read(reinterpret_cast<char*>(mod.bytecode().data()), static_cast<std::streamsize>(bytecodeLength));
|
||||||
|
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|||||||
+26
-1
@@ -1,3 +1,28 @@
|
|||||||
|
#include "furlang/arena.hpp"
|
||||||
|
#include "furvm/furvm.hpp"
|
||||||
|
#include "furvm/thing.hpp"
|
||||||
|
#include "furvm/thing_allocator.hpp"
|
||||||
|
|
||||||
#include "gtest/gtest.h" // IWYU pragma: keep
|
#include "gtest/gtest.h" // IWYU pragma: keep
|
||||||
|
|
||||||
namespace {}
|
namespace {
|
||||||
|
|
||||||
|
// TODO: Basic program tests (e.g. for loops)
|
||||||
|
|
||||||
|
TEST(Things, Ops) {
|
||||||
|
furlang::arena arena;
|
||||||
|
furvm::thing_allocator<std::byte> alloc{ arena };
|
||||||
|
|
||||||
|
furvm::thing lhs{ furvm::thing_type{ furvm::thing_type::U32 }, alloc };
|
||||||
|
lhs.get<furvm::thing_type::u32>() = 6;
|
||||||
|
furvm::thing rhs{ furvm::thing_type{ furvm::thing_type::U32 }, alloc };
|
||||||
|
rhs.get<furvm::thing_type::u32>() = 7;
|
||||||
|
|
||||||
|
auto res = lhs.add(rhs);
|
||||||
|
ASSERT_EQ(res.type().type, furvm::thing_type::U32);
|
||||||
|
EXPECT_EQ(lhs.get<furvm::thing_type::u32>(), 6);
|
||||||
|
EXPECT_EQ(rhs.get<furvm::thing_type::u32>(), 7);
|
||||||
|
EXPECT_EQ(res.get<furvm::thing_type::u32>(), 6 + 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user