public function process($tokenList)
 {
     $skip = 0;
     $warning = true;
     $base_expr = "";
     $expr_type = false;
     $option = false;
     $resultList = array();
     foreach ($tokenList as $k => $v) {
         $token = new ExpressionToken($k, $v);
         if ($token->isWhitespaceToken()) {
             continue;
         }
         if ($skip > 0) {
             $skip--;
             continue;
         }
         switch ($token->getUpper()) {
             case 'VIEW':
             case 'SCHEMA':
             case 'DATABASE':
             case 'TABLE':
                 $expr_type = strtolower($token->getTrim());
                 break;
             case 'IF':
                 $warning = false;
                 $skip = 1;
                 break;
             case 'TEMPORARY':
                 $expr_type = ExpressionType::TEMPORARY_TABLE;
                 $skip = 1;
                 break;
             case 'RESTRICT':
             case 'CASCADE':
                 $option = $token->getUpper();
                 break;
             case ',':
                 $resultList[] = array('expr_type' => $expr_type, 'base_expr' => $base_expr);
                 $base_expr = "";
                 break;
             default:
                 $base_expr .= $token->getToken();
         }
     }
     if ($base_expr !== "") {
         $resultList[] = array('expr_type' => $expr_type, 'base_expr' => $base_expr);
     }
     return array('option' => $option, 'warning' => $warning, 'object_list' => $resultList);
 }
 public function process($tokens)
 {
     $resultList = array();
     $skip_next = false;
     $prev = new ExpressionToken();
     foreach ($tokens as $k => $v) {
         $curr = new ExpressionToken($k, $v);
         if ($curr->isWhitespaceToken()) {
             continue;
         }
         if ($skip_next) {
             // skip the next non-whitespace token
             $skip_next = false;
             continue;
         }
         /* is it a subquery? */
         if ($curr->isSubQueryToken()) {
             $processor = new DefaultProcessor();
             $curr->setSubTree($processor->process($this->removeParenthesisFromStart($curr->getTrim())));
             $curr->setTokenType(ExpressionType::SUBQUERY);
         } elseif ($curr->isEnclosedWithinParenthesis()) {
             /* is it an in-list? */
             $localTokenList = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($curr->getTrim()));
             if ($prev->getUpper() === 'IN') {
                 foreach ($localTokenList as $k => $v) {
                     $tmpToken = new ExpressionToken($k, $v);
                     if ($tmpToken->isCommaToken()) {
                         unset($localTokenList[$k]);
                     }
                 }
                 $localTokenList = array_values($localTokenList);
                 $curr->setSubTree($this->process($localTokenList));
                 $curr->setTokenType(ExpressionType::IN_LIST);
             } elseif ($prev->getUpper() === 'AGAINST') {
                 $match_mode = false;
                 foreach ($localTokenList as $k => $v) {
                     $tmpToken = new ExpressionToken($k, $v);
                     switch ($tmpToken->getUpper()) {
                         case 'WITH':
                             $match_mode = 'WITH QUERY EXPANSION';
                             break;
                         case 'IN':
                             $match_mode = 'IN BOOLEAN MODE';
                             break;
                         default:
                     }
                     if ($match_mode !== false) {
                         unset($localTokenList[$k]);
                     }
                 }
                 $tmpToken = $this->process($localTokenList);
                 if ($match_mode !== false) {
                     $match_mode = new ExpressionToken(0, $match_mode);
                     $match_mode->setTokenType(ExpressionType::MATCH_MODE);
                     $tmpToken[] = $match_mode->toArray();
                 }
                 $curr->setSubTree($tmpToken);
                 $curr->setTokenType(ExpressionType::MATCH_ARGUMENTS);
                 $prev->setTokenType(ExpressionType::SIMPLE_FUNCTION);
             } elseif ($prev->isColumnReference() || $prev->isFunction() || $prev->isAggregateFunction()) {
                 // if we have a colref followed by a parenthesis pair,
                 // it isn't a colref, it is a user-function
                 // TODO: this should be a method, because we need the same code
                 // below for unspecified tokens (expressions).
                 $localExpr = new ExpressionToken();
                 $tmpExprList = array();
                 foreach ($localTokenList as $k => $v) {
                     $tmpToken = new ExpressionToken($k, $v);
                     if (!$tmpToken->isCommaToken()) {
                         $localExpr->addToken($v);
                         $tmpExprList[] = $v;
                     } else {
                         // an expression could have multiple parts split by operands
                         // if we have a comma, it is a split-point for expressions
                         $tmpExprList = array_values($tmpExprList);
                         $localExprList = $this->process($tmpExprList);
                         if (count($localExprList) > 1) {
                             $localExpr->setSubTree($localExprList);
                             $localExpr->setTokenType(ExpressionType::EXPRESSION);
                             $localExprList = $localExpr->toArray();
                             $localExprList['alias'] = false;
                             $localExprList = array($localExprList);
                         }
                         if (!$curr->getSubTree()) {
                             $curr->setSubTree($localExprList);
                         } else {
                             $tmpExprList = $curr->getSubTree();
                             $curr->setSubTree(array_merge($tmpExprList, $localExprList));
                         }
                         $tmpExprList = array();
                         $localExpr = new ExpressionToken();
                     }
                 }
                 $tmpExprList = array_values($tmpExprList);
                 $localExprList = $this->process($tmpExprList);
                 if (count($localExprList) > 1) {
                     $localExpr->setSubTree($localExprList);
                     $localExpr->setTokenType(ExpressionType::EXPRESSION);
                     $localExprList = $localExpr->toArray();
                     $localExprList['alias'] = false;
                     $localExprList = array($localExprList);
                 }
                 if (!$curr->getSubTree()) {
                     $curr->setSubTree($localExprList);
                 } else {
                     $tmpExprList = $curr->getSubTree();
                     $curr->setSubTree(array_merge($tmpExprList, $localExprList));
                 }
                 $prev->setSubTree($curr->getSubTree());
                 if ($prev->isColumnReference()) {
                     $prev->setTokenType(ExpressionType::SIMPLE_FUNCTION);
                     $prev->setNoQuotes(null);
                 }
                 array_pop($resultList);
                 $curr = $prev;
             }
             // we have parenthesis, but it seems to be an expression
             if ($curr->isUnspecified()) {
                 // TODO: the localTokenList could contain commas and further expressions,
                 // we must handle that like function parameters (see above)!
                 // this should solve issue 51
                 $curr->setSubTree($this->process($localTokenList));
                 $curr->setTokenType(ExpressionType::BRACKET_EXPRESSION);
             }
         } elseif ($curr->isVariableToken()) {
             # a variable
             # it can be quoted
             $curr->setTokenType($this->getVariableType($curr->getUpper()));
             $curr->setSubTree(false);
             $curr->setNoQuotes(trim(trim($curr->getToken()), '@'), "`'\"");
         } else {
             /* it is either an operator, a colref or a constant */
             switch ($curr->getUpper()) {
                 case '*':
                     $curr->setSubTree(false);
                     // o subtree
                     // single or first element of expression list -> all-column-alias
                     if (empty($resultList)) {
                         $curr->setTokenType(ExpressionType::COLREF);
                         break;
                     }
                     // if the last token is colref, const or expression
                     // then * is an operator
                     // but if the previous colref ends with a dot, the * is the all-columns-alias
                     if (!$prev->isColumnReference() && !$prev->isConstant() && !$prev->isExpression() && !$prev->isBracketExpression() && !$prev->isAggregateFunction() && !$prev->isVariable()) {
                         $curr->setTokenType(ExpressionType::COLREF);
                         break;
                     }
                     if ($prev->isColumnReference() && $prev->endsWith(".")) {
                         $prev->addToken('*');
                         // tablealias dot *
                         continue 2;
                         // skip the current token
                     }
                     $curr->setTokenType(ExpressionType::OPERATOR);
                     break;
                 case ':=':
                 case 'AND':
                 case '&&':
                 case 'BETWEEN':
                 case 'AND':
                 case 'BINARY':
                 case '&':
                 case '~':
                 case '|':
                 case '^':
                 case 'DIV':
                 case '/':
                 case '<=>':
                 case '=':
                 case '>=':
                 case '>':
                 case 'IS':
                 case 'NOT':
                 case '<<':
                 case '<=':
                 case '<':
                 case 'LIKE':
                 case '%':
                 case '!=':
                 case '<>':
                 case 'REGEXP':
                 case '!':
                 case '||':
                 case 'OR':
                 case '>>':
                 case 'RLIKE':
                 case 'SOUNDS':
                 case 'XOR':
                 case 'IN':
                     $curr->setSubTree(false);
                     $curr->setTokenType(ExpressionType::OPERATOR);
                     break;
                 case 'NULL':
                     $curr->setSubTree(false);
                     $curr->setTokenType(ExpressionType::CONSTANT);
                     break;
                 case '-':
                 case '+':
                     // differ between preceding sign and operator
                     $curr->setSubTree(false);
                     if ($prev->isColumnReference() || $prev->isFunction() || $prev->isAggregateFunction() || $prev->isConstant() || $prev->isSubQuery() || $prev->isExpression() || $prev->isBracketExpression() || $prev->isVariable()) {
                         $curr->setTokenType(ExpressionType::OPERATOR);
                     } else {
                         $curr->setTokenType(ExpressionType::SIGN);
                     }
                     break;
                 default:
                     $curr->setSubTree(false);
                     switch ($curr->getToken(0)) {
                         case "'":
                         case '"':
                             // it is a string literal
                             $curr->setTokenType(ExpressionType::CONSTANT);
                             break;
                         case '`':
                             // it is an escaped colum name
                             $curr->setTokenType(ExpressionType::COLREF);
                             $curr->setNoQuotes($curr->getToken());
                             break;
                         default:
                             if (is_numeric($curr->getToken())) {
                                 if ($prev->isSign()) {
                                     $prev->addToken($curr->getToken());
                                     // it is a negative numeric constant
                                     $prev->setTokenType(ExpressionType::CONSTANT);
                                     continue 3;
                                     // skip current token
                                 } else {
                                     $curr->setTokenType(ExpressionType::CONSTANT);
                                 }
                             } else {
                                 $curr->setTokenType(ExpressionType::COLREF);
                                 $curr->setNoQuotes($curr->getToken());
                             }
                             break;
                     }
             }
         }
         /* is a reserved word? */
         if (!$curr->isOperator() && !$curr->isInList() && !$curr->isFunction() && !$curr->isAggregateFunction() && PHPSQLParserConstants::isReserved($curr->getUpper())) {
             if (PHPSQLParserConstants::isAggregateFunction($curr->getUpper())) {
                 $curr->setTokenType(ExpressionType::AGGREGATE_FUNCTION);
                 $curr->setNoQuotes(null);
             } elseif ($curr->getUpper() === 'NULL') {
                 // it is a reserved word, but we would like to set it as constant
                 $curr->setTokenType(ExpressionType::CONSTANT);
             } else {
                 if (PHPSQLParserConstants::isParameterizedFunction($curr->getUpper())) {
                     // issue 60: check functions with parameters
                     // -> colref (we check parameters later)
                     // -> if there is no parameter, we leave the colref
                     $curr->setTokenType(ExpressionType::COLREF);
                 } elseif (PHPSQLParserConstants::isFunction($curr->getUpper())) {
                     $curr->setTokenType(ExpressionType::SIMPLE_FUNCTION);
                     $curr->setNoQuotes(null);
                 } else {
                     $curr->setTokenType(ExpressionType::RESERVED);
                     $curr->setNoQuotes(null);
                 }
             }
         }
         // issue 94, INTERVAL 1 MONTH
         if ($curr->isConstant() && PHPSQLParserConstants::isParameterizedFunction($prev->getUpper())) {
             $prev->setTokenType(ExpressionType::RESERVED);
             $prev->setNoQuotes(null);
         }
         if ($prev->isConstant() && PHPSQLParserConstants::isParameterizedFunction($curr->getUpper())) {
             $curr->setTokenType(ExpressionType::RESERVED);
             $curr->setNoQuotes(null);
         }
         if ($curr->isUnspecified()) {
             $curr->setTokenType(ExpressionType::EXPRESSION);
             $curr->setNoQuotes(null);
             $curr->setSubTree($this->process($this->splitSQLIntoTokens($curr->getTrim())));
         }
         $resultList[] = $curr;
         $prev = $curr;
     }
     // end of for-loop
     return $this->toArray($resultList);
 }