/*=============================================================================
    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)
=============================================================================*/
#include <iostream>
#include <cassert>

using namespace std;

//#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT 3

#include <boost/spirit/core.hpp>
#include <boost/spirit/dynamic/stored_rule.hpp>
using namespace boost::spirit;

///////////////////////////////////////////////////////////////////////////////
//
//  Rule tests
//
///////////////////////////////////////////////////////////////////////////////
void
aliasing_tests()
{
    rule<>  a = ch_p('a');
    rule<>  b = ch_p('b');
    rule<>  c = ch_p('c');

    cout << "sizeof(rule<>): " << sizeof(rule<>) << endl;

    BOOST_SPIRIT_DEBUG_RULE(a);
    BOOST_SPIRIT_DEBUG_RULE(b);
    BOOST_SPIRIT_DEBUG_RULE(c);

    rule<>  start;
    BOOST_SPIRIT_DEBUG_RULE(start);

    rule<>  d;
    d = start;  // aliasing

    parse_info<char const*> pi;

    start = *(a | b | c);
    pi = parse("abcabcacb", d);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 9);
    assert(*pi.stop == 0);

    start   = (a | b) >> (start | b);
    pi = parse("aaaabababaaabbb", d);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 15);
    assert(*pi.stop == 0);
}

void
rule_template_param_tests()
{
    // test that rules can be issued its template params in any order:

    rule<> rx1;
    rule<scanner<> > rx2;
    rule<scanner<>, parser_context<> > rx3;
    rule<scanner<>, parser_context<>, parser_address_tag> rx4;

    rule<parser_context<> > rx5;
    rule<parser_context<>, parser_address_tag> rx6;
    rule<parser_context<>, parser_address_tag, scanner<> > rx7;

    rule<parser_address_tag> rx8;
    rule<parser_address_tag, scanner<> > rx9;
    rule<parser_address_tag, scanner<>, parser_context<> > rx10;

    rule<parser_address_tag, parser_context<> > rx11;
    rule<parser_address_tag, parser_context<>, scanner<> > rx12;

    rule<parser_context<>, scanner<> > rx13;
    rule<parser_context<>, scanner<>, parser_address_tag> rx14;
}

struct my_grammar : public grammar<my_grammar>
{
    template <typename ScannerT>
    struct definition
    {
        definition(my_grammar const& /*self*/)
        {
            r = lower_p;
            rr = +(lexeme_d[r] >> as_lower_d[r] >> r);
        }

        typedef scanner_list<
            ScannerT
          , typename lexeme_scanner<ScannerT>::type
          , typename as_lower_scanner<ScannerT>::type
        > scanners;

        rule<scanners> r;
        rule<ScannerT> rr;
        rule<ScannerT> const& start() const { return rr; }
    };
};

void
rule_2_or_more_scanners_tests()
{
    { // 2 scanners
        typedef scanner_list<scanner<>, phrase_scanner_t> scanners;

        rule<scanners>  r = +anychar_p;
        assert(parse("abcdefghijk", r).full);
        assert(parse("a b c d e f g h i j k", r, space_p).full);
    }

    { // 3 scanners
        my_grammar g;
        assert(parse("abcdef aBc d e f aBc d E f", g, space_p).full);
    }
}

void
rule_basic_tests()
{
    rule<>  a = ch_p('a');
    rule<>  b = ch_p('b');
    rule<>  c = ch_p('c');

    BOOST_SPIRIT_DEBUG_RULE(a);
    BOOST_SPIRIT_DEBUG_RULE(b);
    BOOST_SPIRIT_DEBUG_RULE(c);

    parse_info<char const*> pi;

    rule<>  start = *(a | b | c);

    BOOST_SPIRIT_DEBUG_RULE(start);

    pi = parse("abcabcacb", start);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 9);
    assert(*pi.stop == 0);

    start   = (a | b) >> (start | b);
    pi = parse("aaaabababaaabbb", start);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 15);
    assert(*pi.stop == 0);

    pi = parse("aaaabababaaabba", start);
    assert(pi.hit);
    assert(!pi.full);
    assert(pi.length == 14);
}

void
stored_rule_basic_tests()
{
    stored_rule<>  a = ch_p('a');
    stored_rule<>  b = ch_p('b');
    stored_rule<>  c = ch_p('c');

    BOOST_SPIRIT_DEBUG_RULE(a);
    BOOST_SPIRIT_DEBUG_RULE(b);
    BOOST_SPIRIT_DEBUG_RULE(c);

    parse_info<char const*> pi;

    stored_rule<>  start = *(a | b | c);

    BOOST_SPIRIT_DEBUG_RULE(start);

    pi = parse("abcabcacb", start);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 9);
    assert(*pi.stop == 0);

    start   = (a | b) >> (start | b);
    pi = parse("aaaabababaaabbb", start);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 15);
    assert(*pi.stop == 0);

    pi = parse("aaaabababaaabba", start);
    assert(pi.hit);
    assert(!pi.full);
    assert(pi.length == 14);
}

void
stored_rule_dynamic_tests()
{
    rule<>  a = ch_p('a');
    rule<>  b = ch_p('b');
    rule<>  c = ch_p('c');

    BOOST_SPIRIT_DEBUG_RULE(a);
    BOOST_SPIRIT_DEBUG_RULE(b);
    BOOST_SPIRIT_DEBUG_RULE(c);

    parse_info<char const*> pi;

    // The FF is the dynamic equivalent of start = *(a | b | c);
    stored_rule<>  start = a;
    start = start.copy() | b;
    start = start.copy() | c;
    start = *(start.copy());

    cout << "sizeof(stored_rule<>): " << sizeof(stored_rule<>) << endl;

    BOOST_SPIRIT_DEBUG_RULE(start);

    pi = parse("abcabcacb", start);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 9);
    assert(*pi.stop == 0);

    // The FF is the dynamic equivalent of start = (a | b) >> (start | b);
    start = b;
    start = a | start.copy();
    start = start.copy() >> (start | b);

    pi = parse("aaaabababaaabbb", start);
    assert(pi.hit);
    assert(pi.full);
    assert(pi.length == 15);
    assert(*pi.stop == 0);

    pi = parse("aaaabababaaabba", start);
    assert(pi.hit);
    assert(!pi.full);
    assert(pi.length == 14);
}

///////////////////////////////////////////////////////////////////////////////
//
//  Main
//
///////////////////////////////////////////////////////////////////////////////
int
main()
{
    rule_basic_tests();
    aliasing_tests();
    rule_template_param_tests();
    rule_2_or_more_scanners_tests();
    stored_rule_basic_tests();
    stored_rule_dynamic_tests();

    cout << "Tests concluded successfully\n";
    return 0;
}

