/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This file contains the implementation of the CLexer class.          //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
#include <sstream>
#include "template_defs.hpp"
#include "ILexerStream.hpp"
#include "CLexer.hpp"
using std::ostringstream;
using std::string;
using std::vector;




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : CLexer (constructor)                                     //
//                                                                     //
// Comments :                                                          //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
CLexer::CLexer(ILexerStream *pStream)
  : m_iLine(1),
    m_pStream(pStream)
{
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : ~CLexer (destructor)                                     //
//                                                                     //
// Comments :                                                          //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
CLexer::~CLexer()
{
  for_each(m_vTokenMatchers.begin(),
           m_vTokenMatchers.end(),
           deletor<ITokenMatcher*>());
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : tokenize()                                               //
//                                                                     //
// Comments : This function returns the next token in the stream by    //
//            checking separator character.                            //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
string CLexer::tokenize()
{
  string returnValue = "";

  bool done = false;
  do
    {
      if (m_pStream->finished())
        {
          done = true;
        }
      else
        {
          char c = m_pStream->get();
          if (c == '\n')
            m_iLine++;

          if (isSeparator(c))
            {
              // If the next character is a separator
              if (returnValue.length() != 0)
                {
                  // If the token string is not empty, we have
                  // completed a token, so we put back the separator
                  // character and we are done
                  m_pStream->put(c);
                  done = true;
                  if (c == '\n')
                    m_iLine--;
                }
              else
                {
                  // If the token string is empty and the character
                  // does not have to be ignored, the token will
                  // contain only that character and we are done
                  if (!isToIgnore(c))
                    {
                      done = true;
                      returnValue += c;
                    }
                }
            }
          else
            {
              // If the character is not a separator and is not to be
              // ignored, we add it.
              if (!isToIgnore(c))
                returnValue += c;
            }
        }
    }
  while (!done);

  return returnValue;
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : getTokenValue()                                          //
//                                                                     //
// Comments : This function tries to match a given string with one of  //
//            the token matcher                                        //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
int CLexer::getTokenValue(const string& s) const
{
  int returnValue = 0;
  for (vector<ITokenMatcher*>::const_iterator it = m_vTokenMatchers.begin();
       it != m_vTokenMatchers.end() && returnValue == 0;
       ++it)
    {
      returnValue = (*it)->match(s);
    }
  return returnValue;
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : lookahead()                                              //
//                                                                     //
// Comments : This function returns the integer value corresponding to //
//            the i-th next token.                                     //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
int CLexer::lookahead(unsigned int nbTokens)
{
  while (m_tokens.size() < nbTokens+1 && !m_pStream->finished())
    {
      SToken token;
      token.string = tokenize();
      token.line   = m_iLine;

      if (token.string == "")
        token.value = SToken::ENDOFFILE_TOKEN;
      else
        token.value  = getTokenValue(token.string);

      if (token.value == 0)
        {
          ostringstream o;
          o << "line " << token.line
            << ": Unmatched token: " << token.string;
          throw o.str();
        }

      m_tokens.push_back(token);
    }

  if (m_tokens.size() > nbTokens)
    return m_tokens[nbTokens].value;
  else
    return SToken::INVALID_TOKEN;
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : match()                                                  //
//                                                                     //
// Comments : This function tries to match the next token with a given //
//            token value and throws an exception (actually, a string) //
//            if they don't match.                                     //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
string CLexer::match(int token)
{
  if (lookahead(0) == token)
    {
      string returnValue = m_tokens.front().string;
      m_tokens.pop_front();
      return returnValue;
    }
  else
    {
      ostringstream o;
      o << "line " << m_tokens.front().line << ": Trying to match token \""
        << m_tokenDescriptions[token] << "\" with string \""
        << m_tokens.front().string << "\"";
      throw o.str();
    }
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
// Class    : CLexer                                                   //
//                                                                     //
// Function : error()                                                  //
//                                                                     //
// Comments : This function is called when an error has been noticed   //
//            by the parser using the lexer.                           //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
void CLexer::error()
{
  throw string("Unexpected token");
}
