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_PARSE_INTO_CONTAINER_JAN_15_2013_0957PM) 8 #define BOOST_SPIRIT_X3_PARSE_INTO_CONTAINER_JAN_15_2013_0957PM 9 10 #include <type_traits> 11 12 #include <boost/spirit/home/x3/support/traits/container_traits.hpp> 13 #include <boost/spirit/home/x3/support/traits/attribute_of.hpp> 14 #include <boost/spirit/home/x3/support/traits/pseudo_attribute.hpp> 15 #include <boost/spirit/home/x3/support/traits/handles_container.hpp> 16 #include <boost/spirit/home/x3/support/traits/has_attribute.hpp> 17 #include <boost/spirit/home/x3/support/traits/is_substitute.hpp> 18 #include <boost/spirit/home/x3/support/traits/move_to.hpp> 19 #include <boost/mpl/and.hpp> 20 #include <boost/fusion/include/at_key.hpp> 21 #include <boost/fusion/include/front.hpp> 22 #include <boost/fusion/include/back.hpp> 23 #include <boost/variant/apply_visitor.hpp> 24 #include <iterator> // for std::make_move_iterator 25 26 namespace boost { namespace spirit { namespace x3 { namespace detail 27 { 28 template <typename Attribute, typename Value> 29 struct saver_visitor; 30 31 // save to associative fusion container where Key is simple type 32 template <typename Key, typename Enable = void> 33 struct save_to_assoc_attr 34 { 35 template <typename Value, typename Attribute> callboost::spirit::x3::detail::save_to_assoc_attr36 static void call(const Key, Value& value, Attribute& attr) 37 { 38 traits::move_to(value, fusion::at_key<Key>(attr)); 39 } 40 }; 41 42 // save to associative fusion container where Key 43 // is variant over possible keys 44 template <typename ...T> 45 struct save_to_assoc_attr<variant<T...> > 46 { 47 typedef variant<T...> variant_t; 48 49 template <typename Value, typename Attribute> callboost::spirit::x3::detail::save_to_assoc_attr50 static void call(const variant_t key, Value& value, Attribute& attr) 51 { 52 apply_visitor(saver_visitor<Attribute, Value>(attr, value), key); 53 } 54 }; 55 56 template <typename Attribute, typename Value> 57 struct saver_visitor : boost::static_visitor<void> 58 { saver_visitorboost::spirit::x3::detail::saver_visitor59 saver_visitor(Attribute& attr, Value& value) 60 : attr(attr), value(value) {}; 61 62 Attribute& attr; 63 Value& value; 64 65 template <typename Key> operator ()boost::spirit::x3::detail::saver_visitor66 void operator()(Key) const 67 { 68 save_to_assoc_attr<Key>::call(Key(), value,attr); 69 } 70 }; 71 72 template <typename Parser, typename Container, typename Context> 73 struct parser_accepts_container 74 : traits::is_substitute< 75 typename traits::attribute_of<Parser, Context>::type 76 , Container 77 > 78 {}; 79 80 template <typename Parser> 81 struct parse_into_container_base_impl 82 { 83 private: 84 85 // Parser has attribute (synthesize; Attribute is a container) 86 template <typename Iterator, typename Context 87 , typename RContext, typename Attribute> call_synthesize_xboost::spirit::x3::detail::parse_into_container_base_impl88 static bool call_synthesize_x( 89 Parser const& parser 90 , Iterator& first, Iterator const& last 91 , Context const& context, RContext& rcontext, Attribute& attr, mpl::false_) 92 { 93 // synthesized attribute needs to be value initialized 94 using value_type = typename traits::container_value<Attribute>::type; 95 value_type val{}; 96 97 if (!parser.parse(first, last, context, rcontext, val)) 98 return false; 99 100 // push the parsed value into our attribute 101 traits::push_back(attr, static_cast<value_type&&>(val)); 102 return true; 103 } 104 105 // Parser has attribute (synthesize; Attribute is a container) 106 template <typename Iterator, typename Context 107 , typename RContext, typename Attribute> call_synthesize_xboost::spirit::x3::detail::parse_into_container_base_impl108 static bool call_synthesize_x( 109 Parser const& parser 110 , Iterator& first, Iterator const& last 111 , Context const& context, RContext& rcontext, Attribute& attr, mpl::true_) 112 { 113 return parser.parse(first, last, context, rcontext, attr); 114 } 115 116 // Parser has attribute (synthesize; Attribute is a container) 117 template <typename Iterator, typename Context 118 , typename RContext, typename Attribute> call_synthesizeboost::spirit::x3::detail::parse_into_container_base_impl119 static bool call_synthesize( 120 Parser const& parser 121 , Iterator& first, Iterator const& last 122 , Context const& context, RContext& rcontext, Attribute& attr) 123 { 124 typedef 125 parser_accepts_container<Parser, Attribute, Context> 126 parser_accepts_container; 127 128 return call_synthesize_x(parser, first, last, context, rcontext, attr 129 , parser_accepts_container()); 130 } 131 132 // Parser has attribute (synthesize; Attribute is a single element fusion sequence) 133 template <typename Iterator, typename Context 134 , typename RContext, typename Attribute> call_synthesize_into_fusion_seqboost::spirit::x3::detail::parse_into_container_base_impl135 static bool call_synthesize_into_fusion_seq(Parser const& parser 136 , Iterator& first, Iterator const& last, Context const& context 137 , RContext& rcontext, Attribute& attr, mpl::false_ /* is_associative */) 138 { 139 static_assert(traits::has_size<Attribute, 1>::value, 140 "Expecting a single element fusion sequence"); 141 return call_synthesize(parser, first, last, context, rcontext, 142 fusion::front(attr)); 143 } 144 145 // Parser has attribute (synthesize; Attribute is fusion map sequence) 146 template <typename Iterator, typename Context, typename RContext, typename Attribute> call_synthesize_into_fusion_seqboost::spirit::x3::detail::parse_into_container_base_impl147 static bool call_synthesize_into_fusion_seq( 148 Parser const& parser 149 , Iterator& first, Iterator const& last, Context const& context 150 , RContext& rcontext, Attribute& attr, mpl::true_ /*is_associative*/) 151 { 152 using attribute_type = typename traits::attribute_of<Parser, Context>::type; 153 static_assert(traits::has_size<attribute_type, 2>::value, 154 "To parse directly into fusion map parser must produce 2 element attr"); 155 156 // use type of first element of attribute as key 157 using key = typename std::remove_reference< 158 typename fusion::result_of::front<attribute_type>::type>::type; 159 160 attribute_type attr_; 161 if (!parser.parse(first, last, context, rcontext, attr_)) 162 return false; 163 164 save_to_assoc_attr<key>::call(fusion::front(attr_), fusion::back(attr_), attr); 165 return true; 166 } 167 168 template <typename Iterator, typename Context, typename RContext, typename Attribute> call_synthesize_dispatch_by_seqboost::spirit::x3::detail::parse_into_container_base_impl169 static bool call_synthesize_dispatch_by_seq(Parser const& parser 170 , Iterator& first, Iterator const& last, Context const& context 171 , RContext& rcontext, Attribute& attr, mpl::true_ /*is_sequence*/) 172 { 173 return call_synthesize_into_fusion_seq( 174 parser, first, last, context, rcontext, attr 175 , fusion::traits::is_associative<Attribute>()); 176 } 177 178 template <typename Iterator, typename Context, typename RContext, typename Attribute> call_synthesize_dispatch_by_seqboost::spirit::x3::detail::parse_into_container_base_impl179 static bool call_synthesize_dispatch_by_seq(Parser const& parser 180 , Iterator& first, Iterator const& last, Context const& context 181 , RContext& rcontext, Attribute& attr, mpl::false_ /*is_sequence*/) 182 { 183 return call_synthesize(parser, first, last, context, rcontext, attr); 184 } 185 186 // Parser has attribute (synthesize) 187 template <typename Iterator, typename Context, typename RContext, typename Attribute> callboost::spirit::x3::detail::parse_into_container_base_impl188 static bool call(Parser const& parser 189 , Iterator& first, Iterator const& last, Context const& context 190 , RContext& rcontext, Attribute& attr, mpl::true_) 191 { 192 return call_synthesize_dispatch_by_seq(parser, first, last, context, rcontext, attr 193 , fusion::traits::is_sequence<Attribute>()); 194 } 195 196 // Parser has no attribute (pass unused) 197 template <typename Iterator, typename Context, typename RContext, typename Attribute> callboost::spirit::x3::detail::parse_into_container_base_impl198 static bool call( 199 Parser const& parser 200 , Iterator& first, Iterator const& last, Context const& context 201 , RContext& rcontext, Attribute& /* attr */, mpl::false_) 202 { 203 return parser.parse(first, last, context, rcontext, unused); 204 } 205 206 207 public: 208 209 template <typename Iterator, typename Context, typename RContext, typename Attribute> callboost::spirit::x3::detail::parse_into_container_base_impl210 static bool call(Parser const& parser 211 , Iterator& first, Iterator const& last, Context const& context 212 , RContext& rcontext, Attribute& attr) 213 { 214 return call(parser, first, last, context, rcontext, attr 215 , mpl::bool_<traits::has_attribute<Parser, Context>::value>()); 216 } 217 }; 218 219 template <typename Parser, typename Context, typename RContext, typename Enable = void> 220 struct parse_into_container_impl : parse_into_container_base_impl<Parser> {}; 221 222 template <typename Parser, typename Iterator, typename Container, typename Context> 223 struct parser_attr_is_substitute_for_container_value 224 : traits::is_substitute< 225 typename traits::pseudo_attribute< 226 Context 227 , typename traits::attribute_of<Parser, Context>::type 228 , Iterator 229 >::type 230 , typename traits::container_value<Container>::type 231 > 232 {}; 233 234 template <typename Parser, typename Context, typename RContext> 235 struct parse_into_container_impl<Parser, Context, RContext, 236 typename enable_if<traits::handles_container<Parser, Context>>::type> 237 { 238 template <typename Iterator, typename Attribute> callboost::spirit::x3::detail::parse_into_container_impl239 static bool call( 240 Parser const& parser 241 , Iterator& first, Iterator const& last 242 , Context const& context, RContext& rcontext, Attribute& attr, mpl::false_) 243 { 244 return parse_into_container_base_impl<Parser>::call( 245 parser, first, last, context, rcontext, attr); 246 } 247 248 template <typename Iterator> callboost::spirit::x3::detail::parse_into_container_impl249 static bool call( 250 Parser const& parser 251 , Iterator& first, Iterator const& last 252 , Context const& context, RContext& rcontext, unused_type attr, mpl::true_) 253 { 254 return parser.parse(first, last, context, rcontext, attr); 255 } 256 257 template <typename Iterator, typename Attribute> callboost::spirit::x3::detail::parse_into_container_impl258 static bool call( 259 Parser const& parser 260 , Iterator& first, Iterator const& last 261 , Context const& context, RContext& rcontext, Attribute& attr, mpl::true_) 262 { 263 if (traits::is_empty(attr)) 264 return parser.parse(first, last, context, rcontext, attr); 265 Attribute rest; 266 bool r = parser.parse(first, last, context, rcontext, rest); 267 if (r) 268 traits::append(attr, std::make_move_iterator(rest.begin()), 269 std::make_move_iterator(rest.end())); 270 return r; 271 } 272 273 template <typename Iterator, typename Attribute> callboost::spirit::x3::detail::parse_into_container_impl274 static bool call(Parser const& parser 275 , Iterator& first, Iterator const& last 276 , Context const& context, RContext& rcontext, Attribute& attr) 277 { 278 typedef parser_accepts_container< 279 Parser, Attribute, Context> 280 parser_accepts_container; 281 282 typedef parser_attr_is_substitute_for_container_value< 283 Parser, Iterator, Attribute, Context> 284 parser_attr_is_substitute_for_container_value; 285 286 typedef mpl::or_< 287 parser_accepts_container 288 , mpl::not_<parser_attr_is_substitute_for_container_value>> 289 pass_attibute_as_is; 290 291 return call(parser, first, last, context, rcontext, attr, 292 pass_attibute_as_is()); 293 } 294 }; 295 296 template <typename Parser, typename Iterator, typename Context 297 , typename RContext, typename Attribute> parse_into_container(Parser const & parser,Iterator & first,Iterator const & last,Context const & context,RContext & rcontext,Attribute & attr)298 bool parse_into_container( 299 Parser const& parser 300 , Iterator& first, Iterator const& last, Context const& context 301 , RContext& rcontext, Attribute& attr) 302 { 303 return parse_into_container_impl<Parser, Context, RContext>::call( 304 parser, first, last, context, rcontext, attr); 305 } 306 307 }}}} 308 309 #endif 310