1 /*============================================================================= 2 Copyright (c) 2011-2012 Thomas Bernard 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 #ifndef BOOST_SPIRIT_REPOSITORY_QI_OPERATOR_DETAIL_KEYWORDS_HPP 8 #define BOOST_SPIRIT_REPOSITORY_QI_OPERATOR_DETAIL_KEYWORDS_HPP 9 10 #if defined(_MSC_VER) 11 #pragma once 12 #endif 13 #include <boost/fusion/include/nview.hpp> 14 #include <boost/spirit/home/qi/string/lit.hpp> 15 #include <boost/fusion/include/at.hpp> 16 namespace boost { namespace spirit { namespace repository { namespace qi { namespace detail { 17 // Variant visitor class which handles dispatching the parsing to the selected parser 18 // This also handles passing the correct attributes and flags/counters to the subject parsers 19 template<typename T> 20 struct is_distinct : T::distinct { }; 21 22 template<typename T, typename Action> 23 struct is_distinct< spirit::qi::action<T,Action> > : T::distinct { }; 24 25 template<typename T> 26 struct is_distinct< spirit::qi::hold_directive<T> > : T::distinct { }; 27 28 29 30 template < typename Elements, typename Iterator ,typename Context ,typename Skipper 31 ,typename Flags ,typename Counters ,typename Attribute, typename NoCasePass> 32 struct parse_dispatcher 33 : public boost::static_visitor<bool> 34 { 35 36 typedef Iterator iterator_type; 37 typedef Context context_type; 38 typedef Skipper skipper_type; 39 typedef Elements elements_type; 40 41 typedef typename add_reference<Attribute>::type attr_reference; 42 public: parse_dispatcherboost::spirit::repository::qi::detail::parse_dispatcher43 parse_dispatcher(const Elements &elements,Iterator& first, Iterator const& last 44 , Context& context, Skipper const& skipper 45 , Flags &flags, Counters &counters, attr_reference attr) : 46 elements(elements), first(first), last(last) 47 , context(context), skipper(skipper) 48 , flags(flags),counters(counters), attr(attr) 49 {} 50 operator ()boost::spirit::repository::qi::detail::parse_dispatcher51 template<typename T> bool operator()(T& idx) const 52 { 53 return call(idx,typename traits::not_is_unused<Attribute>::type()); 54 } 55 56 template <typename Subject,typename Index> call_subject_unusedboost::spirit::repository::qi::detail::parse_dispatcher57 bool call_subject_unused( 58 Subject const &subject, Iterator &first, Iterator const &last 59 , Context& context, Skipper const& skipper 60 , Index& /*idx*/ ) const 61 { 62 Iterator save = first; 63 skipper_keyword_marker<Skipper,NoCasePass> 64 marked_skipper(skipper,flags[Index::value],counters[Index::value]); 65 66 if(subject.parse(first,last,context,marked_skipper,unused)) 67 { 68 return true; 69 } 70 first = save; 71 return false; 72 } 73 74 75 template <typename Subject,typename Index> call_subjectboost::spirit::repository::qi::detail::parse_dispatcher76 bool call_subject( 77 Subject const &subject, Iterator &first, Iterator const &last 78 , Context& context, Skipper const& skipper 79 , Index& /*idx*/ ) const 80 { 81 82 Iterator save = first; 83 skipper_keyword_marker<Skipper,NoCasePass> 84 marked_skipper(skipper,flags[Index::value],counters[Index::value]); 85 if(subject.parse(first,last,context,marked_skipper,fusion::at_c<Index::value>(attr))) 86 { 87 return true; 88 } 89 first = save; 90 return false; 91 } 92 93 #if defined(_MSC_VER) 94 # pragma warning(push) 95 # pragma warning(disable: 4127) // conditional expression is constant 96 #endif 97 // Handle unused attributes callboost::spirit::repository::qi::detail::parse_dispatcher98 template <typename T> bool call(T &idx, mpl::false_) const{ 99 100 typedef typename mpl::at<Elements,T>::type ElementType; 101 if( 102 (!is_distinct<ElementType>::value) 103 || skipper.parse(first,last,unused,unused,unused) 104 ){ 105 spirit::qi::skip_over(first, last, skipper); 106 return call_subject_unused(fusion::at_c<T::value>(elements), first, last, context, skipper, idx ); 107 } 108 return false; 109 } 110 // Handle normal attributes callboost::spirit::repository::qi::detail::parse_dispatcher111 template <typename T> bool call(T &idx, mpl::true_) const{ 112 typedef typename mpl::at<Elements,T>::type ElementType; 113 if( 114 (!is_distinct<ElementType>::value) 115 || skipper.parse(first,last,unused,unused,unused) 116 ){ 117 return call_subject(fusion::at_c<T::value>(elements), first, last, context, skipper, idx); 118 } 119 return false; 120 } 121 #if defined(_MSC_VER) 122 # pragma warning(pop) 123 #endif 124 125 const Elements &elements; 126 Iterator &first; 127 const Iterator &last; 128 Context & context; 129 const Skipper &skipper; 130 Flags &flags; 131 Counters &counters; 132 attr_reference attr; 133 }; 134 // string keyword loop handler 135 template <typename Elements, typename StringKeywords, typename IndexList, typename FlagsType, typename Modifiers> 136 struct string_keywords 137 { 138 // Create a variant type to be able to store parser indexes in the embedded symbols parser 139 typedef typename 140 spirit::detail::as_variant< 141 IndexList >::type parser_index_type; 142 143 /////////////////////////////////////////////////////////////////////////// 144 // build_char_type_sequence 145 // 146 // Build a fusion sequence from the kwd directive specified character type. 147 /////////////////////////////////////////////////////////////////////////// 148 template <typename Sequence > 149 struct build_char_type_sequence 150 { 151 struct element_char_type 152 { 153 template <typename T> 154 struct result; 155 156 template <typename F, typename Element> 157 struct result<F(Element)> 158 { 159 typedef typename Element::char_type type; 160 161 }; 162 template <typename F, typename Element,typename Action> 163 struct result<F(spirit::qi::action<Element,Action>) > 164 { 165 typedef typename Element::char_type type; 166 }; 167 template <typename F, typename Element> 168 struct result<F(spirit::qi::hold_directive<Element>)> 169 { 170 typedef typename Element::char_type type; 171 }; 172 173 // never called, but needed for decltype-based result_of (C++0x) 174 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 175 template <typename Element> 176 typename result<element_char_type(Element)>::type 177 operator()(Element&&) const; 178 #endif 179 }; 180 181 // Compute the list of character types of the child kwd directives 182 typedef typename 183 fusion::result_of::transform<Sequence, element_char_type>::type 184 type; 185 }; 186 187 188 /////////////////////////////////////////////////////////////////////////// 189 // get_keyword_char_type 190 // 191 // Collapses the character type coming from the subject kwd parsers and 192 // and checks that they are all identical (necessary in order to be able 193 // to build a tst parser to parse the keywords. 194 /////////////////////////////////////////////////////////////////////////// 195 template <typename Sequence> 196 struct get_keyword_char_type 197 { 198 // Make sure each of the types occur only once in the type list 199 typedef typename 200 mpl::fold< 201 Sequence, mpl::vector<>, 202 mpl::if_< 203 mpl::contains<mpl::_1, mpl::_2>, 204 mpl::_1, mpl::push_back<mpl::_1, mpl::_2> 205 > 206 >::type 207 no_duplicate_char_types; 208 209 // If the compiler traps here this means you mixed 210 // character type for the keywords specified in the 211 // kwd directive sequence. 212 BOOST_MPL_ASSERT_RELATION( mpl::size<no_duplicate_char_types>::value, ==, 1 ); 213 214 typedef typename mpl::front<no_duplicate_char_types>::type type; 215 216 }; 217 218 // Get the character type for the tst parser 219 typedef typename build_char_type_sequence< StringKeywords >::type char_types; 220 typedef typename get_keyword_char_type< 221 typename mpl::if_< 222 mpl::equal_to< 223 typename mpl::size < char_types >::type 224 , mpl::int_<0> 225 > 226 , mpl::vector< boost::spirit::standard::char_type > 227 , char_types >::type 228 >::type char_type; 229 230 // Our symbols container 231 typedef spirit::qi::tst< char_type, parser_index_type> keywords_type; 232 233 // Filter functor used for case insensitive parsing 234 template <typename CharEncoding> 235 struct no_case_filter 236 { operator ()boost::spirit::repository::qi::detail::string_keywords::no_case_filter237 char_type operator()(char_type ch) const 238 { 239 return static_cast<char_type>(CharEncoding::tolower(ch)); 240 } 241 }; 242 243 /////////////////////////////////////////////////////////////////////////// 244 // build_case_type_sequence 245 // 246 // Build a fusion sequence from the kwd/ikwd directives 247 // in order to determine if case sensitive and case insensitive 248 // keywords have been mixed. 249 /////////////////////////////////////////////////////////////////////////// 250 template <typename Sequence > 251 struct build_case_type_sequence 252 { 253 struct element_case_type 254 { 255 template <typename T> 256 struct result; 257 258 template <typename F, typename Element> 259 struct result<F(Element)> 260 { 261 typedef typename Element::no_case_keyword type; 262 263 }; 264 template <typename F, typename Element,typename Action> 265 struct result<F(spirit::qi::action<Element,Action>) > 266 { 267 typedef typename Element::no_case_keyword type; 268 }; 269 template <typename F, typename Element> 270 struct result<F(spirit::qi::hold_directive<Element>)> 271 { 272 typedef typename Element::no_case_keyword type; 273 }; 274 275 // never called, but needed for decltype-based result_of (C++0x) 276 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 277 template <typename Element> 278 typename result<element_case_type(Element)>::type 279 operator()(Element&&) const; 280 #endif 281 }; 282 283 // Compute the list of character types of the child kwd directives 284 typedef typename 285 fusion::result_of::transform<Sequence, element_case_type>::type 286 type; 287 }; 288 289 /////////////////////////////////////////////////////////////////////////// 290 // get_nb_case_types 291 // 292 // Counts the number of entries in the case type sequence matching the 293 // CaseType parameter (mpl::true_ -> case insensitve 294 // , mpl::false_ -> case sensitive 295 /////////////////////////////////////////////////////////////////////////// 296 template <typename Sequence,typename CaseType> 297 struct get_nb_case_types 298 { 299 // Make sure each of the types occur only once in the type list 300 typedef typename 301 mpl::count_if< 302 Sequence, mpl::equal_to<mpl::_,CaseType> 303 >::type type; 304 305 306 }; 307 // Build the case type sequence 308 typedef typename build_case_type_sequence< StringKeywords >::type case_type_sequence; 309 // Count the number of case sensitive entries and case insensitve entries 310 typedef typename get_nb_case_types<case_type_sequence,mpl::true_>::type ikwd_count; 311 typedef typename get_nb_case_types<case_type_sequence,mpl::false_>::type kwd_count; 312 // Get the size of the original sequence 313 typedef typename mpl::size<IndexList>::type nb_elements; 314 // Determine if all the kwd directive are case sensitive/insensitive 315 typedef typename mpl::and_< 316 typename mpl::greater< nb_elements, mpl::int_<0> >::type 317 , typename mpl::equal_to< ikwd_count, nb_elements>::type 318 >::type all_ikwd; 319 320 typedef typename mpl::and_< 321 typename mpl::greater< nb_elements, mpl::int_<0> >::type 322 , typename mpl::equal_to< kwd_count, nb_elements>::type 323 >::type all_kwd; 324 325 typedef typename mpl::or_< all_kwd, all_ikwd >::type all_directives_of_same_type; 326 327 // Do we have a no case modifier 328 typedef has_modifier<Modifiers, spirit::tag::char_code_base<spirit::tag::no_case> > no_case_modifier; 329 330 // Should the no_case filter always be used ? 331 typedef typename mpl::or_< 332 no_case_modifier, 333 mpl::and_< 334 all_directives_of_same_type 335 ,all_ikwd 336 > 337 >::type 338 no_case; 339 340 typedef no_case_filter< 341 typename spirit::detail::get_encoding_with_case< 342 Modifiers 343 , char_encoding::standard 344 , no_case::value>::type> 345 nc_filter; 346 // Determine the standard case filter type 347 typedef typename mpl::if_< 348 no_case 349 , nc_filter 350 , spirit::qi::tst_pass_through >::type 351 first_pass_filter_type; 352 353 typedef typename mpl::or_< 354 all_directives_of_same_type 355 , no_case_modifier 356 >::type requires_one_pass; 357 358 359 // Functor which adds all the keywords/subject parser indexes 360 // collected from the subject kwd directives to the keyword tst parser 361 struct keyword_entry_adder 362 { 363 typedef int result_type; 364 keyword_entry_adderboost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder365 keyword_entry_adder(shared_ptr<keywords_type> lookup,FlagsType &flags, Elements &elements) : 366 lookup(lookup) 367 ,flags(flags) 368 ,elements(elements) 369 {} 370 371 template <typename T> operator ()boost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder372 int operator()(const T &index) const 373 { 374 return call(fusion::at_c<T::value>(elements),index); 375 } 376 377 template <typename T, typename Position, typename Action> callboost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder378 int call(const spirit::qi::action<T,Action> &parser, const Position position ) const 379 { 380 381 // Make the keyword/parse index entry in the tst parser 382 lookup->add( 383 traits::get_begin<char_type>(get_string(parser.subject.keyword)), 384 traits::get_end<char_type>(get_string(parser.subject.keyword)), 385 position 386 ); 387 // Get the initial state of the flags array and store it in the flags initializer 388 flags[Position::value]=parser.subject.iter.flag_init(); 389 return 0; 390 } 391 392 template <typename T, typename Position> callboost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder393 int call( const T & parser, const Position position) const 394 { 395 // Make the keyword/parse index entry in the tst parser 396 lookup->add( 397 traits::get_begin<char_type>(get_string(parser.keyword)), 398 traits::get_end<char_type>(get_string(parser.keyword)), 399 position 400 ); 401 // Get the initial state of the flags array and store it in the flags initializer 402 flags[Position::value]=parser.iter.flag_init(); 403 return 0; 404 } 405 406 template <typename T, typename Position> callboost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder407 int call( const spirit::qi::hold_directive<T> & parser, const Position position) const 408 { 409 // Make the keyword/parse index entry in the tst parser 410 lookup->add( 411 traits::get_begin<char_type>(get_string(parser.subject.keyword)), 412 traits::get_end<char_type>(get_string(parser.subject.keyword)), 413 position 414 ); 415 // Get the initial state of the flags array and store it in the flags initializer 416 flags[Position::value]=parser.subject.iter.flag_init(); 417 return 0; 418 } 419 420 421 template <typename String, bool no_attribute> get_stringboost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder422 const String get_string(const boost::spirit::qi::literal_string<String,no_attribute> &parser) const 423 { 424 return parser.str; 425 } 426 427 template <typename String, bool no_attribute> 428 const typename boost::spirit::qi::no_case_literal_string<String,no_attribute>::string_type & get_stringboost::spirit::repository::qi::detail::string_keywords::keyword_entry_adder429 get_string(const boost::spirit::qi::no_case_literal_string<String,no_attribute> &parser) const 430 { 431 return parser.str_lo; 432 } 433 434 435 436 shared_ptr<keywords_type> lookup; 437 FlagsType & flags; 438 Elements &elements; 439 }; 440 string_keywordsboost::spirit::repository::qi::detail::string_keywords441 string_keywords(Elements &elements,FlagsType &flags_init) : lookup(new keywords_type()) 442 { 443 // Loop through all the subject parsers to build the keyword parser symbol parser 444 IndexList indexes; 445 keyword_entry_adder f1(lookup,flags_init,elements); 446 fusion::for_each(indexes,f1); 447 448 } 449 template <typename Iterator,typename ParseVisitor, typename Skipper> parseboost::spirit::repository::qi::detail::string_keywords450 bool parse( 451 Iterator &first, 452 const Iterator &last, 453 const ParseVisitor &parse_visitor, 454 const Skipper &/*skipper*/) const 455 { 456 if(parser_index_type* val_ptr = 457 lookup->find(first,last,first_pass_filter_type())) 458 { 459 if(!apply_visitor(parse_visitor,*val_ptr)){ 460 return false; 461 } 462 return true; 463 } 464 return false; 465 } 466 467 template <typename Iterator,typename ParseVisitor, typename NoCaseParseVisitor,typename Skipper> parseboost::spirit::repository::qi::detail::string_keywords468 bool parse( 469 Iterator &first, 470 const Iterator &last, 471 const ParseVisitor &parse_visitor, 472 const NoCaseParseVisitor &no_case_parse_visitor, 473 const Skipper &/*skipper*/) const 474 { 475 Iterator saved_first = first; 476 if(parser_index_type* val_ptr = 477 lookup->find(first,last,first_pass_filter_type())) 478 { 479 if(!apply_visitor(parse_visitor,*val_ptr)){ 480 return false; 481 } 482 return true; 483 } 484 // Second pass case insensitive 485 else if(parser_index_type* val_ptr 486 = lookup->find(saved_first,last,nc_filter())) 487 { 488 first = saved_first; 489 if(!apply_visitor(no_case_parse_visitor,*val_ptr)){ 490 return false; 491 } 492 return true; 493 } 494 return false; 495 } 496 shared_ptr<keywords_type> lookup; 497 498 499 }; 500 501 struct empty_keywords_list 502 { 503 typedef mpl::true_ requires_one_pass; 504 empty_keywords_listboost::spirit::repository::qi::detail::empty_keywords_list505 empty_keywords_list() 506 {} 507 template<typename Elements> empty_keywords_listboost::spirit::repository::qi::detail::empty_keywords_list508 empty_keywords_list(const Elements &) 509 {} 510 511 template<typename Elements, typename FlagsInit> empty_keywords_listboost::spirit::repository::qi::detail::empty_keywords_list512 empty_keywords_list(const Elements &, const FlagsInit &) 513 {} 514 515 template <typename Iterator,typename ParseVisitor, typename NoCaseParseVisitor,typename Skipper> parseboost::spirit::repository::qi::detail::empty_keywords_list516 bool parse( 517 Iterator &/*first*/, 518 const Iterator &/*last*/, 519 const ParseVisitor &/*parse_visitor*/, 520 const NoCaseParseVisitor &/*no_case_parse_visitor*/, 521 const Skipper &/*skipper*/) const 522 { 523 return false; 524 } 525 526 template <typename Iterator,typename ParseVisitor, typename Skipper> parseboost::spirit::repository::qi::detail::empty_keywords_list527 bool parse( 528 Iterator &/*first*/, 529 const Iterator &/*last*/, 530 const ParseVisitor &/*parse_visitor*/, 531 const Skipper &/*skipper*/) const 532 { 533 return false; 534 } 535 536 template <typename ParseFunction> parseboost::spirit::repository::qi::detail::empty_keywords_list537 bool parse( ParseFunction &/*function*/ ) const 538 { 539 return false; 540 } 541 }; 542 543 template<typename ComplexKeywords> 544 struct complex_keywords 545 { 546 // Functor which performs the flag initialization for the complex keyword parsers 547 template <typename FlagsType, typename Elements> 548 struct flag_init_value_setter 549 { 550 typedef int result_type; 551 flag_init_value_setterboost::spirit::repository::qi::detail::complex_keywords::flag_init_value_setter552 flag_init_value_setter(Elements &elements,FlagsType &flags) 553 :flags(flags) 554 ,elements(elements) 555 {} 556 557 template <typename T> operator ()boost::spirit::repository::qi::detail::complex_keywords::flag_init_value_setter558 int operator()(const T &index) const 559 { 560 return call(fusion::at_c<T::value>(elements),index); 561 } 562 563 template <typename T, typename Position, typename Action> callboost::spirit::repository::qi::detail::complex_keywords::flag_init_value_setter564 int call(const spirit::qi::action<T,Action> &parser, const Position /*position*/ ) const 565 { 566 // Get the initial state of the flags array and store it in the flags initializer 567 flags[Position::value]=parser.subject.iter.flag_init(); 568 return 0; 569 } 570 571 template <typename T, typename Position> callboost::spirit::repository::qi::detail::complex_keywords::flag_init_value_setter572 int call( const T & parser, const Position /*position*/) const 573 { 574 // Get the initial state of the flags array and store it in the flags initializer 575 flags[Position::value]=parser.iter.flag_init(); 576 return 0; 577 } 578 579 template <typename T, typename Position> callboost::spirit::repository::qi::detail::complex_keywords::flag_init_value_setter580 int call( const spirit::qi::hold_directive<T> & parser, const Position /*position*/) const 581 { 582 // Get the initial state of the flags array and store it in the flags initializer 583 flags[Position::value]=parser.subject.iter.flag_init(); 584 return 0; 585 } 586 587 FlagsType & flags; 588 Elements &elements; 589 }; 590 591 template <typename Elements, typename Flags> complex_keywordsboost::spirit::repository::qi::detail::complex_keywords592 complex_keywords(Elements &elements, Flags &flags) 593 { 594 flag_init_value_setter<Flags,Elements> flag_initializer(elements,flags); 595 fusion::for_each(complex_keywords_inst,flag_initializer); 596 } 597 598 template <typename ParseFunction> parseboost::spirit::repository::qi::detail::complex_keywords599 bool parse( ParseFunction &function ) const 600 { 601 return fusion::any(complex_keywords_inst,function); 602 } 603 604 ComplexKeywords complex_keywords_inst; 605 }; 606 // This helper class enables jumping over intermediate directives 607 // down the kwd parser iteration count checking policy 608 struct register_successful_parse 609 { 610 template <typename Subject> callboost::spirit::repository::qi::detail::register_successful_parse611 static bool call(Subject const &subject,bool &flag, int &counter) 612 { 613 return subject.iter.register_successful_parse(flag,counter); 614 } 615 template <typename Subject, typename Action> callboost::spirit::repository::qi::detail::register_successful_parse616 static bool call(spirit::qi::action<Subject, Action> const &subject,bool &flag, int &counter) 617 { 618 return subject.subject.iter.register_successful_parse(flag,counter); 619 } 620 template <typename Subject> callboost::spirit::repository::qi::detail::register_successful_parse621 static bool call(spirit::qi::hold_directive<Subject> const &subject,bool &flag, int &counter) 622 { 623 return subject.subject.iter.register_successful_parse(flag,counter); 624 } 625 }; 626 627 // This helper class enables jumping over intermediate directives 628 // down the kwd parser 629 struct extract_keyword 630 { 631 template <typename Subject> callboost::spirit::repository::qi::detail::extract_keyword632 static Subject const& call(Subject const &subject) 633 { 634 return subject; 635 } 636 template <typename Subject, typename Action> callboost::spirit::repository::qi::detail::extract_keyword637 static Subject const& call(spirit::qi::action<Subject, Action> const &subject) 638 { 639 return subject.subject; 640 } 641 template <typename Subject> callboost::spirit::repository::qi::detail::extract_keyword642 static Subject const& call(spirit::qi::hold_directive<Subject> const &subject) 643 { 644 return subject.subject; 645 } 646 }; 647 648 template <typename ParseDispatcher> 649 struct complex_kwd_function 650 { 651 typedef typename ParseDispatcher::iterator_type Iterator; 652 typedef typename ParseDispatcher::context_type Context; 653 typedef typename ParseDispatcher::skipper_type Skipper; complex_kwd_functionboost::spirit::repository::qi::detail::complex_kwd_function654 complex_kwd_function( 655 Iterator& first, Iterator const& last 656 , Context& context, Skipper const& skipper, ParseDispatcher &dispatcher) 657 : first(first) 658 , last(last) 659 , context(context) 660 , skipper(skipper) 661 , dispatcher(dispatcher) 662 { 663 } 664 665 template <typename Component> operator ()boost::spirit::repository::qi::detail::complex_kwd_function666 bool operator()(Component const& component) 667 { 668 Iterator save = first; 669 if( 670 extract_keyword::call( 671 fusion::at_c< 672 Component::value 673 ,typename ParseDispatcher::elements_type 674 >(dispatcher.elements) 675 ) 676 .keyword.parse( 677 first 678 ,last 679 ,context 680 ,skipper 681 ,unused) 682 ) 683 { 684 if(!dispatcher(component)){ 685 first = save; 686 return false; 687 } 688 return true; 689 } 690 return false; 691 } 692 693 Iterator& first; 694 Iterator const& last; 695 Context& context; 696 Skipper const& skipper; 697 ParseDispatcher const& dispatcher; 698 699 // silence MSVC warning C4512: assignment operator could not be generated 700 BOOST_DELETED_FUNCTION(complex_kwd_function& operator= (complex_kwd_function const&)) 701 }; 702 703 704 }}}}} 705 706 #endif 707