From 767970c69b4f8da74309126c0091dd9ad04f8c6a Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Fri, 12 Jun 2026 23:10:26 +0200 Subject: [PATCH] feat(furc): introduce while loop statement Refs: #2 --- furc/include/furc/ast/fwd.hpp | 8 +++- furc/include/furc/ast/statement.hpp | 49 +++++++++++++++++++++- furc/include/furc/ast/visitor.hpp | 10 ++++- furc/include/furc/front/ir_generator.hpp | 5 ++- furc/include/furc/front/token.hpp | 5 ++- furc/src/ast.cpp | 14 ++++++- furc/src/front/ir_generator.cpp | 35 ++++++++++++++-- furc/src/front/lexer.cpp | 5 ++- furc/src/front/parser.cpp | 27 ++++++++++-- furc/src/main.cpp | 14 +++---- furlang/include/furlang/ir/instruction.hpp | 3 ++ 11 files changed, 151 insertions(+), 24 deletions(-) diff --git a/furc/include/furc/ast/fwd.hpp b/furc/include/furc/ast/fwd.hpp index 0f4bca6..cb0d694 100644 --- a/furc/include/furc/ast/fwd.hpp +++ b/furc/include/furc/ast/fwd.hpp @@ -235,7 +235,13 @@ using compound_statement_node_p = using compound_statement_node_r = node_r; /**< Alias for compound_statement_node result */ +class while_statement_node; + +using while_statement_node_p = node_p; /**< Alias for a shared pointer to while_statement_node. */ + +using while_statement_node_r = node_r; /**< Alias for while_statement_node result */ + } // namespace ast } // namespace furc -#endif // FURC_AST_FWD_HPP \ No newline at end of file +#endif // FURC_AST_FWD_HPP diff --git a/furc/include/furc/ast/statement.hpp b/furc/include/furc/ast/statement.hpp index 8363a9b..91226d1 100644 --- a/furc/include/furc/ast/statement.hpp +++ b/furc/include/furc/ast/statement.hpp @@ -1,6 +1,7 @@ #ifndef FURC_AST_STATEMENT_HPP #define FURC_AST_STATEMENT_HPP +#include "furc/ast/fwd.hpp" #include "furc/ast/node.hpp" #include @@ -17,6 +18,7 @@ enum class statement_node_t { Return, /**< Return statement */ If, /**< If statement */ Compound, /**< Compound statement */ + While, /**< While loop statement. */ }; /** @@ -191,7 +193,52 @@ private: struct body m_body; /**< The body handle. */ }; +/** + * @brief while statement AST node. + */ +class while_statement_node final : public statement_node, public abstract_node { +public: + /** + * @brief Construct a new while statement AST node. + * + * @param location Node location. + * @param body Body handle. + */ + while_statement_node(struct location location, expression_node_p&& cond, statement_node_p&& body) + : abstract_node(location), m_cond(std::move(cond)), m_body(std::move(body)) {} +public: + /** + * @brief Returns this node's condition expression. + * + * @return The condition expression. + */ + const expression_node_p& condition() const { return m_cond; } + + /** + * @brief Returns this node's body handle. + * + * @return The body handle. + */ + const statement_node_p& body() const { return m_body; } +public: + /** + * @brief Returns this node's statement type. + * + * @return statement_node_t::while. + */ + statement_node_t statement_type() const override { return statement_node_t::While; } +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_p m_cond; /**< The condition expression. */ + statement_node_p m_body; /**< The body handle. */ +}; + } // namespace ast } // namespace furc -#endif // FURC_AST_STATEMENT_HPP \ No newline at end of file +#endif // FURC_AST_STATEMENT_HPP diff --git a/furc/include/furc/ast/visitor.hpp b/furc/include/furc/ast/visitor.hpp index f3cb9ac..ede00aa 100644 --- a/furc/include/furc/ast/visitor.hpp +++ b/furc/include/furc/ast/visitor.hpp @@ -122,6 +122,14 @@ public: */ virtual void visit(const compound_statement_node& node) {} + /** + * @brief Visit a while_statement_node. + * @see while_statement_node + * + * @param node Node. + */ + virtual void visit(const while_statement_node& node) {} + /** * @brief Visit an AST error. * @@ -133,4 +141,4 @@ public: } // namespace ast } // namespace furc -#endif // FURC_AST_VISITOR_HPP \ No newline at end of file +#endif // FURC_AST_VISITOR_HPP diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 03c7654..ea3adba 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -27,6 +27,7 @@ public: 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::while_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; @@ -42,7 +43,7 @@ private: } } - furlang::ir::block_index push_block(); + furlang::ir::block_index push_block(bool validate = true); private: furlang::ir::module m_module; std::unique_ptr m_currentFunction; @@ -55,4 +56,4 @@ private: } // namespace front } // namespace furc -#endif // FURC_FRONT_IR_GENERATOR_HPP \ No newline at end of file +#endif // FURC_FRONT_IR_GENERATOR_HPP diff --git a/furc/include/furc/front/token.hpp b/furc/include/furc/front/token.hpp index 7f5193f..e7c1058 100644 --- a/furc/include/furc/front/token.hpp +++ b/furc/include/furc/front/token.hpp @@ -145,6 +145,7 @@ enum class keyword_token { Return, /**< `return` */ If, /**< `if` */ Else, /**< `else` */ + While, /**< `while` */ }; static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword) { @@ -154,6 +155,7 @@ static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword) case keyword_token::Return: return os << "return"; case keyword_token::If: return os << "if"; case keyword_token::Else: return os << "else"; + case keyword_token::While: return os << "while"; } return os; } @@ -165,6 +167,7 @@ static inline std::string operator+(const std::string& str, keyword_token keywor case keyword_token::Return: return str + "return"; case keyword_token::If: return str + "if"; case keyword_token::Else: return str + "else"; + case keyword_token::While: return str + "while"; } return str; } @@ -377,4 +380,4 @@ using token_r = furlang::result; /**< Alias to a token resul } // namespace front } // namespace furc -#endif // FURC_FRONT_TOKEN_HPP \ No newline at end of file +#endif // FURC_FRONT_TOKEN_HPP diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index 46f919f..1185ab0 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -186,6 +186,18 @@ bool compound_statement_node::equal(const node& rhs) const { return statement_node::equal(rhs) && m_body == dynamic_cast(rhs).m_body; } +void while_statement_node::accept(visitor& visitor) const { + visitor.visit(*this); +} + +std::ostream& while_statement_node::print(std::ostream& os) const { + return os << m_body; +} + +bool while_statement_node::equal(const node& rhs) const { + return statement_node::equal(rhs) && m_body == dynamic_cast(rhs).m_body; +} + void program_node::accept(visitor& visitor) const { for (const auto& decl : m_declarations) { decl->accept(visitor); @@ -212,4 +224,4 @@ std::ostream& operator<<(std::ostream& os, const body& body) { return os; } -} // namespace furc::ast \ No newline at end of file +} // namespace furc::ast diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index a2f6644..a173a0d 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -4,8 +4,10 @@ #include "furc/ast/expression.hpp" // IWYU pragma: keep #include "furc/ast/literal.hpp" // IWYU pragma: keep #include "furc/ast/statement.hpp" // IWYU pragma: keep +#include "furlang/ir/instruction.hpp" #include +#include namespace furc::front { @@ -55,6 +57,33 @@ void ir_generator::visit(const ast::if_statement_node& node) { push_block(); // merge block } +void ir_generator::visit(const ast::while_statement_node& node) { + node.condition()->accept(*this); + ir_register cond = m_registerCounter - 1; + std::shared_ptr entry = m_currentBlock; + ir::block_index headerIdx = m_currentFunction->blocks().size(); + + push_block(false); // loop header + push(m_currentFunction->blocks().size()); + + push_block(); // loop condition + node.condition()->accept(*this); + std::shared_ptr condBlock = m_currentBlock; + ir_register cond2 = m_registerCounter - 1; + + push_block(false); // loop body + node.body()->accept(*this); + push(headerIdx); + + entry->emplace(ir::operand::new_reg(cond), + headerIdx, + m_currentFunction->blocks().size()); + condBlock->emplace(ir::operand::new_reg(cond2), + m_currentFunction->blocks().size() - 1, + m_currentFunction->blocks().size()); + push_block(); // merge block +} + void ir_generator::visit(const ast::compound_statement_node& node) { for (const auto& stmt : node.body().statements) { stmt.value()->accept(*this); @@ -138,8 +167,8 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) { } } -furlang::ir::block_index ir_generator::push_block() { - if (!m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) { +furlang::ir::block_index ir_generator::push_block(bool validate) { + if (validate && !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"); } @@ -148,4 +177,4 @@ furlang::ir::block_index ir_generator::push_block() { return index; } -} // namespace furc::front \ No newline at end of file +} // namespace furc::front diff --git a/furc/src/front/lexer.cpp b/furc/src/front/lexer.cpp index 366a2c3..374b106 100644 --- a/furc/src/front/lexer.cpp +++ b/furc/src/front/lexer.cpp @@ -1,5 +1,7 @@ #include "furc/front/lexer.hpp" +#include "furc/front/token.hpp" + #include #include #include @@ -97,6 +99,7 @@ token_r lexer::next_token() { { "return", keyword_token::Return }, { "if", keyword_token::If }, { "else", keyword_token::Else }, + { "while", keyword_token::While }, }; if (auto it = s_keywords.find(value); it != s_keywords.end()) return { location, it->second }; @@ -185,4 +188,4 @@ location lexer::current_location() { return { m_filename, m_row, m_cursor - m_lineStart }; } -} // namespace furc::front \ No newline at end of file +} // namespace furc::front diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index 035d5e3..b983e69 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -2,9 +2,10 @@ #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 "furc/ast/fwd.hpp" +#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 @@ -145,6 +146,24 @@ ast::statement_node_r parser::parse_statement() { std::move(cond.value()), std::move(then.value())); } + case keyword_token::While: { + auto tok = next_token(); + auto err = eat_token(token_t::LParen); + if (err.has_error()) return ast::statement_node_r(ast::error{ err.error().location }); + + auto cond = parse_expression(); + if (cond.has_error()) return ast::statement_node_r(ast::error{ cond.error().location }); + + err = eat_token(token_t::RParen); + if (err.has_error()) return ast::statement_node_r(ast::error{ err.error().location }); + + auto body = parse_statement(); + if (body.has_error()) return ast::statement_node_r(ast::error{ body.error().location }); + + return m_arena.allocate_shared(location, + std::move(cond.value()), + std::move(body.value())); + } case keyword_token::None: case keyword_token::Func: default: break; @@ -438,4 +457,4 @@ token_r parser::eat_token(token_t type) { return token; } -} // namespace furc::front \ No newline at end of file +} // namespace furc::front diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 711596e..1849438 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -11,16 +11,12 @@ int main(void) { try { std::string programStr = R"( func main() { - x = 5; - x -= 3; - if (x < 3) { - y = x * 2; - w = y; - } else { - y = x - 3; + x = 0; + y = 10; + z = 1; + while (x < y) { + x = x + z; } - w = x - y; - z = x + y; } )"; furc::front::parser parser("", programStr); diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp index 4bc6779..9b4260c 100644 --- a/furlang/include/furlang/ir/instruction.hpp +++ b/furlang/include/furlang/ir/instruction.hpp @@ -444,6 +444,9 @@ public: public: instruction_t type() const override { return instruction_t::BranchCond; } + std::vector sources() override { return { &m_condition }; } + std::vector sources() const override { return { &m_condition }; } + /** * @brief Returns this instruction's condition operand. *