From 73a865b5350e57192cb345aad5cf6fc5a72d147d Mon Sep 17 00:00:00 2001 From: eido79 <eido79@gmail.com> Date: Sun, 30 Oct 2022 22:32:51 +0100 Subject: [PATCH] Rework attribute for alternative parser Implement the following rules * Remove duplicates * Simplify variant<X> to X * Make unused_type the first type of the variant * Transform variant<unused_type, T> to optional<T> --- doc/x3/quick_reference.qbk | 2 +- .../spirit/home/x3/operator/alternative.hpp | 92 ++++++++++++++++++- .../spirit/home/x3/operator/sequence.hpp | 34 ++++++- include/boost/spirit/home/x3/support/meta.hpp | 57 ++++++++++++ .../x3/support/traits/attribute_of_binary.hpp | 62 ------------- test/x3/Jamfile | 1 + test/x3/alternative.cpp | 28 ++++++ test/x3/sequence.cpp | 20 ++++ 8 files changed, 228 insertions(+), 68 deletions(-) create mode 100644 include/boost/spirit/home/x3/support/meta.hpp delete mode 100644 include/boost/spirit/home/x3/support/traits/attribute_of_binary.hpp diff --git a/doc/x3/quick_reference.qbk b/doc/x3/quick_reference.qbk index 76c5bcb745..2ab15a6921 100644 --- a/doc/x3/quick_reference.qbk +++ b/doc/x3/quick_reference.qbk @@ -298,7 +298,7 @@ a: vector<A>, b: vector<A> --> (a > b): vector<A>``]] [[__x3_alternative__ (`a | b`)] [``a: A, b: B --> (a | b): variant<A, B> a: A, b: Unused --> (a | b): optional<A> -a: A, b: B, c: Unused --> (a | b | c): optional<variant<A, B> > +a: A, b: B, c: Unused --> (a | b | c): variant<Unused, A, B> a: Unused, b: B --> (a | b): optional<B> a: Unused, b: Unused --> (a | b): Unused a: A, b: A --> (a | b): A``]] diff --git a/include/boost/spirit/home/x3/operator/alternative.hpp b/include/boost/spirit/home/x3/operator/alternative.hpp index aeb6998ba4..8d3ef26612 100644 --- a/include/boost/spirit/home/x3/operator/alternative.hpp +++ b/include/boost/spirit/home/x3/operator/alternative.hpp @@ -7,12 +7,13 @@ #if !defined(BOOST_SPIRIT_X3_ALTERNATIVE_JAN_07_2013_1131AM) #define BOOST_SPIRIT_X3_ALTERNATIVE_JAN_07_2013_1131AM -#include <boost/spirit/home/x3/support/traits/attribute_of_binary.hpp> #include <boost/spirit/home/x3/core/parser.hpp> #include <boost/spirit/home/x3/operator/detail/alternative.hpp> - +#include <boost/spirit/home/x3/support/meta.hpp> #include <boost/variant/variant_fwd.hpp> +#include <type_traits> + namespace boost { namespace spirit { namespace x3 { template <typename Left, typename Right> @@ -53,11 +54,96 @@ namespace boost { namespace spirit { namespace x3 } }}} + +namespace boost { namespace spirit { namespace x3 { namespace detail +{ + template <typename Seq, typename... Ts> + struct add_alternative_types_impl; + + template <template<class...> typename Seq, typename... Ts> + struct add_alternative_types_impl<Seq<Ts...>> + { + using type = Seq<Ts...>; + }; + + template <template<class...> typename Seq, typename... Ts, typename U, typename... Us> + struct add_alternative_types_impl<Seq<Ts...>, U, Us...> + { + using next_sequence = conditional_t<Seq<Ts...>::template contains<U>, + Seq<Ts...>, + conditional_t<std::is_same_v<std::remove_const_t<U>, unused_type>, + typename Seq<Ts...>::template prepend<U>, + typename Seq<Ts...>::template append<U> + > + >; + + using type = typename add_alternative_types_impl<next_sequence, Us...>::type; + }; + + template <typename Seq, typename... Ts> + using add_alternative_types = typename add_alternative_types_impl<Seq, Ts...>::type; + + template <typename... Seqs> + struct merge_types_of_alternative_impl; + + template <template <class...> typename Seq1, typename... T1s, template <class...> typename Seq2, typename... T2s> + struct merge_types_of_alternative_impl<Seq1<T1s...>, Seq2<T2s...>> + { + using type = add_alternative_types<Seq1<T1s...>, T2s...>; + }; + + template <typename... Seqs> + using merge_types_of_alternative = typename merge_types_of_alternative_impl<Seqs...>::type; + + template <typename P, typename C> + struct get_types_of_alternative + { + using type = type_sequence<typename traits::attribute_of<P, C>::type>; + }; + + template <typename L, typename R, typename C> + struct get_types_of_alternative<alternative<L, R>, C> + { + using type = merge_types_of_alternative< + typename get_types_of_alternative<L, C>::type, + typename get_types_of_alternative<R, C>::type + >; + }; + + template <template <typename...> typename A, typename Seq> + struct type_sequence_to_alternative_attribute; + + template <template <typename...> typename A, template <typename...> typename Seq> + struct type_sequence_to_alternative_attribute<A, Seq<>> + { + using type = unused_type; + }; + + template <template <typename...> typename A, template <typename...> typename Seq, typename T, typename... Ts> + struct type_sequence_to_alternative_attribute<A, Seq<T, Ts...>> + { + using type = conditional_t<sizeof...(Ts) == 0, + T, + A<T, Ts...> + >; + }; + + template <template <typename...> typename A, template <typename...> typename Seq, typename T> + struct type_sequence_to_alternative_attribute<A, Seq<unused_type, T>> + { + using type = boost::optional<T>; + }; + + template <template <typename...> class A, typename P, typename C> + using attribute_of_alternative = type_sequence_to_alternative_attribute<A, + typename get_types_of_alternative<P, C>::type>; +}}}} + namespace boost { namespace spirit { namespace x3 { namespace traits { template <typename Left, typename Right, typename Context> struct attribute_of<x3::alternative<Left, Right>, Context> - : x3::detail::attribute_of_binary<boost::variant, x3::alternative, Left, Right, Context> {}; + : x3::detail::attribute_of_alternative<boost::variant, x3::alternative<Left, Right>, Context> {}; }}}} #endif diff --git a/include/boost/spirit/home/x3/operator/sequence.hpp b/include/boost/spirit/home/x3/operator/sequence.hpp index 9e6e1702fc..3edbfbfd92 100644 --- a/include/boost/spirit/home/x3/operator/sequence.hpp +++ b/include/boost/spirit/home/x3/operator/sequence.hpp @@ -7,10 +7,11 @@ #if !defined(BOOST_SPIRIT_X3_SEQUENCE_JAN_06_2013_1015AM) #define BOOST_SPIRIT_X3_SEQUENCE_JAN_06_2013_1015AM -#include <boost/spirit/home/x3/support/traits/attribute_of_binary.hpp> #include <boost/spirit/home/x3/core/parser.hpp> #include <boost/spirit/home/x3/operator/detail/sequence.hpp> #include <boost/spirit/home/x3/directive/expect.hpp> +#include <boost/spirit/home/x3/support/meta.hpp> +#include <boost/spirit/home/x3/support/unused.hpp> #include <boost/fusion/include/deque_fwd.hpp> @@ -64,11 +65,40 @@ namespace boost { namespace spirit { namespace x3 } }}} +namespace boost { namespace spirit { namespace x3 { namespace detail +{ + template <typename Attribute> + struct types_of_sequence_init : type_sequence<Attribute> {}; + template <> + struct types_of_sequence_init<unused_type> : type_sequence<> {}; + template <> + struct types_of_sequence_init<unused_type const> : type_sequence<> {}; + + template <typename P, typename C> + struct get_types_of_sequence + : types_of_sequence_init<typename traits::attribute_of<P, C>::type> {}; + + template <typename L, typename R, typename C> + struct get_types_of_sequence<x3::sequence<L, R>, C> + : get_types_of_sequence<L, C>::template extend<get_types_of_sequence<R, C>> {}; + + template <template <typename...> class A, typename T, int = T::size> + struct type_sequence_to_attribute { using type = typename T::template transfer_to<A>; }; + template <template <typename...> class A, typename T> + struct type_sequence_to_attribute<A, T, 1> : T::template transfer_to<type_identity> {}; + template <template <typename...> class A, typename T> + struct type_sequence_to_attribute<A, T, 0> { using type = unused_type; }; + + template <template <typename...> class A, typename P, typename C> + using attribute_of_sequence = type_sequence_to_attribute<A, + typename get_types_of_sequence<P, C>::type>; +}}}} + namespace boost { namespace spirit { namespace x3 { namespace traits { template <typename Left, typename Right, typename Context> struct attribute_of<x3::sequence<Left, Right>, Context> - : x3::detail::attribute_of_binary<fusion::deque, x3::sequence, Left, Right, Context> {}; + : x3::detail::attribute_of_sequence<fusion::deque, x3::sequence<Left, Right>, Context> {}; }}}} #endif diff --git a/include/boost/spirit/home/x3/support/meta.hpp b/include/boost/spirit/home/x3/support/meta.hpp new file mode 100644 index 0000000000..aed1a18d6b --- /dev/null +++ b/include/boost/spirit/home/x3/support/meta.hpp @@ -0,0 +1,57 @@ +/*============================================================================= + Copyright (c) 2020 Nikita Kniazev + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#ifndef BOOST_SPIRIT_X3_SUPPORT_META +#define BOOST_SPIRIT_X3_SUPPORT_META + +#include <boost/spirit/home/x3/support/unused.hpp> +#include <type_traits> + +namespace boost { namespace spirit { namespace x3 { namespace detail +{ + template <typename... T> + struct type_sequence + { + using type = type_sequence; + + static const int size = sizeof...(T); + + template <typename... U> + using append = type_sequence<T..., U...>; + + template <typename... U> + using prepend = type_sequence<U..., T...>; + + template <typename U> + using extend = typename U::template prepend<T...>; + + template <template <typename...> class U> + using transfer_to = U<T...>; + + template <typename U> + static constexpr bool contains = (std::is_same_v<U, T> || ...); + }; + + template <bool> + struct conditional_impl + { + template <typename T, typename> + using type = T; + }; + + template <> + struct conditional_impl<false> + { + template <typename, typename F> + using type = F; + }; + + template <bool B, typename T, typename F> + using conditional_t = typename conditional_impl<B>::template type<T, F>; +}}}} + +#endif diff --git a/include/boost/spirit/home/x3/support/traits/attribute_of_binary.hpp b/include/boost/spirit/home/x3/support/traits/attribute_of_binary.hpp deleted file mode 100644 index 2cbada00bb..0000000000 --- a/include/boost/spirit/home/x3/support/traits/attribute_of_binary.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/*============================================================================= - Copyright (c) 2020 Nikita Kniazev - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -=============================================================================*/ -#ifndef BOOST_SPIRIT_X3_SUPPORT_TRAITS_ATTRIBUTE_OF_BINARY -#define BOOST_SPIRIT_X3_SUPPORT_TRAITS_ATTRIBUTE_OF_BINARY - -#include <boost/spirit/home/x3/support/traits/attribute_of.hpp> -#include <boost/spirit/home/x3/support/unused.hpp> -#include <boost/type_traits/type_identity.hpp> - -namespace boost { namespace spirit { namespace x3 { namespace detail -{ - template <typename... T> - struct type_sequence - { - using type = type_sequence; - - static const int size = sizeof...(T); - - template <typename... U> - using append = type_sequence<T..., U...>; - - template <typename... U> - using prepend = type_sequence<U..., T...>; - - template <typename U> - using extend = typename U::template prepend<T...>; - - template <template <typename...> class U> - using transfer_to = U<T...>; - }; - - template <typename Attribute> - struct types_of_binary_init : type_sequence<Attribute> {}; - template <> - struct types_of_binary_init<unused_type> : type_sequence<> {}; - template <> - struct types_of_binary_init<unused_type const> : type_sequence<> {}; - - template <template <typename, typename> class B, typename P, typename C> - struct get_types_of_binary - : types_of_binary_init<typename traits::attribute_of<P, C>::type> {}; - template <template <typename, typename> class B, typename L, typename R, typename C> - struct get_types_of_binary<B, B<L, R>, C> - : get_types_of_binary<B, L, C>::template extend<get_types_of_binary<B, R, C>> {}; - - template <template <typename...> class A, typename T, int = T::size> - struct type_sequence_to_attribute { using type = typename T::template transfer_to<A>; }; - template <template <typename...> class A, typename T> - struct type_sequence_to_attribute<A, T, 1> : T::template transfer_to<type_identity> {}; - template <template <typename...> class A, typename T> - struct type_sequence_to_attribute<A, T, 0> { using type = unused_type; }; - - template <template <typename...> class A, template <typename, typename> class B, - typename L, typename R, typename C> - using attribute_of_binary = type_sequence_to_attribute<A, - typename get_types_of_binary<B, B<L, R>, C>::type>; -}}}} -#endif diff --git a/test/x3/Jamfile b/test/x3/Jamfile index f2671e6ba1..e03ec50170 100644 --- a/test/x3/Jamfile +++ b/test/x3/Jamfile @@ -32,6 +32,7 @@ project spirit-x3 cxx14_return_type_deduction # grep -Er "auto[^\\(=\\)]+\(" * #cxx14_std_exchange # grep -r "exchange" * cxx14_variable_templates + cxx17_fold_expressions ] ; diff --git a/test/x3/alternative.cpp b/test/x3/alternative.cpp index 6a1a63388d..be1af4faf1 100644 --- a/test/x3/alternative.cpp +++ b/test/x3/alternative.cpp @@ -45,6 +45,14 @@ struct stationary : boost::noncopyable int val; }; +template <typename Expected, typename Expr> +constexpr void test_attribute_of_alternative(Expr) +{ + using namespace boost::spirit::x3; + using namespace boost::spirit::x3::traits; + + static_assert(std::is_same_v<Expected, typename attribute_of<Expr, unused_type>::type>); +} int main() @@ -296,5 +304,25 @@ main() BOOST_TEST(test_attr("abaabb", +('a' >> attr(Foo{}) | 'b' >> attr(int{})), x)); } + { // compile checks + using namespace boost::spirit::x3; + + test_attribute_of_alternative<boost::variant<char, int>>(char_ | int_); + test_attribute_of_alternative<boost::variant<char, int, double>>(char_ | int_ | double_); + test_attribute_of_alternative<boost::variant<unused_type, char, int, double>>(char_ | int_ | double_ | eps); + test_attribute_of_alternative<boost::variant<unused_type, char, int, double>>(char_ | int_ | double_ | eps | int_); + test_attribute_of_alternative<boost::variant<unused_type, char, int, double>>(char_ | int_ | double_ | eps | int_ | eps); + test_attribute_of_alternative<unused_type>(eps); + test_attribute_of_alternative<boost::optional<int>>(eps | int_); + test_attribute_of_alternative<boost::optional<int>>(eps | int_); + test_attribute_of_alternative<unused_type>(eps | eps); + test_attribute_of_alternative<int>(int_ | int_); + test_attribute_of_alternative<int>(int_); + + test_attribute_of_alternative<boost::variant<boost::fusion::deque<int, int>, char>>((int_ >> int_) | char_); + test_attribute_of_alternative<boost::variant<unused_type, char, boost::fusion::deque<int, int>>>(char_ | (int_ >> int_) | eps); + test_attribute_of_alternative<boost::optional<boost::fusion::deque<int, int>>>(eps | (int_ >> int_)); + } + return boost::report_errors(); } diff --git a/test/x3/sequence.cpp b/test/x3/sequence.cpp index 2b94dfa8aa..8948122a23 100644 --- a/test/x3/sequence.cpp +++ b/test/x3/sequence.cpp @@ -15,6 +15,15 @@ #include "test.hpp" #include "utils.hpp" +template <typename Expected, typename Expr> +void test_attribute_of_sequence(Expr) +{ + using namespace boost::spirit::x3; + using namespace boost::spirit::x3::traits; + + static_assert(std::is_same_v<Expected, typename attribute_of<Expr, unused_type>::type>); +} + int main() { @@ -493,5 +502,16 @@ main() BOOST_TEST_EQ(v.size(), 4); } + { // compile checks only + using namespace boost::spirit::x3; + + test_attribute_of_sequence<boost::fusion::deque<int, int>>(int_ >> int_); + test_attribute_of_sequence<int>(int_ >> eps); + test_attribute_of_sequence<int>(eps >> int_); + test_attribute_of_sequence<unused_type>(eps >> eps); + test_attribute_of_sequence<int>(eps >> int_ >> eps); + test_attribute_of_sequence<boost::fusion::deque<int, int>>(int_ >> eps >> eps >> int_ >> eps); + } + return boost::report_errors(); }