1 /*=============================================================================
2     Copyright (c) 2001-2014 Joel de Guzman
3 
4     Distributed under the Boost Software License, Version 1.0. (See accompanying
5     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 =============================================================================*/
7 #if !defined(BOOST_SPIRIT_X3_ALTERNATIVE_DETAIL_JAN_07_2013_1245PM)
8 #define BOOST_SPIRIT_X3_ALTERNATIVE_DETAIL_JAN_07_2013_1245PM
9 
10 #include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
11 #include <boost/spirit/home/x3/support/traits/is_variant.hpp>
12 #include <boost/spirit/home/x3/support/traits/tuple_traits.hpp>
13 #include <boost/spirit/home/x3/support/traits/move_to.hpp>
14 #include <boost/spirit/home/x3/support/traits/variant_has_substitute.hpp>
15 #include <boost/spirit/home/x3/support/traits/variant_find_substitute.hpp>
16 #include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
17 #include <boost/variant/variant.hpp>
18 
19 #include <boost/mpl/copy_if.hpp>
20 #include <boost/mpl/not.hpp>
21 #include <boost/mpl/if.hpp>
22 #include <boost/mpl/insert_range.hpp>
23 #include <boost/mpl/eval_if.hpp>
24 #include <boost/mpl/vector.hpp>
25 
26 #include <boost/fusion/include/front.hpp>
27 
28 #include <boost/type_traits/is_same.hpp>
29 
30 namespace boost { namespace spirit { namespace x3
31 {
32     template <typename Left, typename Right>
33     struct alternative;
34 }}}
35 
36 namespace boost { namespace spirit { namespace x3 { namespace detail
37 {
38     struct pass_variant_unused
39     {
40         typedef unused_type type;
41 
42         template <typename T>
43         static unused_type
callboost::spirit::x3::detail::pass_variant_unused44         call(T&)
45         {
46             return unused_type();
47         }
48     };
49 
50     template <typename Attribute>
51     struct pass_variant_used
52     {
53         typedef Attribute& type;
54 
55         static Attribute&
callboost::spirit::x3::detail::pass_variant_used56         call(Attribute& v)
57         {
58             return v;
59         }
60     };
61 
62     template <>
63     struct pass_variant_used<unused_type> : pass_variant_unused {};
64 
65     template <typename Parser, typename Attribute, typename Context
66       , typename Enable = void>
67     struct pass_parser_attribute
68     {
69         typedef typename
70             traits::attribute_of<Parser, Context>::type
71         attribute_type;
72         typedef typename
73             traits::variant_find_substitute<Attribute, attribute_type>::type
74         substitute_type;
75 
76         typedef typename
77             mpl::if_<
78                 is_same<Attribute, substitute_type>
79               , Attribute&
80               , substitute_type
81             >::type
82         type;
83 
84         template <typename Attribute_>
85         static Attribute_&
callboost::spirit::x3::detail::pass_parser_attribute86         call(Attribute_& attr, mpl::true_)
87         {
88             return attr;
89         }
90 
91         template <typename Attribute_>
92         static type
callboost::spirit::x3::detail::pass_parser_attribute93         call(Attribute_&, mpl::false_)
94         {
95             return type();
96         }
97 
98         template <typename Attribute_>
99         static type
callboost::spirit::x3::detail::pass_parser_attribute100         call(Attribute_& attr)
101         {
102             return call(attr, is_same<Attribute_, typename remove_reference<type>::type>());
103         }
104     };
105 
106     // Pass non-variant attributes as-is
107     template <typename Parser, typename Attribute, typename Context
108       , typename Enable = void>
109     struct pass_non_variant_attribute
110     {
111         typedef Attribute& type;
112 
113         static Attribute&
callboost::spirit::x3::detail::pass_non_variant_attribute114         call(Attribute& attr)
115         {
116             return attr;
117         }
118     };
119 
120     // Unwrap single element sequences
121     template <typename Parser, typename Attribute, typename Context>
122     struct pass_non_variant_attribute<Parser, Attribute, Context,
123         typename enable_if<traits::is_size_one_sequence<Attribute>>::type>
124     {
125         typedef typename remove_reference<
126             typename fusion::result_of::front<Attribute>::type>::type
127         attr_type;
128 
129         typedef pass_parser_attribute<Parser, attr_type, Context> pass;
130         typedef typename pass::type type;
131 
132         template <typename Attribute_>
133         static type
callboost::spirit::x3::detail::pass_non_variant_attribute134         call(Attribute_& attr)
135         {
136             return pass::call(fusion::front(attr));
137         }
138     };
139 
140     template <typename Parser, typename Attribute, typename Context>
141     struct pass_parser_attribute<Parser, Attribute, Context,
142         typename enable_if_c<(!traits::is_variant<Attribute>::value)>::type>
143         : pass_non_variant_attribute<Parser, Attribute, Context>
144     {};
145 
146     template <typename Parser, typename Context>
147     struct pass_parser_attribute<Parser, unused_type, Context>
148         : pass_variant_unused {};
149 
150     template <typename Parser, typename Attribute, typename Context>
151     struct pass_variant_attribute :
152         mpl::if_c<traits::has_attribute<Parser, Context>::value
153           , pass_parser_attribute<Parser, Attribute, Context>
154           , pass_variant_unused>::type
155     {
156         typedef typename mpl::false_ is_alternative;
157     };
158 
159     template <typename L, typename R, typename Attribute, typename Context>
160     struct pass_variant_attribute<alternative<L, R>, Attribute, Context> :
161         mpl::if_c<traits::has_attribute<alternative<L, R>, Context>::value
162           , pass_variant_used<Attribute>
163           , pass_variant_unused>::type
164     {
165         typedef typename mpl::true_ is_alternative;
166     };
167 
168     template <typename L, typename R, typename C>
169     struct get_alternative_types
170     {
171         typedef
172             mpl::vector<
173                 typename traits::attribute_of<L, C>::type
174               , typename traits::attribute_of<R, C>::type
175             >
176         type;
177     };
178 
179     template <typename LL, typename LR, typename R, typename C>
180     struct get_alternative_types<alternative<LL, LR>, R, C>
181         : mpl::push_back< typename get_alternative_types<LL, LR, C>::type
182                         , typename traits::attribute_of<R, C>::type> {};
183 
184     template <typename L, typename RL, typename RR, typename C>
185     struct get_alternative_types<L, alternative<RL, RR>, C>
186         : mpl::push_front< typename get_alternative_types<RL, RR, C>::type
187                          , typename traits::attribute_of<L, C>::type> {};
188 
189     template <typename LL, typename LR, typename RL, typename RR, typename C>
190     struct get_alternative_types<alternative<LL, LR>, alternative<RL, RR>, C>
191     {
192         typedef typename get_alternative_types<LL, LR, C>::type left;
193         typedef typename get_alternative_types<RL, RR, C>::type right;
194         typedef typename
195             mpl::insert_range<left, typename mpl::end<left>::type, right>::type
196         type;
197     };
198 
199     template <typename L, typename R, typename C>
200     struct attribute_of_alternative
201     {
202         // Get all alternative attribute types
203         typedef typename get_alternative_types<L, R, C>::type all_types;
204 
205         // Filter all unused_types
206         typedef typename
207             mpl::copy_if<
208                 all_types
209               , mpl::not_<is_same<mpl::_1, unused_type>>
210               , mpl::back_inserter<mpl::vector<>>
211             >::type
212         filtered_types;
213 
214         // Build a variant if filtered_types is not empty,
215         // else just return unused_type
216         typedef typename
217             mpl::eval_if<
218                 mpl::empty<filtered_types>
219               , mpl::identity<unused_type>
220               , make_variant_over<filtered_types>
221             >::type
222         type;
223     };
224 
225     template <typename IsAlternative>
226     struct move_if_not_alternative
227     {
228         template<typename T1, typename T2>
callboost::spirit::x3::detail::move_if_not_alternative229         static void call(T1& /* attr_ */, T2& /* attr */) {}
230     };
231 
232     template <>
233     struct move_if_not_alternative<mpl::false_ /*is alternative*/>
234     {
235         template<typename T1, typename T2>
callboost::spirit::x3::detail::move_if_not_alternative236         static void call(T1& attr_, T2& attr)
237         {
238             traits::move_to(attr_, attr);
239         }
240     };
241 
242     template <typename Parser, typename Iterator, typename Context
243       , typename RContext, typename Attribute>
parse_alternative(Parser const & p,Iterator & first,Iterator const & last,Context const & context,RContext & rcontext,Attribute & attr)244     bool parse_alternative(Parser const& p, Iterator& first, Iterator const& last
245       , Context const& context, RContext& rcontext, Attribute& attr)
246     {
247         typedef detail::pass_variant_attribute<Parser, Attribute, Context> pass;
248 
249         typename pass::type attr_ = pass::call(attr);
250         if (p.parse(first, last, context, rcontext, attr_))
251         {
252             move_if_not_alternative<typename pass::is_alternative>::call(attr_, attr);
253             return true;
254         }
255         return false;
256     }
257 
258 
259     template <typename Left, typename Right, typename Context, typename RContext>
260     struct parse_into_container_impl<alternative<Left, Right>, Context, RContext>
261     {
262         typedef alternative<Left, Right> parser_type;
263 
264         template <typename Iterator, typename Attribute>
callboost::spirit::x3::detail::parse_into_container_impl265         static bool call(
266             parser_type const& parser
267           , Iterator& first, Iterator const& last
268           , Context const& context, RContext& rcontext, Attribute& attr, mpl::true_)
269         {
270             return parse_alternative(parser, first, last, context, rcontext, attr);
271         }
272 
273         template <typename Iterator, typename Attribute>
callboost::spirit::x3::detail::parse_into_container_impl274         static bool call(
275             parser_type const& parser
276           , Iterator& first, Iterator const& last
277           , Context const& context, RContext& rcontext, Attribute& attr, mpl::false_)
278         {
279             return parse_into_container_base_impl<parser_type>::call(
280                 parser, first, last, context, rcontext, attr);
281         }
282 
283         template <typename Iterator, typename Attribute>
callboost::spirit::x3::detail::parse_into_container_impl284         static bool call(
285             parser_type const& parser
286           , Iterator& first, Iterator const& last
287           , Context const& context, RContext& rcontext, Attribute& attr)
288         {
289             typedef typename
290                 traits::attribute_of<parser_type, Context>::type
291             attribute_type;
292 
293             return call(parser, first, last, context, rcontext, attr
294                 , traits::variant_has_substitute<attribute_type, Attribute>());
295         }
296     };
297 
298 }}}}
299 
300 #endif
301