diff --git a/examples/math.funk b/examples/math.funk index c2e5a326ef3618af56a1c7a1903c6e1c90422757..fa007cf248ae882dc9cf9b6edfe835b3cc2c43a7 100644 --- a/examples/math.funk +++ b/examples/math.funk @@ -1,3 +1,2 @@ -1+1; +numb test = 10; 2+2; -3+3; diff --git a/include/ast/declaration/DeclarationNode.h b/include/ast/declaration/DeclarationNode.h new file mode 100644 index 0000000000000000000000000000000000000000..199c3859bb501b28b4612136e721ec486876dcb1 --- /dev/null +++ b/include/ast/declaration/DeclarationNode.h @@ -0,0 +1,37 @@ +#pragma once + +#include "ast/Node.h" +#include "ast/declaration/VariableNode.h" +#include "ast/expression/LiteralNode.h" +#include "parser/Scope.h" +#include "token/Token.h" + +namespace funk +{ +class DeclarationNode : public Node +{ +public: + DeclarationNode(const SourceLocation& location, bool is_mutable, TokenType type, const String& identifier, + ExpressionNode* initializer); + + DeclarationNode(const SourceLocation& location, bool is_mutable, TokenType type, const String& identifier); + + ~DeclarationNode() override; + + String get_identifier() const; + Node* get_initializer() const; + String to_s() const override; + + String get_type() const; + + Node* evaluate() const override; + +private: + bool is_mutable; + TokenType type; + String identifier; + ExpressionNode* initializer; + bool has_initializer; +}; + +} // namespace funk diff --git a/include/ast/declaration/VariableNode.h b/include/ast/declaration/VariableNode.h new file mode 100644 index 0000000000000000000000000000000000000000..1e5b899d473e5f226329af662435de197e7ef3b9 --- /dev/null +++ b/include/ast/declaration/VariableNode.h @@ -0,0 +1,34 @@ +#pragma once + +#include "ast/Node.h" +#include "ast/expression/ExpressionNode.h" +#include "token/TokenType.h" + +namespace funk +{ + +class VariableNode : public ExpressionNode +{ +public: + VariableNode(const SourceLocation& location, const String& identifier, bool is_mutable, TokenType type, + ExpressionNode* value); + ~VariableNode() override; + + Node* evaluate() const override; + String to_s() const override; + NodeValue get_value() const override; + + bool get_mutable() const; + TokenType get_type() const; + const String& get_identifier() const; + ExpressionNode* get_value_node() const; + void set_value(ExpressionNode* new_value); + +private: + String identifier; + bool is_mutable; + TokenType type; + ExpressionNode* value; +}; + +} // namespace funk \ No newline at end of file diff --git a/include/parser/Parser.h b/include/parser/Parser.h index 48d6d316c24e6c344cd39ea554ac944b783a191e..0ff11c0d60b82e519659d71f65c149c13eebd66b 100644 --- a/include/parser/Parser.h +++ b/include/parser/Parser.h @@ -13,6 +13,7 @@ #include "ast/BlockNode.h" #include "ast/Node.h" +#include "ast/declaration/DeclarationNode.h" #include "ast/expression/BinaryOpNode.h" #include "ast/expression/LiteralNode.h" #include "ast/expression/UnaryOpNode.h" @@ -108,6 +109,12 @@ private: */ Node* parse_statement(); + /** + * @brief Parses a declaration + * @return Node* The AST node representing the declaration + */ + Node* parse_declaration(); + /** * @brief Parses an expression * @return Node* The AST node representing the expression diff --git a/source/ast/declaration/DeclarationNode.cc b/source/ast/declaration/DeclarationNode.cc new file mode 100644 index 0000000000000000000000000000000000000000..e553be171afe156714e0425cebf934e867cb191c --- /dev/null +++ b/source/ast/declaration/DeclarationNode.cc @@ -0,0 +1,60 @@ +#include "ast/declaration/DeclarationNode.h" + +namespace funk +{ +DeclarationNode::DeclarationNode(const SourceLocation& location, bool is_mutable, TokenType type, + const String& identifier, ExpressionNode* initializer) : + Node{location}, is_mutable{is_mutable}, type{type}, identifier{identifier}, initializer{initializer}, + has_initializer{true} +{ +} + +DeclarationNode::DeclarationNode( + const SourceLocation& location, bool is_mutable, TokenType type, const String& identifier) : + Node{location}, is_mutable{is_mutable}, type{type}, identifier{identifier}, initializer{nullptr}, + has_initializer{false} +{ +} + +DeclarationNode::~DeclarationNode() +{ + if (initializer) { delete initializer; } +} + +String DeclarationNode::get_identifier() const +{ + return identifier; +} + +Node* DeclarationNode::get_initializer() const +{ + return initializer; +} + +String DeclarationNode::to_s() const +{ + if (has_initializer) { return "Declaration: " + identifier + " = " + initializer->to_s(); } + return "Declaration: " + identifier; +} + +String DeclarationNode::get_type() const +{ + return token_type_to_s(type); +} + +Node* DeclarationNode::evaluate() const +{ + Node* result = has_initializer ? initializer->evaluate() : new LiteralNode(get_location(), NodeValue{}); + + ExpressionNode* initial_value = dynamic_cast<ExpressionNode*>(result); + if (!initial_value) + { + if (result) { delete result; } + throw RuntimeError(get_location(), "Failed to evaluate initializer for '" + identifier + "'"); + } + + VariableNode* var = new VariableNode(get_location(), identifier, is_mutable, type, initial_value); + Scope::instance().add(identifier, var); + return var; +} +} // namespace funk diff --git a/source/ast/declaration/VariableNode.cc b/source/ast/declaration/VariableNode.cc new file mode 100644 index 0000000000000000000000000000000000000000..0639e34998016008231c2c66f0a71eb0f6aec7d6 --- /dev/null +++ b/source/ast/declaration/VariableNode.cc @@ -0,0 +1,65 @@ +#include "ast/declaration/VariableNode.h" + +namespace funk +{ +VariableNode::VariableNode( + const SourceLocation& location, const String& identifier, bool is_mutable, TokenType type, ExpressionNode* value) : + ExpressionNode{location}, identifier{identifier}, is_mutable{is_mutable}, type{type}, value{value} +{ +} +VariableNode::~VariableNode() +{ + if (value) { delete value; } + value = nullptr; +} + +Node* VariableNode::evaluate() const +{ + return new VariableNode(get_location(), identifier, is_mutable, type, value); +} + +String VariableNode::to_s() const +{ + return "Variable: " + identifier + " = " + value->to_s(); +} + +NodeValue VariableNode::get_value() const +{ + if (value) + { + NodeValue val = value->get_value(); + return val; + } + return NodeValue{}; +} + +bool VariableNode::get_mutable() const +{ + return is_mutable; +} + +TokenType VariableNode::get_type() const +{ + return type; +} + +const String& VariableNode::get_identifier() const +{ + return identifier; +} + +ExpressionNode* VariableNode::get_value_node() const +{ + return value; +} + +void VariableNode::set_value(ExpressionNode* new_value) +{ + if (is_mutable) + { + if (value) { delete value; } + value = new_value; + } + else { throw RuntimeError(get_location(), "Cannot modify immutable variable '" + identifier + "'"); } +} +} // namespace funk \ No newline at end of file diff --git a/source/parser/Parser.cc b/source/parser/Parser.cc index 2d3c518b50405f44a60b36f684ea0dad6b4e58ad..ac96590834acdd778014dcea107f43d82820bc35 100644 --- a/source/parser/Parser.cc +++ b/source/parser/Parser.cc @@ -63,6 +63,8 @@ bool Parser::match(TokenType expected) Node* Parser::parse_statement() { LOG_DEBUG("Parse statement"); + + Node* decl{parse_declaration()}; Node* expr{parse_expression()}; if (!match(TokenType::SEMICOLON)) @@ -73,9 +75,46 @@ Node* Parser::parse_statement() throw SyntaxError(error_loc, "Expected ';'"); } + if (decl) { return decl; } return expr; } +Node* Parser::parse_declaration() +{ + LOG_DEBUG("Parse declaration"); + + bool mut{false}; + if (check(TokenType::MUT)) + { + mut = true; + next(); + } + if (check(TokenType::NUMB_TYPE) || check(TokenType::REAL_TYPE) || check(TokenType::BOOL_TYPE) || + check(TokenType::CHAR_TYPE) || check(TokenType::TEXT_TYPE)) + { + Token type{next()}; + if (check(TokenType::IDENTIFIER)) + { + Token identifier{next()}; + + if (match(TokenType::ASSIGN)) + { + // needs to be parse_statement otherwise whines about semicolon dunno if I do it correctly + Node* expr{parse_statement()}; + if (!expr) { throw SyntaxError(peek_prev().get_location(), "Expected expression after '='"); } + + ExpressionNode* expr_node = dynamic_cast<ExpressionNode*>(expr); + + return new DeclarationNode( + type.get_location(), mut, type.get_type(), identifier.get_lexeme(), expr_node); + } + return new DeclarationNode(type.get_location(), mut, type.get_type(), identifier.get_lexeme()); + } + } + + return nullptr; +} + Node* Parser::parse_expression() { LOG_DEBUG("Parse expression");