<?php

/*
 * PARTLY BASED ON:
 * Copyright (c) 2007 E. W. Bachtal, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * The software is provided "as is", without warranty of any kind, express or implied, including but not
 * limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In
 * no event shall the authors or copyright holders be liable for any claim, damages or other liability,
 * whether in an action of contract, tort or otherwise, arising from, out of or in connection with the
 * software or the use or other dealings in the software.
 *
 * http://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
 * http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
 */

/**
 * PHPExcel_Calculation_FormulaParser
 *
 * Copyright (c) 2006 - 2015 PHPExcel
 *
 * 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.1 of the License, or (at your option) any later version.
 *
 * 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; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * @category PHPExcel
 * @package PHPExcel_Calculation
 * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
 * @version ##VERSION##, ##DATE##
 */
class PHPExcel_Calculation_FormulaParser
{
  /* Character constants */
  const QUOTE_DOUBLE = '"';
  const QUOTE_SINGLE = '\'';
  const BRACKET_CLOSE = ']';
  const BRACKET_OPEN = '[';
  const BRACE_OPEN = '{';
  const BRACE_CLOSE = '}';
  const PAREN_OPEN = '(';
  const PAREN_CLOSE = ')';
  const SEMICOLON = ';';
  const WHITESPACE = ' ';
  const COMMA = ',';
  const ERROR_START = '#';
  const OPERATORS_SN = "+-";
  const OPERATORS_INFIX = "+-*/^&=><";
  const OPERATORS_POSTFIX = "%";
  
  /**
   * Formula
   *
   * @var string
   */
  private $formula;
  
  /**
   * Tokens
   *
   * @var PHPExcel_Calculation_FormulaToken[]
   */
  private $tokens = array ();
  
  /**
   * Create a new PHPExcel_Calculation_FormulaParser
   *
   * @param string $pFormula
   *          Formula to parse
   * @throws PHPExcel_Calculation_Exception
   */
  public function __construct($pFormula = '')
  {
    // Check parameters
    if (is_null ( $pFormula ))
    {
      throw new PHPExcel_Calculation_Exception ( "Invalid parameter passed: formula" );
    }
    
    // Initialise values
    $this->formula = trim ( $pFormula );
    // Parse!
    $this->parseToTokens ();
  }
  
  /**
   * Get Formula
   *
   * @return string
   */
  public function getFormula()
  {
    return $this->formula;
  }
  
  /**
   * Get Token
   *
   * @param int $pId
   *          Token id
   * @return string
   * @throws PHPExcel_Calculation_Exception
   */
  public function getToken($pId = 0)
  {
    if (isset ( $this->tokens [$pId] ))
    {
      return $this->tokens [$pId];
    }
    else
    {
      throw new PHPExcel_Calculation_Exception ( "Token with id $pId does not exist." );
    }
  }
  
  /**
   * Get Token count
   *
   * @return string
   */
  public function getTokenCount()
  {
    return count ( $this->tokens );
  }
  
  /**
   * Get Tokens
   *
   * @return PHPExcel_Calculation_FormulaToken[]
   */
  public function getTokens()
  {
    return $this->tokens;
  }
  
  /**
   * Parse to tokens
   */
  private function parseToTokens()
  {
    // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
    // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
    
    // Check if the formula has a valid starting =
    $formulaLength = strlen ( $this->formula );
    if ($formulaLength < 2 || $this->formula {0} != '=')
    {
      return;
    }
    
    // Helper variables
    $tokens1 = $tokens2 = $stack = array ();
    $inString = $inPath = $inRange = $inError = false;
    $token = $previousToken = $nextToken = null;
    
    $index = 1;
    $value = '';
    
    $ERRORS = array (
        "#NULL!",
        "#DIV/0!",
        "#VALUE!",
        "#REF!",
        "#NAME?",
        "#NUM!",
        "#N/A" 
    );
    $COMPARATORS_MULTI = array (
        ">=",
        "<=",
        "<>" 
    );
    
    while ( $index < $formulaLength )
    {
      // state-dependent character evaluation (order is important)
      
      // double-quoted strings
      // embeds are doubled
      // end marks token
      if ($inString)
      {
        if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE)
        {
          if ((($index + 2) <= $formulaLength) && ($this->formula {$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE))
          {
            $value .= PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE;
            ++ $index;
          }
          else
          {
            $inString = false;
            $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_TEXT );
            $value = "";
          }
        }
        else
        {
          $value .= $this->formula {$index};
        }
        ++ $index;
        continue;
      }
      
      // single-quoted strings (links)
      // embeds are double
      // end does not mark a token
      if ($inPath)
      {
        if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE)
        {
          if ((($index + 2) <= $formulaLength) && ($this->formula {$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE))
          {
            $value .= PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE;
            ++ $index;
          }
          else
          {
            $inPath = false;
          }
        }
        else
        {
          $value .= $this->formula {$index};
        }
        ++ $index;
        continue;
      }
      
      // bracked strings (R1C1 range index or linked workbook name)
      // no embeds (changed to "()" by Excel)
      // end does not mark a token
      if ($inRange)
      {
        if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::BRACKET_CLOSE)
        {
          $inRange = false;
        }
        $value .= $this->formula {$index};
        ++ $index;
        continue;
      }
      
      // error values
      // end marks a token, determined from absolute list of values
      if ($inError)
      {
        $value .= $this->formula {$index};
        ++ $index;
        if (in_array ( $value, $ERRORS ))
        {
          $inError = false;
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_ERROR );
          $value = "";
        }
        continue;
      }
      
      // scientific notation check
      if (strpos ( PHPExcel_Calculation_FormulaParser::OPERATORS_SN, $this->formula {$index} ) !== false)
      {
        if (strlen ( $value ) > 1)
        {
          if (preg_match ( "/^[1-9]{1}(\.[0-9]+)?E{1}$/", $this->formula {$index} ) != 0)
          {
            $value .= $this->formula {$index};
            ++ $index;
            continue;
          }
        }
      }
      
      // independent character evaluation (order not important)
      
      // establish state-dependent character evaluations
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE)
      {
        if (strlen ( $value > 0 ))
        {
          // unexpected
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN );
          $value = "";
        }
        $inString = true;
        ++ $index;
        continue;
      }
      
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE)
      {
        if (strlen ( $value ) > 0)
        {
          // unexpected
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN );
          $value = "";
        }
        $inPath = true;
        ++ $index;
        continue;
      }
      
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::BRACKET_OPEN)
      {
        $inRange = true;
        $value .= PHPExcel_Calculation_FormulaParser::BRACKET_OPEN;
        ++ $index;
        continue;
      }
      
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::ERROR_START)
      {
        if (strlen ( $value ) > 0)
        {
          // unexpected
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN );
          $value = "";
        }
        $inError = true;
        $value .= PHPExcel_Calculation_FormulaParser::ERROR_START;
        ++ $index;
        continue;
      }
      
      // mark start and end of arrays and array rows
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::BRACE_OPEN)
      {
        if (strlen ( $value ) > 0)
        {
          // unexpected
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN );
          $value = "";
        }
        
        $tmp = new PHPExcel_Calculation_FormulaToken ( "ARRAY", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START );
        $tokens1 [] = $tmp;
        $stack [] = clone $tmp;
        
        $tmp = new PHPExcel_Calculation_FormulaToken ( "ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START );
        $tokens1 [] = $tmp;
        $stack [] = clone $tmp;
        
        ++ $index;
        continue;
      }
      
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::SEMICOLON)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        
        $tmp = array_pop ( $stack );
        $tmp->setValue ( "" );
        $tmp->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP );
        $tokens1 [] = $tmp;
        
        $tmp = new PHPExcel_Calculation_FormulaToken ( ",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT );
        $tokens1 [] = $tmp;
        
        $tmp = new PHPExcel_Calculation_FormulaToken ( "ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START );
        $tokens1 [] = $tmp;
        $stack [] = clone $tmp;
        
        ++ $index;
        continue;
      }
      
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::BRACE_CLOSE)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        
        $tmp = array_pop ( $stack );
        $tmp->setValue ( "" );
        $tmp->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP );
        $tokens1 [] = $tmp;
        
        $tmp = array_pop ( $stack );
        $tmp->setValue ( "" );
        $tmp->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP );
        $tokens1 [] = $tmp;
        
        ++ $index;
        continue;
      }
      
      // trim white-space
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( "", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE );
        ++ $index;
        while ( ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) && ($index < $formulaLength) )
        {
          ++ $index;
        }
        continue;
      }
      
      // multi-character comparators
      if (($index + 2) <= $formulaLength)
      {
        if (in_array ( substr ( $this->formula, $index, 2 ), $COMPARATORS_MULTI ))
        {
          if (strlen ( $value ) > 0)
          {
            $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
            $value = "";
          }
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( substr ( $this->formula, $index, 2 ), PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL );
          $index += 2;
          continue;
        }
      }
      
      // standard infix operators
      if (strpos ( PHPExcel_Calculation_FormulaParser::OPERATORS_INFIX, $this->formula {$index} ) !== false)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $this->formula {$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX );
        ++ $index;
        continue;
      }
      
      // standard postfix operators (only one)
      if (strpos ( PHPExcel_Calculation_FormulaParser::OPERATORS_POSTFIX, $this->formula {$index} ) !== false)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $this->formula {$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX );
        ++ $index;
        continue;
      }
      
      // start subexpression or function
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::PAREN_OPEN)
      {
        if (strlen ( $value ) > 0)
        {
          $tmp = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START );
          $tokens1 [] = $tmp;
          $stack [] = clone $tmp;
          $value = "";
        }
        else
        {
          $tmp = new PHPExcel_Calculation_FormulaToken ( "", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START );
          $tokens1 [] = $tmp;
          $stack [] = clone $tmp;
        }
        ++ $index;
        continue;
      }
      
      // function, subexpression, or array parameters, or operand unions
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::COMMA)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        
        $tmp = array_pop ( $stack );
        $tmp->setValue ( "" );
        $tmp->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP );
        $stack [] = $tmp;
        
        if ($tmp->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( ",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_UNION );
        }
        else
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( ",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT );
        }
        ++ $index;
        continue;
      }
      
      // stop subexpression
      if ($this->formula {$index} == PHPExcel_Calculation_FormulaParser::PAREN_CLOSE)
      {
        if (strlen ( $value ) > 0)
        {
          $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
          $value = "";
        }
        
        $tmp = array_pop ( $stack );
        $tmp->setValue ( "" );
        $tmp->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP );
        $tokens1 [] = $tmp;
        
        ++ $index;
        continue;
      }
      
      // token accumulation
      $value .= $this->formula {$index};
      ++ $index;
    }
    
    // dump remaining accumulation
    if (strlen ( $value ) > 0)
    {
      $tokens1 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND );
    }
    
    // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
    $tokenCount = count ( $tokens1 );
    for($i = 0; $i < $tokenCount; ++ $i)
    {
      $token = $tokens1 [$i];
      if (isset ( $tokens1 [$i - 1] ))
      {
        $previousToken = $tokens1 [$i - 1];
      }
      else
      {
        $previousToken = null;
      }
      if (isset ( $tokens1 [$i + 1] ))
      {
        $nextToken = $tokens1 [$i + 1];
      }
      else
      {
        $nextToken = null;
      }
      
      if (is_null ( $token ))
      {
        continue;
      }
      
      if ($token->getTokenType () != PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE)
      {
        $tokens2 [] = $token;
        continue;
      }
      
      if (is_null ( $previousToken ))
      {
        continue;
      }
      
      if (! ((($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) || (($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) || ($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)))
      {
        continue;
      }
      
      if (is_null ( $nextToken ))
      {
        continue;
      }
      
      if (! ((($nextToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) || (($nextToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) || ($nextToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)))
      {
        continue;
      }
      
      $tokens2 [] = new PHPExcel_Calculation_FormulaToken ( $value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_INTERSECTION );
    }
    
    // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
    // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
    $this->tokens = array ();
    
    $tokenCount = count ( $tokens2 );
    for($i = 0; $i < $tokenCount; ++ $i)
    {
      $token = $tokens2 [$i];
      if (isset ( $tokens2 [$i - 1] ))
      {
        $previousToken = $tokens2 [$i - 1];
      }
      else
      {
        $previousToken = null;
      }
      if (isset ( $tokens2 [$i + 1] ))
      {
        $nextToken = $tokens2 [$i + 1];
      }
      else
      {
        $nextToken = null;
      }
      
      if (is_null ( $token ))
      {
        continue;
      }
      
      if ($token->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue () == "-")
      {
        if ($i == 0)
        {
          $token->setTokenType ( PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX );
        }
        elseif ((($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) || (($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) || ($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) || ($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND))
        {
          $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH );
        }
        else
        {
          $token->setTokenType ( PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX );
        }
        
        $this->tokens [] = $token;
        continue;
      }
      
      if ($token->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue () == "+")
      {
        if ($i == 0)
        {
          continue;
        }
        elseif ((($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) || (($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) || ($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) || ($previousToken->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND))
        {
          $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH );
        }
        else
        {
          continue;
        }
        
        $this->tokens [] = $token;
        continue;
      }
      
      if ($token->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING)
      {
        if (strpos ( "<>=", substr ( $token->getValue (), 0, 1 ) ) !== false)
        {
          $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL );
        }
        elseif ($token->getValue () == "&")
        {
          $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_CONCATENATION );
        }
        else
        {
          $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH );
        }
        
        $this->tokens [] = $token;
        continue;
      }
      
      if ($token->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND && $token->getTokenSubType () == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING)
      {
        if (! is_numeric ( $token->getValue () ))
        {
          if (strtoupper ( $token->getValue () ) == "TRUE" || strtoupper ( $token->getValue () == "FALSE" ))
          {
            $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL );
          }
          else
          {
            $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_RANGE );
          }
        }
        else
        {
          $token->setTokenSubType ( PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NUMBER );
        }
        
        $this->tokens [] = $token;
        continue;
      }
      
      if ($token->getTokenType () == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION)
      {
        if (strlen ( $token->getValue () > 0 ))
        {
          if (substr ( $token->getValue (), 0, 1 ) == "@")
          {
            $token->setValue ( substr ( $token->getValue (), 1 ) );
          }
        }
      }
      
      $this->tokens [] = $token;
    }
  }
}
