/*
 *  Copyright (c) 2008,2010 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 "Parser.h"

#include "CompilationMessages.h"
#include "CompilationMessages_p.h"
#include "CompilationMessage.h"
#include "Debug.h"
#include "DeclarationsRegistry.h"
#include "Lexer.h"
#include "Token_p.h"
#include "Utils_p.h"
#include "Macros_p.h"

#include "AST/Tree.h"
#include "AST/Expression.h"
#include "AST/UnaryExpression.h"
#include "VariablesManager.h"
#include "AST/BinaryExpression.h"
#include "AST/FunctionDefinition.h"
#include "Variable.h"
#include "FunctionDeclaration.h"
#include <complex>

using namespace Cauchy;

struct Parser::Private {
  Private(DeclarationsRegistry* _registry) : variablesManager(_registry), functionsDefinitionsRegistry(_registry)
  {
  }
  Lexer* lexer;
  CompilationMessages compilationMessages;
  Token currentToken;
  AST::Tree* tree;
  VariablesManager variablesManager;
  DeclarationsRegistry* functionsDefinitionsRegistry;
};

Parser::Parser(Lexer* lexer, DeclarationsRegistry* _registry) : d(new Private(_registry))
{
  d->lexer = lexer;
  d->tree = 0;
}

Parser::~Parser()
{
  delete d;
}

AST::Tree* Parser::parse()
{
  CAUCHY_ASSERT(d->tree == 0);
  d->tree = new AST::Tree;
  getNextToken(); // Init the parsing, load first token
  d->variablesManager.startContext(); // start the global context
  
  parseBody();
  
  // Check if compilation was successfull
  if( isOfType(currentToken(), Token::END_OF_FILE ) and d->compilationMessages.errors().size() == 0 )
  {
    AST::Tree* tree = d->tree;
    d->tree = 0;
    return tree;
  }
  delete d->tree;
  d->tree = 0;
  return 0;
}

AST::Statement* Parser::parseStatementsList()
{
  std::list<AST::Statement*> list;
  while(true)
  {
    AST::Statement* statement = parseStatement();
    if(statement)
    {
      list.push_back(statement);
    } else {
      switch(currentToken().type)
      {
        case Token::SEMI:
          getNextToken(); // eat the ;
          break;
        case Token::END:
        case Token::ELSE:
        case Token::ELSEIF:
          return new AST::StatementsList(list);
        default:
          deleteAll(list);
          return 0;
      }
    }
  }
}

AST::Statement* Parser::parseStatement()
{
  switch(currentToken().type)
  {
    case Token::GLOBAL:
      getNextToken(); // eat the global
      isOfType(currentToken(), Token::IDENTIFIER);
      while(currentToken().type == Token::IDENTIFIER)
      {
        Variable* var = d->variablesManager.declareGlobal(currentToken().string);
        if(var)
        {
          d->tree->appendGlobal(var);
        } else {
          reportError("Undeclared global '" + currentToken().string + "'", currentToken());
        }
        getNextToken(); // eat
      }
      if(d->currentToken.type == Token::SEMI)
      {
        getNextToken(); // eat ';'
      }
      return parseStatement();
      break;
    case Token::STARTBRACKET:
    case Token::STARTBOXBRACKET:
    case Token::IDENTIFIER:
    {
      AST::Expression* expr = parseExpression(EPM_Normal);
      AST::FunctionCallExpression* fce = dynamic_cast<AST::FunctionCallExpression*>(expr);
      if(currentToken().type != Token::SEMI and (fce == 0 or fce->name() != "disp" ) and expr and expr->type() != Type::Void ) // disp is going to print the statement, no need for printing it twice
      {
        if(currentToken().type == Token::COMA)
        {
          getNextToken();
        }
        return makeComment(new AST::PrintStatement(expr));
      } else {
        getNextToken(); // eat the ;
        return makeComment(expr);
      }
    }
      break;
    case Token::COMMENT:
    {
      AST::Statement* statement = new AST::CommentStatement(currentToken().string);
      getNextToken();
      return statement;
    }
      break;
    case Token::IF:
    {
      getNextToken(); // Eat the IF
      // parse the expression
      AST::Expression* expr = parseExpression(EPM_Condition);
      String comment;
      if(currentToken().type == Token::COMMENT)
      {
        comment = currentToken().string;
      }
      // Parse the statement list;
      AST::Statement* ifStatement = parseStatementsList();
      AST::Statement* elseStatement = 0;
      std::vector< std::pair<AST::Expression*, AST::Statement*> > statements;
      while(currentToken().type == Token::ELSEIF)
      {
        getNextToken(); // eat the elseif
        // parse the exoression and statement
        AST::Expression* expression = parseExpression(EPM_Condition);
        AST::Statement* statement = parseStatementsList();
        statements.push_back(std::pair<AST::Expression*, AST::Statement*>(expression, statement));
      }
      
      // Parse the else statement
      if(currentToken().type == Token::ELSE)
      {
        getNextToken();
        elseStatement = parseStatementsList();
      }
      AST::Statement* statement = 0;
      if(elseStatement or statements.size() > 0)
      {
         statement = new AST::IfElseStatement(expr, ifStatement, statements, elseStatement);
      } else {
         statement = new AST::IfStatement(expr, ifStatement);
      }
      statement->setComment(comment);
      CAUCHY_DEBUG(currentToken());
      isOfType(currentToken(), Token::END);
      getNextToken();
      return statement;
    }
    case Token::WHILE:
    {
      getNextToken();
      AST::Expression* expr = parseExpression(EPM_Condition);
      // Is there a comment on the same line
      String comment;
      if(currentToken().type == Token::COMMENT)
      {
        comment = currentToken().string;
      }
      // Parse the statement
      AST::Statement* statement = new AST::WhileStatement(expr, parseStatementsList());
      statement->setComment(comment);
      // Check that we finish with end
      isOfType(currentToken(), Token::END);
      getNextToken();
      return statement;
    }
    case Token::FOR:
    {
      getNextToken();
      // Check that the first token is an identifier
      isOfType(currentToken(), Token::IDENTIFIER);
      String varname = currentToken().string;
      Token varnametoken = currentToken();
      getNextToken();
      // Next token is =
      isOfType(currentToken(), Token::EQUAL);
      getNextToken();
      AST::Expression* expr = parseExpression(EPM_Normal);
      // Is there a comment on the same line
      String comment;
      if(currentToken().type == Token::COMMENT)
      {
        comment = currentToken().string;
      }
      // Parse the statement
      Variable* var = d->variablesManager.getVariable(varname, false);
      if(var->qualifier() == Variable::Constant)
      {
        var = d->variablesManager.overrideConstant(var->name());
      }
      if(not var->setType(Type::defaultType()))
      {
        reportError("Conflicting type use for variable '" + varname + "'.", varnametoken);
      }
      AST::Statement* statement = new AST::ForStatement( var, expr, parseStatementsList());
      statement->setComment(comment);
      // Check that we finish with end
      isOfType(currentToken(), Token::END);
      getNextToken();
      return statement;
    }
      break;
    case Token::BREAK:
      getNextToken(); // Eat the BREAK
      return makeComment(new AST::BreakStatement);
    case Token::END_OF_LINE:
      // Ignore end of lines
      getNextToken();
      return parseStatement();
    default:
      if( currentToken().isUnaryOperator() or currentToken().isConstant() )
      {
        AST::Expression* expr = parseExpression(EPM_Normal);
        if(currentToken().type != Token::SEMI)
        {
          if(currentToken().type == Token::COMA)
          {
            getNextToken();
          }
          return makeComment(new AST::PrintStatement(expr));
        } else {
          getNextToken(); // eat the ;
          return makeComment(expr);
        }
      }
      
      break;
  }
  return 0;
}

void Parser::parseFunction()
{
  getNextToken();
  std::vector<String> returns;
  if(currentToken().type == Token::STARTBOXBRACKET)
  {
    getNextToken();
    bool more = true;
    while(more)
    {
      switch(currentToken().type)
      {
        case Token::ENDBOXBRACKET:
          getNextToken();
          more = false;
          break;
        case Token::IDENTIFIER:
          returns.push_back(currentToken().string);
        case Token::COMA:
          getNextToken();
          break;
        default:
          CAUCHY_DEBUG("Unexpected");
          reportUnexpected(currentToken());
          return;
      }
    }
    isOfType(currentToken(), Token::EQUAL);
    getNextToken();
  }
  if(isOfType(currentToken(), Token::IDENTIFIER))
  {
    String functionName = currentToken().string;
    Token functionNameTok = currentToken();
    getNextToken();
    // if this token is a = then we have a single parameter function, and functionName contains the name of the variable to return
    if(currentToken().type == Token::EQUAL)
    {
      returns.push_back(functionName);
      getNextToken();
      functionName = currentToken().string;
      getNextToken();
    }
    // We can now start to parse the body of the function
    CAUCHY_DEBUG("Start function: " << functionName);
    std::vector<String> arguments;
    d->variablesManager.startContext();
    
    if(currentToken().type == Token::STARTBRACKET)
    {
      getNextToken();
      while(currentToken().type != Token::ENDBRACKET)
      {
        if(isOfType(currentToken(), Token::IDENTIFIER))
        {
          arguments.push_back(currentToken().string);
        }
        getNextToken();
        if(currentToken().type == Token::COMA)
        {
          getNextToken();
        } else if( currentToken().type != Token::ENDBRACKET)
        {
          CAUCHY_DEBUG("unexpected");
          reportUnexpected(currentToken());
        }
      }
      getNextToken(); // eat the ')'
    }
    const FunctionDeclaration* declaration = 0;
    
    if(d->functionsDefinitionsRegistry)
    {
      declaration = d->functionsDefinitionsRegistry->function(functionName, arguments.size(), returns.size());
      if(declaration)
      {
        for(std::size_t i = 0; i < arguments.size(); ++i)
        {
          d->variablesManager.getVariable(arguments[i], true)->setType(declaration->arguments()[i]);
        }
      } else {
        reportError("Unknown function: '" + functionName + "'", functionNameTok);
      }
    }
    AST::FunctionDefinition* function = new AST::FunctionDefinition(declaration, returns, arguments);
    while(true)
    {
      AST::Statement* statement = parseStatement();
      if(statement)
      {
        function->append(statement);
      } else {
        switch(currentToken().type)
        {
          case Token::END:
            getNextToken(); // Eat the endfunction token
          case Token::END_OF_FILE:
          {
            CAUCHY_DEBUG("End function: " << functionName);
            function->append(new AST::ReturnStatement(function));
            d->tree->appendFunction(function);
            d->variablesManager.endContext();
            return;
          }
          default:
          {
            CAUCHY_DEBUG("unexpected " << Token::typeToString( currentToken().type ) );
            reportUnexpected( currentToken() );
            getNextToken();
          }
        }
      }
    }
  }
}

void Parser::parseBody()
{
  while(true)
  {
    AST::Statement* statement = parseStatement();
    if(statement)
    {
      d->tree->appendToMain(statement);
    } else {
      switch(currentToken().type)
      {
        case Token::END_OF_FILE:
          return;
        case Token::FUNCTION:
          parseFunction();
          break;
        default:
        {
          CAUCHY_DEBUG("unexpected " << Token::typeToString( currentToken().type ) );
          reportUnexpected( currentToken() );
          getNextToken();
        }
      }
    }
  }
}

CompilationMessages Parser::compilationMessages() const
{
  return d->compilationMessages;
}

void Parser::getNextToken()
{
  d->currentToken = d->lexer->nextToken();
}

const Token& Parser::currentToken()
{
  return d->currentToken;
}

void Parser::reportError( const String& errMsg, const Token& token )
{
  CAUCHY_DEBUG(errMsg);
  d->compilationMessages.d->appendMessage(CompilationMessage( CompilationMessage::ERROR, errMsg, token.line, "" ) );
}

void Parser::reportWarning( const String& errMsg, const Token& token )
{
  CAUCHY_DEBUG(errMsg);
  d->compilationMessages.d->appendMessage(CompilationMessage( CompilationMessage::WARNING, errMsg, token.line, "" ) );
}

void Parser::reportUnexpected( const Token& token )
{
  String err = "Unexpected: " + Token::typeToString( token.type );
  if(token.type == Token::IDENTIFIER)
  {
    err += " ( " + token.string + " )";
  }
  reportError(err, token );
  getNextToken();
}

bool Parser::isOfType( const Token& token, Token::Type type )
{
  if( token.type == type )
  {
    return true;
  } else {
    CAUCHY_DEBUG("Unexpected " << token);
    reportError("Expected " + Token::typeToString(type) + " before " + Token::typeToString(token.type)  + ".", token);
    return false;
  }
}

AST::Expression* Parser::parseExpression( Parser::ExpressionParseMode _mode )
{
  CAUCHY_DEBUG(currentToken());
  // The first token of an expression is either a primary, or an unary expression or a parenthesis
  if( d->currentToken.type == Token::STARTBRACKET )
  {
    getNextToken();
    AST::Expression* expr = parseExpression(_mode);
    if( expr and isOfType( d->currentToken, Token::ENDBRACKET ) )
    {
      getNextToken();
      if( expr and d->currentToken.isBinaryOperator() )
      {
        expr = parseFlatBinaryOperator( expr, _mode );
      }
    }
    return new AST::GroupExpression( expr );
  } else if( d->currentToken.isUnaryOperator() )
  {
    AST::Expression* expr = parseUnaryOperator();
    // What's next ? An unary operator is either followed by a binary operator or a semi
    if( d->currentToken.isExpressionTerminal())
    {
      return expr;
    } else if( expr and d->currentToken.isBinaryOperator() and ( _mode != EPM_Matrix or (currentToken().type != Token::PLUSSMTHG and currentToken().type != Token::MINUSSMTHG ) ) ) {
      return parseFlatBinaryOperator( expr, _mode );
    } else if( _mode == EPM_Matrix)
    {
      return expr;
    } else {
      delete expr;
      CAUCHY_DEBUG("unexpected");
      reportUnexpected(d->currentToken);
    }
  } else if( currentToken().type == Token::STARTBOXBRACKET )
  {
    return parseMatrixExpression( );
  } else if( currentToken().type == Token::AROBASE ) {
    getNextToken(); // eat the '@'
    if(isOfType(currentToken(), Token::IDENTIFIER))
    {
      String name = currentToken().string;
      getNextToken(); // eath the identifier
      return new AST::FunctionHandleExpression(name);
    }
  } else {
    AST::Expression* expression = 0;
    if( d->currentToken.isPrimary() )
    {
      CAUCHY_DEBUG( d->currentToken.isPrimary() << " " << d->currentToken);
      expression = parsePrimaryExpression();
      if(not expression)
      {
        // An error has occured
        reportError("Parse error while parsing constant", d->currentToken );
        return 0;
      }
    } else {
      CAUCHY_DEBUG("unexpected");
      reportUnexpected( d->currentToken );
      return 0;
    }
    if( expression )
    { // What's next ? A binary operator or a ;
      if( d->currentToken.isExpressionTerminal() )
      {
        return expression;
      } else if( d->currentToken.isBinaryOperator() and ( _mode != EPM_Matrix or (currentToken().type != Token::PLUSSMTHG and currentToken().type != Token::MINUSSMTHG ) ) ) {
        return parseFlatBinaryOperator( expression, _mode );
      } else if(_mode == EPM_Matrix or _mode == EPM_Condition) {
        return expression;
      }
      delete expression;
    }
    CAUCHY_DEBUG("unexpected");
    reportUnexpected( d->currentToken );
    getNextToken();
  }
  return 0;
}

AST::Expression* Parser::parseFlatBinaryOperator( AST::Expression* _lhs, ExpressionParseMode _mode)
{
  CAUCHY_ASSERT(_lhs);
  AST::Expression* current = _lhs;
  while( d->currentToken.isBinaryOperator() )
  {
    CAUCHY_DEBUG( "Looping in parseFlatBinaryOperator");
    current = parseBinaryOperator( current, _mode );
    if( not current ) // An error has occured
    {
      return 0;
    }
  }
  return current;
}

AST::Expression* Parser::parseBinaryOperator( AST::Expression* _lhs, ExpressionParseMode _mode)
{
  if( not _lhs) return 0;
  Token binOp = d->currentToken;
  CAUCHY_DEBUG( "parseBinaryOperator: " << binOp )
  getNextToken();
  AST::Expression* expr = 0;
  while(true)
  {
    CAUCHY_DEBUG( binOp << " " << expr );
    // What's next ? A primary expression or an unary operator or a parenthesis
    if( expr == 0 )
    {
      if( d->currentToken.type == Token::STARTBRACKET )
      {
        getNextToken();
        expr = parseExpression(EPM_Normal);
        if( isOfType( d->currentToken, Token::ENDBRACKET ) )
        {
          getNextToken();
        }
        expr = new AST::GroupExpression(expr);
      } else if( d->currentToken.isPrimary() )
      {
        expr = parsePrimaryExpression();
      } else if( d->currentToken.isUnaryOperator() ) {
        expr = parseUnaryOperator();
      } else if( currentToken().type == Token::STARTBOXBRACKET ) {
        expr = parseMatrixExpression( );
      } else {
        CAUCHY_DEBUG("unexpected");
        reportUnexpected( d->currentToken );
        return 0;
      }
    }
    // What's next, either a semi or an other binary operator
    if( d->currentToken.type == Token::TRANSPOSE)
    {
      expr = new AST::TransposeUnaryExpression(expr);
      getNextToken();
    }
    if( d->currentToken.isExpressionTerminal() ) {
      CAUCHY_DEBUG( binOp << " terminal" );
      return createBinaryOperator( binOp, _lhs, expr);
    } else if( d->currentToken.isBinaryOperator() ) {
      CAUCHY_DEBUG( binOp << " vs " << d->currentToken.isBinaryOperator());
      CAUCHY_ASSERT( binOp.binaryOperationPriority() != -1 );
      CAUCHY_ASSERT( d->currentToken.binaryOperationPriority() != -1 );
      
      if( d->currentToken.binaryOperationPriority() == binOp.binaryOperationPriority() )
      {
        CAUCHY_DEBUG( binOp << " collapse _lhs and expr and keep going" );
        _lhs = createBinaryOperator( binOp, _lhs, expr );
        binOp = d->currentToken;
        getNextToken(); // eat the current operator
        expr = 0;
      } else if( d->currentToken.binaryOperationPriority() > binOp.binaryOperationPriority() )
      {
        CAUCHY_DEBUG( binOp << " keep parsing to collapse expr with what is next" );
        expr = parseBinaryOperator( expr, _mode );
        if( expr == 0 ) return 0;
        if( d->currentToken.isExpressionTerminal() )
        {
          return createBinaryOperator( binOp, _lhs, expr);
        }
      } else {
        CAUCHY_DEBUG( binOp << " collapse _lhs and expr and return" );
        return createBinaryOperator( binOp, _lhs, expr );
      }
    } else if(_mode == EPM_Condition) {
      return expr;
    } else {
      CAUCHY_DEBUG("unexpected");
      reportUnexpected( d->currentToken );
      return 0;
    }
  }
}

AST::Expression* Parser::parseUnaryOperator( )
{
  Token unaryTok = d->currentToken;
  CAUCHY_ASSERT(unaryTok.isUnaryOperator() );
  getNextToken();
  // What's next ? An unary operator is either followed by a parenthesis or an other unary operator or a primary
  AST::Expression* expr = 0;
  if( d->currentToken.type == Token::STARTBRACKET ) {
    expr = parseExpression(EPM_Normal);
    if( isOfType( d->currentToken, Token::ENDBRACKET ) )
    {
      getNextToken();
    }
    expr = new AST::GroupExpression(expr);
  } else if( d->currentToken.isPrimary() )
  {
    expr = parsePrimaryExpression();
  } else if( d->currentToken.isUnaryOperator() ) {
    expr = parseUnaryOperator();
  } else {
    CAUCHY_DEBUG("unexpected");
    reportUnexpected( d->currentToken );
    return 0;
  }
  switch( unaryTok.type )
  {
    case Token::PLUS:
    case Token::PLUSSMTHG:
      return expr;
    case Token::MINUS:
    case Token::MINUSSMTHG:
      return new AST::MinusUnaryExpression( expr );
    case Token::MINUSMINUS:
    {
      return new AST::MinusMinusUnaryExpression( expr );
    }
    case Token::PLUSPLUS:
    {
      return new AST::PlusPlusUnaryExpression( expr );
    }
    case Token::NOT:
    {
      return new AST::NotUnaryExpression( expr );
    }
    case Token::TILDE:
    {
      return new AST::TildeUnaryExpression( expr );
    }
    default:
    {
      CAUCHY_ASSERT( true );
      return 0;
    }
  }
}

std::vector<const Type*> expressionsToType(std::list<AST::Expression*> expressions)
{
  std::vector<const Type*> types;
  foreach(AST::Expression* expr, expressions)
  {
    types.push_back(expr->type());
  }
  return types;
}

AST::Expression* Parser::parseMatrixExpression( )
{
  Token::Type endToken;
  switch( d->currentToken.type)
  {
    case Token::STARTBOXBRACKET:
      endToken = Token::ENDBOXBRACKET;
      break;
    default:
      CAUCHY_DEBUG("unexpected");
      reportUnexpected( d->currentToken );
      return 0;
  }
  
  getNextToken(); // Eat '['
  std::vector< std::vector< AST::Expression* > > expressions_;
//   bool transpose = false;
  std::vector< AST::Expression* > currentLine;
  while(true)
  {
    while(currentToken().type == Token::END_OF_LINE and currentToken().type != Token::END_OF_FILE)
    {
      getNextToken();
    }
    AST::Expression* expression = 0;
    
    if( d->currentToken.type == Token::STARTBOXBRACKET)
    {
      expression = parseMatrixExpression(  );
    } else {
      expression = parseExpression( EPM_Matrix );
    }
    if( not expression )
    {
//       deleteAll( expressions_ );
      return 0;
    }
    currentLine.push_back( expression );
    if( d->currentToken.type == Token::COMA )
    {
      getNextToken();
    } else if( d->currentToken.type == Token::SEMI ) {
      expressions_.push_back(currentLine);
      currentLine.clear();
      getNextToken();
    } else if( d->currentToken.type == endToken )
    {
      getNextToken();
      if(currentToken().type == Token::EQUAL)
      { // Then we have something that should look like [ a b ] = func()
        getNextToken();
        
        // Next is a function name
        isOfType(currentToken(), Token::IDENTIFIER);
        String name = currentToken().string;
        getNextToken();
        
        // Next is a '('
        isOfType(currentToken(), Token::STARTBRACKET);
        getNextToken();
        
        // Then parse argument
        std::list<AST::Expression*> arguments = parseArguments( );
        
        if(d->variablesManager.hasVariable(name))
        {
          Variable* variable = d->variablesManager.getVariable(name, false);
          if(not variable->type()->isFunctionPointer())
          {
            reportError("Variable '" + name + "' is not a function handle", currentToken());
          }
          return new AST::FunctionCallExpression( variable, arguments, currentLine ) ;
        } else {
          const FunctionDeclaration* declaration = 0;
          
          if(d->functionsDefinitionsRegistry)
          {
            declaration = d->functionsDefinitionsRegistry->function(name, expressionsToType(arguments), currentLine.size());
            if(declaration)
            {
              CAUCHY_ASSERT(currentLine.size() <= declaration->returns().size());
              for(std::size_t i = 0; i < currentLine.size(); ++i)
              {
                AST::VariableExpression* varExpr = dynamic_cast<AST::VariableExpression*>(currentLine[i]);
                if(varExpr)
                {
                  varExpr->variable()->setType(declaration->returns()[i]);
                } else {
                  reportError( "Expect variable name in return expression", currentToken());
                }
                
              }
            } else if(d->functionsDefinitionsRegistry->function(name).empty()) {
              reportError("Unknown function '" + name + "'", currentToken());
            } else {
              reportError("Invalid number of arguments for function '" + name + "'", currentToken());
            }
          }
          return new AST::FunctionCallExpression( name, declaration, arguments, currentLine ) ;
        }
      }
      if(not currentLine.empty())
      {
        expressions_.push_back(currentLine);
      }
      return new AST::MatrixExpression(expressions_);
    }
  }
}

AST::Expression* Parser::parsePrimaryExpression()
{
  switch( d->currentToken.type )
  {
    case Token::INTEGER_CONSTANT:
    {
      String n = d->currentToken.string;
      getNextToken();
      return new AST::NumberExpression( n, Type::INTEGER );
    }
    case Token::FLOAT_CONSTANT:
    {
      String n = d->currentToken.string;
      getNextToken();
      return new AST::NumberExpression( n, Type::DOUBLE );
    }
    case Token::COMPLEX_CONSTANT:
    {
      String n = d->currentToken.string;
      getNextToken();
      return new AST::ComplexNumberExpression( "0", n, Cauchy::Type::DOUBLE );
    }
    case Token::STRING_CONSTANT:
    {
      String s = d->currentToken.string;
      getNextToken();
      return new AST::StringExpression( s );
    }
    case Token::IDENTIFIER:
    {
      // It can be either a call to a function or a variable access
      String name = d->currentToken.string;
      getNextToken(); // eat the identifier
      const FunctionDeclaration* declaration = 0;
      if( d->currentToken.type == Token::STARTBRACKET and not d->variablesManager.hasVariable(name) )
      { // It's a function call
        CAUCHY_DEBUG( name );
        getNextToken();
        // Parse arguments
        std::list<AST::Expression*> arguments = parseArguments( );
        
        if(d->functionsDefinitionsRegistry)
        {
          declaration = d->functionsDefinitionsRegistry->function(name, expressionsToType(arguments), 0);
          if(not declaration)
          {
            if(d->functionsDefinitionsRegistry->function(name).empty()) {
              reportError("Unknown function '" + name + "'", currentToken());
            } else {
              reportError("Invalid number of arguments for function '" + name + "'", currentToken());
            }
            return 0;
          }
        }
        return new AST::FunctionCallExpression( name, declaration, arguments ) ;
      } else if(d->functionsDefinitionsRegistry and (declaration = d->functionsDefinitionsRegistry->function(name, std::vector<const Type*>(), 0)))
      {
        return new AST::FunctionCallExpression(name, declaration, std::list<AST::Expression*>());
      } else {
        Variable* var = d->variablesManager.getVariable( name, false );
        if(not var)
        {
          reportError( "Unknown variable: " + name, d->currentToken );
          return 0;
        }
        return parseMemberArrayExpression( var );
      }
      break;
    }
    default:
      CAUCHY_DEBUG("unexpected");
      reportUnexpected( d->currentToken );
  }
  return 0;
}

AST::Expression* Parser::createBinaryOperator( const Token& token, AST::Expression* lhs, AST::Expression* rhs )
{
  if( not lhs or not rhs )
  {
    delete lhs;
    delete rhs;
    return 0;
  }
  if( token.type == Token::EQUAL or token.type == Token::PLUSEQUAL or token.type == Token::MINUSEQUAL or token.type == Token::MULTIPLYEQUAL or token.type == Token::DIVIDEEQUAL)
  {
    if( token.type == Token::PLUSEQUAL or token.type == Token::DOTPLUSEQUAL )
    {
      rhs = new AST::AdditionBinaryExpression( new AST::ProxyExpression(lhs), rhs );
    } else if(token.type == Token::MINUSEQUAL or token.type == Token::DOTMINUSEQUAL) {
      rhs = new AST::SubtractionBinaryExpression( new AST::ProxyExpression(lhs), rhs );
    } else if(token.type == Token::MULTIPLYEQUAL) {
      rhs = new AST::MultiplicationBinaryExpression( new AST::ProxyExpression(lhs), rhs );
    } else if(token.type == Token::DOTMULTIPLYEQUAL) {
      rhs = new AST::ElementWiseMultiplicationBinaryExpression( new AST::ProxyExpression(lhs), rhs );
    } else if(token.type == Token::DIVIDEEQUAL) {
      rhs = new AST::DivisionBinaryExpression( new AST::ProxyExpression(lhs), rhs );
    } else if(token.type == Token::DOTDIVIDEEQUAL) {
      rhs = new AST::ElementWiseDivisionBinaryExpression( new AST::ProxyExpression(lhs), rhs );
    }
    AST::VariableExpression* lhsv = dynamic_cast<AST::VariableExpression*>(lhs);
    if(lhsv) {
      if(lhsv->variable())
      {
        if(lhsv->variable()->qualifier() == Variable::Constant)
        {
          lhsv->setVariable(d->variablesManager.overrideConstant(lhsv->variable()->name()));
        }
        if(not lhsv->variable()->setType(rhs->type()))
        {
          reportError("Conflicting type use for variable '" + lhsv->variable()->name() + "'.", currentToken());
        }
      }
      return new AST::AssignementBinaryExpression( lhsv, rhs );
    }
    delete lhs;
    delete rhs;
    reportError("Left hand side of an assignement expression must be a variable.", token);
    return 0;
    
  } else {
    switch( token.type )
    {
      case Token::OR:
        return new AST::OrBinaryExpression( lhs, rhs );
      case Token::AND:
        return new AST::AndBinaryExpression( lhs, rhs );
      case Token::EQUALEQUAL:
        return new AST::EqualEqualBinaryExpression( lhs, rhs );
      case Token::DIFFERENT:
        return new AST::DifferentBinaryExpression( lhs, rhs );
      case Token::INFERIOREQUAL:
        return new AST::InferiorEqualBinaryExpression( lhs, rhs );
      case Token::INFERIOR:
        return new AST::InferiorBinaryExpression( lhs, rhs );
      case Token::SUPPERIOREQUAL:
        return new AST::SupperiorEqualBinaryExpression( lhs, rhs );
      case Token::SUPPERIOR:
        return new AST::SupperiorBinaryExpression( lhs, rhs );
      case Token::PLUS:
      case Token::PLUSSMTHG:
      case Token::DOTPLUS:
        return new AST::AdditionBinaryExpression( lhs, rhs );
      case Token::MINUS:
      case Token::MINUSSMTHG:
      case Token::DOTMINUS:
        return new AST::SubtractionBinaryExpression( lhs, rhs );
      case Token::MULTIPLY:
        return new AST::MultiplicationBinaryExpression( lhs, rhs );
      case Token::DOTMULTIPLY:
        return new AST::ElementWiseMultiplicationBinaryExpression( lhs, rhs );
      case Token::DIVIDE:
        return new AST::DivisionBinaryExpression( lhs, rhs );
      case Token::DOTDIVIDE:
        return new AST::ElementWiseDivisionBinaryExpression( lhs, rhs );
      case Token::POWER:
        return new AST::PowerBinaryExpression(lhs, rhs);
      case Token::DOTPOWER:
        return new AST::ElementWisePowerBinaryExpression(lhs, rhs);
      case Token::COLON:
      {
        if(AST::RangeExpression* rlhs = dynamic_cast<AST::RangeExpression*>(lhs)) {
          if(rlhs->hasStepExpression())
          {
            reportUnexpected(token);
          }
        }
        return new AST::RangeExpression(lhs, rhs);
      }
      default:
      {
        delete lhs;
        delete rhs;
        CAUCHY_ABORT("Unknown operator: " << Token::typeToString( token.type ) );
        return 0;
      }
    }
  }
}

std::list<AST::Expression*> Parser::parseArguments( )
{
  std::list<AST::Expression*> arguments;
  while(true)
  {
      if( d->currentToken.type == Token::ENDBRACKET )
    {
      break;
    } else {
      AST::Expression* expression = parseExpression( EPM_Normal );
      if(not expression)
      {
        return arguments;
      }
      arguments.push_back( expression);
      if( d->currentToken.type == Token::COMA )
      {
        getNextToken();
      } else if( d->currentToken.type != Token::ENDBRACKET )
      {
        CAUCHY_DEBUG("Unexpected");
        reportUnexpected( d->currentToken );
        return std::list<AST::Expression*>();
      }
    }
  }
  CAUCHY_ASSERT( d->currentToken.type == Token::ENDBRACKET );
  getNextToken(); // eat the end bracket
  return arguments;
}

AST::Expression* Parser::parseMemberArrayExpression(Variable* _variable)
{
  AST::Expression* expr1 = 0;
  AST::Expression* expr2 = 0;
  if( d->currentToken.type == Token::STARTBRACKET )
  {
    getNextToken();
    if(d->currentToken.type == Token::ENDBRACKET)
    {
      getNextToken();
    } else {
      if( d->currentToken.type == Token::COLON)
      {
        expr1 = new AST::InfiniteRangeExpression;
        getNextToken();
      } else {
        expr1 = parseExpression( EPM_Normal );
      }
      if( d->currentToken.type != Token::ENDBRACKET)
      {
        if(isOfType(d->currentToken, Token::COMA))
        {
          getNextToken();
          if( d->currentToken.type == Token::COLON)
          {
            expr2 = new AST::InfiniteRangeExpression;
            getNextToken();
          } else {
            expr2 = parseExpression( EPM_Normal );
          }
        } else {
          delete expr1;
          return 0;
        }
      }
      if( isOfType( d->currentToken, Token::ENDBRACKET ) )
      {
        getNextToken();
      }
    }
  }
  return new AST::VariableExpression(_variable, expr1, expr2);
}

AST::Statement* Parser::makeComment(AST::Statement* arg1)
{
  if(currentToken().type == Token::COMMENT)
  {
    if(arg1)
    {
      arg1->setComment(currentToken().string);
    }
    getNextToken();
  }
  return arg1;
}
