From 9f950e45e7d4cb7a70cd891b34480bee514132d2 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Fri, 17 Jul 2026 20:43:34 +0200 Subject: [PATCH] feat(furas): introduce basic generator Closes: #56 --- furas/examples/array.furas | 26 ++ furas/include/furas/gen.hpp | 36 +++ furas/include/furas/token.hpp | 10 +- furas/src/gen.cpp | 546 ++++++++++++++++++++++++++++++++++ furas/src/lexer.cpp | 8 +- furas/src/main.cpp | 41 ++- 6 files changed, 654 insertions(+), 13 deletions(-) create mode 100644 furas/examples/array.furas create mode 100644 furas/include/furas/gen.hpp create mode 100644 furas/src/gen.cpp diff --git a/furas/examples/array.furas b/furas/examples/array.furas new file mode 100644 index 0000000..771664e --- /dev/null +++ b/furas/examples/array.furas @@ -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 diff --git a/furas/include/furas/gen.hpp b/furas/include/furas/gen.hpp new file mode 100644 index 0000000..43bb2ec --- /dev/null +++ b/furas/include/furas/gen.hpp @@ -0,0 +1,36 @@ +#ifndef FURAS_GEN_HPP +#define FURAS_GEN_HPP + +#include "furas/lexer.hpp" +#include "furvm/module.hpp" + +#include + +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 diff --git a/furas/include/furas/token.hpp b/furas/include/furas/token.hpp index 5e464da..67e0767 100644 --- a/furas/include/furas/token.hpp +++ b/furas/include/furas/token.hpp @@ -18,9 +18,14 @@ struct token { Sha256, /**< Label marker(`#`). */ Percent, /**< Variable marker(`%`). The more the better. */ - Dot, /**< . */ + 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. */ @@ -29,6 +34,7 @@ struct token { Push, Array, Get, + Set, Drop, Dup, Clone, @@ -50,7 +56,7 @@ struct token { Load, Store, Call, - Jump, + Jmp, Jnz, Ret, diff --git a/furas/src/gen.cpp b/furas/src/gen.cpp new file mode 100644 index 0000000..fb0850f --- /dev/null +++ b/furas/src/gen.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 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::max(); + + std::size_t offset = INVALID; + std::vector functions; + std::vector unknowns; + }; + + furvm::mod mod; + std::unordered_map, + furvm::function_h, + furlang::utility:: + pair_hash, furvm::detail::function_sig_hash>> + functions; + std::unordered_map types; + + std::unordered_map 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(label.offset - unknown); + if (jmpOff < std::numeric_limits::min() || + jmpOff > std::numeric_limits::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::instruction_t::PushS8)); + } break; + case furvm::mod_type::U8: { + mod.bytecode().push_back(static_cast(furvm::instruction_t::PushU8)); + } break; + case furvm::mod_type::S16: { + mod.bytecode().push_back(static_cast(furvm::instruction_t::PushS16)); + } break; + case furvm::mod_type::U16: { + mod.bytecode().push_back(static_cast(furvm::instruction_t::PushU16)); + } break; + case furvm::mod_type::S32: { + mod.bytecode().push_back(static_cast(furvm::instruction_t::PushS32)); + } break; + case furvm::mod_type::U32: { + mod.bytecode().push_back(static_cast(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(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(offset - mod.bytecode().size() + 1); + if (jmpOff < std::numeric_limits::min() || + jmpOff > std::numeric_limits::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 diff --git a/furas/src/lexer.cpp b/furas/src/lexer.cpp index 0d04bd5..bc70dc4 100644 --- a/furas/src/lexer.cpp +++ b/furas/src/lexer.cpp @@ -33,6 +33,9 @@ token_r lexer::next_token() { } static std::unordered_map s_tokens = { + { "func", token::Func }, + { "type", token::Type }, + { "native", token::Native }, { "import", token::Import }, { "public", token::Public }, { "private", token::Private }, @@ -40,6 +43,7 @@ token_r lexer::next_token() { { "push", token::Push }, { "array", token::Array }, { "get", token::Get }, + { "set", token::Set }, { "drop", token::Drop }, { "dup", token::Dup }, { "clone", token::Clone }, @@ -61,7 +65,7 @@ token_r lexer::next_token() { { "load", token::Load }, { "store", token::Store }, { "call", token::Call }, - { "jump", token::Jump }, + { "jmp", token::Jmp }, { "jnz", token::Jnz }, { "ret", token::Ret }, }; @@ -80,7 +84,9 @@ token_r lexer::next_token() { 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] + "'" } diff --git a/furas/src/main.cpp b/furas/src/main.cpp index b797a8a..f6892e6 100644 --- a/furas/src/main.cpp +++ b/furas/src/main.cpp @@ -1,18 +1,39 @@ +#include "furas/gen.hpp" #include "furas/lexer.hpp" +#include +#include #include -int main(void) { // NOLINT - std::string source = "%uwu 10 public private int le"; - furas::lexer lexer("", source); - while (true) { - furas::token_r token = lexer.next_token(); - if (token.has_error()) { - std::cout << token.error().message << '\n'; - break; - } - std::cout << token->type << '\n'; +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(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; }