///////////////////////////////////////////////////////////////////////////////
// action_matcher.hpp
//
//  Copyright 2008 Eric Niebler.
//  Copyright 2008 David Jenkins.
//
//  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_XPRESSIVE_DETAIL_CORE_MATCHER_ACTION_MATCHER_HPP_EAN_10_04_2005
#define BOOST_XPRESSIVE_DETAIL_CORE_MATCHER_ACTION_MATCHER_HPP_EAN_10_04_2005

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <boost/config.hpp>
#include <boost/version.hpp>
#include <boost/ref.hpp>
#include <boost/assert.hpp>
#include <boost/mpl/if.hpp>
#include <boost/throw_exception.hpp>
#include <boost/utility/result_of.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/xpressive/detail/detail_fwd.hpp>
#include <boost/xpressive/detail/core/quant_style.hpp>
#include <boost/xpressive/detail/core/action.hpp>
#include <boost/xpressive/detail/core/state.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/context.hpp>
#include <boost/xpressive/match_results.hpp> // for type_info_less
#include <boost/xpressive/detail/static/transforms/as_action.hpp> // for 'read_attr'
#if BOOST_VERSION >= 103500
# include <boost/proto/fusion.hpp>
# include <boost/fusion/include/transform_view.hpp>
# include <boost/fusion/include/invoke.hpp>
# include <boost/fusion/include/push_front.hpp>
# include <boost/fusion/include/pop_front.hpp>
#endif

#if BOOST_MSVC
#pragma warning(push)
#pragma warning(disable : 4510) // default constructor could not be generated
#pragma warning(disable : 4512) // assignment operator could not be generated
#pragma warning(disable : 4610) // can never be instantiated - user defined constructor required
#endif

namespace boost { namespace xpressive { namespace detail
{

    #if BOOST_VERSION >= 103500
    struct DataMember
      : proto::mem_ptr<proto::_, proto::terminal<proto::_> >
    {};

    template<typename Expr, long N>
    struct child_
      : remove_reference<
            typename proto::result_of::child_c<Expr &, N>::type
        >
    {};

    ///////////////////////////////////////////////////////////////////////////////
    // mem_ptr_eval
    //  Rewrites expressions of the form x->*foo(a) into foo(x, a) and then
    //  evaluates them.
    template<typename Expr, typename Context, bool IsDataMember = proto::matches<Expr, DataMember>::value>
    struct mem_ptr_eval
    {
        typedef typename child_<Expr, 0>::type left_type;
        typedef typename child_<Expr, 1>::type right_type;

        typedef
            typename proto::result_of::value<
                typename proto::result_of::child_c<right_type, 0>::type
            >::type
        function_type;

        typedef
            fusion::transform_view<
                typename fusion::result_of::push_front<
                    typename fusion::result_of::pop_front<right_type>::type const
                  , reference_wrapper<left_type>
                >::type const
              , proto::eval_fun<Context>
            >
        evaluated_args;

        typedef
            typename fusion::result_of::invoke<function_type, evaluated_args>::type
        result_type;

        result_type operator()(Expr &expr, Context &ctx) const
        {
            return fusion::invoke<function_type>(
                proto::value(proto::child_c<0>(proto::right(expr)))
              , evaluated_args(
                    fusion::push_front(fusion::pop_front(proto::right(expr)), boost::ref(proto::left(expr)))
                  , proto::eval_fun<Context>(ctx)
                )
            );
        }
    };

    ///////////////////////////////////////////////////////////////////////////////
    // mem_ptr_eval
    //  Rewrites expressions of the form x->*foo into foo(x) and then
    //  evaluates them.
    template<typename Expr, typename Context>
    struct mem_ptr_eval<Expr, Context, true>
    {
        typedef typename child_<Expr, 0>::type left_type;
        typedef typename child_<Expr, 1>::type right_type;

        typedef
            typename proto::result_of::value<right_type>::type
        function_type;

        typedef typename boost::result_of<
            function_type(typename proto::result_of::eval<left_type, Context>::type)
        >::type result_type;

        result_type operator()(Expr &expr, Context &ctx) const
        {
            return proto::value(proto::right(expr))(
                proto::eval(proto::left(expr), ctx)
            );
        }
    };
    #endif

    struct attr_with_default_tag
    {};

    template<typename T>
    struct opt;

    ///////////////////////////////////////////////////////////////////////////////
    // action_context
    //
    struct action_context
    {
        explicit action_context(action_args_type *action_args)
          : action_args_(action_args)
        {}

        action_args_type const &args() const
        {
            return *this->action_args_;
        }

        // eval_terminal
        template<typename Expr, typename Arg>
        struct eval_terminal
          : proto::default_eval<Expr, action_context const>
        {};

        template<typename Expr, typename Arg>
        struct eval_terminal<Expr, reference_wrapper<Arg> >
        {
            typedef Arg &result_type;
            result_type operator()(Expr &expr, action_context const &) const
            {
                return proto::value(expr).get();
            }
        };

        template<typename Expr, typename Arg>
        struct eval_terminal<Expr, opt<Arg> >
        {
            typedef Arg const &result_type;
            result_type operator()(Expr &expr, action_context const &) const
            {
                return proto::value(expr);
            }
        };

        template<typename Expr, typename Type, typename Int>
        struct eval_terminal<Expr, action_arg<Type, Int> >
        {
            typedef typename action_arg<Type, Int>::reference result_type;
            result_type operator()(Expr &expr, action_context const &ctx) const
            {
                action_args_type::const_iterator where_ = ctx.args().find(&typeid(proto::value(expr)));
                if(where_ == ctx.args().end())
                {
                    BOOST_THROW_EXCEPTION(
                        regex_error(
                            regex_constants::error_badarg
                          , "An argument to an action was unspecified"
                        )
                    );
                }
                return proto::value(expr).cast(where_->second);
            }
        };

        // eval
        template<typename Expr, typename Tag = typename Expr::proto_tag>
        struct eval
          : proto::default_eval<Expr, action_context const>
        {};

        template<typename Expr>
        struct eval<Expr, proto::tag::terminal>
          : eval_terminal<Expr, typename proto::result_of::value<Expr>::type>
        {};

        // Evaluate attributes like a1|42
        template<typename Expr>
        struct eval<Expr, attr_with_default_tag>
        {
            typedef
                typename proto::result_of::value<
                    typename proto::result_of::left<
                        typename proto::result_of::child<
                            Expr
                        >::type
                    >::type
                >::type
            temp_type;

            typedef typename temp_type::type result_type;

            result_type operator ()(Expr const &expr, action_context const &ctx) const
            {
                return proto::value(proto::left(proto::child(expr))).t_
                    ? *proto::value(proto::left(proto::child(expr))).t_
                    :  proto::eval(proto::right(proto::child(expr)), ctx);
            }
        };

        #if BOOST_VERSION >= 103500
        template<typename Expr>
        struct eval<Expr, proto::tag::mem_ptr>
          : mem_ptr_eval<Expr, action_context const>
        {};
        #endif

    private:
        action_args_type *action_args_;
    };

    ///////////////////////////////////////////////////////////////////////////////
    // action
    //
    template<typename Actor>
    struct action
      : actionable
    {
        action(Actor const &actor)
          : actionable()
          , actor_(actor)
        {
        }

        virtual void execute(action_args_type *action_args) const
        {
            action_context const ctx(action_args);
            proto::eval(this->actor_, ctx);
        }

    private:
        Actor actor_;
    };

    ///////////////////////////////////////////////////////////////////////////////
    // subreg_transform
    //
    struct subreg_transform : proto::transform<subreg_transform>
    {
        template<typename Expr, typename State, typename Data>
        struct impl : proto::transform_impl<Expr, State, Data>
        {
            typedef typename impl::state state_type;

            typedef
                typename proto::terminal<sub_match<typename state_type::iterator> >::type
            result_type;

            result_type operator ()(
                typename impl::expr_param
              , typename impl::state_param state
              , typename impl::data_param data
            ) const
            {
                return result_type::make(state.sub_matches_[ data ]);
            }
        };
    };

    ///////////////////////////////////////////////////////////////////////////////
    // mark_transform
    //
    struct mark_transform : proto::transform<mark_transform>
    {
        template<typename Expr, typename State, typename Data>
        struct impl : proto::transform_impl<Expr, State, Data>
        {
            typedef typename impl::state state_type;
            typedef
                typename proto::terminal<sub_match<typename state_type::iterator> >::type
            result_type;

            result_type operator ()(
                typename impl::expr_param expr
              , typename impl::state_param state
              , typename impl::data_param
            ) const
            {
                return result_type::make(state.sub_matches_[ proto::value(expr).mark_number_ ]);
            }
        };
    };

    ///////////////////////////////////////////////////////////////////////////////
    // opt
    //
    template<typename T>
    struct opt
    {
        typedef T type;
        typedef T const &reference;

        opt(T const *t)
          : t_(t)
        {}

        operator reference() const
        {
            BOOST_XPR_ENSURE_(0 != this->t_, regex_constants::error_badattr, "Use of uninitialized regex attribute");
            return *this->t_;
        }

        T const *t_;
    };

    ///////////////////////////////////////////////////////////////////////////////
    // attr_transform
    //
    struct attr_transform : proto::transform<attr_transform>
    {
        template<typename Expr, typename State, typename Data>
        struct impl : proto::transform_impl<Expr, State, Data>
        {
            typedef typename impl::expr expr_type;

            typedef
                typename expr_type::proto_child0::matcher_type::value_type::second_type
            attr_type;

            typedef
                typename proto::terminal<opt<attr_type> >::type
            result_type;

            result_type operator ()(
                typename impl::expr_param
              , typename impl::state_param state
              , typename impl::data_param
            ) const
            {
                int slot = typename expr_type::proto_child0::nbr_type();
                attr_type const *attr = static_cast<attr_type const *>(state.attr_context_.attr_slots_[slot-1]);
                return result_type::make(opt<attr_type>(attr));
            }
        };
    };

    ///////////////////////////////////////////////////////////////////////////////
    // attr_with_default_transform
    //
    template<typename Grammar, typename Callable = proto::callable>
    struct attr_with_default_transform : proto::transform<attr_with_default_transform<Grammar, Callable> >
    {
        template<typename Expr, typename State, typename Data>
        struct impl : proto::transform_impl<Expr, State, Data>
        {
            typedef
                typename proto::unary_expr<
                    attr_with_default_tag
                  , typename Grammar::template impl<Expr, State, Data>::result_type
                >::type
            result_type;

            result_type operator ()(
                typename impl::expr_param expr
              , typename impl::state_param state
              , typename impl::data_param data
            ) const
            {
                result_type that = {
                    typename Grammar::template impl<Expr, State, Data>()(expr, state, data)
                };
                return that;
            }
        };
    };

    ///////////////////////////////////////////////////////////////////////////////
    // by_ref_transform
    //
    struct by_ref_transform : proto::transform<by_ref_transform>
    {
        template<typename Expr, typename State, typename Data>
        struct impl : proto::transform_impl<Expr, State, Data>
        {
            typedef
                typename proto::result_of::value<typename impl::expr_param>::type
            reference;

            typedef
                typename proto::terminal<reference>::type
            result_type;

            result_type operator ()(
                typename impl::expr_param expr
              , typename impl::state_param
              , typename impl::data_param
            ) const
            {
                return result_type::make(proto::value(expr));
            }
        };
    };

    ///////////////////////////////////////////////////////////////////////////////
    // BindActionArgs
    //
    struct BindActionArgs
      : proto::or_<
            proto::when<proto::terminal<any_matcher>,                      subreg_transform>
          , proto::when<proto::terminal<mark_placeholder>,                 mark_transform>
          , proto::when<proto::terminal<read_attr<proto::_, proto::_> >,   attr_transform>
          , proto::when<proto::terminal<proto::_>,                         by_ref_transform>
          , proto::when<
                proto::bitwise_or<proto::terminal<read_attr<proto::_, proto::_> >, BindActionArgs>
              , attr_with_default_transform<proto::bitwise_or<attr_transform, BindActionArgs> >
            >
          , proto::otherwise<proto::nary_expr<proto::_, proto::vararg<BindActionArgs> > >
        >
    {};

    ///////////////////////////////////////////////////////////////////////////////
    // action_matcher
    //
    template<typename Actor>
    struct action_matcher
      : quant_style<quant_none, 0, false>
    {
        int sub_;
        Actor actor_;

        action_matcher(Actor const &actor, int sub)
          : sub_(sub)
          , actor_(actor)
        {
        }

        template<typename BidiIter, typename Next>
        bool match(match_state<BidiIter> &state, Next const &next) const
        {
            // Bind the arguments
            typedef
                typename boost::result_of<BindActionArgs(
                    Actor const &
                  , match_state<BidiIter> &
                  , int const &
                )>::type
            action_type;

            action<action_type> actor(BindActionArgs()(this->actor_, state, this->sub_));

            // Put the action in the action list
            actionable const **action_list_tail = state.action_list_tail_;
            *state.action_list_tail_ = &actor;
            state.action_list_tail_ = &actor.next;

            // Match the rest of the pattern
            if(next.match(state))
            {
                return true;
            }

            BOOST_ASSERT(0 == actor.next);
            // remove action from list
            *action_list_tail = 0;
            state.action_list_tail_ = action_list_tail;
            return false;
        }
    };

}}}

#if BOOST_MSVC
#pragma warning(pop)
#endif

#endif
