diff --git a/include/cpp2util.h b/include/cpp2util.h index b1a7a3339b..018d74b78d 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -615,6 +615,119 @@ auto is( X const& x ) -> bool { return x == X(); } +//------------------------------------------------------------------------------------------------------------- +// Built-in is (literals) +// + +template< auto value, typename X, typename V = CPP2_TYPEOF(value)> + // This requires are needed for gcc compiler to avoid ambiguity when value is double + requires ( + (std::floating_point && !std::floating_point) || + (std::integral && !std::integral) || + (std::is_enum_v && !std::is_same_v ) + ) +constexpr auto is( X const& x ) -> bool { + return false; +} + +template< auto value, typename X > + requires (std::is_enum_v && std::is_same_v) +constexpr auto is( X const& x ) -> bool { + return x == value; +} + +template< auto value, typename X > + requires std::integral && std::integral +constexpr auto is( X const& x ) -> bool { + return x == value; +} + +// Workaroud for lack of support for floating point template argument by clang and MSVC +struct double_wrapper { + double value = {}; + + template + requires std::is_floating_point_v + constexpr double_wrapper(T d) : value(d) {} + + constexpr operator double() const { + return value; + } + + template + constexpr bool operator==(T rhs) const { + return value == rhs; + } +}; + +// This is needed to solve some issue with clang - probably clang bug. +// Without it clang is not able to match with default specialisation: +// template< auto value, typename X > auto is( X const& x ) -> bool; +#if defined(__clang__) + +template< double_wrapper value, typename X > +constexpr auto is( X const& x ) -> bool { + if constexpr (std::is_floating_point_v) + return x == value; + else + return false; +} + +#else + +template< double_wrapper value, typename X > + requires std::is_floating_point_v +constexpr auto is( X const& x ) -> bool { + return x == value; +} + +#endif + +template +struct cstring_wrapper { + char cs[N]; + + constexpr cstring_wrapper(const char (&s)[N]) noexcept { + std::copy(s, s+N, cs); + } + + constexpr bool operator==(const std::string_view& sv) const noexcept { + return std::equal(cs, cs+N-1, sv.begin(), sv.end()); // N-1 as sv is not null-terminated + } +}; + +template< cstring_wrapper value, typename X > +constexpr auto is( X const& x ) -> bool { + if constexpr (std::is_convertible_v) + return value == x; + else + return false; +} + +template + requires ( + requires { &F::operator(); } + ) +struct lambda_wrapper { + F f; + Capture capture; + + constexpr lambda_wrapper(F f, Capture capture = {}) + : f{f}, capture{capture} {} + + template + auto operator()(X&& x) const { + if constexpr (std::is_same_v) + return f(std::forward(x)); + else + return f(std::forward(x), capture); + } +}; + +template< lambda_wrapper value, typename X > +constexpr auto is( X const& x ) -> bool { + return value(x); +} //------------------------------------------------------------------------------------------------------------- // Built-in as (partial) diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 893ae3f09c..f3682689db 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -1282,8 +1282,17 @@ class cppfront auto id = std::string{}; printer.emit_to_string(&id); - assert(alt->id_expression); - emit(*alt->id_expression); + assert(alt->id_expression || alt->literal); + if (alt->id_expression) { + emit(*alt->id_expression); + if (alt->expr) { + push_need_expression_list_parens(true); + emit(*alt->expr); + pop_need_expression_list_parens(); + } + } else if (alt->literal) { + emit(*alt->literal); + } printer.emit_to_string(); assert (*alt->is_as_keyword == "is" || *alt->is_as_keyword == "as"); diff --git a/source/parse.h b/source/parse.h index 9521e34720..3842797237 100644 --- a/source/parse.h +++ b/source/parse.h @@ -667,6 +667,8 @@ struct alternative_node std::unique_ptr id_expression; source_position equal_sign; std::unique_ptr statement; + token const* literal; + std::unique_ptr expr; auto position() const -> source_position { @@ -776,8 +778,11 @@ auto alternative_node::visit(auto& v, int depth) -> void } assert (is_as_keyword); v.start(*is_as_keyword, depth+1); - assert (id_expression); - id_expression->visit(v, depth+1); + assert (id_expression || literal); + if (id_expression) + id_expression->visit(v, depth+1); + else if (literal) + literal->visit(v, depth+1); assert (statement); statement->visit(v, depth+1); v.end(*this, depth); @@ -2346,11 +2351,34 @@ class parser if (auto id = id_expression()) { n->id_expression = std::move(id); } + else if (is_literal(curr().type())) { + n->literal = &curr(); + next(); + } else { - error("expected id-expression after 'is' in inspect alternative"); + error("expected id-expression or literal after 'is' in inspect alternative"); return {}; } + if (curr().type() == lexeme::LeftParen) { + auto open_paren = curr().position(); + next(); + auto expr_list = expression_list(open_paren); + if (!expr_list) { + error("unexpected text - ( is not followed by an expression-list"); + next(); + return {}; + } + if (curr().type() != lexeme::RightParen) { + error("unexpected text - expression-list is not terminated by )"); + next(); + return {}; + } + expr_list->close_paren = curr().position(); + next(); + n->expr = std::move(expr_list); + } + if (curr().type() != lexeme::Assignment) { error("expected = at start of inspect alternative body"); return {};