diff --git a/include/cpp2util.h b/include/cpp2util.h index b1a7a3339b..754231b990 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -615,6 +615,94 @@ 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; +} //------------------------------------------------------------------------------------------------------------- // Built-in as (partial) @@ -725,6 +813,31 @@ auto as( std::variant const& x ) { throw std::bad_variant_access(); } +template< auto value, typename... Ts > +constexpr auto is_impl( std::variant const& v ) -> bool { + if (v.valueless_by_exception()) return false; + // Need to guard this with is_any otherwise the holds_alternative is illegal + if constexpr (is_any) if (std::holds_alternative(v)) return false; + + return std::visit([](auto&& arg) -> bool { + return cpp2::is(CPP2_FORWARD(arg)); + }, v); +} + +template< auto value, typename... Ts > +constexpr auto is( std::variant const& v ) -> bool { + return cpp2::is_impl(v); +} + +template< cstring_wrapper value, typename... Ts > +constexpr auto is( std::variant const& v ) -> bool { + return cpp2::is_impl(v); +} + +template< double_wrapper value, typename... Ts > +constexpr auto is( std::variant const& v ) -> bool { + return cpp2::is_impl(v); +} //------------------------------------------------------------------------------------------------------------- // std::any is and as @@ -758,6 +871,26 @@ template constexpr auto is( std::optional const& x ) -> bool { return !x.has_value(); } +template +constexpr auto is_impl( std::optional const& x ) -> bool { + if ( x.has_value() ) + return cpp2::is(x.value()); + else + return false; +} + +template +constexpr auto is( std::optional const& x ) -> bool + { return cpp2::is_impl(x); } + +template +constexpr auto is( std::optional const& x ) -> bool + { return cpp2::is_impl(x); } + +template +constexpr auto is( std::optional const& x ) -> bool + { return cpp2::is_impl(x); } + template requires std::is_same_v> constexpr auto as( X const& x ) -> auto&& diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 893ae3f09c..9292340f5b 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -1282,8 +1282,11 @@ 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); + 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..5d00a1dcd8 100644 --- a/source/parse.h +++ b/source/parse.h @@ -667,6 +667,7 @@ struct alternative_node std::unique_ptr id_expression; source_position equal_sign; std::unique_ptr statement; + token const* literal; auto position() const -> source_position { @@ -776,8 +777,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,8 +2350,12 @@ 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 {}; }