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