From d9005cfd685cf9bac83224f607304f40182c05ee Mon Sep 17 00:00:00 2001 From: Mattias Ajander <mattias@ajander.se> Date: Wed, 2 Apr 2025 14:41:55 +0200 Subject: [PATCH 1/2] Added function calls, built in standard functions for print and read --- examples/io.funk | 1 + include/ast/expression/CallNode.h | 27 +++++++++++++++++++ include/parser/BuiltIn.h | 22 ++++++++++++++++ include/parser/Parser.h | 1 + include/utils/Common.h | 2 ++ source/ast/expression/CallNode.cc | 44 +++++++++++++++++++++++++++++++ source/main.cc | 2 +- source/parser/BuiltIn.cc | 31 ++++++++++++++++++++++ source/parser/Parser.cc | 16 +++++++++++ source/utils/Exception.cc | 7 ++--- 10 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 examples/io.funk create mode 100644 include/ast/expression/CallNode.h create mode 100644 include/parser/BuiltIn.h create mode 100644 source/ast/expression/CallNode.cc create mode 100644 source/parser/BuiltIn.cc diff --git a/examples/io.funk b/examples/io.funk new file mode 100644 index 0000000..b2fbd56 --- /dev/null +++ b/examples/io.funk @@ -0,0 +1 @@ +test("Hej " + read("Vad heter du?") + "!"); diff --git a/include/ast/expression/CallNode.h b/include/ast/expression/CallNode.h new file mode 100644 index 0000000..1491c28 --- /dev/null +++ b/include/ast/expression/CallNode.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ast/expression/ExpressionNode.h" +#include "ast/expression/LiteralNode.h" +#include "logging/LogMacros.h" +#include "parser/BuiltIn.h" +#include "token/Token.h" + +namespace funk +{ +class CallNode : public ExpressionNode +{ +public: + CallNode(const Token& identifier, const Vector<ExpressionNode*>& args); + ~CallNode() override; + + Node* evaluate() const override; + + String to_s() const override; + + NodeValue get_value() const override; + +private: + Token identifier; + Vector<ExpressionNode*> args; +}; +} // namespace funk diff --git a/include/parser/BuiltIn.h b/include/parser/BuiltIn.h new file mode 100644 index 0000000..130f73e --- /dev/null +++ b/include/parser/BuiltIn.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ast/expression/CallNode.h" +#include "ast/expression/LiteralNode.h" +#include "logging/LogMacros.h" + +namespace funk +{ +/** + * @brief Forward declaration of CallNode. + */ +class CallNode; + +class BuiltIn +{ +public: + static Node* print(const CallNode& call, const Vector<ExpressionNode*>& args); + static Node* read(const CallNode& call, const Vector<ExpressionNode*>& args); + + static HashMap<String, Node* (*)(const CallNode&, const Vector<ExpressionNode*>&)> functions; +}; +} // namespace funk diff --git a/include/parser/Parser.h b/include/parser/Parser.h index 48d6d31..fe5195d 100644 --- a/include/parser/Parser.h +++ b/include/parser/Parser.h @@ -14,6 +14,7 @@ #include "ast/BlockNode.h" #include "ast/Node.h" #include "ast/expression/BinaryOpNode.h" +#include "ast/expression/CallNode.h" #include "ast/expression/LiteralNode.h" #include "ast/expression/UnaryOpNode.h" diff --git a/include/utils/Common.h b/include/utils/Common.h index 38da432..87fd2f0 100644 --- a/include/utils/Common.h +++ b/include/utils/Common.h @@ -18,7 +18,9 @@ #include <algorithm> using std::cerr; +using std::cin; using std::cout; +using std::getline; namespace funk { diff --git a/source/ast/expression/CallNode.cc b/source/ast/expression/CallNode.cc new file mode 100644 index 0000000..eaba781 --- /dev/null +++ b/source/ast/expression/CallNode.cc @@ -0,0 +1,44 @@ +#include "ast/expression/CallNode.h" + +namespace funk +{ +CallNode::CallNode(const Token& identifier, const Vector<ExpressionNode*>& args) : + ExpressionNode(identifier.get_location()), identifier(identifier), args(args) +{ +} + +CallNode::~CallNode() +{ + for (Node* arg : args) { delete arg; } +} + +Node* CallNode::evaluate() const +{ + LOG_DEBUG("Evaluating call to " + identifier.get_lexeme()); + auto it = BuiltIn::functions.find(identifier.get_lexeme()); + if (it != BuiltIn::functions.end()) { return it->second(*this, args); } + throw RuntimeError(location, "Unknown function: " + identifier.get_lexeme()); +} + +String CallNode::to_s() const +{ + String result{identifier.get_lexeme()}; + result += "( "; + for (size_t i{0}; i < args.size(); i++) + { + result += args[i]->to_s(); + if (i < args.size() - 1) { result += ", "; } + } + result += " )"; + return result; +} + +NodeValue CallNode::get_value() const +{ + ExpressionNode* result{dynamic_cast<ExpressionNode*>(evaluate())}; + if (!result) { throw RuntimeError(location, "Call did not evaluate to an expression"); } + + return result->get_value(); +} + +} // namespace funk diff --git a/source/main.cc b/source/main.cc index 5fba43d..d8c959c 100644 --- a/source/main.cc +++ b/source/main.cc @@ -116,7 +116,7 @@ void process_file(const String& file, const Config& config) LOG_INFO("Abstract Syntax Tree:"); std::istringstream stream(ast->to_s()); String line; - while (std::getline(stream, line)) { LOG_INFO(line); } + while (getline(stream, line)) { LOG_INFO(line); } } LOG_DEBUG("Evaluating AST..."); diff --git a/source/parser/BuiltIn.cc b/source/parser/BuiltIn.cc new file mode 100644 index 0000000..9c1c422 --- /dev/null +++ b/source/parser/BuiltIn.cc @@ -0,0 +1,31 @@ +#include "parser/BuiltIn.h" + +namespace funk +{ + +Node* BuiltIn::print(const CallNode& call, const Vector<ExpressionNode*>& args) +{ + LOG_DEBUG("Executing print"); + if (args.empty()) { cout << "\n"; } + for (ExpressionNode* arg : args) + { + ExpressionNode* result{dynamic_cast<ExpressionNode*>(arg->evaluate())}; + if (!result) { throw RuntimeError(arg->get_location(), "Print argument did not evaluate to an expression"); } + cout << result->get_value().cast<String>() << "\n"; + } + return new LiteralNode(call.get_location(), None{}); +} + +Node* BuiltIn::read(const CallNode& call, const Vector<ExpressionNode*>& args) +{ + LOG_DEBUG("Executing read"); + if (!args.empty()) { print(call, args); } + String input; + getline(cin, input); + return new LiteralNode(call.get_location(), input); +} + +HashMap<String, Node* (*)(const CallNode&, const Vector<ExpressionNode*>&)> BuiltIn::functions{ + {"print", print}, {"read", read}}; + +} // namespace funk diff --git a/source/parser/Parser.cc b/source/parser/Parser.cc index 2d3c518..df6721c 100644 --- a/source/parser/Parser.cc +++ b/source/parser/Parser.cc @@ -229,6 +229,22 @@ Node* Parser::parse_literal() Node* Parser::parse_identifier() { + LOG_DEBUG("Parse identifier"); + Token identifier{next()}; + + if (match(TokenType::L_PAR)) + { + Vector<ExpressionNode*> arguments{}; + if (!check(TokenType::R_PAR)) + { + do { + arguments.push_back(dynamic_cast<ExpressionNode*>(parse_expression())); + } while (match(TokenType::COMMA)); + } + if (!match(TokenType::R_PAR)) { throw SyntaxError(peek().get_location(), "Expected ')'"); } + return new CallNode(identifier, arguments); + } + return nullptr; } diff --git a/source/utils/Exception.cc b/source/utils/Exception.cc index dff1c44..954cbe5 100644 --- a/source/utils/Exception.cc +++ b/source/utils/Exception.cc @@ -25,12 +25,9 @@ String FunkError::trace() const std::istringstream stream(source); String line; - for (int i = 1; i < location.line && std::getline(stream, line); i++) {} + for (int i = 1; i < location.line && getline(stream, line); i++) {} - if (std::getline(stream, line)) - { - os << " " << line << '\n' << " " << String(location.column - 1, ' ') << "^\n"; - } + if (getline(stream, line)) { os << " " << line << '\n' << " " << String(location.column - 1, ' ') << "^\n"; } os << what() << '\n'; -- GitLab From d1bd9da389ae9ee2269a12bab51ed208e25e9390 Mon Sep 17 00:00:00 2001 From: Mattias Ajander <mattias@ajander.se> Date: Wed, 2 Apr 2025 14:42:44 +0200 Subject: [PATCH 2/2] Oops --- examples/io.funk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/io.funk b/examples/io.funk index b2fbd56..5dcde6b 100644 --- a/examples/io.funk +++ b/examples/io.funk @@ -1 +1 @@ -test("Hej " + read("Vad heter du?") + "!"); +print("Hej " + read("Vad heter du?") + "!"); -- GitLab