diff --git a/include/ast/control/ControlNode.h b/include/ast/control/ControlNode.h new file mode 100644 index 0000000000000000000000000000000000000000..e959921ce0b4dae6d9c7979407262a590b503354 --- /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 0000000000000000000000000000000000000000..b692f5ff3b6c98792d2c0172a94c2f9cacdfc857 --- /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 0000000000000000000000000000000000000000..6e4a3b905fd75fb662a633e65907fbbbf497691d --- /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 1f60ce4e5337c646d3885ac5b0f70323c473cc0d..f08c405cfeb126866e9cd803818d3869a9a77534 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 0000000000000000000000000000000000000000..7b952df11561a07cbb3999391e6c12f1eebddeaa --- /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 0000000000000000000000000000000000000000..c1badfb5dd8a002fea21ee5b6911a48c54136bbd --- /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 0000000000000000000000000000000000000000..66cc08249bf69c9e9dd0d76cc2f6bddbd4cc47cc --- /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 d8c959c59b28cefc38aeac312497fe33ea37f425..458f95049f83271f288b99e601b14db0e78356b6 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 877c7dfb55e146e4c5949fe88e9ba44bc63d3038..6cb9c26acfd130b967412016effbabe4f420de98 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");