From 91ef7f01a4a1b0b9c98c557a6370cd46e50418cf Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sun, 31 May 2026 17:29:17 +0200 Subject: [PATCH 01/15] Add if statement Signed-off-by: CHatingPython --- furc/include/furc/ast/declaration.hpp | 21 ++------- furc/include/furc/ast/fwd.hpp | 18 ++++++++ furc/include/furc/ast/statement.hpp | 32 +++++++++++-- furc/include/furc/ast/visitor.hpp | 1 + furc/include/furc/front/ir_generator.hpp | 14 ++++-- furc/include/furc/front/parser.hpp | 2 +- furc/include/furc/front/token.hpp | 8 ++++ furc/src/ast.cpp | 24 ++++++++++ furc/src/front/ir_generator.cpp | 31 +++++++++++-- furc/src/front/lexer.cpp | 2 + furc/src/front/parser.cpp | 59 ++++++++++++++++++------ furc/src/main.cpp | 2 +- 12 files changed, 170 insertions(+), 44 deletions(-) diff --git a/furc/include/furc/ast/declaration.hpp b/furc/include/furc/ast/declaration.hpp index 557b618..09a1c67 100644 --- a/furc/include/furc/ast/declaration.hpp +++ b/furc/include/furc/ast/declaration.hpp @@ -5,8 +5,6 @@ #include "furc/ast/statement.hpp" #include "furc/front/token.hpp" -#include - namespace furc { namespace ast { @@ -45,27 +43,14 @@ protected: front::token m_name; }; -struct function_body { - location begin, end; - std::vector> statements; - - bool operator==(const function_body& rhs) const { - return begin == rhs.begin && end == rhs.end && statements == rhs.statements; - } - - bool operator!=(const function_body& rhs) const { return !this->operator==(rhs); } -}; - -using function_body_h = handle; - class function_definition_node : public function_declaration_node { public: - function_definition_node(front::token name, function_body_h&& body) + function_definition_node(front::token name, body_h&& body) : function_declaration_node(name), m_body(std::move(body)) {} public: declaration_node_t declaration_type() const override { return declaration_node_t::FunctionDefinition; } - const function_body_h& body() const { return m_body; } + const body_h& body() const { return m_body; } public: void accept(visitor& visitor) const override; @@ -73,7 +58,7 @@ public: protected: bool equal(const node& rhs) const override; private: - function_body_h m_body; + body_h m_body; }; } // namespace ast diff --git a/furc/include/furc/ast/fwd.hpp b/furc/include/furc/ast/fwd.hpp index 81be929..30d767f 100644 --- a/furc/include/furc/ast/fwd.hpp +++ b/furc/include/furc/ast/fwd.hpp @@ -4,6 +4,7 @@ #include "furc/handle.hpp" #include +#include namespace furc { namespace ast { @@ -38,6 +39,21 @@ using binop_expression_node_h = node_handle; class var_assign_expression_node; using var_assign_expression_node_h = node_handle; +struct body { + location begin, end; + std::vector statements; + + bool operator==(const body& rhs) const { + return begin == rhs.begin && end == rhs.end && statements == rhs.statements; + } + + bool operator!=(const body& rhs) const { return !this->operator==(rhs); } + + friend std::ostream& operator<<(std::ostream&, const body&); +}; + +using body_h = handle; + class function_declaration_node; using function_declaration_node_h = node_handle; class function_definition_node; @@ -45,6 +61,8 @@ using function_definition_node_h = node_handle; class return_statement_node; using return_statement_node_h = node_handle; +class if_statement_node; +using if_statement_node_h = node_handle; } // namespace ast } // namespace furc diff --git a/furc/include/furc/ast/statement.hpp b/furc/include/furc/ast/statement.hpp index f9d1092..ebb09ab 100644 --- a/furc/include/furc/ast/statement.hpp +++ b/furc/include/furc/ast/statement.hpp @@ -10,6 +10,7 @@ enum class statement_node_t { Expression, Declaration, Return, + If, }; class statement_node : public node { @@ -25,10 +26,10 @@ class return_statement_node : public statement_node { public: return_statement_node() = default; - return_statement_node(node_handle&& value) + return_statement_node(expression_node_h&& value) : m_value(std::move(value)) {} public: - node_handle value() const { return m_value; } + expression_node_h value() const { return m_value; } public: statement_node_t statement_type() const override { return statement_node_t::Return; } public: @@ -38,7 +39,32 @@ public: protected: bool equal(const node& rhs) const override; private: - node_handle m_value; + expression_node_h m_value; +}; + +class if_statement_node : public statement_node { +public: + if_statement_node(expression_node_h&& cond, statement_node_h&& then) + : m_cond(std::move(cond)), m_then(std::move(then)) {} + + if_statement_node(expression_node_h&& cond, statement_node_h&& then, statement_node_h&& elze) + : m_cond(std::move(cond)), m_then(std::move(then)), m_else(std::move(elze)) {} +public: + expression_node_h cond() const { return m_cond; } + const statement_node_h& then() const { return m_then; } + const statement_node_h& elze() const { return m_else; } +public: + statement_node_t statement_type() const override { return statement_node_t::If; } +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_h m_cond; + statement_node_h m_then; + statement_node_h m_else; }; } // namespace ast diff --git a/furc/include/furc/ast/visitor.hpp b/furc/include/furc/ast/visitor.hpp index 8123c0f..67dbb0d 100644 --- a/furc/include/furc/ast/visitor.hpp +++ b/furc/include/furc/ast/visitor.hpp @@ -25,6 +25,7 @@ public: virtual void visit_function_declaration_node(const function_declaration_node&) {} virtual void visit_function_definition_node(const function_definition_node&) {} virtual void visit_return_statement_node(const return_statement_node&) {} + virtual void visit_if_statement_node(const if_statement_node&) {} virtual void visit_error(const node_handle& handle) {} }; diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index ac959c5..472cb08 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -17,13 +17,14 @@ public: ir_generator(ir_generator&&) = default; ir_generator& operator=(ir_generator&&) = default; - ir_generator(const ir_generator&) = default; - ir_generator& operator=(const ir_generator&) = default; + ir_generator(const ir_generator&) = delete; + ir_generator& operator=(const ir_generator&) = delete; public: furlang::ir::module&& move_module() { return std::move(m_module); } public: void visit_function_definition_node(const ast::function_definition_node& funcDef) override; void visit_return_statement_node(const ast::return_statement_node& returnStmt) override; + void visit_if_statement_node(const ast::if_statement_node& node) override; void visit_string_literal_node(const ast::string_literal_node& node) override; void visit_integer_literal_node(const ast::integer_literal_node& node) override; void visit_var_read_expression_node(const ast::var_read_expression_node& node) override; @@ -31,9 +32,12 @@ public: void visit_binop_expression_node(const ast::binop_expression_node& node) override; void visit_var_assign_expression_node(const ast::var_assign_expression_node& node) override; private: - furlang::ir::module m_module; - std::shared_ptr m_currentBlock; - std::uint32_t m_registerCounter = 0; + furlang::ir::block_index push_block(); +private: + furlang::ir::module m_module; + std::unique_ptr m_currentFunction; + std::shared_ptr m_currentBlock; + std::uint32_t m_registerCounter = 0; std::unordered_map m_variableMap; }; diff --git a/furc/include/furc/front/parser.hpp b/furc/include/furc/front/parser.hpp index 83987a9..0e60a96 100644 --- a/furc/include/furc/front/parser.hpp +++ b/furc/include/furc/front/parser.hpp @@ -36,7 +36,7 @@ private: ast::expression_node_h parse_expression_unary(std::uint32_t precedence); ast::expression_node_h parse_expression_rhs(ast::expression_node_h&& init, std::uint32_t precedence); - ast::function_body_h parse_body(); + ast::body_h parse_body(); private: token_handle<> next_token(); const token_handle<>& peek_token(); diff --git a/furc/include/furc/front/token.hpp b/furc/include/furc/front/token.hpp index 3cf2f8b..b94354d 100644 --- a/furc/include/furc/front/token.hpp +++ b/furc/include/furc/front/token.hpp @@ -134,6 +134,8 @@ enum class keyword_token { None, Func, Return, + If, + Else, }; static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword) { @@ -141,7 +143,10 @@ static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword) case keyword_token::None: return os << "none"; case keyword_token::Func: return os << "func"; case keyword_token::Return: return os << "return"; + case keyword_token::If: return os << "if"; + case keyword_token::Else: return os << "else"; } + return os; } static inline std::string operator+(const std::string& str, keyword_token keyword) { @@ -149,7 +154,10 @@ static inline std::string operator+(const std::string& str, keyword_token keywor case keyword_token::None: return str + "none"; case keyword_token::Func: return str + "func"; case keyword_token::Return: return str + "return"; + case keyword_token::If: return str + "if"; + case keyword_token::Else: return str + "else"; } + return str; } using integer_token = std::uint64_t; diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index d6a656f..631f4aa 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -188,6 +188,22 @@ bool return_statement_node::equal(const node& rhs) const { return statement_node::equal(rhs) && m_value == reinterpret_cast(rhs).m_value; } +void if_statement_node::accept(visitor& visitor) const { + visitor.visit_if_statement_node(*this); +} + +std::ostream& if_statement_node::print(std::ostream& os) const { + os << "if " << *m_cond << ", then:\n"; + os << m_then; + if (m_else.present()) os << m_else; + return os; +} + +bool if_statement_node::equal(const node& rhsNode) const { + const auto& rhs = reinterpret_cast(rhsNode); + return statement_node::equal(rhs) && m_cond == rhs.m_cond && m_then == rhs.m_then && m_else == rhs.m_else; +} + void program_node::accept(visitor& visitor) const { for (const auto& decl : m_declarations) { if (decl.has_error()) { @@ -210,4 +226,12 @@ bool program_node::equal(const node& rhs) const { return m_declarations == reinterpret_cast(rhs).m_declarations; } +std::ostream& operator<<(std::ostream& os, const body& body) { + os << "body:"; + for (const auto& stmt : body.statements) { + os << '\n' << stmt; + } + return os; +} + } // namespace furc::ast \ No newline at end of file diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index e470a34..788028b 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -14,9 +14,9 @@ namespace ir = furlang::ir; } void ir_generator::visit_function_definition_node(const ast::function_definition_node& funcDef) { - auto func = std::make_unique(std::string(funcDef.name()->string)); + m_currentFunction = std::make_unique(std::string(funcDef.name()->string)); - m_currentBlock = func->push(); + push_block(); if (funcDef.body().has_error()) { std::cerr << funcDef.body().error() << '\n'; return; @@ -25,7 +25,7 @@ void ir_generator::visit_function_definition_node(const ast::function_definition stmt->accept(*this); } - m_module.push(std::move(func)); + m_module.push(std::move(m_currentFunction)); } void ir_generator::visit_return_statement_node(const ast::return_statement_node& returnStmt) { @@ -41,6 +41,25 @@ void ir_generator::visit_return_statement_node(const ast::return_statement_node& } } +void ir_generator::visit_if_statement_node(const ast::if_statement_node& node) { + node.cond()->accept(*this); + std::uint32_t cond = m_registerCounter - 1; + m_currentBlock->emplace(ir::operand::new_reg(cond), + m_currentFunction->blocks().size(), + m_currentFunction->blocks().size() + 1); + + push_block(); // then block + node.then()->accept(*this); + if (node.elze().present()) { + m_currentBlock->emplace(m_currentFunction->blocks().size() + 1); + + push_block(); // else block + node.elze()->accept(*this); + } + + push_block(); +} + void ir_generator::visit_string_literal_node(const ast::string_literal_node& node) { m_currentBlock->emplace(ir::operand::new_string(std::string(*node.value())), ir::operand::new_reg(m_registerCounter++)); @@ -93,4 +112,10 @@ void ir_generator::visit_var_assign_expression_node(const ast::var_assign_expres throw std::runtime_error("unimplemented"); } +furlang::ir::block_index ir_generator::push_block() { + ir::block_index index = m_currentFunction->blocks().size(); + m_currentBlock = m_currentFunction->push(); + return index; +} + } // namespace furc::front \ No newline at end of file diff --git a/furc/src/front/lexer.cpp b/furc/src/front/lexer.cpp index c09c59a..d6c6f8b 100644 --- a/furc/src/front/lexer.cpp +++ b/furc/src/front/lexer.cpp @@ -90,6 +90,8 @@ token_handle<> lexer::next_token() { static std::unordered_map s_keywords = { { "func", keyword_token::Func }, { "return", keyword_token::Return }, + { "if", keyword_token::If }, + { "else", keyword_token::Else }, }; if (auto it = s_keywords.find(value); it != s_keywords.end()) return { location, it->second, value }; diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index cd69149..278b06e 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -1,6 +1,6 @@ #include "furc/front/parser.hpp" -#include "furc/ast/declaration.hpp" +// #include "furc/ast/declaration.hpp" // IWYU pragma: keep #include #include @@ -55,7 +55,7 @@ ast::declaration_node_h parser::parse_declaration() { if (peek.has_error()) return peek; switch (peek->type) { case token_t::Lbrace: { - ast::function_body_h body = parse_body(); + ast::body_h body = parse_body(); if (body.has_error()) return body; return ast::function_definition_node_h{ first.location(), m_arena, *name, std::move(body) }; } @@ -91,16 +91,49 @@ ast::statement_node_h parser::parse_statement() { if (tok.has_error()) return tok; switch (tok->type) { case token_t::Keyword: { - auto tok = next_token(); - if (peek_token()->type == token_t::Semicolon) { - next_token(); - return ast::return_statement_node_h{ tok.location(), m_arena }; - } + switch (tok->value.keyword) { + case keyword_token::Return: { + auto tok = next_token(); + if (peek_token()->type == token_t::Semicolon) { + next_token(); + return ast::return_statement_node_h{ tok.location(), m_arena }; + } - auto value = parse_expression(); - auto err = eat_token(token_t::Semicolon); - if (err.has_error()) return err; - return ast::return_statement_node_h{ tok.location(), m_arena, std::move(value) }; + auto value = parse_expression(); + auto err = eat_token(token_t::Semicolon); + if (err.has_error()) return err; + return ast::return_statement_node_h{ tok.location(), m_arena, std::move(value) }; + } + case keyword_token::If: { + auto tok = next_token(); + auto err = eat_token(token_t::Lparen); + if (err.has_error()) return err; + + auto cond = parse_expression(); + + err = eat_token(token_t::Rparen); + if (err.has_error()) return err; + + auto then = parse_statement(); + if (then.has_error()) return then; + + if (peek_token().present() && peek_token()->type == token_t::Keyword && + peek_token()->value.keyword == keyword_token::Else) { + next_token(); + + return ast::if_statement_node_h{ tok.location(), + m_arena, + std::move(cond), + std::move(then), + std::move(parse_statement()) }; + } + + return ast::if_statement_node_h{ tok.location(), m_arena, std::move(cond), std::move(then) }; + } + case keyword_token::None: + case keyword_token::Func: + default: break; + } } default: break; } @@ -328,8 +361,8 @@ ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& ini return lhs; } -ast::function_body_h parser::parse_body() { - ast::function_body body; +ast::body_h parser::parse_body() { + ast::body body; token_handle<> begin = eat_token(token_t::Lbrace); if (begin.has_error()) return begin; diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 42a9b43..019c904 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -6,7 +6,7 @@ #include int main(void) { - furc::front::parser parser("", "func main() {\n return 7 + 6 * 10;\n}"); + furc::front::parser parser("", "func main() {\n if (1) return 7 + 6 * 10; else return 0;\n}"); furc::front::ir_generator generator; auto program = parser.parse(); From 9b1a7d5a0baa012d6a2373507817a345301d97f3 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sun, 31 May 2026 18:20:46 +0200 Subject: [PATCH 02/15] Implement var read and assign IR Signed-off-by: CHatingPython --- furc/include/furc/front/ir_generator.hpp | 4 -- furc/src/front/ir_generator.cpp | 22 +++++++++- furlang/include/furlang/ir/operand.hpp | 54 ++++++++++++------------ 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 472cb08..4d741aa 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -5,8 +5,6 @@ #include "furc/ast/visitor.hpp" #include "furlang/ir/module.hpp" -#include - namespace furc { namespace front { @@ -38,8 +36,6 @@ private: std::unique_ptr m_currentFunction; std::shared_ptr m_currentBlock; std::uint32_t m_registerCounter = 0; - - std::unordered_map m_variableMap; }; } // namespace front diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 788028b..5cd5d7e 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -71,7 +71,8 @@ void ir_generator::visit_integer_literal_node(const ast::integer_literal_node& n } void ir_generator::visit_var_read_expression_node(const ast::var_read_expression_node& node) { - throw std::runtime_error("unimplemented"); + m_currentBlock->emplace(ir::operand::new_variable(std::string(*node.get_name())), + ir::operand::new_reg(m_registerCounter++)); } void ir_generator::visit_unaryop_expression_node(const ast::unaryop_expression_node& node) { @@ -109,7 +110,24 @@ void ir_generator::visit_binop_expression_node(const ast::binop_expression_node& } void ir_generator::visit_var_assign_expression_node(const ast::var_assign_expression_node& node) { - throw std::runtime_error("unimplemented"); + node.rhs()->accept(*this); + std::uint32_t rhs = m_registerCounter - 1; + assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead); + ast::var_read_expression_node_h lhs = node.lhs(); + + std::uint32_t reg = m_registerCounter++; + + auto compound = node.compound(); + if (compound != ast::binop_expression_node_t::None) { + m_currentBlock->emplace(binary_op_instruction_t(compound), + ir::operand::new_variable(std::string(*lhs->get_name())), + ir::operand::new_reg(rhs), + ir::operand::new_reg(reg)); + } else { + m_currentBlock->emplace(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); + } + m_currentBlock->emplace(ir::operand::new_reg(reg), + ir::operand::new_variable(std::string(*lhs->get_name()))); } furlang::ir::block_index ir_generator::push_block() { diff --git a/furlang/include/furlang/ir/operand.hpp b/furlang/include/furlang/ir/operand.hpp index 7c2397f..4baf57b 100644 --- a/furlang/include/furlang/ir/operand.hpp +++ b/furlang/include/furlang/ir/operand.hpp @@ -11,11 +11,13 @@ namespace ir { enum class operand_t { None, Register, + Variable, Integer, String, }; using register_operand = std::uint32_t; +using variable_operand = std::string; using integer_operand = std::uint64_t; using string_operand = std::string; @@ -34,11 +36,14 @@ public: case operand_t::Register: { m_value.reg = other.m_value.reg; } break; + case operand_t::Variable: { + new (&m_value.variable) variable_operand(std::move(other.m_value.variable)); + } break; case operand_t::Integer: { m_value.integer = other.m_value.integer; } break; case operand_t::String: { - new (&m_value.string) std::string(std::move(other.m_value.string)); + new (&m_value.string) string_operand(std::move(other.m_value.string)); } break; } other.m_value.destroy(other.m_type); @@ -52,11 +57,14 @@ public: case operand_t::Register: { m_value.reg = other.m_value.reg; } break; + case operand_t::Variable: { + new (&m_value.variable) variable_operand(std::move(other.m_value.variable)); + } break; case operand_t::Integer: { m_value.integer = other.m_value.integer; } break; case operand_t::String: { - new (&m_value.string) std::string(std::move(other.m_value.string)); + new (&m_value.string) string_operand(std::move(other.m_value.string)); } break; } other.m_value.destroy(other.m_type); @@ -68,29 +76,31 @@ public: public: static operand new_reg(register_operand value) { operand operand; - operand.m_type = operand_t::Register; - new (&operand.m_value) union value(value); + operand.m_type = operand_t::Register; + operand.m_value.reg = value; + return operand; + } + + template + static operand new_variable(T&& value) { + operand operand; + operand.m_type = operand_t::Variable; + new (&operand.m_value.variable) variable_operand(std::forward(value)); return operand; } static operand new_integer(integer_operand value) { operand operand; - operand.m_type = operand_t::Integer; - new (&operand.m_value) union value(value); + operand.m_type = operand_t::Integer; + operand.m_value.integer = value; return operand; } - static operand new_string(const string_operand& value) { + template + static operand new_string(T&& value) { operand operand; operand.m_type = operand_t::String; - new (&operand.m_value) union value(value); - return operand; - } - - static operand new_string(string_operand&& value) { - operand operand; - operand.m_type = operand_t::String; - new (&operand.m_value) union value(std::move(value)); + new (&operand.m_value.string) string_operand(std::forward(value)); return operand; } public: @@ -103,6 +113,7 @@ public: switch (operand.m_type) { case operand_t::None: return os << "none"; case operand_t::Register: return os << '%' << operand.m_value.reg; + case operand_t::Variable: return os << operand.m_value.variable; case operand_t::Integer: return os << operand.m_value.integer; case operand_t::String: return os << '"' << operand.m_value.string << '"'; } @@ -115,6 +126,7 @@ private: union value { std::nullptr_t null = nullptr; register_operand reg; + variable_operand variable; integer_operand integer; string_operand string; @@ -131,18 +143,6 @@ private: value() = default; ~value() {} - value(register_operand reg) - : reg(reg) {} - - value(integer_operand integer) - : integer(integer) {} - - value(const std::string& string) - : string(string) {} - - value(std::string&& string) - : string(std::move(string)) {} - value(value&&) noexcept = delete; value& operator=(value&&) noexcept = delete; value(const value&) = delete; From 55f4435670c7834b2336b0662b0b44284848568b Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sun, 31 May 2026 20:12:23 +0200 Subject: [PATCH 03/15] fix(furc): bring back the LIBFURC guard to main.cpp --- furc/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 019c904..3f5dfec 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -1,4 +1,4 @@ -// #ifndef LIBFURC +#ifndef LIBFURC #include "furc/front/ir_generator.hpp" #include "furc/front/parser.hpp" @@ -31,4 +31,4 @@ int main(void) { return 0; } -// #endif // LIBFURC \ No newline at end of file +#endif // LIBFURC \ No newline at end of file From 7caaaac4a0dace2cf9cea787d6c1d7cb282bc3cc Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 17:23:00 +0200 Subject: [PATCH 04/15] feat(furc): add compound statement This one: { [statement]... } --- compile_commands.json | 1 + furc/include/furc/ast/declaration.hpp | 3 +-- furc/include/furc/ast/fwd.hpp | 2 ++ furc/include/furc/ast/statement.hpp | 19 +++++++++++++++++++ furc/include/furc/ast/visitor.hpp | 1 + furc/include/furc/front/ir_generator.hpp | 1 + furc/src/ast.cpp | 12 ++++++++++++ furc/src/front/ir_generator.cpp | 6 ++++++ furc/src/front/parser.cpp | 6 ++++-- furc/src/main.cpp | 18 ++++++++++++++++-- 10 files changed, 63 insertions(+), 6 deletions(-) create mode 120000 compile_commands.json diff --git a/compile_commands.json b/compile_commands.json new file mode 120000 index 0000000..25eb4b2 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1 @@ +build/compile_commands.json \ No newline at end of file diff --git a/furc/include/furc/ast/declaration.hpp b/furc/include/furc/ast/declaration.hpp index 09a1c67..5849faa 100644 --- a/furc/include/furc/ast/declaration.hpp +++ b/furc/include/furc/ast/declaration.hpp @@ -11,7 +11,6 @@ namespace ast { enum class declaration_node_t { FunctionDeclaration, FunctionDefinition, - Variable, }; class declaration_node : public statement_node { @@ -64,4 +63,4 @@ private: } // namespace ast } // namespace furc -#endif // FURC_AST_DECLARATION_HPP \ No newline at end of file +#endif // FURC_AST_DECLARATION_HPP diff --git a/furc/include/furc/ast/fwd.hpp b/furc/include/furc/ast/fwd.hpp index 30d767f..33fa45d 100644 --- a/furc/include/furc/ast/fwd.hpp +++ b/furc/include/furc/ast/fwd.hpp @@ -63,6 +63,8 @@ class return_statement_node; using return_statement_node_h = node_handle; class if_statement_node; using if_statement_node_h = node_handle; +class compound_statement_node; +using compound_statement_node_h = node_handle; } // namespace ast } // namespace furc diff --git a/furc/include/furc/ast/statement.hpp b/furc/include/furc/ast/statement.hpp index ebb09ab..5440c65 100644 --- a/furc/include/furc/ast/statement.hpp +++ b/furc/include/furc/ast/statement.hpp @@ -11,6 +11,7 @@ enum class statement_node_t { Declaration, Return, If, + Compound, }; class statement_node : public node { @@ -67,6 +68,24 @@ private: statement_node_h m_else; }; +class compound_statement_node : public statement_node { +public: + compound_statement_node(body_h&& body) + : m_body(std::move(body)) {} +public: + const body_h& body() const { return m_body; } +public: + statement_node_t statement_type() const override { return statement_node_t::Compound; } +public: + void accept(visitor& visitor) const override; + + std::ostream& print(std::ostream& os) const override; +protected: + bool equal(const node& rhs) const override; +private: + body_h m_body; +}; + } // namespace ast } // namespace furc diff --git a/furc/include/furc/ast/visitor.hpp b/furc/include/furc/ast/visitor.hpp index 67dbb0d..5599453 100644 --- a/furc/include/furc/ast/visitor.hpp +++ b/furc/include/furc/ast/visitor.hpp @@ -26,6 +26,7 @@ public: virtual void visit_function_definition_node(const function_definition_node&) {} virtual void visit_return_statement_node(const return_statement_node&) {} virtual void visit_if_statement_node(const if_statement_node&) {} + virtual void visit_compound_statement_node(const compound_statement_node&) {} virtual void visit_error(const node_handle& handle) {} }; diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 4d741aa..5e96e72 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -23,6 +23,7 @@ public: void visit_function_definition_node(const ast::function_definition_node& funcDef) override; void visit_return_statement_node(const ast::return_statement_node& returnStmt) override; void visit_if_statement_node(const ast::if_statement_node& node) override; + void visit_compound_statement_node(const ast::compound_statement_node& node) override; void visit_string_literal_node(const ast::string_literal_node& node) override; void visit_integer_literal_node(const ast::integer_literal_node& node) override; void visit_var_read_expression_node(const ast::var_read_expression_node& node) override; diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index 631f4aa..931f6f3 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -204,6 +204,18 @@ bool if_statement_node::equal(const node& rhsNode) const { return statement_node::equal(rhs) && m_cond == rhs.m_cond && m_then == rhs.m_then && m_else == rhs.m_else; } +void compound_statement_node::accept(visitor& visitor) const { + visitor.visit_compound_statement_node(*this); +} + +std::ostream& compound_statement_node::print(std::ostream& os) const { + return os << m_body; +} + +bool compound_statement_node::equal(const node& rhs) const { + return statement_node::equal(rhs) && m_body == reinterpret_cast(rhs).m_body; +} + void program_node::accept(visitor& visitor) const { for (const auto& decl : m_declarations) { if (decl.has_error()) { diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 5cd5d7e..50e03de 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -60,6 +60,12 @@ void ir_generator::visit_if_statement_node(const ast::if_statement_node& node) { push_block(); } +void ir_generator::visit_compound_statement_node(const ast::compound_statement_node& node) { + for (const auto& stmt : node.body()->statements) { + stmt->accept(*this); + } +} + void ir_generator::visit_string_literal_node(const ast::string_literal_node& node) { m_currentBlock->emplace(ir::operand::new_string(std::string(*node.value())), ir::operand::new_reg(m_registerCounter++)); diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index 278b06e..ae0591b 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -89,6 +89,7 @@ ast::declaration_node_h parser::parse_declaration() { ast::statement_node_h parser::parse_statement() { const auto& tok = peek_token(); if (tok.has_error()) return tok; + auto location = tok.location(); switch (tok->type) { case token_t::Keyword: { switch (tok->value.keyword) { @@ -121,20 +122,21 @@ ast::statement_node_h parser::parse_statement() { peek_token()->value.keyword == keyword_token::Else) { next_token(); - return ast::if_statement_node_h{ tok.location(), + return ast::if_statement_node_h{ location, m_arena, std::move(cond), std::move(then), std::move(parse_statement()) }; } - return ast::if_statement_node_h{ tok.location(), m_arena, std::move(cond), std::move(then) }; + return ast::if_statement_node_h{ location, m_arena, std::move(cond), std::move(then) }; } case keyword_token::None: case keyword_token::Func: default: break; } } + case token_t::Lbrace: return ast::compound_statement_node_h{ location, m_arena, parse_body() }; default: break; } diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 3f5dfec..c755159 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -6,7 +6,21 @@ #include int main(void) { - furc::front::parser parser("", "func main() {\n if (1) return 7 + 6 * 10; else return 0;\n}"); + std::string programStr = R"( + func main() { + x = 5; + x -= 3; + if (x < 3) { + y = x * 2; + w = y; + } else { + y = x - 3; + } + w = x - y; + z = x + y; + } + )"; + furc::front::parser parser("", programStr); furc::front::ir_generator generator; auto program = parser.parse(); @@ -31,4 +45,4 @@ int main(void) { return 0; } -#endif // LIBFURC \ No newline at end of file +#endif // LIBFURC From 0958389b906427af7964b41b24714f2ea118b401 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 17:28:40 +0200 Subject: [PATCH 05/15] refactor(furc): unify visit functions in AST visitor --- furc/include/furc/ast/visitor.hpp | 22 +++++++++++----------- furc/include/furc/front/ir_generator.hpp | 20 ++++++++++---------- furc/src/ast.cpp | 22 +++++++++++----------- furc/src/front/ir_generator.cpp | 20 ++++++++++---------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/furc/include/furc/ast/visitor.hpp b/furc/include/furc/ast/visitor.hpp index 5599453..745dd94 100644 --- a/furc/include/furc/ast/visitor.hpp +++ b/furc/include/furc/ast/visitor.hpp @@ -16,17 +16,17 @@ public: visitor(const visitor&) = default; visitor& operator=(const visitor&) = default; public: - virtual void visit_string_literal_node(const string_literal_node&) {} - virtual void visit_integer_literal_node(const integer_literal_node&) {} - virtual void visit_var_read_expression_node(const var_read_expression_node&) {} - virtual void visit_unaryop_expression_node(const unaryop_expression_node&) {} - virtual void visit_binop_expression_node(const binop_expression_node&) {} - virtual void visit_var_assign_expression_node(const var_assign_expression_node&) {} - virtual void visit_function_declaration_node(const function_declaration_node&) {} - virtual void visit_function_definition_node(const function_definition_node&) {} - virtual void visit_return_statement_node(const return_statement_node&) {} - virtual void visit_if_statement_node(const if_statement_node&) {} - virtual void visit_compound_statement_node(const compound_statement_node&) {} + virtual void visit(const string_literal_node&) {} + virtual void visit(const integer_literal_node&) {} + virtual void visit(const var_read_expression_node&) {} + virtual void visit(const unaryop_expression_node&) {} + virtual void visit(const binop_expression_node&) {} + virtual void visit(const var_assign_expression_node&) {} + virtual void visit(const function_declaration_node&) {} + virtual void visit(const function_definition_node&) {} + virtual void visit(const return_statement_node&) {} + virtual void visit(const if_statement_node&) {} + virtual void visit(const compound_statement_node&) {} virtual void visit_error(const node_handle& handle) {} }; diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 5e96e72..589621e 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -20,16 +20,16 @@ public: public: furlang::ir::module&& move_module() { return std::move(m_module); } public: - void visit_function_definition_node(const ast::function_definition_node& funcDef) override; - void visit_return_statement_node(const ast::return_statement_node& returnStmt) override; - void visit_if_statement_node(const ast::if_statement_node& node) override; - void visit_compound_statement_node(const ast::compound_statement_node& node) override; - void visit_string_literal_node(const ast::string_literal_node& node) override; - void visit_integer_literal_node(const ast::integer_literal_node& node) override; - void visit_var_read_expression_node(const ast::var_read_expression_node& node) override; - void visit_unaryop_expression_node(const ast::unaryop_expression_node& node) override; - void visit_binop_expression_node(const ast::binop_expression_node& node) override; - void visit_var_assign_expression_node(const ast::var_assign_expression_node& node) override; + void visit(const ast::function_definition_node& funcDef) override; + void visit(const ast::return_statement_node& returnStmt) override; + void visit(const ast::if_statement_node& node) override; + void visit(const ast::compound_statement_node& node) override; + void visit(const ast::string_literal_node& node) override; + void visit(const ast::integer_literal_node& node) override; + void visit(const ast::var_read_expression_node& node) override; + void visit(const ast::unaryop_expression_node& node) override; + void visit(const ast::binop_expression_node& node) override; + void visit(const ast::var_assign_expression_node& node) override; private: furlang::ir::block_index push_block(); private: diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index 931f6f3..38bba4c 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -13,7 +13,7 @@ bool literal_node::equal(const node& rhs) const { } void string_literal_node::accept(visitor& visitor) const { - visitor.visit_string_literal_node(*this); + visitor.visit(*this); } std::ostream& string_literal_node::print(std::ostream& os) const { @@ -26,7 +26,7 @@ bool string_literal_node::equal(const node& rhs) const { } void integer_literal_node::accept(visitor& visitor) const { - visitor.visit_integer_literal_node(*this); + visitor.visit(*this); } std::ostream& integer_literal_node::print(std::ostream& os) const { @@ -43,7 +43,7 @@ bool expression_node::equal(const node& rhs) const { } void var_read_expression_node::accept(visitor& visitor) const { - visitor.visit_var_read_expression_node(*this); + visitor.visit(*this); } std::ostream& var_read_expression_node::print(std::ostream& os) const { @@ -69,7 +69,7 @@ std::ostream& operator<<(std::ostream& os, unaryop_expression_node_t type) { } void unaryop_expression_node::accept(visitor& visitor) const { - visitor.visit_unaryop_expression_node(*this); + visitor.visit(*this); } std::ostream& unaryop_expression_node::print(std::ostream& os) const { @@ -108,7 +108,7 @@ std::ostream& operator<<(std::ostream& os, binop_expression_node_t type) { } void binop_expression_node::accept(visitor& visitor) const { - visitor.visit_binop_expression_node(*this); + visitor.visit(*this); } std::ostream& binop_expression_node::print(std::ostream& os) const { @@ -122,7 +122,7 @@ bool binop_expression_node::equal(const node& rhsNode) const { } void var_assign_expression_node::accept(visitor& visitor) const { - visitor.visit_var_assign_expression_node(*this); + visitor.visit(*this); } std::ostream& var_assign_expression_node::print(std::ostream& os) const { @@ -139,7 +139,7 @@ bool declaration_node::equal(const node& rhs) const { } void function_declaration_node::accept(visitor& visitor) const { - visitor.visit_function_declaration_node(*this); + visitor.visit(*this); } std::ostream& function_declaration_node::print(std::ostream& os) const { @@ -151,7 +151,7 @@ bool function_declaration_node::equal(const node& rhs) const { } void function_definition_node::accept(visitor& visitor) const { - visitor.visit_function_definition_node(*this); + visitor.visit(*this); } std::ostream& function_definition_node::print(std::ostream& os) const { @@ -175,7 +175,7 @@ bool statement_node::equal(const node& rhs) const { } void return_statement_node::accept(visitor& visitor) const { - visitor.visit_return_statement_node(*this); + visitor.visit(*this); } std::ostream& return_statement_node::print(std::ostream& os) const { @@ -189,7 +189,7 @@ bool return_statement_node::equal(const node& rhs) const { } void if_statement_node::accept(visitor& visitor) const { - visitor.visit_if_statement_node(*this); + visitor.visit(*this); } std::ostream& if_statement_node::print(std::ostream& os) const { @@ -205,7 +205,7 @@ bool if_statement_node::equal(const node& rhsNode) const { } void compound_statement_node::accept(visitor& visitor) const { - visitor.visit_compound_statement_node(*this); + visitor.visit(*this); } std::ostream& compound_statement_node::print(std::ostream& os) const { diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 50e03de..3f3fde6 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -13,7 +13,7 @@ namespace { namespace ir = furlang::ir; } -void ir_generator::visit_function_definition_node(const ast::function_definition_node& funcDef) { +void ir_generator::visit(const ast::function_definition_node& funcDef) { m_currentFunction = std::make_unique(std::string(funcDef.name()->string)); push_block(); @@ -28,7 +28,7 @@ void ir_generator::visit_function_definition_node(const ast::function_definition m_module.push(std::move(m_currentFunction)); } -void ir_generator::visit_return_statement_node(const ast::return_statement_node& returnStmt) { +void ir_generator::visit(const ast::return_statement_node& returnStmt) { if (returnStmt.value().has_error()) { std::cerr << returnStmt.value() << '\n'; } @@ -41,7 +41,7 @@ void ir_generator::visit_return_statement_node(const ast::return_statement_node& } } -void ir_generator::visit_if_statement_node(const ast::if_statement_node& node) { +void ir_generator::visit(const ast::if_statement_node& node) { node.cond()->accept(*this); std::uint32_t cond = m_registerCounter - 1; m_currentBlock->emplace(ir::operand::new_reg(cond), @@ -60,28 +60,28 @@ void ir_generator::visit_if_statement_node(const ast::if_statement_node& node) { push_block(); } -void ir_generator::visit_compound_statement_node(const ast::compound_statement_node& node) { +void ir_generator::visit(const ast::compound_statement_node& node) { for (const auto& stmt : node.body()->statements) { stmt->accept(*this); } } -void ir_generator::visit_string_literal_node(const ast::string_literal_node& node) { +void ir_generator::visit(const ast::string_literal_node& node) { m_currentBlock->emplace(ir::operand::new_string(std::string(*node.value())), ir::operand::new_reg(m_registerCounter++)); } -void ir_generator::visit_integer_literal_node(const ast::integer_literal_node& node) { +void ir_generator::visit(const ast::integer_literal_node& node) { m_currentBlock->emplace(ir::operand::new_integer(*node.value()), ir::operand::new_reg(m_registerCounter++)); } -void ir_generator::visit_var_read_expression_node(const ast::var_read_expression_node& node) { +void ir_generator::visit(const ast::var_read_expression_node& node) { m_currentBlock->emplace(ir::operand::new_variable(std::string(*node.get_name())), ir::operand::new_reg(m_registerCounter++)); } -void ir_generator::visit_unaryop_expression_node(const ast::unaryop_expression_node& node) { +void ir_generator::visit(const ast::unaryop_expression_node& node) { throw std::runtime_error("unimplemented"); } @@ -103,7 +103,7 @@ static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast:: } } -void ir_generator::visit_binop_expression_node(const ast::binop_expression_node& node) { +void ir_generator::visit(const ast::binop_expression_node& node) { node.lhs()->accept(*this); std::uint32_t lhs = m_registerCounter - 1; node.rhs()->accept(*this); @@ -115,7 +115,7 @@ void ir_generator::visit_binop_expression_node(const ast::binop_expression_node& ir::operand::new_reg(dst)); } -void ir_generator::visit_var_assign_expression_node(const ast::var_assign_expression_node& node) { +void ir_generator::visit(const ast::var_assign_expression_node& node) { node.rhs()->accept(*this); std::uint32_t rhs = m_registerCounter - 1; assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead); From 6ffe1cefbe9ea9c132fbd104ca83719fb4a58d5a Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 17:37:36 +0200 Subject: [PATCH 06/15] refactor(furc): rename some token types to PascalCase --- furc/include/furc/front/token.hpp | 36 +++++++++++++++---------------- furc/src/front/lexer.cpp | 12 +++++------ furc/src/front/parser.cpp | 34 ++++++++++++++--------------- furc/test/lexer.cpp | 18 ++++++++-------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/furc/include/furc/front/token.hpp b/furc/include/furc/front/token.hpp index b94354d..ea70ad9 100644 --- a/furc/include/furc/front/token.hpp +++ b/furc/include/furc/front/token.hpp @@ -16,12 +16,12 @@ enum class token_t { String, Keyword, Integer, - Lparen, - Rparen, - Lbrace, - Rbrace, - Lbracket, - Rbracket, + LParen, + RParen, + LBrace, + RBrace, + LBracket, + RBracket, Semicolon, Colon, Comma, @@ -57,12 +57,12 @@ static inline std::ostream& operator<<(std::ostream& os, token_t type) { case token_t::String: return os << "string"; case token_t::Keyword: return os << "keyword"; case token_t::Integer: return os << "integer"; - case token_t::Lparen: return os << "'('"; - case token_t::Rparen: return os << "')'"; - case token_t::Lbrace: return os << "'{'"; - case token_t::Rbrace: return os << "'}'"; - case token_t::Lbracket: return os << "'['"; - case token_t::Rbracket: return os << "']'"; + case token_t::LParen: return os << "'('"; + case token_t::RParen: return os << "')'"; + case token_t::LBrace: return os << "'{'"; + case token_t::RBrace: return os << "'}'"; + case token_t::LBracket: return os << "'['"; + case token_t::RBracket: return os << "']'"; case token_t::Semicolon: return os << "';'"; case token_t::Colon: return os << "':'"; case token_t::Comma: return os << "','"; @@ -97,12 +97,12 @@ static inline std::string operator+(const std::string& str, token_t type) { case token_t::String: return str + "string"; case token_t::Keyword: return str + "keyword"; case token_t::Integer: return str + "integer"; - case token_t::Lparen: return str + "'('"; - case token_t::Rparen: return str + "')'"; - case token_t::Lbrace: return str + "'{'"; - case token_t::Rbrace: return str + "'}'"; - case token_t::Lbracket: return str + "'['"; - case token_t::Rbracket: return str + "']'"; + case token_t::LParen: return str + "'('"; + case token_t::RParen: return str + "')'"; + case token_t::LBrace: return str + "'{'"; + case token_t::RBrace: return str + "'}'"; + case token_t::LBracket: return str + "'['"; + case token_t::RBracket: return str + "']'"; case token_t::Semicolon: return str + "';'"; case token_t::Colon: return str + "':'"; case token_t::Comma: return str + "','"; diff --git a/furc/src/front/lexer.cpp b/furc/src/front/lexer.cpp index d6c6f8b..09d5c70 100644 --- a/furc/src/front/lexer.cpp +++ b/furc/src/front/lexer.cpp @@ -106,12 +106,12 @@ token_handle<> lexer::next_token() { }; static std::map s_tokens = { - { "(", token_t::Lparen }, - { ")", token_t::Rparen }, - { "{", token_t::Lbrace }, - { "}", token_t::Rbrace }, - { "[", token_t::Lbracket }, - { "]", token_t::Rbracket }, + { "(", token_t::LParen }, + { ")", token_t::RParen }, + { "{", token_t::LBrace }, + { "}", token_t::RBrace }, + { "[", token_t::LBracket }, + { "]", token_t::RBracket }, { ";", token_t::Semicolon }, { ":", token_t::Colon }, { ",", token_t::Comma }, diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index ae0591b..bd69bf7 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -46,15 +46,15 @@ ast::declaration_node_h parser::parse_declaration() { token_handle<> name = eat_token(token_t::Identifier); if (name.has_error()) return { name.location(), name.error() }; - auto tok = eat_token(token_t::Lparen); + auto tok = eat_token(token_t::LParen); if (tok.has_error()) return tok; - tok = eat_token(token_t::Rparen); + tok = eat_token(token_t::RParen); if (tok.has_error()) return tok; const auto& peek = peek_token(); if (peek.has_error()) return peek; switch (peek->type) { - case token_t::Lbrace: { + case token_t::LBrace: { ast::body_h body = parse_body(); if (body.has_error()) return body; return ast::function_definition_node_h{ first.location(), m_arena, *name, std::move(body) }; @@ -72,12 +72,12 @@ ast::declaration_node_h parser::parse_declaration() { case token_t::None: case token_t::Identifier: case token_t::Integer: - case token_t::Lparen: - case token_t::Rparen: - case token_t::Lbrace: - case token_t::Rbrace: - case token_t::Lbracket: - case token_t::Rbracket: + case token_t::LParen: + case token_t::RParen: + case token_t::LBrace: + case token_t::RBrace: + case token_t::LBracket: + case token_t::RBracket: case token_t::Semicolon: case token_t::Colon: default: { @@ -107,12 +107,12 @@ ast::statement_node_h parser::parse_statement() { } case keyword_token::If: { auto tok = next_token(); - auto err = eat_token(token_t::Lparen); + auto err = eat_token(token_t::LParen); if (err.has_error()) return err; auto cond = parse_expression(); - err = eat_token(token_t::Rparen); + err = eat_token(token_t::RParen); if (err.has_error()) return err; auto then = parse_statement(); @@ -136,7 +136,7 @@ ast::statement_node_h parser::parse_statement() { default: break; } } - case token_t::Lbrace: return ast::compound_statement_node_h{ location, m_arena, parse_body() }; + case token_t::LBrace: return ast::compound_statement_node_h{ location, m_arena, parse_body() }; default: break; } @@ -187,10 +187,10 @@ ast::expression_node_h parser::parse_expression_primary() { m_arena, handle{ tok.location(), (*tok)->string } }; } - case token_t::Lparen: { + case token_t::LParen: { auto tok = next_token(); auto node = parse_expression(); - auto err = eat_token(token_t::Rparen); + auto err = eat_token(token_t::RParen); if (err.has_error()) return err; return node; } @@ -366,15 +366,15 @@ ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& ini ast::body_h parser::parse_body() { ast::body body; - token_handle<> begin = eat_token(token_t::Lbrace); + token_handle<> begin = eat_token(token_t::LBrace); if (begin.has_error()) return begin; body.begin = begin.location(); - while (!peek_token().has_error() && peek_token()->type != token_t::None && peek_token()->type != token_t::Rbrace) { + while (!peek_token().has_error() && peek_token()->type != token_t::None && peek_token()->type != token_t::RBrace) { body.statements.push_back(parse_statement()); } - token_handle<> end = eat_token(token_t::Rbrace); + token_handle<> end = eat_token(token_t::RBrace); if (end.has_error()) return end; body.end = end.location(); diff --git a/furc/test/lexer.cpp b/furc/test/lexer.cpp index e29e3fa..6310598 100644 --- a/furc/test/lexer.cpp +++ b/furc/test/lexer.cpp @@ -18,13 +18,13 @@ using namespace std::string_view_literals; TEST(Lexer, Tokens) { lexer lexer("", "()\n\t\t{\n}[\"shto-to\"]; :,.main return func"); - EXPECT_TOKEN(lexer, (token{ token_t::Lparen })); - EXPECT_TOKEN(lexer, (token{ token_t::Rparen })); - EXPECT_TOKEN(lexer, (token{ token_t::Lbrace })); - EXPECT_TOKEN(lexer, (token{ token_t::Rbrace })); - EXPECT_TOKEN(lexer, (token{ token_t::Lbracket })); + EXPECT_TOKEN(lexer, (token{ token_t::LParen })); + EXPECT_TOKEN(lexer, (token{ token_t::RParen })); + EXPECT_TOKEN(lexer, (token{ token_t::LBrace })); + EXPECT_TOKEN(lexer, (token{ token_t::RBrace })); + EXPECT_TOKEN(lexer, (token{ token_t::LBracket })); EXPECT_TOKEN(lexer, (token{ token_t::String, "shto-to"sv })); - EXPECT_TOKEN(lexer, (token{ token_t::Rbracket })); + EXPECT_TOKEN(lexer, (token{ token_t::RBracket })); EXPECT_TOKEN(lexer, (token{ token_t::Semicolon })); EXPECT_TOKEN(lexer, (token{ token_t::Colon })); EXPECT_TOKEN(lexer, (token{ token_t::Comma })); @@ -37,10 +37,10 @@ TEST(Lexer, Tokens) { TEST(Lexer, Comments) { lexer lexer("", "(/** skibidi **/func{//)\n}"); - EXPECT_TOKEN(lexer, (token{ token_t::Lparen, "("sv })); // left out the string-view deliberately + EXPECT_TOKEN(lexer, (token{ token_t::LParen, "("sv })); // left out the string-view deliberately EXPECT_TOKEN(lexer, (token{ keyword_token::Func })); - EXPECT_TOKEN(lexer, (token{ token_t::Lbrace })); - EXPECT_TOKEN(lexer, (token{ token_t::Rbrace })); + EXPECT_TOKEN(lexer, (token{ token_t::LBrace })); + EXPECT_TOKEN(lexer, (token{ token_t::RBrace })); EXPECT_EOF(lexer); } From b6e5f1f5cc0a2792066e57df63450272439b10be Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 17:44:40 +0200 Subject: [PATCH 07/15] refactor(furc): mark AST nodes as final --- furc/include/furc/ast/declaration.hpp | 2 +- furc/include/furc/ast/expression.hpp | 8 ++++---- furc/include/furc/ast/literal.hpp | 4 ++-- furc/include/furc/ast/program.hpp | 2 +- furc/include/furc/ast/statement.hpp | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/furc/include/furc/ast/declaration.hpp b/furc/include/furc/ast/declaration.hpp index 5849faa..6d602fa 100644 --- a/furc/include/furc/ast/declaration.hpp +++ b/furc/include/furc/ast/declaration.hpp @@ -42,7 +42,7 @@ protected: front::token m_name; }; -class function_definition_node : public function_declaration_node { +class function_definition_node final : public function_declaration_node { public: function_definition_node(front::token name, body_h&& body) : function_declaration_node(name), m_body(std::move(body)) {} diff --git a/furc/include/furc/ast/expression.hpp b/furc/include/furc/ast/expression.hpp index 57f2417..4deb83d 100644 --- a/furc/include/furc/ast/expression.hpp +++ b/furc/include/furc/ast/expression.hpp @@ -27,7 +27,7 @@ protected: bool equal(const node& rhs) const override; }; -class var_read_expression_node : public expression_node { +class var_read_expression_node final : public expression_node { public: var_read_expression_node(handle&& name) : m_name(std::move(name)) {} @@ -55,7 +55,7 @@ enum class unaryop_expression_node_t { PostfixDecrement, }; -class unaryop_expression_node : public expression_node { +class unaryop_expression_node final : public expression_node { public: unaryop_expression_node(unaryop_expression_node_t type, expression_node_h&& node) : m_type(type), m_node(std::move(node)) {} @@ -95,7 +95,7 @@ enum class binop_expression_node_t { GreaterEqual, }; -class binop_expression_node : public expression_node { +class binop_expression_node final : public expression_node { public: binop_expression_node(binop_expression_node_t type, expression_node_h&& lhs, expression_node_h&& rhs) : m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {} @@ -121,7 +121,7 @@ private: expression_node_h m_rhs; }; -class var_assign_expression_node : public expression_node { +class var_assign_expression_node final : public expression_node { public: var_assign_expression_node(expression_node_h&& lhs, expression_node_h&& rhs) : m_compound(binop_expression_node_t::None), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {} diff --git a/furc/include/furc/ast/literal.hpp b/furc/include/furc/ast/literal.hpp index 1338580..7e82b1f 100644 --- a/furc/include/furc/ast/literal.hpp +++ b/furc/include/furc/ast/literal.hpp @@ -24,7 +24,7 @@ protected: bool equal(const node& rhs) const override; }; -class string_literal_node : public literal_node { +class string_literal_node final : public literal_node { public: string_literal_node(handle&& value) : m_value(std::move(value)) {} @@ -42,7 +42,7 @@ private: handle m_value; }; -class integer_literal_node : public literal_node { +class integer_literal_node final : public literal_node { public: integer_literal_node(handle&& value) : m_value(std::move(value)) {} diff --git a/furc/include/furc/ast/program.hpp b/furc/include/furc/ast/program.hpp index 62a45f3..89ce195 100644 --- a/furc/include/furc/ast/program.hpp +++ b/furc/include/furc/ast/program.hpp @@ -9,7 +9,7 @@ namespace furc { namespace ast { -class program_node : public node { +class program_node final : public node { public: program_node() = default; diff --git a/furc/include/furc/ast/statement.hpp b/furc/include/furc/ast/statement.hpp index 5440c65..7f8b9f3 100644 --- a/furc/include/furc/ast/statement.hpp +++ b/furc/include/furc/ast/statement.hpp @@ -23,7 +23,7 @@ protected: bool equal(const node& rhs) const override; }; -class return_statement_node : public statement_node { +class return_statement_node final : public statement_node { public: return_statement_node() = default; @@ -43,7 +43,7 @@ private: expression_node_h m_value; }; -class if_statement_node : public statement_node { +class if_statement_node final : public statement_node { public: if_statement_node(expression_node_h&& cond, statement_node_h&& then) : m_cond(std::move(cond)), m_then(std::move(then)) {} @@ -68,7 +68,7 @@ private: statement_node_h m_else; }; -class compound_statement_node : public statement_node { +class compound_statement_node final : public statement_node { public: compound_statement_node(body_h&& body) : m_body(std::move(body)) {} From 011e0e8bad275cfa0e26aa4dd8677f82116d5dc2 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 17:47:40 +0200 Subject: [PATCH 08/15] refactor(furc): introduce `ir_register` type --- furc/include/furc/front/ir_generator.hpp | 4 +++- furc/src/front/ir_generator.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 589621e..feb728e 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -8,6 +8,8 @@ namespace furc { namespace front { +using ir_register = std::uint32_t; + class ir_generator final : public ast::visitor { public: ir_generator() = default; @@ -36,7 +38,7 @@ private: furlang::ir::module m_module; std::unique_ptr m_currentFunction; std::shared_ptr m_currentBlock; - std::uint32_t m_registerCounter = 0; + ir_register m_registerCounter = 0; }; } // namespace front diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 3f3fde6..91e4d6a 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -43,7 +43,7 @@ void ir_generator::visit(const ast::return_statement_node& returnStmt) { void ir_generator::visit(const ast::if_statement_node& node) { node.cond()->accept(*this); - std::uint32_t cond = m_registerCounter - 1; + ir_register cond = m_registerCounter - 1; m_currentBlock->emplace(ir::operand::new_reg(cond), m_currentFunction->blocks().size(), m_currentFunction->blocks().size() + 1); @@ -105,10 +105,10 @@ static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast:: void ir_generator::visit(const ast::binop_expression_node& node) { node.lhs()->accept(*this); - std::uint32_t lhs = m_registerCounter - 1; + ir_register lhs = m_registerCounter - 1; node.rhs()->accept(*this); - std::uint32_t rhs = m_registerCounter - 1; - std::uint32_t dst = m_registerCounter++; + ir_register rhs = m_registerCounter - 1; + ir_register dst = m_registerCounter++; m_currentBlock->emplace(binary_op_instruction_t(node.type()), ir::operand::new_reg(lhs), ir::operand::new_reg(rhs), @@ -117,11 +117,11 @@ void ir_generator::visit(const ast::binop_expression_node& node) { void ir_generator::visit(const ast::var_assign_expression_node& node) { node.rhs()->accept(*this); - std::uint32_t rhs = m_registerCounter - 1; + ir_register rhs = m_registerCounter - 1; assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead); ast::var_read_expression_node_h lhs = node.lhs(); - std::uint32_t reg = m_registerCounter++; + ir_register reg = m_registerCounter++; auto compound = node.compound(); if (compound != ast::binop_expression_node_t::None) { From 045ce7be5975052d91b06a6cbc49f559db18b008 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 18:06:03 +0200 Subject: [PATCH 09/15] fix(IR): use register operands instead of variable for local variables --- furc/include/furc/front/ir_generator.hpp | 4 ++++ furc/src/front/ir_generator.cpp | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index feb728e..1d51da0 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -5,6 +5,8 @@ #include "furc/ast/visitor.hpp" #include "furlang/ir/module.hpp" +#include + namespace furc { namespace front { @@ -39,6 +41,8 @@ private: std::unique_ptr m_currentFunction; std::shared_ptr m_currentBlock; ir_register m_registerCounter = 0; + + std::unordered_map m_variables; }; } // namespace front diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 91e4d6a..c90d403 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -57,7 +57,7 @@ void ir_generator::visit(const ast::if_statement_node& node) { node.elze()->accept(*this); } - push_block(); + push_block(); // merge block } void ir_generator::visit(const ast::compound_statement_node& node) { @@ -77,8 +77,12 @@ void ir_generator::visit(const ast::integer_literal_node& node) { } void ir_generator::visit(const ast::var_read_expression_node& node) { - m_currentBlock->emplace(ir::operand::new_variable(std::string(*node.get_name())), - ir::operand::new_reg(m_registerCounter++)); + if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) { + m_currentBlock->emplace(ir::operand::new_reg(it->second), + ir::operand::new_reg(m_registerCounter++)); + } else { + throw std::runtime_error("unknown variable"); + } } void ir_generator::visit(const ast::unaryop_expression_node& node) { @@ -121,19 +125,22 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) { assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead); ast::var_read_expression_node_h lhs = node.lhs(); - ir_register reg = m_registerCounter++; + ir_register reg = 0; + 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(); if (compound != ast::binop_expression_node_t::None) { m_currentBlock->emplace(binary_op_instruction_t(compound), - ir::operand::new_variable(std::string(*lhs->get_name())), + ir::operand::new_reg(reg), ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); } else { m_currentBlock->emplace(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); } - m_currentBlock->emplace(ir::operand::new_reg(reg), - ir::operand::new_variable(std::string(*lhs->get_name()))); } furlang::ir::block_index ir_generator::push_block() { From de49674f810ad24bd9b8ff70035fe1b6782eff13 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 18:11:38 +0200 Subject: [PATCH 10/15] refactor(IR): add branch instruction on block exit in if statement --- furc/src/front/ir_generator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index c90d403..45fd96d 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -56,6 +56,7 @@ void ir_generator::visit(const ast::if_statement_node& node) { push_block(); // else block node.elze()->accept(*this); } + m_currentBlock->emplace(m_currentFunction->blocks().size()); push_block(); // merge block } From 62619cd0bca70ff6e8372065ea37cdf4fef7e44f Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 18:27:36 +0200 Subject: [PATCH 11/15] refactor(IR)!: change IR block structure It now requires an exit instruction. --- furc/include/furc/front/ir_generator.hpp | 7 +++++++ furc/src/front/ir_generator.cpp | 23 +++++++++++++--------- furc/src/main.cpp | 1 + furlang/include/furlang/ir/block.hpp | 16 +++++++++++++-- furlang/include/furlang/ir/instruction.hpp | 9 +++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 1d51da0..c459005 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -35,6 +35,13 @@ public: void visit(const ast::binop_expression_node& node) override; void visit(const ast::var_assign_expression_node& node) override; private: + template + void push(Args&&... args) { + if (!m_currentBlock->emplace(std::forward(args)...)) { + throw std::runtime_error("block exited too soon"); + } + } + furlang::ir::block_index push_block(); private: furlang::ir::module m_module; diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 45fd96d..037105e 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -24,6 +24,7 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) { for (const auto& stmt : funcDef.body()->statements) { stmt->accept(*this); } + m_currentBlock->emplace(); m_module.push(std::move(m_currentFunction)); } @@ -35,16 +36,16 @@ void ir_generator::visit(const ast::return_statement_node& returnStmt) { if (returnStmt.value().present()) { returnStmt.value()->accept(*this); - m_currentBlock->emplace(ir::operand::new_reg(m_registerCounter - 1)); + push(ir::operand::new_reg(m_registerCounter - 1)); } else { - m_currentBlock->emplace(); + push(); } } void ir_generator::visit(const ast::if_statement_node& node) { node.cond()->accept(*this); ir_register cond = m_registerCounter - 1; - m_currentBlock->emplace(ir::operand::new_reg(cond), + push(ir::operand::new_reg(cond), m_currentFunction->blocks().size(), m_currentFunction->blocks().size() + 1); @@ -68,18 +69,18 @@ void ir_generator::visit(const ast::compound_statement_node& node) { } void ir_generator::visit(const ast::string_literal_node& node) { - m_currentBlock->emplace(ir::operand::new_string(std::string(*node.value())), + push(ir::operand::new_string(std::string(*node.value())), ir::operand::new_reg(m_registerCounter++)); } void ir_generator::visit(const ast::integer_literal_node& node) { - m_currentBlock->emplace(ir::operand::new_integer(*node.value()), + push(ir::operand::new_integer(*node.value()), ir::operand::new_reg(m_registerCounter++)); } void ir_generator::visit(const ast::var_read_expression_node& node) { if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) { - m_currentBlock->emplace(ir::operand::new_reg(it->second), + push(ir::operand::new_reg(it->second), ir::operand::new_reg(m_registerCounter++)); } else { throw std::runtime_error("unknown variable"); @@ -114,7 +115,7 @@ void ir_generator::visit(const ast::binop_expression_node& node) { node.rhs()->accept(*this); ir_register rhs = m_registerCounter - 1; ir_register dst = m_registerCounter++; - m_currentBlock->emplace(binary_op_instruction_t(node.type()), + push(binary_op_instruction_t(node.type()), ir::operand::new_reg(lhs), ir::operand::new_reg(rhs), ir::operand::new_reg(dst)); @@ -135,16 +136,20 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) { auto compound = node.compound(); if (compound != ast::binop_expression_node_t::None) { - m_currentBlock->emplace(binary_op_instruction_t(compound), + push(binary_op_instruction_t(compound), ir::operand::new_reg(reg), ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); } else { - m_currentBlock->emplace(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); + push(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); } } furlang::ir::block_index ir_generator::push_block() { + if (!m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) { + throw std::runtime_error( + "block " + std::to_string(m_currentFunction->blocks().size() - 1) + " is lacking an exit"); + } ir::block_index index = m_currentFunction->blocks().size(); m_currentBlock = m_currentFunction->push(); return index; diff --git a/furc/src/main.cpp b/furc/src/main.cpp index c755159..af5a313 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -39,6 +39,7 @@ int main(void) { for (const auto& instruction : block->instructions()) { std::cout << " " << *instruction << '\n'; } + std::cout << " " << *block->exit() << '\n'; } } diff --git a/furlang/include/furlang/ir/block.hpp b/furlang/include/furlang/ir/block.hpp index 2942760..6b0c8c1 100644 --- a/furlang/include/furlang/ir/block.hpp +++ b/furlang/include/furlang/ir/block.hpp @@ -18,14 +18,26 @@ public: block() = default; public: template >> - void emplace(Args&&... args) { - m_instructions.emplace_back(std::make_unique(std::forward(args)...)); + bool emplace(Args&&... args) { + if (has_exit()) return false; + auto instr = std::make_unique(std::forward(args)...); + if (is_exit_instruction(instr->type())) { + m_exit = std::move(instr); + } else { + m_instructions.emplace_back(std::move(instr)); + } + return true; } std::vector& instructions() { return m_instructions; } const std::vector& instructions() const { return m_instructions; } + + bool has_exit() const { return m_exit != nullptr; } + value_type& exit() { return m_exit; } + const value_type& exit() const { return m_exit; } private: std::vector m_instructions; + value_type m_exit; }; } // namespace ir diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp index e3eb756..fed51f0 100644 --- a/furlang/include/furlang/ir/instruction.hpp +++ b/furlang/include/furlang/ir/instruction.hpp @@ -20,6 +20,15 @@ enum class instruction_t { Return, }; +static inline bool is_exit_instruction(instruction_t type) { + switch (type) { + case instruction_t::Branch: + case instruction_t::BranchCond: + case instruction_t::Return: return true; + default: return false; + } +} + class instruction { public: instruction() = default; From 6775ddf7d4424888471c0ddff434777add4797dd Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 21:14:56 +0200 Subject: [PATCH 12/15] fix(furlang): use std::free() instead of delete in arena --- furlang/src/arena.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/furlang/src/arena.cpp b/furlang/src/arena.cpp index 5863483..dca7d62 100644 --- a/furlang/src/arena.cpp +++ b/furlang/src/arena.cpp @@ -22,7 +22,7 @@ arena::~arena() { region* next = nullptr; for (region* region = m_head; region != nullptr; region = next) { next = region->next; - delete region; + std::free(region); // NOLINT } } From af10dfcd40df909b758de667f60eedddc72ed7f3 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 21:26:11 +0200 Subject: [PATCH 13/15] refactor(parser): organize the includes --- furc/include/furc/front/parser.hpp | 5 +---- furc/src/front/parser.cpp | 6 +++++- furc/src/main.cpp | 1 + furc/test/parser.cpp | 6 ++++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/furc/include/furc/front/parser.hpp b/furc/include/furc/front/parser.hpp index 0e60a96..a2ce903 100644 --- a/furc/include/furc/front/parser.hpp +++ b/furc/include/furc/front/parser.hpp @@ -1,10 +1,7 @@ #ifndef FURC_FRONT_PARSER_HPP #define FURC_FRONT_PARSER_HPP -#include "furc/ast/declaration.hpp" -#include "furc/ast/expression.hpp" -#include "furc/ast/literal.hpp" -#include "furc/ast/program.hpp" +#include "furc/ast/fwd.hpp" #include "furc/front/lexer.hpp" #include "furlang/arena.hpp" diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index bd69bf7..4859830 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -1,6 +1,10 @@ #include "furc/front/parser.hpp" -// #include "furc/ast/declaration.hpp" // IWYU pragma: keep +#include "furc/ast/declaration.hpp" // IWYU pragma: keep +#include "furc/ast/expression.hpp" // IWYU pragma: keep +#include "furc/ast/literal.hpp" // IWYU pragma: keep +#include "furc/ast/program.hpp" // IWYU pragma: keep +#include "furc/ast/statement.hpp" // IWYU pragma: keep #include #include diff --git a/furc/src/main.cpp b/furc/src/main.cpp index af5a313..0ef7b72 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -1,5 +1,6 @@ #ifndef LIBFURC +#include "furc/ast/program.hpp" #include "furc/front/ir_generator.hpp" #include "furc/front/parser.hpp" diff --git a/furc/test/parser.cpp b/furc/test/parser.cpp index 8c73a42..ece6334 100644 --- a/furc/test/parser.cpp +++ b/furc/test/parser.cpp @@ -1,5 +1,11 @@ #include "furc/front/parser.hpp" +#include "furc/ast/declaration.hpp" // IWYU pragma: keep +#include "furc/ast/expression.hpp" // IWYU pragma: keep +#include "furc/ast/literal.hpp" // IWYU pragma: keep +#include "furc/ast/program.hpp" // IWYU pragma: keep +#include "furc/ast/statement.hpp" // IWYU pragma: keep + #include "gtest/gtest.h" namespace { From 270a3394e46cc5e738c33b009c8f21400ca36833 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 23:05:21 +0200 Subject: [PATCH 14/15] fix(result, furlang): fix value construction for error result constructor --- furlang/include/furlang/result.hpp | 42 +++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/furlang/include/furlang/result.hpp b/furlang/include/furlang/result.hpp index e1eb04d..03948ff 100644 --- a/furlang/include/furlang/result.hpp +++ b/furlang/include/furlang/result.hpp @@ -42,12 +42,12 @@ public: explicit result(const error_type& error) : m_error(true) { - m_value.error = error; + new (&m_value.error) error_type(error); } explicit result(error_type&& error) : m_error(true) { - m_value.error = std::move(error); + new (&m_value.error) error_type(std::move(error)); } ~result() { @@ -58,17 +58,43 @@ public: } } - result(result&& other) noexcept {} + result(result&& other) noexcept + : m_error(other.m_error) { + if (m_error) { + new (&m_value.error) error_type(std::move(other.m_value.error)); + } else { + new (&m_value.result) value_type(std::move(other.m_value.result)); + } + } result& operator=(result&& other) noexcept { if (this == &other) return *this; + m_error = other.m_error; + if (m_error) { + new (&m_value.error) error_type(std::move(other.m_value.error)); + } else { + new (&m_value.result) value_type(std::move(other.m_value.result)); + } return *this; } - result(const result& other) {} + result(const result& other) + : m_error(other.m_error) { + if (m_error) { + new (&m_value.error) error_type(other.m_value.error); + } else { + new (&m_value.result) value_type(other.m_value.result); + } + } result& operator=(const result& other) { if (this == &other) return *this; + m_error = other.m_error; + if (m_error) { + new (&m_value.error) error_type(other.m_value.error); + } else { + new (&m_value.result) value_type(other.m_value.result); + } return *this; } public: @@ -134,6 +160,14 @@ private: union value { value_type result; error_type error; + + value() {} + ~value() {} + + value(value&&) noexcept {} + value& operator=(value&&) noexcept {} + value(const value&) {} + value& operator=(const value&) {} } m_value; bool m_error = false; }; From 18d99d4a86986981252ae0ed1ea099bed325cc96 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Mon, 1 Jun 2026 23:07:55 +0200 Subject: [PATCH 15/15] test(lexer): refactor the test suite to use fixtures Shoutout to my man - Krotek-man for giving me this awesome tip. I guess you really could say that the student became master. Also shoutout to stasiu for giving me his tip. --- furc/test/lexer.cpp | 103 ++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/furc/test/lexer.cpp b/furc/test/lexer.cpp index 6310598..4c056d9 100644 --- a/furc/test/lexer.cpp +++ b/furc/test/lexer.cpp @@ -1,55 +1,76 @@ +// NOLINTBEGIN(readability-identifier-naming) + #include "furc/front/lexer.hpp" +#include "furlang/result.hpp" + #include "gtest/gtest.h" +#include namespace { using namespace furc::front; using namespace std::string_view_literals; +using namespace std::string_literals; -#define EXPECT_TOKEN(lexer, t) EXPECT_EQ((lexer).next_token(), (t)); -#define EXPECT_EOF(lexer) EXPECT_EQ((lexer).next_token(), (token{})); -#define EXPECT_ERROR(lexer, err) \ - do { \ - auto handle = (lexer).next_token(); \ - EXPECT_TRUE(handle.has_error()); \ - EXPECT_EQ(handle.error(), (err)); \ - } while (0) +using token_r = furlang::result; +using lexer_case = std::pair>; -TEST(Lexer, Tokens) { - lexer lexer("", "()\n\t\t{\n}[\"shto-to\"]; :,.main return func"); - EXPECT_TOKEN(lexer, (token{ token_t::LParen })); - EXPECT_TOKEN(lexer, (token{ token_t::RParen })); - EXPECT_TOKEN(lexer, (token{ token_t::LBrace })); - EXPECT_TOKEN(lexer, (token{ token_t::RBrace })); - EXPECT_TOKEN(lexer, (token{ token_t::LBracket })); - EXPECT_TOKEN(lexer, (token{ token_t::String, "shto-to"sv })); - EXPECT_TOKEN(lexer, (token{ token_t::RBracket })); - EXPECT_TOKEN(lexer, (token{ token_t::Semicolon })); - EXPECT_TOKEN(lexer, (token{ token_t::Colon })); - EXPECT_TOKEN(lexer, (token{ token_t::Comma })); - EXPECT_TOKEN(lexer, (token{ token_t::Dot })); - EXPECT_TOKEN(lexer, (token{ token_t::Identifier, "main"sv })); - EXPECT_TOKEN(lexer, (token{ keyword_token::Return })); - EXPECT_TOKEN(lexer, (token{ keyword_token::Func })); - EXPECT_EOF(lexer); +class LexerTestingFixture : public testing::TestWithParam {}; + +TEST_P(LexerTestingFixture, LexerTest) { + auto [code, expected] = GetParam(); + + lexer lexer("", code); + auto it = expected.begin(); + while (it != expected.end()) { + const auto& expected = *it++; + + auto token = std::move(lexer.next_token()); + if (expected.has_error()) { + EXPECT_TRUE(token.has_error()); + EXPECT_EQ(token.error(), expected.error()); + } else { + EXPECT_EQ(token, expected.value()); + } + } + // EOF + EXPECT_EQ(lexer.next_token(), token{}); } -TEST(Lexer, Comments) { - lexer lexer("", "(/** skibidi **/func{//)\n}"); - EXPECT_TOKEN(lexer, (token{ token_t::LParen, "("sv })); // left out the string-view deliberately - EXPECT_TOKEN(lexer, (token{ keyword_token::Func })); - EXPECT_TOKEN(lexer, (token{ token_t::LBrace })); - EXPECT_TOKEN(lexer, (token{ token_t::RBrace })); - EXPECT_EOF(lexer); -} +INSTANTIATE_TEST_SUITE_P(EmptyTests, + LexerTestingFixture, + testing::Values(lexer_case("", {}), lexer_case(" ", {}), lexer_case("\t", {}), lexer_case("\n", {}))); -TEST(Lexer, Integers) { - lexer lexer("", "67 18446744073709551615 18446744073709551616"); - EXPECT_TOKEN(lexer, (token{ 67ULL })); - EXPECT_TOKEN(lexer, (token{ 18446744073709551615ULL })); - EXPECT_ERROR(lexer, "integer 18446744073709551616 is too big"); - EXPECT_EOF(lexer); -} +INSTANTIATE_TEST_SUITE_P(Comments, + LexerTestingFixture, + testing::Values(lexer_case("(/** skibidi **/func{//)\n}", + { { token_t::LParen }, { keyword_token::Func }, { token_t::LBrace }, { token_t::RBrace } }))); -} // namespace \ No newline at end of file +INSTANTIATE_TEST_SUITE_P(Integers, + LexerTestingFixture, + testing::Values(lexer_case("67 6\n7", { { 67 }, { 6 }, { 7 } }), + lexer_case("18446744073709551615 18446744073709551616", + { { 18446744073709551615ULL }, token_r(std::string("integer 18446744073709551616 is too big")) }))); + +INSTANTIATE_TEST_SUITE_P(Tokens, + LexerTestingFixture, + testing::Values(lexer_case("()\n\t\t{\n}[\"shto-to\"]; :,.main return func", + { { token_t::LParen }, + { token_t::RParen }, + { token_t::LBrace }, + { token_t::RBrace }, + { token_t::LBracket }, + { token_t::String, "shto-to"sv }, + { token_t::RBracket }, + { token_t::Semicolon }, + { token_t::Colon }, + { token_t::Comma }, + { token_t::Dot }, + { token_t::Identifier, "main"sv }, + { keyword_token::Return }, + { keyword_token::Func } }))); + +} // namespace + +// NOLINTEND(readability-identifier-naming) \ No newline at end of file