/*============================================================================= Copyright (c) 2001-2011 Joel de Guzman Copyright (c) 2011 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_OR_MARCH_13_2007_1145PM) #define SPIRIT_KEYWORDS_OR_MARCH_13_2007_1145PM #if defined(_MSC_VER) #pragma once #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace spirit { /////////////////////////////////////////////////////////////////////////// // Enablers /////////////////////////////////////////////////////////////////////////// template <> struct use_operator // enables / : mpl::true_ {}; template <> struct flatten_tree // flattens / : mpl::true_ {}; }} namespace boost { namespace spirit { namespace repository { namespace qi { // kwd directive parser type identification namespace detail { BOOST_MPL_HAS_XXX_TRAIT_DEF(kwd_parser_id) } // kwd directive type query template struct is_kwd_parser : detail::has_kwd_parser_id {}; template struct is_kwd_parser > : detail::has_kwd_parser_id {}; template struct is_kwd_parser > : detail::has_kwd_parser_id {}; // Keywords operator template struct keywords : spirit::qi::nary_parser > { template struct attribute { // Put all the element attributes in a tuple typedef typename traits::build_attribute_sequence< Elements, Context, traits::sequence_attribute_transform, Iterator, spirit::qi::domain >::type all_attributes; // Now, build a fusion vector over the attributes. Note // that build_fusion_vector 1) removes all unused attributes // and 2) may return unused_type if all elements have // unused_type(s). typedef typename traits::build_fusion_vector::type type; }; /// Make sure that all subjects are of the kwd type typedef typename mpl::count_if< Elements, mpl::not_< is_kwd_parser< mpl::_1 > > > non_kwd_subject_count; /// If the assertion fails here then you probably forgot to wrap a /// subject of the / operator in a kwd directive BOOST_MPL_ASSERT_RELATION( non_kwd_subject_count::value, ==, 0 ); /////////////////////////////////////////////////////////////////////////// // build_parser_tags // // Builds a boost::variant from an mpl::range_c in order to "mark" every // parser of the fusion sequence. The integer constant is used in the parser // dispatcher functor in order to use the parser corresponding to the recognised // keyword. /////////////////////////////////////////////////////////////////////////// template struct build_parser_tags { // Get the sequence size typedef typename mpl::size< Sequence >::type sequence_size; // Create an integer_c constant for every parser in the sequence typedef typename mpl::range_c::type int_range; // Transform the range_c to an mpl vector in order to be able to transform it into a variant typedef typename mpl::copy > >::type int_vector; // Build the variant type containing the indexes of the parsers typedef typename spirit::detail::as_variant< int_vector >::type type; }; // Create a variant type to be able to store parser indexes in the embedded symbols parser typedef typename build_parser_tags< Elements >::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) template typename result::type operator()(Element&) const; }; // 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< Elements >::type char_types; typedef typename get_keyword_char_type< char_types >::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) template typename result::type operator()(Element&) const; }; // 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::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::equal_to< ikwd_count, nb_elements>::type all_ikwd; typedef typename mpl::equal_to< kwd_count, nb_elements>::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 filter_type; // build a bool array and an integer array which will be used to // check that the repetition constraints of the kwd parsers are // met and bail out a soon as possible typedef boost::array::value> flags_type; typedef boost::array::value> counters_type; // Functor which adds all the keywords/subject parser indexes // collected from the subject kwd directives to the keyword tst parser template< typename Sequence > struct keyword_entry_adder { typedef int result_type; keyword_entry_adder(shared_ptr lookup,flags_type &flags) : lookup(lookup) ,flags(flags) {} typedef typename fusion::result_of::begin< Sequence >::type sequence_begin; template int operator()(const int i, const T &parser) const { // Determine the current position being handled typedef typename fusion::result_of::distance< sequence_begin, T >::type position_raw; // Transform the position to a parser index tag typedef typename mpl::integral_c position; return call(i,fusion::deref(parser),position()); } template int call( const int i, const spirit::qi::action &parser, const Position position ) const { // Make the keyword/parse index entry in the tst parser lookup->add( traits::get_begin(parser.subject.keyword.str), traits::get_end(parser.subject.keyword.str), 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 int i, const T & parser, const Position position) const { // Make the keyword/parse index entry in the tst parser lookup->add( traits::get_begin(parser.keyword.str), traits::get_end(parser.keyword.str), 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 int i, 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(parser.subject.keyword.str), traits::get_end(parser.subject.keyword.str), 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; } shared_ptr lookup; flags_type & flags; }; keywords(Elements const& elements) : elements(elements) , lookup(new keywords_type()) { // Loop through all the subject parsers to build the keyword parser symbol parser keyword_entry_adder f1(lookup,flags_init); fusion::iter_fold(this->elements,0,f1); } template bool parse(Iterator& first, Iterator const& last , Context& context, Skipper const& skipper , Attribute& attr_) const { // Select which parse function to call // We need to handle the case where kwd / ikwd directives have been mixed // This is where we decide which function should be called. return parse_impl(first, last, context, skipper, attr_, typename mpl::or_::type() ); } template bool parse_impl(Iterator& first, Iterator const& last , Context& context, Skipper const& skipper , Attribute& attr_,mpl::true_ /* no ikwd */) const { // wrap the attribute in a tuple if it is not a tuple typename traits::wrap_if_not_tuple::type attr(attr_); flags_type flags(flags_init); //flags.assign(false); counters_type counters; counters.assign(0); typedef repository::qi::detail::parse_dispatcher::type , mpl::false_ > parser_visitor_type; parser_visitor_type parse_visitor(elements, first, last , context, skipper, flags , counters, attr); // We have a bool array 'flags' with one flag for each parser as well as a 'counter' // array. // The kwd directive sets and increments the counter when a successeful parse occured // as well as the slot of the corresponding parser to true in the flags array as soon // the minimum repetition requirement is met and keeps that value to true as long as // the maximum repetition requirement is met. // The parsing takes place here in two steps: // 1) parse a keyword and fetch the parser index associated with that keyword // 2) call the associated parser and store the parsed value in the matching attribute. Iterator save = first; while(true) { spirit::qi::skip_over(first, last, skipper); if (parser_index_type* val_ptr = lookup->find(first, last, filter_type())) { spirit::qi::skip_over(first, last, skipper); if(!apply_visitor(parse_visitor,*val_ptr)){ first = save; return false; } save = first; } else { // Check that we are leaving the keywords parser in a successfull state BOOST_FOREACH(bool &valid,flags) { if(!valid) { first = save; return false; } } return true; } } return false; } // Handle the mixed kwd and ikwd case template bool parse_impl(Iterator& first, Iterator const& last , Context& context, Skipper const& skipper , Attribute& attr_,mpl::false_) const { // wrap the attribute in a tuple if it is not a tuple typename traits::wrap_if_not_tuple::type attr(attr_); flags_type flags(flags_init); //flags.assign(false); counters_type counters; counters.assign(0); typedef detail::parse_dispatcher::type , mpl::false_> parser_visitor_type; typedef detail::parse_dispatcher::type , mpl::true_> no_case_parser_visitor_type; parser_visitor_type parse_visitor(elements,first,last ,context,skipper,flags,counters,attr); no_case_parser_visitor_type no_case_parse_visitor(elements,first,last ,context,skipper,flags,counters,attr); // We have a bool array 'flags' with one flag for each parser as well as a 'counter' // array. // The kwd directive sets and increments the counter when a successeful parse occured // as well as the slot of the corresponding parser to true in the flags array as soon // the minimum repetition requirement is met and keeps that value to true as long as // the maximum repetition requirement is met. // The parsing takes place here in two steps: // 1) parse a keyword and fetch the parser index associated with that keyword // 2) call the associated parser and store the parsed value in the matching attribute. Iterator save = first; while(true) { spirit::qi::skip_over(first, last, skipper); // First pass case sensitive Iterator saved_first = first; if (parser_index_type* val_ptr = lookup->find(first, last, spirit::qi::tst_pass_through())) { spirit::qi::skip_over(first, last, skipper); if(!apply_visitor(parse_visitor,*val_ptr)){ first = save; return false; } save = first; } // Second pass case insensitive else if(parser_index_type* val_ptr = lookup->find(saved_first,last,nc_filter())) { first = saved_first; spirit::qi::skip_over(first, last, skipper); if(!apply_visitor(no_case_parse_visitor,*val_ptr)){ first = save; return false; } save = first; } else { // Check that we are leaving the keywords parser in a successfull state BOOST_FOREACH(bool &valid,flags) { if(!valid) { first = save; return false; } } return true; } } return false; } template info what(Context& context) const { info result("keywords"); fusion::for_each(elements, spirit::detail::what_function(result, context)); return result; } flags_type flags_init; Elements elements; shared_ptr lookup; }; }}}} namespace boost { namespace spirit { namespace qi { /////////////////////////////////////////////////////////////////////////// // Parser generators: make_xxx function (objects) /////////////////////////////////////////////////////////////////////////// template struct make_composite { typedef repository::qi::keywords result_type; result_type operator()(Elements ref, unused_type) const { return result_type(ref); } }; }}} namespace boost { namespace spirit { namespace traits { // We specialize this for keywords (see support/attributes.hpp). // For keywords, we only wrap the attribute in a tuple IFF // it is not already a fusion tuple. template struct pass_attribute, Attribute> : wrap_if_not_tuple {}; template struct has_semantic_action > : nary_has_semantic_action {}; }}} #endif