From f85a716ee8bfec1a2f197b049a165304a65900af Mon Sep 17 00:00:00 2001 From: Mattias Ajander <mattias@ajander.se> Date: Wed, 2 Apr 2025 18:02:34 +0200 Subject: [PATCH] If/else/if else + While implemented --- include/ast/control/ControlNode.h | 13 +++++++ include/ast/control/IfNode.h | 22 ++++++++++++ include/ast/control/WhileNode.h | 21 ++++++++++++ include/parser/Parser.h | 36 ++++++++++++++++--- source/ast/control/ControlNode.cc | 8 +++++ source/ast/control/IfNode.cc | 32 +++++++++++++++++ source/ast/control/WhileNode.cc | 27 +++++++++++++++ source/main.cc | 3 +- source/parser/Parser.cc | 57 +++++++++++++++++++++++++++++-- 9 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 include/ast/control/ControlNode.h create mode 100644 include/ast/control/IfNode.h create mode 100644 include/ast/control/WhileNode.h create mode 100644 source/ast/control/ControlNode.cc create mode 100644 source/ast/control/IfNode.cc create mode 100644 source/ast/control/WhileNode.cc diff --git a/include/ast/control/ControlNode.h b/include/ast/control/ControlNode.h new file mode 100644 index 0000000..e959921 --- /dev/null +++ b/include/ast/control/ControlNode.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ast/Node.h" + +namespace funk +{ +class ControlNode : public Node +{ +public: + ControlNode(const SourceLocation& loc); + ~ControlNode() override; +}; +} // namespace funk diff --git a/include/ast/control/IfNode.h b/include/ast/control/IfNode.h new file mode 100644 index 0000000..b692f5f --- /dev/null +++ b/include/ast/control/IfNode.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ast/BlockNode.h" +#include "ast/control/ControlNode.h" +#include "ast/expression/ExpressionNode.h" + +namespace funk +{ +class IfNode : public ControlNode +{ +public: + IfNode(ExpressionNode* condition, BlockNode* body, Node* else_branch = nullptr); + ~IfNode() override; + Node* evaluate() const override; + String to_s() const override; + +private: + ExpressionNode* condition; + BlockNode* body; + Node* else_branch; +}; +} // namespace funk diff --git a/include/ast/control/WhileNode.h b/include/ast/control/WhileNode.h new file mode 100644 index 0000000..6e4a3b9 --- /dev/null +++ b/include/ast/control/WhileNode.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ast/BlockNode.h" +#include "ast/control/ControlNode.h" +#include "ast/expression/ExpressionNode.h" + +namespace funk +{ +class WhileNode : public ControlNode +{ +public: + WhileNode(ExpressionNode* condition, BlockNode* body); + ~WhileNode() override; + Node* evaluate() const override; + String to_s() const override; + +private: + ExpressionNode* condition; + BlockNode* body; +}; +} // namespace funk diff --git a/include/parser/Parser.h b/include/parser/Parser.h index 1f60ce4..f08c405 100644 --- a/include/parser/Parser.h +++ b/include/parser/Parser.h @@ -6,18 +6,20 @@ */ #pragma once -#include "lexer/Lexer.h" -#include "parser/Scope.h" -#include "token/Token.h" -#include "utils/Common.h" - #include "ast/BlockNode.h" #include "ast/Node.h" +#include "ast/control/IfNode.h" +#include "ast/control/WhileNode.h" #include "ast/declaration/DeclarationNode.h" #include "ast/expression/BinaryOpNode.h" #include "ast/expression/CallNode.h" #include "ast/expression/LiteralNode.h" #include "ast/expression/UnaryOpNode.h" +#include "lexer/Lexer.h" +#include "logging/LogMacros.h" +#include "parser/Scope.h" +#include "token/Token.h" +#include "utils/Common.h" namespace funk { @@ -116,6 +118,30 @@ private: */ Node* parse_declaration(); + /** + * @brief Parses a block of statements + * @return Node* The AST node representing the block + */ + Node* parse_block(); + + /** + * @brief Parses a control flow statement + * @return Node* The AST node representing the control flow statement + */ + Node* parse_control(); + + /** + * @brief Parses an if statement + * @return Node* The AST node representing the if statement + */ + Node* parse_if(); + + /** + * @brief Parses a while loop + * @return Node* The AST node representing the while loop + */ + Node* parse_while(); + /** * @brief Parses an expression * @return Node* The AST node representing the expression diff --git a/source/ast/control/ControlNode.cc b/source/ast/control/ControlNode.cc new file mode 100644 index 0000000..7b952df --- /dev/null +++ b/source/ast/control/ControlNode.cc @@ -0,0 +1,8 @@ +#include "ast/control/ControlNode.h" + +namespace funk +{ +ControlNode::ControlNode(const SourceLocation& loc) : Node(loc) {} + +ControlNode::~ControlNode() = default; +} // namespace funk diff --git a/source/ast/control/IfNode.cc b/source/ast/control/IfNode.cc new file mode 100644 index 0000000..c1badfb --- /dev/null +++ b/source/ast/control/IfNode.cc @@ -0,0 +1,32 @@ +#include "ast/control/IfNode.h" + +namespace funk +{ +IfNode::IfNode(ExpressionNode* condition, BlockNode* body, Node* else_branch) : + ControlNode(condition->get_location()), condition(condition), body(body), else_branch(else_branch) +{ +} + +IfNode::~IfNode() +{ + delete condition; + delete body; + + if (else_branch) { delete else_branch; } +} + +Node* IfNode::evaluate() const +{ + LOG_DEBUG("Evaluating if statement"); + if (dynamic_cast<ExpressionNode*>(condition->evaluate())->get_value().cast<bool>()) { return body->evaluate(); } + else if (else_branch) { return else_branch->evaluate(); } + return nullptr; +} + +String IfNode::to_s() const +{ + String result{"if ( " + condition->to_s() + " ) {\n" + body->to_s() + "}"}; + if (else_branch) { result += "\n} else {\n" + else_branch->to_s() + "}"; } + return result; +} +} // namespace funk diff --git a/source/ast/control/WhileNode.cc b/source/ast/control/WhileNode.cc new file mode 100644 index 0000000..66cc082 --- /dev/null +++ b/source/ast/control/WhileNode.cc @@ -0,0 +1,27 @@ +#include "ast/control/WhileNode.h" + +namespace funk +{ +WhileNode::WhileNode(ExpressionNode* condition, BlockNode* body) : + ControlNode(condition->get_location()), condition(condition), body(body) +{ +} + +WhileNode::~WhileNode() +{ + delete condition; + delete body; +} + +Node* WhileNode::evaluate() const +{ + LOG_DEBUG("Evaluating while loop"); + while (dynamic_cast<ExpressionNode*>(condition->evaluate())->get_value().cast<bool>()) { body->evaluate(); } + return nullptr; +} + +String WhileNode::to_s() const +{ + return "while ( " + condition->to_s() + " ) {\n" + body->to_s() + "}"; +} +} // namespace funk diff --git a/source/main.cc b/source/main.cc index d8c959c..458f950 100644 --- a/source/main.cc +++ b/source/main.cc @@ -122,7 +122,8 @@ void process_file(const String& file, const Config& config) LOG_DEBUG("Evaluating AST..."); Node* res{ast->evaluate()}; LOG_DEBUG("AST evaluated!"); - LOG_INFO("Result: " + res->to_s()); + if (!res) { LOG_INFO("Result: nullptr"); } + else { LOG_INFO("Result: " + res->to_s()); } } catch (const FunkError& e) { diff --git a/source/parser/Parser.cc b/source/parser/Parser.cc index 877c7df..6cb9c26 100644 --- a/source/parser/Parser.cc +++ b/source/parser/Parser.cc @@ -1,5 +1,4 @@ #include "parser/Parser.h" -#include "logging/LogMacros.h" namespace funk { @@ -64,8 +63,14 @@ Node* Parser::parse_statement() { LOG_DEBUG("Parse statement"); + Node* control{parse_control()}; + if (control) { return control; } + Node* decl{parse_declaration()}; + if (decl) { return decl; } + Node* expr{parse_expression()}; + if (!expr) { throw SyntaxError(peek().get_location(), "Expected statement"); } if (!match(TokenType::SEMICOLON)) { @@ -75,7 +80,6 @@ Node* Parser::parse_statement() throw SyntaxError(error_loc, "Expected ';'"); } - if (decl) { return decl; } return expr; } @@ -115,6 +119,55 @@ Node* Parser::parse_declaration() return nullptr; } +Node* Parser::parse_block() +{ + LOG_DEBUG("Parse block"); + if (!match(TokenType::L_BRACE)) { throw SyntaxError(peek().get_location(), "Expected '{'"); } + + Vector<Node*> statements{}; + while (!check(TokenType::R_BRACE) && !done()) { statements.push_back(parse_statement()); } + + if (!match(TokenType::R_BRACE)) { throw SyntaxError(peek().get_location(), "Expected '}'"); } + + return new BlockNode(statements.at(0)->get_location(), statements); +} + +Node* Parser::parse_control() +{ + LOG_DEBUG("Parse control flow"); + if (match(TokenType::IF)) { return parse_if(); } + if (match(TokenType::WHILE)) { return parse_while(); } + return nullptr; +} + +Node* Parser::parse_if() +{ + LOG_DEBUG("Parse if"); + if (!match(TokenType::L_PAR)) { throw SyntaxError(peek().get_location(), "Expected '('"); } + + ExpressionNode* condition{dynamic_cast<ExpressionNode*>(parse_expression())}; + if (!match(TokenType::R_PAR)) { throw SyntaxError(peek().get_location(), "Expected ')'"); } + + BlockNode* body{dynamic_cast<BlockNode*>(parse_block())}; + Node* else_branch{nullptr}; + if (match(TokenType::ELSE)) + { + if (match(TokenType::IF)) { else_branch = parse_if(); } + else { else_branch = parse_block(); } + } + return new IfNode(condition, body, else_branch); +} + +Node* Parser::parse_while() +{ + LOG_DEBUG("Parse while loop"); + if (!match(TokenType::L_PAR)) { throw SyntaxError(peek().get_location(), "Expected '('"); } + ExpressionNode* condition{dynamic_cast<ExpressionNode*>(parse_expression())}; + if (!match(TokenType::R_PAR)) { throw SyntaxError(peek().get_location(), "Expected ')'"); } + BlockNode* body{dynamic_cast<BlockNode*>(parse_block())}; + return new WhileNode(condition, body); +} + Node* Parser::parse_expression() { LOG_DEBUG("Parse expression"); -- GitLab