/*=============================================================================
    Copyright (c) 1998-2003 Joel de Guzman
    http://spirit.sourceforge.net/

    Use, modification and distribution is subject to 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_RULE_IPP)
#define BOOST_SPIRIT_RULE_IPP

#if BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT > 1
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/repeat_from_to.hpp>
#include <boost/preprocessor/enum_params.hpp>
#include <boost/preprocessor/enum_params_with_defaults.hpp>
#include <boost/preprocessor/facilities/intercept.hpp>
#include <boost/preprocessor/inc.hpp>
#include <boost/preprocessor/cat.hpp>
#endif

#include <boost/spirit/home/classic/core/parser.hpp>
#include <boost/spirit/home/classic/core/scanner/scanner.hpp>
#include <boost/spirit/home/classic/core/non_terminal/parser_context.hpp>
#include <boost/spirit/home/classic/core/non_terminal/parser_id.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit {

BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN

#if BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT > 1

        template <
            BOOST_PP_ENUM_BINARY_PARAMS(
                BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT,
                typename ScannerT, = mpl::void_ BOOST_PP_INTERCEPT
            )
        >
        struct scanner_list;

#endif // BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT > 1

    ///////////////////////////////////////////////////////////////////////////
    namespace impl
    {
        template <typename BaseT, typename DefaultT
            , typename T0, typename T1, typename T2>
        struct get_param
        {
            typedef typename mpl::if_<
                is_base_and_derived<BaseT, T0>
              , T0
              , typename mpl::if_<
                    is_base_and_derived<BaseT, T1>
                  , T1
                  , typename mpl::if_<
                        is_base_and_derived<BaseT, T2>
                      , T2
                      , DefaultT
                    >::type
                >::type
            >::type type;
        };

        template <typename T0, typename T1, typename T2>
        struct get_context
        {
            typedef typename get_param<
                parser_context_base, parser_context<>, T0, T1, T2>::type
            type;
        };

        template <typename T0, typename T1, typename T2>
        struct get_tag
        {
            typedef typename get_param<
                parser_tag_base, parser_address_tag, T0, T1, T2>::type
            type;
        };

        template <typename T0, typename T1, typename T2>
        struct get_scanner
        {
            typedef typename get_param<
                scanner_base, scanner<>, T0, T1, T2>::type
            type;
        };

        ///////////////////////////////////////////////////////////////////////
        //
        //  rule_base class
        //
        //      The rule_base class implements the basic plumbing for rules
        //      minus the storage mechanism. It is up to the derived class
        //      to actually store the definition somewhere. The rule_base
        //      class assumes that the derived class provides a get() function
        //      that will return a pointer to a parser. The get() function
        //      may return NULL. See rule below for details.
        //
        //      <<< For framework use only. Not for public consumption. >>>
        //
        ///////////////////////////////////////////////////////////////////////
        template <
            typename DerivedT       // derived class
          , typename EmbedT         // how derived class is embedded
          , typename T0 = nil_t     // see rule class
          , typename T1 = nil_t     // see rule class
          , typename T2 = nil_t     // see rule class
        >
        class rule_base; // forward declaration

        class rule_base_access
        {
#if defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) \
    || BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x551))
        public: // YUCK!
#else
            template <
                typename DerivedT
              , typename EmbedT
              , typename T0
              , typename T1
              , typename T2
            >
           friend class rule_base;
#endif
            template <typename RuleT>
            static typename RuleT::abstract_parser_t*
            get(RuleT const& r)
            {
                return r.get();
            }
        };

        template <
            typename DerivedT       // derived class
          , typename EmbedT         // how derived class is embedded
          , typename T0             // see rule class
          , typename T1             // see rule class
          , typename T2             // see rule class
        >
        class rule_base
            : public parser<DerivedT>
            , public impl::get_context<T0, T1, T2>::type::base_t
            , public context_aux<
                typename impl::get_context<T0, T1, T2>::type, DerivedT>
            , public impl::get_tag<T0, T1, T2>::type
        {
        public:

            typedef typename impl::get_scanner<T0, T1, T2>::type scanner_t;
            typedef typename impl::get_context<T0, T1, T2>::type context_t;
            typedef typename impl::get_tag<T0, T1, T2>::type tag_t;

            typedef EmbedT embed_t;
            typedef typename context_t::context_linker_t linked_context_t;
            typedef typename linked_context_t::attr_t attr_t;

            template <typename ScannerT>
            struct result
            {
                typedef typename match_result<ScannerT, attr_t>::type type;
            };

            template <typename ScannerT>
            typename parser_result<DerivedT, ScannerT>::type
            parse(ScannerT const& scan) const
            {
                typedef parser_scanner_linker<ScannerT> linked_scanner_t;
                typedef typename parser_result<DerivedT, ScannerT>::type result_t;
                BOOST_SPIRIT_CONTEXT_PARSE(
                    scan, *this, linked_scanner_t, linked_context_t, result_t);
            }

            template <typename ScannerT>
            typename parser_result<DerivedT, ScannerT>::type
            parse_main(ScannerT const& scan) const
            {
                typename parser_result<DerivedT, ScannerT>::type hit;

                //  MWCW 8.3 needs this cast to be done through a pointer,
                //  not a reference. Otherwise, it will silently construct
                //  a temporary, causing an infinite runtime recursion.
                DerivedT const* derived_this = static_cast<DerivedT const*>(this);

                if (rule_base_access::get(*derived_this))
                {
                    typename ScannerT::iterator_t s(scan.first);
                    hit = rule_base_access::get(*derived_this)
                            ->do_parse_virtual(scan);
                    scan.group_match(hit, this->id(), s, scan.first);
                }
                else
                {
                    hit = scan.no_match();
                }
                return hit;
            }
        };

        ///////////////////////////////////////////////////////////////////////
        //
        //  abstract_parser class
        //
        ///////////////////////////////////////////////////////////////////////
        template <typename ScannerT, typename AttrT>
        struct abstract_parser
        {
            abstract_parser() {}
            virtual ~abstract_parser() {}

            virtual typename match_result<ScannerT, AttrT>::type
            do_parse_virtual(ScannerT const& scan) const = 0;

            virtual abstract_parser*
            clone() const = 0;
        };

        ///////////////////////////////////////////////////////////////////////
        //
        //  concrete_parser class
        //
        ///////////////////////////////////////////////////////////////////////
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(push)
#pragma warning(disable:4512) //assignment operator could not be generated
#endif
        
        template <typename ParserT, typename ScannerT, typename AttrT>
        struct concrete_parser : abstract_parser<ScannerT, AttrT>
        {
            concrete_parser(ParserT const& p_) : p(p_) {}
            virtual ~concrete_parser() {}

            virtual typename match_result<ScannerT, AttrT>::type
            do_parse_virtual(ScannerT const& scan) const
            {
                return p.parse(scan);
            }

            virtual abstract_parser<ScannerT, AttrT>*
            clone() const
            {
                return new concrete_parser(p);
            }

            typename ParserT::embed_t p;
        };
        
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(pop)
#endif

#if BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT > 1

        ///////////////////////////////////////////////////////////////////////
        //
        //  This generates partial specializations for the class
        //
        //          abstract_parser
        //
        //  with an increasing number of different ScannerT template parameters
        //  and corresponding do_parse_virtual function declarations for each
        //  of the different required scanner types:
        //
        //      template <typename ScannerT0, ..., typename AttrT>
        //      struct abstract_parser<scanner_list<ScannerT0, ...>, AttrT>
        //      {
        //          abstract_parser() {}
        //          virtual ~abstract_parser() {}
        //
        //          virtual typename match_result<ScannerT0, AttrT>::type
        //          do_parse_virtual(ScannerT0 const &scan) const = 0;
        //
        //          virtual abstract_parser*
        //          clone() const = 0;
        //
        //          ...
        //      };
        //
        ///////////////////////////////////////////////////////////////////////
        #define BOOST_SPIRIT_RULE_ENUM_DOPARSE_A(z, N, _)                       \
                virtual typename match_result<                                  \
                    BOOST_PP_CAT(ScannerT, N), AttrT                            \
                >::type                                                         \
                do_parse_virtual(                                               \
                    BOOST_PP_CAT(ScannerT, N) const& scan) const = 0;           \

        #define BOOST_SPIRIT_ENUM_ABSTRACT_PARSERS(z, N, _)                     \
            template <                                                          \
                BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), typename ScannerT),  \
                typename AttrT                                                  \
            >                                                                   \
            struct abstract_parser<                                             \
                scanner_list<                                                   \
                    BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), ScannerT)        \
                >,                                                              \
                AttrT                                                           \
            >                                                                   \
            {                                                                   \
                abstract_parser() {}                                            \
                virtual ~abstract_parser() {}                                   \
                                                                                \
                BOOST_PP_REPEAT_ ## z(                                          \
                    BOOST_PP_INC(N), BOOST_SPIRIT_RULE_ENUM_DOPARSE_A, _)       \
                                                                                \
                virtual abstract_parser*                                        \
                clone() const = 0;                                              \
            };                                                                  \

        BOOST_PP_REPEAT_FROM_TO(1, BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT,
            BOOST_SPIRIT_ENUM_ABSTRACT_PARSERS, _)

        #undef BOOST_SPIRIT_RULE_ENUM_DOPARSE_A
        #undef BOOST_SPIRIT_ENUM_ABSTRACT_PARSERS
        ///////////////////////////////////////////////////////////////////////

        ///////////////////////////////////////////////////////////////////////
        //
        //  This generates partial specializations for the class
        //
        //          concrete_parser
        //
        //  with an increasing number of different ScannerT template parameters
        //  and corresponding do_parse_virtual function declarations for each
        //  of the different required scanner types:
        //
        //      template <
        //          typename ParserT, typename ScannerT0, ..., typename AttrT
        //      >
        //      struct concrete_parser<
        //          ParserT, scanner_list<ScannerT0, ...>, AttrT
        //      >
        //      :   public abstract_parser<scanner_list<ScannerT0, ...>, AttrT>
        //      {
        //          concrete_parser(ParserT const& p_) : p(p_) {}
        //          virtual ~concrete_parser() {}
        //
        //          virtual typename match_result<ScannerT0, AttrT>::type
        //          do_parse_virtual(ScannerT0 const &scan) const
        //          { return p.parse(scan); }
        //
        //          virtual abstract_parser<scanner_list<ScannerT0, ...>, AttrT>*
        //          clone() const
        //          {
        //              return new concrete_parser(p);
        //          }
        //
        //          ...
        //
        //          typename ParserT::embed_t p;
        //      };
        //
        ///////////////////////////////////////////////////////////////////////
        #define BOOST_SPIRIT_RULE_ENUM_DOPARSE_C(z, N, _)                       \
                virtual typename match_result<                                  \
                    BOOST_PP_CAT(ScannerT, N), AttrT                            \
                >::type                                                         \
                do_parse_virtual(                                               \
                    BOOST_PP_CAT(ScannerT, N) const& scan) const                \
                { return p.parse(scan); }                                       \

        #define BOOST_SPIRIT_ENUM_CONCRETE_PARSERS(z, N, _)                     \
            template <                                                          \
                typename ParserT,                                               \
                BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), typename ScannerT),  \
                typename AttrT                                                  \
            >                                                                   \
            struct concrete_parser<                                             \
                ParserT,                                                        \
                scanner_list<                                                   \
                    BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), ScannerT)        \
                >,                                                              \
                AttrT                                                           \
            >                                                                   \
            :   abstract_parser<                                                \
                    scanner_list<                                               \
                        BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), ScannerT)    \
                    >,                                                          \
                    AttrT                                                       \
                >                                                               \
            {                                                                   \
                concrete_parser(ParserT const& p_) : p(p_) {}                   \
                virtual ~concrete_parser() {}                                   \
                                                                                \
                BOOST_PP_REPEAT_ ## z(                                          \
                    BOOST_PP_INC(N), BOOST_SPIRIT_RULE_ENUM_DOPARSE_C, _)       \
                                                                                \
                virtual abstract_parser<                                        \
                    scanner_list<                                               \
                        BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), ScannerT)    \
                    >,                                                          \
                    AttrT                                                       \
                >*                                                              \
                clone() const                                                   \
                {                                                               \
                    return new concrete_parser(p);                              \
                }                                                               \
                                                                                \
                typename ParserT::embed_t p;                                    \
            };                                                                  \

        BOOST_PP_REPEAT_FROM_TO(1, BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT,
            BOOST_SPIRIT_ENUM_CONCRETE_PARSERS, _)

        #undef BOOST_SPIRIT_ENUM_CONCRETE_PARSERS
        #undef BOOST_SPIRIT_RULE_ENUM_DOPARSE_C
        ///////////////////////////////////////////////////////////////////////

#endif // BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT > 1

    } // namespace impl

BOOST_SPIRIT_CLASSIC_NAMESPACE_END

}} // namespace boost::spirit

#endif
