/*=============================================================================
    Spirit v1.6.0
    Copyright (c) 2002-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.
=============================================================================*/
#ifndef BOOST_SPIRIT_CONFIX_HPP
#define BOOST_SPIRIT_CONFIX_HPP

///////////////////////////////////////////////////////////////////////////////
#include "boost/type_traits.hpp"

#include "boost/spirit/core/meta/impl/parser_type.hpp"
#include "boost/spirit/core/composite/operators.hpp"
#include "boost/spirit/utility/impl/confix.ipp"

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

///////////////////////////////////////////////////////////////////////////////
//
//  confix_parser class
//
//      Parses a (possibly nested) sequence of 3 sub-matches. This class may
//      be used to parse structures, where the opening part is possibly
//      contained in the expression part and the whole sequence is only
//      parsed after seeing the closing part matching the first opening
//      subsequence. Example: nested PASCAL-comments:
//
//      { This is a { nested } PASCAL-comment }
//
///////////////////////////////////////////////////////////////////////////////

template<typename NestedT = non_nested, typename LexemeT = non_lexeme>
struct confix_parser_gen;

template <
    typename OpenT, typename ExprT, typename CloseT, typename CategoryT,
    typename NestedT = non_nested, typename LexemeT = non_lexeme
>
struct confix_parser :
    public parser<
        confix_parser<OpenT, ExprT, CloseT, CategoryT, NestedT, LexemeT>
    >
{
    typedef
        confix_parser<OpenT, ExprT, CloseT, CategoryT, NestedT, LexemeT>
        self_t;

    confix_parser(OpenT const &open_, ExprT const &expr_, CloseT const &close_)
    : open(open_), expr(expr_), close(close_)
    {}

    template <typename ScannerT>
    typename parser_result<self_t, ScannerT>::type
    parse(ScannerT const& scan) const
    {
        return impl::confix_parser_type<CategoryT>::
            parse(NestedT(), LexemeT(), *this, scan, open, expr, close);
    }

private:

    typename as_parser<OpenT>::type::embed_t open;
    typename as_parser<ExprT>::type::embed_t expr;
    typename as_parser<CloseT>::type::embed_t close;
};

///////////////////////////////////////////////////////////////////////////////
//
//  Confix parser generator template
//
//      This is a helper for generating a correct confix_parser<> from
//      auxiliary parameters. There are the following types supported as
//      parameters yet: parsers, single characters and strings (see
//      as_parser).
//
//      If the body parser is an action_parser_category type parser (a parser
//      with an attached semantic action) we have to do something special. This
//      happens, if the user wrote something like:
//
//          confix_p(open, body[f], close)
//
//      where 'body' is the parser matching the body of the confix sequence
//      and 'f' is a functor to be called after matching the body. If we would
//      do nothing, the resulting code would parse the sequence as follows:
//
//          start >> (body[f] - close) >> close
//
//      what in most cases is not what the user expects.
//      (If this _is_ what you've expected, then please use the confix_p or
//      confix_nest_p generator function 'direct()', which will inhibit
//      re-attaching the actor to the body parser).
//
//      To make the confix parser behave as expected:
//
//          start >> (body - close)[f] >> close
//
//      the actor attached to the 'body' parser has to be re-attached to the
//      (body - close) parser construct, which will make the resulting confix
//      parser 'do the right thing'. This refactoring is done by the help of
//      the refactoring parsers (see the files refactoring.[hi]pp).
//
//      Additionally special care must be taken, if the body parser is a
//      unary_parser_category type parser as
//
//          confix_p(open, *anychar_p, close)
//
//      which without any refactoring would result in
//
//          start >> (*anychar_p - close) >> close
//
//      and will not give the expected result (*anychar_p will eat up all the
//      input up to the end of the input stream). So we have to refactor this
//      into:
//
//          start >> *(anychar_p - close) >> close
//
//      what will give the correct result.
//
//      The case, where the body parser is a combination of the two mentioned
//      problems (i.e. the body parser is a unary parser  with an attached
//      action), is handled accordingly too:
//
//          confix_p(start, (*anychar_p)[f], end)
//
//      will be parsed as expected:
//
//          start >> (*(anychar_p - end))[f] >> end.
//
///////////////////////////////////////////////////////////////////////////////

template<typename NestedT, typename LexemeT>
struct confix_parser_gen
{
    // Generic generator function for creation of concrete confix parsers

    template<typename StartT, typename ExprT, typename EndT>
    confix_parser<
        typename as_parser<StartT>::type,
        typename as_parser<ExprT>::type,
        typename as_parser<EndT>::type,
        typename as_parser<ExprT>::type::parser_category_t,
        NestedT,
        LexemeT
    >
    operator()(
        StartT const &start_, ExprT const &expr_, EndT const &end_) const
    {
        typedef typename as_parser<StartT>::type start_t;
        typedef typename as_parser<ExprT>::type expr_t;
        typedef typename as_parser<EndT>::type end_t;
        typedef
            typename as_parser<ExprT>::type::parser_category_t
            parser_category_t;

        typedef
            confix_parser<
                start_t, expr_t, end_t, parser_category_t, NestedT, LexemeT
            >
            return_t;

        return return_t(
            as_parser<StartT>::convert(start_),
            as_parser<ExprT>::convert(expr_),
            as_parser<EndT>::convert(end_)
        );
    }

    // Generic generator function for creation of concrete confix parsers
    // which have an action directly attached to the ExprT part of the
    // parser (see comment above, no automatic refactoring)

    template<typename StartT, typename ExprT, typename EndT>
    confix_parser<
        typename as_parser<StartT>::type,
        typename as_parser<ExprT>::type,
        typename as_parser<EndT>::type,
        plain_parser_category,   // do not re-attach action
        NestedT,
        LexemeT
    >
    direct(StartT const &start_, ExprT const &expr_, EndT const &end_) const
    {
        typedef typename as_parser<StartT>::type start_t;
        typedef typename as_parser<ExprT>::type expr_t;
        typedef typename as_parser<EndT>::type end_t;
        typedef plain_parser_category parser_category_t;

        typedef
            confix_parser<
                start_t, expr_t, end_t, parser_category_t, NestedT, LexemeT
            >
            return_t;

        return return_t(
            as_parser<StartT>::convert(start_),
            as_parser<ExprT>::convert(expr_),
            as_parser<EndT>::convert(end_)
        );
    }
};

///////////////////////////////////////////////////////////////////////////////
//
//  Predefined non_nested and nested confix parser generators
//
///////////////////////////////////////////////////////////////////////////////

const confix_parser_gen<non_nested, non_lexeme> confix_p =
    confix_parser_gen<non_nested, non_lexeme>();
const confix_parser_gen<is_nested, non_lexeme> confix_nest_p =
    confix_parser_gen<is_nested, non_lexeme>();

///////////////////////////////////////////////////////////////////////////////
//
//  Comments are special types of confix parsers
//
//      Comment parser generator template. This is a helper for generating a
//      correct confix_parser<> from auxiliary parameters, which is able to
//      parse comment constructs: (StartToken >> Comment text >> EndToken).
//
//      There are the following types supported as parameters yet: parsers,
//      single characters and strings (see as_parser).
//
//      There are two diffenerent predefined comment parser generators
//      (comment_p and comment_nest_p, see below), which may be used for
//      creating special comment parsers in two different ways.
//
//      If these are used with one parameter, a comment starting with the given
//      first parser parameter up to the end of the line is matched. So for
//      instance the following parser matches C++ style comments:
//
//          comment_p("//").
//
//      If these are used with two parameters, a comment starting with the
//      first parser parameter up to the second parser parameter is matched.
//      For instance a C style comment parser should be constrcuted as:
//
//          comment_p("/*", "*/").
//
//      Please note, that a comment is parsed implicitly as if the whole
//      comment_p(...) statement were embedded into a lexeme_d[] directive.
//
///////////////////////////////////////////////////////////////////////////////

template<typename NestedT>
struct comment_parser_gen
{
    // Generic generator function for creation of concrete comment parsers
    // from an open token. The newline parser eol_p is used as the
    // closing token.

    template<typename StartT>
    confix_parser<
        typename as_parser<StartT>::type,
        kleene_star<anychar_parser>,
        eol_parser,
        unary_parser_category,          // there is no action to re-attach
        NestedT,
        is_lexeme                       // insert implicit lexeme_d[]
    >
    operator() (StartT const &start_) const
    {
        typedef typename as_parser<StartT>::type start_t;
        typedef kleene_star<anychar_parser> expr_t;
        typedef unary_parser_category parser_category_t;

        typedef
            confix_parser<
                start_t, expr_t, eol_parser, parser_category_t, NestedT, is_lexeme
            >
            return_t;

        return return_t(
            as_parser<StartT>::convert(start_),
            *anychar_p,
            eol_p
        );
    }

    // Generic generator function for creation of concrete comment parsers
    // from an open and a close tokens.

    template<typename StartT, typename EndT>
    confix_parser<
        typename as_parser<StartT>::type,
        kleene_star<anychar_parser>,
        typename as_parser<EndT>::type,
        unary_parser_category,          // there is no action to re-attach
        NestedT,
        is_lexeme                       // insert implicit lexeme_d[]
    >
    operator() (StartT const &start_, EndT const &end_) const
    {
        typedef typename as_parser<StartT>::type start_t;
        typedef kleene_star<anychar_parser> expr_t;
        typedef typename as_parser<EndT>::type end_t;
        typedef unary_parser_category parser_category_t;

        typedef
            confix_parser<
                start_t, expr_t, end_t, parser_category_t, NestedT, is_lexeme
            >
            return_t;

        return return_t(
            as_parser<StartT>::convert(start_),
            *anychar_p,
            as_parser<EndT>::convert(end_)
        );
    }
};

///////////////////////////////////////////////////////////////////////////////
//
//  Predefined non_nested and nested comment parser generator
//
///////////////////////////////////////////////////////////////////////////////

const comment_parser_gen<non_nested> comment_p =
    comment_parser_gen<non_nested>();
const comment_parser_gen<is_nested> comment_nest_p =
    comment_parser_gen<is_nested>();

///////////////////////////////////////////////////////////////////////////////
}} // namespace boost::spirit

#endif
