Compare commits
2 Commits
84b70077c1
...
c841427eff
| Author | SHA1 | Date | |
|---|---|---|---|
|
c841427eff
|
|||
|
a685ecf226
|
@@ -1,22 +1,24 @@
|
|||||||
func println $u32 = native println
|
func println $u32 = native println
|
||||||
|
|
||||||
|
allocate uwu
|
||||||
|
|
||||||
public func main = #main
|
public func main = #main
|
||||||
main:
|
main:
|
||||||
push $u32 0
|
push $u32 0
|
||||||
store %0
|
storeg %uwu
|
||||||
loop_header:
|
loop_header:
|
||||||
load %0
|
loadg %uwu
|
||||||
push $u32 10
|
push $u32 10
|
||||||
ge
|
ge
|
||||||
jnz #loop_end
|
jnz #loop_end
|
||||||
loop_body:
|
loop_body:
|
||||||
load %0
|
loadg %uwu
|
||||||
call $u32 println
|
call $u32 println
|
||||||
|
|
||||||
load %0
|
loadg %uwu
|
||||||
push $u32 1
|
push $u32 1
|
||||||
add
|
add
|
||||||
store %0
|
storeg %uwu
|
||||||
|
|
||||||
jmp #loop_header
|
jmp #loop_header
|
||||||
loop_end:
|
loop_end:
|
||||||
|
|||||||
@@ -23,12 +23,13 @@ struct token {
|
|||||||
Colon, /**< `:` */
|
Colon, /**< `:` */
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
Func, /**< `func` keyword for defining functions. */
|
Func, /**< `func` keyword for defining functions. */
|
||||||
Type, /**< `type` keyword for defining types. */
|
Type, /**< `type` keyword for defining types. */
|
||||||
Native, /**< `native` keyword for native functions. :v: */
|
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. */
|
||||||
|
Allocate, /**< `allocate` keyword for global variables. */
|
||||||
|
|
||||||
// Instructions
|
// Instructions
|
||||||
Push,
|
Push,
|
||||||
@@ -56,6 +57,8 @@ struct token {
|
|||||||
Lenof,
|
Lenof,
|
||||||
Load,
|
Load,
|
||||||
Store,
|
Store,
|
||||||
|
LoadGlobal,
|
||||||
|
StoreGlobal,
|
||||||
Call,
|
Call,
|
||||||
Jmp,
|
Jmp,
|
||||||
Jnz,
|
Jnz,
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ std::unordered_map<enum token::type, furvm::instruction_t> instructions = {
|
|||||||
{ token::Lenof, furvm::instruction_t::Lengthof },
|
{ token::Lenof, furvm::instruction_t::Lengthof },
|
||||||
{ token::Load, furvm::instruction_t::Load },
|
{ token::Load, furvm::instruction_t::Load },
|
||||||
{ token::Store, furvm::instruction_t::Store },
|
{ token::Store, furvm::instruction_t::Store },
|
||||||
|
{ token::LoadGlobal, furvm::instruction_t::LoadGlobal },
|
||||||
|
{ token::StoreGlobal, furvm::instruction_t::StoreGlobal },
|
||||||
{ token::Call, furvm::instruction_t::Call },
|
{ token::Call, furvm::instruction_t::Call },
|
||||||
{ token::Jmp, furvm::instruction_t::Jump },
|
{ token::Jmp, furvm::instruction_t::Jump },
|
||||||
{ token::Jnz, furvm::instruction_t::JumpNotZero },
|
{ token::Jnz, furvm::instruction_t::JumpNotZero },
|
||||||
@@ -72,6 +74,7 @@ const char* token_type(enum token::type type) {
|
|||||||
case token::Import: return "import";
|
case token::Import: return "import";
|
||||||
case token::Public: return "public";
|
case token::Public: return "public";
|
||||||
case token::Private: return "private";
|
case token::Private: return "private";
|
||||||
|
case token::Allocate: return "allocate";
|
||||||
case token::Push: return "push";
|
case token::Push: return "push";
|
||||||
case token::Array: return "array";
|
case token::Array: return "array";
|
||||||
case token::Get: return "get";
|
case token::Get: return "get";
|
||||||
@@ -97,6 +100,8 @@ const char* token_type(enum token::type type) {
|
|||||||
case token::Lenof: return "lenof";
|
case token::Lenof: return "lenof";
|
||||||
case token::Load: return "load";
|
case token::Load: return "load";
|
||||||
case token::Store: return "store";
|
case token::Store: return "store";
|
||||||
|
case token::LoadGlobal: return "loadg";
|
||||||
|
case token::StoreGlobal: return "storeg";
|
||||||
case token::Call: return "call";
|
case token::Call: return "call";
|
||||||
case token::Jmp: return "jmp";
|
case token::Jmp: return "jmp";
|
||||||
case token::Jnz: return "jnz";
|
case token::Jnz: return "jnz";
|
||||||
@@ -128,6 +133,7 @@ struct mod_context {
|
|||||||
pair_hash<std::string, furvm::function_sig, std::hash<std::string>, furvm::detail::function_sig_hash>>
|
pair_hash<std::string, furvm::function_sig, std::hash<std::string>, furvm::detail::function_sig_hash>>
|
||||||
functions;
|
functions;
|
||||||
std::unordered_map<std::string, furvm::mod_type_h> types;
|
std::unordered_map<std::string, furvm::mod_type_h> types;
|
||||||
|
std::unordered_map<std::string, std::uint16_t> variables;
|
||||||
|
|
||||||
std::unordered_map<std::string, label_context> labels;
|
std::unordered_map<std::string, label_context> labels;
|
||||||
|
|
||||||
@@ -214,6 +220,15 @@ struct mod_context {
|
|||||||
return { generator_error::Success };
|
return { generator_error::Success };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case token::Allocate: {
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
std::string name = std::string(result->value.string);
|
||||||
|
variables.emplace(name, variables.size());
|
||||||
|
mod.set_global_variable_count(variables.size());
|
||||||
|
return { generator_error::Success };
|
||||||
|
}
|
||||||
|
|
||||||
case token::Func:
|
case token::Func:
|
||||||
case token::Type:
|
case token::Type:
|
||||||
case token::Public:
|
case token::Public:
|
||||||
@@ -465,6 +480,16 @@ struct mod_context {
|
|||||||
if (!result) return result.error;
|
if (!result) return result.error;
|
||||||
instr.arg.u16 = result->value.uint;
|
instr.arg.u16 = result->value.uint;
|
||||||
} break;
|
} break;
|
||||||
|
case furvm::instruction_argument::GlobalVariable: {
|
||||||
|
result = eat_token(lexer, token::Percent);
|
||||||
|
if (!result) return result.error;
|
||||||
|
result = eat_token(lexer, token::Identifier);
|
||||||
|
if (!result) return result.error;
|
||||||
|
if (auto it = variables.find(std::string(result->value.string)); it != variables.end())
|
||||||
|
instr.arg.u16 = it->second;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Unknown global variable");
|
||||||
|
} break;
|
||||||
case furvm::instruction_argument::Function: {
|
case furvm::instruction_argument::Function: {
|
||||||
furvm::function_sig signature;
|
furvm::function_sig signature;
|
||||||
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
while ((result = next_token(lexer)).error.type == generator_error::Success &&
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ token_r lexer::next_token() {
|
|||||||
{ "import", token::Import },
|
{ "import", token::Import },
|
||||||
{ "public", token::Public },
|
{ "public", token::Public },
|
||||||
{ "private", token::Private },
|
{ "private", token::Private },
|
||||||
|
{ "allocate", token::Allocate },
|
||||||
|
|
||||||
{ "push", token::Push },
|
{ "push", token::Push },
|
||||||
{ "array", token::Array },
|
{ "array", token::Array },
|
||||||
@@ -65,6 +66,8 @@ token_r lexer::next_token() {
|
|||||||
{ "lenof", token::Lenof },
|
{ "lenof", token::Lenof },
|
||||||
{ "load", token::Load },
|
{ "load", token::Load },
|
||||||
{ "store", token::Store },
|
{ "store", token::Store },
|
||||||
|
{ "loadg", token::LoadGlobal },
|
||||||
|
{ "storeg", token::StoreGlobal },
|
||||||
{ "call", token::Call },
|
{ "call", token::Call },
|
||||||
{ "jmp", token::Jmp },
|
{ "jmp", token::Jmp },
|
||||||
{ "jnz", token::Jnz },
|
{ "jnz", token::Jnz },
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ struct instruction_argument {
|
|||||||
Constant,
|
Constant,
|
||||||
Type,
|
Type,
|
||||||
Variable,
|
Variable,
|
||||||
|
GlobalVariable,
|
||||||
Function,
|
Function,
|
||||||
Offset,
|
Offset,
|
||||||
|
|
||||||
@@ -78,6 +79,8 @@ struct instruction {
|
|||||||
Lengthof,
|
Lengthof,
|
||||||
Load,
|
Load,
|
||||||
Store,
|
Store,
|
||||||
|
LoadGlobal,
|
||||||
|
StoreGlobal,
|
||||||
Call,
|
Call,
|
||||||
Jump,
|
Jump,
|
||||||
JumpNotZero,
|
JumpNotZero,
|
||||||
|
|||||||
@@ -6,10 +6,14 @@
|
|||||||
#include "furvm/function.hpp"
|
#include "furvm/function.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
#include "furvm/handle.hpp"
|
#include "furvm/handle.hpp"
|
||||||
|
#include "furvm/thing.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -348,6 +352,33 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handle_container<mod_type_h>& types() const { return m_types; }
|
const handle_container<mod_type_h>& types() const { return m_types; }
|
||||||
|
public:
|
||||||
|
void set_global_variable_count(std::uint16_t count) {
|
||||||
|
m_globalVariables.resize(count);
|
||||||
|
m_globalVariables.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t get_global_variable_count() const { return static_cast<std::uint16_t>(m_globalVariables.size()); }
|
||||||
|
|
||||||
|
void store_global_variable(std::uint16_t var, thing<>&& thing) {
|
||||||
|
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||||
|
m_globalVariables.emplace(m_globalVariables.cbegin() + var, std::move(thing));
|
||||||
|
}
|
||||||
|
|
||||||
|
void store_global_variable(std::uint16_t var, const thing<>& thing) {
|
||||||
|
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||||
|
m_globalVariables.emplace(m_globalVariables.cbegin() + var, thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
thing<>& load_global_variable(std::uint16_t var) {
|
||||||
|
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||||
|
return m_globalVariables[var];
|
||||||
|
}
|
||||||
|
|
||||||
|
const thing<>& load_global_variable(std::uint16_t var) const {
|
||||||
|
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
|
||||||
|
return m_globalVariables[var];
|
||||||
|
}
|
||||||
public:
|
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.
|
||||||
@@ -376,6 +407,8 @@ private:
|
|||||||
|
|
||||||
handle_container<mod_type_h> m_types;
|
handle_container<mod_type_h> m_types;
|
||||||
|
|
||||||
|
std::vector<thing<>> m_globalVariables;
|
||||||
|
|
||||||
std::unordered_map<std::string, native_function> m_nativeFunctions;
|
std::unordered_map<std::string, native_function> m_nativeFunctions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -326,6 +326,12 @@ void executor::step() {
|
|||||||
case instruction_t::Store: {
|
case instruction_t::Store: {
|
||||||
store_thing(instr.arg.u16, std::move(pop_thing()));
|
store_thing(instr.arg.u16, std::move(pop_thing()));
|
||||||
} break;
|
} break;
|
||||||
|
case instruction_t::LoadGlobal: {
|
||||||
|
push_thing(make_reference(frame.mod->load_global_variable(instr.arg.u16)));
|
||||||
|
} break;
|
||||||
|
case instruction_t::StoreGlobal: {
|
||||||
|
frame.mod->store_global_variable(instr.arg.u16, std::move(pop_thing()));
|
||||||
|
} break;
|
||||||
case instruction_t::Call: {
|
case instruction_t::Call: {
|
||||||
push_frame(frame.mod, *frame.mod->function_at(instr.arg.u16));
|
push_frame(frame.mod, *frame.mod->function_at(instr.arg.u16));
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ const std::size_t instruction_argument::s_sizes[instruction_argument::Count] = {
|
|||||||
4,
|
4,
|
||||||
// Variable:
|
// Variable:
|
||||||
2,
|
2,
|
||||||
|
// GlobalVariable:
|
||||||
|
2,
|
||||||
// Function:
|
// Function:
|
||||||
4,
|
4,
|
||||||
// Offset:
|
// Offset:
|
||||||
@@ -54,6 +56,8 @@ const bool instruction_argument::s_signedness[instruction_argument::Count] = {
|
|||||||
false,
|
false,
|
||||||
// Variable:
|
// Variable:
|
||||||
false,
|
false,
|
||||||
|
// GlobalVariable:
|
||||||
|
false,
|
||||||
// Function:
|
// Function:
|
||||||
false,
|
false,
|
||||||
// Offset:
|
// Offset:
|
||||||
@@ -125,6 +129,10 @@ const instruction_argument_t instruction::s_arguments[instruction::Count] = {
|
|||||||
instruction_argument::Variable,
|
instruction_argument::Variable,
|
||||||
// Store:
|
// Store:
|
||||||
instruction_argument::Variable,
|
instruction_argument::Variable,
|
||||||
|
// LoadGlobal:
|
||||||
|
instruction_argument::GlobalVariable,
|
||||||
|
// StoreGlobal:
|
||||||
|
instruction_argument::GlobalVariable,
|
||||||
// Call:
|
// Call:
|
||||||
instruction_argument::Function,
|
instruction_argument::Function,
|
||||||
// Jump:
|
// Jump:
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ std::ostream& mod::serialize(std::ostream& os) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detail::serialize(os, static_cast<std::uint16_t>(m_globalVariables.size()));
|
||||||
|
|
||||||
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()));
|
||||||
}
|
}
|
||||||
@@ -197,6 +199,10 @@ mod mod::load(std::istream& is) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint16_t globalVariables = 0;
|
||||||
|
detail::load(is, globalVariables);
|
||||||
|
mod.set_global_variable_count(globalVariables);
|
||||||
|
|
||||||
std::uint64_t bytecodeLength = 0;
|
std::uint64_t bytecodeLength = 0;
|
||||||
detail::load(is, bytecodeLength);
|
detail::load(is, bytecodeLength);
|
||||||
mod.bytecode().resize(bytecodeLength);
|
mod.bytecode().resize(bytecodeLength);
|
||||||
|
|||||||
Reference in New Issue
Block a user