/*=============================================================================

    Copyright (c) 2002-2003 Joel de Guzman

    Copyright (c) 2002-2003 Hartmut Kaiser

    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_SUBRULE_HPP)

#define BOOST_SPIRIT_SUBRULE_HPP



#include <boost/config.hpp>

#include <boost/static_assert.hpp>



#include <boost/spirit/core/parser.hpp>

#include <boost/spirit/core/non_terminal/parser_context.hpp>



#include <boost/spirit/core/non_terminal/subrule_fwd.hpp>

#include <boost/spirit/core/non_terminal/impl/subrule.ipp>



namespace boost { namespace spirit {



    ///////////////////////////////////////////////////////////////////////////

    //

    //  subrules_scanner class

    //

    ///////////////////////////////////////////////////////////////////////////

    template <typename ScannerT, typename ListT>

    struct subrules_scanner : public ScannerT

    {

        typedef ScannerT                            scanner_t;

        typedef ListT                               list_t;

        typedef subrules_scanner<ScannerT, ListT>   self_t;



        subrules_scanner(ScannerT const& scan, ListT const& list_)

        : ScannerT(scan), list(list_) {}



        template <typename PoliciesT>

        struct rebind_policies

        {

            typedef typename rebind_scanner_policies<ScannerT, PoliciesT>::type

                rebind_scanner;

            typedef subrules_scanner<rebind_scanner, ListT> type;

        };



        template <typename PoliciesT>

        subrules_scanner<

            typename rebind_scanner_policies<ScannerT, PoliciesT>::type,

            ListT>

        change_policies(PoliciesT const& policies) const

        {

            typedef subrules_scanner<

                BOOST_DEDUCED_TYPENAME

                    rebind_scanner_policies<ScannerT, PoliciesT>::type,

                ListT>

            subrules_scanner_t;



            return subrules_scanner_t(

                    ScannerT::change_policies(policies),

                    list);

        }



        template <typename IteratorT>

        struct rebind_iterator

        {

            typedef typename rebind_scanner_iterator<ScannerT, IteratorT>::type

                rebind_scanner;

            typedef subrules_scanner<rebind_scanner, ListT> type;

        };



        template <typename IteratorT>

        subrules_scanner<

            typename rebind_scanner_iterator<ScannerT, IteratorT>::type,

            ListT>

        change_iterator(IteratorT const& first, IteratorT const &last) const

        {

            typedef subrules_scanner<

                BOOST_DEDUCED_TYPENAME

                    rebind_scanner_iterator<ScannerT, IteratorT>::type,

                ListT>

            subrules_scanner_t;



            return subrules_scanner_t(

                    ScannerT::change_iterator(first, last),

                    list);

        }



        ListT const& list;

    };



    ///////////////////////////////////////////////////////////////////////////

    //

    //  subrule_scanner type computer class

    //

    //      This computer ensures that the scanner will not be recursively

    //      instantiated if it's not needed.

    //

    ///////////////////////////////////////////////////////////////////////////

    template <typename ScannerT, typename ListT>

    struct subrules_scanner_finder

    {

          typedef subrules_scanner<ScannerT, ListT> type;

    };



    template <typename ScannerT, typename ListT>

    struct subrules_scanner_finder<subrules_scanner<ScannerT, ListT>, ListT>

    {

          typedef subrules_scanner<ScannerT, ListT> type;

    };



    ///////////////////////////////////////////////////////////////////////////

    //

    //  subrule_list class

    //

    ///////////////////////////////////////////////////////////////////////////

    template <typename FirstT, typename RestT>

    struct subrule_list : public parser<subrule_list<FirstT, RestT> >

    {

        typedef subrule_list<FirstT, RestT> self_t;

        typedef FirstT                      first_t;

        typedef RestT                       rest_t;



        subrule_list(FirstT const& first_, RestT const& rest_)

        : first(first_), rest(rest_) {}



        template <typename ScannerT>

        struct result

        {

            typedef typename parser_result<FirstT, ScannerT>::type type;

        };



        template <typename ScannerT>

        typename parser_result<self_t, ScannerT>::type

        parse(ScannerT const& scan) const

        {

            typedef typename subrules_scanner_finder<ScannerT, self_t>::type

            subrules_scanner_t;

            subrules_scanner_t g_arg(scan, *this);

            return first.start.parse(g_arg);

        }



        template <int ID, typename DefT, typename ContextT>

        subrule_list<

            FirstT,

            subrule_list<

                subrule_parser<ID, DefT, ContextT>,

                RestT> >

        operator,(subrule_parser<ID, DefT, ContextT> const& rhs)

        {

            return subrule_list<

                FirstT,

                subrule_list<

                    subrule_parser<ID, DefT, ContextT>,

                    RestT> >(

                        first,

                        subrule_list<

                            subrule_parser<ID, DefT, ContextT>,

                            RestT>(rhs, rest));

        }



        FirstT first;

        RestT rest;

    };



    ///////////////////////////////////////////////////////////////////////////

    //

    //  subrule_parser class

    //

    ///////////////////////////////////////////////////////////////////////////

    template <int ID, typename DefT, typename ContextT>

    struct subrule_parser

    : public parser<subrule_parser<ID, DefT, ContextT> >

    {

        typedef subrule_parser<ID, DefT, ContextT> self_t;

        typedef subrule<ID, ContextT> subrule_t;

        typedef DefT def_t;



        BOOST_STATIC_CONSTANT(int, id = ID);



        template <typename ScannerT>

        struct result

        {

            typedef typename

                impl::get_subrule_parser_result<

                    DefT, ScannerT, typename subrule_t::attr_t>::type type;

        };



        subrule_parser(subrule_t const& start_, DefT const& rhs_)

        : rhs(rhs_), start(start_) {}



        template <typename ScannerT>

        typename parser_result<self_t, ScannerT>::type

        parse(ScannerT const& scan) const

        {

            // This will only be called when parsing single subrules.

            typedef subrule_list<self_t, nil_t> list_t;

            typedef subrules_scanner<ScannerT, list_t> scanner_t;



            list_t    list(*this, nil_t());

            scanner_t g_arg(scan, list);

            return start.parse(g_arg);

        }



        template <int ID2, typename DefT2, typename ContextT2>

        inline subrule_list<

            self_t,

            subrule_list<

                subrule_parser<ID2, DefT2, ContextT2>,

                nil_t> >

        operator,(subrule_parser<ID2, DefT2, ContextT2> const& rhs) const

        {

            return subrule_list<

                self_t,

                subrule_list<

                    subrule_parser<ID2, DefT2, ContextT2>,

                    nil_t> >(

                        *this,

                        subrule_list<

                            subrule_parser<ID2, DefT2, ContextT2>, nil_t>(

                                rhs, nil_t()));

        }



        typename DefT::embed_t rhs;

        subrule_t const& start;

    };



    ///////////////////////////////////////////////////////////////////////////

    //

    //  subrule class

    //

    ///////////////////////////////////////////////////////////////////////////

    template <int ID, typename ContextT>

    struct subrule

        : public parser<subrule<ID, ContextT> >

        , public ContextT::base_t

        , public context_aux<ContextT, subrule<ID, ContextT> >

    {

        typedef subrule<ID, ContextT> self_t;

        typedef subrule<ID, ContextT> const&  embed_t;



        typedef typename ContextT::context_linker_t context_t;

        typedef typename context_t::attr_t attr_t;



        BOOST_STATIC_CONSTANT(int, id = ID);



        template <typename ScannerT>

        struct result

        {

            typedef typename

                impl::get_subrule_result<ID, ScannerT, attr_t>::type type;

        };



        template <typename ScannerT>

        typename parser_result<self_t, ScannerT>::type

        parse_main(ScannerT const& scan) const

        {

            typedef typename parser_result<self_t, ScannerT>::type result_t;

            result_t result;

            impl::parse_subrule<result_t, ScannerT, ID>::

                do_(result, scan);

            return result;

        }



        template <typename ScannerT>

        typename parser_result<self_t, ScannerT>::type

        parse(ScannerT const& scan) const

        {

            typedef typename parser_result<self_t, ScannerT>::type result_t;

            typedef parser_scanner_linker<ScannerT> scanner_t;

            BOOST_SPIRIT_CONTEXT_PARSE(

                scan, *this, scanner_t, context_t, result_t);

        }



        template <typename DefT>

        subrule_parser<ID, DefT, ContextT>

        operator=(parser<DefT> const& rhs) const

        {

            return subrule_parser<ID, DefT, ContextT>(*this, rhs.derived());

        }



    private:



        //  assignment of subrules is not allowed. Use subrules

        //  with identical IDs if you want to have aliases.



        subrule& operator=(subrule const&);



        template <int ID2, typename ContextT2>

        subrule& operator=(subrule<ID2, ContextT2> const&);

    };



}} // namespace boost::spirit



#endif



