Compare commits

...

2 Commits

Author SHA1 Message Date
CHatingPython c841427eff feat(furas): introduce global variables 2026-08-14 13:58:29 +02:00
CHatingPython a685ecf226 feat(furvm): introduce global variables
Closes: #59
2026-08-14 13:56:10 +02:00
9 changed files with 100 additions and 11 deletions
+7 -5
View File
@@ -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:
+9 -6
View File
@@ -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,
+25
View File
@@ -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 &&
+3
View File
@@ -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 },
+3
View File
@@ -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,
+33
View File
@@ -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;
}; };
+6
View File
@@ -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;
+8
View File
@@ -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:
+6
View File
@@ -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);