Skip to content

Commit

Permalink
Rework attribute for alternative parser
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
eido79 committed Nov 2, 2022
1 parent 859fd9c commit 73a865b
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 68 deletions.
2 changes: 1 addition & 1 deletion doc/x3/quick_reference.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -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``]]
Expand Down
92 changes: 89 additions & 3 deletions include/boost/spirit/home/x3/operator/alternative.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
34 changes: 32 additions & 2 deletions include/boost/spirit/home/x3/operator/sequence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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
57 changes: 57 additions & 0 deletions include/boost/spirit/home/x3/support/meta.hpp
Original file line number Diff line number Diff line change
@@ -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

This file was deleted.

1 change: 1 addition & 0 deletions test/x3/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
]
;

Expand Down
28 changes: 28 additions & 0 deletions test/x3/alternative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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();
}
20 changes: 20 additions & 0 deletions test/x3/sequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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();
}

0 comments on commit 73a865b

Please sign in to comment.