/*
 *  Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * either version 2, or (at your option) any later version of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "Token_p.h"

#include "Debug.h"

using namespace Cauchy;


String Token::typeToString(Type type)
{
  switch(type)
  {
// Not really token
    case COMMENT:
      return "comment";
    case END_OF_FILE:
      return "end of file";
    case END_OF_LINE:
      return "end of line";
    case UNKNOWN:
      return "unknown token";
    case UNFINISHED_STRING:
      return "unfinished string";
// Special characters
    case Token::SEMI:
      return ";";
    case Token::COLON:
      return ":";
    case Token::COMA:
      return ",";
    case Token::DOT:
      return ".";
    case Token::TRANSPOSE:
      return "'";
    case Token::STARTBRACE:
      return "{";
    case Token::ENDBRACE:
      return "}";
    case Token::STARTBRACKET:
      return "(";
    case Token::ENDBRACKET:
      return ")";
    case Token::STARTBOXBRACKET:
      return "[";
    case Token::ENDBOXBRACKET:
      return "]";
    case Token::EQUAL:
      return "=";
    case Token::EQUALEQUAL:
      return "==";
    case Token::PLUSEQUAL:
      return "+=";
    case Token::DOTPLUSEQUAL:
      return ".+=";
    case Token::MINUSEQUAL:
      return "-=";
    case Token::DOTMINUSEQUAL:
      return ".-=";
    case Token::MULTIPLYEQUAL:
      return "*=";
    case Token::DOTMULTIPLYEQUAL:
      return ".*=";
    case Token::DIVIDEEQUAL:
      return "/=";
    case Token::DOTDIVIDEEQUAL:
      return "./=";
    case Token::DIFFERENT:
      return "!=";
    case Token::AND:
      return "and";
    case Token::OR:
      return "or";
    case Token::POWER:
      return "^";
    case Token::DOTPOWER:
      return ".^";
    case Token::INFERIOR:
      return "<";
    case Token::INFERIOREQUAL:
      return "<=";
    case Token::SUPPERIOR:
      return ">";
    case Token::SUPPERIOREQUAL:
      return ">=";
    case Token::PLUS:
    case Token::PLUSSMTHG:
      return "+";
    case Token::DOTPLUS:
      return ".+";
    case Token::PLUSPLUS:
      return "++";
    case Token::MINUS:
    case Token::MINUSSMTHG:
      return "-";
    case Token::DOTMINUS:
      return ".-";
    case Token::MINUSMINUS:
      return "--";
    case Token::MULTIPLY:
      return "*";
    case Token::DOTMULTIPLY:
      return ".*";
    case Token::DIVIDE:
      return "/";
    case Token::DOTDIVIDE:
      return "./";
    case Token::TILDE:
      return "~";
    case Token::NOT:
      return "not";
    case Token::AROBASE:
      return "@";
// Constants
    case FLOAT_CONSTANT:
      return "float constant";
    case INTEGER_CONSTANT:
      return "integer constant";
    case COMPLEX_CONSTANT:
      return "complex constant";
    case STRING_CONSTANT:
      return "string constant";
    case IDENTIFIER:
      return "identifier";
    case ELSE:
      return "else";
    case ELSEIF:
      return "elseif";
    case FOR:
      return "for";
    case IF:
      return "if";
    case RETURN:
      return "return";
    case WHILE:
      return "while";
    case END:
      return "end";
    case GLOBAL:
      return "global";
    case FUNCTION:
      return "function";
    case BREAK:
      return "break";
  }
  return "[TODO] " + String::number( int(type) );
}

Token::Token() : type(UNKNOWN), line(-1), column(-1)
{
}

Token::Token(Type _type, int _line, int _column) : type(_type), line(_line), column(_column)
{
}

Token::Token(Type _type, const String& _string, int _line, int _column) : type(_type), line(_line), column(_column), string(_string)
{
  CAUCHY_ASSERT( _type == STRING_CONSTANT or _type == IDENTIFIER or _type == COMMENT );
}

bool Token::isConstant() const
{
  return type == Token::INTEGER_CONSTANT or type == Token::FLOAT_CONSTANT or type == Token::COMPLEX_CONSTANT or type == Token::STRING_CONSTANT;
}

bool Token::isPrimary() const
{
  return isConstant() or type == Token::IDENTIFIER;
}

bool Token::isBinaryOperator() const
{
  return binaryOperationPriority() != -1;
}

enum Priority {
  ASSIGNEMENT_PRIORITY,
  OR_PRIORITY,
  AND_PRIORITY,
  EQUALITY_PRIORITY,
  RELATIONAL_PRIORITY,
  ADDITIVE_PRIORITY,
  MULTIPLICATIVE_PRIORITY,
  POWER_PRIORITY,
  COLON_PRIORITY,
};

int Token::binaryOperationPriority() const
{
  switch( type )
  {
    case Token::COLON:
      return COLON_PRIORITY;
    case Token::EQUAL:
    case Token::PLUSEQUAL:
    case Token::MINUSEQUAL:
    case Token::MULTIPLYEQUAL:
    case Token::DIVIDEEQUAL:
    case Token::DOTPLUSEQUAL:
    case Token::DOTMINUSEQUAL:
    case Token::DOTMULTIPLYEQUAL:
    case Token::DOTDIVIDEEQUAL:
      return ASSIGNEMENT_PRIORITY;
    case Token::OR:
      return OR_PRIORITY;
    case Token::AND:
      return AND_PRIORITY;
    case Token::EQUALEQUAL:
    case Token::DIFFERENT:
      return EQUALITY_PRIORITY;
    case Token::INFERIOREQUAL:
    case Token::INFERIOR:
    case Token::SUPPERIOREQUAL:
    case Token::SUPPERIOR:
      return RELATIONAL_PRIORITY;
    case Token::PLUS:
    case Token::PLUSSMTHG:
    case Token::DOTPLUS:
    case Token::MINUS:
    case Token::MINUSSMTHG:
    case Token::DOTMINUS:
      return ADDITIVE_PRIORITY;
    case Token::MULTIPLY:
    case Token::DOTMULTIPLY:
    case Token::DIVIDE:
    case Token::DOTDIVIDE:
      return MULTIPLICATIVE_PRIORITY;
    case Token::POWER:
      return POWER_PRIORITY;
    default:
      return -1;
  }
}

bool Token::isUnaryOperator() const
{
  return type == Token::PLUS or type == Token::PLUSSMTHG or type == Token::MINUS or type == Token::MINUSSMTHG or type == Token::TILDE or type == Token::NOT or type == Token::PLUSPLUS or type == Token::MINUSMINUS;
}

bool Token::isOperator() const
{
  return isUnaryOperator() or isBinaryOperator();
}

bool Token::isExpressionTerminal()
{
  return type == Token::SEMI or type == Token::ENDBRACKET or type == Token::COMA or type == Token::ENDBOXBRACKET or type == Token::ENDBRACE or type == Token::END_OF_LINE or type == Token::COMMENT or type == Token::END_OF_FILE;
}
