@willbooster/tree-sitter-php 0.0.0-placeholder.0 → 1.0.0

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