From e8d2158655ba0d88b37ca5617e7d128b8881ee06 Mon Sep 17 00:00:00 2001 From: Bastian Blokland Date: Thu, 9 Jan 2020 21:54:25 +0200 Subject: [PATCH] Add support for instance calls --- src/frontend/internal/get_expr.cpp | 21 +++++++++- src/frontend/internal/get_expr.hpp | 4 ++ src/frontend/internal/get_identifier.cpp | 11 ++++++ src/frontend/internal/get_identifier.hpp | 8 +++- src/frontend/internal/typeinfer_expr.cpp | 4 ++ tests/frontend/get_call_expr_test.cpp | 40 ++++++++++++++++++++ tests/frontend/typeinfer_user_funcs_test.cpp | 14 +++++++ 7 files changed, 100 insertions(+), 2 deletions(-) diff --git a/src/frontend/internal/get_expr.cpp b/src/frontend/internal/get_expr.cpp index ef6e6796..3ab3d897 100644 --- a/src/frontend/internal/get_expr.cpp +++ b/src/frontend/internal/get_expr.cpp @@ -135,6 +135,7 @@ auto GetExpr::visit(const parse::BinaryExprNode& n) -> void { auto GetExpr::visit(const parse::CallExprNode& n) -> void { auto getIdVisitor = GetIdentifier{}; n[0].accept(&getIdVisitor); + auto* instance = getIdVisitor.getInstance(); auto identifier = getIdVisitor.getIdentifier(); auto typeParams = getIdVisitor.getTypeParams(); @@ -146,7 +147,7 @@ auto GetExpr::visit(const parse::CallExprNode& n) -> void { } auto nameToken = *identifier; - auto args = getChildExprs(n, 1); + auto args = getChildExprs(n, instance, 1); if (!args) { assert(m_context->hasErrors()); return; @@ -557,9 +558,27 @@ auto GetExpr::visit(const parse::UnionDeclStmtNode & /*unused*/) -> void { auto GetExpr::getChildExprs(const parse::Node& n, unsigned int skipAmount) -> std::optional, prog::sym::TypeSet>> { + return getChildExprs(n, nullptr, skipAmount); +} + +auto GetExpr::getChildExprs( + const parse::Node& n, const parse::Node* additionalNode, unsigned int skipAmount) + -> std::optional, prog::sym::TypeSet>> { auto isValid = true; auto argTypes = std::vector{}; auto args = std::vector{}; + if (additionalNode) { + auto arg = getSubExpr(*additionalNode, prog::sym::TypeId::inferType()); + if (arg) { + const auto argType = arg->getType(); + if (argType.isConcrete()) { + argTypes.push_back(argType); + args.push_back(std::move(arg)); + } else { + isValid = false; + } + } + } for (auto i = skipAmount; i < n.getChildCount(); ++i) { auto arg = getSubExpr(n[i], prog::sym::TypeId::inferType()); if (arg) { diff --git a/src/frontend/internal/get_expr.hpp b/src/frontend/internal/get_expr.hpp index 0d270e81..3387d2e4 100644 --- a/src/frontend/internal/get_expr.hpp +++ b/src/frontend/internal/get_expr.hpp @@ -55,6 +55,10 @@ class GetExpr final : public parse::NodeVisitor { [[nodiscard]] auto getChildExprs(const parse::Node& n, unsigned int skipAmount = 0U) -> std::optional, prog::sym::TypeSet>>; + [[nodiscard]] auto getChildExprs( + const parse::Node& n, const parse::Node* additionalNode, unsigned int skipAmount = 0U) + -> std::optional, prog::sym::TypeSet>>; + [[nodiscard]] auto getSubExpr(const parse::Node& n, prog::sym::TypeId typeHint, bool checkedConstsAccess = false) -> prog::expr::NodePtr; diff --git a/src/frontend/internal/get_identifier.cpp b/src/frontend/internal/get_identifier.cpp index 19e9c533..4908e079 100644 --- a/src/frontend/internal/get_identifier.cpp +++ b/src/frontend/internal/get_identifier.cpp @@ -1,8 +1,14 @@ #include "internal/get_identifier.hpp" +#include "parse/node_expr_field.hpp" #include "parse/node_expr_id.hpp" namespace frontend::internal { +GetIdentifier::GetIdentifier() : + m_instance{nullptr}, m_identifier{std::nullopt}, m_typeParams{std::nullopt} {} + +auto GetIdentifier::getInstance() const noexcept -> const parse::Node* { return m_instance; } + auto GetIdentifier::getIdentifier() const noexcept -> const std::optional& { return m_identifier; } @@ -16,4 +22,9 @@ auto GetIdentifier::visit(const parse::IdExprNode& n) -> void { m_typeParams = n.getTypeParams(); } +auto GetIdentifier::visit(const parse::FieldExprNode& n) -> void { + m_instance = &n[0]; + m_identifier = n.getId(); +} + } // namespace frontend::internal diff --git a/src/frontend/internal/get_identifier.hpp b/src/frontend/internal/get_identifier.hpp index ee1fa14b..8b65c296 100644 --- a/src/frontend/internal/get_identifier.hpp +++ b/src/frontend/internal/get_identifier.hpp @@ -1,21 +1,27 @@ #pragma once #include "lex/token.hpp" +#include "parse/node.hpp" #include "parse/node_visitor_optional.hpp" #include "parse/type_param_list.hpp" +#include "prog/program.hpp" #include namespace frontend::internal { class GetIdentifier final : public parse::OptionalNodeVisitor { public: - GetIdentifier() = default; + GetIdentifier(); + [[nodiscard]] auto getInstance() const noexcept -> const parse::Node*; [[nodiscard]] auto getIdentifier() const noexcept -> const std::optional&; [[nodiscard]] auto getTypeParams() const noexcept -> const std::optional&; auto visit(const parse::IdExprNode& n) -> void override; + auto visit(const parse::FieldExprNode& n) -> void override; + private: + const parse::Node* m_instance; std::optional m_identifier; std::optional m_typeParams; }; diff --git a/src/frontend/internal/typeinfer_expr.cpp b/src/frontend/internal/typeinfer_expr.cpp index 5086aea4..e304bb19 100644 --- a/src/frontend/internal/typeinfer_expr.cpp +++ b/src/frontend/internal/typeinfer_expr.cpp @@ -71,6 +71,7 @@ auto TypeInferExpr::visit(const parse::BinaryExprNode& n) -> void { auto TypeInferExpr::visit(const parse::CallExprNode& n) -> void { auto getIdVisitor = GetIdentifier{}; n[0].accept(&getIdVisitor); + auto* instance = getIdVisitor.getInstance(); auto identifier = getIdVisitor.getIdentifier(); auto typeParams = getIdVisitor.getTypeParams(); @@ -84,6 +85,9 @@ auto TypeInferExpr::visit(const parse::CallExprNode& n) -> void { auto argTypes = std::vector{}; argTypes.reserve(n.getChildCount()); + if (instance) { + argTypes.push_back(inferSubExpr(*instance)); + } for (auto i = 1U; i < n.getChildCount(); ++i) { argTypes.push_back(inferSubExpr(n[i])); } diff --git a/tests/frontend/get_call_expr_test.cpp b/tests/frontend/get_call_expr_test.cpp index 7aec69db..096072ed 100644 --- a/tests/frontend/get_call_expr_test.cpp +++ b/tests/frontend/get_call_expr_test.cpp @@ -3,6 +3,7 @@ #include "helpers.hpp" #include "prog/expr/node_const.hpp" #include "prog/expr/node_lit_int.hpp" +#include "prog/expr/node_lit_string.hpp" namespace frontend { @@ -105,6 +106,43 @@ TEST_CASE("Analyzing call expressions", "[frontend]") { std::move(args))); } + SECTION("Get instance call") { + const auto& output = ANALYZE("fun f1(int i) -> int i " + "fun f2(int i) -> int i.f1()"); + REQUIRE(output.isSuccess()); + + const auto& fDef = GET_FUNC_DEF(output, "f2", GET_TYPE_ID(output, "int")); + const auto& consts = fDef.getConsts(); + + auto args = std::vector{}; + args.push_back(prog::expr::constExprNode(consts, *consts.lookup("i"))); + CHECK( + fDef.getExpr() == + *prog::expr::callExprNode( + output.getProg(), + GET_FUNC_ID(output, "f1", GET_TYPE_ID(output, "int")), + std::move(args))); + } + + SECTION("Get instance call with args") { + const auto& output = ANALYZE("fun f1(int i, string v) -> string i.string() + v " + "fun f2(int i) -> string i.f1(\"test\")"); + REQUIRE(output.isSuccess()); + + const auto& fDef = GET_FUNC_DEF(output, "f2", GET_TYPE_ID(output, "int")); + const auto& consts = fDef.getConsts(); + + auto args = std::vector{}; + args.push_back(prog::expr::constExprNode(consts, *consts.lookup("i"))); + args.push_back(prog::expr::litStringNode(output.getProg(), "test")); + CHECK( + fDef.getExpr() == + *prog::expr::callExprNode( + output.getProg(), + GET_FUNC_ID(output, "f1", GET_TYPE_ID(output, "int"), GET_TYPE_ID(output, "string")), + std::move(args))); + } + SECTION("Diagnostics") { CHECK_DIAG( "fun f1() -> int 1 " @@ -117,6 +155,8 @@ TEST_CASE("Analyzing call expressions", "[frontend]") { CHECK_DIAG("fun f() -> int 1()", errUndeclaredCallOperator(src, {"int"}, input::Span{15, 17})); CHECK_DIAG( "fun f(int i) -> int i()", errUndeclaredCallOperator(src, {"int"}, input::Span{20, 22})); + CHECK_DIAG( + "fun f1(int i) -> int i.f2()", errUndeclaredFunc(src, "f2", {"int"}, input::Span{21, 26})); } } diff --git a/tests/frontend/typeinfer_user_funcs_test.cpp b/tests/frontend/typeinfer_user_funcs_test.cpp index d1fce2a3..51a02b07 100644 --- a/tests/frontend/typeinfer_user_funcs_test.cpp +++ b/tests/frontend/typeinfer_user_funcs_test.cpp @@ -201,6 +201,20 @@ TEST_CASE("Infer return type of user functions", "[frontend]") { CHECK(GET_FUNC_DECL(output, "f").getOutput() == GET_TYPE_ID(output, "bool")); } + SECTION("Instance function call") { + const auto& output = ANALYZE("fun double(int i) i * 2 " + "fun f() (42).double()"); + REQUIRE(output.isSuccess()); + CHECK(GET_FUNC_DECL(output, "f").getOutput() == GET_TYPE_ID(output, "int")); + } + + SECTION("Instance function call with arguments") { + const auto& output = ANALYZE("fun test(int a, string b) a.string() + b " + "fun f() (42).test(\"test\")"); + REQUIRE(output.isSuccess()); + CHECK(GET_FUNC_DECL(output, "f").getOutput() == GET_TYPE_ID(output, "string")); + } + SECTION("Dynamic call") { const auto& output = ANALYZE("fun f(delegate{int, int} op) op(1)"); REQUIRE(output.isSuccess());