/*============================================================================= | |
Copyright (c) 2002-2003 Hartmut Kaiser | |
http://spirit.sourceforge.net/ | |
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) | |
=============================================================================*/ | |
#ifndef BOOST_SPIRIT_LISTS_HPP | |
#define BOOST_SPIRIT_LISTS_HPP | |
/////////////////////////////////////////////////////////////////////////////// | |
#include <boost/config.hpp> | |
#include <boost/spirit/home/classic/namespace.hpp> | |
#include <boost/spirit/home/classic/meta/as_parser.hpp> | |
#include <boost/spirit/home/classic/core/parser.hpp> | |
#include <boost/spirit/home/classic/core/composite/composite.hpp> | |
#include <boost/spirit/home/classic/utility/lists_fwd.hpp> | |
#include <boost/spirit/home/classic/utility/impl/lists.ipp> | |
/////////////////////////////////////////////////////////////////////////////// | |
namespace boost { namespace spirit { | |
BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// list_parser class | |
// | |
// List parsers allow to parse constructs like | |
// | |
// item >> *(delim >> item) | |
// | |
// where 'item' is an auxiliary expression to parse and 'delim' is an | |
// auxiliary delimiter to parse. | |
// | |
// The list_parser class also can match an optional closing delimiter | |
// represented by the 'end' parser at the end of the list: | |
// | |
// item >> *(delim >> item) >> !end. | |
// | |
// If ItemT is an action_parser_category type (parser with an attached | |
// semantic action) we have to do something special. This happens, if the | |
// user wrote something like: | |
// | |
// list_p(item[f], delim) | |
// | |
// where 'item' is the parser matching one item of the list sequence and | |
// 'f' is a functor to be called after matching one item. If we would do | |
// nothing, the resulting code would parse the sequence as follows: | |
// | |
// (item[f] - delim) >> *(delim >> (item[f] - delim)) | |
// | |
// what in most cases is not what the user expects. | |
// (If this _is_ what you've expected, then please use one of the list_p | |
// generator functions 'direct()', which will inhibit re-attaching | |
// the actor to the item parser). | |
// | |
// To make the list parser behave as expected: | |
// | |
// (item - delim)[f] >> *(delim >> (item - delim)[f]) | |
// | |
// the actor attached to the 'item' parser has to be re-attached to the | |
// *(item - delim) parser construct, which will make the resulting list | |
// parser 'do the right thing'. | |
// | |
// Additionally special care must be taken, if the item parser is a | |
// unary_parser_category type parser as | |
// | |
// list_p(*anychar_p, ',') | |
// | |
// which without any refactoring would result in | |
// | |
// (*anychar_p - ch_p(',')) | |
// >> *( ch_p(',') >> (*anychar_p - ch_p(',')) ) | |
// | |
// and will not give the expected result (the first *anychar_p will eat up | |
// all the input up to the end of the input stream). So we have to | |
// refactor this into: | |
// | |
// *(anychar_p - ch_p(',')) | |
// >> *( ch_p(',') >> *(anychar_p - ch_p(',')) ) | |
// | |
// what will give the correct result. | |
// | |
// The case, where the item parser is a combination of the two mentioned | |
// problems (i.e. the item parser is a unary parser with an attached | |
// action), is handled accordingly too: | |
// | |
// list_p((*anychar_p)[f], ',') | |
// | |
// will be parsed as expected: | |
// | |
// (*(anychar_p - ch_p(',')))[f] | |
// >> *( ch_p(',') >> (*(anychar_p - ch_p(',')))[f] ). | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
template < | |
typename ItemT, typename DelimT, typename EndT, typename CategoryT | |
> | |
struct list_parser : | |
public parser<list_parser<ItemT, DelimT, EndT, CategoryT> > { | |
typedef list_parser<ItemT, DelimT, EndT, CategoryT> self_t; | |
typedef CategoryT parser_category_t; | |
list_parser(ItemT const &item_, DelimT const &delim_, | |
EndT const& end_ = no_list_endtoken()) | |
: item(item_), delim(delim_), end(end_) | |
{} | |
template <typename ScannerT> | |
typename parser_result<self_t, ScannerT>::type | |
parse(ScannerT const& scan) const | |
{ | |
return impl::list_parser_type<CategoryT> | |
::parse(scan, *this, item, delim, end); | |
} | |
private: | |
typename as_parser<ItemT>::type::embed_t item; | |
typename as_parser<DelimT>::type::embed_t delim; | |
typename as_parser<EndT>::type::embed_t end; | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// List parser generator template | |
// | |
// This is a helper for generating a correct list_parser<> from | |
// auxiliary parameters. There are the following types supported as | |
// parameters yet: parsers, single characters and strings (see | |
// as_parser<> in meta/as_parser.hpp). | |
// | |
// The list_parser_gen by itself can be used for parsing comma separated | |
// lists without item formatting: | |
// | |
// list_p.parse(...) | |
// matches any comma separated list. | |
// | |
// If list_p is used with one parameter, this parameter is used to match | |
// the delimiter: | |
// | |
// list_p(';').parse(...) | |
// matches any semicolon separated list. | |
// | |
// If list_p is used with two parameters, the first parameter is used to | |
// match the items and the second parameter matches the delimiters: | |
// | |
// list_p(uint_p, ',').parse(...) | |
// matches comma separated unsigned integers. | |
// | |
// If list_p is used with three parameters, the first parameter is used | |
// to match the items, the second one is used to match the delimiters and | |
// the third one is used to match an optional ending token sequence: | |
// | |
// list_p(real_p, ';', eol_p).parse(...) | |
// matches a semicolon separated list of real numbers optionally | |
// followed by an end of line. | |
// | |
// The list_p in the previous examples denotes the predefined parser | |
// generator, which should be used to define list parsers (see below). | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
template <typename CharT = char> | |
struct list_parser_gen : | |
public list_parser<kleene_star<anychar_parser>, chlit<CharT> > | |
{ | |
typedef list_parser_gen<CharT> self_t; | |
// construct the list_parser_gen object as an list parser for comma separated | |
// lists without item formatting. | |
list_parser_gen() | |
: list_parser<kleene_star<anychar_parser>, chlit<CharT> > | |
(*anychar_p, chlit<CharT>(',')) | |
{} | |
// The following generator functions should be used under normal circumstances. | |
// (the operator()(...) functions) | |
// Generic generator functions for creation of concrete list parsers, which | |
// support 'normal' syntax: | |
// | |
// item >> *(delim >> item) | |
// | |
// If item isn't given, everything between two delimiters is matched. | |
template<typename DelimT> | |
list_parser< | |
kleene_star<anychar_parser>, | |
typename as_parser<DelimT>::type, | |
no_list_endtoken, | |
unary_parser_category // there is no action to re-attach | |
> | |
operator()(DelimT const &delim_) const | |
{ | |
typedef kleene_star<anychar_parser> item_t; | |
typedef typename as_parser<DelimT>::type delim_t; | |
typedef | |
list_parser<item_t, delim_t, no_list_endtoken, unary_parser_category> | |
return_t; | |
return return_t(*anychar_p, as_parser<DelimT>::convert(delim_)); | |
} | |
template<typename ItemT, typename DelimT> | |
list_parser< | |
typename as_parser<ItemT>::type, | |
typename as_parser<DelimT>::type, | |
no_list_endtoken, | |
typename as_parser<ItemT>::type::parser_category_t | |
> | |
operator()(ItemT const &item_, DelimT const &delim_) const | |
{ | |
typedef typename as_parser<ItemT>::type item_t; | |
typedef typename as_parser<DelimT>::type delim_t; | |
typedef list_parser<item_t, delim_t, no_list_endtoken, | |
BOOST_DEDUCED_TYPENAME item_t::parser_category_t> | |
return_t; | |
return return_t( | |
as_parser<ItemT>::convert(item_), | |
as_parser<DelimT>::convert(delim_) | |
); | |
} | |
// Generic generator function for creation of concrete list parsers, which | |
// support 'extended' syntax: | |
// | |
// item >> *(delim >> item) >> !end | |
template<typename ItemT, typename DelimT, typename EndT> | |
list_parser< | |
typename as_parser<ItemT>::type, | |
typename as_parser<DelimT>::type, | |
typename as_parser<EndT>::type, | |
typename as_parser<ItemT>::type::parser_category_t | |
> | |
operator()( | |
ItemT const &item_, DelimT const &delim_, EndT const &end_) const | |
{ | |
typedef typename as_parser<ItemT>::type item_t; | |
typedef typename as_parser<DelimT>::type delim_t; | |
typedef typename as_parser<EndT>::type end_t; | |
typedef list_parser<item_t, delim_t, end_t, | |
BOOST_DEDUCED_TYPENAME item_t::parser_category_t> | |
return_t; | |
return return_t( | |
as_parser<ItemT>::convert(item_), | |
as_parser<DelimT>::convert(delim_), | |
as_parser<EndT>::convert(end_) | |
); | |
} | |
// The following functions should be used, if the 'item' parser has an attached | |
// semantic action or is a unary_parser_category type parser and the structure | |
// of the resulting list parser should _not_ be refactored during parser | |
// construction (see comment above). | |
// Generic generator function for creation of concrete list parsers, which | |
// support 'normal' syntax: | |
// | |
// item >> *(delim >> item) | |
template<typename ItemT, typename DelimT> | |
list_parser< | |
typename as_parser<ItemT>::type, | |
typename as_parser<DelimT>::type, | |
no_list_endtoken, | |
plain_parser_category // inhibit action re-attachment | |
> | |
direct(ItemT const &item_, DelimT const &delim_) const | |
{ | |
typedef typename as_parser<ItemT>::type item_t; | |
typedef typename as_parser<DelimT>::type delim_t; | |
typedef list_parser<item_t, delim_t, no_list_endtoken, | |
plain_parser_category> | |
return_t; | |
return return_t( | |
as_parser<ItemT>::convert(item_), | |
as_parser<DelimT>::convert(delim_) | |
); | |
} | |
// Generic generator function for creation of concrete list parsers, which | |
// support 'extended' syntax: | |
// | |
// item >> *(delim >> item) >> !end | |
template<typename ItemT, typename DelimT, typename EndT> | |
list_parser< | |
typename as_parser<ItemT>::type, | |
typename as_parser<DelimT>::type, | |
typename as_parser<EndT>::type, | |
plain_parser_category // inhibit action re-attachment | |
> | |
direct( | |
ItemT const &item_, DelimT const &delim_, EndT const &end_) const | |
{ | |
typedef typename as_parser<ItemT>::type item_t; | |
typedef typename as_parser<DelimT>::type delim_t; | |
typedef typename as_parser<EndT>::type end_t; | |
typedef | |
list_parser<item_t, delim_t, end_t, plain_parser_category> | |
return_t; | |
return return_t( | |
as_parser<ItemT>::convert(item_), | |
as_parser<DelimT>::convert(delim_), | |
as_parser<EndT>::convert(end_) | |
); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Predefined list parser generator | |
// | |
// The list_p parser generator can be used | |
// - by itself for parsing comma separated lists without item formatting | |
// or | |
// - for generating list parsers with auxiliary parser parameters | |
// for the 'item', 'delim' and 'end' subsequences. | |
// (see comment above) | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
const list_parser_gen<> list_p = list_parser_gen<>(); | |
/////////////////////////////////////////////////////////////////////////////// | |
BOOST_SPIRIT_CLASSIC_NAMESPACE_END | |
}} // namespace BOOST_SPIRIT_CLASSIC_NS | |
#endif |