1 %pure_parser
2 %expect 2
3 
4 %tokens
5 
6 %%
7 
8 start:
9     top_statement_list                                      { $$ = $this->handleNamespaces($1); }
10 ;
11 
12 top_statement_list_ex:
13       top_statement_list_ex top_statement                   { pushNormalizing($1, $2); }
14     | /* empty */                                           { init(); }
15 ;
16 
17 top_statement_list:
18       top_statement_list_ex
19           { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
20             if ($nop !== null) { $1[] = $nop; } $$ = $1; }
21 ;
22 
23 reserved_non_modifiers:
24       T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
25     | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE
26     | T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH
27     | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
28     | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
29     | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
30     | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
31     | T_MATCH
32 ;
33 
34 semi_reserved:
35       reserved_non_modifiers
36     | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
37 ;
38 
39 identifier_ex:
40       T_STRING                                              { $$ = Node\Identifier[$1]; }
41     | semi_reserved                                         { $$ = Node\Identifier[$1]; }
42 ;
43 
44 identifier:
45       T_STRING                                              { $$ = Node\Identifier[$1]; }
46 ;
47 
48 reserved_non_modifiers_identifier:
49       reserved_non_modifiers                                { $$ = Node\Identifier[$1]; }
50 ;
51 
52 namespace_declaration_name:
53       T_STRING                                              { $$ = Name[$1]; }
54     | semi_reserved                                         { $$ = Name[$1]; }
55     | T_NAME_QUALIFIED                                      { $$ = Name[$1]; }
56 ;
57 
58 namespace_name:
59       T_STRING                                              { $$ = Name[$1]; }
60     | T_NAME_QUALIFIED                                      { $$ = Name[$1]; }
61 ;
62 
63 legacy_namespace_name:
64       namespace_name                                        { $$ = $1; }
65     | T_NAME_FULLY_QUALIFIED                                { $$ = Name[substr($1, 1)]; }
66 ;
67 
68 plain_variable:
69       T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
70 ;
71 
72 semi:
73       ';'                                                   { /* nothing */ }
74     | error                                                 { /* nothing */ }
75 ;
76 
77 no_comma:
78       /* empty */ { /* nothing */ }
79     | ',' { $this->emitError(new Error('A trailing comma is not allowed here', attributes())); }
80 ;
81 
82 optional_comma:
83       /* empty */
84     | ','
85 
86 top_statement:
87       statement                                             { $$ = $1; }
88     | function_declaration_statement                        { $$ = $1; }
89     | class_declaration_statement                           { $$ = $1; }
90     | T_HALT_COMPILER
91           { $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; }
92     | T_NAMESPACE namespace_declaration_name semi
93           { $$ = Stmt\Namespace_[$2, null];
94             $$->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON);
95             $this->checkNamespace($$); }
96     | T_NAMESPACE namespace_declaration_name '{' top_statement_list '}'
97           { $$ = Stmt\Namespace_[$2, $4];
98             $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
99             $this->checkNamespace($$); }
100     | T_NAMESPACE '{' top_statement_list '}'
101           { $$ = Stmt\Namespace_[null, $3];
102             $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
103             $this->checkNamespace($$); }
104     | T_USE use_declarations semi                           { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
105     | T_USE use_type use_declarations semi                  { $$ = Stmt\Use_[$3, $2]; }
106     | group_use_declaration semi                            { $$ = $1; }
107     | T_CONST constant_declaration_list semi                { $$ = Stmt\Const_[$2]; }
108 ;
109 
110 use_type:
111       T_FUNCTION                                            { $$ = Stmt\Use_::TYPE_FUNCTION; }
112     | T_CONST                                               { $$ = Stmt\Use_::TYPE_CONSTANT; }
113 ;
114 
115 group_use_declaration:
116       T_USE use_type legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}'
117           { $$ = Stmt\GroupUse[$3, $6, $2]; }
118     | T_USE legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}'
119           { $$ = Stmt\GroupUse[$2, $5, Stmt\Use_::TYPE_UNKNOWN]; }
120 ;
121 
122 unprefixed_use_declarations:
123       non_empty_unprefixed_use_declarations optional_comma  { $$ = $1; }
124 ;
125 
126 non_empty_unprefixed_use_declarations:
127       non_empty_unprefixed_use_declarations ',' unprefixed_use_declaration
128           { push($1, $3); }
129     | unprefixed_use_declaration                            { init($1); }
130 ;
131 
132 use_declarations:
133       non_empty_use_declarations no_comma                   { $$ = $1; }
134 ;
135 
136 non_empty_use_declarations:
137       non_empty_use_declarations ',' use_declaration        { push($1, $3); }
138     | use_declaration                                       { init($1); }
139 ;
140 
141 inline_use_declarations:
142       non_empty_inline_use_declarations optional_comma      { $$ = $1; }
143 ;
144 
145 non_empty_inline_use_declarations:
146       non_empty_inline_use_declarations ',' inline_use_declaration
147           { push($1, $3); }
148     | inline_use_declaration                                { init($1); }
149 ;
150 
151 unprefixed_use_declaration:
152       namespace_name
153           { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
154     | namespace_name T_AS identifier
155           { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
156 ;
157 
158 use_declaration:
159       legacy_namespace_name
160           { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
161     | legacy_namespace_name T_AS identifier
162           { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
163 ;
164 
165 inline_use_declaration:
166       unprefixed_use_declaration                            { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; }
167     | use_type unprefixed_use_declaration                   { $$ = $2; $$->type = $1; }
168 ;
169 
170 constant_declaration_list:
171       non_empty_constant_declaration_list no_comma          { $$ = $1; }
172 ;
173 
174 non_empty_constant_declaration_list:
175       non_empty_constant_declaration_list ',' constant_declaration
176           { push($1, $3); }
177     | constant_declaration                                  { init($1); }
178 ;
179 
180 constant_declaration:
181     identifier '=' expr                                     { $$ = Node\Const_[$1, $3]; }
182 ;
183 
184 class_const_list:
185       non_empty_class_const_list no_comma                   { $$ = $1; }
186 ;
187 
188 non_empty_class_const_list:
189       non_empty_class_const_list ',' class_const            { push($1, $3); }
190     | class_const                                           { init($1); }
191 ;
192 
193 class_const:
194     identifier_ex '=' expr                                  { $$ = Node\Const_[$1, $3]; }
195 ;
196 
197 inner_statement_list_ex:
198       inner_statement_list_ex inner_statement               { pushNormalizing($1, $2); }
199     | /* empty */                                           { init(); }
200 ;
201 
202 inner_statement_list:
203       inner_statement_list_ex
204           { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
205             if ($nop !== null) { $1[] = $nop; } $$ = $1; }
206 ;
207 
208 inner_statement:
209       statement                                             { $$ = $1; }
210     | function_declaration_statement                        { $$ = $1; }
211     | class_declaration_statement                           { $$ = $1; }
212     | T_HALT_COMPILER
213           { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
214 ;
215 
216 non_empty_statement:
217       '{' inner_statement_list '}'
218     {
219         if ($2) {
220             $$ = $2; prependLeadingComments($$);
221         } else {
222             makeNop($$, $this->startAttributeStack[#1], $this->endAttributes);
223             if (null === $$) { $$ = array(); }
224         }
225     }
226     | T_IF '(' expr ')' statement elseif_list else_single
227           { $$ = Stmt\If_[$3, ['stmts' => toArray($5), 'elseifs' => $6, 'else' => $7]]; }
228     | T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
229           { $$ = Stmt\If_[$3, ['stmts' => $6, 'elseifs' => $7, 'else' => $8]]; }
230     | T_WHILE '(' expr ')' while_statement                  { $$ = Stmt\While_[$3, $5]; }
231     | T_DO statement T_WHILE '(' expr ')' ';'               { $$ = Stmt\Do_   [$5, toArray($2)]; }
232     | T_FOR '(' for_expr ';'  for_expr ';' for_expr ')' for_statement
233           { $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
234     | T_SWITCH '(' expr ')' switch_case_list                { $$ = Stmt\Switch_[$3, $5]; }
235     | T_BREAK optional_expr semi                            { $$ = Stmt\Break_[$2]; }
236     | T_CONTINUE optional_expr semi                         { $$ = Stmt\Continue_[$2]; }
237     | T_RETURN optional_expr semi                           { $$ = Stmt\Return_[$2]; }
238     | T_GLOBAL global_var_list semi                         { $$ = Stmt\Global_[$2]; }
239     | T_STATIC static_var_list semi                         { $$ = Stmt\Static_[$2]; }
240     | T_ECHO expr_list_forbid_comma semi                    { $$ = Stmt\Echo_[$2]; }
241     | T_INLINE_HTML                                         { $$ = Stmt\InlineHTML[$1]; }
242     | expr semi {
243         $e = $1;
244         if ($e instanceof Expr\Throw_) {
245             // For backwards-compatibility reasons, convert throw in statement position into
246             // Stmt\Throw_ rather than Stmt\Expression(Expr\Throw_).
247             $$ = Stmt\Throw_[$e->expr];
248         } else {
249             $$ = Stmt\Expression[$e];
250         }
251     }
252     | T_UNSET '(' variables_list ')' semi                   { $$ = Stmt\Unset_[$3]; }
253     | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
254           { $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; }
255     | T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
256           { $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
257     | T_FOREACH '(' expr error ')' foreach_statement
258           { $$ = Stmt\Foreach_[$3, new Expr\Error(stackAttributes(#4)), ['stmts' => $6]]; }
259     | T_DECLARE '(' declare_list ')' declare_statement      { $$ = Stmt\Declare_[$3, $5]; }
260     | T_TRY '{' inner_statement_list '}' catches optional_finally
261           { $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
262     | T_GOTO identifier semi                                { $$ = Stmt\Goto_[$2]; }
263     | identifier ':'                                        { $$ = Stmt\Label[$1]; }
264     | error                                                 { $$ = array(); /* means: no statement */ }
265 ;
266 
267 statement:
268       non_empty_statement                                   { $$ = $1; }
269     | ';'
270           { makeNop($$, $this->startAttributeStack[#1], $this->endAttributes);
271             if ($$ === null) $$ = array(); /* means: no statement */ }
272 ;
273 
274 catches:
275       /* empty */                                           { init(); }
276     | catches catch                                         { push($1, $2); }
277 ;
278 
279 name_union:
280       name                                                  { init($1); }
281     | name_union '|' name                                   { push($1, $3); }
282 ;
283 
284 catch:
285     T_CATCH '(' name_union optional_plain_variable ')' '{' inner_statement_list '}'
286         { $$ = Stmt\Catch_[$3, $4, $7]; }
287 ;
288 
289 optional_finally:
290       /* empty */                                           { $$ = null; }
291     | T_FINALLY '{' inner_statement_list '}'                { $$ = Stmt\Finally_[$3]; }
292 ;
293 
294 variables_list:
295       non_empty_variables_list optional_comma               { $$ = $1; }
296 ;
297 
298 non_empty_variables_list:
299       variable                                              { init($1); }
300     | non_empty_variables_list ',' variable                 { push($1, $3); }
301 ;
302 
303 optional_ref:
304       /* empty */                                           { $$ = false; }
305     | '&'                                                   { $$ = true; }
306 ;
307 
308 optional_ellipsis:
309       /* empty */                                           { $$ = false; }
310     | T_ELLIPSIS                                            { $$ = true; }
311 ;
312 
313 block_or_error:
314       '{' inner_statement_list '}'                          { $$ = $2; }
315     | error                                                 { $$ = []; }
316 ;
317 
318 function_declaration_statement:
319     T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error
320         { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8]]; }
321 ;
322 
323 class_declaration_statement:
324       class_entry_type identifier extends_from implements_list '{' class_statement_list '}'
325           { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]];
326             $this->checkClass($$, #2); }
327     | T_INTERFACE identifier interface_extends_list '{' class_statement_list '}'
328           { $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]];
329             $this->checkInterface($$, #2); }
330     | T_TRAIT identifier '{' class_statement_list '}'
331           { $$ = Stmt\Trait_[$2, ['stmts' => $4]]; }
332 ;
333 
334 class_entry_type:
335       T_CLASS                                               { $$ = 0; }
336     | T_ABSTRACT T_CLASS                                    { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
337     | T_FINAL T_CLASS                                       { $$ = Stmt\Class_::MODIFIER_FINAL; }
338 ;
339 
340 extends_from:
341       /* empty */                                           { $$ = null; }
342     | T_EXTENDS class_name                                  { $$ = $2; }
343 ;
344 
345 interface_extends_list:
346       /* empty */                                           { $$ = array(); }
347     | T_EXTENDS class_name_list                             { $$ = $2; }
348 ;
349 
350 implements_list:
351       /* empty */                                           { $$ = array(); }
352     | T_IMPLEMENTS class_name_list                          { $$ = $2; }
353 ;
354 
355 class_name_list:
356       non_empty_class_name_list no_comma                    { $$ = $1; }
357 ;
358 
359 non_empty_class_name_list:
360       class_name                                            { init($1); }
361     | non_empty_class_name_list ',' class_name              { push($1, $3); }
362 ;
363 
364 for_statement:
365       statement                                             { $$ = toArray($1); }
366     | ':' inner_statement_list T_ENDFOR ';'                 { $$ = $2; }
367 ;
368 
369 foreach_statement:
370       statement                                             { $$ = toArray($1); }
371     | ':' inner_statement_list T_ENDFOREACH ';'             { $$ = $2; }
372 ;
373 
374 declare_statement:
375       non_empty_statement                                   { $$ = toArray($1); }
376     | ';'                                                   { $$ = null; }
377     | ':' inner_statement_list T_ENDDECLARE ';'             { $$ = $2; }
378 ;
379 
380 declare_list:
381       non_empty_declare_list no_comma                       { $$ = $1; }
382 ;
383 
384 non_empty_declare_list:
385       declare_list_element                                  { init($1); }
386     | non_empty_declare_list ',' declare_list_element       { push($1, $3); }
387 ;
388 
389 declare_list_element:
390       identifier '=' expr                                   { $$ = Stmt\DeclareDeclare[$1, $3]; }
391 ;
392 
393 switch_case_list:
394       '{' case_list '}'                                     { $$ = $2; }
395     | '{' ';' case_list '}'                                 { $$ = $3; }
396     | ':' case_list T_ENDSWITCH ';'                         { $$ = $2; }
397     | ':' ';' case_list T_ENDSWITCH ';'                     { $$ = $3; }
398 ;
399 
400 case_list:
401       /* empty */                                           { init(); }
402     | case_list case                                        { push($1, $2); }
403 ;
404 
405 case:
406       T_CASE expr case_separator inner_statement_list_ex    { $$ = Stmt\Case_[$2, $4]; }
407     | T_DEFAULT case_separator inner_statement_list_ex      { $$ = Stmt\Case_[null, $3]; }
408 ;
409 
410 case_separator:
411       ':'
412     | ';'
413 ;
414 
415 match:
416       T_MATCH '(' expr ')' '{' match_arm_list '}'           { $$ = Expr\Match_[$3, $6]; }
417 ;
418 
419 match_arm_list:
420       /* empty */                                           { $$ = []; }
421     | non_empty_match_arm_list optional_comma               { $$ = $1; }
422 ;
423 
424 non_empty_match_arm_list:
425       match_arm                                             { init($1); }
426     | non_empty_match_arm_list ',' match_arm                { push($1, $3); }
427 ;
428 
429 match_arm:
430       expr_list_allow_comma T_DOUBLE_ARROW expr             { $$ = Node\MatchArm[$1, $3]; }
431     | T_DEFAULT optional_comma T_DOUBLE_ARROW expr          { $$ = Node\MatchArm[null, $4]; }
432 ;
433 
434 while_statement:
435       statement                                             { $$ = toArray($1); }
436     | ':' inner_statement_list T_ENDWHILE ';'               { $$ = $2; }
437 ;
438 
439 elseif_list:
440       /* empty */                                           { init(); }
441     | elseif_list elseif                                    { push($1, $2); }
442 ;
443 
444 elseif:
445       T_ELSEIF '(' expr ')' statement                       { $$ = Stmt\ElseIf_[$3, toArray($5)]; }
446 ;
447 
448 new_elseif_list:
449       /* empty */                                           { init(); }
450     | new_elseif_list new_elseif                            { push($1, $2); }
451 ;
452 
453 new_elseif:
454      T_ELSEIF '(' expr ')' ':' inner_statement_list         { $$ = Stmt\ElseIf_[$3, $6]; }
455 ;
456 
457 else_single:
458       /* empty */                                           { $$ = null; }
459     | T_ELSE statement                                      { $$ = Stmt\Else_[toArray($2)]; }
460 ;
461 
462 new_else_single:
463       /* empty */                                           { $$ = null; }
464     | T_ELSE ':' inner_statement_list                       { $$ = Stmt\Else_[$3]; }
465 ;
466 
467 foreach_variable:
468       variable                                              { $$ = array($1, false); }
469     | '&' variable                                          { $$ = array($2, true); }
470     | list_expr                                             { $$ = array($1, false); }
471     | array_short_syntax                                    { $$ = array($1, false); }
472 ;
473 
474 parameter_list:
475       non_empty_parameter_list optional_comma               { $$ = $1; }
476     | /* empty */                                           { $$ = array(); }
477 ;
478 
479 non_empty_parameter_list:
480       parameter                                             { init($1); }
481     | non_empty_parameter_list ',' parameter                { push($1, $3); }
482 ;
483 
484 optional_visibility_modifier:
485       /* empty */               { $$ = 0; }
486     | T_PUBLIC                  { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
487     | T_PROTECTED               { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
488     | T_PRIVATE                 { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
489 ;
490 
491 parameter:
492       optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable
493           { $$ = new Node\Param($5, null, $2, $3, $4, attributes(), $1);
494             $this->checkParam($$); }
495     | optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis plain_variable '=' expr
496           { $$ = new Node\Param($5, $7, $2, $3, $4, attributes(), $1);
497             $this->checkParam($$); }
498     | optional_visibility_modifier optional_type_without_static optional_ref optional_ellipsis error
499           { $$ = new Node\Param(Expr\Error[], null, $2, $3, $4, attributes(), $1); }
500 ;
501 
502 type_expr:
503       type                                                  { $$ = $1; }
504     | '?' type                                              { $$ = Node\NullableType[$2]; }
505     | union_type                                            { $$ = Node\UnionType[$1]; }
506 ;
507 
508 type:
509       type_without_static                                   { $$ = $1; }
510     | T_STATIC                                              { $$ = Node\Name['static']; }
511 ;
512 
513 type_without_static:
514       name                                                  { $$ = $this->handleBuiltinTypes($1); }
515     | T_ARRAY                                               { $$ = Node\Identifier['array']; }
516     | T_CALLABLE                                            { $$ = Node\Identifier['callable']; }
517 ;
518 
519 union_type:
520       type '|' type                                         { init($1, $3); }
521     | union_type '|' type                                   { push($1, $3); }
522 ;
523 
524 union_type_without_static:
525       type_without_static '|' type_without_static           { init($1, $3); }
526     | union_type_without_static '|' type_without_static     { push($1, $3); }
527 ;
528 
529 type_expr_without_static:
530       type_without_static                                   { $$ = $1; }
531     | '?' type_without_static                               { $$ = Node\NullableType[$2]; }
532     | union_type_without_static                             { $$ = Node\UnionType[$1]; }
533 ;
534 
535 optional_type_without_static:
536       /* empty */                                           { $$ = null; }
537     | type_expr_without_static                              { $$ = $1; }
538 ;
539 
540 optional_return_type:
541       /* empty */                                           { $$ = null; }
542     | ':' type_expr                                         { $$ = $2; }
543     | ':' error                                             { $$ = null; }
544 ;
545 
546 argument_list:
547       '(' ')'                                               { $$ = array(); }
548     | '(' non_empty_argument_list optional_comma ')'        { $$ = $2; }
549 ;
550 
551 non_empty_argument_list:
552       argument                                              { init($1); }
553     | non_empty_argument_list ',' argument                  { push($1, $3); }
554 ;
555 
556 argument:
557       expr                                                  { $$ = Node\Arg[$1, false, false]; }
558     | '&' variable                                          { $$ = Node\Arg[$2, true, false]; }
559     | T_ELLIPSIS expr                                       { $$ = Node\Arg[$2, false, true]; }
560     | identifier_ex ':' expr
561           { $$ = new Node\Arg($3, false, false, attributes(), $1); }
562 ;
563 
564 global_var_list:
565       non_empty_global_var_list no_comma                    { $$ = $1; }
566 ;
567 
568 non_empty_global_var_list:
569       non_empty_global_var_list ',' global_var              { push($1, $3); }
570     | global_var                                            { init($1); }
571 ;
572 
573 global_var:
574       simple_variable                                       { $$ = Expr\Variable[$1]; }
575 ;
576 
577 static_var_list:
578       non_empty_static_var_list no_comma                    { $$ = $1; }
579 ;
580 
581 non_empty_static_var_list:
582       non_empty_static_var_list ',' static_var              { push($1, $3); }
583     | static_var                                            { init($1); }
584 ;
585 
586 static_var:
587       plain_variable                                        { $$ = Stmt\StaticVar[$1, null]; }
588     | plain_variable '=' expr                               { $$ = Stmt\StaticVar[$1, $3]; }
589 ;
590 
591 class_statement_list_ex:
592       class_statement_list_ex class_statement               { if ($2 !== null) { push($1, $2); } }
593     | /* empty */                                           { init(); }
594 ;
595 
596 class_statement_list:
597       class_statement_list_ex
598           { makeZeroLengthNop($nop, $this->lookaheadStartAttributes);
599             if ($nop !== null) { $1[] = $nop; } $$ = $1; }
600 ;
601 
602 class_statement:
603       variable_modifiers optional_type_without_static property_declaration_list ';'
604           { $attrs = attributes();
605             $$ = new Stmt\Property($1, $3, $attrs, $2); $this->checkProperty($$, #1); }
606     | method_modifiers T_CONST class_const_list ';'
607           { $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); }
608     | method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
609           { $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]];
610             $this->checkClassMethod($$, #1); }
611     | T_USE class_name_list trait_adaptations               { $$ = Stmt\TraitUse[$2, $3]; }
612     | error                                                 { $$ = null; /* will be skipped */ }
613 ;
614 
615 trait_adaptations:
616       ';'                                                   { $$ = array(); }
617     | '{' trait_adaptation_list '}'                         { $$ = $2; }
618 ;
619 
620 trait_adaptation_list:
621       /* empty */                                           { init(); }
622     | trait_adaptation_list trait_adaptation                { push($1, $2); }
623 ;
624 
625 trait_adaptation:
626       trait_method_reference_fully_qualified T_INSTEADOF class_name_list ';'
627           { $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
628     | trait_method_reference T_AS member_modifier identifier_ex ';'
629           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
630     | trait_method_reference T_AS member_modifier ';'
631           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
632     | trait_method_reference T_AS identifier ';'
633           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
634     | trait_method_reference T_AS reserved_non_modifiers_identifier ';'
635           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
636 ;
637 
638 trait_method_reference_fully_qualified:
639       name T_PAAMAYIM_NEKUDOTAYIM identifier_ex             { $$ = array($1, $3); }
640 ;
641 trait_method_reference:
642       trait_method_reference_fully_qualified                { $$ = $1; }
643     | identifier_ex                                         { $$ = array(null, $1); }
644 ;
645 
646 method_body:
647       ';' /* abstract method */                             { $$ = null; }
648     | block_or_error                                        { $$ = $1; }
649 ;
650 
651 variable_modifiers:
652       non_empty_member_modifiers                            { $$ = $1; }
653     | T_VAR                                                 { $$ = 0; }
654 ;
655 
656 method_modifiers:
657       /* empty */                                           { $$ = 0; }
658     | non_empty_member_modifiers                            { $$ = $1; }
659 ;
660 
661 non_empty_member_modifiers:
662       member_modifier                                       { $$ = $1; }
663     | non_empty_member_modifiers member_modifier            { $this->checkModifier($1, $2, #2); $$ = $1 | $2; }
664 ;
665 
666 member_modifier:
667       T_PUBLIC                                              { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
668     | T_PROTECTED                                           { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
669     | T_PRIVATE                                             { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
670     | T_STATIC                                              { $$ = Stmt\Class_::MODIFIER_STATIC; }
671     | T_ABSTRACT                                            { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
672     | T_FINAL                                               { $$ = Stmt\Class_::MODIFIER_FINAL; }
673 ;
674 
675 property_declaration_list:
676       non_empty_property_declaration_list no_comma          { $$ = $1; }
677 ;
678 
679 non_empty_property_declaration_list:
680       property_declaration                                  { init($1); }
681     | non_empty_property_declaration_list ',' property_declaration
682           { push($1, $3); }
683 ;
684 
685 property_decl_name:
686       T_VARIABLE                                            { $$ = Node\VarLikeIdentifier[parseVar($1)]; }
687 ;
688 
689 property_declaration:
690       property_decl_name                                    { $$ = Stmt\PropertyProperty[$1, null]; }
691     | property_decl_name '=' expr                           { $$ = Stmt\PropertyProperty[$1, $3]; }
692 ;
693 
694 expr_list_forbid_comma:
695       non_empty_expr_list no_comma                          { $$ = $1; }
696 ;
697 
698 expr_list_allow_comma:
699       non_empty_expr_list optional_comma                    { $$ = $1; }
700 ;
701 
702 non_empty_expr_list:
703       non_empty_expr_list ',' expr                          { push($1, $3); }
704     | expr                                                  { init($1); }
705 ;
706 
707 for_expr:
708       /* empty */                                           { $$ = array(); }
709     | expr_list_forbid_comma                                { $$ = $1; }
710 ;
711 
712 expr:
713       variable                                              { $$ = $1; }
714     | list_expr '=' expr                                    { $$ = Expr\Assign[$1, $3]; }
715     | array_short_syntax '=' expr                           { $$ = Expr\Assign[$1, $3]; }
716     | variable '=' expr                                     { $$ = Expr\Assign[$1, $3]; }
717     | variable '=' '&' variable                             { $$ = Expr\AssignRef[$1, $4]; }
718     | new_expr                                              { $$ = $1; }
719     | match                                                 { $$ = $1; }
720     | T_CLONE expr                                          { $$ = Expr\Clone_[$2]; }
721     | variable T_PLUS_EQUAL expr                            { $$ = Expr\AssignOp\Plus      [$1, $3]; }
722     | variable T_MINUS_EQUAL expr                           { $$ = Expr\AssignOp\Minus     [$1, $3]; }
723     | variable T_MUL_EQUAL expr                             { $$ = Expr\AssignOp\Mul       [$1, $3]; }
724     | variable T_DIV_EQUAL expr                             { $$ = Expr\AssignOp\Div       [$1, $3]; }
725     | variable T_CONCAT_EQUAL expr                          { $$ = Expr\AssignOp\Concat    [$1, $3]; }
726     | variable T_MOD_EQUAL expr                             { $$ = Expr\AssignOp\Mod       [$1, $3]; }
727     | variable T_AND_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; }
728     | variable T_OR_EQUAL expr                              { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; }
729     | variable T_XOR_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; }
730     | variable T_SL_EQUAL expr                              { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
731     | variable T_SR_EQUAL expr                              { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
732     | variable T_POW_EQUAL expr                             { $$ = Expr\AssignOp\Pow       [$1, $3]; }
733     | variable T_COALESCE_EQUAL expr                        { $$ = Expr\AssignOp\Coalesce  [$1, $3]; }
734     | variable T_INC                                        { $$ = Expr\PostInc[$1]; }
735     | T_INC variable                                        { $$ = Expr\PreInc [$2]; }
736     | variable T_DEC                                        { $$ = Expr\PostDec[$1]; }
737     | T_DEC variable                                        { $$ = Expr\PreDec [$2]; }
738     | expr T_BOOLEAN_OR expr                                { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; }
739     | expr T_BOOLEAN_AND expr                               { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; }
740     | expr T_LOGICAL_OR expr                                { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; }
741     | expr T_LOGICAL_AND expr                               { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
742     | expr T_LOGICAL_XOR expr                               { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
743     | expr '|' expr                                         { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
744     | expr '&' expr                                         { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
745     | expr '^' expr                                         { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
746     | expr '.' expr                                         { $$ = Expr\BinaryOp\Concat    [$1, $3]; }
747     | expr '+' expr                                         { $$ = Expr\BinaryOp\Plus      [$1, $3]; }
748     | expr '-' expr                                         { $$ = Expr\BinaryOp\Minus     [$1, $3]; }
749     | expr '*' expr                                         { $$ = Expr\BinaryOp\Mul       [$1, $3]; }
750     | expr '/' expr                                         { $$ = Expr\BinaryOp\Div       [$1, $3]; }
751     | expr '%' expr                                         { $$ = Expr\BinaryOp\Mod       [$1, $3]; }
752     | expr T_SL expr                                        { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; }
753     | expr T_SR expr                                        { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; }
754     | expr T_POW expr                                       { $$ = Expr\BinaryOp\Pow       [$1, $3]; }
755     | '+' expr %prec T_INC                                  { $$ = Expr\UnaryPlus [$2]; }
756     | '-' expr %prec T_INC                                  { $$ = Expr\UnaryMinus[$2]; }
757     | '!' expr                                              { $$ = Expr\BooleanNot[$2]; }
758     | '~' expr                                              { $$ = Expr\BitwiseNot[$2]; }
759     | expr T_IS_IDENTICAL expr                              { $$ = Expr\BinaryOp\Identical     [$1, $3]; }
760     | expr T_IS_NOT_IDENTICAL expr                          { $$ = Expr\BinaryOp\NotIdentical  [$1, $3]; }
761     | expr T_IS_EQUAL expr                                  { $$ = Expr\BinaryOp\Equal         [$1, $3]; }
762     | expr T_IS_NOT_EQUAL expr                              { $$ = Expr\BinaryOp\NotEqual      [$1, $3]; }
763     | expr T_SPACESHIP expr                                 { $$ = Expr\BinaryOp\Spaceship     [$1, $3]; }
764     | expr '<' expr                                         { $$ = Expr\BinaryOp\Smaller       [$1, $3]; }
765     | expr T_IS_SMALLER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
766     | expr '>' expr                                         { $$ = Expr\BinaryOp\Greater       [$1, $3]; }
767     | expr T_IS_GREATER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
768     | expr T_INSTANCEOF class_name_reference                { $$ = Expr\Instanceof_[$1, $3]; }
769     | '(' expr ')'                                          { $$ = $2; }
770     | expr '?' expr ':' expr                                { $$ = Expr\Ternary[$1, $3,   $5]; }
771     | expr '?' ':' expr                                     { $$ = Expr\Ternary[$1, null, $4]; }
772     | expr T_COALESCE expr                                  { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
773     | T_ISSET '(' expr_list_allow_comma ')'                 { $$ = Expr\Isset_[$3]; }
774     | T_EMPTY '(' expr ')'                                  { $$ = Expr\Empty_[$3]; }
775     | T_INCLUDE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
776     | T_INCLUDE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; }
777     | T_EVAL '(' expr ')'                                   { $$ = Expr\Eval_[$3]; }
778     | T_REQUIRE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
779     | T_REQUIRE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
780     | T_INT_CAST expr                                       { $$ = Expr\Cast\Int_    [$2]; }
781     | T_DOUBLE_CAST expr
782           { $attrs = attributes();
783             $attrs['kind'] = $this->getFloatCastKind($1);
784             $$ = new Expr\Cast\Double($2, $attrs); }
785     | T_STRING_CAST expr                                    { $$ = Expr\Cast\String_ [$2]; }
786     | T_ARRAY_CAST expr                                     { $$ = Expr\Cast\Array_  [$2]; }
787     | T_OBJECT_CAST expr                                    { $$ = Expr\Cast\Object_ [$2]; }
788     | T_BOOL_CAST expr                                      { $$ = Expr\Cast\Bool_   [$2]; }
789     | T_UNSET_CAST expr                                     { $$ = Expr\Cast\Unset_  [$2]; }
790     | T_EXIT exit_expr
791           { $attrs = attributes();
792             $attrs['kind'] = strtolower($1) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
793             $$ = new Expr\Exit_($2, $attrs); }
794     | '@' expr                                              { $$ = Expr\ErrorSuppress[$2]; }
795     | scalar                                                { $$ = $1; }
796     | '`' backticks_expr '`'                                { $$ = Expr\ShellExec[$2]; }
797     | T_PRINT expr                                          { $$ = Expr\Print_[$2]; }
798     | T_YIELD                                               { $$ = Expr\Yield_[null, null]; }
799     | T_YIELD expr                                          { $$ = Expr\Yield_[$2, null]; }
800     | T_YIELD expr T_DOUBLE_ARROW expr                      { $$ = Expr\Yield_[$4, $2]; }
801     | T_YIELD_FROM expr                                     { $$ = Expr\YieldFrom[$2]; }
802     | T_THROW expr                                          { $$ = Expr\Throw_[$2]; }
803 
804     | T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
805           { $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8]]; }
806     | T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
807           { $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9]]; }
808 
809     | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
810       block_or_error
811           { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; }
812     | T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
813       block_or_error
814           { $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9]]; }
815 ;
816 
817 anonymous_class:
818       T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
819           { $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2);
820             $this->checkClass($$[0], -1); }
821 ;
822 
823 new_expr:
824       T_NEW class_name_reference ctor_arguments             { $$ = Expr\New_[$2, $3]; }
825     | T_NEW anonymous_class
826           { list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
827 ;
828 
829 lexical_vars:
830       /* empty */                                           { $$ = array(); }
831     | T_USE '(' lexical_var_list ')'                        { $$ = $3; }
832 ;
833 
834 lexical_var_list:
835       non_empty_lexical_var_list optional_comma             { $$ = $1; }
836 ;
837 
838 non_empty_lexical_var_list:
839       lexical_var                                           { init($1); }
840     | non_empty_lexical_var_list ',' lexical_var            { push($1, $3); }
841 ;
842 
843 lexical_var:
844       optional_ref plain_variable                           { $$ = Expr\ClosureUse[$2, $1]; }
845 ;
846 
847 function_call:
848       name argument_list                                    { $$ = Expr\FuncCall[$1, $2]; }
849     | callable_expr argument_list                           { $$ = Expr\FuncCall[$1, $2]; }
850     | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
851           { $$ = Expr\StaticCall[$1, $3, $4]; }
852 ;
853 
854 class_name:
855       T_STATIC                                              { $$ = Name[$1]; }
856     | name                                                  { $$ = $1; }
857 ;
858 
859 name:
860       T_STRING                                              { $$ = Name[$1]; }
861     | T_NAME_QUALIFIED                                      { $$ = Name[$1]; }
862     | T_NAME_FULLY_QUALIFIED                                { $$ = Name\FullyQualified[substr($1, 1)]; }
863     | T_NAME_RELATIVE                                       { $$ = Name\Relative[substr($1, 10)]; }
864 ;
865 
866 class_name_reference:
867       class_name                                            { $$ = $1; }
868     | new_variable                                          { $$ = $1; }
869     | '(' expr ')'                                          { $$ = $2; }
870     | error                                                 { $$ = Expr\Error[]; $this->errorState = 2; }
871 ;
872 
873 class_name_or_var:
874       class_name                                            { $$ = $1; }
875     | fully_dereferencable                                  { $$ = $1; }
876 ;
877 
878 exit_expr:
879       /* empty */                                           { $$ = null; }
880     | '(' optional_expr ')'                                 { $$ = $2; }
881 ;
882 
883 backticks_expr:
884       /* empty */                                           { $$ = array(); }
885     | T_ENCAPSED_AND_WHITESPACE
886           { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`')]); }
887     | encaps_list                                           { parseEncapsed($1, '`', true); $$ = $1; }
888 ;
889 
890 ctor_arguments:
891       /* empty */                                           { $$ = array(); }
892     | argument_list                                         { $$ = $1; }
893 ;
894 
895 constant:
896       name                                                  { $$ = Expr\ConstFetch[$1]; }
897     | T_LINE                                                { $$ = Scalar\MagicConst\Line[]; }
898     | T_FILE                                                { $$ = Scalar\MagicConst\File[]; }
899     | T_DIR                                                 { $$ = Scalar\MagicConst\Dir[]; }
900     | T_CLASS_C                                             { $$ = Scalar\MagicConst\Class_[]; }
901     | T_TRAIT_C                                             { $$ = Scalar\MagicConst\Trait_[]; }
902     | T_METHOD_C                                            { $$ = Scalar\MagicConst\Method[]; }
903     | T_FUNC_C                                              { $$ = Scalar\MagicConst\Function_[]; }
904     | T_NS_C                                                { $$ = Scalar\MagicConst\Namespace_[]; }
905 ;
906 
907 class_constant:
908       class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex
909           { $$ = Expr\ClassConstFetch[$1, $3]; }
910     /* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
911        an unfinished static property fetch or unfinished scoped call. */
912     | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
913           { $$ = Expr\ClassConstFetch[$1, new Expr\Error(stackAttributes(#3))]; $this->errorState = 2; }
914 ;
915 
916 array_short_syntax:
917       '[' array_pair_list ']'
918           { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT;
919             $$ = new Expr\Array_($2, $attrs); }
920 ;
921 
922 dereferencable_scalar:
923       T_ARRAY '(' array_pair_list ')'
924           { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
925             $$ = new Expr\Array_($3, $attrs); }
926     | array_short_syntax                                    { $$ = $1; }
927     | T_CONSTANT_ENCAPSED_STRING
928           { $attrs = attributes(); $attrs['kind'] = strKind($1);
929             $$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
930     | '"' encaps_list '"'
931           { $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
932             parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
933 ;
934 
935 scalar:
936       T_LNUMBER                                             { $$ = $this->parseLNumber($1, attributes()); }
937     | T_DNUMBER                                             { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
938     | dereferencable_scalar                                 { $$ = $1; }
939     | constant                                              { $$ = $1; }
940     | class_constant                                        { $$ = $1; }
941     | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
942           { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); }
943     | T_START_HEREDOC T_END_HEREDOC
944           { $$ = $this->parseDocString($1, '', $2, attributes(), stackAttributes(#2), true); }
945     | T_START_HEREDOC encaps_list T_END_HEREDOC
946           { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); }
947 ;
948 
949 optional_expr:
950       /* empty */                                           { $$ = null; }
951     | expr                                                  { $$ = $1; }
952 ;
953 
954 fully_dereferencable:
955       variable                                              { $$ = $1; }
956     | '(' expr ')'                                          { $$ = $2; }
957     | dereferencable_scalar                                 { $$ = $1; }
958     | class_constant                                        { $$ = $1; }
959 ;
960 
961 array_object_dereferencable:
962       fully_dereferencable                                  { $$ = $1; }
963     | constant                                              { $$ = $1; }
964 ;
965 
966 callable_expr:
967       callable_variable                                     { $$ = $1; }
968     | '(' expr ')'                                          { $$ = $2; }
969     | dereferencable_scalar                                 { $$ = $1; }
970 ;
971 
972 callable_variable:
973       simple_variable                                       { $$ = Expr\Variable[$1]; }
974     | array_object_dereferencable '[' optional_expr ']'     { $$ = Expr\ArrayDimFetch[$1, $3]; }
975     | array_object_dereferencable '{' expr '}'              { $$ = Expr\ArrayDimFetch[$1, $3]; }
976     | function_call                                         { $$ = $1; }
977     | array_object_dereferencable T_OBJECT_OPERATOR property_name argument_list
978           { $$ = Expr\MethodCall[$1, $3, $4]; }
979     | array_object_dereferencable T_NULLSAFE_OBJECT_OPERATOR property_name argument_list
980           { $$ = Expr\NullsafeMethodCall[$1, $3, $4]; }
981 ;
982 
983 optional_plain_variable:
984       /* empty */                                           { $$ = null; }
985     | plain_variable                                        { $$ = $1; }
986 ;
987 
988 variable:
989       callable_variable                                     { $$ = $1; }
990     | static_member                                         { $$ = $1; }
991     | array_object_dereferencable T_OBJECT_OPERATOR property_name
992           { $$ = Expr\PropertyFetch[$1, $3]; }
993     | array_object_dereferencable T_NULLSAFE_OBJECT_OPERATOR property_name
994           { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
995 ;
996 
997 simple_variable:
998       T_VARIABLE                                            { $$ = parseVar($1); }
999     | '$' '{' expr '}'                                      { $$ = $3; }
1000     | '$' simple_variable                                   { $$ = Expr\Variable[$2]; }
1001     | '$' error                                             { $$ = Expr\Error[]; $this->errorState = 2; }
1002 ;
1003 
1004 static_member_prop_name:
1005       simple_variable
1006           { $var = $1; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; }
1007 ;
1008 
1009 static_member:
1010       class_name_or_var T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
1011           { $$ = Expr\StaticPropertyFetch[$1, $3]; }
1012 ;
1013 
1014 new_variable:
1015       simple_variable                                       { $$ = Expr\Variable[$1]; }
1016     | new_variable '[' optional_expr ']'                    { $$ = Expr\ArrayDimFetch[$1, $3]; }
1017     | new_variable '{' expr '}'                             { $$ = Expr\ArrayDimFetch[$1, $3]; }
1018     | new_variable T_OBJECT_OPERATOR property_name          { $$ = Expr\PropertyFetch[$1, $3]; }
1019     | new_variable T_NULLSAFE_OBJECT_OPERATOR property_name { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
1020     | class_name T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
1021           { $$ = Expr\StaticPropertyFetch[$1, $3]; }
1022     | new_variable T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
1023           { $$ = Expr\StaticPropertyFetch[$1, $3]; }
1024 ;
1025 
1026 member_name:
1027       identifier_ex                                         { $$ = $1; }
1028     | '{' expr '}'                                          { $$ = $2; }
1029     | simple_variable                                       { $$ = Expr\Variable[$1]; }
1030 ;
1031 
1032 property_name:
1033       identifier                                            { $$ = $1; }
1034     | '{' expr '}'                                          { $$ = $2; }
1035     | simple_variable                                       { $$ = Expr\Variable[$1]; }
1036     | error                                                 { $$ = Expr\Error[]; $this->errorState = 2; }
1037 ;
1038 
1039 list_expr:
1040       T_LIST '(' inner_array_pair_list ')'                  { $$ = Expr\List_[$3]; }
1041 ;
1042 
1043 array_pair_list:
1044       inner_array_pair_list
1045           { $$ = $1; $end = count($$)-1; if ($$[$end] === null) array_pop($$); }
1046 ;
1047 
1048 comma_or_error:
1049       ','
1050     | error
1051           { /* do nothing -- prevent default action of $$=$1. See #551. */ }
1052 ;
1053 
1054 inner_array_pair_list:
1055       inner_array_pair_list comma_or_error array_pair       { push($1, $3); }
1056     | array_pair                                            { init($1); }
1057 ;
1058 
1059 array_pair:
1060       expr                                                  { $$ = Expr\ArrayItem[$1, null, false]; }
1061     | '&' variable                                          { $$ = Expr\ArrayItem[$2, null, true]; }
1062     | list_expr                                             { $$ = Expr\ArrayItem[$1, null, false]; }
1063     | expr T_DOUBLE_ARROW expr                              { $$ = Expr\ArrayItem[$3, $1,   false]; }
1064     | expr T_DOUBLE_ARROW '&' variable                      { $$ = Expr\ArrayItem[$4, $1,   true]; }
1065     | expr T_DOUBLE_ARROW list_expr                         { $$ = Expr\ArrayItem[$3, $1,   false]; }
1066     | T_ELLIPSIS expr                                       { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
1067     | /* empty */                                           { $$ = null; }
1068 ;
1069 
1070 encaps_list:
1071       encaps_list encaps_var                                { push($1, $2); }
1072     | encaps_list encaps_string_part                        { push($1, $2); }
1073     | encaps_var                                            { init($1); }
1074     | encaps_string_part encaps_var                         { init($1, $2); }
1075 ;
1076 
1077 encaps_string_part:
1078       T_ENCAPSED_AND_WHITESPACE                             { $$ = Scalar\EncapsedStringPart[$1]; }
1079 ;
1080 
1081 encaps_str_varname:
1082       T_STRING_VARNAME                                      { $$ = Expr\Variable[$1]; }
1083 ;
1084 
1085 encaps_var:
1086       plain_variable                                        { $$ = $1; }
1087     | plain_variable '[' encaps_var_offset ']'              { $$ = Expr\ArrayDimFetch[$1, $3]; }
1088     | plain_variable T_OBJECT_OPERATOR identifier           { $$ = Expr\PropertyFetch[$1, $3]; }
1089     | plain_variable T_NULLSAFE_OBJECT_OPERATOR identifier  { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
1090     | T_DOLLAR_OPEN_CURLY_BRACES expr '}'                   { $$ = Expr\Variable[$2]; }
1091     | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}'       { $$ = Expr\Variable[$2]; }
1092     | T_DOLLAR_OPEN_CURLY_BRACES encaps_str_varname '[' expr ']' '}'
1093           { $$ = Expr\ArrayDimFetch[$2, $4]; }
1094     | T_CURLY_OPEN variable '}'                             { $$ = $2; }
1095 ;
1096 
1097 encaps_var_offset:
1098       T_STRING                                              { $$ = Scalar\String_[$1]; }
1099     | T_NUM_STRING                                          { $$ = $this->parseNumString($1, attributes()); }
1100     | '-' T_NUM_STRING                                      { $$ = $this->parseNumString('-' . $2, attributes()); }
1101     | plain_variable                                        { $$ = $1; }
1102 ;
1103 
1104 %%
1105