Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Query / Parser.php
1 <?php
2 /*
3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHARNTABILITY AND FITNESS FOR
6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * This software consists of voluntary contributions made by many individuals
16  * and is licensed under the MIT license. For more information, see
17  * <http://www.doctrine-project.org>.
18  */
19
20 namespace Doctrine\ORM\Query;
21
22 use Doctrine\ORM\Query;
23 use Doctrine\ORM\Mapping\ClassMetadata;
24
25 /**
26  * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
27  * Parses a DQL query, reports any errors in it, and generates an AST.
28  *
29  * @since   2.0
30  * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
31  * @author  Jonathan Wage <jonwage@gmail.com>
32  * @author  Roman Borschel <roman@code-factory.org>
33  * @author  Janne Vanhala <jpvanhal@cc.hut.fi>
34  */
35 class Parser
36 {
37     /** READ-ONLY: Maps BUILT-IN string function names to AST class names. */
38     private static $_STRING_FUNCTIONS = array(
39         'concat'    => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction',
40         'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction',
41         'trim'      => 'Doctrine\ORM\Query\AST\Functions\TrimFunction',
42         'lower'     => 'Doctrine\ORM\Query\AST\Functions\LowerFunction',
43         'upper'     => 'Doctrine\ORM\Query\AST\Functions\UpperFunction',
44         'identity'  => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction',
45     );
46
47     /** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
48     private static $_NUMERIC_FUNCTIONS = array(
49         'length'    => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
50         'locate'    => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
51         'abs'       => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
52         'sqrt'      => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
53         'mod'       => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
54         'size'      => 'Doctrine\ORM\Query\AST\Functions\SizeFunction',
55         'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction',
56         'bit_and'   => 'Doctrine\ORM\Query\AST\Functions\BitAndFunction',
57         'bit_or'    => 'Doctrine\ORM\Query\AST\Functions\BitOrFunction',
58     );
59
60     /** READ-ONLY: Maps BUILT-IN datetime function names to AST class names. */
61     private static $_DATETIME_FUNCTIONS = array(
62         'current_date'      => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction',
63         'current_time'      => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction',
64         'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction',
65         'date_add'          => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction',
66         'date_sub'          => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction',
67     );
68
69     /**
70      * Expressions that were encountered during parsing of identifiers and expressions
71      * and still need to be validated.
72      */
73     private $_deferredIdentificationVariables = array();
74     private $_deferredPartialObjectExpressions = array();
75     private $_deferredPathExpressions = array();
76     private $_deferredResultVariables = array();
77
78     /**
79      * The lexer.
80      *
81      * @var \Doctrine\ORM\Query\Lexer
82      */
83     private $_lexer;
84
85     /**
86      * The parser result.
87      *
88      * @var \Doctrine\ORM\Query\ParserResult
89      */
90     private $_parserResult;
91
92     /**
93      * The EntityManager.
94      *
95      * @var EnityManager
96      */
97     private $_em;
98
99     /**
100      * The Query to parse.
101      *
102      * @var Query
103      */
104     private $_query;
105
106     /**
107      * Map of declared query components in the parsed query.
108      *
109      * @var array
110      */
111     private $_queryComponents = array();
112
113     /**
114      * Keeps the nesting level of defined ResultVariables
115      *
116      * @var integer
117      */
118     private $_nestingLevel = 0;
119
120     /**
121      * Any additional custom tree walkers that modify the AST.
122      *
123      * @var array
124      */
125     private $_customTreeWalkers = array();
126
127     /**
128      * The custom last tree walker, if any, that is responsible for producing the output.
129      *
130      * @var TreeWalker
131      */
132     private $_customOutputWalker;
133
134     /**
135      * @var array
136      */
137     private $_identVariableExpressions = array();
138
139     /**
140      * Check if a function is internally defined. Used to prevent overwriting
141      * of built-in functions through user-defined functions.
142      *
143      * @param string $functionName
144      * @return bool
145      */
146     static public function isInternalFunction($functionName)
147     {
148         $functionName = strtolower($functionName);
149
150         return isset(self::$_STRING_FUNCTIONS[$functionName])
151             || isset(self::$_DATETIME_FUNCTIONS[$functionName])
152             || isset(self::$_NUMERIC_FUNCTIONS[$functionName]);
153     }
154
155     /**
156      * Creates a new query parser object.
157      *
158      * @param Query $query The Query to parse.
159      */
160     public function __construct(Query $query)
161     {
162         $this->_query        = $query;
163         $this->_em           = $query->getEntityManager();
164         $this->_lexer        = new Lexer($query->getDql());
165         $this->_parserResult = new ParserResult();
166     }
167
168     /**
169      * Sets a custom tree walker that produces output.
170      * This tree walker will be run last over the AST, after any other walkers.
171      *
172      * @param string $className
173      */
174     public function setCustomOutputTreeWalker($className)
175     {
176         $this->_customOutputWalker = $className;
177     }
178
179     /**
180      * Adds a custom tree walker for modifying the AST.
181      *
182      * @param string $className
183      */
184     public function addCustomTreeWalker($className)
185     {
186         $this->_customTreeWalkers[] = $className;
187     }
188
189     /**
190      * Gets the lexer used by the parser.
191      *
192      * @return \Doctrine\ORM\Query\Lexer
193      */
194     public function getLexer()
195     {
196         return $this->_lexer;
197     }
198
199     /**
200      * Gets the ParserResult that is being filled with information during parsing.
201      *
202      * @return \Doctrine\ORM\Query\ParserResult
203      */
204     public function getParserResult()
205     {
206         return $this->_parserResult;
207     }
208
209     /**
210      * Gets the EntityManager used by the parser.
211      *
212      * @return EntityManager
213      */
214     public function getEntityManager()
215     {
216         return $this->_em;
217     }
218
219     /**
220      * Parse and build AST for the given Query.
221      *
222      * @return \Doctrine\ORM\Query\AST\SelectStatement |
223      *         \Doctrine\ORM\Query\AST\UpdateStatement |
224      *         \Doctrine\ORM\Query\AST\DeleteStatement
225      */
226     public function getAST()
227     {
228         // Parse & build AST
229         $AST = $this->QueryLanguage();
230
231         // Process any deferred validations of some nodes in the AST.
232         // This also allows post-processing of the AST for modification purposes.
233         $this->_processDeferredIdentificationVariables();
234
235         if ($this->_deferredPartialObjectExpressions) {
236             $this->_processDeferredPartialObjectExpressions();
237         }
238
239         if ($this->_deferredPathExpressions) {
240             $this->_processDeferredPathExpressions($AST);
241         }
242
243         if ($this->_deferredResultVariables) {
244             $this->_processDeferredResultVariables();
245         }
246
247         $this->_processRootEntityAliasSelected();
248
249         // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot!
250         $this->fixIdentificationVariableOrder($AST);
251
252         return $AST;
253     }
254
255     /**
256      * Attempts to match the given token with the current lookahead token.
257      *
258      * If they match, updates the lookahead token; otherwise raises a syntax
259      * error.
260      *
261      * @param int token type
262      * @return void
263      * @throws QueryException If the tokens dont match.
264      */
265     public function match($token)
266     {
267         $lookaheadType = $this->_lexer->lookahead['type'];
268
269         // short-circuit on first condition, usually types match
270         if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) {
271             $this->syntaxError($this->_lexer->getLiteral($token));
272         }
273
274         $this->_lexer->moveNext();
275     }
276
277     /**
278      * Free this parser enabling it to be reused
279      *
280      * @param boolean $deep     Whether to clean peek and reset errors
281      * @param integer $position Position to reset
282      */
283     public function free($deep = false, $position = 0)
284     {
285         // WARNING! Use this method with care. It resets the scanner!
286         $this->_lexer->resetPosition($position);
287
288         // Deep = true cleans peek and also any previously defined errors
289         if ($deep) {
290             $this->_lexer->resetPeek();
291         }
292
293         $this->_lexer->token = null;
294         $this->_lexer->lookahead = null;
295     }
296
297     /**
298      * Parses a query string.
299      *
300      * @return ParserResult
301      */
302     public function parse()
303     {
304         $AST = $this->getAST();
305
306         if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
307             $this->_customTreeWalkers = $customWalkers;
308         }
309
310         if (($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) {
311             $this->_customOutputWalker = $customOutputWalker;
312         }
313
314         // Run any custom tree walkers over the AST
315         if ($this->_customTreeWalkers) {
316             $treeWalkerChain = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents);
317
318             foreach ($this->_customTreeWalkers as $walker) {
319                 $treeWalkerChain->addTreeWalker($walker);
320             }
321
322             switch (true) {
323                 case ($AST instanceof AST\UpdateStatement):
324                     $treeWalkerChain->walkUpdateStatement($AST);
325                     break;
326
327                 case ($AST instanceof AST\DeleteStatement):
328                     $treeWalkerChain->walkDeleteStatement($AST);
329                     break;
330
331                 case ($AST instanceof AST\SelectStatement):
332                 default:
333                     $treeWalkerChain->walkSelectStatement($AST);
334             }
335         }
336
337         $outputWalkerClass = $this->_customOutputWalker ?: __NAMESPACE__ . '\SqlWalker';
338         $outputWalker      = new $outputWalkerClass($this->_query, $this->_parserResult, $this->_queryComponents);
339
340         // Assign an SQL executor to the parser result
341         $this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
342
343         return $this->_parserResult;
344     }
345
346     /**
347      * Fix order of identification variables.
348      *
349      * They have to appear in the select clause in the same order as the
350      * declarations (from ... x join ... y join ... z ...) appear in the query
351      * as the hydration process relies on that order for proper operation.
352      *
353      * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
354      * @return void
355      */
356     private function fixIdentificationVariableOrder($AST)
357     {
358         if (count($this->_identVariableExpressions) <= 1) {
359             return;
360         }
361
362         foreach ($this->_queryComponents as $dqlAlias => $qComp) {
363             if ( ! isset($this->_identVariableExpressions[$dqlAlias])) {
364                 continue;
365             }
366
367             $expr = $this->_identVariableExpressions[$dqlAlias];
368             $key  = array_search($expr, $AST->selectClause->selectExpressions);
369
370             unset($AST->selectClause->selectExpressions[$key]);
371
372             $AST->selectClause->selectExpressions[] = $expr;
373         }
374     }
375
376     /**
377      * Generates a new syntax error.
378      *
379      * @param string $expected Expected string.
380      * @param array $token Got token.
381      *
382      * @throws \Doctrine\ORM\Query\QueryException
383      */
384     public function syntaxError($expected = '', $token = null)
385     {
386         if ($token === null) {
387             $token = $this->_lexer->lookahead;
388         }
389
390         $tokenPos = (isset($token['position'])) ? $token['position'] : '-1';
391
392         $message  = "line 0, col {$tokenPos}: Error: ";
393         $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected ';
394         $message .= ($this->_lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'";
395
396         throw QueryException::syntaxError($message, QueryException::dqlError($this->_query->getDQL()));
397     }
398
399     /**
400      * Generates a new semantical error.
401      *
402      * @param string $message Optional message.
403      * @param array $token Optional token.
404      *
405      * @throws \Doctrine\ORM\Query\QueryException
406      */
407     public function semanticalError($message = '', $token = null)
408     {
409         if ($token === null) {
410             $token = $this->_lexer->lookahead;
411         }
412
413         // Minimum exposed chars ahead of token
414         $distance = 12;
415
416         // Find a position of a final word to display in error string
417         $dql    = $this->_query->getDql();
418         $length = strlen($dql);
419         $pos    = $token['position'] + $distance;
420         $pos    = strpos($dql, ' ', ($length > $pos) ? $pos : $length);
421         $length = ($pos !== false) ? $pos - $token['position'] : $distance;
422
423         $tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1';
424         $tokenStr = substr($dql, $token['position'], $length);
425
426         // Building informative message
427         $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message;
428
429         throw QueryException::semanticalError($message, QueryException::dqlError($this->_query->getDQL()));
430     }
431
432     /**
433      * Peek beyond the matched closing parenthesis and return the first token after that one.
434      *
435      * @param boolean $resetPeek Reset peek after finding the closing parenthesis
436      * @return array
437      */
438     private function _peekBeyondClosingParenthesis($resetPeek = true)
439     {
440         $token = $this->_lexer->peek();
441         $numUnmatched = 1;
442
443         while ($numUnmatched > 0 && $token !== null) {
444             switch ($token['type']) {
445                 case Lexer::T_OPEN_PARENTHESIS:
446                     ++$numUnmatched;
447                     break;
448
449                 case Lexer::T_CLOSE_PARENTHESIS:
450                     --$numUnmatched;
451                     break;
452
453                 default:
454                     // Do nothing
455             }
456
457             $token = $this->_lexer->peek();
458         }
459
460         if ($resetPeek) {
461             $this->_lexer->resetPeek();
462         }
463
464         return $token;
465     }
466
467     /**
468      * Checks if the given token indicates a mathematical operator.
469      *
470      * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise.
471      */
472     private function _isMathOperator($token)
473     {
474         return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY));
475     }
476
477     /**
478      * Checks if the next-next (after lookahead) token starts a function.
479      *
480      * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise.
481      */
482     private function _isFunction()
483     {
484         $peek     = $this->_lexer->peek();
485         $nextpeek = $this->_lexer->peek();
486
487         $this->_lexer->resetPeek();
488
489         // We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function
490         return ($peek['type'] === Lexer::T_OPEN_PARENTHESIS && $nextpeek['type'] !== Lexer::T_SELECT);
491     }
492
493     /**
494      * Checks whether the given token type indicates an aggregate function.
495      *
496      * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise.
497      */
498     private function _isAggregateFunction($tokenType)
499     {
500         return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT));
501     }
502
503     /**
504      * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.
505      *
506      * @return boolean
507      */
508     private function _isNextAllAnySome()
509     {
510         return in_array($this->_lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME));
511     }
512
513     /**
514      * Validates that the given <tt>IdentificationVariable</tt> is semantically correct.
515      * It must exist in query components list.
516      *
517      * @return void
518      */
519     private function _processDeferredIdentificationVariables()
520     {
521         foreach ($this->_deferredIdentificationVariables as $deferredItem) {
522             $identVariable = $deferredItem['expression'];
523
524             // Check if IdentificationVariable exists in queryComponents
525             if ( ! isset($this->_queryComponents[$identVariable])) {
526                 $this->semanticalError(
527                     "'$identVariable' is not defined.", $deferredItem['token']
528                 );
529             }
530
531             $qComp = $this->_queryComponents[$identVariable];
532
533             // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
534             if ( ! isset($qComp['metadata'])) {
535                 $this->semanticalError(
536                     "'$identVariable' does not point to a Class.", $deferredItem['token']
537                 );
538             }
539
540             // Validate if identification variable nesting level is lower or equal than the current one
541             if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
542                 $this->semanticalError(
543                     "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token']
544                 );
545             }
546         }
547     }
548
549     /**
550      * Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.
551      * It must exist in query components list.
552      *
553      * @return void
554      */
555     private function _processDeferredPartialObjectExpressions()
556     {
557         foreach ($this->_deferredPartialObjectExpressions as $deferredItem) {
558             $expr = $deferredItem['expression'];
559             $class = $this->_queryComponents[$expr->identificationVariable]['metadata'];
560
561             foreach ($expr->partialFieldSet as $field) {
562                 if (isset($class->fieldMappings[$field])) {
563                     continue;
564                 }
565
566                 $this->semanticalError(
567                     "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token']
568                 );
569             }
570
571             if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) {
572                 $this->semanticalError(
573                     "The partial field selection of class " . $class->name . " must contain the identifier.",
574                     $deferredItem['token']
575                 );
576             }
577         }
578     }
579
580     /**
581      * Validates that the given <tt>ResultVariable</tt> is semantically correct.
582      * It must exist in query components list.
583      *
584      * @return void
585      */
586     private function _processDeferredResultVariables()
587     {
588         foreach ($this->_deferredResultVariables as $deferredItem) {
589             $resultVariable = $deferredItem['expression'];
590
591             // Check if ResultVariable exists in queryComponents
592             if ( ! isset($this->_queryComponents[$resultVariable])) {
593                 $this->semanticalError(
594                     "'$resultVariable' is not defined.", $deferredItem['token']
595                 );
596             }
597
598             $qComp = $this->_queryComponents[$resultVariable];
599
600             // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
601             if ( ! isset($qComp['resultVariable'])) {
602                 $this->semanticalError(
603                     "'$resultVariable' does not point to a ResultVariable.", $deferredItem['token']
604                 );
605             }
606
607             // Validate if identification variable nesting level is lower or equal than the current one
608             if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
609                 $this->semanticalError(
610                     "'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token']
611                 );
612             }
613         }
614     }
615
616     /**
617      * Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules:
618      *
619      * AssociationPathExpression             ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
620      * SingleValuedPathExpression            ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
621      * StateFieldPathExpression              ::= IdentificationVariable "." StateField
622      * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
623      * CollectionValuedPathExpression        ::= IdentificationVariable "." CollectionValuedAssociationField
624      *
625      * @param array $deferredItem
626      * @param mixed $AST
627      */
628     private function _processDeferredPathExpressions($AST)
629     {
630         foreach ($this->_deferredPathExpressions as $deferredItem) {
631             $pathExpression = $deferredItem['expression'];
632
633             $qComp = $this->_queryComponents[$pathExpression->identificationVariable];
634             $class = $qComp['metadata'];
635
636             if (($field = $pathExpression->field) === null) {
637                 $field = $pathExpression->field = $class->identifier[0];
638             }
639
640             // Check if field or association exists
641             if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
642                 $this->semanticalError(
643                     'Class ' . $class->name . ' has no field or association named ' . $field,
644                     $deferredItem['token']
645                 );
646             }
647
648             $fieldType = AST\PathExpression::TYPE_STATE_FIELD;
649
650             if (isset($class->associationMappings[$field])) {
651                 $assoc = $class->associationMappings[$field];
652
653                 $fieldType = ($assoc['type'] & ClassMetadata::TO_ONE)
654                     ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
655                     : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
656             }
657
658             // Validate if PathExpression is one of the expected types
659             $expectedType = $pathExpression->expectedType;
660
661             if ( ! ($expectedType & $fieldType)) {
662                 // We need to recognize which was expected type(s)
663                 $expectedStringTypes = array();
664
665                 // Validate state field type
666                 if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) {
667                     $expectedStringTypes[] = 'StateFieldPathExpression';
668                 }
669
670                 // Validate single valued association (*-to-one)
671                 if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {
672                     $expectedStringTypes[] = 'SingleValuedAssociationField';
673                 }
674
675                 // Validate single valued association (*-to-many)
676                 if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
677                     $expectedStringTypes[] = 'CollectionValuedAssociationField';
678                 }
679
680                 // Build the error message
681                 $semanticalError  = 'Invalid PathExpression. ';
682                 $semanticalError .= (count($expectedStringTypes) == 1)
683                     ? 'Must be a ' . $expectedStringTypes[0] . '.'
684                     : implode(' or ', $expectedStringTypes) . ' expected.';
685
686                 $this->semanticalError($semanticalError, $deferredItem['token']);
687             }
688
689             // We need to force the type in PathExpression
690             $pathExpression->type = $fieldType;
691         }
692     }
693
694     private function _processRootEntityAliasSelected()
695     {
696         if ( ! count($this->_identVariableExpressions)) {
697             return;
698         }
699
700         $foundRootEntity = false;
701
702         foreach ($this->_identVariableExpressions as $dqlAlias => $expr) {
703             if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) {
704                 $foundRootEntity = true;
705             }
706         }
707
708         if ( ! $foundRootEntity) {
709             $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
710         }
711     }
712
713     /**
714      * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
715      *
716      * @return \Doctrine\ORM\Query\AST\SelectStatement |
717      *         \Doctrine\ORM\Query\AST\UpdateStatement |
718      *         \Doctrine\ORM\Query\AST\DeleteStatement
719      */
720     public function QueryLanguage()
721     {
722         $this->_lexer->moveNext();
723
724         switch ($this->_lexer->lookahead['type']) {
725             case Lexer::T_SELECT:
726                 $statement = $this->SelectStatement();
727                 break;
728
729             case Lexer::T_UPDATE:
730                 $statement = $this->UpdateStatement();
731                 break;
732
733             case Lexer::T_DELETE:
734                 $statement = $this->DeleteStatement();
735                 break;
736
737             default:
738                 $this->syntaxError('SELECT, UPDATE or DELETE');
739                 break;
740         }
741
742         // Check for end of string
743         if ($this->_lexer->lookahead !== null) {
744             $this->syntaxError('end of string');
745         }
746
747         return $statement;
748     }
749
750     /**
751      * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
752      *
753      * @return \Doctrine\ORM\Query\AST\SelectStatement
754      */
755     public function SelectStatement()
756     {
757         $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause());
758
759         $selectStatement->whereClause   = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
760         $selectStatement->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
761         $selectStatement->havingClause  = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
762         $selectStatement->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
763
764         return $selectStatement;
765     }
766
767     /**
768      * UpdateStatement ::= UpdateClause [WhereClause]
769      *
770      * @return \Doctrine\ORM\Query\AST\UpdateStatement
771      */
772     public function UpdateStatement()
773     {
774         $updateStatement = new AST\UpdateStatement($this->UpdateClause());
775
776         $updateStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
777
778         return $updateStatement;
779     }
780
781     /**
782      * DeleteStatement ::= DeleteClause [WhereClause]
783      *
784      * @return \Doctrine\ORM\Query\AST\DeleteStatement
785      */
786     public function DeleteStatement()
787     {
788         $deleteStatement = new AST\DeleteStatement($this->DeleteClause());
789
790         $deleteStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
791
792         return $deleteStatement;
793     }
794
795     /**
796      * IdentificationVariable ::= identifier
797      *
798      * @return string
799      */
800     public function IdentificationVariable()
801     {
802         $this->match(Lexer::T_IDENTIFIER);
803
804         $identVariable = $this->_lexer->token['value'];
805
806         $this->_deferredIdentificationVariables[] = array(
807             'expression'   => $identVariable,
808             'nestingLevel' => $this->_nestingLevel,
809             'token'        => $this->_lexer->token,
810         );
811
812         return $identVariable;
813     }
814
815     /**
816      * AliasIdentificationVariable = identifier
817      *
818      * @return string
819      */
820     public function AliasIdentificationVariable()
821     {
822         $this->match(Lexer::T_IDENTIFIER);
823
824         $aliasIdentVariable = $this->_lexer->token['value'];
825         $exists = isset($this->_queryComponents[$aliasIdentVariable]);
826
827         if ($exists) {
828             $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->_lexer->token);
829         }
830
831         return $aliasIdentVariable;
832     }
833
834     /**
835      * AbstractSchemaName ::= identifier
836      *
837      * @return string
838      */
839     public function AbstractSchemaName()
840     {
841         $this->match(Lexer::T_IDENTIFIER);
842
843         $schemaName = ltrim($this->_lexer->token['value'], '\\');
844
845         if (strrpos($schemaName, ':') !== false) {
846             list($namespaceAlias, $simpleClassName) = explode(':', $schemaName);
847
848             $schemaName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
849         }
850
851         $exists = class_exists($schemaName, true);
852
853         if ( ! $exists) {
854             $this->semanticalError("Class '$schemaName' is not defined.", $this->_lexer->token);
855         }
856
857         return $schemaName;
858     }
859
860     /**
861      * AliasResultVariable ::= identifier
862      *
863      * @return string
864      */
865     public function AliasResultVariable()
866     {
867         $this->match(Lexer::T_IDENTIFIER);
868
869         $resultVariable = $this->_lexer->token['value'];
870         $exists = isset($this->_queryComponents[$resultVariable]);
871
872         if ($exists) {
873             $this->semanticalError("'$resultVariable' is already defined.", $this->_lexer->token);
874         }
875
876         return $resultVariable;
877     }
878
879     /**
880      * ResultVariable ::= identifier
881      *
882      * @return string
883      */
884     public function ResultVariable()
885     {
886         $this->match(Lexer::T_IDENTIFIER);
887
888         $resultVariable = $this->_lexer->token['value'];
889
890         // Defer ResultVariable validation
891         $this->_deferredResultVariables[] = array(
892             'expression'   => $resultVariable,
893             'nestingLevel' => $this->_nestingLevel,
894             'token'        => $this->_lexer->token,
895         );
896
897         return $resultVariable;
898     }
899
900     /**
901      * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
902      *
903      * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
904      */
905     public function JoinAssociationPathExpression()
906     {
907         $identVariable = $this->IdentificationVariable();
908
909         if ( ! isset($this->_queryComponents[$identVariable])) {
910             $this->semanticalError(
911                 'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.'
912             );
913         }
914
915         $this->match(Lexer::T_DOT);
916         $this->match(Lexer::T_IDENTIFIER);
917
918         $field = $this->_lexer->token['value'];
919
920         // Validate association field
921         $qComp = $this->_queryComponents[$identVariable];
922         $class = $qComp['metadata'];
923
924         if ( ! $class->hasAssociation($field)) {
925             $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
926         }
927
928         return new AST\JoinAssociationPathExpression($identVariable, $field);
929     }
930
931     /**
932      * Parses an arbitrary path expression and defers semantical validation
933      * based on expected types.
934      *
935      * PathExpression ::= IdentificationVariable "." identifier
936      *
937      * @param integer $expectedTypes
938      * @return \Doctrine\ORM\Query\AST\PathExpression
939      */
940     public function PathExpression($expectedTypes)
941     {
942         $identVariable = $this->IdentificationVariable();
943         $field = null;
944
945         if ($this->_lexer->isNextToken(Lexer::T_DOT)) {
946             $this->match(Lexer::T_DOT);
947             $this->match(Lexer::T_IDENTIFIER);
948
949             $field = $this->_lexer->token['value'];
950         }
951
952         // Creating AST node
953         $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field);
954
955         // Defer PathExpression validation if requested to be defered
956         $this->_deferredPathExpressions[] = array(
957             'expression'   => $pathExpr,
958             'nestingLevel' => $this->_nestingLevel,
959             'token'        => $this->_lexer->token,
960         );
961
962         return $pathExpr;
963     }
964
965     /**
966      * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
967      *
968      * @return \Doctrine\ORM\Query\AST\PathExpression
969      */
970     public function AssociationPathExpression()
971     {
972         return $this->PathExpression(
973             AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION |
974             AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION
975         );
976     }
977
978     /**
979      * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
980      *
981      * @return \Doctrine\ORM\Query\AST\PathExpression
982      */
983     public function SingleValuedPathExpression()
984     {
985         return $this->PathExpression(
986             AST\PathExpression::TYPE_STATE_FIELD |
987             AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
988         );
989     }
990
991     /**
992      * StateFieldPathExpression ::= IdentificationVariable "." StateField
993      *
994      * @return \Doctrine\ORM\Query\AST\PathExpression
995      */
996     public function StateFieldPathExpression()
997     {
998         return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD);
999     }
1000
1001     /**
1002      * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
1003      *
1004      * @return \Doctrine\ORM\Query\AST\PathExpression
1005      */
1006     public function SingleValuedAssociationPathExpression()
1007     {
1008         return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION);
1009     }
1010
1011     /**
1012      * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
1013      *
1014      * @return \Doctrine\ORM\Query\AST\PathExpression
1015      */
1016     public function CollectionValuedPathExpression()
1017     {
1018         return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);
1019     }
1020
1021     /**
1022      * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
1023      *
1024      * @return \Doctrine\ORM\Query\AST\SelectClause
1025      */
1026     public function SelectClause()
1027     {
1028         $isDistinct = false;
1029         $this->match(Lexer::T_SELECT);
1030
1031         // Check for DISTINCT
1032         if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
1033             $this->match(Lexer::T_DISTINCT);
1034
1035             $isDistinct = true;
1036         }
1037
1038         // Process SelectExpressions (1..N)
1039         $selectExpressions = array();
1040         $selectExpressions[] = $this->SelectExpression();
1041
1042         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1043             $this->match(Lexer::T_COMMA);
1044
1045             $selectExpressions[] = $this->SelectExpression();
1046         }
1047
1048         return new AST\SelectClause($selectExpressions, $isDistinct);
1049     }
1050
1051     /**
1052      * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
1053      *
1054      * @return \Doctrine\ORM\Query\AST\SimpleSelectClause
1055      */
1056     public function SimpleSelectClause()
1057     {
1058         $isDistinct = false;
1059         $this->match(Lexer::T_SELECT);
1060
1061         if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
1062             $this->match(Lexer::T_DISTINCT);
1063
1064             $isDistinct = true;
1065         }
1066
1067         return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct);
1068     }
1069
1070     /**
1071      * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}*
1072      *
1073      * @return \Doctrine\ORM\Query\AST\UpdateClause
1074      */
1075     public function UpdateClause()
1076     {
1077         $this->match(Lexer::T_UPDATE);
1078         $token = $this->_lexer->lookahead;
1079         $abstractSchemaName = $this->AbstractSchemaName();
1080
1081         if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1082             $this->match(Lexer::T_AS);
1083         }
1084
1085         $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1086
1087         $class = $this->_em->getClassMetadata($abstractSchemaName);
1088
1089         // Building queryComponent
1090         $queryComponent = array(
1091             'metadata'     => $class,
1092             'parent'       => null,
1093             'relation'     => null,
1094             'map'          => null,
1095             'nestingLevel' => $this->_nestingLevel,
1096             'token'        => $token,
1097         );
1098
1099         $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
1100
1101         $this->match(Lexer::T_SET);
1102
1103         $updateItems = array();
1104         $updateItems[] = $this->UpdateItem();
1105
1106         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1107             $this->match(Lexer::T_COMMA);
1108
1109             $updateItems[] = $this->UpdateItem();
1110         }
1111
1112         $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
1113         $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable;
1114
1115         return $updateClause;
1116     }
1117
1118     /**
1119      * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable
1120      *
1121      * @return \Doctrine\ORM\Query\AST\DeleteClause
1122      */
1123     public function DeleteClause()
1124     {
1125         $this->match(Lexer::T_DELETE);
1126
1127         if ($this->_lexer->isNextToken(Lexer::T_FROM)) {
1128             $this->match(Lexer::T_FROM);
1129         }
1130
1131         $token = $this->_lexer->lookahead;
1132         $deleteClause = new AST\DeleteClause($this->AbstractSchemaName());
1133
1134         if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1135             $this->match(Lexer::T_AS);
1136         }
1137
1138         $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1139
1140         $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;
1141         $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
1142
1143         // Building queryComponent
1144         $queryComponent = array(
1145             'metadata'     => $class,
1146             'parent'       => null,
1147             'relation'     => null,
1148             'map'          => null,
1149             'nestingLevel' => $this->_nestingLevel,
1150             'token'        => $token,
1151         );
1152
1153         $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
1154
1155         return $deleteClause;
1156     }
1157
1158     /**
1159      * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
1160      *
1161      * @return \Doctrine\ORM\Query\AST\FromClause
1162      */
1163     public function FromClause()
1164     {
1165         $this->match(Lexer::T_FROM);
1166
1167         $identificationVariableDeclarations = array();
1168         $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
1169
1170         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1171             $this->match(Lexer::T_COMMA);
1172
1173             $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
1174         }
1175
1176         return new AST\FromClause($identificationVariableDeclarations);
1177     }
1178
1179     /**
1180      * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
1181      *
1182      * @return \Doctrine\ORM\Query\AST\SubselectFromClause
1183      */
1184     public function SubselectFromClause()
1185     {
1186         $this->match(Lexer::T_FROM);
1187
1188         $identificationVariables = array();
1189         $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
1190
1191         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1192             $this->match(Lexer::T_COMMA);
1193
1194             $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
1195         }
1196
1197         return new AST\SubselectFromClause($identificationVariables);
1198     }
1199
1200     /**
1201      * WhereClause ::= "WHERE" ConditionalExpression
1202      *
1203      * @return \Doctrine\ORM\Query\AST\WhereClause
1204      */
1205     public function WhereClause()
1206     {
1207         $this->match(Lexer::T_WHERE);
1208
1209         return new AST\WhereClause($this->ConditionalExpression());
1210     }
1211
1212     /**
1213      * HavingClause ::= "HAVING" ConditionalExpression
1214      *
1215      * @return \Doctrine\ORM\Query\AST\HavingClause
1216      */
1217     public function HavingClause()
1218     {
1219         $this->match(Lexer::T_HAVING);
1220
1221         return new AST\HavingClause($this->ConditionalExpression());
1222     }
1223
1224     /**
1225      * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
1226      *
1227      * @return \Doctrine\ORM\Query\AST\GroupByClause
1228      */
1229     public function GroupByClause()
1230     {
1231         $this->match(Lexer::T_GROUP);
1232         $this->match(Lexer::T_BY);
1233
1234         $groupByItems = array($this->GroupByItem());
1235
1236         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1237             $this->match(Lexer::T_COMMA);
1238
1239             $groupByItems[] = $this->GroupByItem();
1240         }
1241
1242         return new AST\GroupByClause($groupByItems);
1243     }
1244
1245     /**
1246      * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
1247      *
1248      * @return \Doctrine\ORM\Query\AST\OrderByClause
1249      */
1250     public function OrderByClause()
1251     {
1252         $this->match(Lexer::T_ORDER);
1253         $this->match(Lexer::T_BY);
1254
1255         $orderByItems = array();
1256         $orderByItems[] = $this->OrderByItem();
1257
1258         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1259             $this->match(Lexer::T_COMMA);
1260
1261             $orderByItems[] = $this->OrderByItem();
1262         }
1263
1264         return new AST\OrderByClause($orderByItems);
1265     }
1266
1267     /**
1268      * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
1269      *
1270      * @return \Doctrine\ORM\Query\AST\Subselect
1271      */
1272     public function Subselect()
1273     {
1274         // Increase query nesting level
1275         $this->_nestingLevel++;
1276
1277         $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause());
1278
1279         $subselect->whereClause   = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
1280         $subselect->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
1281         $subselect->havingClause  = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
1282         $subselect->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
1283
1284         // Decrease query nesting level
1285         $this->_nestingLevel--;
1286
1287         return $subselect;
1288     }
1289
1290     /**
1291      * UpdateItem ::= SingleValuedPathExpression "=" NewValue
1292      *
1293      * @return \Doctrine\ORM\Query\AST\UpdateItem
1294      */
1295     public function UpdateItem()
1296     {
1297         $pathExpr = $this->SingleValuedPathExpression();
1298
1299         $this->match(Lexer::T_EQUALS);
1300
1301         $updateItem = new AST\UpdateItem($pathExpr, $this->NewValue());
1302
1303         return $updateItem;
1304     }
1305
1306     /**
1307      * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
1308      *
1309      * @return string | \Doctrine\ORM\Query\AST\PathExpression
1310      */
1311     public function GroupByItem()
1312     {
1313         // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
1314         $glimpse = $this->_lexer->glimpse();
1315
1316         if ($glimpse['type'] === Lexer::T_DOT) {
1317             return $this->SingleValuedPathExpression();
1318         }
1319
1320         // Still need to decide between IdentificationVariable or ResultVariable
1321         $lookaheadValue = $this->_lexer->lookahead['value'];
1322
1323         if ( ! isset($this->_queryComponents[$lookaheadValue])) {
1324             $this->semanticalError('Cannot group by undefined identification or result variable.');
1325         }
1326
1327         return (isset($this->_queryComponents[$lookaheadValue]['metadata']))
1328             ? $this->IdentificationVariable()
1329             : $this->ResultVariable();
1330     }
1331
1332     /**
1333      * OrderByItem ::= (
1334      *      SimpleArithmeticExpression | SingleValuedPathExpression |
1335      *      ScalarExpression | ResultVariable
1336      * ) ["ASC" | "DESC"]
1337      *
1338      * @return \Doctrine\ORM\Query\AST\OrderByItem
1339      */
1340     public function OrderByItem()
1341     {
1342
1343         $this->_lexer->peek(); // lookahead => '.'
1344         $this->_lexer->peek(); // lookahead => token after '.'
1345         $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.'
1346         $this->_lexer->resetPeek();
1347         $glimpse = $this->_lexer->glimpse();
1348
1349         switch (true) {
1350
1351             case ($this->_isMathOperator($peek)):
1352                 $expr = $this->SimpleArithmeticExpression();
1353
1354                 break;
1355             case ($glimpse['type'] === Lexer::T_DOT):
1356                 $expr = $this->SingleValuedPathExpression();
1357
1358                 break;
1359             case ($this->_lexer->peek() && $this->_isMathOperator($this->_peekBeyondClosingParenthesis())):
1360                 $expr = $this->ScalarExpression();
1361
1362                 break;
1363             default:
1364                 $expr = $this->ResultVariable();
1365
1366                 break;
1367         }
1368
1369         $type = 'ASC';
1370         $item = new AST\OrderByItem($expr);
1371
1372         switch (true) {
1373             case ($this->_lexer->isNextToken(Lexer::T_DESC)):
1374                 $this->match(Lexer::T_DESC);
1375                 $type = 'DESC';
1376                 break;
1377
1378             case ($this->_lexer->isNextToken(Lexer::T_ASC)):
1379                 $this->match(Lexer::T_ASC);
1380                 break;
1381
1382             default:
1383                 // Do nothing
1384         }
1385
1386         $item->type = $type;
1387
1388         return $item;
1389     }
1390
1391     /**
1392      * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
1393      *      EnumPrimary | SimpleEntityExpression | "NULL"
1394      *
1395      * NOTE: Since it is not possible to correctly recognize individual types, here is the full
1396      * grammar that needs to be supported:
1397      *
1398      * NewValue ::= SimpleArithmeticExpression | "NULL"
1399      *
1400      * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression
1401      */
1402     public function NewValue()
1403     {
1404         if ($this->_lexer->isNextToken(Lexer::T_NULL)) {
1405             $this->match(Lexer::T_NULL);
1406
1407             return null;
1408         }
1409
1410         if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
1411             $this->match(Lexer::T_INPUT_PARAMETER);
1412
1413             return new AST\InputParameter($this->_lexer->token['value']);
1414         }
1415
1416         return $this->SimpleArithmeticExpression();
1417     }
1418
1419     /**
1420      * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
1421      *
1422      * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
1423      */
1424     public function IdentificationVariableDeclaration()
1425     {
1426         $rangeVariableDeclaration = $this->RangeVariableDeclaration();
1427         $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
1428         $joins   = array();
1429
1430         while (
1431             $this->_lexer->isNextToken(Lexer::T_LEFT) ||
1432             $this->_lexer->isNextToken(Lexer::T_INNER) ||
1433             $this->_lexer->isNextToken(Lexer::T_JOIN)
1434         ) {
1435             $joins[] = $this->Join();
1436         }
1437
1438         return new AST\IdentificationVariableDeclaration(
1439             $rangeVariableDeclaration, $indexBy, $joins
1440         );
1441     }
1442
1443     /**
1444      * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable)
1445      *
1446      * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration |
1447      *         \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
1448      */
1449     public function SubselectIdentificationVariableDeclaration()
1450     {
1451         $this->_lexer->glimpse();
1452
1453         /* NOT YET IMPLEMENTED!
1454
1455         if ($glimpse['type'] == Lexer::T_DOT) {
1456             $subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration();
1457             $subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression();
1458             $this->match(Lexer::T_AS);
1459             $subselectIdVarDecl->aliasIdentificationVariable = $this->AliasIdentificationVariable();
1460
1461             return $subselectIdVarDecl;
1462         }
1463         */
1464
1465         return $this->IdentificationVariableDeclaration();
1466     }
1467
1468     /**
1469      * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN"
1470      *          (JoinAssociationDeclaration | RangeVariableDeclaration)
1471      *          ["WITH" ConditionalExpression]
1472      *
1473      * @return \Doctrine\ORM\Query\AST\Join
1474      */
1475     public function Join()
1476     {
1477         // Check Join type
1478         $joinType = AST\Join::JOIN_TYPE_INNER;
1479
1480         switch (true) {
1481             case ($this->_lexer->isNextToken(Lexer::T_LEFT)):
1482                 $this->match(Lexer::T_LEFT);
1483
1484                 $joinType = AST\Join::JOIN_TYPE_LEFT;
1485
1486                 // Possible LEFT OUTER join
1487                 if ($this->_lexer->isNextToken(Lexer::T_OUTER)) {
1488                     $this->match(Lexer::T_OUTER);
1489
1490                     $joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
1491                 }
1492                 break;
1493
1494             case ($this->_lexer->isNextToken(Lexer::T_INNER)):
1495                 $this->match(Lexer::T_INNER);
1496                 break;
1497
1498             default:
1499                 // Do nothing
1500         }
1501
1502         $this->match(Lexer::T_JOIN);
1503
1504         $next            = $this->_lexer->glimpse();
1505         $joinDeclaration = ($next['type'] === Lexer::T_DOT)
1506             ? $this->JoinAssociationDeclaration()
1507             : $this->RangeVariableDeclaration();
1508
1509         // Create AST node
1510         $join = new AST\Join($joinType, $joinDeclaration);
1511
1512         // Check for ad-hoc Join conditions
1513         if ($this->_lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) {
1514             $this->match(Lexer::T_WITH);
1515
1516             $join->conditionalExpression = $this->ConditionalExpression();
1517         }
1518
1519         return $join;
1520     }
1521
1522     /**
1523      * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
1524      *
1525      * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration
1526      */
1527     public function RangeVariableDeclaration()
1528     {
1529         $abstractSchemaName = $this->AbstractSchemaName();
1530
1531         if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1532             $this->match(Lexer::T_AS);
1533         }
1534
1535         $token = $this->_lexer->lookahead;
1536         $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1537         $classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
1538
1539         // Building queryComponent
1540         $queryComponent = array(
1541             'metadata'     => $classMetadata,
1542             'parent'       => null,
1543             'relation'     => null,
1544             'map'          => null,
1545             'nestingLevel' => $this->_nestingLevel,
1546             'token'        => $token
1547         );
1548
1549         $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
1550
1551         return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);
1552     }
1553
1554     /**
1555      * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
1556      *
1557      * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
1558      */
1559     public function JoinAssociationDeclaration()
1560     {
1561         $joinAssociationPathExpression = $this->JoinAssociationPathExpression();
1562
1563         if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1564             $this->match(Lexer::T_AS);
1565         }
1566
1567         $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1568         $indexBy                     = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
1569
1570         $identificationVariable = $joinAssociationPathExpression->identificationVariable;
1571         $field                  = $joinAssociationPathExpression->associationField;
1572
1573         $class       = $this->_queryComponents[$identificationVariable]['metadata'];
1574         $targetClass = $this->_em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
1575
1576         // Building queryComponent
1577         $joinQueryComponent = array(
1578             'metadata'     => $targetClass,
1579             'parent'       => $joinAssociationPathExpression->identificationVariable,
1580             'relation'     => $class->getAssociationMapping($field),
1581             'map'          => null,
1582             'nestingLevel' => $this->_nestingLevel,
1583             'token'        => $this->_lexer->lookahead
1584         );
1585
1586         $this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
1587
1588         return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy);
1589     }
1590
1591     /**
1592      * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
1593      * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
1594      *
1595      * @return array
1596      */
1597     public function PartialObjectExpression()
1598     {
1599         $this->match(Lexer::T_PARTIAL);
1600
1601         $partialFieldSet = array();
1602
1603         $identificationVariable = $this->IdentificationVariable();
1604
1605         $this->match(Lexer::T_DOT);
1606         $this->match(Lexer::T_OPEN_CURLY_BRACE);
1607         $this->match(Lexer::T_IDENTIFIER);
1608
1609         $partialFieldSet[] = $this->_lexer->token['value'];
1610
1611         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1612             $this->match(Lexer::T_COMMA);
1613             $this->match(Lexer::T_IDENTIFIER);
1614
1615             $partialFieldSet[] = $this->_lexer->token['value'];
1616         }
1617
1618         $this->match(Lexer::T_CLOSE_CURLY_BRACE);
1619
1620         $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet);
1621
1622         // Defer PartialObjectExpression validation
1623         $this->_deferredPartialObjectExpressions[] = array(
1624             'expression'   => $partialObjectExpression,
1625             'nestingLevel' => $this->_nestingLevel,
1626             'token'        => $this->_lexer->token,
1627         );
1628
1629         return $partialObjectExpression;
1630     }
1631
1632     /**
1633      * IndexBy ::= "INDEX" "BY" StateFieldPathExpression
1634      *
1635      * @return \Doctrine\ORM\Query\AST\IndexBy
1636      */
1637     public function IndexBy()
1638     {
1639         $this->match(Lexer::T_INDEX);
1640         $this->match(Lexer::T_BY);
1641         $pathExpr = $this->StateFieldPathExpression();
1642
1643         // Add the INDEX BY info to the query component
1644         $this->_queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;
1645
1646         return new AST\IndexBy($pathExpr);
1647     }
1648
1649     /**
1650      * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary |
1651      *                      StateFieldPathExpression | BooleanPrimary | CaseExpression |
1652      *                      InstanceOfExpression
1653      *
1654      * @return mixed One of the possible expressions or subexpressions.
1655      */
1656     public function ScalarExpression()
1657     {
1658         $lookahead = $this->_lexer->lookahead['type'];
1659
1660         switch ($lookahead) {
1661             case Lexer::T_IDENTIFIER:
1662                 $this->_lexer->peek(); // lookahead => '.'
1663                 $this->_lexer->peek(); // lookahead => token after '.'
1664                 $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.'
1665                 $this->_lexer->resetPeek();
1666
1667                 if ($this->_isMathOperator($peek)) {
1668                     return $this->SimpleArithmeticExpression();
1669                 }
1670
1671                 return $this->StateFieldPathExpression();
1672
1673             case Lexer::T_INTEGER:
1674             case Lexer::T_FLOAT:
1675                 return $this->SimpleArithmeticExpression();
1676
1677             case Lexer::T_STRING:
1678                 return $this->StringPrimary();
1679
1680             case Lexer::T_TRUE:
1681             case Lexer::T_FALSE:
1682                 $this->match($lookahead);
1683
1684                 return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
1685
1686             case Lexer::T_INPUT_PARAMETER:
1687                 return $this->InputParameter();
1688
1689             case Lexer::T_CASE:
1690             case Lexer::T_COALESCE:
1691             case Lexer::T_NULLIF:
1692                 // Since NULLIF and COALESCE can be identified as a function,
1693                 // we need to check if before check for FunctionDeclaration
1694                 return $this->CaseExpression();
1695
1696             default:
1697                 if ( ! ($this->_isFunction() || $this->_isAggregateFunction($lookahead))) {
1698                     $this->syntaxError();
1699                 }
1700
1701                 // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator)
1702                 $this->_lexer->peek(); // "("
1703                 $peek = $this->_peekBeyondClosingParenthesis();
1704
1705                 if ($this->_isMathOperator($peek)) {
1706                     return $this->SimpleArithmeticExpression();
1707                 }
1708
1709                 if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
1710                     return $this->AggregateExpression();
1711                 }
1712
1713                 return $this->FunctionDeclaration();
1714         }
1715     }
1716
1717     /**
1718      * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression
1719      * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
1720      * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
1721      * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
1722      * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
1723      * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
1724      * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
1725      * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
1726      *
1727      * @return mixed One of the possible expressions or subexpressions.
1728      */
1729     public function CaseExpression()
1730     {
1731         $lookahead = $this->_lexer->lookahead['type'];
1732
1733         switch ($lookahead) {
1734             case Lexer::T_NULLIF:
1735                 return $this->NullIfExpression();
1736
1737             case Lexer::T_COALESCE:
1738                 return $this->CoalesceExpression();
1739
1740             case Lexer::T_CASE:
1741                 $this->_lexer->resetPeek();
1742                 $peek = $this->_lexer->peek();
1743
1744                 if ($peek['type'] === Lexer::T_WHEN) {
1745                     return $this->GeneralCaseExpression();
1746                 }
1747
1748                 return $this->SimpleCaseExpression();
1749
1750             default:
1751                 // Do nothing
1752                 break;
1753         }
1754
1755         $this->syntaxError();
1756     }
1757
1758     /**
1759      * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
1760      *
1761      * @return \Doctrine\ORM\Query\AST\CoalesceExpression
1762      */
1763     public function CoalesceExpression()
1764     {
1765         $this->match(Lexer::T_COALESCE);
1766         $this->match(Lexer::T_OPEN_PARENTHESIS);
1767
1768         // Process ScalarExpressions (1..N)
1769         $scalarExpressions = array();
1770         $scalarExpressions[] = $this->ScalarExpression();
1771
1772         while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1773             $this->match(Lexer::T_COMMA);
1774
1775             $scalarExpressions[] = $this->ScalarExpression();
1776         }
1777
1778         $this->match(Lexer::T_CLOSE_PARENTHESIS);
1779
1780         return new AST\CoalesceExpression($scalarExpressions);
1781     }
1782
1783     /**
1784      * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
1785      *
1786      * @return \Doctrine\ORM\Query\AST\NullIfExpression
1787      */
1788     public function NullIfExpression()
1789     {
1790         $this->match(Lexer::T_NULLIF);
1791         $this->match(Lexer::T_OPEN_PARENTHESIS);
1792
1793         $firstExpression = $this->ScalarExpression();
1794         $this->match(Lexer::T_COMMA);
1795         $secondExpression = $this->ScalarExpression();
1796
1797         $this->match(Lexer::T_CLOSE_PARENTHESIS);
1798
1799         return new AST\NullIfExpression($firstExpression, $secondExpression);
1800     }
1801
1802     /**
1803      * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
1804      *
1805      * @return \Doctrine\ORM\Query\AST\GeneralExpression
1806      */
1807     public function GeneralCaseExpression()
1808     {
1809         $this->match(Lexer::T_CASE);
1810
1811         // Process WhenClause (1..N)
1812         $whenClauses = array();
1813
1814         do {
1815             $whenClauses[] = $this->WhenClause();
1816         } while ($this->_lexer->isNextToken(Lexer::T_WHEN));
1817
1818         $this->match(Lexer::T_ELSE);
1819         $scalarExpression = $this->ScalarExpression();
1820         $this->match(Lexer::T_END);
1821
1822         return new AST\GeneralCaseExpression($whenClauses, $scalarExpression);
1823     }
1824
1825     /**
1826      * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
1827      * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
1828      */
1829     public function SimpleCaseExpression()
1830     {
1831         $this->match(Lexer::T_CASE);
1832         $caseOperand = $this->StateFieldPathExpression();
1833
1834         // Process SimpleWhenClause (1..N)
1835         $simpleWhenClauses = array();
1836
1837         do {
1838             $simpleWhenClauses[] = $this->SimpleWhenClause();
1839         } while ($this->_lexer->isNextToken(Lexer::T_WHEN));
1840
1841         $this->match(Lexer::T_ELSE);
1842         $scalarExpression = $this->ScalarExpression();
1843         $this->match(Lexer::T_END);
1844
1845         return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression);
1846     }
1847
1848     /**
1849      * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
1850      *
1851      * @return \Doctrine\ORM\Query\AST\WhenExpression
1852      */
1853     public function WhenClause()
1854     {
1855         $this->match(Lexer::T_WHEN);
1856         $conditionalExpression = $this->ConditionalExpression();
1857         $this->match(Lexer::T_THEN);
1858
1859         return new AST\WhenClause($conditionalExpression, $this->ScalarExpression());
1860     }
1861
1862     /**
1863      * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
1864      *
1865      * @return \Doctrine\ORM\Query\AST\SimpleWhenExpression
1866      */
1867     public function SimpleWhenClause()
1868     {
1869         $this->match(Lexer::T_WHEN);
1870         $conditionalExpression = $this->ScalarExpression();
1871         $this->match(Lexer::T_THEN);
1872
1873         return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression());
1874     }
1875
1876     /**
1877      * SelectExpression ::= (
1878      *     IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration |
1879      *     PartialObjectExpression | "(" Subselect ")" | CaseExpression
1880      * ) [["AS"] ["HIDDEN"] AliasResultVariable]
1881      *
1882      * @return \Doctrine\ORM\Query\AST\SelectExpression
1883      */
1884     public function SelectExpression()
1885     {
1886         $expression    = null;
1887         $identVariable = null;
1888         $peek          = $this->_lexer->glimpse();
1889         $lookaheadType = $this->_lexer->lookahead['type'];
1890
1891         switch (true) {
1892             // ScalarExpression (u.name)
1893             case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT):
1894                 $expression = $this->ScalarExpression();
1895                 break;
1896
1897             // IdentificationVariable (u)
1898             case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
1899                 $expression = $identVariable = $this->IdentificationVariable();
1900                 break;
1901
1902             // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
1903             case ($lookaheadType === Lexer::T_CASE):
1904             case ($lookaheadType === Lexer::T_COALESCE):
1905             case ($lookaheadType === Lexer::T_NULLIF):
1906                 $expression = $this->CaseExpression();
1907                 break;
1908
1909             // DQL Function (SUM(u.value) or SUM(u.value) + 1)
1910             case ($this->_isFunction()):
1911                 $this->_lexer->peek(); // "("
1912
1913                 switch (true) {
1914                     case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())):
1915                         // SUM(u.id) + COUNT(u.id)
1916                         $expression = $this->ScalarExpression();
1917                         break;
1918
1919                     case ($this->_isAggregateFunction($lookaheadType)):
1920                         // COUNT(u.id)
1921                         $expression = $this->AggregateExpression();
1922                         break;
1923
1924                     default:
1925                         // IDENTITY(u)
1926                         $expression = $this->FunctionDeclaration();
1927                         break;
1928                 }
1929
1930                 break;
1931
1932             // PartialObjectExpression (PARTIAL u.{id, name})
1933             case ($lookaheadType === Lexer::T_PARTIAL):
1934                 $expression    = $this->PartialObjectExpression();
1935                 $identVariable = $expression->identificationVariable;
1936                 break;
1937
1938             // Subselect
1939             case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT):
1940                 $this->match(Lexer::T_OPEN_PARENTHESIS);
1941                 $expression = $this->Subselect();
1942                 $this->match(Lexer::T_CLOSE_PARENTHESIS);
1943                 break;
1944
1945             // Shortcut: ScalarExpression => SimpleArithmeticExpression
1946             case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS):
1947             case ($lookaheadType === Lexer::T_INTEGER):
1948             case ($lookaheadType === Lexer::T_STRING):
1949             case ($lookaheadType === Lexer::T_FLOAT):
1950             // SimpleArithmeticExpression : (- u.value ) or ( + u.value )
1951             case ($lookaheadType === Lexer::T_MINUS):
1952             case ($lookaheadType === Lexer::T_PLUS):
1953                 $expression = $this->SimpleArithmeticExpression();
1954                 break;
1955
1956             default:
1957                 $this->syntaxError(
1958                     'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
1959                     $this->_lexer->lookahead
1960                 );
1961         }
1962
1963         // [["AS"] ["HIDDEN"] AliasResultVariable]
1964
1965         if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1966             $this->match(Lexer::T_AS);
1967         }
1968
1969         $hiddenAliasResultVariable = false;
1970
1971         if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) {
1972             $this->match(Lexer::T_HIDDEN);
1973
1974             $hiddenAliasResultVariable = true;
1975         }
1976
1977         $aliasResultVariable = null;
1978
1979         if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
1980             $token = $this->_lexer->lookahead;
1981             $aliasResultVariable = $this->AliasResultVariable();
1982
1983             // Include AliasResultVariable in query components.
1984             $this->_queryComponents[$aliasResultVariable] = array(
1985                 'resultVariable' => $expression,
1986                 'nestingLevel'   => $this->_nestingLevel,
1987                 'token'          => $token,
1988             );
1989         }
1990
1991         // AST
1992
1993         $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable);
1994
1995         if ($identVariable) {
1996             $this->_identVariableExpressions[$identVariable] = $expr;
1997         }
1998
1999         return $expr;
2000     }
2001
2002     /**
2003      * SimpleSelectExpression ::= (
2004      *      StateFieldPathExpression | IdentificationVariable | FunctionDeclaration |
2005      *      AggregateExpression | "(" Subselect ")" | ScalarExpression
2006      * ) [["AS"] AliasResultVariable]
2007      *
2008      * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression
2009      */
2010     public function SimpleSelectExpression()
2011     {
2012         $peek = $this->_lexer->glimpse();
2013
2014         switch ($this->_lexer->lookahead['type']) {
2015             case Lexer::T_IDENTIFIER:
2016                 switch (true) {
2017                     case ($peek['type'] === Lexer::T_DOT):
2018                         $expression = $this->StateFieldPathExpression();
2019
2020                         return new AST\SimpleSelectExpression($expression);
2021
2022                     case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
2023                         $expression = $this->IdentificationVariable();
2024
2025                         return new AST\SimpleSelectExpression($expression);
2026
2027                     case ($this->_isFunction()):
2028                         // SUM(u.id) + COUNT(u.id)
2029                         if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) {
2030                             return new AST\SimpleSelectExpression($this->ScalarExpression());
2031                         }
2032                         // COUNT(u.id)
2033                         if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
2034                             return new AST\SimpleSelectExpression($this->AggregateExpression());
2035                         }
2036                         // IDENTITY(u)
2037                         return new AST\SimpleSelectExpression($this->FunctionDeclaration());
2038
2039                     default:
2040                         // Do nothing
2041                 }
2042                 break;
2043
2044             case Lexer::T_OPEN_PARENTHESIS:
2045                 if ($peek['type'] !== Lexer::T_SELECT) {
2046                     // Shortcut: ScalarExpression => SimpleArithmeticExpression
2047                     $expression = $this->SimpleArithmeticExpression();
2048
2049                     return new AST\SimpleSelectExpression($expression);
2050                 }
2051
2052                 // Subselect
2053                 $this->match(Lexer::T_OPEN_PARENTHESIS);
2054                 $expression = $this->Subselect();
2055                 $this->match(Lexer::T_CLOSE_PARENTHESIS);
2056
2057                 return new AST\SimpleSelectExpression($expression);
2058
2059             default:
2060                 // Do nothing
2061         }
2062
2063         $this->_lexer->peek();
2064
2065         $expression = $this->ScalarExpression();
2066         $expr       = new AST\SimpleSelectExpression($expression);
2067
2068         if ($this->_lexer->isNextToken(Lexer::T_AS)) {
2069             $this->match(Lexer::T_AS);
2070         }
2071
2072         if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
2073             $token = $this->_lexer->lookahead;
2074             $resultVariable = $this->AliasResultVariable();
2075             $expr->fieldIdentificationVariable = $resultVariable;
2076
2077             // Include AliasResultVariable in query components.
2078             $this->_queryComponents[$resultVariable] = array(
2079                 'resultvariable' => $expr,
2080                 'nestingLevel'   => $this->_nestingLevel,
2081                 'token'          => $token,
2082             );
2083         }
2084
2085         return $expr;
2086     }
2087
2088     /**
2089      * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
2090      *
2091      * @return \Doctrine\ORM\Query\AST\ConditionalExpression
2092      */
2093     public function ConditionalExpression()
2094     {
2095         $conditionalTerms = array();
2096         $conditionalTerms[] = $this->ConditionalTerm();
2097
2098         while ($this->_lexer->isNextToken(Lexer::T_OR)) {
2099             $this->match(Lexer::T_OR);
2100
2101             $conditionalTerms[] = $this->ConditionalTerm();
2102         }
2103
2104         // Phase 1 AST optimization: Prevent AST\ConditionalExpression
2105         // if only one AST\ConditionalTerm is defined
2106         if (count($conditionalTerms) == 1) {
2107             return $conditionalTerms[0];
2108         }
2109
2110         return new AST\ConditionalExpression($conditionalTerms);
2111     }
2112
2113     /**
2114      * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
2115      *
2116      * @return \Doctrine\ORM\Query\AST\ConditionalTerm
2117      */
2118     public function ConditionalTerm()
2119     {
2120         $conditionalFactors = array();
2121         $conditionalFactors[] = $this->ConditionalFactor();
2122
2123         while ($this->_lexer->isNextToken(Lexer::T_AND)) {
2124             $this->match(Lexer::T_AND);
2125
2126             $conditionalFactors[] = $this->ConditionalFactor();
2127         }
2128
2129         // Phase 1 AST optimization: Prevent AST\ConditionalTerm
2130         // if only one AST\ConditionalFactor is defined
2131         if (count($conditionalFactors) == 1) {
2132             return $conditionalFactors[0];
2133         }
2134
2135         return new AST\ConditionalTerm($conditionalFactors);
2136     }
2137
2138     /**
2139      * ConditionalFactor ::= ["NOT"] ConditionalPrimary
2140      *
2141      * @return \Doctrine\ORM\Query\AST\ConditionalFactor
2142      */
2143     public function ConditionalFactor()
2144     {
2145         $not = false;
2146
2147         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2148             $this->match(Lexer::T_NOT);
2149
2150             $not = true;
2151         }
2152
2153         $conditionalPrimary = $this->ConditionalPrimary();
2154
2155         // Phase 1 AST optimization: Prevent AST\ConditionalFactor
2156         // if only one AST\ConditionalPrimary is defined
2157         if ( ! $not) {
2158             return $conditionalPrimary;
2159         }
2160
2161         $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary);
2162         $conditionalFactor->not = $not;
2163
2164         return $conditionalFactor;
2165     }
2166
2167     /**
2168      * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
2169      *
2170      * @return \Doctrine\ORM\Query\AST\ConditionalPrimary
2171      */
2172     public function ConditionalPrimary()
2173     {
2174         $condPrimary = new AST\ConditionalPrimary;
2175
2176         if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2177             $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
2178
2179             return $condPrimary;
2180         }
2181
2182         // Peek beyond the matching closing paranthesis ')'
2183         $peek = $this->_peekBeyondClosingParenthesis();
2184
2185         if (in_array($peek['value'], array("=",  "<", "<=", "<>", ">", ">=", "!=")) ||
2186             in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS)) ||
2187             $this->_isMathOperator($peek)) {
2188             $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
2189
2190             return $condPrimary;
2191         }
2192
2193         $this->match(Lexer::T_OPEN_PARENTHESIS);
2194         $condPrimary->conditionalExpression = $this->ConditionalExpression();
2195         $this->match(Lexer::T_CLOSE_PARENTHESIS);
2196
2197         return $condPrimary;
2198     }
2199
2200     /**
2201      * SimpleConditionalExpression ::=
2202      *      ComparisonExpression | BetweenExpression | LikeExpression |
2203      *      InExpression | NullComparisonExpression | ExistsExpression |
2204      *      EmptyCollectionComparisonExpression | CollectionMemberExpression |
2205      *      InstanceOfExpression
2206      */
2207     public function SimpleConditionalExpression()
2208     {
2209         $token = $this->_lexer->lookahead;
2210
2211         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2212             $token = $this->_lexer->glimpse();
2213         }
2214
2215         if ($token['type'] === Lexer::T_EXISTS) {
2216             return $this->ExistsExpression();
2217         }
2218
2219         $peek = $this->_lexer->glimpse();
2220
2221         if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) {
2222             if ($peek['value'] == '(') {
2223                 // Peek beyond the matching closing paranthesis ')'
2224                 $this->_lexer->peek();
2225                 $token = $this->_peekBeyondClosingParenthesis(false);
2226
2227                 if ($token['type'] === Lexer::T_NOT) {
2228                     $token = $this->_lexer->peek();
2229                 }
2230
2231                 $this->_lexer->resetPeek();
2232             } else {
2233                 // Peek beyond the PathExpression (or InputParameter)
2234                 $peek = $this->_lexer->peek();
2235
2236                 while ($peek['value'] === '.') {
2237                     $this->_lexer->peek();
2238                     $peek = $this->_lexer->peek();
2239                 }
2240
2241                 // Also peek beyond a NOT if there is one
2242                 if ($peek['type'] === Lexer::T_NOT) {
2243                     $peek = $this->_lexer->peek();
2244                 }
2245
2246                 $token = $peek;
2247
2248                 // We need to go even further in case of IS (differenciate between NULL and EMPTY)
2249                 $lookahead = $this->_lexer->peek();
2250
2251                 // Also peek beyond a NOT if there is one
2252                 if ($lookahead['type'] === Lexer::T_NOT) {
2253                     $lookahead = $this->_lexer->peek();
2254                 }
2255
2256                 $this->_lexer->resetPeek();
2257             }
2258         }
2259
2260         switch ($token['type']) {
2261             case Lexer::T_BETWEEN:
2262                 return $this->BetweenExpression();
2263             case Lexer::T_LIKE:
2264                 return $this->LikeExpression();
2265             case Lexer::T_IN:
2266                 return $this->InExpression();
2267             case Lexer::T_INSTANCE:
2268                 return $this->InstanceOfExpression();
2269             case Lexer::T_IS:
2270                 if ($lookahead['type'] == Lexer::T_NULL) {
2271                     return $this->NullComparisonExpression();
2272                 }
2273                 return $this->EmptyCollectionComparisonExpression();
2274             case Lexer::T_MEMBER:
2275                 return $this->CollectionMemberExpression();
2276             default:
2277                 return $this->ComparisonExpression();
2278         }
2279     }
2280
2281     /**
2282      * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
2283      *
2284      * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression
2285      */
2286     public function EmptyCollectionComparisonExpression()
2287     {
2288         $emptyColletionCompExpr = new AST\EmptyCollectionComparisonExpression(
2289             $this->CollectionValuedPathExpression()
2290         );
2291         $this->match(Lexer::T_IS);
2292
2293         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2294             $this->match(Lexer::T_NOT);
2295             $emptyColletionCompExpr->not = true;
2296         }
2297
2298         $this->match(Lexer::T_EMPTY);
2299
2300         return $emptyColletionCompExpr;
2301     }
2302
2303     /**
2304      * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression
2305      *
2306      * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
2307      * SimpleEntityExpression ::= IdentificationVariable | InputParameter
2308      *
2309      * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression
2310      */
2311     public function CollectionMemberExpression()
2312     {
2313         $not        = false;
2314         $entityExpr = $this->EntityExpression();
2315
2316         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2317             $this->match(Lexer::T_NOT);
2318
2319             $not = true;
2320         }
2321
2322         $this->match(Lexer::T_MEMBER);
2323
2324         if ($this->_lexer->isNextToken(Lexer::T_OF)) {
2325             $this->match(Lexer::T_OF);
2326         }
2327
2328         $collMemberExpr = new AST\CollectionMemberExpression(
2329             $entityExpr, $this->CollectionValuedPathExpression()
2330         );
2331         $collMemberExpr->not = $not;
2332
2333         return $collMemberExpr;
2334     }
2335
2336     /**
2337      * Literal ::= string | char | integer | float | boolean
2338      *
2339      * @return string
2340      */
2341     public function Literal()
2342     {
2343         switch ($this->_lexer->lookahead['type']) {
2344             case Lexer::T_STRING:
2345                 $this->match(Lexer::T_STRING);
2346                 return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
2347
2348             case Lexer::T_INTEGER:
2349             case Lexer::T_FLOAT:
2350                 $this->match(
2351                     $this->_lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT
2352                 );
2353                 return new AST\Literal(AST\Literal::NUMERIC, $this->_lexer->token['value']);
2354
2355             case Lexer::T_TRUE:
2356             case Lexer::T_FALSE:
2357                 $this->match(
2358                     $this->_lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE
2359                 );
2360                 return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
2361
2362             default:
2363                 $this->syntaxError('Literal');
2364         }
2365     }
2366
2367     /**
2368      * InParameter ::= Literal | InputParameter
2369      *
2370      * @return string | \Doctrine\ORM\Query\AST\InputParameter
2371      */
2372     public function InParameter()
2373     {
2374         if ($this->_lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) {
2375             return $this->InputParameter();
2376         }
2377
2378         return $this->Literal();
2379     }
2380
2381     /**
2382      * InputParameter ::= PositionalParameter | NamedParameter
2383      *
2384      * @return \Doctrine\ORM\Query\AST\InputParameter
2385      */
2386     public function InputParameter()
2387     {
2388         $this->match(Lexer::T_INPUT_PARAMETER);
2389
2390         return new AST\InputParameter($this->_lexer->token['value']);
2391     }
2392
2393     /**
2394      * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
2395      *
2396      * @return \Doctrine\ORM\Query\AST\ArithmeticExpression
2397      */
2398     public function ArithmeticExpression()
2399     {
2400         $expr = new AST\ArithmeticExpression;
2401
2402         if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2403             $peek = $this->_lexer->glimpse();
2404
2405             if ($peek['type'] === Lexer::T_SELECT) {
2406                 $this->match(Lexer::T_OPEN_PARENTHESIS);
2407                 $expr->subselect = $this->Subselect();
2408                 $this->match(Lexer::T_CLOSE_PARENTHESIS);
2409
2410                 return $expr;
2411             }
2412         }
2413
2414         $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression();
2415
2416         return $expr;
2417     }
2418
2419     /**
2420      * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}*
2421      *
2422      * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression
2423      */
2424     public function SimpleArithmeticExpression()
2425     {
2426         $terms = array();
2427         $terms[] = $this->ArithmeticTerm();
2428
2429         while (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
2430             $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
2431
2432             $terms[] = $this->_lexer->token['value'];
2433             $terms[] = $this->ArithmeticTerm();
2434         }
2435
2436         // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression
2437         // if only one AST\ArithmeticTerm is defined
2438         if (count($terms) == 1) {
2439             return $terms[0];
2440         }
2441
2442         return new AST\SimpleArithmeticExpression($terms);
2443     }
2444
2445     /**
2446      * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}*
2447      *
2448      * @return \Doctrine\ORM\Query\AST\ArithmeticTerm
2449      */
2450     public function ArithmeticTerm()
2451     {
2452         $factors = array();
2453         $factors[] = $this->ArithmeticFactor();
2454
2455         while (($isMult = $this->_lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->_lexer->isNextToken(Lexer::T_DIVIDE)) {
2456             $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE);
2457
2458             $factors[] = $this->_lexer->token['value'];
2459             $factors[] = $this->ArithmeticFactor();
2460         }
2461
2462         // Phase 1 AST optimization: Prevent AST\ArithmeticTerm
2463         // if only one AST\ArithmeticFactor is defined
2464         if (count($factors) == 1) {
2465             return $factors[0];
2466         }
2467
2468         return new AST\ArithmeticTerm($factors);
2469     }
2470
2471     /**
2472      * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
2473      *
2474      * @return \Doctrine\ORM\Query\AST\ArithmeticFactor
2475      */
2476     public function ArithmeticFactor()
2477     {
2478         $sign = null;
2479
2480         if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
2481             $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
2482             $sign = $isPlus;
2483         }
2484
2485         $primary = $this->ArithmeticPrimary();
2486
2487         // Phase 1 AST optimization: Prevent AST\ArithmeticFactor
2488         // if only one AST\ArithmeticPrimary is defined
2489         if ($sign === null) {
2490             return $primary;
2491         }
2492
2493         return new AST\ArithmeticFactor($primary, $sign);
2494     }
2495
2496     /**
2497      * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
2498      *          | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
2499      *          | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
2500      *          | InputParameter | CaseExpression
2501      */
2502     public function ArithmeticPrimary()
2503     {
2504         if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2505             $this->match(Lexer::T_OPEN_PARENTHESIS);
2506             $expr = $this->SimpleArithmeticExpression();
2507
2508             $this->match(Lexer::T_CLOSE_PARENTHESIS);
2509
2510             return $expr;
2511         }
2512
2513         switch ($this->_lexer->lookahead['type']) {
2514             case Lexer::T_COALESCE:
2515             case Lexer::T_NULLIF:
2516             case Lexer::T_CASE:
2517                 return $this->CaseExpression();
2518
2519             case Lexer::T_IDENTIFIER:
2520                 $peek = $this->_lexer->glimpse();
2521
2522                 if ($peek['value'] == '(') {
2523                     return $this->FunctionDeclaration();
2524                 }
2525
2526                 if ($peek['value'] == '.') {
2527                     return $this->SingleValuedPathExpression();
2528                 }
2529
2530                 if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
2531                     return $this->ResultVariable();
2532                 }
2533
2534                 return $this->StateFieldPathExpression();
2535
2536             case Lexer::T_INPUT_PARAMETER:
2537                 return $this->InputParameter();
2538
2539             default:
2540                 $peek = $this->_lexer->glimpse();
2541
2542                 if ($peek['value'] == '(') {
2543                     if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
2544                         return $this->AggregateExpression();
2545                     }
2546
2547                     return $this->FunctionDeclaration();
2548                 }
2549
2550                 return $this->Literal();
2551         }
2552     }
2553
2554     /**
2555      * StringExpression ::= StringPrimary | "(" Subselect ")"
2556      *
2557      * @return \Doctrine\ORM\Query\AST\StringPrimary |
2558      *         \Doctrine]ORM\Query\AST\Subselect
2559      */
2560     public function StringExpression()
2561     {
2562         if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2563             $peek = $this->_lexer->glimpse();
2564
2565             if ($peek['type'] === Lexer::T_SELECT) {
2566                 $this->match(Lexer::T_OPEN_PARENTHESIS);
2567                 $expr = $this->Subselect();
2568                 $this->match(Lexer::T_CLOSE_PARENTHESIS);
2569
2570                 return $expr;
2571             }
2572         }
2573
2574         return $this->StringPrimary();
2575     }
2576
2577     /**
2578      * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
2579      */
2580     public function StringPrimary()
2581     {
2582         $lookaheadType = $this->_lexer->lookahead['type'];
2583
2584         switch ($lookaheadType) {
2585             case Lexer::T_IDENTIFIER:
2586                 $peek = $this->_lexer->glimpse();
2587
2588                 if ($peek['value'] == '.') {
2589                     return $this->StateFieldPathExpression();
2590                 }
2591
2592                 if ($peek['value'] == '(') {
2593                     // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
2594                     return $this->FunctionDeclaration();
2595                 }
2596
2597                 $this->syntaxError("'.' or '('");
2598                 break;
2599
2600             case Lexer::T_STRING:
2601                 $this->match(Lexer::T_STRING);
2602
2603                 return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
2604
2605             case Lexer::T_INPUT_PARAMETER:
2606                 return $this->InputParameter();
2607
2608             case Lexer::T_CASE:
2609             case Lexer::T_COALESCE:
2610             case Lexer::T_NULLIF:
2611                 return $this->CaseExpression();
2612
2613             default:
2614                 if ($this->_isAggregateFunction($lookaheadType)) {
2615                     return $this->AggregateExpression();
2616                 }
2617         }
2618
2619         $this->syntaxError(
2620             'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
2621         );
2622     }
2623
2624     /**
2625      * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
2626      *
2627      * @return \Doctrine\ORM\Query\AST\SingleValuedAssociationPathExpression |
2628      *         \Doctrine\ORM\Query\AST\SimpleEntityExpression
2629      */
2630     public function EntityExpression()
2631     {
2632         $glimpse = $this->_lexer->glimpse();
2633
2634         if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') {
2635             return $this->SingleValuedAssociationPathExpression();
2636         }
2637
2638         return $this->SimpleEntityExpression();
2639     }
2640
2641     /**
2642      * SimpleEntityExpression ::= IdentificationVariable | InputParameter
2643      *
2644      * @return string | \Doctrine\ORM\Query\AST\InputParameter
2645      */
2646     public function SimpleEntityExpression()
2647     {
2648         if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2649             return $this->InputParameter();
2650         }
2651
2652         return $this->StateFieldPathExpression();
2653     }
2654
2655     /**
2656      * AggregateExpression ::=
2657      *  ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" |
2658      *  "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")"
2659      *
2660      * @return \Doctrine\ORM\Query\AST\AggregateExpression
2661      */
2662     public function AggregateExpression()
2663     {
2664         $lookaheadType = $this->_lexer->lookahead['type'];
2665         $isDistinct = false;
2666
2667         if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) {
2668             $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
2669         }
2670
2671         $this->match($lookaheadType);
2672         $functionName = $this->_lexer->token['value'];
2673         $this->match(Lexer::T_OPEN_PARENTHESIS);
2674
2675         if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
2676             $this->match(Lexer::T_DISTINCT);
2677             $isDistinct = true;
2678         }
2679
2680         $pathExp = ($lookaheadType === Lexer::T_COUNT)
2681             ? $this->SingleValuedPathExpression()
2682             : $this->SimpleArithmeticExpression();
2683
2684         $this->match(Lexer::T_CLOSE_PARENTHESIS);
2685
2686         return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
2687     }
2688
2689     /**
2690      * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
2691      *
2692      * @return \Doctrine\ORM\Query\AST\QuantifiedExpression
2693      */
2694     public function QuantifiedExpression()
2695     {
2696         $lookaheadType = $this->_lexer->lookahead['type'];
2697         $value = $this->_lexer->lookahead['value'];
2698
2699         if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) {
2700             $this->syntaxError('ALL, ANY or SOME');
2701         }
2702
2703         $this->match($lookaheadType);
2704         $this->match(Lexer::T_OPEN_PARENTHESIS);
2705
2706         $qExpr = new AST\QuantifiedExpression($this->Subselect());
2707         $qExpr->type = $value;
2708
2709         $this->match(Lexer::T_CLOSE_PARENTHESIS);
2710
2711         return $qExpr;
2712     }
2713
2714     /**
2715      * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
2716      *
2717      * @return \Doctrine\ORM\Query\AST\BetweenExpression
2718      */
2719     public function BetweenExpression()
2720     {
2721         $not = false;
2722         $arithExpr1 = $this->ArithmeticExpression();
2723
2724         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2725             $this->match(Lexer::T_NOT);
2726             $not = true;
2727         }
2728
2729         $this->match(Lexer::T_BETWEEN);
2730         $arithExpr2 = $this->ArithmeticExpression();
2731         $this->match(Lexer::T_AND);
2732         $arithExpr3 = $this->ArithmeticExpression();
2733
2734         $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3);
2735         $betweenExpr->not = $not;
2736
2737         return $betweenExpr;
2738     }
2739
2740     /**
2741      * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
2742      *
2743      * @return \Doctrine\ORM\Query\AST\ComparisonExpression
2744      */
2745     public function ComparisonExpression()
2746     {
2747         $this->_lexer->glimpse();
2748
2749         $leftExpr  = $this->ArithmeticExpression();
2750         $operator  = $this->ComparisonOperator();
2751         $rightExpr = ($this->_isNextAllAnySome())
2752             ? $this->QuantifiedExpression()
2753             : $this->ArithmeticExpression();
2754
2755         return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
2756     }
2757
2758     /**
2759      * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
2760      *
2761      * @return \Doctrine\ORM\Query\AST\InExpression
2762      */
2763     public function InExpression()
2764     {
2765         $inExpression = new AST\InExpression($this->ArithmeticExpression());
2766
2767         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2768             $this->match(Lexer::T_NOT);
2769             $inExpression->not = true;
2770         }
2771
2772         $this->match(Lexer::T_IN);
2773         $this->match(Lexer::T_OPEN_PARENTHESIS);
2774
2775         if ($this->_lexer->isNextToken(Lexer::T_SELECT)) {
2776             $inExpression->subselect = $this->Subselect();
2777         } else {
2778             $literals = array();
2779             $literals[] = $this->InParameter();
2780
2781             while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
2782                 $this->match(Lexer::T_COMMA);
2783                 $literals[] = $this->InParameter();
2784             }
2785
2786             $inExpression->literals = $literals;
2787         }
2788
2789         $this->match(Lexer::T_CLOSE_PARENTHESIS);
2790
2791         return $inExpression;
2792     }
2793
2794     /**
2795      * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
2796      *
2797      * @return \Doctrine\ORM\Query\AST\InstanceOfExpression
2798      */
2799     public function InstanceOfExpression()
2800     {
2801         $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable());
2802
2803         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2804             $this->match(Lexer::T_NOT);
2805             $instanceOfExpression->not = true;
2806         }
2807
2808         $this->match(Lexer::T_INSTANCE);
2809         $this->match(Lexer::T_OF);
2810
2811         $exprValues = array();
2812
2813         if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2814             $this->match(Lexer::T_OPEN_PARENTHESIS);
2815
2816             $exprValues[] = $this->InstanceOfParameter();
2817
2818             while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
2819                 $this->match(Lexer::T_COMMA);
2820
2821                 $exprValues[] = $this->InstanceOfParameter();
2822             }
2823
2824             $this->match(Lexer::T_CLOSE_PARENTHESIS);
2825
2826             $instanceOfExpression->value = $exprValues;
2827
2828             return $instanceOfExpression;
2829         }
2830
2831         $exprValues[] = $this->InstanceOfParameter();
2832
2833         $instanceOfExpression->value = $exprValues;
2834
2835         return $instanceOfExpression;
2836     }
2837
2838     /**
2839      * InstanceOfParameter ::= AbstractSchemaName | InputParameter
2840      *
2841      * @return mixed
2842      */
2843     public function InstanceOfParameter()
2844     {
2845         if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2846             $this->match(Lexer::T_INPUT_PARAMETER);
2847
2848             return new AST\InputParameter($this->_lexer->token['value']);
2849         }
2850
2851         return $this->AliasIdentificationVariable();
2852     }
2853
2854     /**
2855      * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
2856      *
2857      * @return \Doctrine\ORM\Query\AST\LikeExpression
2858      */
2859     public function LikeExpression()
2860     {
2861         $stringExpr = $this->StringExpression();
2862         $not = false;
2863
2864         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2865             $this->match(Lexer::T_NOT);
2866             $not = true;
2867         }
2868
2869         $this->match(Lexer::T_LIKE);
2870
2871         if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2872             $this->match(Lexer::T_INPUT_PARAMETER);
2873             $stringPattern = new AST\InputParameter($this->_lexer->token['value']);
2874         } else {
2875             $stringPattern = $this->StringPrimary();
2876         }
2877
2878         $escapeChar = null;
2879
2880         if ($this->_lexer->lookahead['type'] === Lexer::T_ESCAPE) {
2881             $this->match(Lexer::T_ESCAPE);
2882             $this->match(Lexer::T_STRING);
2883
2884             $escapeChar = new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
2885         }
2886
2887         $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar);
2888         $likeExpr->not = $not;
2889
2890         return $likeExpr;
2891     }
2892
2893     /**
2894      * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
2895      *
2896      * @return \Doctrine\ORM\Query\AST\NullComparisonExpression
2897      */
2898     public function NullComparisonExpression()
2899     {
2900         if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2901             $this->match(Lexer::T_INPUT_PARAMETER);
2902             $expr = new AST\InputParameter($this->_lexer->token['value']);
2903         } else {
2904             $expr = $this->SingleValuedPathExpression();
2905         }
2906
2907         $nullCompExpr = new AST\NullComparisonExpression($expr);
2908         $this->match(Lexer::T_IS);
2909
2910         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2911             $this->match(Lexer::T_NOT);
2912             $nullCompExpr->not = true;
2913         }
2914
2915         $this->match(Lexer::T_NULL);
2916
2917         return $nullCompExpr;
2918     }
2919
2920     /**
2921      * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
2922      *
2923      * @return \Doctrine\ORM\Query\AST\ExistsExpression
2924      */
2925     public function ExistsExpression()
2926     {
2927         $not = false;
2928
2929         if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2930             $this->match(Lexer::T_NOT);
2931             $not = true;
2932         }
2933
2934         $this->match(Lexer::T_EXISTS);
2935         $this->match(Lexer::T_OPEN_PARENTHESIS);
2936
2937         $existsExpression = new AST\ExistsExpression($this->Subselect());
2938         $existsExpression->not = $not;
2939
2940         $this->match(Lexer::T_CLOSE_PARENTHESIS);
2941
2942         return $existsExpression;
2943     }
2944
2945     /**
2946      * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
2947      *
2948      * @return string
2949      */
2950     public function ComparisonOperator()
2951     {
2952         switch ($this->_lexer->lookahead['value']) {
2953             case '=':
2954                 $this->match(Lexer::T_EQUALS);
2955
2956                 return '=';
2957
2958             case '<':
2959                 $this->match(Lexer::T_LOWER_THAN);
2960                 $operator = '<';
2961
2962                 if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) {
2963                     $this->match(Lexer::T_EQUALS);
2964                     $operator .= '=';
2965                 } else if ($this->_lexer->isNextToken(Lexer::T_GREATER_THAN)) {
2966                     $this->match(Lexer::T_GREATER_THAN);
2967                     $operator .= '>';
2968                 }
2969
2970                 return $operator;
2971
2972             case '>':
2973                 $this->match(Lexer::T_GREATER_THAN);
2974                 $operator = '>';
2975
2976                 if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) {
2977                     $this->match(Lexer::T_EQUALS);
2978                     $operator .= '=';
2979                 }
2980
2981                 return $operator;
2982
2983             case '!':
2984                 $this->match(Lexer::T_NEGATE);
2985                 $this->match(Lexer::T_EQUALS);
2986
2987                 return '<>';
2988
2989             default:
2990                 $this->syntaxError('=, <, <=, <>, >, >=, !=');
2991         }
2992     }
2993
2994     /**
2995      * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime
2996      */
2997     public function FunctionDeclaration()
2998     {
2999         $token = $this->_lexer->lookahead;
3000         $funcName = strtolower($token['value']);
3001
3002         // Check for built-in functions first!
3003         switch (true) {
3004             case (isset(self::$_STRING_FUNCTIONS[$funcName])):
3005                 return $this->FunctionsReturningStrings();
3006
3007             case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])):
3008                 return $this->FunctionsReturningNumerics();
3009
3010             case (isset(self::$_DATETIME_FUNCTIONS[$funcName])):
3011                 return $this->FunctionsReturningDatetime();
3012
3013             default:
3014                 return $this->CustomFunctionDeclaration();
3015         }
3016     }
3017
3018     /**
3019      * Helper function for FunctionDeclaration grammar rule
3020      */
3021     private function CustomFunctionDeclaration()
3022     {
3023         $token = $this->_lexer->lookahead;
3024         $funcName = strtolower($token['value']);
3025
3026         // Check for custom functions afterwards
3027         $config = $this->_em->getConfiguration();
3028
3029         switch (true) {
3030             case ($config->getCustomStringFunction($funcName) !== null):
3031                 return $this->CustomFunctionsReturningStrings();
3032
3033             case ($config->getCustomNumericFunction($funcName) !== null):
3034                 return $this->CustomFunctionsReturningNumerics();
3035
3036             case ($config->getCustomDatetimeFunction($funcName) !== null):
3037                 return $this->CustomFunctionsReturningDatetime();
3038
3039             default:
3040                 $this->syntaxError('known function', $token);
3041         }
3042     }
3043
3044     /**
3045      * FunctionsReturningNumerics ::=
3046      *      "LENGTH" "(" StringPrimary ")" |
3047      *      "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
3048      *      "ABS" "(" SimpleArithmeticExpression ")" |
3049      *      "SQRT" "(" SimpleArithmeticExpression ")" |
3050      *      "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
3051      *      "SIZE" "(" CollectionValuedPathExpression ")"
3052      */
3053     public function FunctionsReturningNumerics()
3054     {
3055         $funcNameLower = strtolower($this->_lexer->lookahead['value']);
3056         $funcClass     = self::$_NUMERIC_FUNCTIONS[$funcNameLower];
3057
3058         $function = new $funcClass($funcNameLower);
3059         $function->parse($this);
3060
3061         return $function;
3062     }
3063
3064     public function CustomFunctionsReturningNumerics()
3065     {
3066         // getCustomNumericFunction is case-insensitive
3067         $funcName  = strtolower($this->_lexer->lookahead['value']);
3068         $funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName);
3069
3070         $function = new $funcClass($funcName);
3071         $function->parse($this);
3072
3073         return $function;
3074     }
3075
3076     /**
3077      * FunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP"
3078      */
3079     public function FunctionsReturningDatetime()
3080     {
3081         $funcNameLower = strtolower($this->_lexer->lookahead['value']);
3082         $funcClass     = self::$_DATETIME_FUNCTIONS[$funcNameLower];
3083
3084         $function = new $funcClass($funcNameLower);
3085         $function->parse($this);
3086
3087         return $function;
3088     }
3089
3090     public function CustomFunctionsReturningDatetime()
3091     {
3092         // getCustomDatetimeFunction is case-insensitive
3093         $funcName  = $this->_lexer->lookahead['value'];
3094         $funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName);
3095
3096         $function = new $funcClass($funcName);
3097         $function->parse($this);
3098
3099         return $function;
3100     }
3101
3102     /**
3103      * FunctionsReturningStrings ::=
3104      *   "CONCAT" "(" StringPrimary "," StringPrimary ")" |
3105      *   "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
3106      *   "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
3107      *   "LOWER" "(" StringPrimary ")" |
3108      *   "UPPER" "(" StringPrimary ")"
3109      */
3110     public function FunctionsReturningStrings()
3111     {
3112         $funcNameLower = strtolower($this->_lexer->lookahead['value']);
3113         $funcClass     = self::$_STRING_FUNCTIONS[$funcNameLower];
3114
3115         $function = new $funcClass($funcNameLower);
3116         $function->parse($this);
3117
3118         return $function;
3119     }
3120
3121     public function CustomFunctionsReturningStrings()
3122     {
3123         // getCustomStringFunction is case-insensitive
3124         $funcName  = $this->_lexer->lookahead['value'];
3125         $funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName);
3126
3127         $function = new $funcClass($funcName);
3128         $function->parse($this);
3129
3130         return $function;
3131     }
3132 }