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