/*=============================================================================
    Spirit v1.6.1
    Copyright (c) 2001-2003 Hartmut Kaiser
    http://spirit.sourceforge.net/

    Permission to copy, use, modify, sell and distribute this software is
    granted provided this copyright notice appears in all copies. This
    software is provided "as is" without express or implied warranty, and
    with no claim as to its suitability for any purpose.
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  This example shows:
//  1.  Parsing of different comment styles
//          parsing C/C++-style comment
//          parsing C++-style comment
//          parsing PASCAL-style comment
//  2.  Parsing tagged data with the help of the confix_parser
//  3.  Parsing tagged data with the help of the confix_parser but the semantic
//      action is directly attached to the body sequence parser
//
///////////////////////////////////////////////////////////////////////////////

#include <string>
#include <iostream>
#include <cassert>

#include <boost/spirit/core.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/utility/chset.hpp>


///////////////////////////////////////////////////////////////////////////////
// used namespaces
using namespace std;
using namespace boost::spirit;

///////////////////////////////////////////////////////////////////////////////
// actor called after successfully matching a single character
class actor_string
{
public:
    actor_string(std::string &rstr) :
        matched(rstr)
    {
    }

    void operator() (const char *pbegin, const char *pend) const
    {
        matched += std::string(pbegin, pend-pbegin);
    }

private:
    std::string &matched;
};

///////////////////////////////////////////////////////////////////////////////
// actor called after successfully matching a C++-comment
void actor_cpp (const char *pfirst, const char *plast)
{
    cout << "Parsing C++-comment" <<endl;
    cout << "Matched (" << plast-pfirst << ") characters: ";

    char cbbuffer[128];

    strncpy(cbbuffer, pfirst, plast-pfirst);
    cbbuffer[plast-pfirst] = '\0';

    cout << "\"" << cbbuffer << "\"" << endl;
}

///////////////////////////////////////////////////////////////////////////////
// main entry point
int main ()
{

///////////////////////////////////////////////////////////////////////////////
//
//  1.  Parsing different comment styles
//      parsing C/C++-style comments (non-nested!)
//
///////////////////////////////////////////////////////////////////////////////

    char const* pCComment = "/* This is a /* nested */ C-comment */";

    rule<> cpp_comment;

    cpp_comment =
            comment_p("/*", "*/")           // rule for C-comments
        |   comment_p("//")                 // rule for C++ comments
        ;

    std::string comment_c;
    parse_info<> result;

    result = parse (pCComment, cpp_comment[actor_string(comment_c)]);
    if (result.hit)
    {
        cout << "Parsed C-comment successfully!" << endl;
        cout << "Matched (" << (int)comment_c.size() << ") characters: ";
        cout << "\"" << comment_c << "\"" << endl;
    }
    else
    {
        cout << "Failed to parse C/C++-comment!" << endl;
    }
    cout << endl;

    //        parsing C++-style comment
    char const* pCPPComment = "// This is a C++-comment\n";
    std::string comment_cpp;

    result = parse (pCPPComment, cpp_comment[&actor_cpp]);
    if (result.hit)
        cout << "Parsed C++-comment successfully!" << endl;
    else
        cout << "Failed to parse C++-comment!" << endl;

    cout << endl;


    //        parsing PASCAL-style comment (nested!)
    char const* pPComment = "{ This is a (* nested *) PASCAL-comment }";

    rule<> pascal_comment;

    pascal_comment =                    // in PASCAL we have two comment styles
            comment_nest_p('{', '}')    // both may be nested
        |   comment_nest_p("(*", "*)")
        ;

    std::string comment_pascal;

    result = parse (pPComment, pascal_comment[actor_string(comment_pascal)]);
    if (result.hit)
    {
        cout << "Parsed PASCAL-comment successfully!" << endl;
        cout << "Matched (" << (int)comment_pascal.size() << ") characters: ";
        cout << "\"" << comment_pascal << "\"" << endl;
    }
    else
    {
        cout << "Failed to parse PASCAL-comment!" << endl;
    }
    cout << endl;

///////////////////////////////////////////////////////////////////////////////
//
//  2.  Parsing tagged data with the help of the confix parser
//
///////////////////////////////////////////////////////////////////////////////

    std::string body;
    rule<> open_tag, html_tag, close_tag, body_text;

    open_tag =
            str_p("<b>")
        ;

    body_text =
            anychar_p
        ;

    close_tag =
            str_p("</b>")
        ;

    html_tag =
            confix_p (open_tag, (*body_text)[actor_string(body)], close_tag)
        ;

    char const* pTag = "<b>Body text</b>";

    result = parse (pTag, html_tag);
    if (result.hit)
    {
        cout << "Parsed HTML snippet \"<b>Body text</b>\" successfully "
            "(with re-attached actor)!" << endl;
        cout << "Found body (" << (int)body.size() << " characters): ";
        cout << "\"" << body << "\"" << endl;
    }
    else
    {
        cout << "Failed to parse HTML snippet (with re-attached actor)!"
            << endl;
    }
    cout << endl;

///////////////////////////////////////////////////////////////////////////////
//
//  3.  Parsing tagged data with the help of the confix_parser but the
//      semantic action is directly attached to the body sequence parser
//      (see comment in confix.hpp) and out of the usage of the 'direct()'
//      construction function no automatic refactoring takes place.
//
//      As you can see, for successful parsing it is required to refactor the
//      confix parser by hand. To see, how it fails, you can try the following:
//
//          html_tag_direct =
//              confix_p.direct(
//                  str_p("<b>"),
//                  (*body_text)[actor_string(bodydirect)],
//                  str_p("</b>")
//              )
//              ;
//
//      Here the *body_text parser eats up all the input up to the end of the
//      input sequence.
//
///////////////////////////////////////////////////////////////////////////////

    rule<> html_tag_direct;
    std::string bodydirect;

    html_tag_direct =
            confix_p.direct(
                str_p("<b>"),
                (*(body_text - str_p("</b>")))[actor_string(bodydirect)],
                str_p("</b>")
            )
        ;

    char const* pTagDirect = "<b>Body text</b>";

    result = parse (pTagDirect, html_tag_direct);
    if (result.hit)
    {
        cout << "Parsed HTML snippet \"<b>Body text</b>\" successfully "
            "(with direct actor)!" << endl;
        cout << "Found body (" << (int)bodydirect.size() << " characters): ";
        cout << "\"" << bodydirect << "\"" << endl;
    }
    else
    {
        cout << "Failed to parse HTML snippet (with direct actor)!" << endl;
    }
    cout << endl;

    return 0;
}
