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}