blob: 4b19115a675d5e1dc331674a1d72a2b785dd26b1 [file] [log] [blame]
/*=============================================================================
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(SPIRIT_PARSE_INTO_CONTAINER_JAN_15_2013_0957PM)
#define SPIRIT_PARSE_INTO_CONTAINER_JAN_15_2013_0957PM
#if defined(_MSC_VER)
#pragma once
#endif
#include <type_traits>
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
#include <boost/spirit/home/x3/support/traits/value_traits.hpp>
#include <boost/spirit/home/x3/support/traits/attribute_of.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/front.hpp>
#include <boost/fusion/include/back.hpp>
#include <boost/variant/apply_visitor.hpp>
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>
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(
Parser const& parser
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr)
{
// synthesized attribute needs to be value initialized
typedef typename
traits::container_value<Attribute>::type
value_type;
value_type val = traits::value_initialize<value_type>::call();
if (!parser.parse(first, last, context, rcontext, val))
return false;
// push the parsed value into our attribute
traits::push_back(attr, val);
return true;
}
// 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 Container, typename RContext, typename Context>
struct parser_attr_is_substitute_for_container_value
: traits::is_substitute<
typename traits::attribute_of<Parser, Context>::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::true_)
{
return parse_into_container_base_impl<Parser>::call(
parser, 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::false_)
{
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)
{
return call(parser, first, last, context, rcontext, attr,
parser_attr_is_substitute_for_container_value<
Parser, Attribute, Context, RContext>());
}
};
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