keywords.hpp 29 KB

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