blob: 1c9d8ff2db55d10568ebb5b00e7b91130b5b2dd1 [file] [log] [blame]
/*=============================================================================
Copyright (c) 2001-2011 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_TERMINAL_NOVEMBER_04_2008_0906AM)
#define BOOST_SPIRIT_TERMINAL_NOVEMBER_04_2008_0906AM
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/proto/proto.hpp>
#include <boost/fusion/include/void.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/home/support/meta_compiler.hpp>
#include <boost/spirit/home/support/detail/make_vector.hpp>
#include <boost/spirit/home/support/unused.hpp>
namespace boost { namespace spirit
{
template <typename Terminal, typename Args>
struct terminal_ex
{
typedef Terminal terminal_type;
typedef Args args_type;
terminal_ex(Args const& args)
: args(args) {}
terminal_ex(Args const& args, Terminal const& term)
: args(args), term(term) {}
Args args; // Args is guaranteed to be a fusion::vectorN so you
// can use that template for detection and specialization
Terminal term;
};
template <typename Terminal, typename Actor, int Arity>
struct lazy_terminal
{
typedef Terminal terminal_type;
typedef Actor actor_type;
static int const arity = Arity;
lazy_terminal(Actor const& actor)
: actor(actor) {}
lazy_terminal(Actor const& actor, Terminal const& term)
: actor(actor), term(term) {}
Actor actor;
Terminal term;
};
template <typename Domain, typename Terminal, int Arity, typename Enable = void>
struct use_lazy_terminal : mpl::false_ {};
template <typename Domain, typename Terminal, int Arity, typename Enable = void>
struct use_lazy_directive : mpl::false_ {};
template <typename Domain, typename Terminal, int Arity, typename Actor>
struct use_terminal<Domain, lazy_terminal<Terminal, Actor, Arity> >
: use_lazy_terminal<Domain, Terminal, Arity> {};
template <typename Domain, typename Terminal, int Arity, typename Actor>
struct use_directive<Domain, lazy_terminal<Terminal, Actor, Arity> >
: use_lazy_directive<Domain, Terminal, Arity> {};
template <
typename F
, typename A0 = unused_type
, typename A1 = unused_type
, typename A2 = unused_type
, typename Unused = unused_type
>
struct make_lazy;
template <typename F, typename A0>
struct make_lazy<F, A0>
{
typedef typename
proto::terminal<
lazy_terminal<
typename F::terminal_type
, phoenix::actor<
typename phoenix::as_composite<
phoenix::detail::function_eval<1>, F, A0
>::type
>
, 1 // arity
>
>::type
result_type;
typedef result_type type;
result_type
operator()(F f, A0 const& _0) const
{
typedef typename result_type::proto_child0 child_type;
return result_type::make(child_type(
phoenix::compose<phoenix::detail::function_eval<1> >(f, _0)
, f.proto_base().child0
));
}
};
template <typename F, typename A0, typename A1>
struct make_lazy<F, A0, A1>
{
typedef typename
proto::terminal<
lazy_terminal<
typename F::terminal_type
, phoenix::actor<
typename phoenix::as_composite<
phoenix::detail::function_eval<2>, F, A0, A1
>::type
>
, 2 // arity
>
>::type
result_type;
typedef result_type type;
result_type
operator()(F f, A0 const& _0, A1 const& _1) const
{
typedef typename result_type::proto_child0 child_type;
return result_type::make(child_type(
phoenix::compose<phoenix::detail::function_eval<2> >(f, _0, _1)
, f.proto_base().child0
));
}
};
template <typename F, typename A0, typename A1, typename A2>
struct make_lazy<F, A0, A1, A2>
{
typedef typename
proto::terminal<
lazy_terminal<
typename F::terminal_type
, phoenix::actor<
typename phoenix::as_composite<
phoenix::detail::function_eval<3>, F, A0, A1, A2
>::type
>
, 3 // arity
>
>::type
result_type;
typedef result_type type;
result_type
operator()(F f, A0 const& _0, A1 const& _1, A2 const& _2) const
{
typedef typename result_type::proto_child0 child_type;
return result_type::make(child_type(
phoenix::compose<phoenix::detail::function_eval<3> >(f, _0, _1, _2)
, f.proto_base().child0
));
}
};
namespace detail
{
// Helper struct for SFINAE purposes
template <bool C>
struct bool_;
template <>
struct bool_<true> : mpl::bool_<true>
{ typedef bool_<true>* is_true; };
template <>
struct bool_<false> : mpl::bool_<false>
{ typedef bool_<false>* is_false; };
// Metafunction to detect if at least one arg is a Phoenix actor
template <
typename A0
, typename A1 = unused_type
, typename A2 = unused_type
>
struct contains_actor
: bool_<
phoenix::is_actor<A0>::value
|| phoenix::is_actor<A1>::value
|| phoenix::is_actor<A2>::value
>
{};
// to_lazy_arg: convert a terminal arg type to the type make_lazy needs
template <typename A>
struct to_lazy_arg
: phoenix::as_actor<A> // wrap A in a Phoenix actor if not already one
{};
template <typename A>
struct to_lazy_arg<const A>
: to_lazy_arg<A>
{};
template <>
struct to_lazy_arg<unused_type>
{
// unused arg: make_lazy wants unused_type
typedef unused_type type;
};
// to_nonlazy_arg: convert a terminal arg type to the type make_vector needs
template <typename A>
struct to_nonlazy_arg
{
// identity
typedef A type;
};
template <typename A>
struct to_nonlazy_arg<const A>
: to_nonlazy_arg<A>
{};
template <>
struct to_nonlazy_arg<unused_type>
{
// unused arg: make_vector wants fusion::void_
typedef fusion::void_ type;
};
}
template <typename Terminal>
struct terminal
: proto::extends<
typename proto::terminal<Terminal>::type
, terminal<Terminal>
>
{
typedef terminal<Terminal> this_type;
typedef Terminal terminal_type;
typedef proto::extends<
typename proto::terminal<Terminal>::type
, terminal<Terminal>
> base_type;
terminal() {}
terminal(Terminal const& t)
: base_type(proto::terminal<Terminal>::type::make(t)) {}
template <
bool Lazy
, typename A0
, typename A1
, typename A2
>
struct result_helper;
template <
typename A0
, typename A1
, typename A2
>
struct result_helper<false, A0, A1, A2>
{
typedef typename
proto::terminal<
terminal_ex<
Terminal
, typename detail::result_of::make_vector<
typename detail::to_nonlazy_arg<A0>::type
, typename detail::to_nonlazy_arg<A1>::type
, typename detail::to_nonlazy_arg<A2>::type>::type>
>::type
type;
};
template <
typename A0
, typename A1
, typename A2
>
struct result_helper<true, A0, A1, A2>
{
typedef typename
make_lazy<this_type
, typename detail::to_lazy_arg<A0>::type
, typename detail::to_lazy_arg<A1>::type
, typename detail::to_lazy_arg<A2>::type>::type
type;
};
// FIXME: we need to change this to conform to the result_of protocol
template <
typename A0
, typename A1 = unused_type
, typename A2 = unused_type // Support up to 3 args
>
struct result
{
typedef typename
result_helper<
detail::contains_actor<A0, A1, A2>::value
, A0, A1, A2
>::type
type;
};
// Note: in the following overloads, SFINAE cannot
// be done on return type because of gcc bug #24915:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24915
// Hence an additional, fake argument is used for SFINAE,
// using a type which can never be a real argument type.
// Non-lazy overloads. Only enabled when all
// args are immediates (no Phoenix actor).
template <typename A0>
typename result<A0>::type
operator()(A0 const& _0
, typename detail::contains_actor<A0>::is_false = 0) const
{
typedef typename result<A0>::type result_type;
typedef typename result_type::proto_child0 child_type;
return result_type::make(
child_type(
detail::make_vector(_0)
, this->proto_base().child0)
);
}
template <typename A0, typename A1>
typename result<A0, A1>::type
operator()(A0 const& _0, A1 const& _1
, typename detail::contains_actor<A0, A1>::is_false = 0) const
{
typedef typename result<A0, A1>::type result_type;
typedef typename result_type::proto_child0 child_type;
return result_type::make(
child_type(
detail::make_vector(_0, _1)
, this->proto_base().child0)
);
}
template <typename A0, typename A1, typename A2>
typename result<A0, A1, A2>::type
operator()(A0 const& _0, A1 const& _1, A2 const& _2
, typename detail::contains_actor<A0, A1, A2>::is_false = 0) const
{
typedef typename result<A0, A1, A2>::type result_type;
typedef typename result_type::proto_child0 child_type;
return result_type::make(
child_type(
detail::make_vector(_0, _1, _2)
, this->proto_base().child0)
);
}
// Lazy overloads. Enabled when at
// least one arg is a Phoenix actor.
template <typename A0>
typename result<A0>::type
operator()(A0 const& _0
, typename detail::contains_actor<A0>::is_true = 0) const
{
return make_lazy<this_type
, typename phoenix::as_actor<A0>::type>()(*this
, phoenix::as_actor<A0>::convert(_0));
}
template <typename A0, typename A1>
typename result<A0, A1>::type
operator()(A0 const& _0, A1 const& _1
, typename detail::contains_actor<A0, A1>::is_true = 0) const
{
return make_lazy<this_type
, typename phoenix::as_actor<A0>::type
, typename phoenix::as_actor<A1>::type>()(*this
, phoenix::as_actor<A0>::convert(_0)
, phoenix::as_actor<A1>::convert(_1));
}
template <typename A0, typename A1, typename A2>
typename result<A0, A1, A2>::type
operator()(A0 const& _0, A1 const& _1, A2 const& _2
, typename detail::contains_actor<A0, A1, A2>::is_true = 0) const
{
return make_lazy<this_type
, typename phoenix::as_actor<A0>::type
, typename phoenix::as_actor<A1>::type
, typename phoenix::as_actor<A2>::type>()(*this
, phoenix::as_actor<A0>::convert(_0)
, phoenix::as_actor<A1>::convert(_1)
, phoenix::as_actor<A2>::convert(_2));
}
private:
// silence MSVC warning C4512: assignment operator could not be generated
terminal& operator= (terminal const&);
};
///////////////////////////////////////////////////////////////////////////
namespace result_of
{
// Calculate the type of the compound terminal if generated by one of
// the spirit::terminal::operator() overloads above
// The terminal type itself is passed through without modification
template <typename Tag>
struct terminal
{
typedef spirit::terminal<Tag> type;
};
template <typename Tag, typename A0>
struct terminal<Tag(A0)>
{
typedef typename spirit::terminal<Tag>::
template result<A0>::type type;
};
template <typename Tag, typename A0, typename A1>
struct terminal<Tag(A0, A1)>
{
typedef typename spirit::terminal<Tag>::
template result<A0, A1>::type type;
};
template <typename Tag, typename A0, typename A1, typename A2>
struct terminal<Tag(A0, A1, A2)>
{
typedef typename spirit::terminal<Tag>::
template result<A0, A1, A2>::type type;
};
}
///////////////////////////////////////////////////////////////////////////
// support for stateful tag types
namespace tag
{
template <typename Data, typename Tag
, typename DataTag1 = unused_type, typename DataTag2 = unused_type>
struct stateful_tag
{
typedef Data data_type;
stateful_tag() {}
stateful_tag(data_type const& data) : data_(data) {}
data_type data_;
private:
// silence MSVC warning C4512: assignment operator could not be generated
stateful_tag& operator= (stateful_tag const&);
};
}
template <typename Data, typename Tag
, typename DataTag1 = unused_type, typename DataTag2 = unused_type>
struct stateful_tag_type
: spirit::terminal<tag::stateful_tag<Data, Tag, DataTag1, DataTag2> >
{
typedef tag::stateful_tag<Data, Tag, DataTag1, DataTag2> tag_type;
stateful_tag_type() {}
stateful_tag_type(Data const& data)
: spirit::terminal<tag_type>(data) {}
private:
// silence MSVC warning C4512: assignment operator could not be generated
stateful_tag_type& operator= (stateful_tag_type const&);
};
namespace detail
{
// extract expression if this is a Tag
template <typename StatefulTag>
struct get_stateful_data
{
typedef typename StatefulTag::data_type data_type;
// is invoked if given tag is != Tag
template <typename Tag_>
static data_type call(Tag_) { return data_type(); }
// this is invoked if given tag is same as'Tag'
static data_type const& call(StatefulTag const& t) { return t.data_; }
};
}
}}
// Define a spirit terminal. This macro may be placed in any namespace.
// Common placeholders are placed in the main boost::spirit namespace
// (see common_terminals.hpp)
#define BOOST_SPIRIT_TERMINAL(name) \
namespace tag { struct name {}; } \
typedef boost::proto::terminal<tag::name>::type name##_type; \
name##_type const name = {{}}; \
inline void silence_unused_warnings__##name() { (void) name; } \
/***/
#define BOOST_SPIRIT_DEFINE_TERMINALS_A(r, _, name) \
BOOST_SPIRIT_TERMINAL(name) \
/***/
#define BOOST_SPIRIT_DEFINE_TERMINALS(seq) \
BOOST_PP_SEQ_FOR_EACH(BOOST_SPIRIT_DEFINE_TERMINALS_A, _, seq) \
/***/
// Define a spirit extended terminal. This macro may be placed in any namespace.
// Common placeholders are placed in the main boost::spirit namespace
// (see common_terminals.hpp)
#define BOOST_SPIRIT_TERMINAL_EX(name) \
namespace tag { struct name {}; } \
typedef boost::spirit::terminal<tag::name> name##_type; \
name##_type const name = name##_type(); \
inline void silence_unused_warnings__##name() { (void) name; } \
/***/
#define BOOST_SPIRIT_DEFINE_TERMINALS_EX_A(r, _, name) \
BOOST_SPIRIT_TERMINAL_EX(name) \
/***/
#define BOOST_SPIRIT_DEFINE_TERMINALS_EX(seq) \
BOOST_PP_SEQ_FOR_EACH(BOOST_SPIRIT_DEFINE_TERMINALS_EX_A, _, seq) \
/***/
#endif