/*=============================================================================
|
Copyright (c) 2001-2014 Joel de Guzman
|
|
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(BOOST_SPIRIT_X3_PARSE_INTO_CONTAINER_JAN_15_2013_0957PM)
|
#define BOOST_SPIRIT_X3_PARSE_INTO_CONTAINER_JAN_15_2013_0957PM
|
|
#include <type_traits>
|
|
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
|
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
#include <boost/spirit/home/x3/support/traits/pseudo_attribute.hpp>
|
#include <boost/spirit/home/x3/support/traits/handles_container.hpp>
|
#include <boost/spirit/home/x3/support/traits/has_attribute.hpp>
|
#include <boost/spirit/home/x3/support/traits/is_substitute.hpp>
|
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
|
#include <boost/mpl/and.hpp>
|
#include <boost/fusion/include/at_key.hpp>
|
#include <boost/fusion/include/front.hpp>
|
#include <boost/fusion/include/back.hpp>
|
#include <boost/variant/apply_visitor.hpp>
|
#include <iterator> // for std::make_move_iterator
|
|
namespace boost { namespace spirit { namespace x3 { namespace detail
|
{
|
template <typename Attribute, typename Value>
|
struct saver_visitor;
|
|
// save to associative fusion container where Key is simple type
|
template <typename Key, typename Enable = void>
|
struct save_to_assoc_attr
|
{
|
template <typename Value, typename Attribute>
|
static void call(const Key, Value& value, Attribute& attr)
|
{
|
traits::move_to(value, fusion::at_key<Key>(attr));
|
}
|
};
|
|
// save to associative fusion container where Key
|
// is variant over possible keys
|
template <typename ...T>
|
struct save_to_assoc_attr<variant<T...> >
|
{
|
typedef variant<T...> variant_t;
|
|
template <typename Value, typename Attribute>
|
static void call(const variant_t key, Value& value, Attribute& attr)
|
{
|
apply_visitor(saver_visitor<Attribute, Value>(attr, value), key);
|
}
|
};
|
|
template <typename Attribute, typename Value>
|
struct saver_visitor : boost::static_visitor<void>
|
{
|
saver_visitor(Attribute& attr, Value& value)
|
: attr(attr), value(value) {};
|
|
Attribute& attr;
|
Value& value;
|
|
template <typename Key>
|
void operator()(Key) const
|
{
|
save_to_assoc_attr<Key>::call(Key(), value,attr);
|
}
|
};
|
|
template <typename Parser, typename Container, typename Context>
|
struct parser_accepts_container
|
: traits::is_substitute<
|
typename traits::attribute_of<Parser, Context>::type
|
, Container
|
>
|
{};
|
|
template <typename Parser>
|
struct parse_into_container_base_impl
|
{
|
private:
|
|
// Parser has attribute (synthesize; Attribute is a container)
|
template <typename Iterator, typename Context
|
, typename RContext, typename Attribute>
|
static bool call_synthesize_x(
|
Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, Attribute& attr, mpl::false_)
|
{
|
// synthesized attribute needs to be value initialized
|
using value_type = typename traits::container_value<Attribute>::type;
|
value_type val{};
|
|
if (!parser.parse(first, last, context, rcontext, val))
|
return false;
|
|
// push the parsed value into our attribute
|
traits::push_back(attr, static_cast<value_type&&>(val));
|
return true;
|
}
|
|
// Parser has attribute (synthesize; Attribute is a container)
|
template <typename Iterator, typename Context
|
, typename RContext, typename Attribute>
|
static bool call_synthesize_x(
|
Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, Attribute& attr, mpl::true_)
|
{
|
return parser.parse(first, last, context, rcontext, attr);
|
}
|
|
// Parser has attribute (synthesize; Attribute is a container)
|
template <typename Iterator, typename Context
|
, typename RContext, typename Attribute>
|
static bool call_synthesize(
|
Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, Attribute& attr)
|
{
|
typedef
|
parser_accepts_container<Parser, Attribute, Context>
|
parser_accepts_container;
|
|
return call_synthesize_x(parser, first, last, context, rcontext, attr
|
, parser_accepts_container());
|
}
|
|
// Parser has attribute (synthesize; Attribute is a single element fusion sequence)
|
template <typename Iterator, typename Context
|
, typename RContext, typename Attribute>
|
static bool call_synthesize_into_fusion_seq(Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr, mpl::false_ /* is_associative */)
|
{
|
static_assert(traits::has_size<Attribute, 1>::value,
|
"Expecting a single element fusion sequence");
|
return call_synthesize(parser, first, last, context, rcontext,
|
fusion::front(attr));
|
}
|
|
// Parser has attribute (synthesize; Attribute is fusion map sequence)
|
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
static bool call_synthesize_into_fusion_seq(
|
Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr, mpl::true_ /*is_associative*/)
|
{
|
using attribute_type = typename traits::attribute_of<Parser, Context>::type;
|
static_assert(traits::has_size<attribute_type, 2>::value,
|
"To parse directly into fusion map parser must produce 2 element attr");
|
|
// use type of first element of attribute as key
|
using key = typename std::remove_reference<
|
typename fusion::result_of::front<attribute_type>::type>::type;
|
|
attribute_type attr_;
|
if (!parser.parse(first, last, context, rcontext, attr_))
|
return false;
|
|
save_to_assoc_attr<key>::call(fusion::front(attr_), fusion::back(attr_), attr);
|
return true;
|
}
|
|
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
static bool call_synthesize_dispatch_by_seq(Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr, mpl::true_ /*is_sequence*/)
|
{
|
return call_synthesize_into_fusion_seq(
|
parser, first, last, context, rcontext, attr
|
, fusion::traits::is_associative<Attribute>());
|
}
|
|
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
static bool call_synthesize_dispatch_by_seq(Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr, mpl::false_ /*is_sequence*/)
|
{
|
return call_synthesize(parser, first, last, context, rcontext, attr);
|
}
|
|
// Parser has attribute (synthesize)
|
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
static bool call(Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr, mpl::true_)
|
{
|
return call_synthesize_dispatch_by_seq(parser, first, last, context, rcontext, attr
|
, fusion::traits::is_sequence<Attribute>());
|
}
|
|
// Parser has no attribute (pass unused)
|
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
static bool call(
|
Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& /* attr */, mpl::false_)
|
{
|
return parser.parse(first, last, context, rcontext, unused);
|
}
|
|
|
public:
|
|
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
static bool call(Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr)
|
{
|
return call(parser, first, last, context, rcontext, attr
|
, mpl::bool_<traits::has_attribute<Parser, Context>::value>());
|
}
|
};
|
|
template <typename Parser, typename Context, typename RContext, typename Enable = void>
|
struct parse_into_container_impl : parse_into_container_base_impl<Parser> {};
|
|
template <typename Parser, typename Iterator, typename Container, typename Context>
|
struct parser_attr_is_substitute_for_container_value
|
: traits::is_substitute<
|
typename traits::pseudo_attribute<
|
Context
|
, typename traits::attribute_of<Parser, Context>::type
|
, Iterator
|
>::type
|
, typename traits::container_value<Container>::type
|
>
|
{};
|
|
template <typename Parser, typename Context, typename RContext>
|
struct parse_into_container_impl<Parser, Context, RContext,
|
typename enable_if<traits::handles_container<Parser, Context>>::type>
|
{
|
template <typename Iterator, typename Attribute>
|
static bool call(
|
Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, Attribute& attr, mpl::false_)
|
{
|
return parse_into_container_base_impl<Parser>::call(
|
parser, first, last, context, rcontext, attr);
|
}
|
|
template <typename Iterator>
|
static bool call(
|
Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, unused_type attr, mpl::true_)
|
{
|
return parser.parse(first, last, context, rcontext, attr);
|
}
|
|
template <typename Iterator, typename Attribute>
|
static bool call(
|
Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, Attribute& attr, mpl::true_)
|
{
|
if (traits::is_empty(attr))
|
return parser.parse(first, last, context, rcontext, attr);
|
Attribute rest;
|
bool r = parser.parse(first, last, context, rcontext, rest);
|
if (r)
|
traits::append(attr, std::make_move_iterator(rest.begin()),
|
std::make_move_iterator(rest.end()));
|
return r;
|
}
|
|
template <typename Iterator, typename Attribute>
|
static bool call(Parser const& parser
|
, Iterator& first, Iterator const& last
|
, Context const& context, RContext& rcontext, Attribute& attr)
|
{
|
typedef parser_accepts_container<
|
Parser, Attribute, Context>
|
parser_accepts_container;
|
|
typedef parser_attr_is_substitute_for_container_value<
|
Parser, Iterator, Attribute, Context>
|
parser_attr_is_substitute_for_container_value;
|
|
typedef mpl::or_<
|
parser_accepts_container
|
, mpl::not_<parser_attr_is_substitute_for_container_value>>
|
pass_attibute_as_is;
|
|
return call(parser, first, last, context, rcontext, attr,
|
pass_attibute_as_is());
|
}
|
};
|
|
template <typename Parser, typename Iterator, typename Context
|
, typename RContext, typename Attribute>
|
bool parse_into_container(
|
Parser const& parser
|
, Iterator& first, Iterator const& last, Context const& context
|
, RContext& rcontext, Attribute& attr)
|
{
|
return parse_into_container_impl<Parser, Context, RContext>::call(
|
parser, first, last, context, rcontext, attr);
|
}
|
|
}}}}
|
|
#endif
|