/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This file contains the declaration of the CLexer class.             //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
#ifndef __CLEXER_HPP__
#define __CLEXER_HPP__


#include <algorithm>
#include <deque>
#include <map>
#include <memory>
#include <string>
#include <vector>

class ILexerStream;


/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This structure represents a token identified by the lexer.          //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
struct SToken
{
  int         line;
  int         value;
  std::string string;

  static const int ENDOFFILE_TOKEN = -1;
  static const int INVALID_TOKEN   = -2;
};




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This class is the base class for concrete lexer.  It contains all   //
// the necessary functionnality used by a lexer, so concrete derived   //
// lexer classes only need to fill language specific information.      //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
class CLexer
{
public:
  virtual ~CLexer();

  int         lookahead(unsigned int nbTokens = 0);
  std::string match(int token);
  void        error();


  class ITokenMatcher
  {
  public:
    ITokenMatcher(int tokenValue) : m_tokenValue(tokenValue) {}
    virtual ~ITokenMatcher() {}
    virtual int match(const std::string& s) const = 0;

  protected:
    int m_tokenValue;
  };


protected:
  CLexer(ILexerStream *pStream);
  inline void addSeparatorChar(char c);
  inline void addToIgnoreChar(char c);
  inline void addTokenDescription(int tokenValue, const std::string& s);
  inline void addStringMatcher(int tokenValue, const std::string& s);
  inline void addMatcher(ITokenMatcher *pTokenMatcher);


private:
  inline bool isSeparator(char c) const;
  inline bool isToIgnore(char c) const;

  std::string tokenize();
  int getTokenValue(const std::string& s) const;


  int                         m_iLine;
  std::auto_ptr<ILexerStream> m_pStream;


  // Characters that splits tokens
  std::vector<char> m_vSeparatorChars;
  // Characters to ignore
  std::vector<char> m_vToIgnoreChars;
  // Token matchers
  std::vector<ITokenMatcher*> m_vTokenMatchers;
  // Token matched already;
  std::deque<SToken> m_tokens;
  // Map to associate token with a descriptive string
  std::map<int, std::string> m_tokenDescriptions;


  // Class to recognize token that matches a certain string
  class CStringMatcher : public ITokenMatcher
  {
  public:
    CStringMatcher(int tokenValue, const std::string& stringToMatch)
      : ITokenMatcher(tokenValue), m_stringToMatch(stringToMatch)
    {
    }
    int match(const std::string& s) const
    {
      if (s == m_stringToMatch)
        return m_tokenValue;
      else
        return 0;
    }

  private:
    std::string m_stringToMatch;
  };
};




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Functions used by concrete derived class to configure the general   //
// lexer.                                                              //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
inline void CLexer::addSeparatorChar(char c)
{
  m_vSeparatorChars.push_back(c);
}
inline void CLexer::addToIgnoreChar(char c)
{
  m_vToIgnoreChars.push_back(c);
}
inline void CLexer::addTokenDescription(int tokenValue, const std::string& s)
{
  m_tokenDescriptions[tokenValue] = s;
}
inline void CLexer::addStringMatcher(int tokenValue, const std::string& s)
{
  std::auto_ptr<ITokenMatcher> p(new CStringMatcher(tokenValue, s));
  m_vTokenMatchers.push_back(p.get());
  p.release();
}
inline void CLexer::addMatcher(ITokenMatcher *pTokenMatcher)
{
  m_vTokenMatchers.push_back(pTokenMatcher);
}
#define ADD_STRING_TOKEN(tokenValue, stringToMatch)        \
{                                                          \
  addStringMatcher(tokenValue, stringToMatch);             \
  addTokenDescription(tokenValue, #tokenValue );           \
}




// Functions to inquire about a character
inline bool CLexer::isSeparator(char c) const
{
  return find(m_vSeparatorChars.begin(),
              m_vSeparatorChars.end(),
              c) != m_vSeparatorChars.end();
}
inline bool CLexer::isToIgnore(char c) const
{
  return find(m_vToIgnoreChars.begin(),
              m_vToIgnoreChars.end(),
              c) != m_vToIgnoreChars.end();
}


#endif // __CLEXER_HPP__
