xref: /PHP-Parser/grammar/php.y (revision 9c7a3f8d)
1 %pure_parser
2 %expect 2
3 
4 %right T_THROW
5 %left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
6 %left ','
7 %left T_LOGICAL_OR
8 %left T_LOGICAL_XOR
9 %left T_LOGICAL_AND
10 %right T_PRINT
11 %right T_YIELD
12 %right T_DOUBLE_ARROW
13 %right T_YIELD_FROM
14 %left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL
15 %left '?' ':'
16 %right T_COALESCE
17 %left T_BOOLEAN_OR
18 %left T_BOOLEAN_AND
19 %left '|'
20 %left '^'
21 %left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
22 %nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
23 %nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
24 #if PHP7
25 %left T_SL T_SR
26 %left '+' '-' '.'
27 #endif
28 #if PHP8
29 %left '.'
30 %left T_SL T_SR
31 %left '+' '-'
32 #endif
33 %left '*' '/' '%'
34 %right '!'
35 %nonassoc T_INSTANCEOF
36 %right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
37 %right T_POW
38 %right '['
39 %nonassoc T_NEW T_CLONE
40 %token T_EXIT
41 %token T_IF
42 %left T_ELSEIF
43 %left T_ELSE
44 %left T_ENDIF
45 %token T_LNUMBER
46 %token T_DNUMBER
47 %token T_STRING
48 %token T_STRING_VARNAME
49 %token T_VARIABLE
50 %token T_NUM_STRING
51 %token T_INLINE_HTML
52 %token T_ENCAPSED_AND_WHITESPACE
53 %token T_CONSTANT_ENCAPSED_STRING
54 %token T_ECHO
55 %token T_DO
56 %token T_WHILE
57 %token T_ENDWHILE
58 %token T_FOR
59 %token T_ENDFOR
60 %token T_FOREACH
61 %token T_ENDFOREACH
62 %token T_DECLARE
63 %token T_ENDDECLARE
64 %token T_AS
65 %token T_SWITCH
66 %token T_MATCH
67 %token T_ENDSWITCH
68 %token T_CASE
69 %token T_DEFAULT
70 %token T_BREAK
71 %token T_CONTINUE
72 %token T_GOTO
73 %token T_FUNCTION
74 %token T_FN
75 %token T_CONST
76 %token T_RETURN
77 %token T_TRY
78 %token T_CATCH
79 %token T_FINALLY
80 %token T_THROW
81 %token T_USE
82 %token T_INSTEADOF
83 %token T_GLOBAL
84 %token T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
85 %token T_PUBLIC_SET
86 %token T_PROTECTED_SET
87 %token T_PRIVATE_SET
88 %token T_VAR
89 %token T_UNSET
90 %token T_ISSET
91 %token T_EMPTY
92 %token T_HALT_COMPILER
93 %token T_CLASS
94 %token T_TRAIT
95 %token T_INTERFACE
96 %token T_ENUM
97 %token T_EXTENDS
98 %token T_IMPLEMENTS
99 %token T_OBJECT_OPERATOR
100 %token T_NULLSAFE_OBJECT_OPERATOR
101 %token T_DOUBLE_ARROW
102 %token T_LIST
103 %token T_ARRAY
104 %token T_CALLABLE
105 %token T_CLASS_C
106 %token T_TRAIT_C
107 %token T_METHOD_C
108 %token T_FUNC_C
109 %token T_PROPERTY_C
110 %token T_LINE
111 %token T_FILE
112 %token T_START_HEREDOC
113 %token T_END_HEREDOC
114 %token T_DOLLAR_OPEN_CURLY_BRACES
115 %token T_CURLY_OPEN
116 %token T_PAAMAYIM_NEKUDOTAYIM
117 %token T_NAMESPACE
118 %token T_NS_C
119 %token T_DIR
120 %token T_NS_SEPARATOR
121 %token T_ELLIPSIS
122 %token T_NAME_FULLY_QUALIFIED
123 %token T_NAME_QUALIFIED
124 %token T_NAME_RELATIVE
125 %token T_ATTRIBUTE
126 %token T_ENUM
127 
128 %%
129 
130 start:
131     top_statement_list                                      { $$ = $this->handleNamespaces($1); }
132 ;
133 
134 top_statement_list_ex:
135       top_statement_list_ex top_statement                   { pushNormalizing($1, $2); }
136     | /* empty */                                           { init(); }
137 ;
138 
139 top_statement_list:
140       top_statement_list_ex
141           { makeZeroLengthNop($nop);
142             if ($nop !== null) { $1[] = $nop; } $$ = $1; }
143 ;
144 
145 ampersand:
146       T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
147     | T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
148 ;
149 
150 reserved_non_modifiers:
151       T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
152     | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_DO | T_WHILE
153     | T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH
154     | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
155     | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
156     | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
157     | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_FN
158     | T_MATCH | T_ENUM
159     | T_ECHO { $$ = $1; if ($$ === "<?=") $this->emitError(new Error('Cannot use "<?=" as an identifier', attributes())); }
160 ;
161 
162 semi_reserved:
163       reserved_non_modifiers
164     | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY
165 ;
166 
167 identifier_maybe_reserved:
168       T_STRING                                              { $$ = Node\Identifier[$1]; }
169     | semi_reserved                                         { $$ = Node\Identifier[$1]; }
170 ;
171 
172 identifier_not_reserved:
173       T_STRING                                              { $$ = Node\Identifier[$1]; }
174 ;
175 
176 reserved_non_modifiers_identifier:
177       reserved_non_modifiers                                { $$ = Node\Identifier[$1]; }
178 ;
179 
180 namespace_declaration_name:
181       T_STRING                                              { $$ = Name[$1]; }
182     | semi_reserved                                         { $$ = Name[$1]; }
183     | T_NAME_QUALIFIED                                      { $$ = Name[$1]; }
184 ;
185 
186 namespace_name:
187       T_STRING                                              { $$ = Name[$1]; }
188     | T_NAME_QUALIFIED                                      { $$ = Name[$1]; }
189 ;
190 
191 legacy_namespace_name:
192       namespace_name
193     | T_NAME_FULLY_QUALIFIED                                { $$ = Name[substr($1, 1)]; }
194 ;
195 
196 plain_variable:
197       T_VARIABLE                                            { $$ = Expr\Variable[parseVar($1)]; }
198 ;
199 
200 semi:
201       ';'                                                   { /* nothing */ }
202     | error                                                 { /* nothing */ }
203 ;
204 
205 no_comma:
206       /* empty */ { /* nothing */ }
207     | ',' { $this->emitError(new Error('A trailing comma is not allowed here', attributes())); }
208 ;
209 
210 optional_comma:
211       /* empty */
212     | ','
213 ;
214 
215 attribute_decl:
216       class_name                                            { $$ = Node\Attribute[$1, []]; }
217     | class_name argument_list                              { $$ = Node\Attribute[$1, $2]; }
218 ;
219 
220 attribute_group:
221       attribute_decl                                        { init($1); }
222     | attribute_group ',' attribute_decl                    { push($1, $3); }
223 ;
224 
225 attribute:
226       T_ATTRIBUTE attribute_group optional_comma ']'        { $$ = Node\AttributeGroup[$2]; }
227 ;
228 
229 attributes:
230       attribute                                             { init($1); }
231     | attributes attribute                                  { push($1, $2); }
232 ;
233 
234 optional_attributes:
235       /* empty */                                           { $$ = []; }
236     | attributes
237 ;
238 
239 top_statement:
240       statement
241     | function_declaration_statement
242     | class_declaration_statement
243     | T_HALT_COMPILER '(' ')' ';'
244           { $$ = Stmt\HaltCompiler[$this->handleHaltCompiler()]; }
245     | T_NAMESPACE namespace_declaration_name semi
246           { $$ = Stmt\Namespace_[$2, null];
247             $$->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON);
248             $this->checkNamespace($$); }
249     | T_NAMESPACE namespace_declaration_name '{' top_statement_list '}'
250           { $$ = Stmt\Namespace_[$2, $4];
251             $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
252             $this->checkNamespace($$); }
253     | T_NAMESPACE '{' top_statement_list '}'
254           { $$ = Stmt\Namespace_[null, $3];
255             $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
256             $this->checkNamespace($$); }
257     | T_USE use_declarations semi                           { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; }
258     | T_USE use_type use_declarations semi                  { $$ = Stmt\Use_[$3, $2]; }
259     | group_use_declaration
260     | T_CONST constant_declaration_list semi                { $$ = Stmt\Const_[$2]; }
261 ;
262 
263 use_type:
264       T_FUNCTION                                            { $$ = Stmt\Use_::TYPE_FUNCTION; }
265     | T_CONST                                               { $$ = Stmt\Use_::TYPE_CONSTANT; }
266 ;
267 
268 group_use_declaration:
269       T_USE use_type legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations '}' semi
270           { $$ = Stmt\GroupUse[$3, $6, $2]; }
271     | T_USE legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations '}' semi
272           { $$ = Stmt\GroupUse[$2, $5, Stmt\Use_::TYPE_UNKNOWN]; }
273 ;
274 
275 unprefixed_use_declarations:
276       non_empty_unprefixed_use_declarations optional_comma
277 ;
278 
279 non_empty_unprefixed_use_declarations:
280       non_empty_unprefixed_use_declarations ',' unprefixed_use_declaration
281           { push($1, $3); }
282     | unprefixed_use_declaration                            { init($1); }
283 ;
284 
285 use_declarations:
286       non_empty_use_declarations no_comma
287 ;
288 
289 non_empty_use_declarations:
290       non_empty_use_declarations ',' use_declaration        { push($1, $3); }
291     | use_declaration                                       { init($1); }
292 ;
293 
294 inline_use_declarations:
295       non_empty_inline_use_declarations optional_comma
296 ;
297 
298 non_empty_inline_use_declarations:
299       non_empty_inline_use_declarations ',' inline_use_declaration
300           { push($1, $3); }
301     | inline_use_declaration                                { init($1); }
302 ;
303 
304 unprefixed_use_declaration:
305       namespace_name
306           { $$ = Node\UseItem[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
307     | namespace_name T_AS identifier_not_reserved
308           { $$ = Node\UseItem[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
309 ;
310 
311 use_declaration:
312       legacy_namespace_name
313           { $$ = Node\UseItem[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); }
314     | legacy_namespace_name T_AS identifier_not_reserved
315           { $$ = Node\UseItem[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); }
316 ;
317 
318 inline_use_declaration:
319       unprefixed_use_declaration                            { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; }
320     | use_type unprefixed_use_declaration                   { $$ = $2; $$->type = $1; }
321 ;
322 
323 constant_declaration_list:
324       non_empty_constant_declaration_list no_comma
325 ;
326 
327 non_empty_constant_declaration_list:
328       non_empty_constant_declaration_list ',' constant_declaration
329           { push($1, $3); }
330     | constant_declaration                                  { init($1); }
331 ;
332 
333 constant_declaration:
334     identifier_not_reserved '=' expr                        { $$ = Node\Const_[$1, $3]; }
335 ;
336 
337 class_const_list:
338       non_empty_class_const_list no_comma
339 ;
340 
341 non_empty_class_const_list:
342       non_empty_class_const_list ',' class_const            { push($1, $3); }
343     | class_const                                           { init($1); }
344 ;
345 
346 class_const:
347       T_STRING '=' expr
348           { $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
349     | semi_reserved '=' expr
350           { $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
351 ;
352 
353 inner_statement_list_ex:
354       inner_statement_list_ex inner_statement               { pushNormalizing($1, $2); }
355     | /* empty */                                           { init(); }
356 ;
357 
358 inner_statement_list:
359       inner_statement_list_ex
360           { makeZeroLengthNop($nop);
361             if ($nop !== null) { $1[] = $nop; } $$ = $1; }
362 ;
363 
364 inner_statement:
365       statement
366     | function_declaration_statement
367     | class_declaration_statement
368     | T_HALT_COMPILER
369           { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
370 ;
371 
372 non_empty_statement:
373       '{' inner_statement_list '}'                          { $$ = Stmt\Block[$2]; }
374     | T_IF '(' expr ')' blocklike_statement elseif_list else_single
375           { $$ = Stmt\If_[$3, ['stmts' => $5, 'elseifs' => $6, 'else' => $7]]; }
376     | T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
377           { $$ = Stmt\If_[$3, ['stmts' => $6, 'elseifs' => $7, 'else' => $8]]; }
378     | T_WHILE '(' expr ')' while_statement                  { $$ = Stmt\While_[$3, $5]; }
379     | T_DO blocklike_statement T_WHILE '(' expr ')' ';'     { $$ = Stmt\Do_   [$5, $2]; }
380     | T_FOR '(' for_expr ';'  for_expr ';' for_expr ')' for_statement
381           { $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
382     | T_SWITCH '(' expr ')' switch_case_list                { $$ = Stmt\Switch_[$3, $5]; }
383     | T_BREAK optional_expr semi                            { $$ = Stmt\Break_[$2]; }
384     | T_CONTINUE optional_expr semi                         { $$ = Stmt\Continue_[$2]; }
385     | T_RETURN optional_expr semi                           { $$ = Stmt\Return_[$2]; }
386     | T_GLOBAL global_var_list semi                         { $$ = Stmt\Global_[$2]; }
387     | T_STATIC static_var_list semi                         { $$ = Stmt\Static_[$2]; }
388     | T_ECHO expr_list_forbid_comma semi                    { $$ = Stmt\Echo_[$2]; }
389     | T_INLINE_HTML {
390         $$ = Stmt\InlineHTML[$1];
391         $$->setAttribute('hasLeadingNewline', $this->inlineHtmlHasLeadingNewline(#1));
392     }
393     | expr semi                                             { $$ = Stmt\Expression[$1]; }
394     | T_UNSET '(' variables_list ')' semi                   { $$ = Stmt\Unset_[$3]; }
395     | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
396           { $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; }
397     | T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
398           { $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; }
399     | T_FOREACH '(' expr error ')' foreach_statement
400           { $$ = Stmt\Foreach_[$3, new Expr\Error(stackAttributes(#4)), ['stmts' => $6]]; }
401     | T_DECLARE '(' declare_list ')' declare_statement      { $$ = Stmt\Declare_[$3, $5]; }
402     | T_TRY '{' inner_statement_list '}' catches optional_finally
403           { $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
404     | T_GOTO identifier_not_reserved semi                   { $$ = Stmt\Goto_[$2]; }
405     | identifier_not_reserved ':'                           { $$ = Stmt\Label[$1]; }
406     | error                                                 { $$ = null; /* means: no statement */ }
407 ;
408 
409 statement:
410       non_empty_statement
411     | ';'                                                   { makeNop($$); }
412 ;
413 
414 blocklike_statement:
415      statement                                              { toBlock($1); }
416 ;
417 
418 catches:
419       /* empty */                                           { init(); }
420     | catches catch                                         { push($1, $2); }
421 ;
422 
423 name_union:
424       name                                                  { init($1); }
425     | name_union '|' name                                   { push($1, $3); }
426 ;
427 
428 catch:
429     T_CATCH '(' name_union optional_plain_variable ')' '{' inner_statement_list '}'
430         { $$ = Stmt\Catch_[$3, $4, $7]; }
431 ;
432 
433 optional_finally:
434       /* empty */                                           { $$ = null; }
435     | T_FINALLY '{' inner_statement_list '}'                { $$ = Stmt\Finally_[$3]; }
436 ;
437 
438 variables_list:
439       non_empty_variables_list optional_comma
440 ;
441 
442 non_empty_variables_list:
443       variable                                              { init($1); }
444     | non_empty_variables_list ',' variable                 { push($1, $3); }
445 ;
446 
447 optional_ref:
448       /* empty */                                           { $$ = false; }
449     | ampersand                                             { $$ = true; }
450 ;
451 
452 optional_arg_ref:
453       /* empty */                                           { $$ = false; }
454     | T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG                 { $$ = true; }
455 ;
456 
457 optional_ellipsis:
458       /* empty */                                           { $$ = false; }
459     | T_ELLIPSIS                                            { $$ = true; }
460 ;
461 
462 block_or_error:
463       '{' inner_statement_list '}'                          { $$ = $2; }
464     | error                                                 { $$ = []; }
465 ;
466 
467 fn_identifier:
468       identifier_not_reserved
469     | T_READONLY                                            { $$ = Node\Identifier[$1]; }
470     | T_EXIT                                                { $$ = Node\Identifier[$1]; }
471 ;
472 
473 function_declaration_statement:
474       T_FUNCTION optional_ref fn_identifier '(' parameter_list ')' optional_return_type block_or_error
475           { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
476     | attributes T_FUNCTION optional_ref fn_identifier '(' parameter_list ')' optional_return_type block_or_error
477           { $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
478 ;
479 
480 class_declaration_statement:
481       class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
482           { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6, 'attrGroups' => []]];
483             $this->checkClass($$, #2); }
484     | attributes class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
485           { $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
486             $this->checkClass($$, #3); }
487     | optional_attributes T_INTERFACE identifier_not_reserved interface_extends_list '{' class_statement_list '}'
488           { $$ = Stmt\Interface_[$3, ['extends' => $4, 'stmts' => $6, 'attrGroups' => $1]];
489             $this->checkInterface($$, #3); }
490     | optional_attributes T_TRAIT identifier_not_reserved '{' class_statement_list '}'
491           { $$ = Stmt\Trait_[$3, ['stmts' => $5, 'attrGroups' => $1]]; }
492     | optional_attributes T_ENUM identifier_not_reserved enum_scalar_type implements_list '{' class_statement_list '}'
493           { $$ = Stmt\Enum_[$3, ['scalarType' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
494             $this->checkEnum($$, #3); }
495 ;
496 
497 enum_scalar_type:
498       /* empty */                                           { $$ = null; }
499     | ':' type                                              { $$ = $2; }
500 
501 enum_case_expr:
502       /* empty */                                           { $$ = null; }
503     | '=' expr                                              { $$ = $2; }
504 ;
505 
506 class_entry_type:
507       T_CLASS                                               { $$ = 0; }
508     | class_modifiers T_CLASS
509 ;
510 
511 class_modifiers:
512       class_modifier
513     | class_modifiers class_modifier                        { $this->checkClassModifier($1, $2, #2); $$ = $1 | $2; }
514 ;
515 
516 class_modifier:
517       T_ABSTRACT                                            { $$ = Modifiers::ABSTRACT; }
518     | T_FINAL                                               { $$ = Modifiers::FINAL; }
519     | T_READONLY                                            { $$ = Modifiers::READONLY; }
520 ;
521 
522 extends_from:
523       /* empty */                                           { $$ = null; }
524     | T_EXTENDS class_name                                  { $$ = $2; }
525 ;
526 
527 interface_extends_list:
528       /* empty */                                           { $$ = array(); }
529     | T_EXTENDS class_name_list                             { $$ = $2; }
530 ;
531 
532 implements_list:
533       /* empty */                                           { $$ = array(); }
534     | T_IMPLEMENTS class_name_list                          { $$ = $2; }
535 ;
536 
537 class_name_list:
538       non_empty_class_name_list no_comma
539 ;
540 
541 non_empty_class_name_list:
542       class_name                                            { init($1); }
543     | non_empty_class_name_list ',' class_name              { push($1, $3); }
544 ;
545 
546 for_statement:
547       blocklike_statement
548     | ':' inner_statement_list T_ENDFOR ';'                 { $$ = $2; }
549 ;
550 
551 foreach_statement:
552       blocklike_statement
553     | ':' inner_statement_list T_ENDFOREACH ';'             { $$ = $2; }
554 ;
555 
556 declare_statement:
557       non_empty_statement                                   { toBlock($1); }
558     | ';'                                                   { $$ = null; }
559     | ':' inner_statement_list T_ENDDECLARE ';'             { $$ = $2; }
560 ;
561 
562 declare_list:
563       non_empty_declare_list no_comma
564 ;
565 
566 non_empty_declare_list:
567       declare_list_element                                  { init($1); }
568     | non_empty_declare_list ',' declare_list_element       { push($1, $3); }
569 ;
570 
571 declare_list_element:
572       identifier_not_reserved '=' expr                      { $$ = Node\DeclareItem[$1, $3]; }
573 ;
574 
575 switch_case_list:
576       '{' case_list '}'                                     { $$ = $2; }
577     | '{' ';' case_list '}'                                 { $$ = $3; }
578     | ':' case_list T_ENDSWITCH ';'                         { $$ = $2; }
579     | ':' ';' case_list T_ENDSWITCH ';'                     { $$ = $3; }
580 ;
581 
582 case_list:
583       /* empty */                                           { init(); }
584     | case_list case                                        { push($1, $2); }
585 ;
586 
587 case:
588       T_CASE expr case_separator inner_statement_list_ex    { $$ = Stmt\Case_[$2, $4]; }
589     | T_DEFAULT case_separator inner_statement_list_ex      { $$ = Stmt\Case_[null, $3]; }
590 ;
591 
592 case_separator:
593       ':'
594     | ';'
595 ;
596 
597 match:
598       T_MATCH '(' expr ')' '{' match_arm_list '}'           { $$ = Expr\Match_[$3, $6]; }
599 ;
600 
601 match_arm_list:
602       /* empty */                                           { $$ = []; }
603     | non_empty_match_arm_list optional_comma
604 ;
605 
606 non_empty_match_arm_list:
607       match_arm                                             { init($1); }
608     | non_empty_match_arm_list ',' match_arm                { push($1, $3); }
609 ;
610 
611 match_arm:
612       expr_list_allow_comma T_DOUBLE_ARROW expr             { $$ = Node\MatchArm[$1, $3]; }
613     | T_DEFAULT optional_comma T_DOUBLE_ARROW expr          { $$ = Node\MatchArm[null, $4]; }
614 ;
615 
616 while_statement:
617       blocklike_statement                                   { $$ = $1; }
618     | ':' inner_statement_list T_ENDWHILE ';'               { $$ = $2; }
619 ;
620 
621 elseif_list:
622       /* empty */                                           { init(); }
623     | elseif_list elseif                                    { push($1, $2); }
624 ;
625 
626 elseif:
627       T_ELSEIF '(' expr ')' blocklike_statement             { $$ = Stmt\ElseIf_[$3, $5]; }
628 ;
629 
630 new_elseif_list:
631       /* empty */                                           { init(); }
632     | new_elseif_list new_elseif                            { push($1, $2); }
633 ;
634 
635 new_elseif:
636      T_ELSEIF '(' expr ')' ':' inner_statement_list
637          { $$ = Stmt\ElseIf_[$3, $6]; $this->fixupAlternativeElse($$); }
638 ;
639 
640 else_single:
641       /* empty */                                           { $$ = null; }
642     | T_ELSE blocklike_statement                            { $$ = Stmt\Else_[$2]; }
643 ;
644 
645 new_else_single:
646       /* empty */                                           { $$ = null; }
647     | T_ELSE ':' inner_statement_list
648           { $$ = Stmt\Else_[$3]; $this->fixupAlternativeElse($$); }
649 ;
650 
651 foreach_variable:
652       variable                                              { $$ = array($1, false); }
653     | ampersand variable                                    { $$ = array($2, true); }
654     | list_expr                                             { $$ = array($1, false); }
655     | array_short_syntax
656           { $$ = array($this->fixupArrayDestructuring($1), false); }
657 ;
658 
659 parameter_list:
660       non_empty_parameter_list optional_comma
661     | /* empty */                                           { $$ = array(); }
662 ;
663 
664 non_empty_parameter_list:
665       parameter                                             { init($1); }
666     | non_empty_parameter_list ',' parameter                { push($1, $3); }
667 ;
668 
669 optional_property_modifiers:
670       /* empty */               { $$ = 0; }
671     | optional_property_modifiers property_modifier
672           { $this->checkModifier($1, $2, #2); $$ = $1 | $2; }
673 ;
674 
675 property_modifier:
676       T_PUBLIC                  { $$ = Modifiers::PUBLIC; }
677     | T_PROTECTED               { $$ = Modifiers::PROTECTED; }
678     | T_PRIVATE                 { $$ = Modifiers::PRIVATE; }
679     | T_PUBLIC_SET              { $$ = Modifiers::PUBLIC_SET; }
680     | T_PROTECTED_SET           { $$ = Modifiers::PROTECTED_SET; }
681     | T_PRIVATE_SET             { $$ = Modifiers::PRIVATE_SET; }
682     | T_READONLY                { $$ = Modifiers::READONLY; }
683 ;
684 
685 parameter:
686       optional_attributes optional_property_modifiers optional_type_without_static
687       optional_arg_ref optional_ellipsis plain_variable optional_property_hook_list
688           { $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1, $7);
689             $this->checkParam($$); }
690     | optional_attributes optional_property_modifiers optional_type_without_static
691       optional_arg_ref optional_ellipsis plain_variable '=' expr optional_property_hook_list
692           { $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1, $9);
693             $this->checkParam($$); }
694     | optional_attributes optional_property_modifiers optional_type_without_static
695       optional_arg_ref optional_ellipsis error
696           { $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); }
697 ;
698 
699 type_expr:
700       type
701     | '?' type                                              { $$ = Node\NullableType[$2]; }
702     | union_type                                            { $$ = Node\UnionType[$1]; }
703     | intersection_type
704 ;
705 
706 type:
707       type_without_static
708     | T_STATIC                                              { $$ = Node\Name['static']; }
709 ;
710 
711 type_without_static:
712       name                                                  { $$ = $this->handleBuiltinTypes($1); }
713     | T_ARRAY                                               { $$ = Node\Identifier['array']; }
714     | T_CALLABLE                                            { $$ = Node\Identifier['callable']; }
715 ;
716 
717 union_type_element:
718       type
719     | '(' intersection_type ')' { $$ = $2; }
720 ;
721 
722 union_type:
723       union_type_element '|' union_type_element             { init($1, $3); }
724     | union_type '|' union_type_element                     { push($1, $3); }
725 ;
726 
727 union_type_without_static_element:
728                 type_without_static
729         |        '(' intersection_type_without_static ')' { $$ = $2; }
730 ;
731 
732 union_type_without_static:
733       union_type_without_static_element '|' union_type_without_static_element   { init($1, $3); }
734     | union_type_without_static '|' union_type_without_static_element           { push($1, $3); }
735 ;
736 
737 intersection_type_list:
738       type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type   { init($1, $3); }
739     | intersection_type_list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
740           { push($1, $3); }
741 ;
742 
743 intersection_type:
744       intersection_type_list { $$ = Node\IntersectionType[$1]; }
745 ;
746 
747 intersection_type_without_static_list:
748       type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
749           { init($1, $3); }
750     | intersection_type_without_static_list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
751           { push($1, $3); }
752 ;
753 
754 intersection_type_without_static:
755       intersection_type_without_static_list { $$ = Node\IntersectionType[$1]; }
756 ;
757 
758 type_expr_without_static:
759       type_without_static
760     | '?' type_without_static                               { $$ = Node\NullableType[$2]; }
761     | union_type_without_static                             { $$ = Node\UnionType[$1]; }
762     | intersection_type_without_static
763 ;
764 
765 optional_type_without_static:
766       /* empty */                                           { $$ = null; }
767     | type_expr_without_static
768 ;
769 
770 optional_return_type:
771       /* empty */                                           { $$ = null; }
772     | ':' type_expr                                         { $$ = $2; }
773     | ':' error                                             { $$ = null; }
774 ;
775 
776 argument_list:
777       '(' ')'                                               { $$ = array(); }
778     | '(' non_empty_argument_list optional_comma ')'        { $$ = $2; }
779     | '(' variadic_placeholder ')'                          { init($2); }
780 ;
781 
782 variadic_placeholder:
783       T_ELLIPSIS                                            { $$ = Node\VariadicPlaceholder[]; }
784 ;
785 
786 non_empty_argument_list:
787       argument                                              { init($1); }
788     | non_empty_argument_list ',' argument                  { push($1, $3); }
789 ;
790 
791 argument:
792       expr                                                  { $$ = Node\Arg[$1, false, false]; }
793     | ampersand variable                                    { $$ = Node\Arg[$2, true, false]; }
794     | T_ELLIPSIS expr                                       { $$ = Node\Arg[$2, false, true]; }
795     | identifier_maybe_reserved ':' expr
796           { $$ = new Node\Arg($3, false, false, attributes(), $1); }
797 ;
798 
799 global_var_list:
800       non_empty_global_var_list no_comma
801 ;
802 
803 non_empty_global_var_list:
804       non_empty_global_var_list ',' global_var              { push($1, $3); }
805     | global_var                                            { init($1); }
806 ;
807 
808 global_var:
809       simple_variable
810 ;
811 
812 static_var_list:
813       non_empty_static_var_list no_comma
814 ;
815 
816 non_empty_static_var_list:
817       non_empty_static_var_list ',' static_var              { push($1, $3); }
818     | static_var                                            { init($1); }
819 ;
820 
821 static_var:
822       plain_variable                                        { $$ = Node\StaticVar[$1, null]; }
823     | plain_variable '=' expr                               { $$ = Node\StaticVar[$1, $3]; }
824 ;
825 
826 class_statement_list_ex:
827       class_statement_list_ex class_statement               { if ($2 !== null) { push($1, $2); } else { $$ = $1; } }
828     | /* empty */                                           { init(); }
829 ;
830 
831 class_statement_list:
832       class_statement_list_ex
833           { makeZeroLengthNop($nop);
834             if ($nop !== null) { $1[] = $nop; } $$ = $1; }
835 ;
836 
837 class_statement:
838       optional_attributes variable_modifiers optional_type_without_static property_declaration_list semi
839           { $$ = new Stmt\Property($2, $4, attributes(), $3, $1); }
840 #if PHP8
841     | optional_attributes variable_modifiers optional_type_without_static property_declaration_list '{' property_hook_list '}'
842           { $$ = new Stmt\Property($2, $4, attributes(), $3, $1, $6);
843             $this->checkPropertyHookList($6, #5); }
844 #endif
845     | optional_attributes method_modifiers T_CONST class_const_list semi
846           { $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
847             $this->checkClassConst($$, #2); }
848     | optional_attributes method_modifiers T_CONST type_expr class_const_list semi
849           { $$ = new Stmt\ClassConst($5, $2, attributes(), $1, $4);
850             $this->checkClassConst($$, #2); }
851     | optional_attributes method_modifiers T_FUNCTION optional_ref identifier_maybe_reserved '(' parameter_list ')'
852       optional_return_type method_body
853           { $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];
854             $this->checkClassMethod($$, #2); }
855     | T_USE class_name_list trait_adaptations               { $$ = Stmt\TraitUse[$2, $3]; }
856     | optional_attributes T_CASE identifier_maybe_reserved enum_case_expr semi
857          { $$ = Stmt\EnumCase[$3, $4, $1]; }
858     | error                                                 { $$ = null; /* will be skipped */ }
859 ;
860 
861 trait_adaptations:
862       ';'                                                   { $$ = array(); }
863     | '{' trait_adaptation_list '}'                         { $$ = $2; }
864 ;
865 
866 trait_adaptation_list:
867       /* empty */                                           { init(); }
868     | trait_adaptation_list trait_adaptation                { push($1, $2); }
869 ;
870 
871 trait_adaptation:
872       trait_method_reference_fully_qualified T_INSTEADOF class_name_list ';'
873           { $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; }
874     | trait_method_reference T_AS member_modifier identifier_maybe_reserved ';'
875           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; }
876     | trait_method_reference T_AS member_modifier ';'
877           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; }
878     | trait_method_reference T_AS identifier_not_reserved ';'
879           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
880     | trait_method_reference T_AS reserved_non_modifiers_identifier ';'
881           { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; }
882 ;
883 
884 trait_method_reference_fully_qualified:
885       name T_PAAMAYIM_NEKUDOTAYIM identifier_maybe_reserved { $$ = array($1, $3); }
886 ;
887 trait_method_reference:
888       trait_method_reference_fully_qualified
889     | identifier_maybe_reserved                             { $$ = array(null, $1); }
890 ;
891 
892 method_body:
893       ';' /* abstract method */                             { $$ = null; }
894     | block_or_error
895 ;
896 
897 variable_modifiers:
898       non_empty_member_modifiers
899     | T_VAR                                                 { $$ = 0; }
900 ;
901 
902 method_modifiers:
903       /* empty */                                           { $$ = 0; }
904     | non_empty_member_modifiers
905 ;
906 
907 non_empty_member_modifiers:
908       member_modifier
909     | non_empty_member_modifiers member_modifier            { $this->checkModifier($1, $2, #2); $$ = $1 | $2; }
910 ;
911 
912 member_modifier:
913       T_PUBLIC                                              { $$ = Modifiers::PUBLIC; }
914     | T_PROTECTED                                           { $$ = Modifiers::PROTECTED; }
915     | T_PRIVATE                                             { $$ = Modifiers::PRIVATE; }
916     | T_PUBLIC_SET                                          { $$ = Modifiers::PUBLIC_SET; }
917     | T_PROTECTED_SET                                       { $$ = Modifiers::PROTECTED_SET; }
918     | T_PRIVATE_SET                                         { $$ = Modifiers::PRIVATE_SET; }
919     | T_STATIC                                              { $$ = Modifiers::STATIC; }
920     | T_ABSTRACT                                            { $$ = Modifiers::ABSTRACT; }
921     | T_FINAL                                               { $$ = Modifiers::FINAL; }
922     | T_READONLY                                            { $$ = Modifiers::READONLY; }
923 ;
924 
925 property_declaration_list:
926       non_empty_property_declaration_list no_comma
927 ;
928 
929 non_empty_property_declaration_list:
930       property_declaration                                  { init($1); }
931     | non_empty_property_declaration_list ',' property_declaration
932           { push($1, $3); }
933 ;
934 
935 property_decl_name:
936       T_VARIABLE                                            { $$ = Node\VarLikeIdentifier[parseVar($1)]; }
937 ;
938 
939 property_declaration:
940       property_decl_name                                    { $$ = Node\PropertyItem[$1, null]; }
941     | property_decl_name '=' expr                           { $$ = Node\PropertyItem[$1, $3]; }
942 ;
943 
944 property_hook_list:
945       /* empty */                                           { $$ = []; }
946     | property_hook_list property_hook                      { push($1, $2); }
947 ;
948 
949 optional_property_hook_list:
950       /* empty */                                           { $$ = []; }
951 #if PHP8
952     | '{' property_hook_list '}'                            { $$ = $2; $this->checkPropertyHookList($2, #1); }
953 #endif
954 ;
955 
956 property_hook:
957       optional_attributes property_hook_modifiers optional_ref identifier_not_reserved property_hook_body
958           { $$ = Node\PropertyHook[$4, $5, ['flags' => $2, 'byRef' => $3, 'params' => [], 'attrGroups' => $1]];
959             $this->checkPropertyHook($$, null); }
960     | optional_attributes property_hook_modifiers optional_ref identifier_not_reserved '(' parameter_list ')' property_hook_body
961           { $$ = Node\PropertyHook[$4, $8, ['flags' => $2, 'byRef' => $3, 'params' => $6, 'attrGroups' => $1]];
962             $this->checkPropertyHook($$, #5); }
963 ;
964 
965 property_hook_body:
966       ';'                                                   { $$ = null; }
967     | '{' inner_statement_list '}'                          { $$ = $2; }
968     | T_DOUBLE_ARROW expr ';'                               { $$ = $2; }
969 ;
970 
971 property_hook_modifiers:
972       /* empty */                                           { $$ = 0; }
973     | property_hook_modifiers member_modifier
974           { $this->checkPropertyHookModifiers($1, $2, #2); $$ = $1 | $2; }
975 ;
976 
977 expr_list_forbid_comma:
978       non_empty_expr_list no_comma
979 ;
980 
981 expr_list_allow_comma:
982       non_empty_expr_list optional_comma
983 ;
984 
985 non_empty_expr_list:
986       non_empty_expr_list ',' expr                          { push($1, $3); }
987     | expr                                                  { init($1); }
988 ;
989 
990 for_expr:
991       /* empty */                                           { $$ = array(); }
992     | expr_list_forbid_comma
993 ;
994 
995 expr:
996       variable
997     | list_expr '=' expr                                    { $$ = Expr\Assign[$1, $3]; }
998     | array_short_syntax '=' expr
999           { $$ = Expr\Assign[$this->fixupArrayDestructuring($1), $3]; }
1000     | variable '=' expr                                     { $$ = Expr\Assign[$1, $3]; }
1001     | variable '=' ampersand variable                       { $$ = Expr\AssignRef[$1, $4]; }
1002     | variable '=' ampersand new_expr
1003           { $$ = Expr\AssignRef[$1, $4];
1004             if (!$this->phpVersion->allowsAssignNewByReference()) {
1005                 $this->emitError(new Error('Cannot assign new by reference', attributes()));
1006             }
1007           }
1008     | new_expr
1009     | match
1010     | T_CLONE expr                                          { $$ = Expr\Clone_[$2]; }
1011     | variable T_PLUS_EQUAL expr                            { $$ = Expr\AssignOp\Plus      [$1, $3]; }
1012     | variable T_MINUS_EQUAL expr                           { $$ = Expr\AssignOp\Minus     [$1, $3]; }
1013     | variable T_MUL_EQUAL expr                             { $$ = Expr\AssignOp\Mul       [$1, $3]; }
1014     | variable T_DIV_EQUAL expr                             { $$ = Expr\AssignOp\Div       [$1, $3]; }
1015     | variable T_CONCAT_EQUAL expr                          { $$ = Expr\AssignOp\Concat    [$1, $3]; }
1016     | variable T_MOD_EQUAL expr                             { $$ = Expr\AssignOp\Mod       [$1, $3]; }
1017     | variable T_AND_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; }
1018     | variable T_OR_EQUAL expr                              { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; }
1019     | variable T_XOR_EQUAL expr                             { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; }
1020     | variable T_SL_EQUAL expr                              { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; }
1021     | variable T_SR_EQUAL expr                              { $$ = Expr\AssignOp\ShiftRight[$1, $3]; }
1022     | variable T_POW_EQUAL expr                             { $$ = Expr\AssignOp\Pow       [$1, $3]; }
1023     | variable T_COALESCE_EQUAL expr                        { $$ = Expr\AssignOp\Coalesce  [$1, $3]; }
1024     | variable T_INC                                        { $$ = Expr\PostInc[$1]; }
1025     | T_INC variable                                        { $$ = Expr\PreInc [$2]; }
1026     | variable T_DEC                                        { $$ = Expr\PostDec[$1]; }
1027     | T_DEC variable                                        { $$ = Expr\PreDec [$2]; }
1028     | expr T_BOOLEAN_OR expr                                { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; }
1029     | expr T_BOOLEAN_AND expr                               { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; }
1030     | expr T_LOGICAL_OR expr                                { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; }
1031     | expr T_LOGICAL_AND expr                               { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; }
1032     | expr T_LOGICAL_XOR expr                               { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; }
1033     | expr '|' expr                                         { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; }
1034     | expr T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG expr   { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
1035     | expr T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG expr       { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; }
1036     | expr '^' expr                                         { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; }
1037     | expr '.' expr                                         { $$ = Expr\BinaryOp\Concat    [$1, $3]; }
1038     | expr '+' expr                                         { $$ = Expr\BinaryOp\Plus      [$1, $3]; }
1039     | expr '-' expr                                         { $$ = Expr\BinaryOp\Minus     [$1, $3]; }
1040     | expr '*' expr                                         { $$ = Expr\BinaryOp\Mul       [$1, $3]; }
1041     | expr '/' expr                                         { $$ = Expr\BinaryOp\Div       [$1, $3]; }
1042     | expr '%' expr                                         { $$ = Expr\BinaryOp\Mod       [$1, $3]; }
1043     | expr T_SL expr                                        { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; }
1044     | expr T_SR expr                                        { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; }
1045     | expr T_POW expr                                       { $$ = Expr\BinaryOp\Pow       [$1, $3]; }
1046     | '+' expr %prec T_INC                                  { $$ = Expr\UnaryPlus [$2]; }
1047     | '-' expr %prec T_INC                                  { $$ = Expr\UnaryMinus[$2]; }
1048     | '!' expr                                              { $$ = Expr\BooleanNot[$2]; }
1049     | '~' expr                                              { $$ = Expr\BitwiseNot[$2]; }
1050     | expr T_IS_IDENTICAL expr                              { $$ = Expr\BinaryOp\Identical     [$1, $3]; }
1051     | expr T_IS_NOT_IDENTICAL expr                          { $$ = Expr\BinaryOp\NotIdentical  [$1, $3]; }
1052     | expr T_IS_EQUAL expr                                  { $$ = Expr\BinaryOp\Equal         [$1, $3]; }
1053     | expr T_IS_NOT_EQUAL expr                              { $$ = Expr\BinaryOp\NotEqual      [$1, $3]; }
1054     | expr T_SPACESHIP expr                                 { $$ = Expr\BinaryOp\Spaceship     [$1, $3]; }
1055     | expr '<' expr                                         { $$ = Expr\BinaryOp\Smaller       [$1, $3]; }
1056     | expr T_IS_SMALLER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
1057     | expr '>' expr                                         { $$ = Expr\BinaryOp\Greater       [$1, $3]; }
1058     | expr T_IS_GREATER_OR_EQUAL expr                       { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
1059     | expr T_INSTANCEOF class_name_reference                { $$ = Expr\Instanceof_[$1, $3]; }
1060     | '(' expr ')'                                          { $$ = $2; }
1061     | expr '?' expr ':' expr                                { $$ = Expr\Ternary[$1, $3,   $5]; }
1062     | expr '?' ':' expr                                     { $$ = Expr\Ternary[$1, null, $4]; }
1063     | expr T_COALESCE expr                                  { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
1064     | T_ISSET '(' expr_list_allow_comma ')'                 { $$ = Expr\Isset_[$3]; }
1065     | T_EMPTY '(' expr ')'                                  { $$ = Expr\Empty_[$3]; }
1066     | T_INCLUDE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
1067     | T_INCLUDE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; }
1068     | T_EVAL '(' expr ')'                                   { $$ = Expr\Eval_[$3]; }
1069     | T_REQUIRE expr                                        { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; }
1070     | T_REQUIRE_ONCE expr                                   { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; }
1071     | T_INT_CAST expr                                       { $$ = Expr\Cast\Int_    [$2]; }
1072     | T_DOUBLE_CAST expr
1073           { $attrs = attributes();
1074             $attrs['kind'] = $this->getFloatCastKind($1);
1075             $$ = new Expr\Cast\Double($2, $attrs); }
1076     | T_STRING_CAST expr                                    { $$ = Expr\Cast\String_ [$2]; }
1077     | T_ARRAY_CAST expr                                     { $$ = Expr\Cast\Array_  [$2]; }
1078     | T_OBJECT_CAST expr                                    { $$ = Expr\Cast\Object_ [$2]; }
1079     | T_BOOL_CAST expr                                      { $$ = Expr\Cast\Bool_   [$2]; }
1080     | T_UNSET_CAST expr                                     { $$ = Expr\Cast\Unset_  [$2]; }
1081     | T_EXIT ctor_arguments
1082           { $$ = $this->createExitExpr($1, #1, $2, attributes()); }
1083     | '@' expr                                              { $$ = Expr\ErrorSuppress[$2]; }
1084     | scalar
1085     | '`' backticks_expr '`'                                { $$ = Expr\ShellExec[$2]; }
1086     | T_PRINT expr                                          { $$ = Expr\Print_[$2]; }
1087     | T_YIELD                                               { $$ = Expr\Yield_[null, null]; }
1088     | T_YIELD expr                                          { $$ = Expr\Yield_[$2, null]; }
1089     | T_YIELD expr T_DOUBLE_ARROW expr                      { $$ = Expr\Yield_[$4, $2]; }
1090     | T_YIELD_FROM expr                                     { $$ = Expr\YieldFrom[$2]; }
1091     | T_THROW expr                                          { $$ = Expr\Throw_[$2]; }
1092 
1093     | T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
1094           { $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8, 'attrGroups' => []]]; }
1095     | T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
1096           { $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9, 'attrGroups' => []]]; }
1097     | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
1098           { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
1099     | T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type       block_or_error
1100           { $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => []]]; }
1101 
1102     | attributes T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
1103           { $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9, 'attrGroups' => $1]]; }
1104     | attributes T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr %prec T_THROW
1105           { $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $4, 'params' => $6, 'returnType' => $8, 'expr' => $10, 'attrGroups' => $1]]; }
1106     | attributes T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type block_or_error
1107           { $$ = Expr\Closure[['static' => false, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
1108     | attributes T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type       block_or_error
1109           { $$ = Expr\Closure[['static' => true, 'byRef' => $4, 'params' => $6, 'uses' => $8, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]]; }
1110 ;
1111 
1112 anonymous_class:
1113       optional_attributes class_entry_type ctor_arguments extends_from implements_list '{' class_statement_list '}'
1114           { $$ = array(Stmt\Class_[null, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]], $3);
1115             $this->checkClass($$[0], -1); }
1116 ;
1117 
1118 new_dereferenceable:
1119       T_NEW class_name_reference argument_list              { $$ = Expr\New_[$2, $3]; }
1120     | T_NEW anonymous_class
1121           { list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
1122 ;
1123 
1124 new_non_dereferenceable:
1125       T_NEW class_name_reference                            { $$ = Expr\New_[$2, []]; }
1126 ;
1127 
1128 new_expr:
1129       new_dereferenceable
1130     | new_non_dereferenceable
1131 ;
1132 
1133 lexical_vars:
1134       /* empty */                                           { $$ = array(); }
1135     | T_USE '(' lexical_var_list ')'                        { $$ = $3; }
1136 ;
1137 
1138 lexical_var_list:
1139       non_empty_lexical_var_list optional_comma
1140 ;
1141 
1142 non_empty_lexical_var_list:
1143       lexical_var                                           { init($1); }
1144     | non_empty_lexical_var_list ',' lexical_var            { push($1, $3); }
1145 ;
1146 
1147 lexical_var:
1148       optional_ref plain_variable                           { $$ = Node\ClosureUse[$2, $1]; }
1149 ;
1150 
1151 name_readonly:
1152       T_READONLY                                            { $$ = Name[$1]; }
1153 ;
1154 
1155 function_call:
1156       name argument_list                                    { $$ = Expr\FuncCall[$1, $2]; }
1157     | name_readonly argument_list                           { $$ = Expr\FuncCall[$1, $2]; }
1158     | callable_expr argument_list                           { $$ = Expr\FuncCall[$1, $2]; }
1159     | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
1160           { $$ = Expr\StaticCall[$1, $3, $4]; }
1161 ;
1162 
1163 class_name:
1164       T_STATIC                                              { $$ = Name[$1]; }
1165     | name
1166 ;
1167 
1168 name:
1169       T_STRING                                              { $$ = Name[$1]; }
1170     | T_NAME_QUALIFIED                                      { $$ = Name[$1]; }
1171     | T_NAME_FULLY_QUALIFIED                                { $$ = Name\FullyQualified[substr($1, 1)]; }
1172     | T_NAME_RELATIVE                                       { $$ = Name\Relative[substr($1, 10)]; }
1173 ;
1174 
1175 class_name_reference:
1176       class_name
1177     | new_variable
1178     | '(' expr ')'                                          { $$ = $2; }
1179     | error                                                 { $$ = Expr\Error[]; $this->errorState = 2; }
1180 ;
1181 
1182 class_name_or_var:
1183       class_name
1184     | fully_dereferenceable
1185 ;
1186 
1187 backticks_expr:
1188       /* empty */                                           { $$ = array(); }
1189     | encaps_string_part
1190           { $$ = array($1); parseEncapsed($$, '`', $this->phpVersion->supportsUnicodeEscapes()); }
1191     | encaps_list                                           { parseEncapsed($1, '`', $this->phpVersion->supportsUnicodeEscapes()); $$ = $1; }
1192 ;
1193 
1194 ctor_arguments:
1195       /* empty */                                           { $$ = array(); }
1196     | argument_list
1197 ;
1198 
1199 constant:
1200       name                                                  { $$ = Expr\ConstFetch[$1]; }
1201     | T_LINE                                                { $$ = Scalar\MagicConst\Line[]; }
1202     | T_FILE                                                { $$ = Scalar\MagicConst\File[]; }
1203     | T_DIR                                                 { $$ = Scalar\MagicConst\Dir[]; }
1204     | T_CLASS_C                                             { $$ = Scalar\MagicConst\Class_[]; }
1205     | T_TRAIT_C                                             { $$ = Scalar\MagicConst\Trait_[]; }
1206     | T_METHOD_C                                            { $$ = Scalar\MagicConst\Method[]; }
1207     | T_FUNC_C                                              { $$ = Scalar\MagicConst\Function_[]; }
1208     | T_NS_C                                                { $$ = Scalar\MagicConst\Namespace_[]; }
1209     | T_PROPERTY_C                                          { $$ = Scalar\MagicConst\Property[]; }
1210 ;
1211 
1212 class_constant:
1213       class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_maybe_reserved
1214           { $$ = Expr\ClassConstFetch[$1, $3]; }
1215     | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}'
1216           { $$ = Expr\ClassConstFetch[$1, $4]; }
1217     /* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
1218        an unfinished static property fetch or unfinished scoped call. */
1219     | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
1220           { $$ = Expr\ClassConstFetch[$1, new Expr\Error(stackAttributes(#3))]; $this->errorState = 2; }
1221 ;
1222 
1223 array_short_syntax:
1224       '[' array_pair_list ']'
1225           { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT;
1226             $$ = new Expr\Array_($2, $attrs); }
1227 ;
1228 
1229 dereferenceable_scalar:
1230       T_ARRAY '(' array_pair_list ')'
1231           { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
1232             $$ = new Expr\Array_($3, $attrs);
1233             $this->createdArrays->attach($$); }
1234     | array_short_syntax                                    { $$ = $1; $this->createdArrays->attach($$); }
1235     | T_CONSTANT_ENCAPSED_STRING
1236           { $$ = Scalar\String_::fromString($1, attributes(), $this->phpVersion->supportsUnicodeEscapes()); }
1237     | '"' encaps_list '"'
1238           { $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
1239             parseEncapsed($2, '"', $this->phpVersion->supportsUnicodeEscapes()); $$ = new Scalar\InterpolatedString($2, $attrs); }
1240 ;
1241 
1242 scalar:
1243       T_LNUMBER
1244           { $$ = $this->parseLNumber($1, attributes(), $this->phpVersion->allowsInvalidOctals()); }
1245     | T_DNUMBER                                             { $$ = Scalar\Float_::fromString($1, attributes()); }
1246     | dereferenceable_scalar
1247     | constant
1248     | class_constant
1249     | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
1250           { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); }
1251     | T_START_HEREDOC T_END_HEREDOC
1252           { $$ = $this->parseDocString($1, '', $2, attributes(), stackAttributes(#2), true); }
1253     | T_START_HEREDOC encaps_list T_END_HEREDOC
1254           { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); }
1255 ;
1256 
1257 optional_expr:
1258       /* empty */                                           { $$ = null; }
1259     | expr
1260 ;
1261 
1262 fully_dereferenceable:
1263       variable
1264     | '(' expr ')'                                          { $$ = $2; }
1265     | dereferenceable_scalar
1266     | class_constant
1267     | new_dereferenceable
1268 ;
1269 
1270 array_object_dereferenceable:
1271       fully_dereferenceable
1272     | constant
1273 ;
1274 
1275 callable_expr:
1276       callable_variable
1277     | '(' expr ')'                                          { $$ = $2; }
1278     | dereferenceable_scalar
1279     | new_dereferenceable
1280 ;
1281 
1282 callable_variable:
1283       simple_variable
1284     | array_object_dereferenceable '[' optional_expr ']'     { $$ = Expr\ArrayDimFetch[$1, $3]; }
1285 #if PHP7
1286     | array_object_dereferenceable '{' expr '}'              { $$ = Expr\ArrayDimFetch[$1, $3]; }
1287 #endif
1288     | function_call
1289     | array_object_dereferenceable T_OBJECT_OPERATOR property_name argument_list
1290           { $$ = Expr\MethodCall[$1, $3, $4]; }
1291     | array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name argument_list
1292           { $$ = Expr\NullsafeMethodCall[$1, $3, $4]; }
1293 ;
1294 
1295 optional_plain_variable:
1296       /* empty */                                           { $$ = null; }
1297     | plain_variable
1298 ;
1299 
1300 variable:
1301       callable_variable
1302     | static_member
1303     | array_object_dereferenceable T_OBJECT_OPERATOR property_name
1304           { $$ = Expr\PropertyFetch[$1, $3]; }
1305     | array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name
1306           { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
1307 ;
1308 
1309 simple_variable:
1310       plain_variable
1311     | '$' '{' expr '}'                                      { $$ = Expr\Variable[$3]; }
1312     | '$' simple_variable                                   { $$ = Expr\Variable[$2]; }
1313     | '$' error                                             { $$ = Expr\Variable[Expr\Error[]]; $this->errorState = 2; }
1314 ;
1315 
1316 static_member_prop_name:
1317       simple_variable
1318           { $var = $1->name; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; }
1319 ;
1320 
1321 static_member:
1322       class_name_or_var T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
1323           { $$ = Expr\StaticPropertyFetch[$1, $3]; }
1324 ;
1325 
1326 new_variable:
1327       simple_variable
1328     | new_variable '[' optional_expr ']'                    { $$ = Expr\ArrayDimFetch[$1, $3]; }
1329 #if PHP7
1330     | new_variable '{' expr '}'                             { $$ = Expr\ArrayDimFetch[$1, $3]; }
1331 #endif
1332     | new_variable T_OBJECT_OPERATOR property_name          { $$ = Expr\PropertyFetch[$1, $3]; }
1333     | new_variable T_NULLSAFE_OBJECT_OPERATOR property_name { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
1334     | class_name T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
1335           { $$ = Expr\StaticPropertyFetch[$1, $3]; }
1336     | new_variable T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name
1337           { $$ = Expr\StaticPropertyFetch[$1, $3]; }
1338 ;
1339 
1340 member_name:
1341       identifier_maybe_reserved
1342     | '{' expr '}'                                          { $$ = $2; }
1343     | simple_variable
1344 ;
1345 
1346 property_name:
1347       identifier_not_reserved
1348     | '{' expr '}'                                          { $$ = $2; }
1349     | simple_variable
1350     | error                                                 { $$ = Expr\Error[]; $this->errorState = 2; }
1351 ;
1352 
1353 list_expr:
1354       T_LIST '(' inner_array_pair_list ')'
1355           { $$ = Expr\List_[$3]; $$->setAttribute('kind', Expr\List_::KIND_LIST);
1356             $this->postprocessList($$); }
1357 ;
1358 
1359 array_pair_list:
1360       inner_array_pair_list
1361           { $$ = $1; $end = count($$)-1; if ($$[$end]->value instanceof Expr\Error) array_pop($$); }
1362 ;
1363 
1364 comma_or_error:
1365       ','
1366     | error
1367           { /* do nothing -- prevent default action of $$=$1. See #551. */ }
1368 ;
1369 
1370 inner_array_pair_list:
1371       inner_array_pair_list comma_or_error array_pair       { push($1, $3); }
1372     | array_pair                                            { init($1); }
1373 ;
1374 
1375 array_pair:
1376       expr                                                  { $$ = Node\ArrayItem[$1, null, false]; }
1377     | ampersand variable                                    { $$ = Node\ArrayItem[$2, null, true]; }
1378     | list_expr                                             { $$ = Node\ArrayItem[$1, null, false]; }
1379     | expr T_DOUBLE_ARROW expr                              { $$ = Node\ArrayItem[$3, $1,   false]; }
1380     | expr T_DOUBLE_ARROW ampersand variable                { $$ = Node\ArrayItem[$4, $1,   true]; }
1381     | expr T_DOUBLE_ARROW list_expr                         { $$ = Node\ArrayItem[$3, $1,   false]; }
1382     | T_ELLIPSIS expr                                       { $$ = new Node\ArrayItem($2, null, false, attributes(), true); }
1383     | /* empty */
1384         { /* Create an Error node now to remember the position. We'll later either report an error,
1385              or convert this into a null element, depending on whether this is a creation or destructuring context. */
1386           $attrs = $this->createEmptyElemAttributes($this->tokenPos);
1387           $$ = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs); }
1388 ;
1389 
1390 encaps_list:
1391       encaps_list encaps_var                                { push($1, $2); }
1392     | encaps_list encaps_string_part                        { push($1, $2); }
1393     | encaps_var                                            { init($1); }
1394     | encaps_string_part encaps_var                         { init($1, $2); }
1395 ;
1396 
1397 encaps_string_part:
1398       T_ENCAPSED_AND_WHITESPACE
1399           { $attrs = attributes(); $attrs['rawValue'] = $1; $$ = new Node\InterpolatedStringPart($1, $attrs); }
1400 ;
1401 
1402 encaps_str_varname:
1403       T_STRING_VARNAME                                      { $$ = Expr\Variable[$1]; }
1404 ;
1405 
1406 encaps_var:
1407       plain_variable
1408     | plain_variable '[' encaps_var_offset ']'              { $$ = Expr\ArrayDimFetch[$1, $3]; }
1409     | plain_variable T_OBJECT_OPERATOR identifier_not_reserved
1410           { $$ = Expr\PropertyFetch[$1, $3]; }
1411     | plain_variable T_NULLSAFE_OBJECT_OPERATOR identifier_not_reserved
1412           { $$ = Expr\NullsafePropertyFetch[$1, $3]; }
1413     | T_DOLLAR_OPEN_CURLY_BRACES expr '}'                   { $$ = Expr\Variable[$2]; }
1414     | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}'       { $$ = Expr\Variable[$2]; }
1415     | T_DOLLAR_OPEN_CURLY_BRACES encaps_str_varname '[' expr ']' '}'
1416           { $$ = Expr\ArrayDimFetch[$2, $4]; }
1417     | T_CURLY_OPEN variable '}'                             { $$ = $2; }
1418 ;
1419 
1420 encaps_var_offset:
1421       T_STRING                                              { $$ = Scalar\String_[$1]; }
1422     | T_NUM_STRING                                          { $$ = $this->parseNumString($1, attributes()); }
1423     | '-' T_NUM_STRING                                      { $$ = $this->parseNumString('-' . $2, attributes()); }
1424     | plain_variable
1425 ;
1426 
1427 %%
1428