Compare commits

...

17 Commits

Author SHA1 Message Date
CHatingPython 549af109b9 fix: fix this mess
I am stupid
I am stupid
I am stupid
I can't believe how f- ricking stupid I am
2026-07-19 22:04:50 +02:00
CHatingPython 6dce7016ce test(furvm): introduce an add things test 2026-07-19 21:53:37 +02:00
CHatingPython a1e43576cc this code is a mess and I am losing my mind
This commit does not work and I am so down I don't even bother
committing to conventional commits until I fix this mess.
2026-07-19 21:38:00 +02:00
CHatingPython 9f950e45e7 feat(furas): introduce basic generator
Closes: #56
2026-07-17 20:43:34 +02:00
CHatingPython dfaa3081cb fix: use thing::true_type and fix module serialization 2026-07-17 20:41:27 +02:00
CHatingPython 46e92deed5 feat(furvm): introduce missing push instructions
Closes: #58
2026-07-14 12:55:34 +02:00
CHatingPython 42e71080a2 feat(furvm): introduce set instruction
Closes: #57
2026-07-14 12:40:47 +02:00
CHatingPython 03448d865e feat(furas): introduce a basic lexer
Closes: #56
2026-07-14 00:38:27 +02:00
CHatingPython cb845ca625 Merge branch 'master' into furas 2026-07-13 20:34:06 +02:00
CHatingPython 798287ef47 refactor: remove ReturnValue instruction 2026-07-13 20:33:37 +02:00
CHatingPython 466d7f003b chore: initialize furas 2026-07-13 19:12:13 +02:00
CHatingPython ff3fce53f5 feat: add return type to function
Closes: #51
2026-07-13 17:28:22 +02:00
CHatingPython 9b280fab22 refactor(furvm): improve references
Closes: #53
2026-07-12 00:03:39 +02:00
CHatingPython e1b75ccc8e feat(furvm): introduce length of operator
Closes: #54
2026-07-11 23:17:59 +02:00
CHatingPython cd715ae779 feat(furvm): introduce pointer type
Closes: #35
2026-07-11 22:44:18 +02:00
CHatingPython 8d3859ce70 refactor(furvm): improve primitive types
Improve primitive types and binary operations.

Closes: #55
2026-07-11 02:28:23 +02:00
CHatingPython 3c0588e8db refactor(furvm): improve type system
Closes: #52
2026-07-11 00:45:09 +02:00
29 changed files with 1895 additions and 488 deletions
+3 -1
View File
@@ -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: "*"
+2 -1
View File
@@ -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)
@@ -24,4 +25,4 @@ if(DOXYGEN_FOUND)
doxygen_add_docs(doxygen_doc ${CMAKE_SOURCE_DIR}/furlang/ ${CMAKE_SOURCE_DIR}/furvm/ ${CMAKE_SOURCE_DIR}/furc/ doxygen_add_docs(doxygen_doc ${CMAKE_SOURCE_DIR}/furlang/ ${CMAKE_SOURCE_DIR}/furvm/ ${CMAKE_SOURCE_DIR}/furc/
ALL WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ALL WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen") COMMENT "Generating API documentation with Doxygen")
endif() endif()
+5
View File
@@ -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)
+35
View File
@@ -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
+34
View File
@@ -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
+23
View File
@@ -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
View File
+36
View File
@@ -0,0 +1,36 @@
#ifndef FURAS_GEN_HPP
#define FURAS_GEN_HPP
#include "furas/lexer.hpp"
#include "furvm/module.hpp"
#include <string>
namespace furas {
struct generator_error {
enum type {
Success = 0,
Eof = 1,
UnexpectedEof = -1,
UnexpectedToken = -2,
UnknownCharacter = -3,
UnknownType = -4,
} type = Success;
std::string message;
};
class generator {
public:
struct result {
generator_error error;
furvm::mod mod;
};
public:
static result generate(lexer lexer);
};
} // namespace furas
#endif // FURAS_GEN_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURAS_LEXER_HPP
#define FURAS_LEXER_HPP
#include "furas/token.hpp"
#include "furlang/result.hpp"
#include <cstddef>
#include <string_view>
namespace furas {
struct lexer_location {
std::string_view filename;
std::size_t row, col;
};
struct lexer_error {
enum type {
EndOfFile = 0,
UnknownCharacter,
} type;
lexer_location location;
std::string message;
};
using token_r = furlang::result<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
+94
View File
@@ -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
+558
View File
@@ -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
+98
View File
@@ -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
+39
View File
@@ -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;
}
+2 -6
View File
@@ -143,11 +143,7 @@ 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);
@@ -174,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:
+5 -1
View File
@@ -21,7 +21,8 @@ public:
/** /**
* @brief Constructs a context. * @brief Constructs a context.
*/ */
context(); context()
: m_thingAllocator(m_thingArena) {}
~context() = default; ~context() = default;
@@ -124,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;
@@ -131,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
+7 -1
View File
@@ -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,7 +41,8 @@ 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. */
std::vector<thing_h> variables; /**< Frame variables. */ thing_type* returnType; /**< Return type. */
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;
+4 -4
View File
@@ -6,6 +6,7 @@
#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>
@@ -37,7 +38,8 @@ struct import_function {
* @brief Function signature. * @brief Function signature.
*/ */
struct function_sig { struct function_sig {
std::vector<type_h> params; 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 params == rhs.params; }
@@ -190,9 +192,7 @@ private:
namespace detail { namespace detail {
struct function_sig_hash { struct function_sig_hash {
std::size_t operator()(const function_sig& signature) const { std::size_t operator()(const function_sig& signature) const;
return furlang::utility::vector_hash<type_h, detail::handle_hash<type_h>>{}(signature.params);
}
}; };
} // namespace detail } // namespace detail
+12 -20
View File
@@ -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,34 +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>;
using type_id = std::uint32_t;
using type_h = handle<type_p, generic_header<type_id>>;
/** /**
* @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
+42 -9
View File
@@ -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.
@@ -38,6 +61,11 @@ enum class instruction_t : byte {
*/ */
Get, Get,
/**
* @brief Sets an array element.
*/
Set,
/** /**
* @brief Pops top element from the stack. * @brief Pops top element from the stack.
*/ */
@@ -48,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.
*/ */
@@ -125,6 +158,11 @@ enum class instruction_t : byte {
*/ */
Sizeof, 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.
* *
@@ -165,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 {
+125 -3
View File
@@ -5,7 +5,6 @@
#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 "furvm/type.hpp" // IWYU pragma: keep
#include <functional> #include <functional>
#include <istream> #include <istream>
@@ -18,6 +17,129 @@
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;
@@ -181,7 +303,7 @@ public:
*/ */
template <typename... Args> template <typename... Args>
auto emplace_type(Args&&... args) { auto emplace_type(Args&&... args) {
if constexpr (std::is_constructible_v<type_p, Args...>) { if constexpr (std::is_constructible_v<mod_type, Args...>) {
return m_types.emplace_back(std::forward<Args>(args)...); return m_types.emplace_back(std::forward<Args>(args)...);
} else { } else {
return m_types.emplace(std::forward<Args>(args)...); return m_types.emplace(std::forward<Args>(args)...);
@@ -245,7 +367,7 @@ private:
std::unordered_map<function_id, pair_type> m_functionMap; std::unordered_map<function_id, pair_type> m_functionMap;
handle_container<function_h> m_functions; handle_container<function_h> m_functions;
handle_container<type_h> m_types; 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;
}; };
+475 -134
View File
@@ -1,38 +1,219 @@
#ifndef FURVM_THING_HPP #ifndef FURVM_THING_HPP
#define FURVM_THING_HPP #define FURVM_THING_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/module.hpp" #include "furvm/thing_allocator.hpp" // IWYU pragma: keep
#include "furvm/type.hpp"
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#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>
namespace furvm { namespace furvm {
struct thing_type {
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
struct array {
thing_type* type;
std::size_t size;
};
enum type { // NOLINT
S8 = 0,
S16,
S32,
S64,
U8,
U16,
U32,
U64,
Ptr,
Ref,
Array,
Count,
} type;
union value {
std::nullptr_t null = nullptr;
thing_type* typeRef;
array array;
value() = default;
value(thing_type* type)
: typeRef(type) {}
value(thing_type* type, std::size_t size)
: array({}) {
array.type = type;
array.size = size;
}
} value;
static constexpr thing_type_id INVALID_ID = std::numeric_limits<thing_type_id>::max();
thing_type_id id = INVALID_ID;
bool operator==(const thing_type& other) const {
if (type != other.type) return false;
switch (type) {
case S8:
case S16:
case S32:
case S64:
case U8:
case U16:
case U32:
case U64: 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;
}
bool operator!=(const thing_type& other) const { return !this->operator==(other); }
static bool is_primitive(enum type type) {
switch (type) {
case S8:
case S16:
case S32:
case S64:
case U8:
case U16:
case U32:
case U64: return true;
case Ptr:
case Ref:
case Array: return false;
case Count: break;
}
throw std::runtime_error("unreachable");
}
static std::size_t primitive_size(enum type type) {
switch (type) {
case thing_type::S8: return sizeof(s8);
case thing_type::S16: return sizeof(s16);
case thing_type::S32: return sizeof(s32);
case thing_type::S64: return sizeof(s64);
case thing_type::U8: return sizeof(u8);
case thing_type::U16: return sizeof(u16);
case thing_type::U32: return sizeof(u32);
case thing_type::U64: return sizeof(u64);
case Ptr:
case Ref:
case Array: return 0;
case Count: break;
}
throw std::runtime_error("unreachable");
}
};
namespace detail {
struct thing_type_hash {
std::size_t operator()(const thing_type& type) const {
std::size_t seed = std::hash<decltype(type.type)>{}(type.type);
switch (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 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
class thing_type_store {
public:
thing_type* insert(thing_type& type) {
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);
m_map[type.id] = ptr;
m_typeMap[type] = type.id;
return ptr;
}
thing_type* insert(const thing_type& type) {
if (auto it = m_typeMap.find(type); it != m_typeMap.end()) return m_map[it->second];
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:
using mod_container = std::shared_ptr<handle_container<mod_h>>; union array {
std::byte flat[];
struct {
std::size_t size;
std::byte* data;
} dynamic;
};
public: public:
/** /**
* @brief Constructs a thing. * @brief Constructs a thing.
* *
* @param type Thing type reference. * @param type Thing type.
* @param allocator Allocator for the thing's data. * @param allocator Allocator for the thing's data.
*/ */
thing(const type_ref& type, const mod_container& modules = nullptr, const allocator_type& allocator = {}) thing(const thing_type& type, const allocator_type& allocator = {})
: m_type(type), m_size(compute_size(resolve_type(type, modules))), m_modules(modules), 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);
@@ -42,21 +223,17 @@ 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), other.m_data = nullptr;
m_modules(std::move(other.m_modules)), other.m_size = 0;
m_allocator(std::move(other.m_allocator)) {
other.m_type = {};
other.m_data = nullptr;
other.m_size = 0;
} }
/** /**
@@ -64,20 +241,17 @@ public:
*/ */
thing& operator=(thing&& other) noexcept { thing& operator=(thing&& other) noexcept {
if (this == &other) return *this; if (this == &other) return *this;
m_type = other.m_type; m_type = other.m_type;
m_data = other.m_data; m_data = other.m_data;
m_modules = std::move(other.m_modules); m_allocator = std::move(other.m_allocator);
m_allocator = std::move(other.m_allocator); other.m_type.type = thing_type::Count;
other.m_type = {}; other.m_data = nullptr;
other.m_data = nullptr; other.m_size = 0;
other.m_size = 0;
return *this; return *this;
} }
thing(const thing&) = delete; thing(const thing&) = delete;
thing& operator=(const thing&) = delete; thing& operator=(const thing&) = delete;
public:
void assign_mod_container(const mod_container& modules) { m_modules = modules; }
public: public:
/** /**
* @brief Returns a clone of the thing. * @brief Returns a clone of the thing.
@@ -85,36 +259,49 @@ public:
* @return A clone of this thing. * @return A clone of this thing.
*/ */
thing clone() const { thing clone() const {
auto type = resolve_type(m_type, m_modules); thing res(m_type, m_allocator);
if (m_size == 0) { switch (m_type.type) {
return reference(); 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: 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;
} }
throw std::runtime_error("unreachable");
thing res(type, m_modules, m_allocator);
switch (type->t) {
case type_t::Primitive:
case type_t::Array: {
copy_list(*type.type, res.get<array_t>(), get<array_t>());
} break;
case type_t::Import:
default: throw std::runtime_error("unreachable");
}
return std::move(res);
} }
public: public:
/** /**
* @brief Returns the thing's type reference. * @brief Returns the thing's type.
* *
* @return The type reference. * @return The type.
*/ */
auto type() { return m_type; } constexpr thing_type type() const { return m_type; }
/** /**
* @brief Returns the thing's type reference. * @brief Returns the thing's true type.
* *
* @return The type reference. * If the thing is a reference, returns the referenced type.
*
* @return The true type.
*/ */
constexpr auto type() const { return m_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.
@@ -137,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(resolve_type(m_type, m_modules)); 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));
} }
@@ -149,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(resolve_type(m_type, m_modules)); 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:
@@ -247,79 +432,114 @@ 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 { return { m_type, m_data, m_modules, m_allocator }; } void resize(thing_type::u64 newSize) {
if (!is(thing_type::Array)) throw bad_thing_access();
if (true_type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
constexpr bool is_reference() const { return m_size == 0; } auto& array = get<union array>();
void resize(long_t newSize) {
if (m_type->t != type_t::Array) throw bad_thing_access();
if (m_type->array.size > 0) throw std::runtime_error("cannot resize a static array");
auto& array = get<array_t>();
if (newSize < 0 || newSize == array.dynamic.size) return; if (newSize < 0 || newSize == array.dynamic.size) return;
std::size_t innerSize = compute_size_na(*m_type->array.type); std::size_t innerSize = compute_size_na(*true_type().value.array.type);
std::byte* newData = new std::byte[innerSize * newSize]; std::byte* newData = new std::byte[innerSize * newSize];
std::memcpy(newData, array.dynamic.data, innerSize * std::min(array.dynamic.size, newSize)); std::memcpy(newData,
array.dynamic.data,
innerSize * std::min(static_cast<thing_type::u64>(array.dynamic.size), newSize));
array.dynamic.size = newSize; array.dynamic.size = newSize;
delete[] array.dynamic.data; delete[] array.dynamic.data;
array.dynamic.data = newData; array.dynamic.data = newData;
} }
thing at(long_t index) const { thing at(thing_type::u64 index) const {
if (m_type->t != type_t::Array) throw bad_thing_access(); if (!is(thing_type::Array)) throw bad_thing_access();
std::size_t elementSize = compute_size_na(*m_type->array.type); std::size_t elementSize = compute_size_na(*m_type.value.array.type);
if (m_type->array.size == 0) { if (m_type.value.array.size == 0) {
auto& array = get<array_t>(); auto& array = get<union array>();
if (index < 0 || index >= array.dynamic.size) throw std::out_of_range("index out of range"); if (index < 0 || index >= array.dynamic.size) throw std::out_of_range("index out of range");
return { { m_type.mod, m_type->array.type }, thing ref = { { thing_type::Ref, m_type.value.array.type }, m_allocator };
array.dynamic.data + (index * elementSize), ref.m_data = array.dynamic.data + (index * elementSize);
m_modules, return ref;
m_allocator };
} }
std::byte* data = reinterpret_cast<array_t*>(m_data)->data; std::byte* data = reinterpret_cast<array*>(m_data)->flat;
if (index < 0 || index >= m_type->array.size) throw std::out_of_range("index out of range"); if (index < 0 || index >= m_type.value.array.size) throw std::out_of_range("index out of range");
return { { m_type.mod, m_type->array.type }, data + (index * elementSize), m_modules, m_allocator }; thing ref = { { thing_type::Ref, m_type.value.array.type }, m_allocator };
ref.m_data = data + (index * elementSize);
return ref;
} }
std::size_t size() const { thing_type::u64 length() const {
if (m_type->t != type_t::Array) throw std::runtime_error("not an array"); if (!is(thing_type::Array)) throw bad_thing_access();
if (m_type->array.size == 0) return get<array_t>().dynamic.size; return true_type().value.array.size == 0 ? get<array>().dynamic.size : true_type().value.array.size;
return m_type->array.size;
} }
public:
static type_ref resolve_type(const type_ref& initType, const mod_container& modules) { template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
type_ref rsv = initType; T cast_to() const {
while (rsv->t == type_t::Import) { return visit_primitive([](auto value) { return static_cast<T>(value); });
auto mod = modules->at(initType->imp.mod); }
rsv = type_ref{ mod, mod->type_at(initType->imp.type) };
/**
* @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;
} }
return rsv; throw std::runtime_error("unreachable");
} }
private: private:
static void copy_list(const type_p& arrayType, array_t& dst, const array_t& src) { static void copy_list(const thing_type& arrayType, array& dst, const array& src) {
if (arrayType == nullptr || arrayType->t != type_t::Array || *arrayType->array.type == nullptr) if (arrayType.type != thing_type::Array || arrayType.value.array.type == nullptr)
throw std::runtime_error("invalid type"); throw std::runtime_error("invalid type");
auto innerType = *arrayType->array.type; const auto& innerType = *arrayType.value.array.type;
std::size_t elementSize = compute_size_na(innerType); std::size_t elementSize = compute_size_na(innerType);
std::byte* data = nullptr; std::byte* data = nullptr;
const std::byte* srcData = nullptr; const std::byte* srcData = nullptr;
std::size_t size = 0; std::size_t size = 0;
if (arrayType->array.size == 0) { if (arrayType.value.array.size == 0) {
size = dst.dynamic.size = src.dynamic.size; size = dst.dynamic.size = src.dynamic.size;
if (dst.dynamic.size < 0) { if (dst.dynamic.size < 0) {
dst.dynamic.data = nullptr; dst.dynamic.data = nullptr;
@@ -329,73 +549,194 @@ private:
srcData = src.dynamic.data; srcData = src.dynamic.data;
data = dst.dynamic.data = new std::byte[dst.dynamic.size]; data = dst.dynamic.data = new std::byte[dst.dynamic.size];
} else { } else {
data = dst.data; data = dst.flat;
srcData = src.data; srcData = src.flat;
size = arrayType->array.size; size = arrayType.value.array.size;
} }
switch (innerType->t) { switch (innerType.type) {
case type_t::Primitive: std::memcpy(dst.data, src.data, size); break; case thing_type::S8:
case type_t::Array: 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->array.type, copy_list(*innerType.value.array.type,
*std::launder(reinterpret_cast<array_t*>(data + (i * elementSize))), *std::launder(reinterpret_cast<array*>(data + (i * elementSize))),
*std::launder(reinterpret_cast<const array_t*>(srcData + (i * elementSize)))); *std::launder(reinterpret_cast<const array*>(srcData + (i * elementSize))));
} }
break; return;
case type_t::Import: throw std::runtime_error("unresolved type"); 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::Array: { case thing_type::S16: return sizeof(thing_type::s16);
if (type->array.size == 0) return sizeof(array_t); case thing_type::S32: return sizeof(thing_type::s32);
return compute_size_na(*type->array.type) * type->array.size; case thing_type::S64: return sizeof(thing_type::s64);
} case thing_type::U8: return sizeof(thing_type::u8);
case type_t::Import: throw std::runtime_error("unresolved type"); 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");
} }
static std::size_t compute_size_na(const type_ref& type) { return compute_size_na(*type.type); }
// 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; }
static std::size_t compute_size(const type_ref& type) { return compute_size(*type.type); }
private:
thing(const type_ref& type, std::byte* data, const mod_container& modules, const allocator_type& allocator = {})
: m_type(type), m_size(0), m_data(data), m_modules(modules), 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 {
if (m_type->t == type_t::Primitive && rhs.type()->t == type_t::Primitive) { if (thing_type::is_primitive(true_type().type) && thing_type::is_primitive(true_type().type)) {
type_ref type = m_type->primitive >= rhs.type()->primitive ? m_type : rhs.type(); static constexpr enum thing_type::type promotions[8 * 8] = {
std::size_t size = std::max(m_type->primitive, rhs.type()->primitive); // 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,
};
long_t result = op(integer(), rhs.integer()); enum thing_type::type resultType = promotions[true_type().type + (rhs.true_type().type * 8)];
thing res(type, m_modules, m_allocator); thing res = { thing_type{ resultType }, m_allocator };
switch (size) { switch (resultType) {
case sizeof(byte_t): res.get<byte_t>() = result; break; case thing_type::S8:
case sizeof(short_t): res.get<short_t>() = result; break; res.get<thing_type::s8>() = Op{}(cast_to<thing_type::s8>(), rhs.cast_to<thing_type::s8>());
case sizeof(int_t): res.get<int_t>() = result; break; return res;
case sizeof(long_t): res.get<long_t>() = result; break; case thing_type::S16:
default: throw std::runtime_error("unreachable"); res.get<thing_type::s16>() = Op{}(cast_to<thing_type::s16>(), rhs.cast_to<thing_type::s16>());
return res;
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_ref m_type; thing_type m_type;
std::size_t m_size; std::size_t m_size;
std::byte* m_data; std::byte* m_data;
mod_container m_modules;
allocator_type m_allocator; allocator_type m_allocator;
}; };
+2 -1
View File
@@ -69,8 +69,9 @@ public:
[[nodiscard]] T* allocate(std::size_t count = 1) { [[nodiscard]] T* allocate(std::size_t count = 1) {
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) { for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
if (it->second != count) continue; if (it->second != count) continue;
T* data = it->first;
m_deadThings->erase(it); m_deadThings->erase(it);
return it->first; return data;
} }
return m_arena->allocate<T>(count); return m_arena->allocate<T>(count);
} }
-181
View File
@@ -1,181 +0,0 @@
#ifndef FURVM_TYPE_HPP
#define FURVM_TYPE_HPP
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp" // IWYU pragma: keep
#include <cstdint>
#include <stdexcept>
namespace furvm {
enum class type_t : std::uint32_t {
Primitive = 0,
Array,
Import,
};
using primitive_type = std::uint64_t;
/**
* @brief Array type.
*/
struct array_type {
type_h type; /**< Type of the array's elements. */
/**
* @brief Size of the array.
*
* Size of the array. If size is equal to zero, then the array becomes dynamic.
*/
std::size_t size;
bool operator==(const array_type& rhs) const { return type == rhs.type && size == rhs.size; }
bool operator!=(const array_type& rhs) const { return !this->operator==(rhs); }
};
struct import_type {
mod_id mod;
type_id type;
bool operator==(const import_type& rhs) const { return mod == rhs.mod && type == rhs.type; }
bool operator!=(const import_type& rhs) const { return !this->operator==(rhs); }
};
struct type {
type_t t;
union {
primitive_type primitive{};
array_type array;
import_type imp;
};
type(type_t type)
: t(type) {}
type(primitive_type primitive)
: t(type_t::Primitive), primitive(primitive) {}
type(const type_h& type, std::size_t size = 0)
: t(type_t::Array), array(array_type{ type, size }) {}
type(const array_type& list)
: t(type_t::Array), array(list) {}
type(const import_type& imp)
: t(type_t::Import), imp(imp) {}
~type() {
switch (t) {
case type_t::Array: array.~array_type(); break;
case type_t::Import: imp.~import_type(); break;
default: break;
}
}
type(type&& other) noexcept
: t(other.t) {
switch (t) {
case type_t::Primitive: primitive = other.primitive; break;
case type_t::Array: array = std::move(other.array); break;
case type_t::Import: imp = std::move(other.imp); break;
}
}
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::Array: array = std::move(other.array); break;
case type_t::Import: imp = std::move(other.imp); break;
}
return *this;
}
type(const type& other)
: t(other.t) {
switch (t) {
case type_t::Primitive: primitive = other.primitive; break;
case type_t::Array: array = other.array; break;
case type_t::Import: imp = other.imp; 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::Array: array = other.array; break;
case type_t::Import: imp = other.imp; break;
}
return *this;
}
bool operator==(const type& rhs) const {
if (t != rhs.t) return false;
switch (t) {
case type_t::Primitive: return primitive == rhs.primitive;
case type_t::Array: return array == rhs.array;
case type_t::Import: return imp == rhs.imp;
}
throw std::runtime_error("unreachable");
}
bool operator!=(const type& rhs) const { return !this->operator==(rhs); }
};
using byte_t = std::int8_t; /**< A 1-byte integer. */
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. */
/**
* @brief Array type's data layout.
*/
union array_t {
std::byte data[]; /**< Static array's elements' data. */
struct {
long_t size; /**< Size of the array (in items). */
std::byte* data; /**< Pointer to dynamic array's elements' data array. */
} dynamic; /**< Dynamic array's info. */
};
/**
* @brief A 1-byte integer thing type.
*/
inline static type byteType = { sizeof(byte_t) }; // NOLINT
/**
* @brief A 2-byte integer thing type.
*/
inline static type shortType = { sizeof(short_t) }; // NOLINT
/**
* @brief A 4-byte integer thing type.
*/
inline static type intType = { sizeof(int_t) }; // NOLINT
/**
* @brief An 8-byte integer thing type.
*/
inline static type longType = { sizeof(long_t) }; // NOLINT
/**
* @brief Reference to a type.
*/
struct type_ref {
mod_h mod;
type_h type;
class type* operator->() { return &**type; }
const class type* operator->() const { return &**type; }
};
} // namespace furvm
#endif // FURVM_TYPE_HPP
-24
View File
@@ -1,24 +0,0 @@
#include "furvm/context.hpp"
#include "furvm/thing.hpp" // IWYU pragma: keep
#include "furvm/type.hpp"
#include <cstdint>
#include <memory>
namespace furvm {
context::context()
: m_thingAllocator(m_thingArena) {
mod core;
core.emplace_type(std::make_shared<type>(byteType));
core.emplace_type(std::make_shared<type>(shortType));
core.emplace_type(std::make_shared<type>(intType));
core.emplace_type(std::make_shared<type>(longType));
static_assert(sizeof(std::uintptr_t) == 8, "Unsupported platform");
core.emplace_type(*core.type_at(3));
emplace("core", std::move(core)).dispatch();
}
} // namespace furvm
+129 -52
View File
@@ -6,14 +6,44 @@
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/instruction.hpp" #include "furvm/instruction.hpp"
#include "furvm/thing.hpp" #include "furvm/thing.hpp"
#include "furvm/type.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) {
@@ -26,18 +56,23 @@ void executor::push_frame(const mod_h& mod, function function) {
args.reserve(signature.params.size()); args.reserve(signature.params.size());
for (const auto& param : signature.params) { for (const auto& param : signature.params) {
auto arg = pop_thing(); auto arg = pop_thing();
if (arg->type().type != param) throw std::runtime_error("function argument type mismatch"); if (arg->type() != *thing_type(mod, *param)) throw std::runtime_error("function argument type mismatch");
args.push_back(std::move(arg)); 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.native())(*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");
} }
@@ -47,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;
} }
@@ -62,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 {
@@ -84,11 +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] };
}
static type_ref get_type_ref(const mod_h& mod, type_id id) {
return { mod, mod->type_at(id) };
} }
void executor::step() { void executor::step() {
@@ -99,48 +134,84 @@ 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({ get_type_ref(m_context->at("core"), 2), m_context, m_context->thing_alloc() })->get<int_t>() = push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() })->get<thing_type::s8>() =
frame.mod->byte(frame.position++); 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; } break;
case instruction_t::Array: { case instruction_t::Array: {
type_id typeId = static_cast<type_id>(frame.mod->byte(frame.position)) | mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte(frame.position)) |
(static_cast<type_id>(frame.mod->byte(frame.position + 1)) << 8) | (static_cast<mod_type_id>(frame.mod->byte(frame.position + 1)) << 8) |
(static_cast<type_id>(frame.mod->byte(frame.position + 2)) << 16) | (static_cast<mod_type_id>(frame.mod->byte(frame.position + 2)) << 16) |
(static_cast<type_id>(frame.mod->byte(frame.position + 3)) << 24); (static_cast<mod_type_id>(frame.mod->byte(frame.position + 3)) << 24);
frame.position += 4; frame.position += 4;
auto type = furvm::thing<>::resolve_type(get_type_ref(frame.mod, typeId), m_context); const auto& type = *thing_type(frame.mod, *frame.mod->type_at(typeId));
if (*type.type == nullptr || type->t != type_t::Array || *type->array.type == nullptr) if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
throw std::runtime_error("invalid array type"); throw std::runtime_error("invalid array type");
auto array = push_thing({ type, m_context, m_context->thing_alloc() }); auto array = push_thing({ type, m_context->thing_alloc() });
if (type->array.size == 0) { if (type.value.array.size == 0) {
auto sizeThing = pop_thing(); auto sizeThing = pop_thing();
long_t size = sizeThing->integer(); std::int64_t size = sizeThing->integer();
array->resize(size); array->resize(size);
} }
push_thing(std::move(array));
} break; } break;
case instruction_t::Get: { case instruction_t::Get: {
auto index = pop_thing(); auto index = pop_thing();
auto array = pop_thing(); auto array = pop_thing();
push_thing(array->at(index->integer())); push_thing(array->at(index->integer()));
} break; } 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;
case instruction_t::Drop: { case instruction_t::Drop: {
pop_thing(); pop_thing();
} break; } break;
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();
@@ -199,30 +270,40 @@ void executor::step() {
} break; } break;
case instruction_t::Pointerof: { case instruction_t::Pointerof: {
auto thing = pop_thing(); auto thing = pop_thing();
auto ptr = push_thing({ get_type_ref(m_context->at("core"), 4), m_context, m_context->thing_alloc() }); auto ptr =
switch (furvm::thing<>::resolve_type(thing->type(), m_context)->t) { push_thing({ (struct thing_type){ thing_type::Ptr, m_context->thing_type_store().at(thing->type().id) },
case type_t::Primitive: ptr->get<std::uintptr_t>() = reinterpret_cast<std::uintptr_t>(thing->raw()); break; m_context->thing_alloc() });
case type_t::Array: ptr->get<void*>() = thing->raw();
ptr->get<std::uintptr_t>() = reinterpret_cast<std::uintptr_t>(thing->get<array_t>().data);
break;
case type_t::Import:
default: throw std::runtime_error("unreachable");
}
} break; } break;
case instruction_t::Sizeof: { case instruction_t::Sizeof: {
auto thing = pop_thing(); auto thing = pop_thing();
auto size = push_thing({ get_type_ref(m_context->at("core"), 3), m_context, m_context->thing_alloc() }); auto size = push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() });
auto type = furvm::thing<>::resolve_type(thing->type(), m_context); switch (thing->type().type) {
switch (type->t) { case thing_type::S8:
case type_t::Primitive: size->get<long_t>() = static_cast<long_t>(type->primitive); break; case thing_type::S16:
case type_t::Array: case thing_type::S32:
size->get<long_t>() = case thing_type::S64:
(type->array.size == 0) ? thing->get<array_t>().dynamic.size : static_cast<long_t>(type->array.size); 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; break;
case type_t::Import:
default: throw std::runtime_error("unreachable"); default: throw std::runtime_error("unreachable");
} }
} break; } 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);
@@ -242,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");
} }
+8
View File
@@ -98,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
+33 -18
View File
@@ -11,59 +11,74 @@
#include <sstream> #include <sstream>
static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) { static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) {
switch (thing.type()->t) { using namespace furvm;
case furvm::type_t::Primitive: {
std::cout << thing.integer(); switch (thing.true_type().type) {
} break; case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
case furvm::type_t::Array: { 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 << "{ "; std::cout << "{ ";
for (furvm::long_t i = 0; i < thing.size(); ++i) { for (thing_type::u64 i = 0; i < thing.length(); ++i) {
if (i > 0) std::cout << ", "; if (i > 0) std::cout << ", ";
print_thing(thing.at(i)); print_thing(thing.at(i));
} }
std::cout << " }\n"; std::cout << " }";
} break; break;
default: std::cerr << "{Something went wrong}"; default: std::cerr << "{Type not recognized}";
} }
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
auto context = std::make_shared<furvm::context>(); auto context = std::make_shared<furvm::context>();
#if 0 // NOLINT #if 1 // NOLINT
if (argc != 2) { if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <mod.fmod>\n"; std::cerr << "Usage: " << argv[0] << " <mod.fmod>\n";
return 1; return 1;
} }
std::ifstream file(argv[1]); std::ifstream file(argv[1]);
if (!file.is_open()) { if (!file.is_open()) {
std::cerr << "Failed to open " << argv[1] << '\n'; std::cerr << "Failed to open " << argv[1] << '\n';
return 1; return 1;
} }
furvm::mod_h mod = context->emplace("main", std::move(furvm::mod::load(file))); furvm::mod_h mod = context->emplace("main", std::move(furvm::mod::load(file)));
auto mainFunc = mod->function_at("main", furvm::function_sig{});
#else #else
static const furvm::byte s_bytecode[] = { static const furvm::byte s_bytecode[] = {
static_cast<furvm::byte>(furvm::instruction_t::Array), static_cast<furvm::byte>(furvm::instruction_t::Array),
0, 1,
0, 0,
0, 0,
0, 0,
static_cast<furvm::byte>(furvm::instruction_t::Reference), static_cast<furvm::byte>(furvm::instruction_t::Reference),
static_cast<furvm::byte>(furvm::instruction_t::Lengthof),
static_cast<furvm::byte>(furvm::instruction_t::Call), static_cast<furvm::byte>(furvm::instruction_t::Call),
1, 1,
0, 0,
static_cast<furvm::byte>(furvm::instruction_t::Drop),
static_cast<furvm::byte>(furvm::instruction_t::Return), static_cast<furvm::byte>(furvm::instruction_t::Return),
}; };
furvm::mod_h mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode)); auto mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode));
furvm::type_h intType = mod->emplace_type(std::make_shared<furvm::type>(context->at("core")->type_at(2), 10)); auto charType = mod->emplace_type(furvm::mod_type::S8);
auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0); auto arrayType = mod->emplace_type(charType.id(), 10);
mod->emplace_function(furvm::function_sig{ { intType } }, "print").dispatch(); 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 #endif
mod->set_native_function("print", [](furvm::executor& executor) { print_thing(*executor.load_thing(0)); }); 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(mod, *mainFunc); executor->push_frame(mod, *mainFunc);
+51 -31
View File
@@ -3,12 +3,10 @@
#include "furvm/detail/serialization.hpp" #include "furvm/detail/serialization.hpp"
#include "furvm/function.hpp" #include "furvm/function.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/type.hpp"
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <ios> #include <ios>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
@@ -18,25 +16,36 @@ std::ostream& mod::serialize(std::ostream& os) const {
os.write(MAGIC, sizeof(MAGIC)); os.write(MAGIC, sizeof(MAGIC));
detail::serialize(os, std::uint32_t(0)); // version detail::serialize(os, std::uint32_t(0)); // version
type_id typeCount = type_id(m_types.cend() - m_types.cbegin()); mod_type_id typeCount = mod_type_id(m_types.cend() - m_types.cbegin());
detail::serialize(os, typeCount); detail::serialize(os, typeCount);
for (type_id id = 0; id < typeCount; ++id) { for (mod_type_id id = 0; id < typeCount; ++id) {
if (!m_types.contains(id)) { if (!m_types.contains(id)) {
detail::serialize(os, std::uint8_t(0xFF)); // null type detail::serialize(os, std::uint8_t(0xFF)); // null type
continue; continue;
} }
auto type = *m_types.at(id); auto type = *m_types.at(id);
switch (type->t) { detail::serialize(os, static_cast<std::uint8_t>(type.type));
case type_t::Primitive: detail::serialize(os, type->primitive); break; switch (type.type) {
case type_t::Array: { case mod_type::S8:
detail::serialize(os, type->array.size); case mod_type::S16:
detail::serialize(os, type->array.type.id()); 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; } break;
case type_t::Import: { case mod_type::Import: {
detail::serialize(os, type->imp.mod); detail::serialize(os, type.value.imprt.modId);
detail::serialize(os, type->imp.type); detail::serialize(os, type.value.imprt.typeId);
} break; } break;
case mod_type::Count: throw std::runtime_error("unreachable");
} }
} }
@@ -59,7 +68,7 @@ std::ostream& mod::serialize(std::ostream& os) const {
} }
// Function signature // Function signature
detail::serialize(os, func->signature().params.size()); detail::serialize(os, static_cast<std::uint32_t>(func->signature().params.size()));
for (std::uint32_t i = 0; i < func->signature().params.size(); ++i) for (std::uint32_t i = 0; i < func->signature().params.size(); ++i)
detail::serialize(os, func->signature().params[i].id()); detail::serialize(os, func->signature().params[i].id());
@@ -93,32 +102,43 @@ mod mod::load(std::istream& is) {
mod mod; mod mod;
type_id typeCount = 0; mod_type_id typeCount = 0;
detail::load(is, typeCount); detail::load(is, typeCount);
for (type_id id = 0; id < typeCount; ++id) { for (mod_type_id id = 0; id < typeCount; ++id) {
std::uint8_t type = 0; std::uint8_t type = 0;
detail::load(is, type); detail::load(is, type);
if (type == 0xFF) continue; if (type == 0xFF) continue;
switch ((type_t)type) { switch ((enum mod_type::type)type) {
case type_t::Primitive: { case mod_type::S8:
primitive_type primitive = 0; case mod_type::S16:
detail::load(is, primitive); case mod_type::S32:
mod.emplace_type(id, std::make_shared<class type>(primitive)).dispatch(); 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; } break;
case type_t::Array: { 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; std::size_t size = 0;
detail::load(is, size); detail::load(is, size);
type_id typeId = 0; mod.emplace_type(id, typeId, size).dispatch();
detail::load(is, typeId);
mod.emplace_type(id, std::make_shared<class type>(mod.type_at(typeId), size)).dispatch();
} break; } break;
case type_t::Import: { case mod_type::Import: {
std::string modName; std::string modName;
detail::load(is, modName); detail::load(is, modName);
type_id typeId = 0; mod_type_id typeId = 0;
detail::load(is, typeId); detail::load(is, typeId);
mod.emplace_type(id, std::make_shared<class type>(import_type{ std::move(modName), typeId })).dispatch(); mod.emplace_type(id, std::move(modName), typeId).dispatch();
} break; } break;
default: throw std::runtime_error("unknown type type"); default: throw std::runtime_error("unknown type type");
} }
@@ -149,17 +169,17 @@ mod mod::load(std::istream& is) {
decltype(std::declval<function>().position()) offset = 0; decltype(std::declval<function>().position()) offset = 0;
detail::load(is, offset); detail::load(is, offset);
if (name.empty()) if (name.empty())
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
else
mod.emplace_function(id, std::move(signature), offset).dispatch(); mod.emplace_function(id, std::move(signature), offset).dispatch();
else
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
} break; } break;
case function_t::Native: { case function_t::Native: {
std::string native; std::string native;
detail::load(is, native); detail::load(is, native);
if (name.empty()) if (name.empty())
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
else
mod.emplace_function(id, std::move(signature), std::move(native)).dispatch(); 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; } break;
case function_t::Import: { case function_t::Import: {
std::string modName; std::string modName;
+26 -1
View File
@@ -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