////////////////////////////////////////////////////////////////////////////////
// The TextFormator Framework
// Version 0.9 beta
//
// Copyright (c) 2003 by Morning
// http://morningspace.51.net
// mailto:moyingzz@etang.com
//
// Permission to use, copy, modify, distribute and sell this program for any 
// purpose is hereby granted without fee, provided that the above copyright 
// notice appear in all copies and that both that copyright notice and this 
// permission notice appear in supporting documentation.
//
// It is provided "as is" without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////

#ifndef _CONCRETE_PARSE_HANDLERS_H_
#define _CONCRETE_PARSE_HANDLERS_H_
//
#include "../GeneralDefine.h"
#include "../core/ParseHandler.h"
#include <iostream>
//
namespace TextFormator {

/**
 * Subclass of ParseHandler used to parse string from source text.
 * @see ParseHandler
 */
class StringParseHandler : public ParseHandler
{
public:
    StringParseHandler() : ParseHandler(MAX_PRIORITY)
    {}

    virtual bool accept(const Line& line, Line::size_type& pos, 
        Context& context, TokenInfo& token_info)
    {
        if (0 == context.session().get("comment").compare("T"))
            return false;

        if (line[pos]!='\"' && line[pos]!='\'')
            return false;

        Line::size_type pos0;
        for (pos0=pos+1; pos0<line.length(); ++pos0)
        {
            if (line[pos]=='\"' && line[pos0]=='\"' && line[pos0-1]!='\\')
                break;
            if (line[pos]=='\"' && line[pos0]=='\"' && 
                line[pos0-1]=='\\' && line[pos0-2]=='\\')
                break;
            if (line[pos]=='\'' && line[pos0]=='\'' && line[pos0-1]!='\\')
                break;
            if (line[pos]=='\'' && line[pos0]=='\'' && 
                line[pos0-1]=='\\' && line[pos0-2]=='\\')
                break;
        }

        token_info.token.assign(line, pos, pos0+1-pos);
        token_info.type = "STRING";
        pos = pos0+1;
        return true;
    }
};

/**
 * Subclass of ParseHandler used to parse number from source text.
 * @see ParseHandler
 */
class NumberParseHandler : public ParseHandler
{
public:
    virtual bool accept(const Line& line, Line::size_type& pos, 
        Context& context, TokenInfo& token_info)
    {
        if (!isdigit(line[pos]))
            return false;

        Line::size_type pos0;
        for (pos0=pos+1; pos0<line.length(); ++pos0)
        {
            if (!isdigit(line[pos0]))
                break;
        }

        token_info.token.assign(line, pos, pos0-pos);
        token_info.type = "NUMBER";
        pos = pos0;
        return true;
    }
};

/**
 * Subclass of ParseHandler used to parse operator from source text.
 * @see ParseHandler
 */
class OperatorParseHandler : public ParseHandler
{
private:
    const std::string _operators;

public:
    OperatorParseHandler() : _operators("+-*/%~!|<>=()[]{}#,.?:;&\\")
    {}

    virtual bool accept(const Line& line, Line::size_type& pos, 
        Context& context, TokenInfo& token_info)
    {
        if (Line::npos == _operators.find(line[pos]))
            return false;

        Line::size_type pos0;
        for (pos0=pos+1; pos0<line.length(); ++pos0)
        {
            if (Line::npos == _operators.find(line[pos0]))
                break;
            if (line[pos0]=='/' && line[pos0+1]=='/')
                break;
        }

        token_info.token.assign(line, pos, pos0-pos);
        token_info.type = "OPERATOR";
        pos = pos0;
        return true;
    }
};

/**
 * Subclass of ParseHandler used to parse identifier from source text.
 * @see ParseHandler
 */
class IdentifierParseHandler : public ParseHandler
{
private:
    typedef std::list<std::string> Keywords;
    Keywords* _keywords;

    bool isKeyword(const std::string token)
    {
        if (NULL == _keywords)
            return false;

        for (Keywords::iterator i=_keywords->begin(); 
             i!=_keywords->end(); ++i)
        {
            if (0 == (*i).compare(token))
            {
                return true;
            }
        }
        return false;
    };

public:
    IdentifierParseHandler(Keywords* keywords = NULL)
        : _keywords(keywords)
    {
    }

    void setKeywords(Keywords* keywords)
    {
        _keywords = keywords;
    }

    virtual bool accept(const Line& line, Line::size_type& pos, 
        Context& context, TokenInfo& token_info)
    {
        if (!isalpha(line[pos]) && line[pos]!='_')
            return false;

        Line::size_type pos0;
        for (pos0=pos+1; pos0<line.length(); ++pos0)
        {
            if (!isalpha(line[pos0]) && line[pos0]!='_' && !isdigit(line[pos0]))
                break;
        }

        token_info.token.assign(line, pos, pos0-pos);
        token_info.type = isKeyword(token_info.token) ? "KEYWORD" : "IDENTIFIER";
        pos = pos0;
        return true;
    }
};

/**
 * Subclass of ParseHandler used to parse whitespace from source text.
 * @see ParseHandler
 */
class WhitespaceParseHandler : public ParseHandler
{
private:
    const std::string _whitespace;

public:
    WhitespaceParseHandler() : _whitespace(" \t")
    {}

    virtual bool accept(const Line& line, Line::size_type& pos, 
        Context& context, TokenInfo& token_info)
    {
        if (Line::npos == _whitespace.find(line[pos]))
            return false;

        Line::size_type pos0;
        for (pos0=pos+1; pos0<line.length(); ++pos0)
        {
            if (Line::npos == _whitespace.find(line[pos0]))
                break;
        }

        token_info.token.assign(line, pos, pos0-pos);
        token_info.type = "WHITESPACE";
        pos = pos0;
        return true;
    }
};

/**
 * Subclass of ParseHandler used to parse comment from source text.
 * @see ParseHandler
 */
class CommentParseHandler : public ParseHandler
{
public:
    CommentParseHandler() : ParseHandler(MAX_PRIORITY)
    {}

    virtual bool accept(const Line& line, Line::size_type& pos, 
        Context& context, TokenInfo& token_info)
    {
        bool is_comment = (0 == context.session().get("comment").compare("T"));

        if (line.empty() && is_comment)
        {
            token_info.token = "";
            token_info.type = "COMMENT";
            pos = 0;
            return true;
        }

        if (line[pos]=='/' && line[pos+1]=='/')
        {
            token_info.token.assign(line, pos, line.length()-pos);
            token_info.type = "COMMENT";
            pos = line.length();
            return true;
        }

        if ((line[pos]!='/' || line[pos+1]!='*') && !is_comment)
            return false;

        Line::size_type pos0;
        std::string nest = context.session().get("nest");
        for (pos0=pos; pos0<line.length(); ++pos0)
        {
            if (line[pos0]=='/' && line[pos0+1]=='*')
            {
                nest += "#";
                ++pos0;
            }
            else if (line[pos0]=='*' && line[pos0+1]=='/')
            {
                nest.assign(nest, 0, nest.length()-1);
                ++pos0;
            }
            if (nest.empty())
            {
                ++pos0;
                break;
            }
        }

        token_info.token.assign(line, pos, pos0-pos);
        token_info.type = "COMMENT";
        pos = pos0;
        context.session().set("nest", nest);
        if (nest.empty())
            context.session().set("comment", "F");
        else
            context.session().set("comment", "T");
        return true;
    }
};

} // namespace TextFormator

#endif // _CONCRETE_PARSE_HANDLERS_H_