diff --git a/examples/io.funk b/examples/io.funk new file mode 100644 index 0000000000000000000000000000000000000000..5dcde6bf9ea8c79171af0f89017fb10f10595f54 --- /dev/null +++ b/examples/io.funk @@ -0,0 +1 @@ +print("Hej " + read("Vad heter du?") + "!"); diff --git a/include/ast/expression/CallNode.h b/include/ast/expression/CallNode.h new file mode 100644 index 0000000000000000000000000000000000000000..1491c28c7ccce6c878adca71b4b7c6fb24577cd4 --- /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 0000000000000000000000000000000000000000..130f73ebeb19f727762743a0c639987905fa5975 --- /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 0ff11c0d60b82e519659d71f65c149c13eebd66b..1f60ce4e5337c646d3885ac5b0f70323c473cc0d 100644 --- a/include/parser/Parser.h +++ b/include/parser/Parser.h @@ -15,6 +15,7 @@ #include "ast/Node.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" diff --git a/include/utils/Common.h b/include/utils/Common.h index 38da432c9cc113c1b5b0d21be93d00597708ab30..87fd2f0f64052bc0166c3b1a5f72cb9126394d11 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 0000000000000000000000000000000000000000..eaba781f56fbb78c565db40c5fe8d4fbab000710 --- /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 5fba43d16accbbe298b04216de9f4a0c7ade220a..d8c959c59b28cefc38aeac312497fe33ea37f425 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 0000000000000000000000000000000000000000..9c1c4221426216c1cfd21afd011e7dad660d1719 --- /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 ac96590834acdd778014dcea107f43d82820bc35..877c7dfb55e146e4c5949fe88e9ba44bc63d3038 100644 --- a/source/parser/Parser.cc +++ b/source/parser/Parser.cc @@ -268,6 +268,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 dff1c445cc1e073c524a672ac971aa4bf1e44a8c..954cbe57cbe8e59626c97dac065eae1a3dd5ade8 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';