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