# OqlParser.class.php

Go to the documentation of this file.

    00001 <?php
    00002 /****************************************************************************
    00003  *   Copyright (C) 2008-2009 by Vladlen Y. Koshelev                         *
    00004  *                                                                          *
    00005  *   This program is free software; you can redistribute it and/or modify   *
    00006  *   it under the terms of the GNU Lesser General Public License as         *
    00007  *   published by the Free Software Foundation; either version 3 of the     *
    00008  *   License, or (at your option) any later version.                        *
    00009  *                                                                          *
    00010  ****************************************************************************/
    00011
    00015     abstract class OqlParser
    00016     {
    00017         const INITIAL_STATE = 254;
    00018         const FINAL_STATE   = 255;
    00019
    00020         // class map
    00021         const PREFIX_UNARY_EXPRESSION   = 1;
    00022         const POSTFIX_UNARY_EXPRESSION  = 2;
    00023         const BINARY_EXPRESSION         = 3;
    00024         const BETWEEN_EXPRESSION        = 4;
    00025
    00026         private static $classMap = array(
    00027             self::PREFIX_UNARY_EXPRESSION   => 'PrefixUnaryExpression',
    00028             self::POSTFIX_UNARY_EXPRESSION  => 'PostfixUnaryExpression',
    00029             self::BINARY_EXPRESSION         => 'BinaryExpression',
    00030             self::BETWEEN_EXPRESSION        => 'LogicalBetween'
    00031         );
    00032
    00033         // binary operator map
    00034         private static $binaryOperatorMap = array(
    00035             '='                 => BinaryExpression::EQUALS,
    00036             '!='                => BinaryExpression::NOT_EQUALS,
    00037             'and'               => BinaryExpression::EXPRESSION_AND,
    00038             'or'                => BinaryExpression::EXPRESSION_OR,
    00039             '>'                 => BinaryExpression::GREATER_THAN,
    00040             '>='                => BinaryExpression::GREATER_OR_EQUALS,
    00041             '<'                 => BinaryExpression::LOWER_THAN,
    00042             '<='                => BinaryExpression::LOWER_OR_EQUALS,
    00043             'like'              => BinaryExpression::LIKE,
    00044             'not like'          => BinaryExpression::NOT_LIKE,
    00045             'ilike'             => BinaryExpression::ILIKE,
    00046             'not ilike'         => BinaryExpression::NOT_ILIKE,
    00047             'similar to'        => BinaryExpression::SIMILAR_TO,
    00048             'not similar to'    => BinaryExpression::NOT_SIMILAR_TO,
    00049             '+'                 => BinaryExpression::ADD,
    00050             '-'                 => BinaryExpression::SUBSTRACT,
    00051             '*'                 => BinaryExpression::MULTIPLY,
    00052             '/'                 => BinaryExpression::DIVIDE
    00053         );
    00054
    00055         // boolean operators priority
    00056         const LOGIC_PRIORITY_OR         = 1;
    00057         const LOGIC_PRIORITY_AND        = 2;
    00058         const LOGIC_PRIORITY_LT_GT      = 3;
    00059         const LOGIC_PRIORITY_EQ         = 4;
    00060         const LOGIC_PRIORITY_TERMINAL   = 5;
    00061
    00062         const LOGIC_PRIORITY_LOWEST     = self::LOGIC_PRIORITY_OR;
    00063         const LOGIC_PRIORITY_UNARY_NOT  = self::LOGIC_PRIORITY_LT_GT;
    00064
    00065         private static $logicPriorityMap = array(
    00066             self::LOGIC_PRIORITY_OR         => 'or',
    00067             self::LOGIC_PRIORITY_AND        => 'and',
    00068             self::LOGIC_PRIORITY_LT_GT      => array('>', '<', '>=', '<='),
    00069             self::LOGIC_PRIORITY_EQ         => array('=', '!='),
    00070             self::LOGIC_PRIORITY_TERMINAL   => null
    00071         );
    00072
    00073         // arithmetic operators priority
    00074         const ARITHMETIC_PRIORITY_ADD       = 1;
    00075         const ARITHMETIC_PRIORITY_MUL       = 2;
    00076         const ARITHMETIC_PRIORITY_TERMINAL  = 3;
    00077
    00078         const ARITHMETIC_PRIORITY_LOWEST    = self::ARITHMETIC_PRIORITY_ADD;
    00079
    00080         private static $arithmeticPriorityMap = array(
    00081             self::ARITHMETIC_PRIORITY_ADD       => array('+', '-'),
    00082             self::ARITHMETIC_PRIORITY_MUL       => array('*', '/'),
    00083             self::ARITHMETIC_PRIORITY_TERMINAL  => null
    00084         );
    00085
    00086         protected $state        = null;
    00087         protected $tokenizer    = null;
    00088         protected $oqlObject    = null;
    00089
    00090         protected $parentheses  = null;
    00091
    00095         abstract protected function makeOqlObject();
    00096
    00097         abstract protected function handleState();
    00098
    00102         public function parse($string = null)
    00103         {
    00104             if ($string === null) {
    00105                 Assert::isNotNull($this->tokenizer);
    00106
    00107             } else {
    00108                 Assert::isString($string);
    00109                 $this->tokenizer = new OqlTokenizer($string);
    00110             }
    00111
    00112             $this->state = self::INITIAL_STATE;
    00113             $this->oqlObject = $this->makeOqlObject();
    00114             $this->parentheses = 0;
    00115
    00116             while ($this->state != self::FINAL_STATE)
    00117                 $this->state = $this->handleState();
    00118
    00119             $this->checkParentheses();
    00120
    00121             return $this->oqlObject;
    00122         }
    00123
    00127         public function getTokenizer()
    00128         {
    00129             return $this->tokenizer;
    00130         }
    00131
    00135         public function setTokenizer(OqlTokenizer $tokenizer)
    00136         {
    00137             $this->tokenizer = $tokenizer;
    00138
    00139             return $this;
    00140         }
    00141
    00142         protected function getTokenValue($token, $raw = false)
    00143         {
    00144             if ($token instanceof OqlToken)
    00145                 return $raw
    00146                     ? $token->getRawValue()
    00147                     : $token->getValue();
    00148
    00149             return null;
    00150         }
    00151
    00152         protected function checkToken($token, $type, $value = null)
    00153         {
    00154             if (
    00155                 $token instanceof OqlToken
    00156                 && $token->getType() == $type
    00157             ) {
    00158                 if ($value === null) {
    00159                     return true;
    00160
    00161                 } elseif (is_array($value)) {
    00162                     return in_array($token->getValue(), $value);
    00163
    00164                 } else {
    00165                     return $token->getValue() == $value;
    00166                 }
    00167             }
    00168
    00169             return false;
    00170         }
    00171
    00172         protected function checkKeyword($token, $value)
    00173         {
    00174             return $this->checkToken($token, OqlToken::KEYWORD, $value);
    00175         }
    00176
    00177         protected function checkIdentifier($token)
    00178         {
    00179             if ($token instanceof OqlToken) {
    00180                 if ($token->getType() == OqlToken::IDENTIFIER)
    00181                     return true;
    00182
    00183                 // fix token value if identifier name is equal to
    00184                 // reserved word or aggregate function name
    00185                 elseif (
    00186                     $token->getType() == OqlToken::KEYWORD
    00187                     || $token->getType() == OqlToken::AGGREGATE_FUNCTION
    00188                 ) {
    00189                     $token->setValue($token->getRawValue());
    00190
    00191                     return true;
    00192                 }
    00193             }
    00194
    00195             return false;
    00196         }
    00197
    00198         protected function checkConstant($token)
    00199         {
    00200             return
    00201                 $token instanceof OqlToken
    00202                 && (
    00203                     $token->getType() == OqlToken::STRING
    00204                     || $token->getType() == OqlToken::NUMBER
    00205                     || $token->getType() == OqlToken::BOOLEAN
    00206                     || $token->getType() == OqlToken::NULL
    00207                     || $token->getType() == OqlToken::SUBSTITUTION
    00208                 );
    00209         }
    00210
    00211         protected function checkUnaryMinus($token)
    00212         {
    00213             return $this->checkToken($token, OqlToken::ARITHMETIC_OPERATOR, '-');
    00214         }
    00215
    00219         protected function checkParentheses($message = null)
    00220         {
    00221             if ($this->openParentheses(false, $message)) {
    00222                 $this->error("unexpected '('", $message);
    00223
    00224             } elseif ($this->closeParentheses(false, $message)) {
    00225                 $this->error("unexpected ')'", $message);
    00226             }
    00227
    00228             if ($this->parentheses > 0)
    00229                 $this->error("unexpected '('", $message);
    00230
    00231             return true;
    00232         }
    00233
    00237         protected function openParentheses($required, $message = null)
    00238         {
    00239             if (
    00240                 $this->checkToken($this->tokenizer->peek(), OqlToken::PARENTHESES, '(')
    00241             ) {
    00242                 $this->tokenizer->next();
    00243                 $this->parentheses++;
    00244
    00245                 return true;
    00246
    00247             } elseif ($required) {
    00248                 $this->error("expecting ')'", $message);
    00249             }
    00250
    00251             return false;
    00252         }
    00253
    00257         protected function closeParentheses($required, $message = null)
    00258         {
    00259             if (
    00260                 $this->checkToken($this->tokenizer->peek(), OqlToken::PARENTHESES, ')')
    00261             ) {
    00262                 $this->tokenizer->next();
    00263                 $this->parentheses--;
    00264                 if ($this->parentheses < 0)
    00265                     $this->error("unexpected ')'", $message);
    00266
    00267                 return true;
    00268
    00269             } elseif ($required) {
    00270                 $this->error("expecting ')'", $message);
    00271             }
    00272
    00273             return false;
    00274         }
    00275
    00279         protected function getIdentifierExpression()
    00280         {
    00281             if ($isUnaryMinus = $this->checkUnaryMinus($this->tokenizer->peek()))
    00282                 $this->tokenizer->next();
    00283
    00284             $token = $this->tokenizer->peek();
    00285
    00286             if ($this->checkIdentifier($token)) {
    00287                 $this->tokenizer->next();
    00288
    00289                 return $this->makeQuerySignedExpression($token, $isUnaryMinus);
    00290             }
    00291
    00292             return null;
    00293         }
    00294
    00298         protected function getConstantExpression()
    00299         {
    00300             if ($isUnaryMinus = $this->checkUnaryMinus($this->tokenizer->peek()))
    00301                 $this->tokenizer->next();
    00302
    00303             $token = $this->tokenizer->peek();
    00304
    00305             if (
    00306                 $token instanceof OqlToken
    00307                 && (
    00308                     (
    00309                         !$isUnaryMinus
    00310                         && (
    00311                             $token->getType() == OqlToken::STRING
    00312                             || $token->getType() == OqlToken::BOOLEAN
    00313                             || $token->getType() == OqlToken::NULL
    00314                         )
    00315                     ) || (
    00316                         $token->getType() == OqlToken::NUMBER
    00317                         || $token->getType() == OqlToken::SUBSTITUTION
    00318                     )
    00319                 )
    00320             ) {
    00321                 $this->tokenizer->next();
    00322
    00323                 return $this->makeQuerySignedExpression($token, $isUnaryMinus);
    00324             }
    00325
    00326             return null;
    00327         }
    00328
    00332         protected function getLogicExpression(
    00333             $priority = self::LOGIC_PRIORITY_LOWEST
    00334         )
    00335         {
    00336             $expression = null;
    00337
    00338             // terminal boolean expressions
    00339             if ($priority == self::LOGIC_PRIORITY_TERMINAL) {
    00340                 $token = $this->tokenizer->peek();
    00341                 if (!$token)
    00342                     return null;
    00343
    00344                 // arithmetic expression
    00345                 if ($this->isArithmeticExpression())
    00346                     return $this->getArithmeticExpression();
    00347
    00348                 // parentheses
    00349                 if ($this->openParentheses(false)) {
    00350                     $expression = $this->getLogicExpression();
    00351                     $this->closeParentheses(true, 'in expression');
    00352
    00353                     return $expression;
    00354                 }
    00355
    00356                 // prefix unary 'not'
    00357                 if ($this->checkKeyword($token, 'not')) {
    00358                     $this->tokenizer->next();
    00359
    00360                     if (
    00361                         $argument = $this->getLogicExpression(self::LOGIC_PRIORITY_UNARY_NOT)
    00362                     ) {
    00363                         return $this->makeQueryExpression(
    00364                             self::$classMap[self::PREFIX_UNARY_EXPRESSION],
    00365                             PrefixUnaryExpression::NOT,
    00366                             $argument
    00367                         );
    00368
    00369                     } else {
    00370                         $this->error('expecting argument in expression: not');
    00371                     }
    00372                 }
    00373
    00374                 // first argument
    00375                 if (
    00376                     !($expression = $this->getIdentifierExpression())
    00377                     && !($expression = $this->getConstantExpression())
    00378                 ) {
    00379                     $this->error(
    00380                         'expecting first argument in expression:',
    00381                         $this->getTokenValue($this->tokenizer->peek(), true)
    00382                     );
    00383                 }
    00384
    00385                 // not (like|ilike|between|similar to|in)
    00386                 $operator = $this->tokenizer->peek();
    00387                 if ($this->checkKeyword($operator, 'not')) {
    00388                     $this->tokenizer->next();
    00389                     $operator = $this->tokenizer->peek();
    00390                     $isNot = true;
    00391
    00392                 } else {
    00393                     $isNot = false;
    00394                 }
    00395
    00396                 // is ([not] null|true|false)
    00397                 if (
    00398                     !$isNot
    00399                     && $this->checkKeyword($operator, 'is')
    00400                 ) {
    00401                     $this->tokenizer->next();
    00402
    00403                     $logic = null;
    00404
    00405                     if ($this->checkKeyword($this->tokenizer->peek(), 'not')) {
    00406                         $this->tokenizer->next();
    00407                         $isNot = true;
    00408
    00409                     } else {
    00410                         $isNot = false;
    00411                     }
    00412
    00413                     if ($this->checkToken($this->tokenizer->peek(), OqlToken::NULL)) {
    00414                         $this->tokenizer->next();
    00415                         $logic = $isNot
    00416                             ? PostfixUnaryExpression::IS_NOT_NULL
    00417                             : PostfixUnaryExpression::IS_NULL;
    00418
    00419                     } elseif (
    00420                         !$isNot
    00421                         && $this->checkToken($this->tokenizer->peek(), OqlToken::BOOLEAN)
    00422                     ) {
    00423                         $logic = $this->tokenizer->next()->getValue() === true
    00424                             ? PostfixUnaryExpression::IS_TRUE
    00425                             : PostfixUnaryExpression::IS_FALSE;
    00426                     }
    00427
    00428                     if ($logic) {
    00429                         return $this->makeQueryExpression(
    00430                             self::$classMap[self::POSTFIX_UNARY_EXPRESSION],
    00431                             $expression,
    00432                             $logic
    00433                         );
    00434
    00435                     } else {
    00436                         $this->error("expecting 'null', 'not null', 'true' or 'false'");
    00437                     }
    00438
    00439                 // [not] in
    00440                 } elseif ($this->checkKeyword($operator, 'in')) {
    00441                     $isNotString = ($isNot ? 'not ' : '');
    00442                     $this->tokenizer->next();
    00443
    00444                     $this->openParentheses(true, 'in expression: '.$isNotString.'in');
    00445
    00446                     $list = $this->getCommaSeparatedList(
    00447                         array($this, 'getConstantExpression'),
    00448                         'expecting constant or substitution in expression: '
    00449                         .$isNotString.'in'
    00450                     );
    00451
    00452                     if (is_array($list) && count($list) == 1)
    00453                         $list = reset($list);
    00454
    00455                     $this->closeParentheses(true, 'in expression: '.$isNotString.'in');
    00456
    00457                     return new OqlInExpression(
    00458                         $expression,
    00459                         $this->makeQueryParameter($list),
    00460                         $isNot ? InExpression::NOT_IN : InExpression::IN
    00461                     );
    00462
    00463                 // [not] (like|ilike|similar to)
    00464                 } elseif (
    00465                     $this->checkKeyword($operator, array('like', 'ilike', 'similar to'))
    00466                 ) {
    00467                     $this->tokenizer->next();
    00468
    00469                     $isNotString = ($isNot ? 'not ' : '');
    00470                     $argument = $this->tokenizer->next();
    00471
    00472                     if (
    00473                         $this->checkToken($argument, OqlToken::STRING)
    00474                         || $this->checkToken($argument, OqlToken::SUBSTITUTION)
    00475                     ) {
    00476                         return $this->makeQueryExpression(
    00477                             self::$classMap[self::BINARY_EXPRESSION],
    00478                             $expression,
    00479                             $argument,
    00480                             self::$binaryOperatorMap[
    00481                                 $isNotString
    00482                                 .$this->getTokenValue($operator)
    00483                             ]
    00484                         );
    00485
    00486                     } else {
    00487                         $this->error(
    00488                             'expecting string constant or substitution:',
    00489                             $isNotString.$this->getTokenValue($operator, true)
    00490                         );
    00491                     }
    00492
    00493                 // between
    00494                 } elseif (
    00495                     !$isNot
    00496                     && $this->checkKeyword($operator, 'between')
    00497                 ) {
    00498                     $this->tokenizer->next();
    00499
    00500                     if (
    00501                         ($argument1 = $this->getIdentifierExpression())
    00502                         || ($argument1 = $this->getConstantExpression())
    00503                     ) {
    00504                         if ($this->checkKeyword($this->tokenizer->next(), 'and')) {
    00505                             if (
    00506                                 ($argument2 = $this->getIdentifierExpression())
    00507                                 || ($argument2 = $this->getConstantExpression())
    00508                             ) {
    00509                                 return $this->makeQueryExpression(
    00510                                     self::$classMap[self::BETWEEN_EXPRESSION],
    00511                                     $expression,
    00512                                     $argument1,
    00513                                     $argument2
    00514                                 );
    00515
    00516                             } else {
    00517                                 $this->error(
    00518                                     'expecting second argument in expression: between'
    00519                                 );
    00520                             }
    00521
    00522                         } else {
    00523                             $this->error(
    00524                                 "expecting 'and' in expression: between"
    00525                             );
    00526                         }
    00527
    00528                     } else {
    00529                         $this->error(
    00530                             'expecting first argument in expression: between'
    00531                         );
    00532                     }
    00533                 }
    00534
    00535                 if ($isNot)
    00536                     $this->error('expecting in, like, ilike or similar to');
    00537
    00538             // and|or|comparison expression chain
    00539             } else {
    00540                 $operatorList = self::$logicPriorityMap[$priority];
    00541                 $higherPriority = $priority + 1;
    00542
    00543                 if (!($expression = $this->getLogicExpression($higherPriority))) {
    00544                     $this->error(
    00545                         'expecting first argument in expression:',
    00546                         is_array($operatorList)
    00547                             ? implode('|', $operatorList)
    00548                             : $operatorList
    00549                     );
    00550                 }
    00551
    00552                 $tokenType =
    00553                     $priority == self::LOGIC_PRIORITY_OR
    00554                     || $priority == self::LOGIC_PRIORITY_AND
    00555                         ? OqlToken::KEYWORD
    00556                         : OqlToken::COMPARISON_OPERATOR;
    00557
    00558                 while (
    00559                     $this->checkToken(
    00560                         $this->tokenizer->peek(),
    00561                         $tokenType,
    00562                         $operatorList
    00563                     )
    00564                 ) {
    00565                     $operator = $this->tokenizer->next();
    00566
    00567                     if ($expression2 = $this->getLogicExpression($higherPriority)) {
    00568                         $expression = $this->makeQueryExpression(
    00569                             self::$classMap[self::BINARY_EXPRESSION],
    00570                             $expression,
    00571                             $expression2,
    00572                             self::$binaryOperatorMap[$operator->getValue()]
    00573                         );
    00574
    00575                     } else {
    00576                         $this->error(
    00577                             'expecting second argument in expression:',
    00578                             $this->getTokenValue($operator, true)
    00579                         );
    00580                     }
    00581                 }
    00582             }
    00583
    00584             return $expression;
    00585         }
    00586
    00590         protected function getArithmeticExpression(
    00591             $priority = self::ARITHMETIC_PRIORITY_LOWEST
    00592         )
    00593         {
    00594             // terminal arithmetic expressions
    00595             if ($priority == self::ARITHMETIC_PRIORITY_TERMINAL) {
    00596                 $token = $this->tokenizer->peek();
    00597                 if (!$token)
    00598                     return null;
    00599
    00600                 // unary minus
    00601                 if ($isUnaryMinus = $this->checkUnaryMinus($token))
    00602                     $this->tokenizer->next();
    00603
    00604                 // parentheses
    00605                 if ($this->openParentheses(false)) {
    00606                     $expression = $this->getArithmeticExpression();
    00607                     $this->closeParentheses(true, 'in expression');
    00608
    00609                 // argument
    00610                 } elseif ($expression = $this->getArithmeticArgumentExpression()) {
    00611                     // $expression
    00612
    00613                 } else {
    00614                     $this->error(
    00615                         'expecting argument in expression:',
    00616                         $this->getTokenValue($this->tokenizer->peek(), true)
    00617                     );
    00618                 }
    00619
    00620                 $expression = $this->makeQuerySignedExpression($expression, $isUnaryMinus);
    00621
    00622             // +|-|*|/ expression chain
    00623             } else {
    00624                 $operatorList = self::$arithmeticPriorityMap[$priority];
    00625                 $higherPriority = $priority + 1;
    00626
    00627                 if (!($expression = $this->getArithmeticExpression($higherPriority))) {
    00628                     $this->error(
    00629                         'expecting first argument in expression:',
    00630                         implode('|', $operatorList)
    00631                     );
    00632                 }
    00633
    00634                 while (
    00635                     $this->checkToken(
    00636                         $this->tokenizer->peek(),
    00637                         OqlToken::ARITHMETIC_OPERATOR,
    00638                         $operatorList
    00639                     )
    00640                 ) {
    00641                     $operator = $this->tokenizer->next();
    00642
    00643                     if ($expression2 = $this->getArithmeticExpression($higherPriority)) {
    00644                         $expression = $this->makeQueryExpression(
    00645                             self::$classMap[self::BINARY_EXPRESSION],
    00646                             $expression,
    00647                             $expression2,
    00648                             self::$binaryOperatorMap[$operator->getValue()]
    00649                         );
    00650
    00651                     } else {
    00652                         $this->error(
    00653                             'expecting second argument in expression:',
    00654                             $this->getTokenValue($operator, true)
    00655                         );
    00656                     }
    00657                 }
    00658             }
    00659
    00660             return $expression;
    00661         }
    00662
    00663         protected function getCommaSeparatedList($callback, $message)
    00664         {
    00665             $isComma = false;
    00666             $list = array();
    00667
    00668             do {
    00669                 if ($isComma)
    00670                     $this->tokenizer->next();
    00671
    00672                 if ($argument = call_user_func($callback))
    00673                     $list[] = $argument;
    00674                 else
    00675                     $this->error($message);
    00676
    00677             } while (
    00678                 $isComma
    00679                 = $this->checkToken($this->tokenizer->peek(), OqlToken::PUNCTUATION, ',')
    00680             );
    00681
    00682             return $list;
    00683         }
    00684
    00688         protected function makeQueryExpression($className /*, ... */)
    00689         {
    00690             $expression = OqlQueryExpression::create()->
    00691                 setClassName($className);
    00692
    00693             $arguments = func_get_args();
    00694             reset($arguments);
    00695             $argument = next($arguments);
    00696
    00697             while ($argument) {
    00698                 $expression->addParameter(
    00699                     $this->makeQueryParameter($argument)
    00700                 );
    00701
    00702                 $argument = next($arguments);
    00703             }
    00704
    00705             return $expression;
    00706         }
    00707
    00711         protected function makeQuerySignedExpression($argument, $isUnaryMinus)
    00712         {
    00713             $expression = $this->makeQueryParameter($argument);
    00714             if ($isUnaryMinus)
    00715                 $expression = new OqlPrefixMinusExpression($expression);
    00716
    00717             return $expression;
    00718         }
    00719
    00723         protected function makeQueryParameter($argument)
    00724         {
    00725             if ($argument instanceof OqlQueryParameter) {
    00726                 return $argument;
    00727
    00728             } elseif ($argument instanceof OqlToken) {
    00729                 return OqlQueryParameter::create()->
    00730                     setValue($argument->getValue())->
    00731                     setBindable($argument->getType() == OqlToken::SUBSTITUTION);
    00732
    00733             } else {
    00734                 return OqlQueryParameter::create()->
    00735                     setValue($argument);
    00736             }
    00737         }
    00738
    00742         protected function error($message, $extraMessage = null)
    00743         {
    00744             if ($extraMessage)
    00745                 $message .= ' '.$extraMessage;
    00746
    00747             throw new SyntaxErrorException(
    00748                 $message,
    00749                 $this->tokenizer->getLine(),
    00750                 $this->tokenizer->getPosition()
    00751             );
    00752         }
    00753
    00754         private function isArithmeticExpression()
    00755         {
    00756             $index = $this->tokenizer->getIndex();
    00757
    00758             // skip open parentheses
    00759             while (
    00760                 $this->checkToken($this->tokenizer->peek(), OqlToken::PARENTHESES, '(')
    00761             ) {
    00762                 $this->tokenizer->next();
    00763             }
    00764
    00765             // skip unary minus
    00766             if ($this->checkUnaryMinus($this->tokenizer->peek()))
    00767                 $this->tokenizer->next();
    00768
    00769             $result =
    00770                 $this->getArithmeticArgumentExpression()
    00771                 && $this->checkToken($this->tokenizer->peek(), OqlToken::ARITHMETIC_OPERATOR);
    00772
    00773             $this->tokenizer->setIndex($index);
    00774
    00775             return $result;
    00776         }
    00777
    00781         private function getArithmeticArgumentExpression()
    00782         {
    00783             $token = $this->tokenizer->peek();
    00784
    00785             if (
    00786                 $this->checkIdentifier($token)
    00787                 || $this->checkToken($token, OqlToken::NUMBER)
    00788                 || $this->checkToken($token, OqlToken::SUBSTITUTION)
    00789             ) {
    00790                 $this->tokenizer->next();
    00791
    00792                 return $this->makeQueryParameter($token);
    00793             }
    00794
    00795             return null;
    00796         }
    00797     }
    00798 ?>