1 // Copyright (c) 2009 Francois Barel 2 // Copyright (c) 2001-2011 Joel de Guzman 3 // Copyright (c) 2001-2012 Hartmut Kaiser 4 // 5 // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 8 #if !defined(BOOST_SPIRIT_REPOSITORY_KARMA_SUBRULE_AUGUST_12_2009_0813PM) 9 #define BOOST_SPIRIT_REPOSITORY_KARMA_SUBRULE_AUGUST_12_2009_0813PM 10 11 #if defined(_MSC_VER) 12 #pragma once 13 #endif 14 15 #include <boost/spirit/home/karma/domain.hpp> 16 #include <boost/spirit/home/karma/meta_compiler.hpp> 17 #include <boost/spirit/home/karma/generator.hpp> 18 #include <boost/spirit/home/karma/reference.hpp> 19 #include <boost/spirit/home/karma/nonterminal/detail/generator_binder.hpp> 20 #include <boost/spirit/home/karma/nonterminal/detail/parameterized.hpp> 21 #include <boost/spirit/home/support/argument.hpp> 22 #include <boost/spirit/home/support/assert_msg.hpp> 23 #include <boost/spirit/home/karma/detail/attributes.hpp> 24 #include <boost/spirit/home/support/info.hpp> 25 #include <boost/spirit/home/support/unused.hpp> 26 #include <boost/spirit/home/support/nonterminal/extract_param.hpp> 27 #include <boost/spirit/home/support/nonterminal/locals.hpp> 28 #include <boost/spirit/repository/home/support/subrule_context.hpp> 29 30 #include <boost/static_assert.hpp> 31 #include <boost/fusion/include/as_map.hpp> 32 #include <boost/fusion/include/at_key.hpp> 33 #include <boost/fusion/include/cons.hpp> 34 #include <boost/fusion/include/front.hpp> 35 #include <boost/fusion/include/has_key.hpp> 36 #include <boost/fusion/include/join.hpp> 37 #include <boost/fusion/include/make_map.hpp> 38 #include <boost/fusion/include/make_vector.hpp> 39 #include <boost/fusion/include/size.hpp> 40 #include <boost/fusion/include/vector.hpp> 41 #include <boost/mpl/bool.hpp> 42 #include <boost/mpl/identity.hpp> 43 #include <boost/mpl/int.hpp> 44 #include <boost/mpl/vector.hpp> 45 #include <boost/proto/extends.hpp> 46 #include <boost/proto/traits.hpp> 47 #include <boost/type_traits/is_const.hpp> 48 #include <boost/type_traits/is_reference.hpp> 49 #include <boost/type_traits/is_same.hpp> 50 #include <boost/type_traits/remove_reference.hpp> 51 52 #if defined(BOOST_MSVC) 53 # pragma warning(push) 54 # pragma warning(disable: 4355) // 'this' : used in base member initializer list warning 55 #endif 56 57 /////////////////////////////////////////////////////////////////////////////// 58 namespace boost { namespace spirit { namespace repository { namespace karma 59 { 60 /////////////////////////////////////////////////////////////////////////// 61 // subrule_group_generator: 62 // - generator representing a group of subrule definitions (one or more), 63 // invokes first subrule on entry, 64 /////////////////////////////////////////////////////////////////////////// 65 template <typename Defs> 66 struct subrule_group_generator 67 : spirit::karma::generator<subrule_group_generator<Defs> > 68 { 69 // Fusion associative sequence, associating each subrule ID in this 70 // group (as an MPL integral constant) with its definition 71 typedef Defs defs_type; 72 73 typedef subrule_group_generator<Defs> this_type; 74 subrule_group_generatorboost::spirit::repository::karma::subrule_group_generator75 explicit subrule_group_generator(Defs const& defs) 76 : defs(defs) 77 { 78 } 79 // from a subrule ID, get the type of a reference to its definition 80 template <int ID> 81 struct def_type 82 { 83 typedef mpl::int_<ID> id_type; 84 85 // If you are seeing a compilation error here, you are trying 86 // to use a subrule which was not defined in this group. 87 BOOST_SPIRIT_ASSERT_MSG( 88 (fusion::result_of::has_key< 89 defs_type const, id_type>::type::value) 90 , subrule_used_without_being_defined, (mpl::int_<ID>)); 91 92 typedef typename 93 fusion::result_of::at_key<defs_type const, id_type>::type 94 type; 95 }; 96 97 // from a subrule ID, get a reference to its definition 98 template <int ID> defboost::spirit::repository::karma::subrule_group_generator99 typename def_type<ID>::type def() const 100 { 101 return fusion::at_key<mpl::int_<ID> >(defs); 102 } 103 104 template <typename Context, typename Iterator> 105 struct attribute 106 // Forward to first subrule. 107 : mpl::identity< 108 typename remove_reference< 109 typename fusion::result_of::front<Defs>::type 110 >::type::second_type::attr_type> {}; 111 112 template <typename OutputIterator, typename Context 113 , typename Delimiter, typename Attribute> generateboost::spirit::repository::karma::subrule_group_generator114 bool generate(OutputIterator& sink, Context& context 115 , Delimiter const& delimiter, Attribute const& attr) const 116 { 117 // Forward to first subrule. 118 return generate_subrule(fusion::front(defs).second 119 , sink, context, delimiter, attr); 120 } 121 122 template <typename OutputIterator, typename Context 123 , typename Delimiter, typename Attribute 124 , typename Params> generateboost::spirit::repository::karma::subrule_group_generator125 bool generate(OutputIterator& sink, Context& context 126 , Delimiter const& delimiter, Attribute const& attr 127 , Params const& params) const 128 { 129 // Forward to first subrule. 130 return generate_subrule(fusion::front(defs).second 131 , sink, context, delimiter, attr, params); 132 } 133 134 template <int ID, typename OutputIterator, typename Context 135 , typename Delimiter, typename Attribute> generate_subrule_idboost::spirit::repository::karma::subrule_group_generator136 bool generate_subrule_id(OutputIterator& sink 137 , Context& context, Delimiter const& delimiter 138 , Attribute const& attr) const 139 { 140 return generate_subrule(def<ID>() 141 , sink, context, delimiter, attr); 142 } 143 144 template <int ID, typename OutputIterator, typename Context 145 , typename Delimiter, typename Attribute, typename Params> generate_subrule_idboost::spirit::repository::karma::subrule_group_generator146 bool generate_subrule_id(OutputIterator& sink 147 , Context& context, Delimiter const& delimiter 148 , Attribute const& attr, Params const& params) const 149 { 150 return generate_subrule(def<ID>() 151 , sink, context, delimiter, attr, params); 152 } 153 154 template <typename Def, typename OutputIterator, typename Context 155 , typename Delimiter, typename Attribute> generate_subruleboost::spirit::repository::karma::subrule_group_generator156 bool generate_subrule(Def const& def, OutputIterator& sink 157 , Context& /*caller_context*/, Delimiter const& delimiter 158 , Attribute const& attr) const 159 { 160 // compute context type for this subrule 161 typedef typename Def::locals_type subrule_locals_type; 162 typedef typename Def::attr_type subrule_attr_type; 163 typedef typename Def::attr_reference_type subrule_attr_reference_type; 164 typedef typename Def::parameter_types subrule_parameter_types; 165 166 typedef 167 subrule_context< 168 this_type 169 , fusion::cons< 170 subrule_attr_reference_type, subrule_parameter_types> 171 , subrule_locals_type 172 > 173 context_type; 174 175 typedef traits::transform_attribute<Attribute const 176 , subrule_attr_type, spirit::karma::domain> transform; 177 178 // If you are seeing a compilation error here, you are probably 179 // trying to use a subrule which has inherited attributes, 180 // without passing values for them. 181 context_type context(*this, transform::pre(attr)); 182 183 return def.binder(sink, context, delimiter); 184 } 185 186 template <typename Def, typename OutputIterator, typename Context 187 , typename Delimiter, typename Attribute, typename Params> generate_subruleboost::spirit::repository::karma::subrule_group_generator188 bool generate_subrule(Def const& def, OutputIterator& sink 189 , Context& caller_context, Delimiter const& delimiter 190 , Attribute const& attr, Params const& params) const 191 { 192 // compute context type for this subrule 193 typedef typename Def::locals_type subrule_locals_type; 194 typedef typename Def::attr_type subrule_attr_type; 195 typedef typename Def::attr_reference_type subrule_attr_reference_type; 196 typedef typename Def::parameter_types subrule_parameter_types; 197 198 typedef 199 subrule_context< 200 this_type 201 , fusion::cons< 202 subrule_attr_reference_type, subrule_parameter_types> 203 , subrule_locals_type 204 > 205 context_type; 206 207 typedef traits::transform_attribute<Attribute const 208 , subrule_attr_type, spirit::karma::domain> transform; 209 210 // If you are seeing a compilation error here, you are probably 211 // trying to use a subrule which has inherited attributes, 212 // passing values of incompatible types for them. 213 context_type context(*this 214 , transform::pre(attr), params, caller_context); 215 216 return def.binder(sink, context, delimiter); 217 } 218 219 template <typename Context> whatboost::spirit::repository::karma::subrule_group_generator220 info what(Context& context) const 221 { 222 // Forward to first subrule. 223 return fusion::front(defs).second.binder.g.what(context); 224 } 225 226 Defs defs; 227 }; 228 229 /////////////////////////////////////////////////////////////////////////// 230 // subrule_group: 231 // - a Proto terminal, so that a group behaves like any Spirit 232 // expression. 233 /////////////////////////////////////////////////////////////////////////// 234 template <typename Defs> 235 struct subrule_group 236 : proto::extends< 237 typename proto::terminal< 238 subrule_group_generator<Defs> 239 >::type 240 , subrule_group<Defs> 241 > 242 { 243 typedef subrule_group_generator<Defs> generator_type; 244 typedef typename proto::terminal<generator_type>::type terminal; 245 246 struct properties 247 // Forward to first subrule. 248 : remove_reference< 249 typename fusion::result_of::front<Defs>::type 250 >::type::second_type::subject_type::properties {}; 251 252 static size_t const params_size = 253 // Forward to first subrule. 254 remove_reference< 255 typename fusion::result_of::front<Defs>::type 256 >::type::second_type::params_size; 257 subrule_groupboost::spirit::repository::karma::subrule_group258 explicit subrule_group(Defs const& defs) 259 : subrule_group::proto_extends(terminal::make(generator_type(defs))) 260 { 261 } 262 generatorboost::spirit::repository::karma::subrule_group263 generator_type const& generator() const { return proto::value(*this); } 264 defsboost::spirit::repository::karma::subrule_group265 Defs const& defs() const { return generator().defs; } 266 267 template <typename Defs2> 268 subrule_group< 269 typename fusion::result_of::as_map< 270 typename fusion::result_of::join< 271 Defs const, Defs2 const>::type>::type> operator ,boost::spirit::repository::karma::subrule_group272 operator,(subrule_group<Defs2> const& other) const 273 { 274 typedef subrule_group< 275 typename fusion::result_of::as_map< 276 typename fusion::result_of::join< 277 Defs const, Defs2 const>::type>::type> result_type; 278 return result_type(fusion::as_map(fusion::join(defs(), other.defs()))); 279 } 280 281 // non-const versions needed to suppress proto's comma op kicking in 282 template <typename Defs2> 283 friend subrule_group< 284 typename fusion::result_of::as_map< 285 typename fusion::result_of::join< 286 Defs const, Defs2 const>::type>::type> 287 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES operator ,(subrule_group && left,subrule_group<Defs2> && other)288 operator,(subrule_group&& left, subrule_group<Defs2>&& other) 289 #else 290 operator,(subrule_group& left, subrule_group<Defs2>& other) 291 #endif 292 { 293 return static_cast<subrule_group const&>(left) 294 .operator,(static_cast<subrule_group<Defs2> const&>(other)); 295 } 296 297 // bring in the operator() overloads get_parameterized_subjectboost::spirit::repository::karma::subrule_group298 generator_type const& get_parameterized_subject() const { return generator(); } 299 typedef generator_type parameterized_subject_type; 300 #include <boost/spirit/home/karma/nonterminal/detail/fcall.hpp> 301 }; 302 303 /////////////////////////////////////////////////////////////////////////// 304 // subrule_definition: holds one particular definition of a subrule 305 /////////////////////////////////////////////////////////////////////////// 306 template < 307 int ID_ 308 , typename Locals 309 , typename Attr 310 , typename AttrRef 311 , typename Parameters 312 , size_t ParamsSize 313 , typename Subject 314 , bool Auto_ 315 > 316 struct subrule_definition 317 { 318 typedef mpl::int_<ID_> id_type; 319 BOOST_STATIC_CONSTANT(int, ID = ID_); 320 321 typedef Locals locals_type; 322 typedef Attr attr_type; 323 typedef AttrRef attr_reference_type; 324 typedef Parameters parameter_types; 325 static size_t const params_size = ParamsSize; 326 327 typedef Subject subject_type; 328 typedef mpl::bool_<Auto_> auto_type; 329 BOOST_STATIC_CONSTANT(bool, Auto = Auto_); 330 331 typedef spirit::karma::detail::generator_binder< 332 Subject, auto_type> binder_type; 333 subrule_definitionboost::spirit::repository::karma::subrule_definition334 subrule_definition(Subject const& subject, std::string const& name) 335 : binder(subject), name(name) 336 { 337 } 338 339 binder_type const binder; 340 std::string const name; 341 }; 342 343 /////////////////////////////////////////////////////////////////////////// 344 // subrule placeholder: 345 // - on subrule definition: helper for creation of subrule_group, 346 // - on subrule invocation: Proto terminal and generator. 347 /////////////////////////////////////////////////////////////////////////// 348 template < 349 int ID_ 350 , typename T1 = unused_type 351 , typename T2 = unused_type 352 > 353 struct subrule 354 : proto::extends< 355 typename proto::terminal< 356 spirit::karma::reference<subrule<ID_, T1, T2> const> 357 >::type 358 , subrule<ID_, T1, T2> 359 > 360 , spirit::karma::generator<subrule<ID_, T1, T2> > 361 { 362 //FIXME should go fetch the real properties of this subrule's definition in the current context, but we don't 363 // have the context here (properties would need to be 'template<typename Context> struct properties' instead) 364 typedef mpl::int_< 365 spirit::karma::generator_properties::all_properties> properties; 366 367 typedef mpl::int_<ID_> id_type; 368 BOOST_STATIC_CONSTANT(int, ID = ID_); 369 370 typedef subrule<ID_, T1, T2> this_type; 371 typedef spirit::karma::reference<this_type const> reference_; 372 typedef typename proto::terminal<reference_>::type terminal; 373 typedef proto::extends<terminal, this_type> base_type; 374 375 typedef mpl::vector<T1, T2> template_params; 376 377 // The subrule's locals_type: a sequence of types to be used as local variables 378 typedef typename 379 spirit::detail::extract_locals<template_params>::type 380 locals_type; 381 382 // The subrule's encoding type 383 typedef typename 384 spirit::detail::extract_encoding<template_params>::type 385 encoding_type; 386 387 // The subrule's signature 388 typedef typename 389 spirit::detail::extract_sig<template_params, encoding_type 390 , spirit::karma::domain>::type 391 sig_type; 392 393 // This is the subrule's attribute type 394 typedef typename 395 spirit::detail::attr_from_sig<sig_type>::type 396 attr_type; 397 BOOST_STATIC_ASSERT_MSG( 398 !is_reference<attr_type>::value && !is_const<attr_type>::value, 399 "Const/reference qualifiers on Karma subrule attribute are meaningless"); 400 typedef attr_type const& attr_reference_type; 401 402 // parameter_types is a sequence of types passed as parameters to the subrule 403 typedef typename 404 spirit::detail::params_from_sig<sig_type>::type 405 parameter_types; 406 407 static size_t const params_size = 408 fusion::result_of::size<parameter_types>::type::value; 409 subruleboost::spirit::repository::karma::subrule410 explicit subrule(std::string const& name_ = "unnamed-subrule") 411 : base_type(terminal::make(reference_(*this))) 412 , name_(name_) 413 { 414 } 415 416 // compute type of this subrule's definition for expr type Expr 417 template <typename Expr, bool Auto> 418 struct def_type_helper 419 { 420 // Report invalid expression error as early as possible. 421 // If you got an error_invalid_expression error message here, 422 // then the expression (Expr) is not a valid spirit karma expression. 423 BOOST_SPIRIT_ASSERT_MATCH(spirit::karma::domain, Expr); 424 425 typedef typename result_of::compile< 426 spirit::karma::domain, Expr>::type subject_type; 427 428 typedef subrule_definition< 429 ID_ 430 , locals_type 431 , attr_type 432 , attr_reference_type 433 , parameter_types 434 , params_size 435 , subject_type 436 , Auto 437 > const type; 438 }; 439 440 // compute type of subrule group containing only this 441 // subrule's definition for expr type Expr 442 template <typename Expr, bool Auto> 443 struct group_type_helper 444 { 445 typedef typename def_type_helper<Expr, Auto>::type def_type; 446 447 // create Defs map with only one entry: (ID -> def) 448 typedef typename 449 #ifndef BOOST_FUSION_HAS_VARIADIC_MAP 450 fusion::result_of::make_map<id_type, def_type>::type 451 #else 452 fusion::result_of::make_map<id_type>::template apply<def_type>::type 453 #endif 454 defs_type; 455 456 typedef subrule_group<defs_type> type; 457 }; 458 459 template <typename Expr> 460 typename group_type_helper<Expr, false>::type operator =boost::spirit::repository::karma::subrule461 operator=(Expr const& expr) const 462 { 463 typedef group_type_helper<Expr, false> helper; 464 typedef typename helper::def_type def_type; 465 typedef typename helper::type result_type; 466 return result_type(fusion::make_map<id_type>( 467 def_type(compile<spirit::karma::domain>(expr), name_))); 468 } 469 470 #define BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(lhs_ref, rhs_ref) \ 471 template <typename Expr> \ 472 friend typename group_type_helper<Expr, true>::type \ 473 operator%=(subrule lhs_ref sr, Expr rhs_ref expr) \ 474 { \ 475 typedef group_type_helper<Expr, true> helper; \ 476 typedef typename helper::def_type def_type; \ 477 typedef typename helper::type result_type; \ 478 return result_type(fusion::make_map<id_type>( \ 479 def_type(compile<spirit::karma::domain>(expr), sr.name_))); \ 480 } \ 481 /**/ 482 483 // non-const versions needed to suppress proto's %= kicking in BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATORboost::spirit::repository::karma::subrule484 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, const&) 485 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 486 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &&) 487 #else 488 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &) 489 #endif 490 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, const&) 491 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 492 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &&) 493 #else 494 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &) 495 #endif 496 497 #undef BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR 498 499 std::string const& name() const 500 { 501 return name_; 502 } 503 nameboost::spirit::repository::karma::subrule504 void name(std::string const& str) 505 { 506 name_ = str; 507 } 508 509 template <typename Context, typename Iterator> 510 struct attribute 511 { 512 typedef attr_type type; 513 }; 514 515 template <typename OutputIterator, typename Group 516 , typename Attributes, typename Locals 517 , typename Delimiter, typename Attribute> generateboost::spirit::repository::karma::subrule518 bool generate(OutputIterator& sink 519 , subrule_context<Group, Attributes, Locals>& context 520 , Delimiter const& delimiter, Attribute const& attr) const 521 { 522 return context.group.template generate_subrule_id<ID_>( 523 sink, context, delimiter, attr); 524 } 525 526 template <typename OutputIterator, typename Context 527 , typename Delimiter, typename Attribute> generateboost::spirit::repository::karma::subrule528 bool generate(OutputIterator& /*sink*/ 529 , Context& /*context*/ 530 , Delimiter const& /*delimiter*/, Attribute const& /*attr*/) const 531 { 532 // If you are seeing a compilation error here, you are trying 533 // to use a subrule as a generator outside of a subrule group. 534 BOOST_SPIRIT_ASSERT_FAIL(OutputIterator 535 , subrule_used_outside_subrule_group, (id_type)); 536 537 return false; 538 } 539 540 template <typename OutputIterator, typename Group 541 , typename Attributes, typename Locals 542 , typename Delimiter, typename Attribute 543 , typename Params> generateboost::spirit::repository::karma::subrule544 bool generate(OutputIterator& sink 545 , subrule_context<Group, Attributes, Locals>& context 546 , Delimiter const& delimiter, Attribute const& attr 547 , Params const& params) const 548 { 549 return context.group.template generate_subrule_id<ID_>( 550 sink, context, delimiter, attr, params); 551 } 552 553 template <typename OutputIterator, typename Context 554 , typename Delimiter, typename Attribute 555 , typename Params> generateboost::spirit::repository::karma::subrule556 bool generate(OutputIterator& /*sink*/ 557 , Context& /*context*/ 558 , Delimiter const& /*delimiter*/, Attribute const& /*attr*/ 559 , Params const& /*params*/) const 560 { 561 // If you are seeing a compilation error here, you are trying 562 // to use a subrule as a generator outside of a subrule group. 563 BOOST_SPIRIT_ASSERT_FAIL(OutputIterator 564 , subrule_used_outside_subrule_group, (id_type)); 565 566 return false; 567 } 568 569 template <typename Context> whatboost::spirit::repository::karma::subrule570 info what(Context& /*context*/) const 571 { 572 return info(name_); 573 } 574 575 // bring in the operator() overloads get_parameterized_subjectboost::spirit::repository::karma::subrule576 this_type const& get_parameterized_subject() const { return *this; } 577 typedef this_type parameterized_subject_type; 578 #include <boost/spirit/home/karma/nonterminal/detail/fcall.hpp> 579 580 std::string name_; 581 }; 582 }}}} 583 584 #if defined(BOOST_MSVC) 585 # pragma warning(pop) 586 #endif 587 588 #endif 589