Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for instance calls #47

Merged
merged 1 commit into from Jan 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/frontend/internal/get_expr.cpp
Expand Up @@ -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();

Expand All @@ -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;
Expand Down Expand Up @@ -557,9 +558,27 @@ auto GetExpr::visit(const parse::UnionDeclStmtNode & /*unused*/) -> void {

auto GetExpr::getChildExprs(const parse::Node& n, unsigned int skipAmount)
-> std::optional<std::pair<std::vector<prog::expr::NodePtr>, prog::sym::TypeSet>> {
return getChildExprs(n, nullptr, skipAmount);
}

auto GetExpr::getChildExprs(
const parse::Node& n, const parse::Node* additionalNode, unsigned int skipAmount)
-> std::optional<std::pair<std::vector<prog::expr::NodePtr>, prog::sym::TypeSet>> {
auto isValid = true;
auto argTypes = std::vector<prog::sym::TypeId>{};
auto args = std::vector<prog::expr::NodePtr>{};
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) {
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/internal/get_expr.hpp
Expand Up @@ -55,6 +55,10 @@ class GetExpr final : public parse::NodeVisitor {
[[nodiscard]] auto getChildExprs(const parse::Node& n, unsigned int skipAmount = 0U)
-> std::optional<std::pair<std::vector<prog::expr::NodePtr>, prog::sym::TypeSet>>;

[[nodiscard]] auto getChildExprs(
const parse::Node& n, const parse::Node* additionalNode, unsigned int skipAmount = 0U)
-> std::optional<std::pair<std::vector<prog::expr::NodePtr>, prog::sym::TypeSet>>;

[[nodiscard]] auto
getSubExpr(const parse::Node& n, prog::sym::TypeId typeHint, bool checkedConstsAccess = false)
-> prog::expr::NodePtr;
Expand Down
11 changes: 11 additions & 0 deletions 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<lex::Token>& {
return m_identifier;
}
Expand All @@ -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
8 changes: 7 additions & 1 deletion 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 <optional>

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<lex::Token>&;
[[nodiscard]] auto getTypeParams() const noexcept -> const std::optional<parse::TypeParamList>&;

auto visit(const parse::IdExprNode& n) -> void override;

auto visit(const parse::FieldExprNode& n) -> void override;

private:
const parse::Node* m_instance;
std::optional<lex::Token> m_identifier;
std::optional<parse::TypeParamList> m_typeParams;
};
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/internal/typeinfer_expr.cpp
Expand Up @@ -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();

Expand All @@ -84,6 +85,9 @@ auto TypeInferExpr::visit(const parse::CallExprNode& n) -> void {

auto argTypes = std::vector<prog::sym::TypeId>{};
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]));
}
Expand Down
40 changes: 40 additions & 0 deletions tests/frontend/get_call_expr_test.cpp
Expand Up @@ -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 {

Expand Down Expand Up @@ -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<prog::expr::NodePtr>{};
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<prog::expr::NodePtr>{};
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 "
Expand All @@ -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}));
}
}

Expand Down
14 changes: 14 additions & 0 deletions tests/frontend/typeinfer_user_funcs_test.cpp
Expand Up @@ -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());
Expand Down