/*============================================================================= Copyright (c) 2011-2012 Thomas Bernard Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #if !defined(SPIRIT_KEYWORDS_DETAIL_MARCH_13_2007_1145PM) #define SPIRIT_KEYWORDS_DETAIL_MARCH_13_2007_1145PM #if defined(_MSC_VER) #pragma once #endif #include #include #include namespace boost { namespace spirit { namespace repository { namespace qi { namespace detail { // Variant visitor class which handles dispatching the parsing to the selected parser // This also handles passing the correct attributes and flags/counters to the subject parsers template struct is_distinct : T::distinct { }; template struct is_distinct< spirit::qi::action > : T::distinct { }; template struct is_distinct< spirit::qi::hold_directive > : T::distinct { }; template < typename Elements, typename Iterator ,typename Context ,typename Skipper ,typename Flags ,typename Counters ,typename Attribute, typename NoCasePass> struct parse_dispatcher : public boost::static_visitor { typedef Iterator iterator_type; typedef Context context_type; typedef Skipper skipper_type; typedef Elements elements_type; typedef typename add_reference::type attr_reference; public: parse_dispatcher(const Elements &elements,Iterator& first, Iterator const& last , Context& context, Skipper const& skipper , Flags &flags, Counters &counters, attr_reference attr) : elements(elements), first(first), last(last) , context(context), skipper(skipper) , flags(flags),counters(counters), attr(attr) {} template bool operator()(T& idx) const { return call(idx,typename traits::not_is_unused::type()); } template bool call_subject_unused( Subject const &subject, Iterator &first, Iterator const &last , Context& context, Skipper const& skipper , Index& /*idx*/ ) const { Iterator save = first; skipper_keyword_marker marked_skipper(skipper,flags[Index::value],counters[Index::value]); if(subject.parse(first,last,context,marked_skipper,unused)) { return true; } first = save; return false; } template bool call_subject( Subject const &subject, Iterator &first, Iterator const &last , Context& context, Skipper const& skipper , Index& /*idx*/ ) const { Iterator save = first; skipper_keyword_marker marked_skipper(skipper,flags[Index::value],counters[Index::value]); if(subject.parse(first,last,context,marked_skipper,fusion::at_c(attr))) { return true; } first = save; return false; } #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant #endif // Handle unused attributes template bool call(T &idx, mpl::false_) const{ typedef typename mpl::at::type ElementType; if( (!is_distinct::value) || skipper.parse(first,last,unused,unused,unused) ){ spirit::qi::skip_over(first, last, skipper); return call_subject_unused(fusion::at_c(elements), first, last, context, skipper, idx ); } return false; } // Handle normal attributes template bool call(T &idx, mpl::true_) const{ typedef typename mpl::at::type ElementType; if( (!is_distinct::value) || skipper.parse(first,last,unused,unused,unused) ){ return call_subject(fusion::at_c(elements), first, last, context, skipper, idx); } return false; } #if defined(_MSC_VER) # pragma warning(pop) #endif const Elements &elements; Iterator &first; const Iterator &last; Context & context; const Skipper &skipper; Flags &flags; Counters &counters; attr_reference attr; }; // string keyword loop handler template struct string_keywords { // Create a variant type to be able to store parser indexes in the embedded symbols parser typedef typename spirit::detail::as_variant< IndexList >::type parser_index_type; /////////////////////////////////////////////////////////////////////////// // build_char_type_sequence // // Build a fusion sequence from the kwd directive specified character type. /////////////////////////////////////////////////////////////////////////// template struct build_char_type_sequence { struct element_char_type { template struct result; template struct result { typedef typename Element::char_type type; }; template struct result) > { typedef typename Element::char_type type; }; template struct result)> { typedef typename Element::char_type type; }; // never called, but needed for decltype-based result_of (C++0x) #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template typename result::type operator()(Element&&) const; #endif }; // Compute the list of character types of the child kwd directives typedef typename fusion::result_of::transform::type type; }; /////////////////////////////////////////////////////////////////////////// // get_keyword_char_type // // Collapses the character type comming from the subject kwd parsers and // and checks that they are all identical (necessary in order to be able // to build a tst parser to parse the keywords. /////////////////////////////////////////////////////////////////////////// template struct get_keyword_char_type { // Make sure each of the types occur only once in the type list typedef typename mpl::fold< Sequence, mpl::vector<>, mpl::if_< mpl::contains, mpl::_1, mpl::push_back > >::type no_duplicate_char_types; // If the compiler traps here this means you mixed // character type for the keywords specified in the // kwd directive sequence. BOOST_MPL_ASSERT_RELATION( mpl::size::value, ==, 1 ); typedef typename mpl::front::type type; }; // Get the character type for the tst parser typedef typename build_char_type_sequence< StringKeywords >::type char_types; typedef typename get_keyword_char_type< typename mpl::if_< mpl::equal_to< typename mpl::size < char_types >::type , mpl::int_<0> > , mpl::vector< boost::spirit::standard::char_type > , char_types >::type >::type char_type; // Our symbols container typedef spirit::qi::tst< char_type, parser_index_type> keywords_type; // Filter functor used for case insensitive parsing template struct no_case_filter { char_type operator()(char_type ch) const { return static_cast(CharEncoding::tolower(ch)); } }; /////////////////////////////////////////////////////////////////////////// // build_case_type_sequence // // Build a fusion sequence from the kwd/ikwd directives // in order to determine if case sensitive and case insensitive // keywords have been mixed. /////////////////////////////////////////////////////////////////////////// template struct build_case_type_sequence { struct element_case_type { template struct result; template struct result { typedef typename Element::no_case_keyword type; }; template struct result) > { typedef typename Element::no_case_keyword type; }; template struct result)> { typedef typename Element::no_case_keyword type; }; // never called, but needed for decltype-based result_of (C++0x) #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template typename result::type operator()(Element&&) const; #endif }; // Compute the list of character types of the child kwd directives typedef typename fusion::result_of::transform::type type; }; /////////////////////////////////////////////////////////////////////////// // get_nb_case_types // // Counts the number of entries in the case type sequence matching the // CaseType parameter (mpl::true_ -> case insensitve // , mpl::false_ -> case sensitive /////////////////////////////////////////////////////////////////////////// template struct get_nb_case_types { // Make sure each of the types occur only once in the type list typedef typename mpl::count_if< Sequence, mpl::equal_to >::type type; }; // Build the case type sequence typedef typename build_case_type_sequence< StringKeywords >::type case_type_sequence; // Count the number of case sensitive entries and case insensitve entries typedef typename get_nb_case_types::type ikwd_count; typedef typename get_nb_case_types::type kwd_count; // Get the size of the original sequence typedef typename mpl::size::type nb_elements; // Determine if all the kwd directive are case sensitive/insensitive typedef typename mpl::and_< typename mpl::greater< nb_elements, mpl::int_<0> >::type , typename mpl::equal_to< ikwd_count, nb_elements>::type >::type all_ikwd; typedef typename mpl::and_< typename mpl::greater< nb_elements, mpl::int_<0> >::type , typename mpl::equal_to< kwd_count, nb_elements>::type >::type all_kwd; typedef typename mpl::or_< all_kwd, all_ikwd >::type all_directives_of_same_type; // Do we have a no case modifier typedef has_modifier > no_case_modifier; // Should the no_case filter always be used ? typedef typename mpl::or_< no_case_modifier, mpl::and_< all_directives_of_same_type ,all_ikwd > >::type no_case; typedef no_case_filter< typename spirit::detail::get_encoding_with_case< Modifiers , char_encoding::standard , no_case::value>::type> nc_filter; // Determine the standard case filter type typedef typename mpl::if_< no_case , nc_filter , spirit::qi::tst_pass_through >::type first_pass_filter_type; typedef typename mpl::or_< all_directives_of_same_type , no_case_modifier >::type requires_one_pass; // Functor which adds all the keywords/subject parser indexes // collected from the subject kwd directives to the keyword tst parser struct keyword_entry_adder { typedef int result_type; keyword_entry_adder(shared_ptr lookup,FlagsType &flags, Elements &elements) : lookup(lookup) ,flags(flags) ,elements(elements) {} template int operator()(const T &index) const { return call(fusion::at_c(elements),index); } template int call(const spirit::qi::action &parser, const Position position ) const { // Make the keyword/parse index entry in the tst parser lookup->add( traits::get_begin(get_string(parser.subject.keyword)), traits::get_end(get_string(parser.subject.keyword)), position ); // Get the initial state of the flags array and store it in the flags initializer flags[Position::value]=parser.subject.iter.flag_init(); return 0; } template int call( const T & parser, const Position position) const { // Make the keyword/parse index entry in the tst parser lookup->add( traits::get_begin(get_string(parser.keyword)), traits::get_end(get_string(parser.keyword)), position ); // Get the initial state of the flags array and store it in the flags initializer flags[Position::value]=parser.iter.flag_init(); return 0; } template int call( const spirit::qi::hold_directive & parser, const Position position) const { // Make the keyword/parse index entry in the tst parser lookup->add( traits::get_begin(get_string(parser.subject.keyword)), traits::get_end(get_string(parser.subject.keyword)), position ); // Get the initial state of the flags array and store it in the flags initializer flags[Position::value]=parser.subject.iter.flag_init(); return 0; } template const String get_string(const boost::spirit::qi::literal_string &parser) const { return parser.str; } template const typename boost::spirit::qi::no_case_literal_string::string_type & get_string(const boost::spirit::qi::no_case_literal_string &parser) const { return parser.str_lo; } shared_ptr lookup; FlagsType & flags; Elements &elements; }; string_keywords(Elements &elements,FlagsType &flags_init) : lookup(new keywords_type()) { // Loop through all the subject parsers to build the keyword parser symbol parser IndexList indexes; keyword_entry_adder f1(lookup,flags_init,elements); fusion::for_each(indexes,f1); } template bool parse( Iterator &first, const Iterator &last, const ParseVisitor &parse_visitor, const Skipper &/*skipper*/) const { if(parser_index_type* val_ptr = lookup->find(first,last,first_pass_filter_type())) { if(!apply_visitor(parse_visitor,*val_ptr)){ return false; } return true; } return false; } template bool parse( Iterator &first, const Iterator &last, const ParseVisitor &parse_visitor, const NoCaseParseVisitor &no_case_parse_visitor, const Skipper &/*skipper*/) const { Iterator saved_first = first; if(parser_index_type* val_ptr = lookup->find(first,last,first_pass_filter_type())) { if(!apply_visitor(parse_visitor,*val_ptr)){ return false; } return true; } // Second pass case insensitive else if(parser_index_type* val_ptr = lookup->find(saved_first,last,nc_filter())) { first = saved_first; if(!apply_visitor(no_case_parse_visitor,*val_ptr)){ return false; } return true; } return false; } shared_ptr lookup; }; struct empty_keywords_list { typedef mpl::true_ requires_one_pass; empty_keywords_list() {} template empty_keywords_list(const Elements &) {} template empty_keywords_list(const Elements &, const FlagsInit &) {} template bool parse( Iterator &/*first*/, const Iterator &/*last*/, const ParseVisitor &/*parse_visitor*/, const NoCaseParseVisitor &/*no_case_parse_visitor*/, const Skipper &/*skipper*/) const { return false; } template bool parse( Iterator &/*first*/, const Iterator &/*last*/, const ParseVisitor &/*parse_visitor*/, const Skipper &/*skipper*/) const { return false; } template bool parse( ParseFunction &/*function*/ ) const { return false; } }; template struct complex_keywords { // Functor which performs the flag initialization for the complex keyword parsers template struct flag_init_value_setter { typedef int result_type; flag_init_value_setter(Elements &elements,FlagsType &flags) :flags(flags) ,elements(elements) {} template int operator()(const T &index) const { return call(fusion::at_c(elements),index); } template int call(const spirit::qi::action &parser, const Position /*position*/ ) const { // Get the initial state of the flags array and store it in the flags initializer flags[Position::value]=parser.subject.iter.flag_init(); return 0; } template int call( const T & parser, const Position /*position*/) const { // Get the initial state of the flags array and store it in the flags initializer flags[Position::value]=parser.iter.flag_init(); return 0; } template int call( const spirit::qi::hold_directive & parser, const Position /*position*/) const { // Get the initial state of the flags array and store it in the flags initializer flags[Position::value]=parser.subject.iter.flag_init(); return 0; } FlagsType & flags; Elements &elements; }; template complex_keywords(Elements &elements, Flags &flags) { flag_init_value_setter flag_initializer(elements,flags); fusion::for_each(complex_keywords_inst,flag_initializer); } template bool parse( ParseFunction &function ) const { return fusion::any(complex_keywords_inst,function); } ComplexKeywords complex_keywords_inst; }; // This helper class enables jumping over intermediate directives // down the kwd parser iteration count checking policy struct register_successful_parse { template static bool call(Subject const &subject,bool &flag, int &counter) { return subject.iter.register_successful_parse(flag,counter); } template static bool call(spirit::qi::action const &subject,bool &flag, int &counter) { return subject.subject.iter.register_successful_parse(flag,counter); } template static bool call(spirit::qi::hold_directive const &subject,bool &flag, int &counter) { return subject.subject.iter.register_successful_parse(flag,counter); } }; // This helper class enables jumping over intermediate directives // down the kwd parser struct extract_keyword { template static Subject const& call(Subject const &subject) { return subject; } template static Subject const& call(spirit::qi::action const &subject) { return subject.subject; } template static Subject const& call(spirit::qi::hold_directive const &subject) { return subject.subject; } }; template struct complex_kwd_function { typedef typename ParseDispatcher::iterator_type Iterator; typedef typename ParseDispatcher::context_type Context; typedef typename ParseDispatcher::skipper_type Skipper; complex_kwd_function( Iterator& first, Iterator const& last , Context& context, Skipper const& skipper, ParseDispatcher &dispatcher) : first(first) , last(last) , context(context) , skipper(skipper) , dispatcher(dispatcher) { } template bool operator()(Component const& component) { Iterator save = first; if( extract_keyword::call( fusion::at_c< Component::value ,typename ParseDispatcher::elements_type >(dispatcher.elements) ) .keyword.parse( first ,last ,context ,skipper ,unused) ) { if(!dispatcher(component)){ first = save; return false; } return true; } return false; } Iterator& first; Iterator const& last; Context& context; Skipper const& skipper; ParseDispatcher const& dispatcher; // silence MSVC warning C4512: assignment operator could not be generated BOOST_DELETED_FUNCTION(complex_kwd_function& operator= (complex_kwd_function const&)) }; }}}}} #endif