Compare commits

..

51 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
CHatingPython abbc1714c1 refactor(furvm): enforce argument types 2026-07-09 15:02:37 +02:00
CHatingPython b7da2f3d32 feat: add missing furlang/utility/hash.hpp
I forgot to stage it from untracked lol
2026-07-08 00:20:52 +02:00
CHatingPython d49b4f5bc7 refactor: improve functions
Closes: #49
2026-07-08 00:19:37 +02:00
CHatingPython 7de645d323 refactor: replace type_p with type_ref in thing
Damn, this was such a great call.

Closes: #46
2026-07-07 19:29:30 +02:00
CHatingPython 2519560451 refactor: remove name from function
Closes: #47
2026-07-06 22:06:53 +02:00
CHatingPython 7c74850aff feat: introduce array and get instruction 2026-07-06 10:53:48 +02:00
CHatingPython 81a439b2ad refactor: add size argument to array type
Closes: #43
2026-07-05 23:15:40 +02:00
CHatingPython b913fc0c8b refactor: rename list to array
Closes: #42
2026-07-05 22:41:53 +02:00
CHatingPython ee9eadeea8 feat: introduce sizeof instruction
Closes: #34
2026-07-05 22:33:15 +02:00
CHatingPython 642d57622a feat: introduce pointerof operation
Introduce pointerof operation and do some side-quests along the way.

Refs: #34
Closes: #17
2026-07-05 14:54:16 +02:00
CHatingPython 9690ac3617 feat: add core module to context
Closes: #45
2026-07-05 13:31:15 +02:00
CHatingPython f89f643870 feat: implement module loading
I couldn't care less to commit all the changes separately after fight
with this stupid codebase.
Also I am closing the issue since serializing contexts meant doing
something like jars which I don't want to do right now. I'll reopen the
issue though for sure clueless.

Closes: #23
2026-07-04 18:41:06 +02:00
CHatingPython 100c542fe1 refactor: serialize types in module
Refs: #23
2026-07-04 16:15:40 +02:00
CHatingPython cf284592fd fix: fix handle's copy constructors 2026-07-04 16:14:46 +02:00
CHatingPython f5ebc767ab fix: fix module's function serialization
Refs: #23
2026-07-04 13:20:08 +02:00
CHatingPython 484e16963f refactor: move thing allocator to a separate file
Closes: #44
2026-07-03 19:51:09 +02:00
CHatingPython 575054b75a feat: introduce import type
Closes: #39
2026-07-03 19:43:18 +02:00
CHatingPython a7ab214ce3 refactor: make context inherit module handle container
Refs: #39
2026-07-03 19:41:28 +02:00
CHatingPython e087c11008 feat: add types to module
Refs: #39
2026-07-03 19:41:27 +02:00
CHatingPython 2164ab0d97 refactor: move type to separate file 2026-07-03 19:41:10 +02:00
CHatingPython 11b05b8c18 fix: fix handle class
Fix handle class for headers without reference counting.
2026-07-03 18:42:06 +02:00
CHatingPython 6ec4710eaa feat: introduce reference instruction 2026-07-01 21:58:33 +02:00
CHatingPython 09252c3ec3 feat: introduce list type
Closes: #33
2026-07-01 21:39:59 +02:00
CHatingPython 3d461048cc feat: introduce reference thing type
Closes: #32
2026-07-01 16:56:57 +02:00
CHatingPython 482ab859ed refactor: rename thing_type to type
Rename thing_type to type and use shared pointer instead of value in
thing.
2026-07-01 16:25:46 +02:00
CHatingPython 42f020977e feat: introduce more integer thing types
Closes: #31
2026-06-29 20:43:00 +02:00
CHatingPython 6ab9254746 refactor: improve thing types
Change thing_t enumeration to thing_type structure.

Closes: #30
2026-06-29 20:36:42 +02:00
CHatingPython 8466902281 feat(IR): improve IR functions
Add access specifiers and parameter counts to furlang's IR functions.
2026-06-28 13:21:13 +02:00
CHatingPython 7b609f87c9 feat(furc): introduce function call expression and call instruction 2026-06-28 13:21:13 +02:00
CHatingPython 0bcbe1d7d1 feat(furc): add access specifiers to functions
Introduce public and private access specifiers to functions.

Closes: #26
2026-06-28 13:21:05 +02:00
CHatingPython 8702298cd2 refactor(furvm): improve functions
Define native functions inside the module instead of the function
itself (native functions hold only the key to the definition), introduce
private functions, add parameter count to function and fix some bugs.

Closes: #20
Closes: #25
2026-06-28 12:37:36 +02:00
CHatingPython 55932f6113 feat(furvm): add iterator functions to handle container 2026-06-28 12:36:31 +02:00
CHatingPython a3db4ef1e1 feat(furvm): add type function to thing 2026-06-28 12:35:46 +02:00
CHatingPython 079f6326a4 Merge pull request 'furc' (#24) from furc into master
Reviewed-on: #24
2026-06-24 16:29:25 +00:00
47 changed files with 3549 additions and 596 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: "*"
+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)
+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;
}
+30 -4
View File
@@ -26,6 +26,16 @@ private:
std::string m_name; std::string m_name;
}; };
enum class declaration_access_t {
Implicit = 0, /**< Implicit access. */
Public, /**< Public access. */
Private, /**< Private access. */
};
static inline bool same_access(declaration_access_t lhs, declaration_access_t rhs) {
return lhs == declaration_access_t::Implicit || lhs == rhs;
}
/** /**
* @brief Declaration node type. * @brief Declaration node type.
*/ */
@@ -43,9 +53,10 @@ public:
* @brief Construct a new declaration AST node. * @brief Construct a new declaration AST node.
* *
* @param location Node location. * @param location Node location.
* @param access Declaration access.
*/ */
declaration_node(struct location location) declaration_node(struct location location, declaration_access_t access)
: abstract_node(location) {} : abstract_node(location), p_access(access) {}
public: public:
/** /**
* @brief Returns this node's category. * @brief Returns this node's category.
@@ -67,8 +78,17 @@ public:
* @return The declaration type. * @return The declaration type.
*/ */
virtual declaration_node_t declaration_type() const = 0; virtual declaration_node_t declaration_type() const = 0;
/**
* @brief Returns the declaration's access.
*
* @return Access.
*/
declaration_access_t access() const { return p_access; }
protected: protected:
bool equal(const node& rhs) const override; bool equal(const node& rhs) const override;
protected:
declaration_access_t p_access;
}; };
/** /**
@@ -99,11 +119,12 @@ public:
*/ */
template <typename T, typename ParamsFwd> template <typename T, typename ParamsFwd>
function_declaration_node(struct location location, function_declaration_node(struct location location,
declaration_access_t access,
T&& name, T&& name,
std::optional<type>&& returnType, std::optional<type>&& returnType,
ParamsFwd&& params, ParamsFwd&& params,
function_declaration_node_t type = function_declaration_node_t::Normal) function_declaration_node_t type = function_declaration_node_t::Normal)
: declaration_node(location), : declaration_node(location, access),
p_name(std::forward<T>(name)), p_name(std::forward<T>(name)),
p_returnType(std::move(returnType)), p_returnType(std::move(returnType)),
p_params(std::forward<ParamsFwd>(params)), p_params(std::forward<ParamsFwd>(params)),
@@ -186,11 +207,16 @@ public:
*/ */
template <typename T, typename ParamsFwd> template <typename T, typename ParamsFwd>
function_definition_node(struct location location, function_definition_node(struct location location,
declaration_access_t access,
T&& name, T&& name,
std::optional<class type>&& type, std::optional<class type>&& type,
ParamsFwd&& params, ParamsFwd&& params,
body&& body) body&& body)
: function_declaration_node(location, std::forward<T>(name), std::move(type), std::forward<ParamsFwd>(params)), : function_declaration_node(location,
access,
std::forward<T>(name),
std::move(type),
std::forward<ParamsFwd>(params)),
m_body(std::move(body)) {} m_body(std::move(body)) {}
public: public:
/** /**
+53
View File
@@ -4,6 +4,8 @@
#include "furc/ast/node.hpp" #include "furc/ast/node.hpp"
#include "furc/ast/statement.hpp" #include "furc/ast/statement.hpp"
#include <vector>
namespace furc { namespace furc {
namespace ast { namespace ast {
@@ -16,6 +18,7 @@ enum class expression_node_t {
Unaryop, /**< Unary operation expression */ Unaryop, /**< Unary operation expression */
Binop, /**< Binary operation expression */ Binop, /**< Binary operation expression */
VarAssign, /**< Variable assignment expression */ VarAssign, /**< Variable assignment expression */
FuncCall, /**< Function call expression. */
}; };
/** /**
@@ -110,6 +113,8 @@ enum class unaryop_expression_node_t {
PostfixIncrement, /**< Postfix increment */ PostfixIncrement, /**< Postfix increment */
PrefixDecrement, /**< Prefix decrement */ PrefixDecrement, /**< Prefix decrement */
PostfixDecrement, /**< Postfix decrement */ PostfixDecrement, /**< Postfix decrement */
Pointerof, /**< Pointerof */
Sizeof, /**< Sizeof */
}; };
/** /**
@@ -350,6 +355,54 @@ private:
expression_node_p m_rhs; expression_node_p m_rhs;
}; };
/**
* @brief Function call expression AST node.
*/
class function_call_expression_node final : public expression_node {
public:
/**
* @brief Construct a new function call expression AST node.
*
* @param location Node location.
* @param func Left-hand-side expression.
* @param args Function arguments.
*/
function_call_expression_node(struct location location,
expression_node_p&& func,
std::vector<expression_node_p>&& args)
: expression_node(location), m_func(std::move(func)), m_args(std::move(args)) {}
/**
* @brief Returns this node's left-hand-side expression.
*
* @return The left-hand-side expression.
*/
const expression_node_p& func() const { return m_func; }
/**
* @brief Returns this node's argument expressions.
*
* @return The argument expressions.
*/
const std::vector<expression_node_p>& args() const { return m_args; }
public:
/**
* @brief Returns this node's expression type.
*
* @return expression_node_t::FuncCall.
*/
expression_node_t expression_type() const override { return expression_node_t::FuncCall; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
expression_node_p m_func;
std::vector<expression_node_p> m_args;
};
} // namespace ast } // namespace ast
} // namespace furc } // namespace furc
+8
View File
@@ -151,6 +151,14 @@ using var_assign_expression_node_p =
using var_assign_expression_node_r = using var_assign_expression_node_r =
node_r<var_assign_expression_node>; /**< Alias for var_assign_expression_node result */ node_r<var_assign_expression_node>; /**< Alias for var_assign_expression_node result */
class function_call_expression_node;
using function_call_expression_node_p =
node_p<function_call_expression_node>; /**< Alias for a shared pointer to function_call_expression_node. */
using function_call_expression_node_r =
node_r<function_call_expression_node>; /**< Alias for function_call_expression_node result. */
/** /**
* @brief List of statements. * @brief List of statements.
*/ */
+8
View File
@@ -82,6 +82,14 @@ public:
*/ */
virtual void visit(const var_assign_expression_node& node) {} virtual void visit(const var_assign_expression_node& node) {}
/**
* @brief Visit a function_call_expression_node.
* @see function_call_expression_node
*
* @param node Node.
*/
virtual void visit(const function_call_expression_node& node) {}
/** /**
* @brief Visit a function_declaration_node. * @brief Visit a function_declaration_node.
* @see function_declaration_node * @see function_declaration_node
+3 -1
View File
@@ -25,6 +25,7 @@ public:
furlang::ir::mod&& move_module() { return std::move(m_module); } furlang::ir::mod&& move_module() { return std::move(m_module); }
public: public:
void visit(const ast::function_definition_node& funcDef) override; void visit(const ast::function_definition_node& funcDef) override;
void visit(const ast::function_declaration_node& funcDecl) override;
void visit(const ast::return_statement_node& returnStmt) override; void visit(const ast::return_statement_node& returnStmt) override;
void visit(const ast::if_statement_node& node) override; void visit(const ast::if_statement_node& node) override;
void visit(const ast::while_statement_node& node) override; void visit(const ast::while_statement_node& node) override;
@@ -35,6 +36,7 @@ public:
void visit(const ast::unary_op_expression_node& node) override; void visit(const ast::unary_op_expression_node& node) override;
void visit(const ast::binary_op_expression_node& node) override; void visit(const ast::binary_op_expression_node& node) override;
void visit(const ast::var_assign_expression_node& node) override; void visit(const ast::var_assign_expression_node& node) override;
void visit(const ast::function_call_expression_node& node) override;
private: private:
template <typename T, typename... Args> template <typename T, typename... Args>
void push(Args&&... args) { void push(Args&&... args) {
@@ -45,7 +47,7 @@ private:
furlang::ir::block_index push_block(bool validate = true); furlang::ir::block_index push_block(bool validate = true);
private: private:
furlang::ir::mod m_module; furlang::ir::mod m_module;
std::unique_ptr<furlang::ir::function> m_currentFunction; std::unique_ptr<furlang::ir::function> m_currentFunction;
std::shared_ptr<furlang::ir::block> m_currentBlock; std::shared_ptr<furlang::ir::block> m_currentBlock;
ir_register m_registerCounter = 0; ir_register m_registerCounter = 0;
+20 -8
View File
@@ -147,14 +147,18 @@ static inline std::string operator+(const std::string& str, token_t type) {
* @brief Keyword token. * @brief Keyword token.
*/ */
enum class keyword_token { enum class keyword_token {
None, /**< None */ None, /**< None */
Func, /**< `func` */ Func, /**< `func` */
Return, /**< `return` */ Return, /**< `return` */
If, /**< `if` */ If, /**< `if` */
Else, /**< `else` */ Else, /**< `else` */
While, /**< `while` */ While, /**< `while` */
Import, /**< `import` */ Import, /**< `import` */
Native, /**< `native` */ Native, /**< `native` */
Public, /**< `public` */
Private, /**< `private` */
Pointerof, /**< `pointerof` */
Sizeof, /**< `sizeof` */
Int32, /**< `int32` */ Int32, /**< `int32` */
}; };
@@ -169,6 +173,10 @@ static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword)
case keyword_token::While: return os << "while"; case keyword_token::While: return os << "while";
case keyword_token::Import: return os << "import"; case keyword_token::Import: return os << "import";
case keyword_token::Native: return os << "native"; case keyword_token::Native: return os << "native";
case keyword_token::Public: return os << "public";
case keyword_token::Private: return os << "private";
case keyword_token::Pointerof: return os << "pointerof";
case keyword_token::Sizeof: return os << "sizeof";
case keyword_token::Int32: return os << "int32"; case keyword_token::Int32: return os << "int32";
} }
return os; return os;
@@ -184,6 +192,10 @@ static inline std::string operator+(const std::string& str, keyword_token keywor
case keyword_token::While: return str + "while"; case keyword_token::While: return str + "while";
case keyword_token::Import: return str + "import"; case keyword_token::Import: return str + "import";
case keyword_token::Native: return str + "native"; case keyword_token::Native: return str + "native";
case keyword_token::Public: return str + "public";
case keyword_token::Private: return str + "private";
case keyword_token::Pointerof: return str + "pointerof";
case keyword_token::Sizeof: return str + "sizeof";
case keyword_token::Int32: return str + "int32"; case keyword_token::Int32: return str + "int32";
} }
return str; return str;
+24
View File
@@ -37,6 +37,8 @@ std::ostream& operator<<(std::ostream& os, unaryop_expression_node_t type) {
case unaryop_expression_node_t::PostfixIncrement: return os << "++"; case unaryop_expression_node_t::PostfixIncrement: return os << "++";
case unaryop_expression_node_t::PrefixDecrement: case unaryop_expression_node_t::PrefixDecrement:
case unaryop_expression_node_t::PostfixDecrement: return os << "--"; case unaryop_expression_node_t::PostfixDecrement: return os << "--";
case unaryop_expression_node_t::Pointerof: return os << "pointerof";
case unaryop_expression_node_t::Sizeof: return os << "sizeof";
} }
return os; return os;
} }
@@ -54,6 +56,8 @@ std::ostream& unary_op_expression_node::print(std::ostream& os) const {
case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')'; case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')';
case unaryop_expression_node_t::PostfixIncrement: case unaryop_expression_node_t::PostfixIncrement:
case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')'; case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')';
case unaryop_expression_node_t::Pointerof: return os << "pointerof " << *m_node;
case unaryop_expression_node_t::Sizeof: return os << "sizeof " << *m_node;
} }
return os; return os;
} }
@@ -108,6 +112,26 @@ bool var_assign_expression_node::equal(const node& rhsNode) const {
return expression_node::equal(rhsNode) && m_compound == rhs.m_compound && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs; return expression_node::equal(rhsNode) && m_compound == rhs.m_compound && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs;
} }
void function_call_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& function_call_expression_node::print(std::ostream& os) const {
os << *m_func << '(';
bool first = true;
for (const auto& arg : m_args) {
if (!first) os << ", ";
first = false;
os << *arg;
}
return os << ')';
}
bool function_call_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const function_call_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_func == rhs.m_func && m_args == rhs.m_args;
}
bool declaration_node::equal(const node& rhs) const { bool declaration_node::equal(const node& rhs) const {
return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type(); return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type();
} }
+89 -36
View File
@@ -1,6 +1,9 @@
#include "furc/back/furvm.hpp" #include "furc/back/furvm.hpp"
#include "furlang/ir/function.hpp"
#include "furlang/ir/instruction.hpp" #include "furlang/ir/instruction.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include <furvm/instruction.hpp> #include <furvm/instruction.hpp>
#include <stdexcept> #include <stdexcept>
@@ -18,40 +21,64 @@ furvm::mod furvm_generator::generate(furlang::ir::mod& mod) {
} }
void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) { void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) {
auto func = mod.emplace_function(function.name(), mod.bytecode().size()); furvm::function_sig signature; // TODO: Complete
func.dispatch();
function_context ctx; switch (function.type()) {
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) { case furlang::ir::function_t::Normal: {
if (auto it = ctx.incompleteJumps.find(idx); it != ctx.incompleteJumps.end()) { if (function.access() == furlang::ir::function_access_t::Public)
for (std::size_t offset : it->second) { mod.emplace_function(function.name(), std::move(signature), mod.bytecode().size()).dispatch();
mod.bytecode()[offset] = mod.bytecode().size() - offset - 1; else
mod.emplace_function(std::move(signature), mod.bytecode().size()).dispatch();
function_context ctx;
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
if (auto it = ctx.incompleteJumps.find(idx); it != ctx.incompleteJumps.end()) {
for (std::size_t offset : it->second) {
mod.bytecode()[offset] = mod.bytecode().size() - offset - 1;
}
ctx.incompleteJumps.erase(it);
} }
ctx.incompleteJumps.erase(it);
}
ctx.blockOffsets[idx] = mod.bytecode().size(); ctx.blockOffsets[idx] = mod.bytecode().size();
for (const auto& instr : function.blocks()[idx]->instructions()) for (const auto& instr : function.blocks()[idx]->instructions())
generate_instruction(mod, ctx, *instr); generate_instruction(mod, ctx, *instr);
generate_instruction(mod, ctx, *function.blocks()[idx]->exit()); generate_instruction(mod, ctx, *function.blocks()[idx]->exit());
}
} break;
case furlang::ir::function_t::Import: {
throw std::runtime_error("unimplemented");
// mod.emplace_function_private(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
} break;
case furlang::ir::function_t::Native: {
if (function.access() == furlang::ir::function_access_t::Public)
mod.emplace_function(function.name(), std::move(signature), function.name()).dispatch();
else
mod.emplace_function(std::move(signature), function.name()).dispatch();
} break;
} }
} }
static inline furvm::instruction_t binary_op_type(furlang::ir::binary_op_instruction_t type) { static inline furvm::instruction_t op_type(furlang::ir::instruction_t type) {
switch (type) { switch (type) {
case furlang::ir::binary_op_instruction_t::Add: return furvm::instruction_t::Add; // Unary
case furlang::ir::binary_op_instruction_t::Sub: return furvm::instruction_t::Sub; case furlang::ir::instruction_t::Pointerof: return furvm::instruction_t::Pointerof;
case furlang::ir::binary_op_instruction_t::Mul: return furvm::instruction_t::Mul; case furlang::ir::instruction_t::Sizeof: return furvm::instruction_t::Sizeof;
case furlang::ir::binary_op_instruction_t::Div: return furvm::instruction_t::Div;
case furlang::ir::binary_op_instruction_t::Mod: return furvm::instruction_t::Mod; // Binary
case furlang::ir::binary_op_instruction_t::Eq: return furvm::instruction_t::Equals; case furlang::ir::instruction_t::Add: return furvm::instruction_t::Add;
case furlang::ir::binary_op_instruction_t::NotEq: return furvm::instruction_t::NotEquals; case furlang::ir::instruction_t::Sub: return furvm::instruction_t::Sub;
case furlang::ir::binary_op_instruction_t::LessThan: return furvm::instruction_t::LessThan; case furlang::ir::instruction_t::Mul: return furvm::instruction_t::Mul;
case furlang::ir::binary_op_instruction_t::GreaterThan: return furvm::instruction_t::GreaterThan; case furlang::ir::instruction_t::Div: return furvm::instruction_t::Div;
case furlang::ir::binary_op_instruction_t::LessEq: return furvm::instruction_t::LessEqual; case furlang::ir::instruction_t::Mod: return furvm::instruction_t::Mod;
case furlang::ir::binary_op_instruction_t::GreaterEq: return furvm::instruction_t::GreaterEqual; case furlang::ir::instruction_t::Eq: return furvm::instruction_t::Equals;
case furlang::ir::instruction_t::NotEq: return furvm::instruction_t::NotEquals;
case furlang::ir::instruction_t::LessThan: return furvm::instruction_t::LessThan;
case furlang::ir::instruction_t::GreaterThan: return furvm::instruction_t::GreaterThan;
case furlang::ir::instruction_t::LessEq: return furvm::instruction_t::LessEqual;
case furlang::ir::instruction_t::GreaterEq: return furvm::instruction_t::GreaterEqual;
default: throw std::runtime_error("unreachable");
} }
throw std::runtime_error("unreachable");
} }
void furvm_generator::generate_instruction(furvm::mod& mod, void furvm_generator::generate_instruction(furvm::mod& mod,
@@ -71,9 +98,31 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
mod.bytecode().push_back((var >> 8) & 0xFF); mod.bytecode().push_back((var >> 8) & 0xFF);
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed"); static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
} break; } break;
case furlang::ir::instruction_t::BinaryOp: { case furlang::ir::instruction_t::Add:
const auto& op = dynamic_cast<const furlang::ir::binary_op_instruction&>(instr); case furlang::ir::instruction_t::Sub:
mod.bytecode().push_back(static_cast<furvm::byte>(binary_op_type(op.op_type()))); case furlang::ir::instruction_t::Mul:
case furlang::ir::instruction_t::Div:
case furlang::ir::instruction_t::Mod:
case furlang::ir::instruction_t::Eq:
case furlang::ir::instruction_t::NotEq:
case furlang::ir::instruction_t::LessThan:
case furlang::ir::instruction_t::GreaterThan:
case furlang::ir::instruction_t::LessEq:
case furlang::ir::instruction_t::GreaterEq: {
mod.bytecode().push_back(static_cast<furvm::byte>(op_type(instr.type())));
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
}
auto var = ctx.variables[instr.destination().reg()];
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Store));
mod.bytecode().push_back((var >> 0) & 0xFF);
mod.bytecode().push_back((var >> 8) & 0xFF);
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
} break;
case furlang::ir::instruction_t::Pointerof:
case furlang::ir::instruction_t::Sizeof: {
mod.bytecode().push_back(static_cast<furvm::byte>(op_type(instr.type())));
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) { if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
ctx.variables[instr.destination().reg()] = ctx.variableCounter++; ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
@@ -94,13 +143,17 @@ 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)); } break;
} else { case furlang::ir::instruction_t::Call: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::ReturnValue)); const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr);
}
// TODO: Implement a queue for unknown functions
furvm::function_id func = mod.function_at(call.name(), furvm::function_sig{}).id(); // TODO: Complete
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call));
mod.bytecode().push_back((func >> 0) & 0xFF);
mod.bytecode().push_back((func >> 8) & 0xFF);
} break; } break;
case furlang::ir::instruction_t::Call:
case furlang::ir::instruction_t::Alloca: throw std::runtime_error("unimplemented instruction"); case furlang::ir::instruction_t::Alloca: throw std::runtime_error("unimplemented instruction");
case furlang::ir::instruction_t::Phi: throw std::runtime_error("unreachable"); case furlang::ir::instruction_t::Phi: throw std::runtime_error("unreachable");
} }
@@ -117,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:
+75 -23
View File
@@ -4,10 +4,14 @@
#include "furc/ast/expression.hpp" // IWYU pragma: keep #include "furc/ast/expression.hpp" // IWYU pragma: keep
#include "furc/ast/literal.hpp" // IWYU pragma: keep #include "furc/ast/literal.hpp" // IWYU pragma: keep
#include "furc/ast/statement.hpp" // IWYU pragma: keep #include "furc/ast/statement.hpp" // IWYU pragma: keep
#include "furlang/ir/function.hpp"
#include "furlang/ir/instruction.hpp" #include "furlang/ir/instruction.hpp"
#include "furlang/ir/operand.hpp"
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <stdexcept>
#include <vector>
namespace furc::front { namespace furc::front {
@@ -16,7 +20,11 @@ namespace ir = furlang::ir;
} }
void ir_generator::visit(const ast::function_definition_node& funcDef) { void ir_generator::visit(const ast::function_definition_node& funcDef) {
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name())); furlang::ir::function_access_t access = (funcDef.access() == ast::declaration_access_t::Public)
? furlang::ir::function_access_t::Public
: furlang::ir::function_access_t::Private;
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name()), access, 0);
push_block(); push_block();
for (const auto& stmt : funcDef.body().statements) { for (const auto& stmt : funcDef.body().statements) {
@@ -28,6 +36,21 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) {
m_module.push(std::move(m_currentFunction)); m_module.push(std::move(m_currentFunction));
} }
void ir_generator::visit(const ast::function_declaration_node& funcDecl) {
if (funcDecl.type() == ast::function_declaration_node_t::Normal) return;
furlang::ir::function_t type = funcDecl.type() == ast::function_declaration_node_t::Import
? furlang::ir::function_t::Import
: furlang::ir::function_t::Native;
furlang::ir::function_access_t access = (funcDecl.access() == ast::declaration_access_t::Public)
? furlang::ir::function_access_t::Public
: furlang::ir::function_access_t::Private;
m_module.push(
std::make_unique<furlang::ir::function>(std::string(funcDecl.name()), access, funcDecl.params().size(), type));
}
void ir_generator::visit(const ast::return_statement_node& returnStmt) { void ir_generator::visit(const ast::return_statement_node& returnStmt) {
if (returnStmt.value().has_value()) { if (returnStmt.value().has_value()) {
returnStmt.value().value()->accept(*this); returnStmt.value().value()->accept(*this);
@@ -109,23 +132,36 @@ void ir_generator::visit(const ast::var_read_expression_node& node) {
} }
} }
void ir_generator::visit(const ast::unary_op_expression_node& node) { static inline furlang::ir::instruction_t unary_op_instruction_t(ast::unaryop_expression_node_t type) {
throw std::runtime_error("unimplemented"); switch (type) {
case ast::unaryop_expression_node_t::Pointerof: return furlang::ir::instruction_t::Pointerof;
case ast::unaryop_expression_node_t::Sizeof: return furlang::ir::instruction_t::Sizeof;
default: throw std::runtime_error("unimplemented");
}
} }
static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) { void ir_generator::visit(const ast::unary_op_expression_node& node) {
node.get_node()->accept(*this);
ir_register src = m_registerCounter - 1;
ir_register dst = m_registerCounter++;
push<furlang::ir::unary_instruction>(unary_op_instruction_t(node.type()),
ir::operand::new_reg(src),
ir::operand::new_reg(dst));
}
static inline furlang::ir::instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) {
switch (type) { switch (type) {
case ast::binop_expression_node_t::Add: return furlang::ir::binary_op_instruction_t::Add; case ast::binop_expression_node_t::Add: return furlang::ir::instruction_t::Add;
case ast::binop_expression_node_t::Sub: return furlang::ir::binary_op_instruction_t::Sub; case ast::binop_expression_node_t::Sub: return furlang::ir::instruction_t::Sub;
case ast::binop_expression_node_t::Mul: return furlang::ir::binary_op_instruction_t::Mul; case ast::binop_expression_node_t::Mul: return furlang::ir::instruction_t::Mul;
case ast::binop_expression_node_t::Div: return furlang::ir::binary_op_instruction_t::Div; case ast::binop_expression_node_t::Div: return furlang::ir::instruction_t::Div;
case ast::binop_expression_node_t::Mod: return furlang::ir::binary_op_instruction_t::Mod; case ast::binop_expression_node_t::Mod: return furlang::ir::instruction_t::Mod;
case ast::binop_expression_node_t::Equal: return furlang::ir::binary_op_instruction_t::Eq; case ast::binop_expression_node_t::Equal: return furlang::ir::instruction_t::Eq;
case ast::binop_expression_node_t::NotEqual: return furlang::ir::binary_op_instruction_t::NotEq; case ast::binop_expression_node_t::NotEqual: return furlang::ir::instruction_t::NotEq;
case ast::binop_expression_node_t::LessThan: return furlang::ir::binary_op_instruction_t::LessThan; case ast::binop_expression_node_t::LessThan: return furlang::ir::instruction_t::LessThan;
case ast::binop_expression_node_t::GreaterThan: return furlang::ir::binary_op_instruction_t::GreaterThan; case ast::binop_expression_node_t::GreaterThan: return furlang::ir::instruction_t::GreaterThan;
case ast::binop_expression_node_t::LessEqual: return furlang::ir::binary_op_instruction_t::LessEq; case ast::binop_expression_node_t::LessEqual: return furlang::ir::instruction_t::LessEq;
case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::binary_op_instruction_t::GreaterEq; case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::instruction_t::GreaterEq;
case ast::binop_expression_node_t::None: case ast::binop_expression_node_t::None:
default: throw std::runtime_error("unreachable"); default: throw std::runtime_error("unreachable");
} }
@@ -137,7 +173,7 @@ void ir_generator::visit(const ast::binary_op_expression_node& node) {
node.rhs()->accept(*this); node.rhs()->accept(*this);
ir_register rhs = m_registerCounter - 1; ir_register rhs = m_registerCounter - 1;
ir_register dst = m_registerCounter++; ir_register dst = m_registerCounter++;
push<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()), push<furlang::ir::binary_instruction>(binary_op_instruction_t(node.type()),
ir::operand::new_reg(lhs), ir::operand::new_reg(lhs),
ir::operand::new_reg(rhs), ir::operand::new_reg(rhs),
ir::operand::new_reg(dst)); ir::operand::new_reg(dst));
@@ -149,22 +185,38 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead); assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead);
auto lhs = std::dynamic_pointer_cast<ast::var_read_expression_node>(node.lhs()); auto lhs = std::dynamic_pointer_cast<ast::var_read_expression_node>(node.lhs());
ir_register reg = 0; ir_register reg = m_registerCounter++;
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
reg = it->second;
} else {
m_variables[lhs->get_name()] = reg = m_registerCounter++;
}
auto compound = node.compound(); auto compound = node.compound();
if (compound != ast::binop_expression_node_t::None) { if (compound != ast::binop_expression_node_t::None) {
push<ir::binary_op_instruction>(binary_op_instruction_t(compound), push<ir::binary_instruction>(binary_op_instruction_t(compound),
ir::operand::new_reg(reg), ir::operand::new_reg(reg),
ir::operand::new_reg(rhs), ir::operand::new_reg(rhs),
ir::operand::new_reg(reg)); ir::operand::new_reg(reg));
} else { } else {
push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
} }
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
push<ir::assign_instruction>(ir::operand::new_reg(reg), ir::operand::new_reg(it->second));
} else {
m_variables[lhs->get_name()] = reg;
}
}
void ir_generator::visit(const ast::function_call_expression_node& node) {
std::vector<ir::operand> args;
args.reserve(node.args().size());
for (const auto& arg : node.args()) {
arg->accept(*this);
args.push_back(ir::operand::new_reg(m_registerCounter - 1));
}
if (node.func()->expression_type() != ast::expression_node_t::VarRead)
throw std::runtime_error("invalid function call left-hand-side expression");
push<ir::call_instruction>(dynamic_cast<const ast::var_read_expression_node&>(*node.func()).get_name(),
ir::operand::new_reg(m_registerCounter++),
std::move(args));
} }
furlang::ir::block_index ir_generator::push_block(bool validate) { furlang::ir::block_index ir_generator::push_block(bool validate) {
+4
View File
@@ -102,6 +102,10 @@ token_r lexer::next_token() {
{ "while", keyword_token::While }, { "while", keyword_token::While },
{ "import", keyword_token::Import }, { "import", keyword_token::Import },
{ "native", keyword_token::Native }, { "native", keyword_token::Native },
{ "public", keyword_token::Public },
{ "private", keyword_token::Private },
{ "pointerof", keyword_token::Pointerof },
{ "sizeof", keyword_token::Sizeof },
{ "int32", keyword_token::Int32 }, { "int32", keyword_token::Int32 },
}; };
+98 -23
View File
@@ -9,7 +9,7 @@
#include "furc/front/token.hpp" #include "furc/front/token.hpp"
#include <fstream> #include <fstream>
#include <iostream> #include <optional>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@@ -54,21 +54,40 @@ ast::type_r parser::parse_type() {
ast::declaration_node_r parser::parse_declaration() { ast::declaration_node_r parser::parse_declaration() {
const auto& first = peek_token(); const auto& first = peek_token();
if (first.has_error()) return ast::declaration_node_r(ast::error{ first.error().location });
switch (first->type) { switch (first->type) {
case token_t::Keyword: { case token_t::Keyword: {
token firstToken = *first;
ast::declaration_access_t accessOverride = ast::declaration_access_t::Implicit;
switch ((*first)->keyword) {
default: break;
case keyword_token::Public:
case keyword_token::Private: {
if ((*first)->keyword == keyword_token::Public) accessOverride = ast::declaration_access_t::Public;
if ((*first)->keyword == keyword_token::Private) accessOverride = ast::declaration_access_t::Private;
auto kw = eat_token(token_t::Keyword);
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
firstToken = *kw;
} break;
}
ast::function_declaration_node_t funcDeclType{}; ast::function_declaration_node_t funcDeclType{};
auto first = next_token(); auto kw = next_token();
switch ((*first)->keyword) { if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
firstToken = *kw;
switch (firstToken->keyword) {
case keyword_token::Import: case keyword_token::Import:
case keyword_token::Native: { case keyword_token::Native: {
funcDeclType = ((*first)->keyword == keyword_token::Import) ? ast::function_declaration_node_t::Import funcDeclType = (firstToken->keyword == keyword_token::Import) ? ast::function_declaration_node_t::Import
: ast::function_declaration_node_t::Native; : ast::function_declaration_node_t::Native;
first = eat_token(token_t::Keyword); auto kw = eat_token(token_t::Keyword);
if (first.has_error()) return ast::declaration_node_r(ast::error{ first.error().location }); if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
if (first->value.keyword != keyword_token::Func) firstToken = *kw;
return ast::declaration_node_r(ast::error{ first->location }); if (firstToken.value.keyword != keyword_token::Func)
return ast::declaration_node_r(ast::error{ firstToken.location });
} }
case keyword_token::Func: { case keyword_token::Func: {
auto name = eat_token(token_t::Identifier); auto name = eat_token(token_t::Identifier);
@@ -106,6 +125,12 @@ ast::declaration_node_r parser::parse_declaration() {
returnType = *type; returnType = *type;
} }
auto access = (funcDeclType == ast::function_declaration_node_t::Import)
? ast::declaration_access_t::Private
: accessOverride;
if (access == ast::declaration_access_t::Implicit) access = ast::declaration_access_t::Public;
if (!ast::same_access(accessOverride, access)) return ast::declaration_node_r(ast::error{ tok->location });
const auto& peek = peek_token(); const auto& peek = peek_token();
if (peek.has_error()) return ast::declaration_node_r(ast::error{ peek.error().location }); if (peek.has_error()) return ast::declaration_node_r(ast::error{ peek.error().location });
switch (peek->type) { switch (peek->type) {
@@ -114,7 +139,8 @@ ast::declaration_node_r parser::parse_declaration() {
if (body.has_error()) return ast::declaration_node_r(ast::error{ body.error().location }); if (body.has_error()) return ast::declaration_node_r(ast::error{ body.error().location });
if (funcDeclType != ast::function_declaration_node_t::Normal) if (funcDeclType != ast::function_declaration_node_t::Normal)
return ast::declaration_node_r(ast::error{ body->begin }); return ast::declaration_node_r(ast::error{ body->begin });
return m_arena->allocate_shared<ast::function_definition_node>(first->location, return m_arena->allocate_shared<ast::function_definition_node>(firstToken.location,
access,
name->value.string, name->value.string,
std::move(returnType), std::move(returnType),
std::move(params), std::move(params),
@@ -122,7 +148,8 @@ ast::declaration_node_r parser::parse_declaration() {
} }
case token_t::Semicolon: { case token_t::Semicolon: {
m_peekBuffer.clear(); m_peekBuffer.clear();
return m_arena->allocate_shared<ast::function_declaration_node>(first->location, return m_arena->allocate_shared<ast::function_declaration_node>(firstToken.location,
access,
name->value.string, name->value.string,
std::move(returnType), std::move(returnType),
std::move(params), std::move(params),
@@ -131,7 +158,7 @@ ast::declaration_node_r parser::parse_declaration() {
default: return ast::declaration_node_r(ast::error{ tok->location }); default: return ast::declaration_node_r(ast::error{ tok->location });
} }
} }
default: return ast::declaration_node_r(ast::error{ first->location }); default: return ast::declaration_node_r(ast::error{ firstToken.location });
} }
} }
case token_t::None: case token_t::None:
@@ -287,27 +314,43 @@ struct unaryop_info {
std::uint32_t precedence; std::uint32_t precedence;
}; };
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) { static inline std::optional<unaryop_info> get_unaryop_info(const token_r& token) {
static std::unordered_map<token_t, unaryop_info> s_prefixes = { static std::unordered_map<token_t, unaryop_info> s_prefixes = {
{ token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 3 } }, { token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 2 } },
{ token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 3 } }, { token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 2 } },
{ token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } }, { token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } },
{ token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } }, { token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } },
}; };
static std::unordered_map<keyword_token, unaryop_info> s_keywords = {
{ keyword_token::Pointerof, unaryop_info{ ast::unaryop_expression_node_t::Pointerof, 2 } },
{ keyword_token::Sizeof, unaryop_info{ ast::unaryop_expression_node_t::Sizeof, 2 } },
};
if (token->type == token_t::Keyword) {
auto it = s_keywords.find(token->value.keyword);
if (it == s_keywords.end()) return {};
return it->second;
}
auto it = s_prefixes.find(token->type);
if (it == s_prefixes.end()) return {};
return it->second;
}
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) {
std::shared_ptr<ast::unary_op_expression_node> result; std::shared_ptr<ast::unary_op_expression_node> result;
while (true) { while (true) {
auto it = s_prefixes.find(peek_token()->type); auto opt = get_unaryop_info(peek_token());
if (it == s_prefixes.end()) break; if (!opt.has_value()) break;
auto current = it->second; unaryop_info current = opt.value();
if (current.precedence >= precedence) break; if (current.precedence >= precedence) break;
auto token = next_token(); auto token = next_token();
ast::expression_node_p expression; ast::expression_node_p expression;
auto nextIt = s_prefixes.find(peek_token()->type); opt = get_unaryop_info(peek_token());
if (nextIt != s_prefixes.end()) { if (opt.has_value()) {
auto next = nextIt->second; auto next = opt.value();
auto expr = parse_expression_unary(current.precedence + 1); auto expr = parse_expression_unary(current.precedence + 1);
if (expr.has_error()) { if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location }); return ast::expression_node_r(ast::error{ expr.error().location });
@@ -340,6 +383,7 @@ enum class rhsop_info_t {
Unaryop, Unaryop,
Binop, Binop,
Assignment, Assignment,
FuncCall,
}; };
struct rhsop_info { struct rhsop_info {
@@ -352,6 +396,8 @@ struct rhsop_info {
ast::binop_expression_node_t assignment; ast::binop_expression_node_t assignment;
}; };
bool has_rhs() const { return type == rhsop_info_t::Binop || type == rhsop_info_t::Assignment; }
static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) { static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) {
rhsop_info info{}; rhsop_info info{};
info.type = rhsop_info_t::Unaryop; info.type = rhsop_info_t::Unaryop;
@@ -380,6 +426,14 @@ struct rhsop_info {
info.assignment = compound; info.assignment = compound;
return info; return info;
} }
static rhsop_info create_function_call() {
rhsop_info info{};
info.type = rhsop_info_t::FuncCall;
info.precedence = 1;
info.associativity = associativity::Left;
return info;
}
}; };
ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& init, std::uint32_t precedence) { ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& init, std::uint32_t precedence) {
@@ -404,6 +458,7 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
{ token_t::GreaterThan, rhsop_info::create(ast::binop_expression_node_t::GreaterThan, 9, associativity::Left) }, { token_t::GreaterThan, rhsop_info::create(ast::binop_expression_node_t::GreaterThan, 9, associativity::Left) },
{ token_t::LessEq, rhsop_info::create(ast::binop_expression_node_t::LessEqual, 9, associativity::Left) }, { token_t::LessEq, rhsop_info::create(ast::binop_expression_node_t::LessEqual, 9, associativity::Left) },
{ token_t::GreaterEq, rhsop_info::create(ast::binop_expression_node_t::GreaterEqual, 9, associativity::Left) }, { token_t::GreaterEq, rhsop_info::create(ast::binop_expression_node_t::GreaterEqual, 9, associativity::Left) },
{ token_t::LParen, rhsop_info::create_function_call() },
}; };
ast::expression_node_p lhs = std::move(init); ast::expression_node_p lhs = std::move(init);
@@ -415,13 +470,28 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
if (current.precedence >= precedence) return lhs; if (current.precedence >= precedence) return lhs;
auto opToken = next_token(); auto opToken = next_token();
ast::expression_node_p rhs; ast::expression_node_p rhs;
if (current.type != rhsop_info_t::Unaryop) { std::vector<ast::expression_node_p> params;
if (current.has_rhs()) {
auto expr = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative auto expr = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative
if (expr.has_error()) { if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location }); return ast::expression_node_r(ast::error{ expr.error().location });
} }
rhs = std::move(expr.value()); rhs = std::move(expr.value());
} else if (current.type == rhsop_info_t::FuncCall && peek_token().has_value()) {
if (peek_token()->type != token_t::RParen) {
while (true) {
auto expr = parse_expression_unary(16);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
params.emplace_back(std::move(expr.value()));
if (eat_token(token_t::Comma).has_error()) break;
}
}
auto enclosing = eat_token(token_t::RParen);
if (enclosing.has_error()) return ast::expression_node_r(ast::error{ enclosing.error().location });
} }
auto nextIt = s_rhsops.find(peek_token()->type); auto nextIt = s_rhsops.find(peek_token()->type);
@@ -458,6 +528,11 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
std::move(lhs), std::move(lhs),
std::move(rhs)); std::move(rhs));
break; break;
case rhsop_info_t::FuncCall:
lhs = m_arena->allocate_shared<ast::function_call_expression_node>(opToken->location,
std::move(lhs),
std::move(params));
break;
} }
} }
return lhs; return lhs;
+29 -23
View File
@@ -11,6 +11,7 @@
#include <queue> #include <queue>
#include <set> #include <set>
#include <stack> #include <stack>
#include <stdexcept>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <utility> #include <utility>
@@ -427,7 +428,17 @@ static void sccp_stage(function_context& ctx) {
case furlang::ir::instruction_t::Assign: { case furlang::ir::instruction_t::Assign: {
newLat = sccp_stage_get_lattice(latticeValues, *instr->sources().front()); newLat = sccp_stage_get_lattice(latticeValues, *instr->sources().front());
} break; } break;
case furlang::ir::instruction_t::BinaryOp: { case furlang::ir::instruction_t::Add:
case furlang::ir::instruction_t::Sub:
case furlang::ir::instruction_t::Mul:
case furlang::ir::instruction_t::Div:
case furlang::ir::instruction_t::Mod:
case furlang::ir::instruction_t::Eq:
case furlang::ir::instruction_t::NotEq:
case furlang::ir::instruction_t::LessThan:
case furlang::ir::instruction_t::GreaterThan:
case furlang::ir::instruction_t::LessEq:
case furlang::ir::instruction_t::GreaterEq: {
lattice lhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[0]); lattice lhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[0]);
lattice rhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[1]); lattice rhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[1]);
@@ -435,40 +446,31 @@ static void sccp_stage(function_context& ctx) {
newLat.type = lattice::Bottom; newLat.type = lattice::Bottom;
} else if (lhs.type == lattice::Constant && rhs.type == lattice::Constant) { } else if (lhs.type == lattice::Constant && rhs.type == lattice::Constant) {
newLat.type = lattice::Constant; newLat.type = lattice::Constant;
switch (dynamic_cast<const furlang::ir::binary_op_instruction&>(*instr).op_type()) { switch (instr->type()) {
case furlang::ir::binary_op_instruction_t::Add: case furlang::ir::instruction_t::Add: newLat.constant = lhs.constant + rhs.constant; break;
newLat.constant = lhs.constant + rhs.constant; case furlang::ir::instruction_t::Sub: newLat.constant = lhs.constant - rhs.constant; break;
break; case furlang::ir::instruction_t::Mul: newLat.constant = lhs.constant * rhs.constant; break;
case furlang::ir::binary_op_instruction_t::Sub: case furlang::ir::instruction_t::Div: newLat.constant = lhs.constant / rhs.constant; break;
newLat.constant = lhs.constant - rhs.constant; case furlang::ir::instruction_t::Mod: newLat.constant = lhs.constant % rhs.constant; break;
break; case furlang::ir::instruction_t::Eq:
case furlang::ir::binary_op_instruction_t::Mul:
newLat.constant = lhs.constant * rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Div:
newLat.constant = lhs.constant / rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Mod:
newLat.constant = lhs.constant % rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Eq:
newLat.constant = (lhs.constant == rhs.constant) ? 1 : 0; newLat.constant = (lhs.constant == rhs.constant) ? 1 : 0;
break; break;
case furlang::ir::binary_op_instruction_t::NotEq: case furlang::ir::instruction_t::NotEq:
newLat.constant = (lhs.constant != rhs.constant) ? 1 : 0; newLat.constant = (lhs.constant != rhs.constant) ? 1 : 0;
break; break;
case furlang::ir::binary_op_instruction_t::LessThan: case furlang::ir::instruction_t::LessThan:
newLat.constant = (lhs.constant < rhs.constant) ? 1 : 0; newLat.constant = (lhs.constant < rhs.constant) ? 1 : 0;
break; break;
case furlang::ir::binary_op_instruction_t::GreaterThan: case furlang::ir::instruction_t::GreaterThan:
newLat.constant = (lhs.constant > rhs.constant) ? 1 : 0; newLat.constant = (lhs.constant > rhs.constant) ? 1 : 0;
break; break;
case furlang::ir::binary_op_instruction_t::LessEq: case furlang::ir::instruction_t::LessEq:
newLat.constant = (lhs.constant <= rhs.constant) ? 1 : 0; newLat.constant = (lhs.constant <= rhs.constant) ? 1 : 0;
break; break;
case furlang::ir::binary_op_instruction_t::GreaterEq: case furlang::ir::instruction_t::GreaterEq:
newLat.constant = (lhs.constant >= rhs.constant) ? 1 : 0; newLat.constant = (lhs.constant >= rhs.constant) ? 1 : 0;
break; break;
default: throw std::runtime_error("unreachable");
} }
} }
} break; } break;
@@ -546,6 +548,10 @@ static void adce_stage(function_context& ctx) {
if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) {
defMap[instr->destination().reg()] = instr.get(); defMap[instr->destination().reg()] = instr.get();
} }
if (instr->type() == furlang::ir::instruction_t::Call) {
// TODO: Check if the function has side effects
if (alive.insert(instr.get()).second) worklist.push(instr.get());
}
} }
auto* exit = block->exit().get(); auto* exit = block->exit().get();
+20 -2
View File
@@ -8,12 +8,13 @@
#include "furlang/arena.hpp" #include "furlang/arena.hpp"
#include <fstream> #include <fstream>
#include <furvm/furvm.hpp>
#include <iostream> #include <iostream>
int main(void) { int main(void) {
try { try {
std::string programStr = R"( std::string programStr = R"(
native func print(value: int32); private native func print(value: int32);
func main() -> int32 { func main() -> int32 {
x = 0; x = 0;
@@ -22,6 +23,7 @@ int main(void) {
while (x < y) { while (x < y) {
x = x + z; x = x + z;
} }
print(sizeof x);
} }
)"; )";
furlang::arena arena{}; furlang::arena arena{};
@@ -58,8 +60,24 @@ int main(void) {
} }
} }
auto context = std::make_shared<furvm::context>();
auto furvmMod = context->emplace("main", furc::back::furvm_generator::generate(mod));
std::ofstream file("./a.fmod", std::ios::binary); std::ofstream file("./a.fmod", std::ios::binary);
furc::back::furvm_generator::generate(mod).serialize(file); furvmMod->serialize(file);
file.close();
furvmMod->set_native_function("print",
[](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; });
furvm::executor_h executor = context->emplace_executor(context);
executor->push_frame(furvmMod, *furvmMod->function_at("main", furvm::function_sig{}));
std::cout << "--- Interpreting:\n";
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
executor->step();
}
return 0; return 0;
} catch (...) { } catch (...) {
+41 -11
View File
@@ -3,12 +3,25 @@
#include "furlang/ir/block.hpp" #include "furlang/ir/block.hpp"
#include <cstdint>
#include <memory> #include <memory>
#include <type_traits>
#include <vector> #include <vector>
namespace furlang { namespace furlang {
namespace ir { namespace ir {
enum class function_t : std::uint8_t {
Normal = 0,
Import,
Native,
};
enum class function_access_t : std::uint8_t {
Public = 0,
Private,
};
/** /**
* @brief IR function. * @brief IR function.
* *
@@ -22,18 +35,11 @@ public:
/** /**
* @brief Construct a new IR function. * @brief Construct a new IR function.
* *
* @param name Name to copy. * @param name Name to forward.
*/ */
function(const std::string& name) template <typename StringFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, StringFwd>>>
: m_name(name) {} function(StringFwd&& name, function_access_t access, std::uint32_t paramCount, function_t type = function_t::Normal)
: m_name(std::forward<StringFwd>(name)), m_access(access), m_paramCount(paramCount), m_type(type) {}
/**
* @brief Construct a new IR function.
*
* @param name Name to move.
*/
function(std::string&& name)
: m_name(std::move(name)) {}
public: public:
/** /**
* @brief Returns this function's name. * @brief Returns this function's name.
@@ -42,6 +48,27 @@ public:
*/ */
const std::string& name() const { return m_name; } const std::string& name() const { return m_name; }
/**
* @brief Returns the function's access.
*
* @return The access.
*/
function_access_t access() const { return m_access; }
/**
* @brief Returns this function's parameter count.
*
* @return The parameter count.
*/
std::uint32_t param_count() const { return m_paramCount; }
/**
* @brief Returns this function's type.
*
* @return The type.
*/
function_t type() const { return m_type; }
/** /**
* @brief Pushes and returns a new IR block. * @brief Pushes and returns a new IR block.
* *
@@ -57,6 +84,9 @@ public:
const std::vector<value_type>& blocks() const { return m_blocks; } const std::vector<value_type>& blocks() const { return m_blocks; }
private: private:
std::string m_name; std::string m_name;
function_access_t m_access;
function_t m_type;
std::uint32_t m_paramCount;
std::vector<value_type> m_blocks; std::vector<value_type> m_blocks;
}; };
+196 -61
View File
@@ -7,6 +7,7 @@
#include <optional> #include <optional>
#include <ostream> #include <ostream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -17,16 +18,53 @@ namespace ir {
* @brief IR instruction type. * @brief IR instruction type.
*/ */
enum class instruction_t { enum class instruction_t {
Alloca, /**< Unused */ Alloca, /**< Unused */
Assign, /**< Assign */ Assign, /**< Assign */
BinaryOp, /**< Binary operation */ Add, /**< Addition */
Call, /**< Call */ Sub, /**< Subtraction */
Branch, /**< Branch */ Mul, /**< Multiplication */
BranchCond, /**< Conditional branch */ Div, /**< Division */
Return, /**< Return */ Mod, /**< Modulo */
Phi, /**< Phi function */ Eq, /**< Equal */
NotEq, /**< Not equal */
LessThan, /**< Less than */
GreaterThan, /**< Greater than */
LessEq, /**< Less or equal */
GreaterEq, /**< Greater or equal */
Pointerof, /**< Pointerof */
Sizeof, /**< Sizeof */
Call, /**< Call */
Branch, /**< Branch */
BranchCond, /**< Conditional branch */
Return, /**< Return */
Phi, /**< Phi function */
}; };
static inline std::ostream& operator<<(std::ostream& os, instruction_t type) {
switch (type) {
case instruction_t::Alloca: return os << "alloca";
case instruction_t::Assign: return os << "assign";
case instruction_t::Add: return os << "add";
case instruction_t::Sub: return os << "sub";
case instruction_t::Mul: return os << "mul";
case instruction_t::Div: return os << "div";
case instruction_t::Mod: return os << "mod";
case instruction_t::Eq: return os << "eq";
case instruction_t::NotEq: return os << "notEq";
case instruction_t::LessThan: return os << "lessThan";
case instruction_t::GreaterThan: return os << "greaterThan";
case instruction_t::LessEq: return os << "lessEq";
case instruction_t::GreaterEq: return os << "greaterEq";
case instruction_t::Pointerof: return os << "pointerof";
case instruction_t::Sizeof: return os << "sizeof";
case instruction_t::Call: return os << "call";
case instruction_t::Branch: return os << "branch";
case instruction_t::BranchCond: return os << "branchCond";
case instruction_t::Return: return os << "return";
case instruction_t::Phi: return os << "phi";
}
}
/** /**
* @brief Checks if an instruction type exits. * @brief Checks if an instruction type exits.
* *
@@ -240,78 +278,112 @@ protected:
}; };
/** /**
* @brief Binary operation instruction type * @brief Generic unary instruction
*/ */
enum class binary_op_instruction_t { class unary_instruction final : public instruction {
Add, /**< Addition */ public:
Sub, /**< Subtraction */ /**
Mul, /**< Multiplication */ * @brief Construct a new unary instruction.
Div, /**< Division */ *
Mod, /**< Modulo */ * @param type Instruction type.
* @param src Source operand.
* @param dst Destination operand.
*/
unary_instruction(instruction_t type, operand&& src, operand&& dst)
: m_type(type), m_src(std::move(src)), m_dst(std::move(dst)) {}
Eq, /**< Equal */ ~unary_instruction() override = default;
NotEq, /**< Not equal */
LessThan, /**< Less than */ /**
GreaterThan, /**< Greater than */ * @brief Move constructor
LessEq, /**< Less or equal */ */
GreaterEq, /**< Greater or equal */ unary_instruction(unary_instruction&&) = default;
/**
* @brief Move constructor
*/
unary_instruction& operator=(unary_instruction&&) = default;
unary_instruction(const unary_instruction&) = delete;
unary_instruction& operator=(const unary_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return The instruction type.
*/
instruction_t type() const override { return m_type; }
bool has_destination() const override { return true; }
operand& destination() override { return m_dst; }
const operand& destination() const override { return m_dst; }
std::vector<operand*> sources() override { return { &m_src }; }
std::vector<const operand*> sources() const override { return { &m_src }; }
/**
* @brief Returns this instruction's source operand.
*
* @return The operand.
*/
const operand& src() const { return m_src; }
/**
* @brief Returns this instruction's destination operand.
*
* @return The operand.
*/
const operand& dst() const { return m_dst; }
private:
instruction_t m_type;
operand m_src;
operand m_dst;
protected:
std::ostream& print(std::ostream& os) const override { return os << m_dst << " = " << m_type << ' ' << m_src; }
}; };
static inline std::ostream& operator<<(std::ostream& os, binary_op_instruction_t type) {
switch (type) {
case binary_op_instruction_t::Add: return os << '+';
case binary_op_instruction_t::Sub: return os << '-';
case binary_op_instruction_t::Mul: return os << '*';
case binary_op_instruction_t::Div: return os << '/';
case binary_op_instruction_t::Mod: return os << '%';
case binary_op_instruction_t::Eq: return os << "==";
case binary_op_instruction_t::NotEq: return os << "!=";
case binary_op_instruction_t::LessThan: return os << '<';
case binary_op_instruction_t::GreaterThan: return os << '>';
case binary_op_instruction_t::LessEq: return os << "<=";
case binary_op_instruction_t::GreaterEq: return os << ">=";
}
return os;
}
/** /**
* @brief Binary operation instruction * @brief Generic binary instruction
*/ */
class binary_op_instruction final : public instruction { class binary_instruction final : public instruction {
public: public:
/** /**
* @brief Construct a new binary operation instruction. * @brief Construct a new binary operation instruction.
* *
* @param type Operation type. * @param type Instruction type.
* @param lhs Left-hand-side operand. * @param lhs Left-hand-side operand.
* @param rhs Right-hand-side operand. * @param rhs Right-hand-side operand.
* @param dst Destination operand. * @param dst Destination operand.
*/ */
binary_op_instruction(binary_op_instruction_t type, operand&& lhs, operand&& rhs, operand&& dst) binary_instruction(instruction_t type, operand&& lhs, operand&& rhs, operand&& dst)
: m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)), m_dst(std::move(dst)) {} : m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)), m_dst(std::move(dst)) {}
~binary_op_instruction() override = default; ~binary_instruction() override = default;
/** /**
* @brief Move constructor * @brief Move constructor
*/ */
binary_op_instruction(binary_op_instruction&&) = default; binary_instruction(binary_instruction&&) = default;
/** /**
* @brief Move constructor * @brief Move constructor
*/ */
binary_op_instruction& operator=(binary_op_instruction&&) = default; binary_instruction& operator=(binary_instruction&&) = default;
binary_op_instruction(const binary_op_instruction&) = delete; binary_instruction(const binary_instruction&) = delete;
binary_op_instruction& operator=(const binary_op_instruction&) = delete; binary_instruction& operator=(const binary_instruction&) = delete;
public: public:
/** /**
* @brief Returns this instruction's type. * @brief Returns this instruction's type.
* *
* @return instruction_t::BinaryOp. * @return instruction_t::BinaryOp.
*/ */
instruction_t type() const override { return instruction_t::BinaryOp; } instruction_t type() const override { return m_type; }
bool has_destination() const override { return true; } bool has_destination() const override { return true; }
@@ -323,13 +395,6 @@ public:
std::vector<const operand*> sources() const override { return { &m_lhs, &m_rhs }; } std::vector<const operand*> sources() const override { return { &m_lhs, &m_rhs }; }
/**
* @brief Returns this instruction's operation type.
*
* @return The operation type.
*/
binary_op_instruction_t op_type() const { return m_type; }
/** /**
* @brief Returns this instruction's left-hand-side operand. * @brief Returns this instruction's left-hand-side operand.
* *
@@ -351,13 +416,13 @@ public:
*/ */
const operand& dst() const { return m_dst; } const operand& dst() const { return m_dst; }
private: private:
binary_op_instruction_t m_type; instruction_t m_type;
operand m_lhs /**< Left-hand-side operand */; operand m_lhs /**< Left-hand-side operand */;
operand m_rhs /**< Right-hand-side operand */; operand m_rhs /**< Right-hand-side operand */;
operand m_dst /**< Destination operand */; operand m_dst /**< Destination operand */;
protected: protected:
std::ostream& print(std::ostream& os) const override { std::ostream& print(std::ostream& os) const override {
return os << "binop(" << m_type << ") " << m_lhs << ", " << m_rhs << ", " << m_dst; return os << m_dst << " = " << m_lhs << ' ' << m_type << ' ' << m_rhs;
} }
}; };
@@ -616,7 +681,7 @@ private:
std::vector<std::pair<operand, block_index>> m_labels; std::vector<std::pair<operand, block_index>> m_labels;
protected: protected:
std::ostream& print(std::ostream& os) const override { std::ostream& print(std::ostream& os) const override {
os << "phi " << m_dst << " ="; os << m_dst << " = phi";
bool first = true; bool first = true;
for (const auto& pair : m_labels) { for (const auto& pair : m_labels) {
if (!first) os << ','; if (!first) os << ',';
@@ -627,6 +692,76 @@ protected:
} }
}; };
/**
* @brief Function call instruction.
*/
class call_instruction final : public instruction {
public:
template <typename NameFwd, typename ArgsFwd>
call_instruction(NameFwd&& name, operand&& dst, ArgsFwd&& args)
: m_name(std::forward<NameFwd>(name)), m_dst(std::move(dst)), m_args(std::forward<ArgsFwd>(args)) {}
~call_instruction() override = default;
call_instruction(call_instruction&&) noexcept = default;
call_instruction& operator=(call_instruction&&) noexcept = default;
call_instruction(const call_instruction&) = delete;
call_instruction& operator=(const call_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return instruction_t::Call.
*/
instruction_t type() const override { return instruction_t::Call; }
bool has_destination() const override { return true; }
/**
* @brief Returns this instruction's destination register.
*
* @return The register.
*/
operand& destination() override { return m_dst; }
/**
* @brief Returns this instruction's destination register.
*
* @return The register.
*/
const operand& destination() const override { return m_dst; }
std::vector<const operand*> sources() const override {
std::vector<const operand*> srcs;
srcs.reserve(m_args.size());
for (const auto& op : m_args)
srcs.push_back(&op);
return srcs;
}
/**
* @brief Returns this instruction's callee name.
*
* @return The name.
*/
const std::string& name() const { return m_name; }
private:
std::string m_name;
operand m_dst;
std::vector<operand> m_args;
protected:
std::ostream& print(std::ostream& os) const override {
os << "call " << m_name << '(';
bool first = true;
for (const auto& op : m_args) {
if (!first) os << ", ";
first = false;
os << op;
}
return os << ") = " << m_dst;
}
};
} // namespace ir } // namespace ir
} // namespace furlang } // namespace furlang
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURLANG_UTILITY_HASH_HPP
#define FURLANG_UTILITY_HASH_HPP
#include <cstddef>
#include <functional>
namespace furlang {
namespace utility {
// Source - https://stackoverflow.com/a/27952689
// Posted by Yakk - Adam Nevraumont, modified by community. See post 'Timeline' for change history
// Retrieved 2026-07-07, License - CC BY-SA 4.0
static inline std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
if constexpr (sizeof(std::size_t) >= 8) {
lhs ^= rhs + 0x517cc1b727220a95 + (lhs << 6) + (lhs >> 2);
} else {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
}
return lhs;
}
// Source - https://stackoverflow.com/a/20602159
// Posted by Casey, modified by community. See post 'Timeline' for change history
// Retrieved 2026-07-07, License - CC BY-SA 3.0
template <typename T, typename U, typename FirstHash = std::hash<T>, typename SecondHash = std::hash<U>>
struct pair_hash {
std::size_t operator()(const std::pair<T, U>& pair) const {
return hash_combine(FirstHash()(pair.first), SecondHash()(pair.second));
}
};
template <typename T, typename Hash = std::hash<T>>
struct vector_hash {
std::size_t operator()(const std::vector<T>& vec) const {
std::size_t seed = 0;
for (const auto& element : vec) {
hash_combine(seed, Hash()(element));
}
return seed;
}
};
} // namespace utility
} // namespace furlang
#endif // FURLANG_UTILITY_HASH_HPP
+7 -45
View File
@@ -7,20 +7,22 @@
#include "furvm/handle.hpp" #include "furvm/handle.hpp"
#include "furvm/module.hpp" // IWYU pragma: keep #include "furvm/module.hpp" // IWYU pragma: keep
#include "furvm/thing.hpp" // IWYU pragma: keep #include "furvm/thing.hpp" // IWYU pragma: keep
#include "furvm/thing_allocator.hpp"
#include <cstddef> #include <cstddef>
#include <utility> #include <utility>
namespace furvm { namespace furvm {
class context { class context : public handle_container<mod_h> {
public: public:
friend class executor; friend class executor;
public: public:
/** /**
* @brief Constructs a context. * @brief Constructs a context.
*/ */
context(); context()
: m_thingAllocator(m_thingArena) {}
~context() = default; ~context() = default;
@@ -36,49 +38,6 @@ public:
context(const context&) = delete; context(const context&) = delete;
context& operator=(const context&) = delete; context& operator=(const context&) = delete;
public:
/**
* @brief Emplaces a module in the context.
*
* @param args Arguments forwarded to the module constructor.
* @return The emplaced module.
*/
template <typename... Args>
auto emplace_module(Args&&... args) {
return m_modules.emplace(std::forward<Args>(args)...);
}
/**
* @brief Returns a module from the context.
*
* @param args Module's id.
* @return A handle to the module.
*/
template <typename... Args>
auto module_at(Args&&... args) {
return m_modules.at(std::forward<Args>(args)...);
}
/**
* @brief Returns a module from the context.
*
* @param args Module's id.
* @return A handle to the module.
*/
template <typename... Args>
auto module_at(Args&&... args) const {
return m_modules.at(std::forward<Args>(args)...);
}
/**
* @brief Erases a module from the context.
*
* @param args Module's id.
*/
template <typename... Args>
void erase_module(Args&&... args) {
m_modules.erase(std::forward<Args>(args)...);
}
public: public:
/** /**
* @brief Emplaces an executor in the context. * @brief Emplaces an executor in the context.
@@ -166,6 +125,8 @@ public:
* @return The thing allocator. * @return The thing allocator.
*/ */
thing_allocator<std::byte> thing_alloc() const { return m_thingAllocator; } thing_allocator<std::byte> thing_alloc() const { return m_thingAllocator; }
thing_type_store& thing_type_store() { return m_thingTypeStore; }
private: private:
handle_container<mod_h> m_modules; handle_container<mod_h> m_modules;
handle_container<thing_h> m_things; handle_container<thing_h> m_things;
@@ -173,6 +134,7 @@ private:
furlang::arena m_thingArena; furlang::arena m_thingArena;
thing_allocator<std::byte> m_thingAllocator; thing_allocator<std::byte> m_thingAllocator;
class thing_type_store m_thingTypeStore;
}; };
} // namespace furvm } // namespace furvm
+6
View File
@@ -1,6 +1,7 @@
#ifndef FURVM_DETAIL_HANDLE_HPP #ifndef FURVM_DETAIL_HANDLE_HPP
#define FURVM_DETAIL_HANDLE_HPP #define FURVM_DETAIL_HANDLE_HPP
#include <functional>
#include <type_traits> #include <type_traits>
namespace furvm { namespace furvm {
@@ -27,6 +28,11 @@ struct header_has_refcount<Header,
template <typename Header> template <typename Header>
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value; static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
template <typename Handle, typename IdHash = std::hash<typename Handle::id_type>>
struct handle_hash {
std::size_t operator()(const Handle& handle) const { return IdHash{}(handle.id()); }
};
} // namespace detail } // namespace detail
} // namespace furvm } // namespace furvm
@@ -16,6 +16,14 @@ namespace detail {
* @return The output stream. * @return The output stream.
*/ */
std::ostream& serialize(std::ostream& os, std::int8_t value); std::ostream& serialize(std::ostream& os, std::int8_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int16_t value); std::ostream& serialize(std::ostream& os, std::int16_t value);
/** /**
@@ -26,6 +34,14 @@ std::ostream& serialize(std::ostream& os, std::int16_t value);
* @return The output stream. * @return The output stream.
*/ */
std::ostream& serialize(std::ostream& os, std::int32_t value); std::ostream& serialize(std::ostream& os, std::int32_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int64_t value); std::ostream& serialize(std::ostream& os, std::int64_t value);
/** /**
@@ -73,6 +89,87 @@ std::ostream& serialize(std::ostream& os, std::uint64_t value);
*/ */
std::ostream& serialize(std::ostream& os, const std::string& value); std::ostream& serialize(std::ostream& os, const std::string& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int8_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int16_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int32_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int64_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint8_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint16_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint32_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint64_t& value);
/**
* @brief Deserializes a string.
*
* @param is Input stream.
* @param value String.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::string& value);
} // namespace detail } // namespace detail
} // namespace furvm } // namespace furvm
+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;
+67 -28
View File
@@ -1,12 +1,17 @@
#ifndef FURVM_FUNCTION_HPP #ifndef FURVM_FUNCTION_HPP
#define FURVM_FUNCTION_HPP #define FURVM_FUNCTION_HPP
#include "furlang/utility/hash.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/handle.hpp" // IWYU pragma: keep
#include <functional> #include <cstdint>
#include <optional>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <type_traits>
#include <utility> #include <utility>
#include <vector>
namespace furvm { namespace furvm {
@@ -19,7 +24,7 @@ enum class function_t : std::uint8_t {
/** /**
* @brief A native function. * @brief A native function.
*/ */
using native_function = std::function<void(executor&)>; using native_function = std::string;
/** /**
* @brief A function import. * @brief A function import.
@@ -29,37 +34,62 @@ struct import_function {
function_id function; function_id function;
}; };
/**
* @brief Function signature.
*/
struct function_sig {
std::vector<mod_type_h> params;
std::optional<mod_type_h> returnType;
bool operator==(const function_sig& rhs) const { return params == rhs.params; }
bool operator!=(const function_sig& rhs) const { return !this->operator==(rhs); }
};
class function { class function {
public: public:
/** /**
* @brief Constructs a normal function. * @brief Constructs a normal function.
* *
* @param name Name of the function. * @param signature Function's signature.
* @param position Offset in bytecode of the function. * @param position Offset in bytecode of the function.
*/ */
template <typename Name> template <typename SigFwd, typename = std::enable_if_t<std::is_constructible_v<function_sig, SigFwd>>>
function(Name&& name, bytecode_pos position) function(SigFwd&& signature, bytecode_pos position)
: m_name(std::forward<Name>(name)), m_type(function_t::Normal), m_value(position) {} : m_type(function_t::Normal), m_signature(std::forward<SigFwd>(signature)), m_value(position) {}
/** /**
* @brief Constructs a native function. * @brief Constructs a native function.
* *
* @param name Name of the function. * @param signature Function's signature.
* @param native Native function. * @param native Native function tag.
*/ */
template <typename Name> template <typename SigFwd,
function(Name&& name, const native_function& native) typename Native,
: m_name(std::forward<Name>(name)), m_type(function_t::Native), m_value(native) {} typename = std::enable_if_t<std::is_constructible_v<native_function, Native> &&
std::is_constructible_v<function_sig, SigFwd>>>
function(SigFwd&& signature, Native&& native)
: m_type(function_t::Native),
m_signature(std::forward<SigFwd>(signature)),
m_value(std::forward<Native>(native)) {}
/** /**
* @brief Constructs an import function. * @brief Constructs an import function.
* *
* @param name Name of the function. * @param mod Module's id.
* @param imp Import function. * @param function Function's id.
*/ */
template <typename Name> template <typename ModFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModFwd>>>
function(Name&& name, const import_function& imp) function(ModFwd&& mod, function_id function)
: m_name(std::forward<Name>(name)), m_type(function_t::Import), m_value(imp) {} : m_type(function_t::Import), m_signature(), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
/**
* @brief Constructs an import function.
*
* @param mod Module.
* @param function Function.
*/
function(const mod_h& mod, const function_h& function);
/** /**
* @brief Destructs a function. * @brief Destructs a function.
@@ -86,19 +116,19 @@ public:
*/ */
function& operator=(const function&); function& operator=(const function&);
public: public:
/**
* @brief Returns a name of this function.
*
* @return The name.
*/
constexpr const std::string& name() const { return m_name; }
/** /**
* @brief Returns a type of this function. * @brief Returns a type of this function.
* *
* @return The type. * @return The type.
*/ */
constexpr function_t type() const { return m_type; } constexpr function_t type() const { return m_type; }
/**
* @brief Returns this function's signature.
*
* @return The signature.
*/
function_sig signature() const { return m_signature; }
public: public:
/** /**
* @brief Returns normal function's value. * @brief Returns normal function's value.
@@ -123,15 +153,15 @@ public:
/** /**
* @brief Returns import function's value. * @brief Returns import function's value.
* *
* @return The name. * @return The value.
*/ */
const import_function& imp() const { const import_function& imp() const {
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch"); if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
return m_value.imp; return m_value.imp;
} }
private: private:
std::string m_name; function_t m_type;
function_t m_type; function_sig m_signature;
union value { union value {
std::size_t position = 0; std::size_t position = 0;
@@ -143,8 +173,9 @@ private:
value(std::size_t position) value(std::size_t position)
: position(position) {} : position(position) {}
value(const native_function& native) template <typename Native, typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>>
: native(native) {} value(Native&& native)
: native(std::forward<Native>(native)) {}
value(const import_function& imp) value(const import_function& imp)
: imp(imp) {} : imp(imp) {}
@@ -158,6 +189,14 @@ private:
} m_value; } m_value;
}; };
namespace detail {
struct function_sig_hash {
std::size_t operator()(const function_sig& signature) const;
};
} // namespace detail
} // namespace furvm } // namespace furvm
#endif // FURVM_FUNCTION_HPP #endif // FURVM_FUNCTION_HPP
+12 -8
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,13 +144,12 @@ using mod_id = std::string;
*/ */
using mod_h = handle<mod, refcount_header<mod_id>>; using mod_h = handle<mod, refcount_header<mod_id>>;
// thing.hpp // thing_allocator.hpp
/** template <typename T>
* @enum thing_t class thing_allocator;
* @brief Thing type.
*/ // thing.hpp
enum class thing_t : std::uint8_t;
/** /**
* @class bad_thing_access * @class bad_thing_access
@@ -152,8 +157,7 @@ enum class thing_t : std::uint8_t;
*/ */
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
+51 -7
View File
@@ -98,6 +98,8 @@ public:
using const_pointer = const Value*; /** Constant pointer type. */ using const_pointer = const Value*; /** Constant pointer type. */
public: public:
using id_type = typename Header::id_type; /** Id type of the header. */ using id_type = typename Header::id_type; /** Id type of the header. */
using header_type = Header;
public: public:
using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */ using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */
public: public:
@@ -148,7 +150,9 @@ public:
*/ */
handle(const handle& other) handle(const handle& other)
: m_value(other.m_value) { : m_value(other.m_value) {
m_value->first.acquire(); if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
} }
/** /**
@@ -157,7 +161,9 @@ public:
handle& operator=(const handle& other) { handle& operator=(const handle& other) {
if (this == &other) return *this; if (this == &other) return *this;
m_value = other.m_value; m_value = other.m_value;
m_value->first.acquire(); if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
return *this; return *this;
} }
public: public:
@@ -180,8 +186,8 @@ public:
* *
* @return The reference count. * @return The reference count.
*/ */
template <typename ReferenceCount = typename Header::refcount_type> template <typename U = Header, typename = std::enable_if_t<detail::header_has_refcount_v<U>>>
ReferenceCount reference_count() const { auto reference_count() const {
return m_value->first.reference_count(); return m_value->first.reference_count();
} }
@@ -231,6 +237,10 @@ public:
* @brief Invalidates the handle without releasing. * @brief Invalidates the handle without releasing.
*/ */
void dispatch() { m_value = nullptr; } void dispatch() { m_value = nullptr; }
public:
bool operator==(const handle& rhs) const { return m_value == rhs.m_value; }
bool operator!=(const handle& rhs) const { return !this->operator==(rhs); }
private: private:
pair_type* m_value = nullptr; pair_type* m_value = nullptr;
}; };
@@ -308,6 +318,17 @@ public:
delete it->second; delete it->second;
m_pairs.erase(it); m_pairs.erase(it);
} }
/**
* @brief Checks whether a handle exists inside.
*
* @param id Identifier of the handle.
* @return true if the handle exists insdie of this container.
*/
template <typename IdFwd>
constexpr bool contains(IdFwd&& id) const {
return m_pairs.find(std::forward<IdFwd>(id)) != m_pairs.end();
}
private: private:
std::unordered_map<id_type, pair_type*> m_pairs; std::unordered_map<id_type, pair_type*> m_pairs;
}; };
@@ -347,9 +368,16 @@ public:
delete m_pairs[id]; delete m_pairs[id];
} }
pair_type* newPair = new pair_type(std::piecewise_construct, pair_type* newPair = nullptr;
std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }), if constexpr (detail::header_has_refcount_v<typename Handle::header_type>) {
std::forward_as_tuple(std::forward<Args>(args)...)); newPair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }),
std::forward_as_tuple(std::forward<Args>(args)...));
} else {
newPair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(std::forward<Args>(args)...));
}
m_pairs[id] = newPair; m_pairs[id] = newPair;
return { newPair }; return { newPair };
@@ -395,6 +423,22 @@ public:
delete m_pairs[id]; delete m_pairs[id];
m_pairs[id] = nullptr; m_pairs[id] = nullptr;
} }
/**
* @brief Checks whether a handle exists inside.
*
* @param id Identifier of the handle.
* @return true if the handle exists insdie of this container.
*/
constexpr bool contains(id_type id) const { return id < m_pairs.size() && m_pairs[id] != nullptr; }
public:
auto begin() { return m_pairs.begin(); }
auto begin() const { return m_pairs.begin(); }
auto cbegin() const { return m_pairs.cbegin(); }
auto end() { return m_pairs.end(); }
auto end() const { return m_pairs.end(); }
auto cend() const { return m_pairs.cend(); }
private: private:
std::vector<pair_type*> m_pairs; std::vector<pair_type*> m_pairs;
}; };
+72 -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.
@@ -25,6 +48,24 @@ enum class instruction_t : byte {
*/ */
PushConstant, PushConstant,
/**
* @brief Pushes a new array onto the stack.
*
* Type is the next 4 bytes in little-endian.
* If the type is dynamic the array's size will be popped off of the stack.
*/
Array,
/**
* @brief Pushes an element from an array onto the stack.
*/
Get,
/**
* @brief Sets an array element.
*/
Set,
/** /**
* @brief Pops top element from the stack. * @brief Pops top element from the stack.
*/ */
@@ -35,11 +76,23 @@ 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.
*/ */
Clone, Clone,
/**
* @brief Pushes a new reference onto the stack.
*
* Pops the top thing from the stack and pushes its reference.
*/
Reference,
/** /**
* @brief Adds two things together on the stack. * @brief Adds two things together on the stack.
*/ */
@@ -95,6 +148,21 @@ enum class instruction_t : byte {
*/ */
GreaterEqual, GreaterEqual,
/**
* @brief Pushes a pointer of popped-off thing onto the stack.
*/
Pointerof,
/**
* @brief Pushes a size of popped-off thing onto the stack.
*/
Sizeof,
/**
* @brief Pushes a length of popped-off thing onto the stack.
*/
Lengthof,
/** /**
* @brief Pushes a variable onto the stack. * @brief Pushes a variable onto the stack.
* *
@@ -135,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 {
+246 -24
View File
@@ -1,18 +1,145 @@
#ifndef FURVM_MODULE_HPP #ifndef FURVM_MODULE_HPP
#define FURVM_MODULE_HPP #define FURVM_MODULE_HPP
#include "furlang/utility/hash.hpp"
#include "furvm/function.hpp" #include "furvm/function.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/handle.hpp" #include "furvm/handle.hpp"
#include <functional>
#include <istream>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace furvm { namespace furvm {
struct mod_type {
struct array {
mod_type_id typeId;
std::size_t size;
};
struct imprt {
mod_id modId;
mod_type_id typeId;
};
enum type {
S8 = 0,
S16,
S32,
S64,
U8,
U16,
U32,
U64,
Ptr,
Ref,
Array,
Import,
Count,
} type;
union value {
std::nullptr_t null = nullptr;
mod_type_id typeRef;
array array;
imprt imprt;
value() = default;
value(mod_type_id id)
: typeRef(id) {}
value(mod_type_id id, std::size_t size)
: array({}) {
array.typeId = id;
array.size = size;
}
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
value(ModIdFwd&& modId, mod_type_id typeId)
: imprt({}) {
imprt.modId = std::forward<ModIdFwd>(modId);
imprt.typeId = typeId;
}
~value() {}
value(value&& other) = delete;
value& operator=(value&& other) = delete;
value(const value& other) = delete;
value& operator=(const value& other) = delete;
} value;
mod_type(enum type type)
: type(type) {}
mod_type(enum type type, mod_type_id typeRef)
: type(type), value(typeRef) {}
mod_type(mod_type_id id, std::size_t size)
: type(Array), value(id, size) {}
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
mod_type(ModIdFwd&& modId, mod_type_id typeId)
: type(Import), value(std::forward<ModIdFwd>(modId), typeId) {}
~mod_type() {
switch (type) {
case Array: value.array.~array(); break;
case Import: value.imprt.~imprt(); break;
default: break;
}
}
mod_type(mod_type&& other) noexcept
: type(other.type) {
switch (type) {
case Array: new (&value.array) array(other.value.array); break;
case Import: new (&value.imprt) imprt(std::move(other.value.imprt)); break;
default: break;
}
other.type = Count;
}
mod_type& operator=(mod_type&& other) noexcept {
if (this == &other) return *this;
type = other.type;
switch (type) {
case Array: new (&value.array) array(other.value.array); break;
case Import: new (&value.imprt) imprt(std::move(other.value.imprt)); break;
default: break;
}
other.type = Count;
return *this;
}
mod_type(const mod_type& other)
: type(other.type) {
switch (type) {
case Array: new (&value.array) array(other.value.array); break;
case Import: new (&value.imprt) imprt(other.value.imprt); break;
default: break;
}
}
mod_type& operator=(const mod_type& other) {
if (this == &other) return *this;
type = other.type;
switch (type) {
case Array: new (&value.array) array(other.value.array); break;
case Import: new (&value.imprt) imprt(other.value.imprt); break;
default: break;
}
return *this;
}
};
class mod { class mod {
friend class function; friend class function;
friend class serializer; friend class serializer;
@@ -20,6 +147,8 @@ public:
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */ using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
static constexpr char MAGIC[4] = { 'F', 'u', 'r', 'M' }; /** Furvm module file magic. */ static constexpr char MAGIC[4] = { 'F', 'u', 'r', 'M' }; /** Furvm module file magic. */
using native_function = std::function<void(executor&)>;
public: public:
/** /**
* @brief Constructs a module. * @brief Constructs a module.
@@ -71,24 +200,45 @@ public:
/** /**
* @brief Emplaces a function in the module's function container. * @brief Emplaces a function in the module's function container.
* *
* Emplaces the function in module's function container and name to function map and public functions map.
*
* @param args Arguments forwarded into the container's emplace_back function. * @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function. * @return A handle to the emplaced function.
*/ */
template <typename... Args> template <typename... Args>
function_h emplace_function(Args&&... args) { function_h emplace_function(Args&&... args) {
function_h function = m_functions.emplace_back(std::forward<Args>(args)...); function_h function;
m_functionMap[function->name()] = function.id(); if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
}
return std::move(function); return std::move(function);
} }
/** /**
* @brief Inserts a function in the module's function container. * @brief Emplaces a function in the module's function container.
* *
* @param function Function to insert. * Emplaces the function in module's function container and name to function map.
*
* @param name Name of the function.
* @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function.
*/ */
template <typename Function> template <typename NameFwd,
void push_back(Function&& function) { typename... Args,
m_functions.emplace_back(std::forward<Function>(function)); typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
function_h emplace_function(NameFwd&& name, Args&&... args) {
function_h function;
if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
}
auto pair = std::make_pair(std::forward<NameFwd>(name), function->signature());
m_functionMap[function.id()] = pair;
m_functionSigs[std::move(pair)] = function.id();
return std::move(function);
} }
/** /**
@@ -113,20 +263,13 @@ public:
* @param name Name of the function. * @param name Name of the function.
* @return A handle to the function. * @return A handle to the function.
*/ */
template <typename NameFwd> template <typename NameFwd,
auto function_at(NameFwd&& name) { typename SigFwd,
return function_at(m_functionMap.at(std::forward<NameFwd>(name))); typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd> &&
} std::is_constructible_v<function_sig, SigFwd>>>
auto function_at(NameFwd&& name, SigFwd&& signature) {
/** return function_at(
* @brief Returns a function from the module. m_functionSigs.at(std::make_pair<>(std::forward<NameFwd>(name), std::forward<SigFwd>(signature))));
*
* @param name Name of the function.
* @return A handle to the function.
*/
template <typename NameFwd>
auto function_at(NameFwd&& name) const {
return function_at(m_functionMap.at(std::forward<NameFwd>(name)));
} }
/** /**
@@ -134,7 +277,70 @@ public:
* *
* @param id Identifier of the function. * @param id Identifier of the function.
*/ */
void erase_function(function_id id) { m_functions.erase(id); } void erase_function(function_id id) {
m_functions.erase(id);
if (auto it = m_functionMap.find(id); it != m_functionMap.end()) {
m_functionSigs.erase(it->second);
m_functionMap.erase(it);
}
}
public:
template <typename NameFwd, typename Func>
void set_native_function(NameFwd&& name, Func&& func) {
m_nativeFunctions.emplace(std::forward<NameFwd>(name), std::forward<Func>(func));
}
template <typename NameFwd>
native_function get_native_function(NameFwd&& name) const {
return m_nativeFunctions.at(std::forward<NameFwd>(name));
}
public:
/**
* @brief Emplaces a type in the context.
*
* @param args Arguments forwarded to the type constructor.
* @return The emplaced type.
*/
template <typename... Args>
auto emplace_type(Args&&... args) {
if constexpr (std::is_constructible_v<mod_type, Args...>) {
return m_types.emplace_back(std::forward<Args>(args)...);
} else {
return m_types.emplace(std::forward<Args>(args)...);
}
}
/**
* @brief Returns a type from the context.
*
* @param args type's id.
* @return A handle to the type.
*/
template <typename... Args>
auto type_at(Args&&... args) {
return m_types.at(std::forward<Args>(args)...);
}
/**
* @brief Returns a type from the context.
*
* @param args type's id.
* @return A handle to the type.
*/
template <typename... Args>
auto type_at(Args&&... args) const {
return m_types.at(std::forward<Args>(args)...);
}
/**
* @brief Erases a type from the context.
*
* @param args type's id.
*/
template <typename... Args>
void erase_type(Args&&... args) {
m_types.erase(std::forward<Args>(args)...);
}
public: public:
/** /**
* @brief Prints the module in a bytecode form to an output stream. * @brief Prints the module in a bytecode form to an output stream.
@@ -143,11 +349,27 @@ public:
* @return The output stream. * @return The output stream.
*/ */
std::ostream& serialize(std::ostream& os) const; std::ostream& serialize(std::ostream& os) const;
/**
* @brief Loads a module in a bytecode form from an input stream.
*
* @param is Input stream.
* @return The loaded module.
*/
static mod load(std::istream& is);
private: private:
bytecode_t m_bytecode; bytecode_t m_bytecode;
std::unordered_map<std::string, function_id> m_functionMap; using pair_type = std::pair<std::string, function_sig>;
handle_container<function_h> m_functions; using pair_hash =
furlang::utility::pair_hash<std::string, function_sig, std::hash<std::string>, detail::function_sig_hash>;
std::unordered_map<pair_type, function_id, pair_hash> m_functionSigs;
std::unordered_map<function_id, pair_type> m_functionMap;
handle_container<function_h> m_functions;
handle_container<mod_type_h> m_types;
std::unordered_map<std::string, native_function> m_nativeFunctions;
}; };
} // namespace furvm } // namespace furvm
+586 -197
View File
@@ -2,66 +2,238 @@
#define FURVM_THING_HPP #define FURVM_THING_HPP
#include "furlang/arena.hpp" #include "furlang/arena.hpp"
#include "furlang/utility/hash.hpp"
#include "furvm/exceptions.hpp" #include "furvm/exceptions.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/thing_allocator.hpp" // IWYU pragma: keep
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <cstring> #include <cstring>
#include <memory> #include <functional>
#include <limits>
#include <new>
#include <stdexcept>
#include <type_traits>
#include <unordered_map>
#include <utility> #include <utility>
#include <vector>
namespace furvm { namespace furvm {
enum class thing_t : std::uint8_t { struct thing_type {
Int32, 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 {
* @brief Returns how many bytes a thing would take up.
* struct thing_type_hash {
* @param type Type of the thing. std::size_t operator()(const thing_type& type) const {
* @return The byte count. std::size_t seed = std::hash<decltype(type.type)>{}(type.type);
*/ switch (type.type) {
static inline std::size_t thing_type_size(thing_t type) { case thing_type::S8:
switch (type) { case thing_type::S16:
case thing_t::Int32: return sizeof(std::int32_t); case thing_type::S32:
default: return 0; 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:
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 Type of the thing. * @param type Thing type.
* @param allocator Allocator for the thing's data. * @param allocator Allocator for the thing's data.
*/ */
thing(thing_t type, const allocator_type& allocator = {}) thing(const thing_type& type, const allocator_type& allocator = {})
: m_type(type), m_allocator(allocator) { : m_type(type), m_size(compute_size(type)), m_allocator(allocator) {
m_data = m_allocator.allocate(thing_type_size(type)); if (m_type.type == thing_type::Ref) return;
// TODO: Account for alignment
m_data = m_allocator.allocate(m_size);
std::memset(m_data, 0, m_size);
} }
/** /**
* @brief Destructs a thing. * @brief Destructs a thing.
*/ */
~thing() { ~thing() {
if (m_data != nullptr) m_allocator.deallocate(m_data, thing_type_size(thing_t::Int32)); 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(other.m_type), m_data(other.m_data), m_allocator(std::move(other.m_allocator)) { : m_type(other.m_type), m_data(other.m_data), m_size(other.m_size), m_allocator(std::move(other.m_allocator)) {
other.m_type = {}; other.m_type.type = thing_type::Count;
other.m_data = nullptr; other.m_data = nullptr;
other.m_size = 0;
} }
/** /**
@@ -69,11 +241,12 @@ 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_allocator = std::move(other.m_allocator); m_allocator = std::move(other.m_allocator);
other.m_type = {}; other.m_type.type = thing_type::Count;
other.m_data = nullptr; other.m_data = nullptr;
other.m_size = 0;
return *this; return *this;
} }
@@ -86,33 +259,84 @@ public:
* @return A clone of this thing. * @return A clone of this thing.
*/ */
thing clone() const { thing clone() const {
class thing res(m_type, m_allocator); thing res(m_type, m_allocator);
switch (m_type) { switch (m_type.type) {
default: { case thing_type::S8:
std::memcpy(res.m_data, m_data, thing_type_size(m_type)); case thing_type::S16:
} break; 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;
} }
return std::move(res); throw std::runtime_error("unreachable");
} }
public: public:
/** /**
* @brief Returns the thing's int32 value. * @brief Returns the thing's type.
*
* @return The type.
*/
constexpr thing_type type() const { return m_type; }
/**
* @brief Returns the thing's true type.
*
* If the thing is a reference, returns the referenced type.
*
* @return The true type.
*/
constexpr thing_type true_type() const { return (m_type.type == thing_type::Ref) ? *m_type.value.typeRef : m_type; }
/**
* @brief Checks if the thing is of a specified type.
*
* Compares the true type.
*
* @param type Type to compare.
* @return true if the types match.
*/
constexpr bool is(enum thing_type::type type) const { return true_type().type == type; }
public:
/**
* @brief Returns a raw data pointer.
*
* @return The data pointer.
*/
void* raw() { return m_data; }
/**
* @brief Returns a raw data pointer.
*
* @return The data pointer.
*/
const void* raw() const { return m_data; }
public:
/**
* @brief Returns the thing's value.
* *
* @return The value. * @return The value.
*/ */
std::int32_t& int32() { template <typename T>
if (m_type != thing_t::Int32) throw bad_thing_access(); T& get() {
return *reinterpret_cast<std::int32_t*>(m_data); if (compute_size_na(m_type) != sizeof(T)) throw bad_thing_access();
return *std::launder(reinterpret_cast<T*>(m_data));
} }
/** /**
* @brief Returns the thing's int32 value. * @brief Returns the thing's value.
* *
* @return The value. * @return The value.
*/ */
const std::int32_t& int32() const { template <typename T>
if (m_type != thing_t::Int32) throw bad_thing_access(); const T& get() const {
return *reinterpret_cast<std::int32_t*>(m_data); if (compute_size_na(m_type) != sizeof(T)) throw bad_thing_access();
return *std::launder(reinterpret_cast<const T*>(m_data));
} }
public: public:
/** /**
@@ -121,11 +345,7 @@ public:
* @param rhs Thing to sum with this thing (right-hand-side). * @param rhs Thing to sum with this thing (right-hand-side).
* @return The sum. * @return The sum.
*/ */
thing add(const thing& rhs) const { thing add(const thing& rhs) const { return binary_op(rhs, std::plus<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() + rhs.int32();
return res;
}
/** /**
* @brief Returns a difference of two things. * @brief Returns a difference of two things.
@@ -133,11 +353,7 @@ public:
* @param rhs Thing to subtract from this thing (right-hand-side). * @param rhs Thing to subtract from this thing (right-hand-side).
* @return The sum. * @return The sum.
*/ */
thing sub(const thing& rhs) const { thing sub(const thing& rhs) const { return binary_op(rhs, std::minus<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() - rhs.int32();
return res;
}
/** /**
* @brief Returns a product of two things. * @brief Returns a product of two things.
@@ -145,11 +361,7 @@ public:
* @param rhs Thing to multiply with this thing (right-hand-side). * @param rhs Thing to multiply with this thing (right-hand-side).
* @return The product. * @return The product.
*/ */
thing mul(const thing& rhs) const { thing mul(const thing& rhs) const { return binary_op(rhs, std::multiplies<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() * rhs.int32();
return res;
}
/** /**
* @brief Returns a quotient of two things. * @brief Returns a quotient of two things.
@@ -157,11 +369,7 @@ public:
* @param rhs Thing to divide this thing by (right-hand-side). * @param rhs Thing to divide this thing by (right-hand-side).
* @return The quotient. * @return The quotient.
*/ */
thing div(const thing& rhs) const { thing div(const thing& rhs) const { return binary_op(rhs, std::divides<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() / rhs.int32();
return res;
}
/** /**
* @brief Returns a remainder of two things. * @brief Returns a remainder of two things.
@@ -169,11 +377,7 @@ public:
* @param rhs Thing to divide this thing by (right-hand-side). * @param rhs Thing to divide this thing by (right-hand-side).
* @return The remainder. * @return The remainder.
*/ */
thing mod(const thing& rhs) const { thing mod(const thing& rhs) const { return binary_op(rhs, std::modulus<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() % rhs.int32();
return res;
}
/** /**
* @brief Compares two things for equality. * @brief Compares two things for equality.
@@ -181,11 +385,7 @@ public:
* @param rhs Thing to compare this thing with (right-hand-side). * @param rhs Thing to compare this thing with (right-hand-side).
* @return A boolean result of the comparison in a thing form. * @return A boolean result of the comparison in a thing form.
*/ */
thing equals(const thing& rhs) const { thing equals(const thing& rhs) const { return binary_op(rhs, std::equal_to<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() == rhs.int32();
return res;
}
/** /**
* @brief Compares two things for inequality. * @brief Compares two things for inequality.
@@ -193,11 +393,7 @@ public:
* @param rhs Thing to compare this thing with (right-hand-side). * @param rhs Thing to compare this thing with (right-hand-side).
* @return A boolean result of the comparison in a thing form. * @return A boolean result of the comparison in a thing form.
*/ */
thing not_equals(const thing& rhs) const { thing not_equals(const thing& rhs) const { return binary_op(rhs, std::not_equal_to<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() != rhs.int32();
return res;
}
/** /**
* @brief Compares if this thing is less than an another thing. * @brief Compares if this thing is less than an another thing.
@@ -205,11 +401,7 @@ public:
* @param rhs The another thing. * @param rhs The another thing.
* @return A boolean result of the comparison in a thing form. * @return A boolean result of the comparison in a thing form.
*/ */
thing less_than(const thing& rhs) const { thing less_than(const thing& rhs) const { return binary_op(rhs, std::less<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() < rhs.int32();
return res;
}
/** /**
* @brief Compares if this thing is greater than an another thing. * @brief Compares if this thing is greater than an another thing.
@@ -217,11 +409,7 @@ public:
* @param rhs The another thing. * @param rhs The another thing.
* @return A boolean result of the comparison in a thing form. * @return A boolean result of the comparison in a thing form.
*/ */
thing greater_than(const thing& rhs) const { thing greater_than(const thing& rhs) const { return binary_op(rhs, std::greater<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() > rhs.int32();
return res;
}
/** /**
* @brief Compares if this thing is less than or equal to an another thing. * @brief Compares if this thing is less than or equal to an another thing.
@@ -229,11 +417,7 @@ public:
* @param rhs The another thing. * @param rhs The another thing.
* @return A boolean result of the comparison in a thing form. * @return A boolean result of the comparison in a thing form.
*/ */
thing less_equals(const thing& rhs) const { thing less_equals(const thing& rhs) const { return binary_op(rhs, std::less_equal<>{}); }
thing res(m_type, m_allocator);
res.int32() = int32() <= rhs.int32();
return res;
}
/** /**
* @brief Compares if this thing is greater than or equal to an another thing. * @brief Compares if this thing is greater than or equal to an another thing.
@@ -241,116 +425,321 @@ public:
* @param rhs The another thing. * @param rhs The another thing.
* @return A boolean result of the comparison in a thing form. * @return A boolean result of the comparison in a thing form.
*/ */
thing greater_equals(const thing& rhs) const { thing greater_equals(const thing& rhs) const { return binary_op(rhs, std::greater_equal<>{}); }
thing res(m_type, m_allocator); public:
res.int32() = int32() >= rhs.int32(); /**
return res; * @brief Returns a largest integer value of the thing.
*
* @return The integer value.
*/
thing_type::s64 integer() const {
switch (true_type().type) {
case thing_type::S8: return get<thing_type::s8>();
case thing_type::S16: return get<thing_type::s16>();
case thing_type::S32: return get<thing_type::s32>();
case thing_type::S64: return get<thing_type::s64>();
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");
}
}
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");
auto& array = get<union array>();
if (newSize < 0 || newSize == array.dynamic.size) return;
std::size_t innerSize = compute_size_na(*true_type().value.array.type);
std::byte* newData = new std::byte[innerSize * newSize];
std::memcpy(newData,
array.dynamic.data,
innerSize * std::min(static_cast<thing_type::u64>(array.dynamic.size), newSize));
array.dynamic.size = newSize;
delete[] array.dynamic.data;
array.dynamic.data = newData;
}
thing at(thing_type::u64 index) const {
if (!is(thing_type::Array)) throw bad_thing_access();
std::size_t elementSize = compute_size_na(*m_type.value.array.type);
if (m_type.value.array.size == 0) {
auto& array = get<union array>();
if (index < 0 || index >= array.dynamic.size) throw std::out_of_range("index out of range");
thing ref = { { thing_type::Ref, m_type.value.array.type }, m_allocator };
ref.m_data = array.dynamic.data + (index * elementSize);
return ref;
}
std::byte* data = reinterpret_cast<array*>(m_data)->flat;
if (index < 0 || index >= m_type.value.array.size) throw std::out_of_range("index out of range");
thing ref = { { thing_type::Ref, m_type.value.array.type }, m_allocator };
ref.m_data = data + (index * elementSize);
return ref;
}
thing_type::u64 length() const {
if (!is(thing_type::Array)) throw bad_thing_access();
return true_type().value.array.size == 0 ? get<array>().dynamic.size : true_type().value.array.size;
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
T cast_to() const {
return visit_primitive([](auto value) { return static_cast<T>(value); });
}
/**
* @brief Changes reference thing's referenced thing.
*
* Yes.
*
* @param thing Thing.
*/
void reference(const thing& thing) {
if (m_type.type != thing_type::Ref || *m_type.value.typeRef != thing.type()) throw bad_thing_access();
m_data = thing.m_data;
}
/**
* @brief Self-explainatory.
*
* TODO: Document
*/
void assign(thing&& thing) {
class thing rhs = std::move(thing);
if (true_type() != rhs.true_type()) throw std::runtime_error("thing type mismatch");
// TODO: Move this to another function
switch (true_type().type) {
case thing_type::S8:
case thing_type::S16:
case thing_type::S32:
case thing_type::S64:
case thing_type::U8:
case thing_type::U16:
case thing_type::U32:
case thing_type::U64: std::memcpy(m_data, rhs.m_data, m_size); return;
case thing_type::Ptr:
case thing_type::Ref:
case thing_type::Array: throw std::runtime_error("unimplemented");
case thing_type::Count: break;
}
throw std::runtime_error("unreachable");
} }
private: private:
thing_t m_type{}; static void copy_list(const thing_type& arrayType, array& dst, const array& src) {
std::byte* m_data; if (arrayType.type != thing_type::Array || arrayType.value.array.type == nullptr)
throw std::runtime_error("invalid type");
const auto& innerType = *arrayType.value.array.type;
std::size_t elementSize = compute_size_na(innerType);
std::byte* data = nullptr;
const std::byte* srcData = nullptr;
std::size_t size = 0;
if (arrayType.value.array.size == 0) {
size = dst.dynamic.size = src.dynamic.size;
if (dst.dynamic.size < 0) {
dst.dynamic.data = nullptr;
return;
}
srcData = src.dynamic.data;
data = dst.dynamic.data = new std::byte[dst.dynamic.size];
} else {
data = dst.flat;
srcData = src.flat;
size = arrayType.value.array.size;
}
switch (innerType.type) {
case thing_type::S8:
case thing_type::S16:
case thing_type::S32:
case thing_type::S64:
case thing_type::U8:
case thing_type::U16:
case thing_type::U32:
case thing_type::U64:
case thing_type::Ptr:
case thing_type::Ref: std::memcpy(dst.flat, src.flat, size); return;
case thing_type::Array:
for (std::size_t i = 0; i < size; ++i) {
copy_list(*innerType.value.array.type,
*std::launder(reinterpret_cast<array*>(data + (i * elementSize))),
*std::launder(reinterpret_cast<const array*>(srcData + (i * elementSize))));
}
return;
case thing_type::Count: break;
}
throw std::runtime_error("unreachable");
}
private:
static std::size_t compute_size_na(const thing_type& type) {
switch (type.type) {
case thing_type::S8: return sizeof(thing_type::s8);
case thing_type::S16: return sizeof(thing_type::s16);
case thing_type::S32: return sizeof(thing_type::s32);
case thing_type::S64: return sizeof(thing_type::s64);
case thing_type::U8: return sizeof(thing_type::u8);
case thing_type::U16: return sizeof(thing_type::u16);
case thing_type::U32: return sizeof(thing_type::u32);
case thing_type::U64: return sizeof(thing_type::u64);
case thing_type::Ptr: return sizeof(void*);
case thing_type::Ref: return compute_size_na(*type.value.typeRef);
case thing_type::Array:
return type.value.array.size == 0 ? sizeof(array)
: compute_size_na(*type.value.array.type) * type.value.array.size;
case thing_type::Count: break;
}
throw std::runtime_error("unreachable");
}
// NOTE: Align to 4 bytes
static std::size_t compute_size(const thing_type& type) { return (compute_size_na(type) + 3) & ~3; }
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>
thing binary_op(const thing& rhs, const Op& op) const {
if (thing_type::is_primitive(true_type().type) && thing_type::is_primitive(true_type().type)) {
static constexpr enum thing_type::type promotions[8 * 8] = {
// S8
thing_type::S8,
thing_type::S16,
thing_type::S32,
thing_type::S64,
thing_type::S16,
thing_type::S16,
thing_type::S32,
thing_type::U64,
// S16
thing_type::S16,
thing_type::S16,
thing_type::S32,
thing_type::S64,
thing_type::S16,
thing_type::S16,
thing_type::S32,
thing_type::U64,
// S32
thing_type::S32,
thing_type::S32,
thing_type::S32,
thing_type::S64,
thing_type::S32,
thing_type::S32,
thing_type::U32,
thing_type::U64,
// S64
thing_type::S64,
thing_type::S64,
thing_type::S64,
thing_type::S64,
thing_type::S64,
thing_type::S64,
thing_type::S64,
thing_type::U64,
// U8
thing_type::S16,
thing_type::S16,
thing_type::S32,
thing_type::S64,
thing_type::U8,
thing_type::U16,
thing_type::U32,
thing_type::U64,
// U16
thing_type::S16,
thing_type::S16,
thing_type::S32,
thing_type::S64,
thing_type::U16,
thing_type::U16,
thing_type::U32,
thing_type::U64,
// U32
thing_type::S32,
thing_type::S32,
thing_type::U32,
thing_type::S64,
thing_type::U32,
thing_type::U32,
thing_type::U32,
thing_type::U64,
// U64
thing_type::U64,
thing_type::U64,
thing_type::U64,
thing_type::U64,
thing_type::U64,
thing_type::U64,
thing_type::U64,
thing_type::U64,
};
enum thing_type::type resultType = promotions[true_type().type + (rhs.true_type().type * 8)];
thing res = { thing_type{ resultType }, m_allocator };
switch (resultType) {
case thing_type::S8:
res.get<thing_type::s8>() = Op{}(cast_to<thing_type::s8>(), rhs.cast_to<thing_type::s8>());
return res;
case thing_type::S16:
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;
}
throw std::runtime_error("unreachable");
}
throw std::runtime_error("unexpected operation");
}
private:
thing_type m_type;
std::size_t m_size;
std::byte* m_data;
allocator_type m_allocator; allocator_type m_allocator;
}; };
template <typename T>
class thing_allocator {
template <typename>
friend class thing_allocator;
using dead_things = std::vector<std::pair<T*, std::size_t>>;
public:
using value_type = T; /**< Value type. */
public:
/**
* @brief Constructs a thing allocator.
*
* @param arena Base arena allocator.
*/
explicit thing_allocator(furlang::arena& arena) noexcept
: m_arena(&arena), m_deadThings(std::make_shared<dead_things>()) {}
/**
* @brief Move constructor.
*/
template <typename U>
thing_allocator(thing_allocator<U>&& other) noexcept
: m_arena(std::move(other.m_arena)), m_deadThings(std::move(other.m_deadThings)) {}
/**
* @brief Move constructor.
*/
template <typename U>
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
if (this == &other) return *this;
m_arena = std::move(other.m_arena);
m_deadThings = std::move(other.m_deadThings);
return *this;
}
/**
* @brief Copy constructor.
*/
template <typename U>
thing_allocator(const thing_allocator<U>& other) noexcept
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
/**
* @brief Copy constructor.
*/
template <typename U>
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
if (this == &other) return *this;
m_arena = other.m_arena;
m_deadThings = other.m_deadThings;
return *this;
}
public:
/**
* @brief Returns a free chunk of memory.
*
* @param count Count of the things that must fit inside the chunk.
* @return The chunk.
*/
[[nodiscard]] T* allocate(std::size_t count = 1) {
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
if (it->second != count) continue;
m_deadThings->erase(it);
return it->first;
}
return m_arena->allocate<T>(count);
}
/**
* @brief Recycles the pointer.
*/
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings->emplace_back(ptr, count); }
public:
/**
* @brief Compares two thing allocators for equality.
*
* @return true if the two things are equal.
*/
template <typename U>
bool operator==(const thing_allocator<U>& other) const noexcept {
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
}
/**
* @brief Compares two thing allocators for inequality.
*
* @return true if the two things are not equal.
*/
template <typename U>
bool operator!=(const thing_allocator<U>& other) const noexcept {
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
}
private:
furlang::arena* m_arena;
std::shared_ptr<dead_things> m_deadThings;
};
} // namespace furvm } // namespace furvm
#endif // FURVM_THING_HPP #endif // FURVM_THING_HPP
+111
View File
@@ -0,0 +1,111 @@
#ifndef FURVM_THING_ALLOCATOR_HPP
#define FURVM_THING_ALLOCATOR_HPP
#include "furlang/arena.hpp"
#include <vector>
namespace furvm {
template <typename T>
class thing_allocator {
template <typename>
friend class thing_allocator;
using dead_things = std::vector<std::pair<T*, std::size_t>>;
public:
using value_type = T; /**< Value type. */
public:
/**
* @brief Constructs a thing allocator.
*
* @param arena Base arena allocator.
*/
explicit thing_allocator(furlang::arena& arena) noexcept
: m_arena(&arena), m_deadThings(std::make_shared<dead_things>()) {}
/**
* @brief Move constructor.
*/
template <typename U>
thing_allocator(thing_allocator<U>&& other) noexcept
: m_arena(std::move(other.m_arena)), m_deadThings(std::move(other.m_deadThings)) {}
/**
* @brief Move constructor.
*/
template <typename U>
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
if (this == &other) return *this;
m_arena = std::move(other.m_arena);
m_deadThings = std::move(other.m_deadThings);
return *this;
}
/**
* @brief Copy constructor.
*/
template <typename U>
thing_allocator(const thing_allocator<U>& other) noexcept
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
/**
* @brief Copy constructor.
*/
template <typename U>
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
if (this == &other) return *this;
m_arena = other.m_arena;
m_deadThings = other.m_deadThings;
return *this;
}
public:
/**
* @brief Returns a free chunk of memory.
*
* @param count Count of the things that must fit inside the chunk.
* @return The chunk.
*/
[[nodiscard]] T* allocate(std::size_t count = 1) {
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
if (it->second != count) continue;
T* data = it->first;
m_deadThings->erase(it);
return data;
}
return m_arena->allocate<T>(count);
}
/**
* @brief Recycles the pointer.
*/
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings->emplace_back(ptr, count); }
public:
/**
* @brief Compares two thing allocators for equality.
*
* @return true if the two things are equal.
*/
template <typename U>
bool operator==(const thing_allocator<U>& other) const noexcept {
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
}
/**
* @brief Compares two thing allocators for inequality.
*
* @return true if the two things are not equal.
*/
template <typename U>
bool operator!=(const thing_allocator<U>& other) const noexcept {
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
}
private:
furlang::arena* m_arena;
std::shared_ptr<dead_things> m_deadThings;
};
} // namespace furvm
#endif // FURVM_THING_ALLOCATOR_HPP
-10
View File
@@ -1,10 +0,0 @@
#include "furvm/context.hpp"
#include "furvm/thing.hpp" // IWYU pragma: keep
namespace furvm {
context::context()
: m_thingAllocator(m_thingArena) {}
} // namespace furvm
+93 -15
View File
@@ -1,49 +1,127 @@
#include "furvm/detail/serialization.hpp" #include "furvm/detail/serialization.hpp"
#include <istream>
#include <ostream> #include <ostream>
namespace furvm::detail { namespace furvm::detail {
namespace {
template <typename T, std::size_t... I>
void serialize_integral_impl(std::ostream& os, std::make_unsigned_t<T> u_value, std::index_sequence<I...>) {
((os << static_cast<char>((u_value >> (I * 8)) & 0xFF)), ...);
}
template <typename T, std::size_t... I>
void load_integral_impl(const char* bs, std::make_unsigned_t<T>& u_value, std::index_sequence<I...>) {
using UnsignedT = std::make_unsigned_t<T>;
((u_value |= (static_cast<UnsignedT>(static_cast<std::uint8_t>(bs[I])) << (I * 8))), ...);
}
template <typename T>
std::ostream& serialize_integral(std::ostream& os, T value) {
static_assert(std::is_integral_v<T>, "Type must be an integral type.");
using UnsignedT = std::make_unsigned_t<T>;
serialize_integral_impl<T>(os, static_cast<UnsignedT>(value), std::make_index_sequence<sizeof(T)>{});
return os;
}
template <typename T>
std::istream& load_integral(std::istream& is, T& value) {
static_assert(std::is_integral_v<T>, "Type must be an integral type.");
char bs[sizeof(T)];
if (!is.read(bs, sizeof(T))) {
return is;
}
using UnsignedT = std::make_unsigned_t<T>;
UnsignedT uVal = 0;
load_integral_impl<T>(bs, uVal, std::make_index_sequence<sizeof(T)>{});
value = static_cast<T>(uVal);
return is;
}
} // namespace
std::ostream& serialize(std::ostream& os, std::int8_t value) { std::ostream& serialize(std::ostream& os, std::int8_t value) {
return os << (char)((value >> 0ULL) & 0xFF); return serialize_integral(os, value);
} }
std::ostream& serialize(std::ostream& os, std::int16_t value) { std::ostream& serialize(std::ostream& os, std::int16_t value) {
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF); return serialize_integral(os, value);
} }
std::ostream& serialize(std::ostream& os, std::int32_t value) { std::ostream& serialize(std::ostream& os, std::int32_t value) {
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) return serialize_integral(os, value);
<< (char)((value >> 24ULL) & 0xFF);
} }
std::ostream& serialize(std::ostream& os, std::int64_t value) { std::ostream& serialize(std::ostream& os, std::int64_t value) {
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) return serialize_integral(os, value);
<< (char)((value >> 24ULL) & 0xFF) << (char)((value >> 32ULL) & 0xFF) << (char)((value >> 40ULL) & 0xFF)
<< (char)((value >> 48ULL) & 0xFF) << (char)((value >> 56ULL) & 0xFF);
} }
std::ostream& serialize(std::ostream& os, std::uint8_t value) { std::ostream& serialize(std::ostream& os, std::uint8_t value) {
return os << (char)((value >> 0ULL) & 0xFF); return serialize_integral(os, value);
} }
std::ostream& serialize(std::ostream& os, std::uint16_t value) { std::ostream& serialize(std::ostream& os, std::uint16_t value) {
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF); return serialize_integral(os, value);
} }
std::ostream& serialize(std::ostream& os, std::uint32_t value) { std::ostream& serialize(std::ostream& os, std::uint32_t value) {
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) return serialize_integral(os, value);
<< (char)((value >> 24ULL) & 0xFF);
} }
std::ostream& serialize(std::ostream& os, std::uint64_t value) { std::ostream& serialize(std::ostream& os, std::uint64_t value) {
return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) return serialize_integral(os, value);
<< (char)((value >> 24ULL) & 0xFF) << (char)((value >> 32ULL) & 0xFF) << (char)((value >> 40ULL) & 0xFF)
<< (char)((value >> 48ULL) & 0xFF) << (char)((value >> 56ULL) & 0xFF);
} }
std::ostream& serialize(std::ostream& os, const std::string& value) { std::ostream& serialize(std::ostream& os, const std::string& value) {
return serialize(os, std::uint16_t(value.size())).write(value.data(), static_cast<std::streamsize>(value.size())); return serialize(os, static_cast<std::uint16_t>(value.size()))
.write(value.data(), static_cast<std::streamsize>(value.size()));
}
std::istream& load(std::istream& is, std::int8_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::int16_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::int32_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::int64_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::uint8_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::uint16_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::uint32_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::uint64_t& value) {
return load_integral(is, value);
}
std::istream& load(std::istream& is, std::string& value) {
std::uint16_t length = 0;
load(is, length);
value.resize(length);
return is.read(value.data(), static_cast<std::streamsize>(length));
} }
} // namespace furvm::detail } // namespace furvm::detail
+175 -25
View File
@@ -7,20 +7,73 @@
#include "furvm/instruction.hpp" #include "furvm/instruction.hpp"
#include "furvm/thing.hpp" #include "furvm/thing.hpp"
#include <cassert>
#include <cstdint> #include <cstdint>
#include <stdexcept> #include <stdexcept>
#include <utility>
#include <vector>
namespace furvm { namespace furvm {
void executor::push_frame(const mod_h& mod, function function) { thing_type executor::thing_type_impl(mod_h mod, mod_type type) const {
while (function.type() == function_t::Import) { while (type.type == mod_type::Import) {
function = *m_context->module_at(function.imp().mod)->function_at(function.imp().function); 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) {
mod_h modInst = mod;
while (function.type() == function_t::Import) {
modInst = m_context->at(function.imp().mod);
function = *modInst->function_at(function.imp().function);
}
auto signature = function.signature();
std::vector<thing_h> args;
args.reserve(signature.params.size());
for (const auto& param : signature.params) {
auto arg = pop_thing();
if (arg->type() != *thing_type(mod, *param)) throw std::runtime_error("function argument type mismatch");
args.push_back(std::move(arg));
}
struct thing_type* returnType = nullptr;
if (function.signature().returnType.has_value())
returnType = thing_type(mod, *function.signature().returnType.value()); // NOLINT
switch (function.type()) { switch (function.type()) {
case function_t::Normal: { case function_t::Normal: {
m_frames.emplace((struct executor::frame){ mod, function.position(), m_stack.size() }); m_frames.emplace(
(struct executor::frame){ mod, function.position(), m_stack.size(), returnType, std::move(args) });
} break;
case function_t::Native: {
m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), returnType, std::move(args) });
modInst->get_native_function(function.native())(*this);
pop_frame();
} break; } break;
case function_t::Native:
default: throw std::runtime_error("unexpected function type"); default: throw std::runtime_error("unexpected function type");
} }
} }
@@ -29,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;
} }
@@ -44,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 {
@@ -54,19 +111,19 @@ thing_h executor::thing() const {
void executor::store_thing(variable_t variable, const thing_h& thing) { void executor::store_thing(variable_t variable, const thing_h& thing) {
auto& frame = m_frames.top(); auto& frame = m_frames.top();
frame.variables.resize(variable + 1); if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
frame.variables[variable] = thing; frame.variables[variable] = thing;
} }
void executor::store_thing(variable_t variable, thing_h&& thing) { void executor::store_thing(variable_t variable, thing_h&& thing) {
auto& frame = m_frames.top(); auto& frame = m_frames.top();
frame.variables.resize(variable + 1); if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
frame.variables[variable] = std::move(thing); frame.variables[variable] = std::move(thing);
} }
thing_h executor::load_thing(variable_t variable) const { thing_h executor::load_thing(variable_t variable) const {
const auto& frame = m_frames.top(); const auto& frame = m_frames.top();
return frame.variables[variable]; return { frame.variables[variable] };
} }
void executor::step() { void executor::step() {
@@ -77,8 +134,63 @@ void executor::step() {
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte(frame.position++)); instruction_t instr = static_cast<instruction_t>((*frame.mod).byte(frame.position++));
switch (instr) { switch (instr) {
case instruction_t::NoOperation: break; case instruction_t::NoOperation: break;
case instruction_t::PushB2I: { case instruction_t::PushS8: {
push_thing({ thing_t::Int32, m_context->thing_alloc() })->int32() = frame.mod->byte(frame.position++); push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() })->get<thing_type::s8>() =
static_cast<thing_type::s8>(frame.mod->byte(frame.position++));
} break;
case instruction_t::PushU8: {
push_thing({ (struct thing_type){ thing_type::U8 }, m_context->thing_alloc() })->get<thing_type::u8>() =
static_cast<thing_type::u8>(frame.mod->byte(frame.position++));
} break;
case instruction_t::PushS16: {
thing_type::u16 value = frame.mod->byte(frame.position++);
value |= static_cast<thing_type::u16>(frame.mod->byte(frame.position++) << 8);
push_thing({ (struct thing_type){ thing_type::S16 }, m_context->thing_alloc() })->get<thing_type::s16>() =
static_cast<thing_type::s16>(value);
} break;
case instruction_t::PushU16: {
thing_type::u16 value = frame.mod->byte(frame.position++);
value |= static_cast<thing_type::u16>(frame.mod->byte(frame.position++) << 8);
push_thing({ (struct thing_type){ thing_type::U16 }, m_context->thing_alloc() })->get<thing_type::u16>() =
value;
} break;
case instruction_t::PushS32: {
push_thing({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() })->get<thing_type::s32>() =
static_cast<thing_type::s32>(frame.mod->byte(frame.position++));
} break;
case instruction_t::PushU32: {
push_thing({ (struct thing_type){ thing_type::U32 }, m_context->thing_alloc() })->get<thing_type::u32>() =
static_cast<thing_type::u32>(frame.mod->byte(frame.position++));
} break;
case instruction_t::Array: {
mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte(frame.position)) |
(static_cast<mod_type_id>(frame.mod->byte(frame.position + 1)) << 8) |
(static_cast<mod_type_id>(frame.mod->byte(frame.position + 2)) << 16) |
(static_cast<mod_type_id>(frame.mod->byte(frame.position + 3)) << 24);
frame.position += 4;
const auto& type = *thing_type(frame.mod, *frame.mod->type_at(typeId));
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
throw std::runtime_error("invalid array type");
auto array = push_thing({ type, m_context->thing_alloc() });
if (type.value.array.size == 0) {
auto sizeThing = pop_thing();
std::int64_t size = sizeThing->integer();
array->resize(size);
}
} break;
case instruction_t::Get: {
auto index = pop_thing();
auto array = pop_thing();
push_thing(array->at(index->integer()));
} break;
case instruction_t::Set: {
auto element = pop_thing();
auto index = pop_thing();
auto array = pop_thing();
array->at(index->integer()).assign(std::move(element->clone()));
} break; } break;
case instruction_t::Drop: { case instruction_t::Drop: {
pop_thing(); pop_thing();
@@ -86,9 +198,21 @@ void executor::step() {
case instruction_t::Duplicate: { case instruction_t::Duplicate: {
push_thing(thing()); push_thing(thing());
} break; } break;
case instruction_t::Swap: {
auto thing1 = pop_thing();
auto thing2 = pop_thing();
push_thing(std::move(thing1));
push_thing(std::move(thing2));
} break;
case instruction_t::Clone: { case instruction_t::Clone: {
push_thing(std::move(thing()->clone())); push_thing(std::move(thing()->clone()));
} break; } break;
case instruction_t::Reference: {
auto thing = pop_thing();
push_thing({ (struct thing_type){ thing_type::Ref, m_context->thing_type_store().insert(thing->type()) },
m_context->thing_alloc() })
->reference(*thing);
} break;
case instruction_t::Add: { case instruction_t::Add: {
auto rhs = pop_thing(); auto rhs = pop_thing();
auto lhs = pop_thing(); auto lhs = pop_thing();
@@ -144,6 +268,42 @@ void executor::step() {
auto lhs = pop_thing(); auto lhs = pop_thing();
push_thing(lhs->greater_equals(*rhs)); push_thing(lhs->greater_equals(*rhs));
} break; } break;
case instruction_t::Pointerof: {
auto thing = pop_thing();
auto ptr =
push_thing({ (struct thing_type){ thing_type::Ptr, m_context->thing_type_store().at(thing->type().id) },
m_context->thing_alloc() });
ptr->get<void*>() = thing->raw();
} break;
case instruction_t::Sizeof: {
auto thing = pop_thing();
auto size = push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() });
switch (thing->type().type) {
case thing_type::S8:
case thing_type::S16:
case thing_type::S32:
case thing_type::S64:
case thing_type::U8:
case thing_type::U16:
case thing_type::U32:
case thing_type::U64:
size->get<thing_type::u64>() = static_cast<thing_type::u64>(thing_type::primitive_size(thing->type().type));
break;
case thing_type::Ptr: size->get<thing_type::u64>() = static_cast<thing_type::u64>(sizeof(void*)); break;
case thing_type::Array:
/* TODO: Return actual memory size of the array
* By the memory size I mean the length times sizeof single element.
*/
size->get<thing_type::u64>() = thing->length();
break;
default: throw std::runtime_error("unreachable");
}
} break;
case instruction_t::Lengthof: {
auto thing = pop_thing();
auto length = push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() });
length->get<thing_type::u64>() = thing->length();
} break;
case instruction_t::Load: { case instruction_t::Load: {
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) | variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8); (static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
@@ -160,31 +320,21 @@ void executor::step() {
function_id funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) | function_id funcId = 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);
frame.position += 2; frame.position += 2;
push_frame(frame.mod, *frame.mod->function_at(funcId));
const function_h& function = frame.mod->function_at(funcId);
switch (function->type()) {
case function_t::Normal:
case function_t::Import: push_frame(frame.mod, *function); break;
case function_t::Native: function->native()(*this); break;
}
} break; } break;
case instruction_t::Jump: { case instruction_t::Jump: {
frame.position += frame.mod->byte(frame.position++); 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->int32() != 0) frame.position += 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");
} }
+30 -11
View File
@@ -1,14 +1,21 @@
#include "furvm/function.hpp" #include "furvm/function.hpp"
#include "furvm/furvm.hpp"
#include <utility> #include <utility>
namespace furvm { namespace furvm {
function::function(const mod_h& mod, const function_h& function)
: m_type(function_t::Import), m_value(import_function{ mod.id(), function.id() }) {}
function::~function() { function::~function() {
switch (m_type) { switch (m_type) {
case function_t::Normal: case function_t::Normal:
case function_t::Native:
default: break; default: break;
case function_t::Native: {
m_value.native.~native_function();
} break;
case function_t::Import: { case function_t::Import: {
m_value.imp.~import_function(); m_value.imp.~import_function();
} break; } break;
@@ -16,7 +23,7 @@ function::~function() {
} }
function::function(function&& other) noexcept function::function(function&& other) noexcept
: m_name(std::move(other.m_name)), m_type(other.m_type) { : m_type(other.m_type), m_signature(std::move(other.m_signature)) {
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -25,8 +32,9 @@ function::function(function&& other) noexcept
new (&m_value.native) native_function(std::move(other.m_value.native)); new (&m_value.native) native_function(std::move(other.m_value.native));
} break; } break;
case function_t::Import: { case function_t::Import: {
m_value.imp = std::move(other.m_value.imp); new (&m_value.imp) import_function(std::move(other.m_value.imp));
} break; } break;
default: break;
} }
other.m_value.position = 0; other.m_value.position = 0;
} }
@@ -34,8 +42,8 @@ function::function(function&& other) noexcept
function& function::operator=(function&& other) noexcept { function& function::operator=(function&& other) noexcept {
if (this == &other) return *this; if (this == &other) return *this;
m_name = std::move(other.m_name); m_type = other.m_type;
m_type = other.m_type; m_signature = other.m_signature;
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -44,8 +52,9 @@ function& function::operator=(function&& other) noexcept {
new (&m_value.native) native_function(std::move(other.m_value.native)); new (&m_value.native) native_function(std::move(other.m_value.native));
} break; } break;
case function_t::Import: { case function_t::Import: {
m_value.imp = std::move(other.m_value.imp); new (&m_value.imp) import_function(std::move(other.m_value.imp));
} break; } break;
default: break;
} }
other.m_value.position = 0; other.m_value.position = 0;
@@ -53,7 +62,7 @@ function& function::operator=(function&& other) noexcept {
} }
function::function(const function& other) function::function(const function& other)
: m_name(other.m_name), m_type(other.m_type) { : m_type(other.m_type), m_signature(other.m_signature) {
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -62,16 +71,17 @@ function::function(const function& other)
new (&m_value.native) native_function(other.m_value.native); new (&m_value.native) native_function(other.m_value.native);
} break; } break;
case function_t::Import: { case function_t::Import: {
m_value.imp = other.m_value.imp; new (&m_value.imp) import_function(other.m_value.imp);
} break; } break;
default: break;
} }
} }
function& function::operator=(const function& other) { function& function::operator=(const function& other) {
if (this == &other) return *this; if (this == &other) return *this;
m_name = other.m_name; m_type = other.m_type;
m_type = other.m_type; m_signature = other.m_signature;
switch (m_type) { switch (m_type) {
case function_t::Normal: { case function_t::Normal: {
m_value.position = other.m_value.position; m_value.position = other.m_value.position;
@@ -80,11 +90,20 @@ function& function::operator=(const function& other) {
new (&m_value.native) native_function(other.m_value.native); new (&m_value.native) native_function(other.m_value.native);
} break; } break;
case function_t::Import: { case function_t::Import: {
m_value.imp = other.m_value.imp; new (&m_value.imp) import_function(other.m_value.imp);
} break; } break;
default: break;
} }
return *this; return *this;
} }
namespace detail {
std::size_t function_sig_hash::operator()(const function_sig& signature) const {
return furlang::utility::vector_hash<mod_type_h, detail::handle_hash<mod_type_h>>{}(signature.params);
}
} // namespace detail
} // namespace furvm } // namespace furvm
+68 -14
View File
@@ -1,5 +1,6 @@
#ifndef LIBFURVM #ifndef LIBFURVM
#include "furvm/detail/serialization.hpp"
#include "furvm/furvm.hpp" #include "furvm/furvm.hpp"
#include <array> #include <array>
@@ -7,27 +8,80 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <sstream>
static constexpr std::array<furvm::byte, 8> s_bytecode = { static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) {
furvm::byte(furvm::instruction_t::PushB2I), using namespace furvm;
67,
furvm::byte(furvm::instruction_t::Clone),
furvm::byte(furvm::instruction_t::Add),
furvm::byte(furvm::instruction_t::Return),
};
int main(void) { switch (thing.true_type().type) {
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break;
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;
case thing_type::S64: std::cout << thing.get<thing_type::s64>(); break;
case thing_type::U8: std::cout << thing.get<thing_type::u8>(); break;
case thing_type::U16: std::cout << thing.get<thing_type::u16>(); break;
case thing_type::U32: std::cout << thing.get<thing_type::u32>(); break;
case thing_type::U64: std::cout << thing.get<thing_type::u64>(); break;
case thing_type::Array:
std::cout << "{ ";
for (thing_type::u64 i = 0; i < thing.length(); ++i) {
if (i > 0) std::cout << ", ";
print_thing(thing.at(i));
}
std::cout << " }";
break;
default: std::cerr << "{Type not recognized}";
}
}
int main(int argc, char** argv) {
auto context = std::make_shared<furvm::context>(); auto context = std::make_shared<furvm::context>();
furvm::mod_h mainModule = context->emplace_module("main", s_bytecode.begin(), s_bytecode.end()); #if 1 // NOLINT
furvm::function_h mainFunc = mainModule->emplace_function("main", 0); if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <mod.fmod>\n";
return 1;
}
mainModule->serialize(std::cout); std::ifstream file(argv[1]);
if (!file.is_open()) {
std::cerr << "Failed to open " << argv[1] << '\n';
return 1;
}
furvm::mod_h mod = context->emplace("main", std::move(furvm::mod::load(file)));
auto mainFunc = mod->function_at("main", furvm::function_sig{});
#else
static const furvm::byte s_bytecode[] = {
static_cast<furvm::byte>(furvm::instruction_t::Array),
1,
0,
0,
0,
static_cast<furvm::byte>(furvm::instruction_t::Reference),
static_cast<furvm::byte>(furvm::instruction_t::Lengthof),
static_cast<furvm::byte>(furvm::instruction_t::Call),
1,
0,
static_cast<furvm::byte>(furvm::instruction_t::Drop),
static_cast<furvm::byte>(furvm::instruction_t::Return),
};
auto mod = context->emplace("main", s_bytecode, s_bytecode + sizeof(s_bytecode));
auto charType = mod->emplace_type(furvm::mod_type::S8);
auto arrayType = mod->emplace_type(charType.id(), 10);
auto u64Type = mod->emplace_type(furvm::mod_type::U64);
auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0);
mod->emplace_function(furvm::function_sig{ { u64Type }, u64Type }, "print").dispatch();
#endif
mod->set_native_function("println", [](furvm::executor& executor) {
auto arg = executor.load_thing(0);
print_thing(*arg);
std::cout << '\n';
});
furvm::executor_h executor = context->emplace_executor(context); furvm::executor_h executor = context->emplace_executor(context);
executor->push_frame(mainModule, *mainFunc); executor->push_frame(mod, *mainFunc);
static constexpr std::size_t FPC = 3; // Frames per collection
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) { while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
executor->step(); executor->step();
+174 -5
View File
@@ -1,26 +1,86 @@
#include "furvm/module.hpp" #include "furvm/module.hpp"
#include "furvm/detail/serialization.hpp" #include "furvm/detail/serialization.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include <cstdint>
#include <cstring>
#include <ios> #include <ios>
#include <stdexcept>
#include <utility>
namespace furvm { namespace furvm {
std::ostream& mod::serialize(std::ostream& os) const { std::ostream& mod::serialize(std::ostream& os) const {
os << MAGIC; os.write(MAGIC, sizeof(MAGIC));
detail::serialize(os, std::uint32_t(0)); // version detail::serialize(os, std::uint32_t(0)); // version
detail::serialize(os, function_id(m_functionMap.size())); mod_type_id typeCount = mod_type_id(m_types.cend() - m_types.cbegin());
for (const auto& [name, id] : m_functionMap) { detail::serialize(os, typeCount);
detail::serialize(os, name); for (mod_type_id id = 0; id < typeCount; ++id) {
if (!m_types.contains(id)) {
detail::serialize(os, std::uint8_t(0xFF)); // null type
continue;
}
auto type = *m_types.at(id);
detail::serialize(os, static_cast<std::uint8_t>(type.type));
switch (type.type) {
case mod_type::S8:
case mod_type::S16:
case mod_type::S32:
case mod_type::S64:
case mod_type::U8:
case mod_type::U16:
case mod_type::U32:
case mod_type::U64: break;
case mod_type::Ptr:
case mod_type::Ref: detail::serialize(os, type.value.typeRef); break;
case mod_type::Array: {
detail::serialize(os, type.value.array.typeId);
detail::serialize(os, type.value.array.size);
} break;
case mod_type::Import: {
detail::serialize(os, type.value.imprt.modId);
detail::serialize(os, type.value.imprt.typeId);
} break;
case mod_type::Count: throw std::runtime_error("unreachable");
}
}
function_id funcCount = m_functions.cend() - m_functions.cbegin();
detail::serialize(os, funcCount);
for (function_id id = 0; id < funcCount; ++id) {
if (!m_functions.contains(id)) {
detail::serialize(os, "");
detail::serialize(os, 0);
detail::serialize(os, std::uint8_t(0xFF)); // null function
continue;
}
// Function name
function_h func = m_functions.at(id); function_h func = m_functions.at(id);
if (auto it = m_functionMap.find(func.id()); it != m_functionMap.end()) {
detail::serialize(os, it->second.first);
} else {
detail::serialize(os, "");
}
// Function signature
detail::serialize(os, static_cast<std::uint32_t>(func->signature().params.size()));
for (std::uint32_t i = 0; i < func->signature().params.size(); ++i)
detail::serialize(os, func->signature().params[i].id());
// Function body/value
detail::serialize(os, std::uint8_t(func->type())); detail::serialize(os, std::uint8_t(func->type()));
switch (func->type()) { switch (func->type()) {
case function_t::Normal: { case function_t::Normal: {
detail::serialize(os, func->position()); detail::serialize(os, func->position());
} break; } break;
case function_t::Native: break; case function_t::Native: {
detail::serialize(os, func->native());
} break;
case function_t::Import: { case function_t::Import: {
detail::serialize(os, func->imp().mod); detail::serialize(os, func->imp().mod);
detail::serialize(os, func->imp().function); detail::serialize(os, func->imp().function);
@@ -31,4 +91,113 @@ std::ostream& mod::serialize(std::ostream& os) const {
detail::serialize(os, std::uint64_t(m_bytecode.size())); detail::serialize(os, std::uint64_t(m_bytecode.size()));
return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size())); return os.write(reinterpret_cast<const char*>(m_bytecode.data()), static_cast<std::streamsize>(m_bytecode.size()));
} }
mod mod::load(std::istream& is) {
std::uint32_t magic = 0;
detail::load(is, magic);
std::uint32_t version = 0;
detail::load(is, version);
if (std::memcmp(&magic, MAGIC, sizeof(MAGIC)) != 0) throw std::runtime_error("invalid magic");
if (version != 0) throw std::runtime_error("unsupported version");
mod mod;
mod_type_id typeCount = 0;
detail::load(is, typeCount);
for (mod_type_id id = 0; id < typeCount; ++id) {
std::uint8_t type = 0;
detail::load(is, type);
if (type == 0xFF) continue;
switch ((enum mod_type::type)type) {
case mod_type::S8:
case mod_type::S16:
case mod_type::S32:
case mod_type::S64:
case mod_type::U8:
case mod_type::U16:
case mod_type::U32:
case mod_type::U64: {
mod_type theType = { (enum mod_type::type)type };
mod.emplace_type(id, theType).dispatch();
} break;
case mod_type::Ptr: {
mod_type_id typeId = 0;
detail::load(is, typeId);
mod.emplace_type(id, typeId).dispatch();
} break;
case mod_type::Array: {
mod_type_id typeId = 0;
detail::load(is, typeId);
std::size_t size = 0;
detail::load(is, size);
mod.emplace_type(id, typeId, size).dispatch();
} break;
case mod_type::Import: {
std::string modName;
detail::load(is, modName);
mod_type_id typeId = 0;
detail::load(is, typeId);
mod.emplace_type(id, std::move(modName), typeId).dispatch();
} break;
default: throw std::runtime_error("unknown type type");
}
}
function_id functionCount = 0;
detail::load(is, functionCount);
for (function_id id = 0; id < functionCount; ++id) {
std::string name;
detail::load(is, name);
function_sig signature;
std::uint32_t paramCount = 0;
detail::load(is, paramCount);
signature.params.reserve(paramCount);
while (signature.params.size() < paramCount) {
thing_id param{ 0 };
detail::load(is, param);
signature.params.emplace_back(mod.type_at(param));
}
std::uint8_t type = 0;
detail::load(is, type);
if (type == 0xFF) continue;
switch ((function_t)type) {
case function_t::Normal: {
decltype(std::declval<function>().position()) offset = 0;
detail::load(is, offset);
if (name.empty())
mod.emplace_function(id, std::move(signature), offset).dispatch();
else
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
} break;
case function_t::Native: {
std::string native;
detail::load(is, native);
if (name.empty())
mod.emplace_function(id, std::move(signature), std::move(native)).dispatch();
else
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
} break;
case function_t::Import: {
std::string modName;
detail::load(is, modName);
function_id function = 0;
detail::load(is, function);
mod.emplace_function(id, std::move(modName), function).dispatch();
} break;
default: throw std::runtime_error("unknown function type");
}
}
std::uint64_t bytecodeLength = 0;
detail::load(is, bytecodeLength);
mod.bytecode().resize(bytecodeLength);
is.read(reinterpret_cast<char*>(mod.bytecode().data()), static_cast<std::streamsize>(bytecodeLength));
return mod;
}
} // namespace furvm } // namespace furvm
+26 -1
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