blob: c32c55c7af2af521fba4babd72c8e8ad738b4964 [file] [log] [blame]
/*=============================================================================
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 <boost/spirit/home/qi/meta_compiler.hpp>
#include <boost/spirit/home/qi/domain.hpp>
#include <boost/spirit/home/qi/detail/permute_function.hpp>
#include <boost/spirit/home/qi/detail/attributes.hpp>
#include <boost/spirit/home/support/detail/what_function.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/fusion/include/iter_fold.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/fusion/include/value_at.hpp>
#include <boost/optional.hpp>
#include <boost/foreach.hpp>
#include <boost/array.hpp>
#include <boost/spirit/home/qi/string/symbols.hpp>
#include <boost/spirit/home/qi/string/lit.hpp>
#include <boost/spirit/home/qi/action/action.hpp>
#include <boost/spirit/home/qi/directive/hold.hpp>
#include <boost/mpl/count_if.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/spirit/repository/home/qi/operator/detail/keywords.hpp>
namespace boost { namespace spirit
{
///////////////////////////////////////////////////////////////////////////
// Enablers
///////////////////////////////////////////////////////////////////////////
template <>
struct use_operator<qi::domain, proto::tag::divides > // enables /
: mpl::true_ {};
template <>
struct flatten_tree<qi::domain, proto::tag::divides> // 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 <typename T>
struct is_kwd_parser : detail::has_kwd_parser_id<T> {};
template <typename Subject, typename Action>
struct is_kwd_parser<spirit::qi::action<Subject,Action> > : detail::has_kwd_parser_id<Subject> {};
template <typename Subject>
struct is_kwd_parser<spirit::qi::hold_directive<Subject> > : detail::has_kwd_parser_id<Subject> {};
// Keywords operator
template <typename Elements, typename Modifiers>
struct keywords : spirit::qi::nary_parser<keywords<Elements,Modifiers> >
{
template <typename Context, typename Iterator>
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<all_attributes>::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 <typename Sequence>
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<int, 0, sequence_size::value>::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<int_range, mpl::back_inserter<mpl::vector<> > >::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 <typename Sequence >
struct build_char_type_sequence
{
struct element_char_type
{
template <typename T>
struct result;
template <typename F, typename Element>
struct result<F(Element)>
{
typedef typename Element::char_type type;
};
template <typename F, typename Element,typename Action>
struct result<F(spirit::qi::action<Element,Action>) >
{
typedef typename Element::char_type type;
};
template <typename F, typename Element>
struct result<F(spirit::qi::hold_directive<Element>)>
{
typedef typename Element::char_type type;
};
// never called, but needed for decltype-based result_of (C++0x)
template <typename Element>
typename result<element_char_type(Element)>::type
operator()(Element&) const;
};
// Compute the list of character types of the child kwd directives
typedef typename
fusion::result_of::transform<Sequence, element_char_type>::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 <typename Sequence>
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::_2>,
mpl::_1, mpl::push_back<mpl::_1, mpl::_2>
>
>::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<no_duplicate_char_types>::value, ==, 1 );
typedef typename mpl::front<no_duplicate_char_types>::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 <typename CharEncoding>
struct no_case_filter
{
char_type operator()(char_type ch) const
{
return static_cast<char_type>(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 <typename Sequence >
struct build_case_type_sequence
{
struct element_case_type
{
template <typename T>
struct result;
template <typename F, typename Element>
struct result<F(Element)>
{
typedef typename Element::no_case_keyword type;
};
template <typename F, typename Element,typename Action>
struct result<F(spirit::qi::action<Element,Action>) >
{
typedef typename Element::no_case_keyword type;
};
template <typename F, typename Element>
struct result<F(spirit::qi::hold_directive<Element>)>
{
typedef typename Element::no_case_keyword type;
};
// never called, but needed for decltype-based result_of (C++0x)
template <typename Element>
typename result<element_case_type(Element)>::type
operator()(Element&) const;
};
// Compute the list of character types of the child kwd directives
typedef typename
fusion::result_of::transform<Sequence, element_case_type>::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 <typename Sequence,typename CaseType>
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<mpl::_,CaseType>
>::type type;
};
// Build the case type sequence
typedef typename build_case_type_sequence<Elements>::type case_type_sequence;
// Count the number of case sensitive entries and case insensitve entries
typedef typename get_nb_case_types<case_type_sequence,mpl::true_>::type ikwd_count;
typedef typename get_nb_case_types<case_type_sequence,mpl::false_>::type kwd_count;
// Get the size of the original sequence
typedef typename mpl::size<Elements>::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<Modifiers, spirit::tag::char_code_base<spirit::tag::no_case> > 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<bool, fusion::result_of::size<Elements>::value> flags_type;
typedef boost::array<int, fusion::result_of::size<Elements>::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<keywords_type> lookup,flags_type &flags) :
lookup(lookup)
,flags(flags)
{}
typedef typename fusion::result_of::begin< Sequence >::type sequence_begin;
template <typename T>
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<int,position_raw::value> position;
return call(i,fusion::deref(parser),position());
}
template <typename T, typename Position, typename Action>
int call( const int i, const spirit::qi::action<T,Action> &parser, const Position position ) const
{
// Make the keyword/parse index entry in the tst parser
lookup->add(
traits::get_begin<char_type>(parser.subject.keyword.str),
traits::get_end<char_type>(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 <typename T, typename Position>
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<char_type>(parser.keyword.str),
traits::get_end<char_type>(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 <typename T, typename Position>
int call( const int i, const spirit::qi::hold_directive<T> & parser, const Position position) const
{
// Make the keyword/parse index entry in the tst parser
lookup->add(
traits::get_begin<char_type>(parser.subject.keyword.str),
traits::get_end<char_type>(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<keywords_type> 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<Elements> f1(lookup,flags_init);
fusion::iter_fold(this->elements,0,f1);
}
template <typename Iterator, typename Context
, typename Skipper, typename Attribute>
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_<all_directives_of_same_type, no_case>::type()
);
}
template <typename Iterator, typename Context
, typename Skipper, typename Attribute>
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<Attribute>::type attr(attr_);
flags_type flags(flags_init);
//flags.assign(false);
counters_type counters;
counters.assign(0);
typedef repository::qi::detail::parse_dispatcher<Elements,Iterator, Context, Skipper
, flags_type, counters_type
, typename traits::wrap_if_not_tuple<Attribute>::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 <typename Iterator, typename Context
, typename Skipper, typename Attribute>
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<Attribute>::type attr(attr_);
flags_type flags(flags_init);
//flags.assign(false);
counters_type counters;
counters.assign(0);
typedef detail::parse_dispatcher<Elements, Iterator, Context, Skipper
, flags_type, counters_type
, typename traits::wrap_if_not_tuple<Attribute>::type
, mpl::false_> parser_visitor_type;
typedef detail::parse_dispatcher<Elements, Iterator, Context, Skipper
, flags_type, counters_type
, typename traits::wrap_if_not_tuple<Attribute>::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 <typename Context>
info what(Context& context) const
{
info result("keywords");
fusion::for_each(elements,
spirit::detail::what_function<Context>(result, context));
return result;
}
flags_type flags_init;
Elements elements;
shared_ptr<keywords_type> lookup;
};
}}}}
namespace boost { namespace spirit { namespace qi {
///////////////////////////////////////////////////////////////////////////
// Parser generators: make_xxx function (objects)
///////////////////////////////////////////////////////////////////////////
template <typename Elements, typename Modifiers >
struct make_composite<proto::tag::divides, Elements, Modifiers >
{
typedef repository::qi::keywords<Elements,Modifiers> 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 <typename Elements, typename Modifiers,typename Attribute>
struct pass_attribute<repository::qi::keywords<Elements,Modifiers>, Attribute>
: wrap_if_not_tuple<Attribute> {};
template <typename Elements, typename Modifiers>
struct has_semantic_action<repository::qi::keywords<Elements, Modifiers> >
: nary_has_semantic_action<Elements> {};
}}}
#endif