Compare commits

...

4 Commits

12 changed files with 741 additions and 39 deletions
+26
View File
@@ -0,0 +1,26 @@
type arr = array $s8 11
func println $arr = native println
public func main = #main
main:
array $arr
store %0
push $s32 0
loop:
dup
push $s32 10
eq
jnz #end
body:
dup
load %0
push $s8 69
set
jmp #loop
end:
drop
load %0
call $arr println
ret
+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
+8 -2
View File
@@ -18,9 +18,14 @@ struct token {
Sha256, /**< Label marker(`#`). */ Sha256, /**< Label marker(`#`). */
Percent, /**< Variable marker(`%`). The more the better. */ Percent, /**< Variable marker(`%`). The more the better. */
Dot, /**< . */ EqSign, /**< `=` */
Dot, /**< . */
Colon, /**< `:` */
// Keywords // 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. */ Import, /**< `import` keyword for importing functions and types. */
Public, /**< `public` access specifier. */ Public, /**< `public` access specifier. */
Private, /**< `private` access specifier. */ Private, /**< `private` access specifier. */
@@ -29,6 +34,7 @@ struct token {
Push, Push,
Array, Array,
Get, Get,
Set,
Drop, Drop,
Dup, Dup,
Clone, Clone,
@@ -50,7 +56,7 @@ struct token {
Load, Load,
Store, Store,
Call, Call,
Jump, Jmp,
Jnz, Jnz,
Ret, Ret,
+546
View File
@@ -0,0 +1,546 @@
#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::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::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) };
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));
} break;
case furvm::mod_type::U8: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU8));
} break;
case furvm::mod_type::S16: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS16));
} break;
case furvm::mod_type::U16: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU16));
} break;
case furvm::mod_type::S32: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS32));
} break;
case furvm::mod_type::U32: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU32));
} 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::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
+7 -1
View File
@@ -33,6 +33,9 @@ token_r lexer::next_token() {
} }
static std::unordered_map<std::string_view, enum token::type> s_tokens = { static std::unordered_map<std::string_view, enum token::type> s_tokens = {
{ "func", token::Func },
{ "type", token::Type },
{ "native", token::Native },
{ "import", token::Import }, { "import", token::Import },
{ "public", token::Public }, { "public", token::Public },
{ "private", token::Private }, { "private", token::Private },
@@ -40,6 +43,7 @@ token_r lexer::next_token() {
{ "push", token::Push }, { "push", token::Push },
{ "array", token::Array }, { "array", token::Array },
{ "get", token::Get }, { "get", token::Get },
{ "set", token::Set },
{ "drop", token::Drop }, { "drop", token::Drop },
{ "dup", token::Dup }, { "dup", token::Dup },
{ "clone", token::Clone }, { "clone", token::Clone },
@@ -61,7 +65,7 @@ token_r lexer::next_token() {
{ "load", token::Load }, { "load", token::Load },
{ "store", token::Store }, { "store", token::Store },
{ "call", token::Call }, { "call", token::Call },
{ "jump", token::Jump }, { "jmp", token::Jmp },
{ "jnz", token::Jnz }, { "jnz", token::Jnz },
{ "ret", token::Ret }, { "ret", token::Ret },
}; };
@@ -80,7 +84,9 @@ token_r lexer::next_token() {
case '$': ++m_cursor; return { token::Dolar }; case '$': ++m_cursor; return { token::Dolar };
case '#': ++m_cursor; return { token::Sha256 }; case '#': ++m_cursor; return { token::Sha256 };
case '%': ++m_cursor; return { token::Percent }; case '%': ++m_cursor; return { token::Percent };
case '=': ++m_cursor; return { token::EqSign };
case '.': ++m_cursor; return { token::Dot }; case '.': ++m_cursor; return { token::Dot };
case ':': ++m_cursor; return { token::Colon };
default: default:
return token_r{ return token_r{
lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" } lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" }
+31 -10
View File
@@ -1,18 +1,39 @@
#include "furas/gen.hpp"
#include "furas/lexer.hpp" #include "furas/lexer.hpp"
#include <fstream>
#include <furvm/module.hpp>
#include <iostream> #include <iostream>
int main(void) { // NOLINT int main(int argc, char** argv) { // NOLINT
std::string source = "%uwu 10 public private int le"; if (argc < 2) {
furas::lexer lexer("<NONE>", source); std::cerr << "feed me more arguments daddy >_<\n";
while (true) { return 1;
furas::token_r token = lexer.next_token();
if (token.has_error()) {
std::cout << token.error().message << '\n';
break;
}
std::cout << token->type << '\n';
} }
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; return 0;
} }
+1 -1
View File
@@ -170,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:
+32 -4
View File
@@ -12,11 +12,34 @@ enum class instruction_t : byte {
NoOperation = 0, NoOperation = 0,
/** /**
* @brief Pushes an integer from a byte onto the stack. * @brief Pushes an s8 integer from a byte onto the stack.
*
* Pushes an integer constructed from a next byte onto the stack.
*/ */
PushB2I, PushS8,
/**
* @brief Pushes an u8 integer from a byte onto the stack.
*/
PushU8,
/**
* @brief Pushes an s16 integer from two byte onto the stack.
*/
PushS16,
/**
* @brief Pushes an u16 integer from two byte onto the stack.
*/
PushU16,
/**
* @brief Pushes an s32 integer from a byte onto the stack.
*/
PushS32,
/**
* @brief Pushes an u32 integer from a byte onto the stack.
*/
PushU32,
/** /**
* @brief Pushes a constant onto the stack. * @brief Pushes a constant onto the stack.
@@ -38,6 +61,11 @@ enum class instruction_t : byte {
*/ */
Get, Get,
/**
* @brief Sets an array element.
*/
Set,
/** /**
* @brief Pops top element from the stack. * @brief Pops top element from the stack.
*/ */
+5 -5
View File
@@ -433,7 +433,7 @@ public:
* @return The integer value. * @return The integer value.
*/ */
thing_type::s64 integer() const { thing_type::s64 integer() const {
switch (m_type.type) { switch (true_type().type) {
case thing_type::S8: return get<thing_type::s8>(); case thing_type::S8: return get<thing_type::s8>();
case thing_type::S16: return get<thing_type::s16>(); case thing_type::S16: return get<thing_type::s16>();
case thing_type::S32: return get<thing_type::s32>(); case thing_type::S32: return get<thing_type::s32>();
@@ -448,11 +448,11 @@ public:
void resize(thing_type::u64 newSize) { void resize(thing_type::u64 newSize) {
if (!is(thing_type::Array)) throw bad_thing_access(); if (!is(thing_type::Array)) throw bad_thing_access();
if (m_type.value.array.size > 0) throw std::runtime_error("cannot resize a static array"); if (true_type().value.array.size > 0) throw std::runtime_error("cannot resize a static array");
auto& array = get<union array>(); auto& array = get<union array>();
if (newSize < 0 || newSize == array.dynamic.size) return; if (newSize < 0 || newSize == array.dynamic.size) return;
std::size_t innerSize = compute_size_na(*m_type.value.array.type); std::size_t innerSize = compute_size_na(*true_type().value.array.type);
std::byte* newData = new std::byte[innerSize * newSize]; std::byte* newData = new std::byte[innerSize * newSize];
std::memcpy(newData, std::memcpy(newData,
array.dynamic.data, array.dynamic.data,
@@ -577,7 +577,7 @@ private:
private: private:
template <typename Func> template <typename Func>
decltype(auto) visit_primitive(Func&& func) const { decltype(auto) visit_primitive(Func&& func) const {
switch (m_type.type) { switch (true_type().type) {
case thing_type::S8: return std::forward<Func>(func)(get<thing_type::s8>()); 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::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::S32: return std::forward<Func>(func)(get<thing_type::s32>());
@@ -592,7 +592,7 @@ private:
template <typename Op> template <typename Op>
thing binary_op(const thing& rhs, const Op& op) const { thing binary_op(const thing& rhs, const Op& op) const {
if (thing_type::is_primitive(m_type.type) && thing_type::is_primitive(rhs.m_type.type)) { if (thing_type::is_primitive(true_type().type) && thing_type::is_primitive(true_type().type)) {
static constexpr enum thing_type::type promotions[8 * 8] = { static constexpr enum thing_type::type promotions[8 * 8] = {
// S8 // S8
thing_type::S8, thing_type::S8,
+33 -3
View File
@@ -133,9 +133,33 @@ 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({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() })->get<int>() = push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() })->get<thing_type::s8>() =
frame.mod->byte(frame.position++); static_cast<thing_type::s8>(frame.mod->byte(frame.position++));
} break;
case instruction_t::PushU8: {
push_thing({ (struct thing_type){ thing_type::U8 }, m_context->thing_alloc() })->get<thing_type::u8>() =
static_cast<thing_type::u8>(frame.mod->byte(frame.position++));
} break;
case instruction_t::PushS16: {
thing_type::u16 value = frame.mod->byte(frame.position++);
value |= static_cast<thing_type::u16>(frame.mod->byte(frame.position++) << 8);
push_thing({ (struct thing_type){ thing_type::S16 }, m_context->thing_alloc() })->get<thing_type::s16>() =
static_cast<thing_type::s16>(value);
} break;
case instruction_t::PushU16: {
thing_type::u16 value = frame.mod->byte(frame.position++);
value |= static_cast<thing_type::u16>(frame.mod->byte(frame.position++) << 8);
push_thing({ (struct thing_type){ thing_type::U16 }, m_context->thing_alloc() })->get<thing_type::u16>() =
value;
} break;
case instruction_t::PushS32: {
push_thing({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() })->get<thing_type::s32>() =
static_cast<thing_type::s32>(frame.mod->byte(frame.position++));
} break;
case instruction_t::PushU32: {
push_thing({ (struct thing_type){ thing_type::U32 }, m_context->thing_alloc() })->get<thing_type::u32>() =
static_cast<thing_type::u32>(frame.mod->byte(frame.position++));
} break; } break;
case instruction_t::Array: { case instruction_t::Array: {
mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte(frame.position)) | mod_type_id typeId = static_cast<mod_type_id>(frame.mod->byte(frame.position)) |
@@ -161,6 +185,12 @@ void executor::step() {
auto array = pop_thing(); auto array = pop_thing();
push_thing(array->at(index->integer())); push_thing(array->at(index->integer()));
} break; } break;
case instruction_t::Set: {
auto index = pop_thing();
auto array = pop_thing();
auto element = pop_thing();
array->at(index->integer()) = std::move(element->clone());
} break;
case instruction_t::Drop: { case instruction_t::Drop: {
pop_thing(); pop_thing();
} break; } break;
+6 -7
View File
@@ -13,7 +13,7 @@
static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) { static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) {
using namespace furvm; using namespace furvm;
switch (thing.type().type) { switch (thing.true_type().type) {
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break; 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::S16: std::cout << thing.get<thing_type::s16>(); break;
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break; case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;
@@ -22,7 +22,7 @@ static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) {
case thing_type::U16: std::cout << thing.get<thing_type::u16>(); 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::U32: std::cout << thing.get<thing_type::u32>(); break;
case thing_type::U64: std::cout << thing.get<thing_type::u64>(); break; case thing_type::U64: std::cout << thing.get<thing_type::u64>(); break;
case furvm::thing_type::Array: case thing_type::Array:
std::cout << "{ "; std::cout << "{ ";
for (thing_type::u64 i = 0; i < thing.length(); ++i) { for (thing_type::u64 i = 0; i < thing.length(); ++i) {
if (i > 0) std::cout << ", "; if (i > 0) std::cout << ", ";
@@ -37,20 +37,20 @@ static void print_thing(const furvm::thing<furvm::thing_allocator>& thing) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
auto context = std::make_shared<furvm::context>(); auto context = std::make_shared<furvm::context>();
#if 0 // NOLINT #if 1 // NOLINT
if (argc != 2) { if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <mod.fmod>\n"; std::cerr << "Usage: " << argv[0] << " <mod.fmod>\n";
return 1; return 1;
} }
std::ifstream file(argv[1]); std::ifstream file(argv[1]);
if (!file.is_open()) { if (!file.is_open()) {
std::cerr << "Failed to open " << argv[1] << '\n'; std::cerr << "Failed to open " << argv[1] << '\n';
return 1; return 1;
} }
furvm::mod_h mod = context->emplace("main", std::move(furvm::mod::load(file))); furvm::mod_h mod = context->emplace("main", std::move(furvm::mod::load(file)));
auto mainFunc = mod->function_at("main", furvm::function_sig{});
#else #else
static const furvm::byte s_bytecode[] = { static const furvm::byte s_bytecode[] = {
static_cast<furvm::byte>(furvm::instruction_t::Array), static_cast<furvm::byte>(furvm::instruction_t::Array),
@@ -74,11 +74,10 @@ int main(int argc, char** argv) {
auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0); auto mainFunc = mod->emplace_function("main", furvm::function_sig{}, 0);
mod->emplace_function(furvm::function_sig{ { u64Type }, u64Type }, "print").dispatch(); mod->emplace_function(furvm::function_sig{ { u64Type }, u64Type }, "print").dispatch();
#endif #endif
mod->set_native_function("print", [](furvm::executor& executor) { mod->set_native_function("println", [](furvm::executor& executor) {
auto arg = std::move(*executor.load_thing(0)); auto arg = std::move(*executor.load_thing(0));
print_thing(arg); print_thing(arg);
std::cout << '\n'; std::cout << '\n';
executor.push_thing(std::move(arg));
}); });
furvm::executor_h executor = context->emplace_executor(context); furvm::executor_h executor = context->emplace_executor(context);
+10 -6
View File
@@ -25,6 +25,7 @@ std::ostream& mod::serialize(std::ostream& os) const {
} }
auto type = *m_types.at(id); auto type = *m_types.at(id);
detail::serialize(os, static_cast<std::uint8_t>(type.type));
switch (type.type) { switch (type.type) {
case mod_type::S8: case mod_type::S8:
case mod_type::S16: case mod_type::S16:
@@ -67,7 +68,7 @@ std::ostream& mod::serialize(std::ostream& os) const {
} }
// Function signature // Function signature
detail::serialize(os, func->signature().params.size()); detail::serialize(os, static_cast<std::uint32_t>(func->signature().params.size()));
for (std::uint32_t i = 0; i < func->signature().params.size(); ++i) for (std::uint32_t i = 0; i < func->signature().params.size(); ++i)
detail::serialize(os, func->signature().params[i].id()); detail::serialize(os, func->signature().params[i].id());
@@ -116,7 +117,10 @@ mod mod::load(std::istream& is) {
case mod_type::U8: case mod_type::U8:
case mod_type::U16: case mod_type::U16:
case mod_type::U32: case mod_type::U32:
case mod_type::U64: break; case mod_type::U64: {
mod_type theType = { (enum mod_type::type)type };
mod.emplace_type(id, theType).dispatch();
} break;
case mod_type::Ptr: { case mod_type::Ptr: {
mod_type_id typeId = 0; mod_type_id typeId = 0;
detail::load(is, typeId); detail::load(is, typeId);
@@ -165,17 +169,17 @@ mod mod::load(std::istream& is) {
decltype(std::declval<function>().position()) offset = 0; decltype(std::declval<function>().position()) offset = 0;
detail::load(is, offset); detail::load(is, offset);
if (name.empty()) if (name.empty())
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
else
mod.emplace_function(id, std::move(signature), offset).dispatch(); mod.emplace_function(id, std::move(signature), offset).dispatch();
else
mod.emplace_function(std::move(name), id, std::move(signature), offset).dispatch();
} break; } break;
case function_t::Native: { case function_t::Native: {
std::string native; std::string native;
detail::load(is, native); detail::load(is, native);
if (name.empty()) if (name.empty())
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
else
mod.emplace_function(id, std::move(signature), std::move(native)).dispatch(); mod.emplace_function(id, std::move(signature), std::move(native)).dispatch();
else
mod.emplace_function(std::move(name), id, std::move(signature), std::move(native)).dispatch();
} break; } break;
case function_t::Import: { case function_t::Import: {
std::string modName; std::string modName;