aboutsummaryrefslogtreecommitdiff
path: root/vendor/tree-sitter-php/src/common/define-grammar.js
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 23:45:20 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 23:45:20 +0100
commit8ab1da7853f6dd309f2d3677ca109737f929ab4a (patch)
tree8afa3fafa23badd9b99aca51fce05a15ee7c2f48 /vendor/tree-sitter-php/src/common/define-grammar.js
parentdcacc00e3750300617ba6e16eb346713f91a783a (diff)
downloadcrep-8ab1da7853f6dd309f2d3677ca109737f929ab4a.tar.gz
Add PHP support
Diffstat (limited to 'vendor/tree-sitter-php/src/common/define-grammar.js')
-rw-r--r--vendor/tree-sitter-php/src/common/define-grammar.js1681
1 files changed, 1681 insertions, 0 deletions
diff --git a/vendor/tree-sitter-php/src/common/define-grammar.js b/vendor/tree-sitter-php/src/common/define-grammar.js
new file mode 100644
index 0000000..4869733
--- /dev/null
+++ b/vendor/tree-sitter-php/src/common/define-grammar.js
@@ -0,0 +1,1681 @@
1/**
2 * @author Josh Vera <vera@github.com>
3 * @author Max Brunsfeld <maxbrunsfeld@gmail.com>
4 * @author Amaan Qureshi <amaanq12@gmail.com>
5 * @author Caleb White <cdwhite3@pm.me>
6 * @author Christian Frøystad <christian@xist.no>
7 * @license MIT
8 */
9
10/// <reference types="tree-sitter-cli/dsl" />
11// @ts-check
12
13const PREC = {
14 COMMA: -1,
15 CAST: -1,
16 LOGICAL_OR_2: 1,
17 LOGICAL_XOR: 2,
18 LOGICAL_AND_2: 3,
19 ASSIGNMENT: 4,
20 TERNARY: 5,
21 NULL_COALESCE: 6,
22 LOGICAL_OR_1: 7,
23 LOGICAL_AND_1: 8,
24 BITWISE_OR: 9,
25 BITWISE_XOR: 10,
26 BITWISE_AND: 11,
27 EQUALITY: 12,
28 INEQUALITY: 13,
29 CONCAT: 14,
30 SHIFT: 15,
31 PLUS: 16,
32 TIMES: 17,
33 EXPONENTIAL: 18,
34 NEG: 19,
35 INSTANCEOF: 20,
36 INC: 21,
37 SCOPE: 22,
38 NEW: 23,
39 CALL: 24,
40 MEMBER: 25,
41 DEREF: 26,
42};
43
44module.exports = function defineGrammar(dialect) {
45 if (dialect !== 'php' && dialect !== 'php_only') {
46 throw new Error(`Unknown dialect ${dialect}`);
47 }
48
49 return grammar({
50 name: dialect,
51
52 conflicts: $ => [
53 [$._array_destructing, $.array_creation_expression],
54 [$._array_destructing_element, $.array_element_initializer],
55 [$.primary_expression, $._array_destructing_element],
56
57 [$.type, $.union_type, $.intersection_type, $.disjunctive_normal_form_type],
58 [$.union_type, $.disjunctive_normal_form_type],
59 [$.intersection_type],
60 [$.if_statement],
61
62 [$.namespace_name],
63 [$.heredoc_body],
64 ],
65
66 externals: $ => [
67 $._automatic_semicolon,
68 $.encapsed_string_chars,
69 $.encapsed_string_chars_after_variable,
70 $.execution_string_chars,
71 $.execution_string_chars_after_variable,
72 $.encapsed_string_chars_heredoc,
73 $.encapsed_string_chars_after_variable_heredoc,
74 $._eof,
75 $.heredoc_start,
76 $.heredoc_end,
77 $.nowdoc_string,
78 $.sentinel_error, // Unused token used to indicate error recovery mode
79 ],
80
81 extras: $ => {
82 const extras = [
83 $.comment,
84 /[\s\u00A0\u200B\u2060\uFEFF]/,
85 ];
86
87 if (dialect === 'php') {
88 extras.push($.text_interpolation);
89 }
90
91 return extras;
92 },
93
94 inline: $ => [
95 $._variable,
96 $._namespace_use_type,
97 ],
98
99 supertypes: $ => [
100 $.statement,
101 $.expression,
102 $.primary_expression,
103 $.type,
104 $.literal,
105 ],
106
107 word: $ => $.name,
108
109 rules: {
110 program: $ => {
111 if (dialect === 'php') {
112 return seq(
113 optional($.text),
114 optional(seq(
115 $.php_tag,
116 repeat($.statement),
117 )),
118 );
119 }
120
121 return seq(
122 optional($.php_tag),
123 repeat($.statement),
124 optional('?>'),
125 );
126 },
127
128 php_tag: _ => /<\?([pP][hH][pP]|=)?/,
129
130 text_interpolation: $ => seq(
131 '?>',
132 optional($.text),
133 choice($.php_tag, $._eof),
134 ),
135
136 text: _ => repeat1(choice(
137 token(prec(-1, /</)),
138 token(prec(1, /[^\s<][^<]*/)),
139 )),
140
141 statement: $ => choice(
142 $.empty_statement,
143 $.compound_statement,
144 $.named_label_statement,
145 $.expression_statement,
146 $.if_statement,
147 $.switch_statement,
148 $.while_statement,
149 $.do_statement,
150 $.for_statement,
151 $.foreach_statement,
152 $.goto_statement,
153 $.continue_statement,
154 $.break_statement,
155 $.return_statement,
156 $.try_statement,
157 $.declare_statement,
158 $.echo_statement,
159 $.exit_statement,
160 $.unset_statement,
161 $.const_declaration,
162 $.function_definition,
163 $.class_declaration,
164 $.interface_declaration,
165 $.trait_declaration,
166 $.enum_declaration,
167 $.namespace_definition,
168 $.namespace_use_declaration,
169 $.global_declaration,
170 $.function_static_declaration,
171 ),
172
173 empty_statement: _ => prec(-1, ';'),
174
175 reference_modifier: _ => '&',
176
177 function_static_declaration: $ => seq(
178 keyword('static'),
179 commaSep1($.static_variable_declaration),
180 $._semicolon,
181 ),
182
183 static_variable_declaration: $ => seq(
184 field('name', $.variable_name),
185 optional(seq(
186 '=',
187 field('value', $.expression),
188 )),
189 ),
190
191 global_declaration: $ => seq(
192 keyword('global'),
193 commaSep1($._simple_variable),
194 $._semicolon,
195 ),
196
197 namespace_definition: $ => seq(
198 keyword('namespace'),
199 choice(
200 seq(field('name', $.namespace_name), $._semicolon),
201 seq(
202 field('name', optional($.namespace_name)),
203 field('body', $.compound_statement),
204 ),
205 ),
206 ),
207
208 namespace_use_declaration: $ => seq(
209 keyword('use'),
210 choice(
211 commaSep1($.namespace_use_clause),
212 $._namespace_use_group,
213 ),
214 $._semicolon,
215 ),
216
217 namespace_use_clause: $ => seq(
218 field('type', optional($._namespace_use_type)),
219 $._name,
220 optional(seq(keyword('as'), field('alias', $.name))),
221 ),
222
223 _namespace_use_type: $ => choice(keyword('function'), keyword('const')),
224
225 qualified_name: $ => seq(
226 field('prefix', seq(optional($.namespace_name), '\\')),
227 $.name,
228 ),
229
230 _name: $ => choice($._identifier, $.qualified_name),
231
232 namespace_name: $ => seq(optional('\\'), $.name, repeat(seq('\\', $.name))),
233
234 _namespace_use_group: $ => seq(
235 field('type', optional($._namespace_use_type)),
236 $.namespace_name,
237 '\\',
238 field('body', $.namespace_use_group),
239 ),
240
241 namespace_use_group: $ => seq('{', commaSep1($.namespace_use_clause), '}'),
242
243 trait_declaration: $ => seq(
244 optional(field('attributes', $.attribute_list)),
245 keyword('trait'),
246 field('name', $.name),
247 field('body', $.declaration_list),
248 ),
249
250 interface_declaration: $ => seq(
251 optional(field('attributes', $.attribute_list)),
252 keyword('interface'),
253 field('name', $.name),
254 optional($.base_clause),
255 field('body', $.declaration_list),
256 ),
257
258 base_clause: $ => seq(
259 keyword('extends'),
260 commaSep1($._name),
261 ),
262
263 enum_declaration: $ => prec.right(seq(
264 optional(field('attributes', $.attribute_list)),
265 keyword('enum'),
266 field('name', $.name),
267 optional(seq(':', alias(choice('string', 'int'), $.primitive_type))),
268 optional($.class_interface_clause),
269 field('body', $.enum_declaration_list),
270 )),
271
272 enum_declaration_list: $ => seq('{', repeat($._enum_member_declaration), '}'),
273
274 _enum_member_declaration: $ => choice(
275 $.enum_case,
276 $.method_declaration,
277 $.use_declaration,
278 ),
279
280 enum_case: $ => seq(
281 optional(field('attributes', $.attribute_list)),
282 keyword('case'),
283 field('name', $.name),
284 optional(seq('=', field('value', choice($._string, $.integer)))),
285 $._semicolon,
286 ),
287
288 class_declaration: $ => prec.right(seq(
289 optional(field('attributes', $.attribute_list)),
290 repeat($._modifier),
291 keyword('class'),
292 field('name', $.name),
293 optional($.base_clause),
294 optional($.class_interface_clause),
295 field('body', $.declaration_list),
296 )),
297
298 declaration_list: $ => seq('{', repeat($._member_declaration), '}'),
299
300 final_modifier: _ => keyword('final'),
301 abstract_modifier: _ => keyword('abstract'),
302 readonly_modifier: _ => keyword('readonly'),
303
304 class_interface_clause: $ => seq(
305 keyword('implements'),
306 commaSep1($._name),
307 ),
308
309 _member_declaration: $ => choice(
310 alias($._class_const_declaration, $.const_declaration),
311 $.property_declaration,
312 $.method_declaration,
313 $.use_declaration,
314 ),
315
316 const_declaration: $ => $._const_declaration,
317
318 _class_const_declaration: $ => seq(
319 optional(field('attributes', $.attribute_list)),
320 optional($.final_modifier),
321 $._const_declaration,
322 ),
323
324 _const_declaration: $ => seq(
325 repeat($._modifier),
326 keyword('const'),
327 optional(field('type', $.type)),
328 commaSep1($.const_element),
329 $._semicolon,
330 ),
331
332 property_declaration: $ => seq(
333 optional(field('attributes', $.attribute_list)),
334 repeat1($._modifier),
335 optional(field('type', $.type)),
336 commaSep1($.property_element),
337 choice(
338 $._semicolon,
339 $.property_hook_list,
340 ),
341 ),
342
343 _modifier: $ => prec.left(choice(
344 $.var_modifier,
345 $.visibility_modifier,
346 $.static_modifier,
347 $.final_modifier,
348 $.abstract_modifier,
349 $.readonly_modifier,
350 )),
351
352 property_element: $ => seq(
353 field('name', $.variable_name),
354 optional(seq('=', field('default_value', $.expression))),
355 ),
356
357 property_hook_list: $ => seq('{', repeat($.property_hook), '}'),
358
359 property_hook: $ => seq(
360 optional(field('attributes', $.attribute_list)),
361 optional(field('final', $.final_modifier)),
362 optional(field('reference_modifier', $.reference_modifier)),
363 $.name,
364 optional(field('parameters', $.formal_parameters)),
365 $._property_hook_body,
366 ),
367
368 _property_hook_body: $ => choice(
369 seq('=>', field('body', $.expression), $._semicolon),
370 field('body', $.compound_statement),
371 $._semicolon,
372 ),
373
374 method_declaration: $ => seq(
375 optional(field('attributes', $.attribute_list)),
376 repeat($._modifier),
377 $._function_definition_header,
378 choice(
379 field('body', $.compound_statement),
380 $._semicolon,
381 ),
382 ),
383
384 var_modifier: _ => keyword('var', false),
385 static_modifier: _ => keyword('static'),
386
387 use_declaration: $ => seq(
388 keyword('use'),
389 commaSep1($._name),
390 choice($.use_list, $._semicolon),
391 ),
392
393 use_list: $ => seq(
394 '{',
395 repeat(seq(
396 choice(
397 $.use_instead_of_clause,
398 $.use_as_clause,
399 ),
400 $._semicolon,
401 )),
402 '}',
403 ),
404
405 use_instead_of_clause: $ => prec.left(seq(
406 $.class_constant_access_expression,
407 keyword('insteadof'),
408 $.name,
409 )),
410
411 use_as_clause: $ => seq(
412 choice($.class_constant_access_expression, $.name),
413 keyword('as'),
414 choice(
415 seq(
416 optional($.visibility_modifier),
417 $.name,
418 ),
419 seq(
420 $.visibility_modifier,
421 optional($.name),
422 ),
423 ),
424 ),
425
426 visibility_modifier: _ => choice(
427 keyword('public'),
428 keyword('protected'),
429 keyword('private'),
430 ),
431
432 function_definition: $ => seq(
433 optional(field('attributes', $.attribute_list)),
434 $._function_definition_header,
435 field('body', $.compound_statement),
436 ),
437
438 _function_definition_header: $ => seq(
439 keyword('function'),
440 optional($.reference_modifier),
441 field('name', $._identifier),
442 field('parameters', $.formal_parameters),
443 optional($._return_type),
444 ),
445
446 anonymous_function: $ => seq(
447 $._anonymous_function_header,
448 field('body', $.compound_statement),
449 ),
450
451 anonymous_function_use_clause: $ => seq(
452 keyword('use'),
453 '(',
454 commaSep1(choice($.by_ref, $.variable_name)),
455 optional(','),
456 ')',
457 ),
458
459 _anonymous_function_header: $ => seq(
460 optional(field('attributes', $.attribute_list)),
461 optional(field('static_modifier', $.static_modifier)),
462 keyword('function'),
463 optional(field('reference_modifier', $.reference_modifier)),
464 field('parameters', $.formal_parameters),
465 optional($.anonymous_function_use_clause),
466 optional($._return_type),
467 ),
468
469 _arrow_function_header: $ => seq(
470 optional(field('attributes', $.attribute_list)),
471 optional(field('static_modifier', $.static_modifier)),
472 keyword('fn'),
473 optional(field('reference_modifier', $.reference_modifier)),
474 field('parameters', $.formal_parameters),
475 optional($._return_type),
476 ),
477
478 arrow_function: $ => seq(
479 $._arrow_function_header,
480 '=>',
481 field('body', $.expression),
482 ),
483
484 formal_parameters: $ => seq(
485 '(',
486 commaSep(choice(
487 $.simple_parameter,
488 $.variadic_parameter,
489 $.property_promotion_parameter,
490 )),
491 optional(','),
492 ')',
493 ),
494
495 property_promotion_parameter: $ => seq(
496 optional(field('attributes', $.attribute_list)),
497 field('visibility', $.visibility_modifier),
498 field('readonly', optional($.readonly_modifier)),
499 field('type', optional($.type)), // Note: callable is not a valid type here, but instead of complicating the parser, we defer this checking to any intelligence using the parser
500 field('name', choice($.by_ref, $.variable_name)),
501 optional(seq('=', field('default_value', $.expression))),
502 optional($.property_hook_list),
503 ),
504
505 simple_parameter: $ => seq(
506 optional(field('attributes', $.attribute_list)),
507 field('type', optional($.type)),
508 optional(field('reference_modifier', $.reference_modifier)),
509 field('name', $.variable_name),
510 optional(seq('=', field('default_value', $.expression))),
511 ),
512
513 variadic_parameter: $ => seq(
514 optional(field('attributes', $.attribute_list)),
515 field('type', optional($.type)),
516 optional(field('reference_modifier', $.reference_modifier)),
517 '...',
518 field('name', $.variable_name),
519 ),
520
521 type: $ => choice(
522 $._types,
523 $.union_type,
524 $.intersection_type,
525 $.disjunctive_normal_form_type,
526 ),
527
528 _types: $ => choice(
529 $.optional_type,
530 $.named_type,
531 $.primitive_type,
532 ),
533
534 named_type: $ => choice($.name, $.qualified_name),
535
536 optional_type: $ => seq(
537 '?',
538 choice(
539 $.named_type,
540 $.primitive_type,
541 ),
542 ),
543
544 bottom_type: _ => 'never',
545
546 union_type: $ => pipeSep1($._types),
547
548 intersection_type: $ => ampSep1($._types),
549
550 disjunctive_normal_form_type: $ => prec.dynamic(-1, pipeSep1(choice(
551 seq('(', $.intersection_type, ')'),
552 $._types,
553 ))),
554
555 primitive_type: _ => choice(
556 'array',
557 keyword('callable'), // not legal in property types
558 'iterable',
559 'bool',
560 'float',
561 'int',
562 'string',
563 'void',
564 'mixed',
565 'false',
566 'null',
567 'true',
568 ),
569
570 cast_type: _ => choice(
571 keyword('array', false),
572 keyword('binary', false),
573 keyword('bool', false),
574 keyword('boolean', false),
575 keyword('double', false),
576 keyword('int', false),
577 keyword('integer', false),
578 keyword('float', false),
579 keyword('object', false),
580 keyword('real', false),
581 keyword('string', false),
582 keyword('unset', false),
583 ),
584
585 _return_type: $ => seq(':', field('return_type', choice($.type, $.bottom_type))),
586
587 const_element: $ => seq($._identifier, '=', $.expression),
588
589 echo_statement: $ => seq(keyword('echo'), $._expressions, $._semicolon),
590
591 exit_statement: $ => seq(
592 keyword('exit'),
593 optional(seq('(', optional($.expression), ')')),
594 $._semicolon,
595 ),
596
597 unset_statement: $ => seq(
598 'unset',
599 '(',
600 commaSep1($._variable),
601 optional(','),
602 ')',
603 $._semicolon,
604 ),
605
606 declare_statement: $ => seq(
607 keyword('declare'),
608 '(',
609 $.declare_directive,
610 ')',
611 choice(
612 $.statement,
613 $._semicolon,
614 seq(
615 ':',
616 repeat($.statement),
617 keyword('enddeclare'),
618 $._semicolon,
619 ),
620 ),
621 ),
622
623 declare_directive: $ => seq(
624 choice('ticks', 'encoding', 'strict_types'),
625 '=',
626 $.literal,
627 ),
628
629 literal: $ => choice(
630 $.integer,
631 $.float,
632 $._string,
633 $.boolean,
634 $.null,
635 ),
636
637 float: _ => /\d*(_\d+)*((\.\d*(_\d+)*)?([eE][\+-]?\d+(_\d+)*)|(\.\d*(_\d+)*)([eE][\+-]?\d+(_\d+)*)?)/,
638
639 try_statement: $ => seq(
640 keyword('try'),
641 field('body', $.compound_statement),
642 repeat1(choice($.catch_clause, $.finally_clause)),
643 ),
644
645 catch_clause: $ => seq(
646 keyword('catch'),
647 '(',
648 field('type', $.type_list),
649 optional(field('name', $.variable_name)),
650 ')',
651 field('body', $.compound_statement),
652 ),
653
654 type_list: $ => pipeSep1($.named_type),
655
656 finally_clause: $ => seq(
657 keyword('finally'),
658 field('body', $.compound_statement),
659 ),
660
661 goto_statement: $ => seq(
662 keyword('goto'), $.name, $._semicolon,
663 ),
664
665 continue_statement: $ => seq(
666 keyword('continue'), optional($.expression), $._semicolon,
667 ),
668
669 break_statement: $ => seq(
670 keyword('break'), optional($.expression), $._semicolon,
671 ),
672
673 integer: _ => {
674 const decimal = /[1-9]\d*(_\d+)*/;
675 const octal = /0[oO]?[0-7]*(_[0-7]+)*/;
676 const hex = /0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)*/;
677 const binary = /0[bB][01]+(_[01]+)*/;
678 return token(choice(
679 decimal,
680 octal,
681 hex,
682 binary,
683 ));
684 },
685
686 return_statement: $ => seq(
687 keyword('return'), optional($.expression), $._semicolon,
688 ),
689
690 throw_expression: $ => seq(
691 keyword('throw'),
692 $.expression,
693 ),
694
695 while_statement: $ => seq(
696 keyword('while'),
697 field('condition', $.parenthesized_expression),
698 choice(
699 field('body', $.statement),
700 seq(
701 field('body', $.colon_block),
702 keyword('endwhile'),
703 $._semicolon,
704 ),
705 ),
706 ),
707
708 do_statement: $ => seq(
709 keyword('do'),
710 field('body', $.statement),
711 keyword('while'),
712 field('condition', $.parenthesized_expression),
713 $._semicolon,
714 ),
715
716 for_statement: $ => seq(
717 keyword('for'),
718 '(',
719 field('initialize', optional($._expressions)),
720 ';',
721 field('condition', optional($._expressions)),
722 ';',
723 field('update', optional($._expressions)),
724 ')',
725 choice(
726 $._semicolon,
727 field('body', $.statement),
728 seq(
729 ':',
730 field('body', repeat($.statement)),
731 keyword('endfor'),
732 $._semicolon,
733 ),
734 ),
735 ),
736
737 _expressions: $ => choice(
738 $.expression,
739 $.sequence_expression,
740 ),
741
742 sequence_expression: $ => prec(PREC.COMMA, seq(
743 $.expression, ',', choice($.sequence_expression, $.expression)),
744 ),
745
746 foreach_statement: $ => seq(
747 keyword('foreach'),
748 '(',
749 $.expression,
750 keyword('as'),
751 choice(
752 alias($.foreach_pair, $.pair),
753 $._foreach_value,
754 ),
755 ')',
756 choice(
757 $._semicolon,
758 field('body', $.statement),
759 seq(
760 field('body', $.colon_block),
761 keyword('endforeach'),
762 $._semicolon,
763 ),
764 ),
765 ),
766
767 foreach_pair: $ => seq($.expression, '=>', $._foreach_value),
768
769 _foreach_value: $ => choice(
770 $.by_ref,
771 $.expression,
772 $.list_literal,
773 ),
774
775 if_statement: $ => seq(
776 keyword('if'),
777 field('condition', $.parenthesized_expression),
778 choice(
779 seq(
780 field('body', $.statement),
781 repeat(field('alternative', $.else_if_clause)),
782 optional(field('alternative', $.else_clause)),
783 ),
784 seq(
785 field('body', $.colon_block),
786 repeat(field('alternative', alias($.else_if_clause_2, $.else_if_clause))),
787 optional(field('alternative', alias($.else_clause_2, $.else_clause))),
788 keyword('endif'),
789 $._semicolon,
790 ),
791 ),
792 ),
793
794 colon_block: $ => seq(
795 ':',
796 repeat($.statement),
797 ),
798
799 else_if_clause: $ => seq(
800 keyword('elseif'),
801 field('condition', $.parenthesized_expression),
802 field('body', $.statement),
803 ),
804
805 else_clause: $ => seq(
806 keyword('else'),
807 field('body', $.statement),
808 ),
809
810 else_if_clause_2: $ => seq(
811 keyword('elseif'),
812 field('condition', $.parenthesized_expression),
813 field('body', $.colon_block),
814 ),
815
816 else_clause_2: $ => seq(
817 keyword('else'),
818 field('body', $.colon_block),
819 ),
820
821 match_expression: $ => seq(
822 keyword('match'),
823 field('condition', $.parenthesized_expression),
824 field('body', $.match_block),
825 ),
826
827 match_block: $ => prec.left(
828 seq(
829 '{',
830 commaSep(
831 choice(
832 $.match_conditional_expression,
833 $.match_default_expression,
834 ),
835 ),
836 optional(','),
837 '}',
838 ),
839 ),
840
841 match_condition_list: $ => seq(commaSep1($.expression), optional(',')),
842
843 match_conditional_expression: $ => seq(
844 field('conditional_expressions', $.match_condition_list),
845 '=>',
846 field('return_expression', $.expression),
847 ),
848
849 match_default_expression: $ => seq(
850 keyword('default'),
851 '=>',
852 field('return_expression', $.expression),
853 ),
854
855 switch_statement: $ => seq(
856 keyword('switch'),
857 field('condition', $.parenthesized_expression),
858 field('body', $.switch_block),
859 ),
860
861 switch_block: $ => choice(
862 seq(
863 '{',
864 repeat(choice($.case_statement, $.default_statement)),
865 '}',
866 ),
867 seq(
868 ':',
869 repeat(choice($.case_statement, $.default_statement)),
870 keyword('endswitch'),
871 $._semicolon,
872 ),
873 ),
874
875 case_statement: $ => seq(
876 keyword('case'),
877 field('value', $.expression),
878 choice(':', ';'),
879 repeat($.statement),
880 ),
881
882 default_statement: $ => seq(
883 keyword('default'),
884 choice(':', ';'),
885 repeat($.statement),
886 ),
887
888 compound_statement: $ => seq('{', repeat($.statement), '}'),
889
890 named_label_statement: $ => seq($.name, ':'),
891
892 expression_statement: $ => seq($.expression, $._semicolon),
893
894 expression: $ => choice(
895 $.conditional_expression,
896 $.match_expression,
897 $.augmented_assignment_expression,
898 $.assignment_expression,
899 $.reference_assignment_expression,
900 $.yield_expression,
901 $._unary_expression,
902 $.error_suppression_expression,
903 $.binary_expression,
904 $.include_expression,
905 $.include_once_expression,
906 $.require_expression,
907 $.require_once_expression,
908 ),
909
910 _unary_expression: $ => choice(
911 $.clone_expression,
912 $.primary_expression,
913 $.unary_op_expression,
914 $.cast_expression,
915 ),
916
917 unary_op_expression: $ => prec.left(PREC.NEG, seq(
918 field('operator', choice('+', '-', '~', '!')),
919 field('argument', $.expression),
920 )),
921
922 error_suppression_expression: $ => prec(PREC.INC, seq('@', $.expression)),
923
924 clone_expression: $ => seq(keyword('clone'), $.primary_expression),
925
926 primary_expression: $ => choice(
927 $._variable,
928 $.literal,
929 $.class_constant_access_expression,
930 $.qualified_name,
931 $.name,
932 $.array_creation_expression,
933 $.print_intrinsic,
934 $.anonymous_function,
935 $.arrow_function,
936 $.object_creation_expression,
937 $.update_expression,
938 $.shell_command_expression,
939 $.parenthesized_expression,
940 $.throw_expression,
941 $.arrow_function,
942 ),
943
944 parenthesized_expression: $ => seq('(', $.expression, ')'),
945
946 class_constant_access_expression: $ => seq(
947 $._scope_resolution_qualifier,
948 '::',
949 choice(
950 $._identifier,
951 seq('{', alias($.expression, $.name), '}'),
952 ),
953 ),
954
955 print_intrinsic: $ => seq(
956 keyword('print'), $.expression,
957 ),
958
959 object_creation_expression: $ => prec.right(PREC.NEW, seq(
960 keyword('new'),
961 choice(
962 seq($._class_name_reference, optional($.arguments)),
963 $.anonymous_class,
964 ),
965 )),
966
967 object_creation_expression: $ => choice(
968 $._new_dereferencable_expression,
969 $._new_non_dereferencable_expression,
970 ),
971
972 _new_non_dereferencable_expression: $ => prec.right(PREC.NEW, seq(
973 keyword('new'),
974 $._class_name_reference,
975 )),
976
977 _new_dereferencable_expression: $ => prec.right(PREC.NEW, seq(
978 keyword('new'),
979 choice(
980 seq($._class_name_reference, $.arguments),
981 $.anonymous_class,
982 ),
983 )),
984
985 _class_name_reference: $ => choice(
986 $._name,
987 $._new_variable,
988 $.parenthesized_expression,
989 ),
990
991 anonymous_class: $ => prec.right(seq(
992 optional(field('attributes', $.attribute_list)),
993 repeat($._modifier),
994 keyword('class'),
995 optional($.arguments),
996 optional($.base_clause),
997 optional($.class_interface_clause),
998 field('body', $.declaration_list),
999 )),
1000
1001 update_expression: $ => {
1002 const argument = field('argument', $._variable);
1003 const operator = field('operator', choice('--', '++'));
1004 return prec.left(PREC.INC, choice(
1005 seq(operator, argument),
1006 seq(argument, operator),
1007 ));
1008 },
1009
1010 cast_expression: $ => prec(PREC.CAST, seq(
1011 '(',
1012 field('type', $.cast_type),
1013 ')',
1014 field('value', choice(
1015 $._unary_expression,
1016 $.include_expression,
1017 $.include_once_expression,
1018 $.error_suppression_expression,
1019 )),
1020 )),
1021
1022 cast_variable: $ => prec(PREC.CAST, seq(
1023 '(', field('type', $.cast_type), ')',
1024 field('value', $._variable),
1025 )),
1026
1027 assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq(
1028 field('left', choice(
1029 $._variable,
1030 $.list_literal,
1031 )),
1032 '=',
1033 field('right', $.expression),
1034 )),
1035
1036 reference_assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq(
1037 field('left', choice(
1038 $._variable,
1039 $.list_literal,
1040 )),
1041 '=',
1042 '&',
1043 field('right', $.expression),
1044 )),
1045
1046 conditional_expression: $ => prec.left(PREC.TERNARY, seq( // TODO: Ternay is non-assossiative after PHP 8
1047 field('condition', $.expression),
1048 '?',
1049 field('body', optional($.expression)),
1050 ':',
1051 field('alternative', $.expression),
1052 )),
1053
1054 augmented_assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq(
1055 field('left', $._variable),
1056 field('operator', choice(
1057 '**=',
1058 '*=',
1059 '/=',
1060 '%=',
1061 '+=',
1062 '-=',
1063 '.=',
1064 '<<=',
1065 '>>=',
1066 '&=',
1067 '^=',
1068 '|=',
1069 '??=',
1070 )),
1071 field('right', $.expression),
1072 )),
1073
1074 _variable: $ => choice(
1075 alias($.cast_variable, $.cast_expression),
1076 $._new_variable,
1077 $._callable_variable,
1078 $.scoped_property_access_expression,
1079 $.member_access_expression,
1080 $.nullsafe_member_access_expression,
1081 ),
1082
1083 _variable_member_access_expression: $ => prec(PREC.MEMBER, seq(
1084 field('object', $._new_variable),
1085 '->',
1086 $._member_name,
1087 )),
1088
1089 member_access_expression: $ => prec(PREC.MEMBER, seq(
1090 field('object', $._dereferencable_expression),
1091 '->',
1092 $._member_name,
1093 )),
1094
1095 _variable_nullsafe_member_access_expression: $ => prec(PREC.MEMBER, seq(
1096 field('object', $._new_variable),
1097 '?->',
1098 $._member_name,
1099 )),
1100
1101 nullsafe_member_access_expression: $ => prec(PREC.MEMBER, seq(
1102 field('object', $._dereferencable_expression),
1103 '?->',
1104 $._member_name,
1105 )),
1106
1107 _variable_scoped_property_access_expression: $ => prec(PREC.MEMBER, seq(
1108 field('scope', choice($._name, $._new_variable)),
1109 '::',
1110 field('name', $._simple_variable),
1111 )),
1112
1113 scoped_property_access_expression: $ => prec(PREC.MEMBER, seq(
1114 field('scope', $._scope_resolution_qualifier),
1115 '::',
1116 field('name', $._simple_variable),
1117 )),
1118
1119 list_literal: $ => choice($._list_destructing, $._array_destructing),
1120
1121 _list_destructing: $ => seq(
1122 keyword('list'),
1123 '(',
1124 commaSep1(optional(
1125 choice(
1126 alias($._list_destructing, $.list_literal),
1127 $._variable,
1128 $.by_ref,
1129 seq(
1130 $.expression,
1131 '=>',
1132 choice(
1133 alias($._list_destructing, $.list_literal),
1134 $._variable,
1135 $.by_ref,
1136 ),
1137 ),
1138 ),
1139 )),
1140 ')',
1141 ),
1142
1143 _array_destructing: $ => seq(
1144 '[',
1145 commaSep1(optional($._array_destructing_element)),
1146 ']',
1147 ),
1148
1149 _array_destructing_element: $ => choice(
1150 choice(
1151 alias($._array_destructing, $.list_literal),
1152 $._variable,
1153 $.by_ref,
1154 ),
1155 seq(
1156 $.expression,
1157 '=>',
1158 choice(
1159 alias($._array_destructing, $.list_literal),
1160 $._variable,
1161 $.by_ref,
1162 ),
1163 ),
1164 ),
1165
1166 function_call_expression: $ => prec(PREC.CALL, seq(
1167 field('function', choice($._name, $._callable_expression)),
1168 field('arguments', $.arguments),
1169 )),
1170
1171 _callable_expression: $ => choice(
1172 $._callable_variable,
1173 $.parenthesized_expression,
1174 $._dereferencable_scalar,
1175 alias($._new_dereferencable_expression, $.object_creation_expression),
1176 ),
1177
1178 scoped_call_expression: $ => prec(PREC.CALL, seq(
1179 field('scope', $._scope_resolution_qualifier),
1180 '::',
1181 $._member_name,
1182 field('arguments', $.arguments),
1183 )),
1184
1185 _scope_resolution_qualifier: $ => choice(
1186 $.relative_scope,
1187 $._name,
1188 $._dereferencable_expression,
1189 ),
1190
1191 relative_scope: _ => prec(PREC.SCOPE, choice(
1192 'self',
1193 'parent',
1194 keyword('static'),
1195 )),
1196
1197 variadic_placeholder: _ => '...',
1198
1199 arguments: $ => seq(
1200 '(',
1201 optional(choice(
1202 seq(commaSep1($.argument), optional(',')),
1203 $.variadic_placeholder,
1204 )),
1205 ')',
1206 ),
1207
1208 argument: $ => seq(
1209 optional($._argument_name),
1210 optional(field('reference_modifier', $.reference_modifier)),
1211 choice(
1212 alias($._reserved_identifier, $.name),
1213 $.variadic_unpacking,
1214 $.expression,
1215 ),
1216 ),
1217
1218 _argument_name: $ => seq(
1219 field('name', alias(
1220 choice(
1221 $.name,
1222 keyword('array', false),
1223 keyword('fn', false),
1224 keyword('function', false),
1225 keyword('match', false),
1226 keyword('namespace', false),
1227 keyword('null', false),
1228 keyword('static', false),
1229 keyword('throw', false),
1230 'parent',
1231 'self',
1232 /true|false/i,
1233 ),
1234 $.name,
1235 )),
1236 ':',
1237 ),
1238
1239 member_call_expression: $ => prec(PREC.CALL, seq(
1240 field('object', $._dereferencable_expression),
1241 '->',
1242 $._member_name,
1243 field('arguments', $.arguments),
1244 )),
1245
1246 nullsafe_member_call_expression: $ => prec(PREC.CALL, seq(
1247 field('object', $._dereferencable_expression),
1248 '?->',
1249 $._member_name,
1250 field('arguments', $.arguments),
1251 )),
1252
1253 variadic_unpacking: $ => seq('...', $.expression),
1254
1255 _member_name: $ => choice(
1256 field('name', choice(
1257 $._identifier,
1258 $._simple_variable,
1259 )),
1260 seq('{', field('name', $.expression), '}'),
1261 ),
1262
1263 _variable_subscript_expression: $ => seq(
1264 $._new_variable,
1265 seq('[', optional($.expression), ']'),
1266 ),
1267
1268 _dereferencable_subscript_expression: $ => seq(
1269 $._dereferencable_expression,
1270 seq('[', optional($.expression), ']'),
1271 ),
1272
1273 _dereferencable_expression: $ => prec(PREC.DEREF, choice(
1274 $._variable,
1275 alias($._new_dereferencable_expression, $.object_creation_expression),
1276 $.class_constant_access_expression,
1277 $.parenthesized_expression,
1278 $._dereferencable_scalar,
1279 $._name,
1280 )),
1281
1282 _dereferencable_scalar: $ => prec(PREC.DEREF, choice(
1283 $.array_creation_expression,
1284 $._string,
1285 )),
1286
1287 array_creation_expression: $ => choice(
1288 seq(keyword('array'), '(', commaSep($.array_element_initializer), optional(','), ')'),
1289 seq('[', commaSep($.array_element_initializer), optional(','), ']'),
1290 ),
1291
1292 attribute_group: $ => seq(
1293 '#[',
1294 commaSep1($.attribute),
1295 optional(','),
1296 ']',
1297 ),
1298
1299 attribute_list: $ => repeat1($.attribute_group),
1300
1301 attribute: $ => seq(
1302 $._name,
1303 optional(field('parameters', $.arguments)),
1304 ),
1305
1306 _complex_string_part: $ => seq('{', $.expression, '}'),
1307
1308 _simple_string_member_access_expression: $ => prec(PREC.MEMBER, seq(
1309 field('object', $.variable_name),
1310 '->',
1311 field('name', $.name),
1312 )),
1313
1314 _simple_string_subscript_unary_expression: $ => prec.left(seq('-', $.integer)),
1315
1316 _simple_string_array_access_argument: $ => choice(
1317 $.integer,
1318 alias($._simple_string_subscript_unary_expression, $.unary_op_expression),
1319 $.name,
1320 $.variable_name,
1321 ),
1322
1323 _simple_string_subscript_expression: $ => prec(PREC.DEREF, seq(
1324 $.variable_name,
1325 seq('[', $._simple_string_array_access_argument, ']'),
1326 )),
1327
1328 _simple_string_part: $ => choice(
1329 alias($._simple_string_member_access_expression, $.member_access_expression),
1330 $._simple_variable,
1331 alias($._simple_string_subscript_expression, $.subscript_expression),
1332 ),
1333
1334 // Note: remember to also update the is_escapable_sequence method in the
1335 // external scanner whenever changing these rules
1336 escape_sequence: _ => token.immediate(seq(
1337 '\\',
1338 choice(
1339 'n',
1340 'r',
1341 't',
1342 'v',
1343 'e',
1344 'f',
1345 '\\',
1346 /\$/,
1347 '"',
1348 '`',
1349 /[0-7]{1,3}/,
1350 /x[0-9A-Fa-f]{1,2}/,
1351 /u\{[0-9A-Fa-f]+\}/,
1352 ),
1353 )),
1354
1355 _interpolated_string_body: $ => repeat1(
1356 choice(
1357 $.escape_sequence,
1358 seq($.variable_name, alias($.encapsed_string_chars_after_variable, $.string_content)),
1359 alias($.encapsed_string_chars, $.string_content),
1360 $._simple_string_part,
1361 $._complex_string_part,
1362 alias('\\u', $.string_content),
1363 ),
1364 ),
1365
1366 _interpolated_string_body_heredoc: $ => repeat1(
1367 choice(
1368 $.escape_sequence,
1369 seq(
1370 $.variable_name,
1371 alias($.encapsed_string_chars_after_variable_heredoc, $.string_content),
1372 ),
1373 alias($.encapsed_string_chars_heredoc, $.string_content),
1374 $._simple_string_part,
1375 $._complex_string_part,
1376 alias('\\u', $.string_content),
1377 ),
1378 ),
1379
1380 encapsed_string: $ => prec.right(seq(
1381 choice(/[bB]"/, '"'),
1382 optional($._interpolated_string_body),
1383 '"',
1384 )),
1385
1386 string: $ => seq(
1387 choice(/[bB]'/, '\''),
1388 repeat(choice(
1389 alias(token(choice('\\\\', '\\\'')), $.escape_sequence),
1390 $.string_content,
1391 )),
1392 '\'',
1393 ),
1394
1395 string_content: _ => prec.right(repeat1(token.immediate(prec(1, /\\?[^'\\]+/)))),
1396
1397 heredoc_body: $ => seq(
1398 $._new_line,
1399 repeat1(prec.right(seq(
1400 optional($._new_line),
1401 $._interpolated_string_body_heredoc,
1402 ))),
1403 ),
1404
1405 heredoc: $ => seq(
1406 token('<<<'),
1407 optional('"'),
1408 field('identifier', $.heredoc_start),
1409 optional(token.immediate('"')),
1410 choice(
1411 seq(
1412 field('value', $.heredoc_body),
1413 $._new_line,
1414 ),
1415 field('value', optional($.heredoc_body)),
1416 ),
1417 field('end_tag', $.heredoc_end),
1418 ),
1419
1420 _new_line: _ => /\r?\n|\r/,
1421
1422 nowdoc_body: $ => seq(
1423 $._new_line,
1424 repeat1($.nowdoc_string),
1425 ),
1426
1427 nowdoc: $ => seq(
1428 token('<<<'),
1429 '\'',
1430 field('identifier', $.heredoc_start),
1431 token.immediate('\''),
1432 choice(
1433 seq(
1434 field('value', $.nowdoc_body),
1435 $._new_line,
1436 ),
1437 field('value', optional($.nowdoc_body)),
1438 ),
1439 field('end_tag', $.heredoc_end),
1440 ),
1441
1442 _interpolated_execution_operator_body: $ => repeat1(
1443 choice(
1444 $.escape_sequence,
1445 seq($.variable_name, alias($.execution_string_chars_after_variable, $.string_content)),
1446 alias($.execution_string_chars, $.string_content),
1447 $._simple_string_part,
1448 $._complex_string_part,
1449 alias('\\u', $.string_content),
1450 ),
1451 ),
1452
1453 shell_command_expression: $ => seq(
1454 '`',
1455 optional($._interpolated_execution_operator_body),
1456 '`',
1457 ),
1458
1459 boolean: _ => /true|false/i,
1460
1461 null: _ => keyword('null', false),
1462
1463 _string: $ => choice($.encapsed_string, $.string, $.heredoc, $.nowdoc),
1464
1465 dynamic_variable_name: $ => choice(
1466 seq('$', $._simple_variable),
1467 seq('$', '{', $.expression, '}'),
1468 ),
1469
1470 _simple_variable: $ => choice($.variable_name, $.dynamic_variable_name),
1471
1472 _new_variable: $ => prec(1, choice(
1473 $._simple_variable,
1474 alias($._variable_subscript_expression, $.subscript_expression),
1475 alias($._variable_member_access_expression, $.member_access_expression),
1476 alias($._variable_nullsafe_member_access_expression, $.nullsafe_member_access_expression),
1477 alias($._variable_scoped_property_access_expression, $.scoped_property_access_expression),
1478 )),
1479
1480 _callable_variable: $ => choice(
1481 $._simple_variable,
1482 alias($._dereferencable_subscript_expression, $.subscript_expression),
1483 $.member_call_expression,
1484 $.nullsafe_member_call_expression,
1485 $.function_call_expression,
1486 $.scoped_call_expression,
1487 ),
1488
1489 variable_name: $ => seq('$', $.name),
1490
1491 by_ref: $ => seq('&', $._variable),
1492
1493 yield_expression: $ => prec.right(seq(
1494 keyword('yield'),
1495 optional(choice(
1496 $.array_element_initializer,
1497 seq(keyword('from'), $.expression),
1498 )),
1499 )),
1500
1501 array_element_initializer: $ => prec.right(choice(
1502 choice($.by_ref, $.expression),
1503 seq($.expression, '=>', choice($.by_ref, $.expression)),
1504 $.variadic_unpacking,
1505 )),
1506
1507 binary_expression: $ => choice(
1508 prec(PREC.INSTANCEOF, seq(
1509 field('left', $._unary_expression),
1510 field('operator', keyword('instanceof')),
1511 field('right', $._class_name_reference),
1512 )),
1513 prec.right(PREC.NULL_COALESCE, seq(
1514 field('left', $.expression),
1515 field('operator', '??'),
1516 field('right', $.expression),
1517 )),
1518 prec.right(PREC.EXPONENTIAL, seq(
1519 field('left', $.expression),
1520 field('operator', '**'),
1521 field('right', $.expression),
1522 )),
1523 ...[
1524 [keyword('and'), PREC.LOGICAL_AND_2],
1525 [keyword('or'), PREC.LOGICAL_OR_2],
1526 [keyword('xor'), PREC.LOGICAL_XOR],
1527 ['||', PREC.LOGICAL_OR_1],
1528 ['&&', PREC.LOGICAL_AND_1],
1529 ['|', PREC.BITWISE_OR],
1530 ['^', PREC.BITWISE_XOR],
1531 ['&', PREC.BITWISE_AND],
1532 ['==', PREC.EQUALITY],
1533 ['!=', PREC.EQUALITY],
1534 ['<>', PREC.EQUALITY],
1535 ['===', PREC.EQUALITY],
1536 ['!==', PREC.EQUALITY],
1537 ['<', PREC.INEQUALITY],
1538 ['>', PREC.INEQUALITY],
1539 ['<=', PREC.INEQUALITY],
1540 ['>=', PREC.INEQUALITY],
1541 ['<=>', PREC.EQUALITY],
1542 ['<<', PREC.SHIFT],
1543 ['>>', PREC.SHIFT],
1544 ['+', PREC.PLUS],
1545 ['-', PREC.PLUS],
1546 ['.', PREC.CONCAT],
1547 ['*', PREC.TIMES],
1548 ['/', PREC.TIMES],
1549 ['%', PREC.TIMES],
1550 // @ts-ignore
1551 ].map(([op, p]) => prec.left(p, seq(
1552 field('left', $.expression),
1553 // @ts-ignore
1554 field('operator', op),
1555 field('right', $.expression),
1556 ))),
1557 ),
1558
1559 include_expression: $ => seq(
1560 keyword('include'),
1561 $.expression,
1562 ),
1563
1564 include_once_expression: $ => seq(
1565 keyword('include_once'),
1566 $.expression,
1567 ),
1568
1569 require_expression: $ => seq(
1570 keyword('require'),
1571 $.expression,
1572 ),
1573
1574 require_once_expression: $ => seq(
1575 keyword('require_once'),
1576 $.expression,
1577 ),
1578
1579 // Note that PHP officially only supports the following character regex
1580 // for identifiers: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
1581 // However, there is a "bug" in how PHP parses multi-byte characters that allows
1582 // for a much larger range of characters to be used in identifiers.
1583 //
1584 // See: https://www.php.net/manual/en/language.variables.basics.php
1585 name: _ => {
1586 // We need to side step around the whitespace characters in the extras array.
1587 const range = String.raw`\u0080-\u009f\u00a1-\u200a\u200c-\u205f\u2061-\ufefe\uff00-\uffff`;
1588 return new RegExp(`[_a-zA-Z${range}][_a-zA-Z${range}\\d]*`);
1589 },
1590
1591 _reserved_identifier: _ => choice(
1592 'self',
1593 'parent',
1594 keyword('static'),
1595 ),
1596
1597 _identifier: $ => choice(
1598 $.name,
1599 alias($._reserved_identifier, $.name),
1600 ),
1601
1602 comment: _ => token(choice(
1603 seq(
1604 choice('//', /#[^?\[?\r?\n]/),
1605 repeat(/[^?\r?\n]|\?[^>\r\n]/),
1606 optional(/\?\r?\n/),
1607 ),
1608 '#',
1609 seq(
1610 '/*',
1611 /[^*]*\*+([^/*][^*]*\*+)*/,
1612 '/',
1613 ),
1614 )),
1615
1616 _semicolon: $ => choice($._automatic_semicolon, ';'),
1617 },
1618 });
1619};
1620
1621/**
1622 * Creates a regex that matches the given word case-insensitively,
1623 * and will alias the regex to the word if aliasAsWord is true
1624 *
1625 * @param {string} word
1626 * @param {boolean} aliasAsWord?
1627 *
1628 * @return {RegExp|AliasRule}
1629 */
1630function keyword(word, aliasAsWord = true) {
1631 /** @type {RegExp|AliasRule} */
1632 let result = new RegExp(word, 'i');
1633 if (aliasAsWord) result = alias(result, word);
1634 return result;
1635}
1636
1637/**
1638 * Creates a rule to match one or more of the rules separated by a comma
1639 *
1640 * @param {Rule} rule
1641 *
1642 * @return {SeqRule}
1643 *
1644 */
1645function commaSep1(rule) {
1646 return seq(rule, repeat(seq(',', rule)));
1647}
1648
1649/**
1650 * Creates a rule to optionally match one or more of the rules separated by a comma
1651 *
1652 * @param {Rule} rule
1653 *
1654 * @return {ChoiceRule}
1655 *
1656 */
1657function commaSep(rule) {
1658 return optional(commaSep1(rule));
1659}
1660
1661/**
1662 * Creates a rule to match one or more of the rules separated by a pipe
1663 *
1664 * @param {Rule} rule
1665 *
1666 * @return {SeqRule}
1667 */
1668function pipeSep1(rule) {
1669 return seq(rule, repeat(seq('|', rule)));
1670}
1671
1672/**
1673 * Creates a rule to match one or more of the rules separated by an ampersand
1674 *
1675 * @param {Rule} rule
1676 *
1677 * @return {SeqRule}
1678 */
1679function ampSep1(rule) {
1680 return seq(rule, repeat(seq(token('&'), rule)));
1681}