@@ -9,23 +9,13 @@ class Parser
99{
1010 /** @var Lexer */
1111 private $ lexer ;
12-
13- /** @var array Array of parsed tokens */
1412 private $ tokens ;
15-
16- /** @var array Current token */
1713 private $ token ;
18-
19- /** @var int Current token position */
2014 private $ tpos ;
21-
22- /** @var string Expression being parsed */
2315 private $ expression ;
16+ private static $ nullToken = ['type ' => 'eof ' ];
17+ private static $ currentNode = ['type ' => 'current ' ];
2418
25- /** @var array Null token that is reused over and over */
26- private static $ nullToken = ['type ' => 'eof ' , 'value ' => '' ];
27-
28- /** @var array Token binding power */
2919 private static $ bp = [
3020 'eof ' => 0 ,
3121 'quoted_identifier ' => 0 ,
@@ -49,9 +39,6 @@ class Parser
4939 'lparen ' => 60 ,
5040 ];
5141
52- /** @var array Cached current AST node */
53- private static $ currentNode = ['type ' => 'current ' ];
54-
5542 /** @var array Acceptable tokens after a dot token */
5643 private static $ afterDot = [
5744 'identifier ' => true , // foo.bar
@@ -87,13 +74,11 @@ public function parse($expression)
8774 $ this ->next ();
8875 $ result = $ this ->expr ();
8976
90- if ($ this ->token ['type ' ] != 'eof ' ) {
91- $ this ->throwSyntax ('Encountered an unexpected " '
92- . $ this ->token ['type ' ] . '" token and did not reach '
93- . ' the end of the token stream ' );
77+ if ($ this ->token ['type ' ] === 'eof ' ) {
78+ return $ result ;
9479 }
9580
96- return $ result ;
81+ throw $ this -> syntax ( ' Did not reach the end of the token stream ' ) ;
9782 }
9883
9984 /**
@@ -117,20 +102,15 @@ private function nud_identifier()
117102 {
118103 $ token = $ this ->token ;
119104 $ this ->next ();
120- return ['type ' => 'field ' , 'key ' => $ token ['value ' ]];
105+ return ['type ' => 'field ' , 'value ' => $ token ['value ' ]];
121106 }
122107
123108 private function nud_quoted_identifier ()
124109 {
125110 $ token = $ this ->token ;
126111 $ this ->next ();
127- if ($ this ->token ['type ' ] == 'lparen ' ) {
128- $ this ->throwSyntax (
129- 'Quoted identifiers are not allowed for function names. '
130- );
131- }
132-
133- return ['type ' => 'field ' , 'key ' => $ token ['value ' ]];
112+ $ this ->assertNotToken ('lparen ' );
113+ return ['type ' => 'field ' , 'value ' => $ token ['value ' ]];
134114 }
135115
136116 private function nud_current ()
@@ -149,8 +129,7 @@ private function nud_literal()
149129 private function nud_expref ()
150130 {
151131 $ this ->next ();
152-
153- return ['type ' => 'expression ' , 'children ' => [$ this ->expr (2 )]];
132+ return ['type ' => 'expref ' , 'children ' => [$ this ->expr (2 )]];
154133 }
155134
156135 private function nud_lbrace ()
@@ -192,7 +171,7 @@ private function nud_lbracket()
192171 $ type = $ this ->token ['type ' ];
193172 if ($ type == 'number ' || $ type == 'colon ' ) {
194173 return $ this ->parseArrayIndexExpression ();
195- } elseif ($ type == 'star ' && $ this ->lookahead ()[ ' type ' ] == 'rbracket ' ) {
174+ } elseif ($ type == 'star ' && $ this ->lookahead () == 'rbracket ' ) {
196175 return $ this ->parseWildcardArray ();
197176 } else {
198177 return $ this ->parseMultiSelectList ();
@@ -203,16 +182,16 @@ private function led_lbracket(array $left)
203182 {
204183 static $ nextTypes = ['number ' => true , 'colon ' => true , 'star ' => true ];
205184 $ this ->next ($ nextTypes );
206- $ type = $ this ->token ['type ' ];
207-
208- if ($ type == 'number ' || $ type == 'colon ' ) {
209- return [
210- 'type ' => 'subexpression ' ,
211- 'children ' => [$ left , $ this ->parseArrayIndexExpression ()]
212- ];
185+ switch ($ this ->token ['type ' ]) {
186+ case 'number ' :
187+ case 'colon ' :
188+ return [
189+ 'type ' => 'subexpression ' ,
190+ 'children ' => [$ left , $ this ->parseArrayIndexExpression ()]
191+ ];
192+ default :
193+ return $ this ->parseWildcardArray ($ left );
213194 }
214-
215- return $ this ->parseWildcardArray ($ left );
216195 }
217196
218197 private function led_flatten (array $ left )
@@ -264,7 +243,6 @@ private function led_pipe(array $left)
264243 private function led_lparen (array $ left )
265244 {
266245 $ args = [];
267- $ name = $ left ['key ' ];
268246 $ this ->next ();
269247
270248 while ($ this ->token ['type ' ] != 'rparen ' ) {
@@ -276,15 +254,19 @@ private function led_lparen(array $left)
276254
277255 $ this ->next ();
278256
279- return ['type ' => 'function ' , 'fn ' => $ name , 'children ' => $ args ];
257+ return [
258+ 'type ' => 'function ' ,
259+ 'value ' => $ left ['value ' ],
260+ 'children ' => $ args
261+ ];
280262 }
281263
282264 private function led_filter (array $ left )
283265 {
284266 $ this ->next ();
285267 $ expression = $ this ->expr ();
286268 if ($ this ->token ['type ' ] != 'rbracket ' ) {
287- $ this ->throwSyntax ('Expected a closing rbracket for the filter ' );
269+ throw $ this ->syntax ('Expected a closing rbracket for the filter ' );
288270 }
289271
290272 $ this ->next ();
@@ -294,7 +276,7 @@ private function led_filter(array $left)
294276 'type ' => 'projection ' ,
295277 'from ' => 'array ' ,
296278 'children ' => [
297- $ left ?:self ::$ currentNode ,
279+ $ left ?: self ::$ currentNode ,
298280 [
299281 'type ' => 'condition ' ,
300282 'children ' => [$ expression , $ rhs ]
@@ -310,7 +292,7 @@ private function led_comparator(array $left)
310292
311293 return [
312294 'type ' => 'comparator ' ,
313- 'relation ' => $ token ['value ' ],
295+ 'value ' => $ token ['value ' ],
314296 'children ' => [$ left , $ this ->expr ()]
315297 ];
316298 }
@@ -327,7 +309,7 @@ private function parseProjection($bp)
327309 return $ this ->expr ($ bp );
328310 }
329311
330- $ this ->throwSyntax ('Syntax error after projection ' );
312+ throw $ this ->syntax ('Syntax error after projection ' );
331313 }
332314
333315 private function parseDot ($ bp )
@@ -348,8 +330,8 @@ private function parseKeyValuePair()
348330 $ this ->next ();
349331
350332 return [
351- 'type ' => 'key_value_pair ' ,
352- 'key ' => $ key ,
333+ 'type ' => 'key_val_pair ' ,
334+ 'value ' => $ key ,
353335 'children ' => [$ this ->expr ()]
354336 ];
355337 }
@@ -415,19 +397,15 @@ private function parseArrayIndexExpression()
415397
416398 if ($ pos == 0 ) {
417399 // No colons were found so this is a simple index extraction
418- return ['type ' => 'index ' , 'index ' => $ parts [0 ]];
400+ return ['type ' => 'index ' , 'value ' => $ parts [0 ]];
419401 } elseif ($ pos > 2 ) {
420- $ this ->throwSyntax ('Invalid array slice syntax: too many colons ' );
402+ throw $ this ->syntax ('Invalid array slice syntax: too many colons ' );
403+ } else {
404+ // Sliced array from start (e.g., [2:])
405+ return ['type ' => 'slice ' , 'value ' => $ parts ];
421406 }
422-
423- // Sliced array from start (e.g., [2:])
424- return ['type ' => 'slice ' , 'args ' => $ parts ];
425407 }
426408
427- /**
428- * Parses a multi-select-list expression:
429- * multi-select-list = "[" ( expression *( "," expression ) ) "]"
430- */
431409 private function parseMultiSelectList ()
432410 {
433411 $ nodes = [];
@@ -436,47 +414,26 @@ private function parseMultiSelectList()
436414 $ nodes [] = $ this ->expr ();
437415 if ($ this ->token ['type ' ] == 'comma ' ) {
438416 $ this ->next ();
439- if ($ this ->token ['type ' ] == 'rbracket ' ) {
440- $ this ->throwSyntax ('Expected expression, found rbracket ' );
441- }
417+ $ this ->assertNotToken ('rbracket ' );
442418 }
443419 } while ($ this ->token ['type ' ] != 'rbracket ' );
444-
445420 $ this ->next ();
446421
447422 return ['type ' => 'multi_select_list ' , 'children ' => $ nodes ];
448423 }
449424
450- /**
451- * Throws a SyntaxErrorException for the current token
452- *
453- * @param string $msg Error message
454- * @throws SyntaxErrorException
455- */
456- private function throwSyntax ($ msg )
425+ private function syntax ($ msg )
457426 {
458- throw new SyntaxErrorException ($ msg , $ this ->token , $ this ->expression );
427+ return new SyntaxErrorException ($ msg , $ this ->token , $ this ->expression );
459428 }
460429
461- /**
462- * Lookahead at the next token.
463- *
464- * @return array
465- */
466430 private function lookahead ()
467431 {
468432 return (!isset ($ this ->tokens [$ this ->tpos + 1 ]))
469- ? self :: $ nullToken
470- : $ this ->tokens [$ this ->tpos + 1 ];
433+ ? ' eof '
434+ : $ this ->tokens [$ this ->tpos + 1 ][ ' type ' ] ;
471435 }
472436
473- /**
474- * Move the token stream cursor to the next token
475- *
476- * @param array $match Associative array of acceptable next tokens
477- *
478- * @throws SyntaxErrorException if the next token is not acceptable
479- */
480437 private function next (array $ match = null )
481438 {
482439 if (!isset ($ this ->tokens [$ this ->tpos + 1 ])) {
@@ -486,11 +443,14 @@ private function next(array $match = null)
486443 }
487444
488445 if ($ match && !isset ($ match [$ this ->token ['type ' ]])) {
489- throw new SyntaxErrorException (
490- $ match ,
491- $ this ->token ,
492- $ this ->expression
493- );
446+ throw $ this ->syntax ($ match );
447+ }
448+ }
449+
450+ private function assertNotToken ($ type )
451+ {
452+ if ($ this ->token ['type ' ] == $ type ) {
453+ throw $ this ->syntax ("Token {$ this ->tpos } not allowed to be $ type " );
494454 }
495455 }
496456
@@ -512,7 +472,7 @@ function ($i) use ($prefix) {
512472 return strpos ($ i , $ prefix ) === 0 ;
513473 }
514474 )));
515- $ this ->throwSyntax ($ message );
475+ throw $ this ->syntax ($ message );
516476 }
517477
518478 throw new \BadMethodCallException ("Call to undefined method $ method " );
0 commit comments