1 /*============================================================================= 2 Copyright (c) 2009 Francois Barel 3 Copyright (c) 2001-2011 Joel de Guzman 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_QI_SUBRULE_AUGUST_06_2009_0239AM) 9 #define BOOST_SPIRIT_REPOSITORY_QI_SUBRULE_AUGUST_06_2009_0239AM 10 11 #if defined(_MSC_VER) 12 #pragma once 13 #endif 14 15 #include <boost/spirit/home/qi/domain.hpp> 16 #include <boost/spirit/home/qi/meta_compiler.hpp> 17 #include <boost/spirit/home/qi/parser.hpp> 18 #include <boost/spirit/home/qi/reference.hpp> 19 #include <boost/spirit/home/qi/nonterminal/detail/parameterized.hpp> 20 #include <boost/spirit/home/qi/nonterminal/detail/parser_binder.hpp> 21 #include <boost/spirit/home/support/argument.hpp> 22 #include <boost/spirit/home/support/assert_msg.hpp> 23 #include <boost/spirit/home/qi/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_reference.hpp> 48 #include <boost/type_traits/is_same.hpp> 49 #include <boost/type_traits/remove_reference.hpp> 50 51 #if defined(BOOST_MSVC) 52 # pragma warning(push) 53 # pragma warning(disable: 4355) // 'this' : used in base member initializer list warning 54 #endif 55 56 /////////////////////////////////////////////////////////////////////////////// 57 namespace boost { namespace spirit { namespace repository { namespace qi 58 { 59 /////////////////////////////////////////////////////////////////////////// 60 // subrule_group_parser: 61 // - parser representing a group of subrule definitions (one or more), 62 // invokes first subrule on entry, 63 /////////////////////////////////////////////////////////////////////////// 64 template <typename Defs> 65 struct subrule_group_parser 66 : spirit::qi::parser<subrule_group_parser<Defs> > 67 { 68 // Fusion associative sequence, associating each subrule ID in this 69 // group (as an MPL integral constant) with its definition 70 typedef Defs defs_type; 71 72 typedef subrule_group_parser<Defs> this_type; 73 subrule_group_parserboost::spirit::repository::qi::subrule_group_parser74 explicit subrule_group_parser(Defs const& defs) 75 : defs(defs) 76 { 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::qi::subrule_group_parser99 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 Iterator, typename Context 113 , typename Skipper, typename Attribute> parseboost::spirit::repository::qi::subrule_group_parser114 bool parse(Iterator& first, Iterator const& last 115 , Context& context, Skipper const& skipper 116 , Attribute& attr) const 117 { 118 // Forward to first subrule. 119 return parse_subrule(fusion::front(defs).second 120 , first, last, context, skipper, attr); 121 } 122 123 template <typename Iterator, typename Context 124 , typename Skipper, typename Attribute, typename Params> parseboost::spirit::repository::qi::subrule_group_parser125 bool parse(Iterator& first, Iterator const& last 126 , Context& context, Skipper const& skipper 127 , Attribute& attr, Params const& params) const 128 { 129 // Forward to first subrule. 130 return parse_subrule(fusion::front(defs).second 131 , first, last, context, skipper, attr, params); 132 } 133 134 template <int ID, typename Iterator, typename Context 135 , typename Skipper, typename Attribute> parse_subrule_idboost::spirit::repository::qi::subrule_group_parser136 bool parse_subrule_id(Iterator& first, Iterator const& last 137 , Context& context, Skipper const& skipper 138 , Attribute& attr) const 139 { 140 return parse_subrule(def<ID>() 141 , first, last, context, skipper, attr); 142 } 143 144 template <int ID, typename Iterator, typename Context 145 , typename Skipper, typename Attribute, typename Params> parse_subrule_idboost::spirit::repository::qi::subrule_group_parser146 bool parse_subrule_id(Iterator& first, Iterator const& last 147 , Context& context, Skipper const& skipper 148 , Attribute& attr, Params const& params) const 149 { 150 return parse_subrule(def<ID>() 151 , first, last, context, skipper, attr, params); 152 } 153 154 template <typename Def 155 , typename Iterator, typename Context 156 , typename Skipper, typename Attribute> parse_subruleboost::spirit::repository::qi::subrule_group_parser157 bool parse_subrule(Def const& def 158 , Iterator& first, Iterator const& last 159 , Context& /*caller_context*/, Skipper const& skipper 160 , Attribute& attr) const 161 { 162 // compute context type for this subrule 163 typedef typename Def::locals_type subrule_locals_type; 164 typedef typename Def::attr_type subrule_attr_type; 165 typedef typename Def::attr_reference_type subrule_attr_reference_type; 166 typedef typename Def::parameter_types subrule_parameter_types; 167 168 typedef 169 subrule_context< 170 this_type 171 , fusion::cons< 172 subrule_attr_reference_type, subrule_parameter_types> 173 , subrule_locals_type 174 > 175 context_type; 176 177 // do down-stream transformation, provides attribute for 178 // rhs parser 179 typedef traits::transform_attribute< 180 Attribute, subrule_attr_type, spirit::qi::domain> 181 transform; 182 183 typename transform::type attr_ = transform::pre(attr); 184 185 // If you are seeing a compilation error here, you are probably 186 // trying to use a subrule which has inherited attributes, 187 // without passing values for them. 188 context_type context(*this, attr_); 189 190 if (def.binder(first, last, context, skipper)) 191 { 192 // do up-stream transformation, this integrates the results 193 // back into the original attribute value, if appropriate 194 transform::post(attr, attr_); 195 return true; 196 } 197 198 // inform attribute transformation of failed rhs 199 transform::fail(attr); 200 return false; 201 } 202 203 template <typename Def 204 , typename Iterator, typename Context 205 , typename Skipper, typename Attribute, typename Params> parse_subruleboost::spirit::repository::qi::subrule_group_parser206 bool parse_subrule(Def const& def 207 , Iterator& first, Iterator const& last 208 , Context& caller_context, Skipper const& skipper 209 , Attribute& attr, Params const& params) const 210 { 211 // compute context type for this subrule 212 typedef typename Def::locals_type subrule_locals_type; 213 typedef typename Def::attr_type subrule_attr_type; 214 typedef typename Def::attr_reference_type subrule_attr_reference_type; 215 typedef typename Def::parameter_types subrule_parameter_types; 216 217 typedef 218 subrule_context< 219 this_type 220 , fusion::cons< 221 subrule_attr_reference_type, subrule_parameter_types> 222 , subrule_locals_type 223 > 224 context_type; 225 226 // do down-stream transformation, provides attribute for 227 // rhs parser 228 typedef traits::transform_attribute< 229 Attribute, subrule_attr_type, spirit::qi::domain> 230 transform; 231 232 typename transform::type attr_ = transform::pre(attr); 233 234 // If you are seeing a compilation error here, you are probably 235 // trying to use a subrule which has inherited attributes, 236 // passing values of incompatible types for them. 237 context_type context(*this, attr_, params, caller_context); 238 239 if (def.binder(first, last, context, skipper)) 240 { 241 // do up-stream transformation, this integrates the results 242 // back into the original attribute value, if appropriate 243 transform::post(attr, attr_); 244 return true; 245 } 246 247 // inform attribute transformation of failed rhs 248 transform::fail(attr); 249 return false; 250 } 251 252 template <typename Context> whatboost::spirit::repository::qi::subrule_group_parser253 info what(Context& context) const 254 { 255 // Forward to first subrule. 256 return fusion::front(defs).second.binder.p.what(context); 257 } 258 259 Defs defs; 260 }; 261 262 /////////////////////////////////////////////////////////////////////////// 263 // subrule_group: 264 // - a Proto terminal, so that a group behaves like any Spirit 265 // expression. 266 /////////////////////////////////////////////////////////////////////////// 267 template <typename Defs> 268 struct subrule_group 269 : proto::extends< 270 typename proto::terminal< 271 subrule_group_parser<Defs> 272 >::type 273 , subrule_group<Defs> 274 > 275 { 276 typedef subrule_group_parser<Defs> parser_type; 277 typedef typename proto::terminal<parser_type>::type terminal; 278 279 static size_t const params_size = 280 // Forward to first subrule. 281 remove_reference< 282 typename fusion::result_of::front<Defs>::type 283 >::type::second_type::params_size; 284 subrule_groupboost::spirit::repository::qi::subrule_group285 explicit subrule_group(Defs const& defs) 286 : subrule_group::proto_extends(terminal::make(parser_type(defs))) 287 { 288 } 289 parserboost::spirit::repository::qi::subrule_group290 parser_type const& parser() const { return proto::value(*this); } 291 defsboost::spirit::repository::qi::subrule_group292 Defs const& defs() const { return parser().defs; } 293 294 template <typename Defs2> 295 subrule_group< 296 typename fusion::result_of::as_map< 297 typename fusion::result_of::join< 298 Defs const, Defs2 const>::type>::type> operator ,boost::spirit::repository::qi::subrule_group299 operator,(subrule_group<Defs2> const& other) const 300 { 301 typedef subrule_group< 302 typename fusion::result_of::as_map< 303 typename fusion::result_of::join< 304 Defs const, Defs2 const>::type>::type> result_type; 305 return result_type(fusion::as_map(fusion::join(defs(), other.defs()))); 306 } 307 308 // non-const versions needed to suppress proto's comma op kicking in 309 template <typename Defs2> 310 friend subrule_group< 311 typename fusion::result_of::as_map< 312 typename fusion::result_of::join< 313 Defs const, Defs2 const>::type>::type> 314 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES operator ,(subrule_group && left,subrule_group<Defs2> && other)315 operator,(subrule_group&& left, subrule_group<Defs2>&& other) 316 #else 317 operator,(subrule_group& left, subrule_group<Defs2>& other) 318 #endif 319 { 320 return static_cast<subrule_group const&>(left) 321 .operator,(static_cast<subrule_group<Defs2> const&>(other)); 322 } 323 324 // bring in the operator() overloads get_parameterized_subjectboost::spirit::repository::qi::subrule_group325 parser_type const& get_parameterized_subject() const { return parser(); } 326 typedef parser_type parameterized_subject_type; 327 #include <boost/spirit/home/qi/nonterminal/detail/fcall.hpp> 328 }; 329 330 /////////////////////////////////////////////////////////////////////////// 331 // subrule_definition: holds one particular definition of a subrule 332 /////////////////////////////////////////////////////////////////////////// 333 template < 334 int ID_ 335 , typename Locals 336 , typename Attr 337 , typename AttrRef 338 , typename Parameters 339 , size_t ParamsSize 340 , typename Subject 341 , bool Auto_ 342 > 343 struct subrule_definition 344 { 345 typedef mpl::int_<ID_> id_type; 346 BOOST_STATIC_CONSTANT(int, ID = ID_); 347 348 typedef Locals locals_type; 349 typedef Attr attr_type; 350 typedef AttrRef attr_reference_type; 351 typedef Parameters parameter_types; 352 static size_t const params_size = ParamsSize; 353 354 typedef Subject subject_type; 355 typedef mpl::bool_<Auto_> auto_type; 356 BOOST_STATIC_CONSTANT(bool, Auto = Auto_); 357 358 typedef spirit::qi::detail::parser_binder< 359 Subject, auto_type> binder_type; 360 subrule_definitionboost::spirit::repository::qi::subrule_definition361 subrule_definition(Subject const& subject, std::string const& name) 362 : binder(subject), name(name) 363 { 364 } 365 366 binder_type const binder; 367 std::string const name; 368 }; 369 370 /////////////////////////////////////////////////////////////////////////// 371 // subrule placeholder: 372 // - on subrule definition: helper for creation of subrule_group, 373 // - on subrule invocation: Proto terminal and parser. 374 /////////////////////////////////////////////////////////////////////////// 375 template < 376 int ID_ 377 , typename T1 = unused_type 378 , typename T2 = unused_type 379 > 380 struct subrule 381 : proto::extends< 382 typename proto::terminal< 383 spirit::qi::reference<subrule<ID_, T1, T2> const> 384 >::type 385 , subrule<ID_, T1, T2> 386 > 387 , spirit::qi::parser<subrule<ID_, T1, T2> > 388 { 389 typedef mpl::int_<ID_> id_type; 390 BOOST_STATIC_CONSTANT(int, ID = ID_); 391 392 typedef subrule<ID_, T1, T2> this_type; 393 typedef spirit::qi::reference<this_type const> reference_; 394 typedef typename proto::terminal<reference_>::type terminal; 395 typedef proto::extends<terminal, this_type> base_type; 396 397 typedef mpl::vector<T1, T2> template_params; 398 399 // The subrule's locals_type: a sequence of types to be used as local variables 400 typedef typename 401 spirit::detail::extract_locals<template_params>::type 402 locals_type; 403 404 // The subrule's encoding type 405 typedef typename 406 spirit::detail::extract_encoding<template_params>::type 407 encoding_type; 408 409 // The subrule's signature 410 typedef typename 411 spirit::detail::extract_sig<template_params, encoding_type 412 , spirit::qi::domain>::type 413 sig_type; 414 415 // This is the subrule's attribute type 416 typedef typename 417 spirit::detail::attr_from_sig<sig_type>::type 418 attr_type; 419 BOOST_STATIC_ASSERT_MSG( 420 !is_reference<attr_type>::value, 421 "Reference qualifier on Qi subrule attribute type is meaningless"); 422 typedef attr_type& attr_reference_type; 423 424 // parameter_types is a sequence of types passed as parameters to the subrule 425 typedef typename 426 spirit::detail::params_from_sig<sig_type>::type 427 parameter_types; 428 429 static size_t const params_size = 430 fusion::result_of::size<parameter_types>::type::value; 431 subruleboost::spirit::repository::qi::subrule432 explicit subrule(std::string const& name_ = "unnamed-subrule") 433 : base_type(terminal::make(reference_(*this))) 434 , name_(name_) 435 { 436 } 437 438 // compute type of this subrule's definition for expr type Expr 439 template <typename Expr, bool Auto> 440 struct def_type_helper 441 { 442 // Report invalid expression error as early as possible. 443 // If you got an error_invalid_expression error message here, 444 // then the expression (Expr) is not a valid spirit qi expression. 445 BOOST_SPIRIT_ASSERT_MATCH(spirit::qi::domain, Expr); 446 447 typedef typename result_of::compile< 448 spirit::qi::domain, Expr>::type subject_type; 449 450 typedef subrule_definition< 451 ID_ 452 , locals_type 453 , attr_type 454 , attr_reference_type 455 , parameter_types 456 , params_size 457 , subject_type 458 , Auto 459 > const type; 460 }; 461 462 // compute type of subrule group containing only this 463 // subrule's definition for expr type Expr 464 template <typename Expr, bool Auto> 465 struct group_type_helper 466 { 467 typedef typename def_type_helper<Expr, Auto>::type def_type; 468 469 // create Defs map with only one entry: (ID -> def) 470 typedef typename 471 #ifndef BOOST_FUSION_HAS_VARIADIC_MAP 472 fusion::result_of::make_map<id_type, def_type>::type 473 #else 474 fusion::result_of::make_map<id_type>::template apply<def_type>::type 475 #endif 476 defs_type; 477 478 typedef subrule_group<defs_type> type; 479 }; 480 481 template <typename Expr> 482 typename group_type_helper<Expr, false>::type operator =boost::spirit::repository::qi::subrule483 operator=(Expr const& expr) const 484 { 485 typedef group_type_helper<Expr, false> helper; 486 typedef typename helper::def_type def_type; 487 typedef typename helper::type result_type; 488 return result_type(fusion::make_map<id_type>( 489 def_type(compile<spirit::qi::domain>(expr), name_))); 490 } 491 492 #define BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(lhs_ref, rhs_ref) \ 493 template <typename Expr> \ 494 friend typename group_type_helper<Expr, true>::type \ 495 operator%=(subrule lhs_ref sr, Expr rhs_ref expr) \ 496 { \ 497 typedef group_type_helper<Expr, true> helper; \ 498 typedef typename helper::def_type def_type; \ 499 typedef typename helper::type result_type; \ 500 return result_type(fusion::make_map<id_type>( \ 501 def_type(compile<spirit::qi::domain>(expr), sr.name_))); \ 502 } \ 503 /**/ 504 505 // non-const versions needed to suppress proto's %= kicking in BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATORboost::spirit::repository::qi::subrule506 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, const&) 507 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 508 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &&) 509 #else 510 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &) 511 #endif 512 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, const&) 513 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 514 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &&) 515 #else 516 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &) 517 #endif 518 519 #undef BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR 520 521 std::string const& name() const 522 { 523 return name_; 524 } 525 nameboost::spirit::repository::qi::subrule526 void name(std::string const& str) 527 { 528 name_ = str; 529 } 530 531 template <typename Context, typename Iterator> 532 struct attribute 533 { 534 typedef attr_type type; 535 }; 536 537 template <typename Iterator, typename Group 538 , typename Attributes, typename Locals 539 , typename Skipper, typename Attribute> parseboost::spirit::repository::qi::subrule540 bool parse(Iterator& first, Iterator const& last 541 , subrule_context<Group, Attributes, Locals>& context 542 , Skipper const& skipper, Attribute& attr) const 543 { 544 return context.group.template parse_subrule_id<ID_>( 545 first, last, context, skipper, attr); 546 } 547 548 template <typename Iterator, typename Context 549 , typename Skipper, typename Attribute> parseboost::spirit::repository::qi::subrule550 bool parse(Iterator& /*first*/, Iterator const& /*last*/ 551 , Context& /*context*/ 552 , Skipper const& /*skipper*/, Attribute& /*attr*/) const 553 { 554 // If you are seeing a compilation error here, you are trying 555 // to use a subrule as a parser outside of a subrule group. 556 BOOST_SPIRIT_ASSERT_FAIL(Iterator 557 , subrule_used_outside_subrule_group, (id_type)); 558 559 return false; 560 } 561 562 template <typename Iterator, typename Group 563 , typename Attributes, typename Locals 564 , typename Skipper, typename Attribute 565 , typename Params> parseboost::spirit::repository::qi::subrule566 bool parse(Iterator& first, Iterator const& last 567 , subrule_context<Group, Attributes, Locals>& context 568 , Skipper const& skipper, Attribute& attr 569 , Params const& params) const 570 { 571 return context.group.template parse_subrule_id<ID_>( 572 first, last, context, skipper, attr, params); 573 } 574 575 template <typename Iterator, typename Context 576 , typename Skipper, typename Attribute 577 , typename Params> parseboost::spirit::repository::qi::subrule578 bool parse(Iterator& /*first*/, Iterator const& /*last*/ 579 , Context& /*context*/ 580 , Skipper const& /*skipper*/, Attribute& /*attr*/ 581 , Params const& /*params*/) const 582 { 583 // If you are seeing a compilation error here, you are trying 584 // to use a subrule as a parser outside of a subrule group. 585 BOOST_SPIRIT_ASSERT_FAIL(Iterator 586 , subrule_used_outside_subrule_group, (id_type)); 587 588 return false; 589 } 590 591 template <typename Context> whatboost::spirit::repository::qi::subrule592 info what(Context& /*context*/) const 593 { 594 return info(name_); 595 } 596 597 // bring in the operator() overloads get_parameterized_subjectboost::spirit::repository::qi::subrule598 this_type const& get_parameterized_subject() const { return *this; } 599 typedef this_type parameterized_subject_type; 600 #include <boost/spirit/home/qi/nonterminal/detail/fcall.hpp> 601 602 std::string name_; 603 }; 604 }}}} 605 606 #if defined(BOOST_MSVC) 607 # pragma warning(pop) 608 #endif 609 610 #endif 611