/*=============================================================================
    Copyright (c) 2001-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)
=============================================================================*/
#include <iostream>


#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_closure.hpp>
#include <boost/spirit/include/classic_parametric.hpp>
#include <boost/spirit/include/phoenix1_binders.hpp>

#include <boost/core/lightweight_test.hpp>

using namespace BOOST_SPIRIT_CLASSIC_NS;
using namespace phoenix;

///////////////////////////////////////////////////////////////////////////////
//
//  Closure tests
//
///////////////////////////////////////////////////////////////////////////////
struct my_closure1 : BOOST_SPIRIT_CLASSIC_NS::closure<my_closure1, double>
{
    member1 val;
};

struct my_closure2 : BOOST_SPIRIT_CLASSIC_NS::closure<my_closure2, char>
{
    member1 ch;
};

struct my_closure3 : BOOST_SPIRIT_CLASSIC_NS::closure<my_closure3, char>
{
    member1 ch;
};

struct X { int a; int b; };

#if defined(BOOST_SPIRIT_DEBUG)
//  If debugging is switched on, all closure members should have a 
//  corresponding output streaming operator
std::ostream &
operator<< (std::ostream& o, X const &x)
{
    o << "X(" << x.a << ", " << x.b << ")";
    return o;
}
#endif // defined(BOOST_SPIRIT_DEBUG)

struct my_closure4 : BOOST_SPIRIT_CLASSIC_NS::closure<my_closure4, X>
{
    member1 x;
};

// MWCW8.3 needs the default constructor here or it won't compile.
// It should not be needed.
struct Y { Y() {} Y(int) {} };

#if defined(BOOST_SPIRIT_DEBUG)
//  If debugging is switched on, all closure members should have a 
//  corresponding output streaming operator
std::ostream &
operator<< (std::ostream& o, Y const &/*x*/)
{
    o << "Y";
    return o;
}
#endif // defined(BOOST_SPIRIT_DEBUG)

struct my_closure5 : BOOST_SPIRIT_CLASSIC_NS::closure<my_closure5, int, Y>
{
    member1 y;
};

struct my_closure6 : BOOST_SPIRIT_CLASSIC_NS::closure<my_closure6, int, int, int>
{
    member1 x;
    member2 y;
    member3 z;
};

void
closure_tests()
{
    rule<phrase_scanner_t, my_closure1::context_t> num_list;
    double n;

    num_list =
    (
        real_p[num_list.val = arg1] >>  *(',' >> real_p[num_list.val += arg1])
    )
    [var(n) = num_list.val];

    parse_info<char const*> pi;
    pi = parse("123, 456, 789", num_list, space_p);
    BOOST_TEST(pi.hit);
    BOOST_TEST(pi.full);
    BOOST_TEST(n == 123 + 456 + 789);

    rule<scanner<>, my_closure2::context_t> rev;
    rev = anychar_p[rev.ch = arg1] >> !rev >> f_ch_p(rev.ch);

    pi = parse("xyzzyx", rev);
    BOOST_TEST(pi.hit);
    BOOST_TEST(pi.full);

    pi = parse("xyzczyx", rev);
    BOOST_TEST(!pi.hit);

    subrule<0, my_closure3::context_t> rev2;
    pi = parse("atoyyota",
        rev2 = anychar_p[rev2.ch = arg1] >> !rev2 >> f_ch_p(rev2.ch)
    );

    BOOST_TEST(pi.hit);
    BOOST_TEST(pi.full);

    pi = parse("whatdahell",
        rev2 = anychar_p[rev2.ch = arg1] >> !rev2 >> f_ch_p(rev2.ch)
    );
    BOOST_TEST(!pi.hit);

    rule<phrase_scanner_t, my_closure4::context_t> complex_p;
    complex_p =
            int_p[bind(&X::a)(complex_p.x) = arg1]
        >>  ','
        >>  int_p[bind(&X::b)(complex_p.x) = arg1]
    ;

    X x;
    pi = parse("123, 456", complex_p[var(x) = arg1], space_p);
    BOOST_TEST(pi.hit);
    BOOST_TEST(x.a == 123);
    BOOST_TEST(x.b == 456);

    rule<scanner<>, my_closure5::context_t> init1; // compile check only
    rule<> r1 = init1(3, 3); // member2 is constructed from int

    rule<scanner<>, my_closure6::context_t> init2; // compile check only
    rule<> r2 = init2(3); // member2 and member3 are default constructed
}

///////////////////////////////////////////////////////////////////////////////
//
//  Main
//
///////////////////////////////////////////////////////////////////////////////
int
main()
{
    closure_tests();
    return boost::report_errors();
}

