tree-sitter-sed 0.2.0 → 0.4.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.
package/common/grammar.js CHANGED
@@ -1,72 +1,11 @@
1
1
  const {
2
+ defineIssueRules,
2
3
  issueField,
3
4
  issueNode,
4
- issueRuleName,
5
5
  namedExternal,
6
6
  } = require("./dsl");
7
7
  const regularExpressionRules = require("./regex");
8
8
 
9
- const outcomes = [
10
- "undefined_syntax",
11
- "unspecified_syntax",
12
- "implementation_defined_syntax",
13
- "implementation_option_syntax",
14
- "nonconforming_syntax",
15
- "incomplete_syntax",
16
- ];
17
-
18
- function outcomeRuleName(id) {
19
- return `_${id}_outcome`;
20
- }
21
-
22
- function reasonRuleName(id) {
23
- return `_${id}_reason`;
24
- }
25
-
26
- function defineIssueRules(definitions) {
27
- const activeOutcomes = outcomes.filter((outcome) =>
28
- definitions.some((definition) => definition.outcome === outcome),
29
- );
30
- const rules = {
31
- syntax_issue: ($) => choice(...activeOutcomes.map((outcome) => $[outcome])),
32
- };
33
-
34
- for (const outcome of activeOutcomes) {
35
- rules[outcome] = ($) => {
36
- const reasons = [
37
- ...new Set(
38
- definitions
39
- .filter((definition) => definition.outcome === outcome)
40
- .map(({ reason }) => reason),
41
- ),
42
- ].map((reason) => $[reason]);
43
- return reasons.length === 1 ? reasons[0] : choice(...reasons);
44
- };
45
- }
46
-
47
- for (const reason of new Set(definitions.map(({ reason }) => reason))) {
48
- rules[reason] = ($) => {
49
- const variants = definitions
50
- .filter((definition) => definition.reason === reason)
51
- .map((definition) => {
52
- const id = definition.id ?? reason;
53
- return $[reasonRuleName(id)];
54
- });
55
- return variants.length === 1 ? variants[0] : choice(...variants);
56
- };
57
- }
58
-
59
- for (const definition of definitions) {
60
- const { outcome, reason, rule } = definition;
61
- const id = definition.id ?? reason;
62
- rules[reasonRuleName(id)] = rule;
63
- rules[outcomeRuleName(id)] = ($) => alias($[reasonRuleName(id)], $[reason]);
64
- rules[issueRuleName(id)] = ($) => alias($[outcomeRuleName(id)], $[outcome]);
65
- }
66
-
67
- return rules;
68
- }
69
-
70
9
  function missingAtEndOrBoundary($, reason) {
71
10
  return choice(
72
11
  issueField($, reason),
@@ -207,6 +146,13 @@ const nonWriteSubstitutionFlagRules = [
207
146
  "print_flag",
208
147
  ];
209
148
 
149
+ function postWriteSubstitutionFlagChoice($) {
150
+ return choice(
151
+ ...nonWriteSubstitutionFlagRules.map((rule) => $[rule]),
152
+ alias("w", $.substitution_flag),
153
+ );
154
+ }
155
+
210
156
  function delimiter($, name) {
211
157
  return alias($[`_${name}_delimiter`], $.delimiter);
212
158
  }
@@ -250,8 +196,6 @@ function commandListRules() {
250
196
  return {
251
197
  script: ($) => optional(alias($._script_command_list, $.command_list)),
252
198
 
253
- command_list: ($) => $._command_list,
254
-
255
199
  _script_command_list: ($) =>
256
200
  choice($._initial_suppressing_comment_command_list, $._command_list),
257
201
 
@@ -438,6 +382,14 @@ function commandListRules() {
438
382
  function addressRules(mode) {
439
383
  const expression = mode === "bre" ? "basic_reg_exp" : "extended_reg_exp";
440
384
 
385
+ function addressChoice($) {
386
+ return choice(
387
+ $.line_number_address,
388
+ $.last_line_address,
389
+ $.context_address,
390
+ );
391
+ }
392
+
441
393
  function contextAddress($, opening) {
442
394
  return seq(
443
395
  field("opening", opening),
@@ -452,84 +404,97 @@ function addressRules(mode) {
452
404
  );
453
405
  }
454
406
 
407
+ function commaSeparator($) {
408
+ return field(
409
+ "separator",
410
+ alias($._comma_address_separator, $.address_separator),
411
+ );
412
+ }
413
+
455
414
  return {
456
415
  address_clause: ($) =>
457
416
  choice(
458
417
  $._single_address_clause,
459
418
  $._double_address_clause,
460
- $._address_clause_with_excess,
419
+ $._max2_excess_address_clause,
461
420
  ),
462
421
 
463
422
  _single_address_clause: ($) => field("first", $.address),
464
423
 
465
424
  _double_address_clause: ($) =>
466
425
  choice(
467
- prec(
468
- 1,
469
- seq(
470
- field("first", $.address),
471
- field(
472
- "separator",
473
- alias($._address_separator, $.address_separator),
474
- ),
475
- field("second", $.address),
476
- ),
426
+ seq(
427
+ field("first", $.address),
428
+ field("separator", alias($._address_separator, $.address_separator)),
429
+ field("second", $.address),
477
430
  ),
478
- prec(
479
- -1,
480
- choice(
481
- seq(
482
- field(
483
- "separator",
484
- alias($._comma_address_separator, $.address_separator),
485
- ),
486
- field("second", $.address),
487
- issueField($, "omitted_first_address"),
488
- ),
489
- seq(
490
- field("first", $.address),
491
- field(
492
- "separator",
493
- alias($._comma_address_separator, $.address_separator),
494
- ),
495
- issueField($, "omitted_address"),
496
- ),
497
- seq(
498
- field(
499
- "separator",
500
- alias($._comma_address_separator, $.address_separator),
501
- ),
502
- issueField($, "omitted_address"),
503
- ),
504
- ),
431
+ seq(
432
+ issueField($, "omitted_first_address"),
433
+ commaSeparator($),
434
+ field("second", $.address),
435
+ ),
436
+ seq(
437
+ field("first", $.address),
438
+ commaSeparator($),
439
+ $._omitted_second_address_issue,
505
440
  ),
506
- ),
507
-
508
- _address_clause_with_excess: ($) =>
509
- prec(
510
- 2,
511
441
  seq(
512
- $._double_address_clause,
513
- issueField($, "additional_address"),
514
- repeat1(field("excess", $.excess_address)),
442
+ issueField($, "omitted_first_address"),
443
+ commaSeparator($),
444
+ $._omitted_second_address_issue,
515
445
  ),
516
446
  ),
517
447
 
518
- excess_address: ($) =>
448
+ _omitted_second_address_issue: ($) =>
519
449
  choice(
520
- seq(
521
- field("separator", alias($._address_separator, $.address_separator)),
522
- field("address", $.address),
450
+ issueField($, "omitted_address"),
451
+ issueField($, "incomplete_omitted_address"),
452
+ ),
453
+
454
+ _max2_excess_address_clause: ($) =>
455
+ prec.dynamic(
456
+ 1,
457
+ choice(
458
+ seq($._double_address_clause, $._max2_excess_address_tail),
459
+ seq($._max2_excess_address_clause, $._max2_excess_address_tail),
523
460
  ),
524
- seq(
525
- field(
526
- "separator",
527
- alias($._comma_address_separator, $.address_separator),
461
+ ),
462
+
463
+ _max1_address_clause: ($) =>
464
+ choice($._single_address_clause, $._max1_excess_address_clause),
465
+
466
+ _max1_excess_address_clause: ($) =>
467
+ prec.dynamic(
468
+ 1,
469
+ choice(
470
+ seq(field("first", $.address), $._max1_excess_address_tail),
471
+ seq(
472
+ issueField($, "omitted_first_address_unit1"),
473
+ $._max1_excess_address_tail,
528
474
  ),
529
- issueField($, "omitted_address"),
475
+ seq($._max1_excess_address_clause, $._max1_excess_address_tail),
530
476
  ),
531
477
  ),
532
478
 
479
+ _max0_address_clause: ($) =>
480
+ prec.dynamic(
481
+ 1,
482
+ choice(
483
+ issueField($, "excess_address"),
484
+ seq(
485
+ issueField($, "omitted_first_address_unit0"),
486
+ $._max0_excess_address_tail,
487
+ ),
488
+ seq($._max0_address_clause, $._max0_excess_address_tail),
489
+ ),
490
+ ),
491
+
492
+ _max0_excess_address_tail: ($) => issueField($, "excess_address_unit0"),
493
+
494
+ _max1_excess_address_tail: ($) => issueField($, "excess_address_unit1"),
495
+
496
+ _max2_excess_address_tail: ($) => issueField($, "excess_address_unit2"),
497
+
533
498
  _address_separator: ($) =>
534
499
  choice(
535
500
  $._comma_address_separator,
@@ -540,25 +505,23 @@ function addressRules(mode) {
540
505
  ),
541
506
 
542
507
  _comma_address_separator: ($) =>
543
- choice(
544
- prec(
545
- 1,
546
- seq(
547
- issueField($, "blanks_around_address_separator"),
548
- choice(
549
- seq($._blanks, $._address_separator_token, optional($._blanks)),
550
- seq($._address_separator_token, $._blanks),
551
- ),
552
- ),
553
- ),
508
+ seq(
509
+ optional(issueField($, "blanks_around_address_separator")),
554
510
  $._address_separator_token,
511
+ optional(issueField($, "blanks_around_address_separator")),
555
512
  ),
556
513
 
557
514
  _address_separator_token: ($) =>
558
515
  field("token", alias(",", $.address_separator_token)),
559
516
 
560
- address: ($) =>
561
- choice($.line_number_address, $.last_line_address, $.context_address),
517
+ address: addressChoice,
518
+
519
+ // Keep maximum-specific GLR paths distinct until the function verb.
520
+ _max0_address: addressChoice,
521
+
522
+ _max1_address: addressChoice,
523
+
524
+ _max2_address: addressChoice,
562
525
 
563
526
  line_number_address: () => /[[:digit:]]+/,
564
527
 
@@ -588,12 +551,6 @@ function operandRules(mode) {
588
551
  }
589
552
 
590
553
  return {
591
- substitute_function: ($) =>
592
- choice(
593
- $._substitute_function_without_write,
594
- $._substitute_function_with_write,
595
- ),
596
-
597
554
  _substitute_function_without_write: ($) =>
598
555
  choice(
599
556
  seq(
@@ -702,12 +659,6 @@ function operandRules(mode) {
702
659
  escaped_newline: ($) =>
703
660
  namedExternal($, $._replacement_escaped_newline, "escaped_newline_token"),
704
661
 
705
- substitution_flags: ($) =>
706
- choice(
707
- $._substitution_flags_without_write,
708
- $._substitution_flags_with_write,
709
- ),
710
-
711
662
  _substitution_flags_without_write: ($) =>
712
663
  choice(
713
664
  substitutionFlagChoice($),
@@ -731,6 +682,16 @@ function operandRules(mode) {
731
682
  seq(
732
683
  field("verb", alias("w", $.substitution_flag)),
733
684
  choice(
685
+ seq(
686
+ $._flag_after_write_marker,
687
+ issueField($, "flag_after_write_flag"),
688
+ repeat(postWriteSubstitutionFlagChoice($)),
689
+ $._blanks,
690
+ choice(
691
+ field("wfile", alias($._substitution_wfile, $.wfile)),
692
+ missingAtEndOrBoundary($, "missing_wfile"),
693
+ ),
694
+ ),
734
695
  seq($._blanks, field("wfile", alias($._substitution_wfile, $.wfile))),
735
696
  seq($._blanks, missingAtEndOrBoundary($, "missing_wfile")),
736
697
  seq(
@@ -799,8 +760,6 @@ function functionRules() {
799
760
  );
800
761
 
801
762
  const rules = {
802
- function: ($) => choice(...functionDefinitions.map(({ rule }) => $[rule])),
803
-
804
763
  text: ($) =>
805
764
  seq(
806
765
  repeat1(
@@ -847,77 +806,67 @@ function functionRules() {
847
806
  ),
848
807
  };
849
808
 
850
- for (const { rule, spelling } of noArgumentDefinitions) {
851
- rules[rule] = ($) => functionVerb($, spelling);
809
+ function fileForm(form) {
810
+ const missingFileReason = `missing_${form}`;
811
+ return ($) => [
812
+ choice(
813
+ seq($._blanks, field(form, $[form])),
814
+ seq($._blanks, missingAtEndOrBoundary($, missingFileReason)),
815
+ seq(issueField($, "omitted_file_separator"), field(form, $[form])),
816
+ missingAtEndOrBoundary($, missingFileReason),
817
+ ),
818
+ ];
852
819
  }
853
820
 
854
- for (const descriptor of functionDefinitions) {
855
- const { form, rule, spelling } = descriptor;
856
- if (form === undefined || form === "substitute" || form === "translate") {
857
- continue;
858
- }
859
- if (form === "block") {
860
- rules[rule] = ($) =>
861
- seq(
862
- functionVerb($, spelling),
863
- field("commands", alias($._block_commands, $.command_list)),
864
- field("closing", $.closing_brace),
865
- optional($._blanks),
866
- );
867
- } else if (form === "text") {
868
- rules[rule] = ($) =>
869
- seq(
870
- functionVerb($, spelling),
871
- choice(
872
- seq(
873
- field("introducer", $.text_introducer),
874
- choice(
875
- field("text", $.text),
876
- seq(optional(issueField($, "missing_text")), $._text_end),
877
- ),
878
- ),
879
- missingAtEndOrBoundary($, "missing_text_introducer"),
880
- ),
881
- );
882
- } else if (form === "optionalLabel") {
883
- rules[rule] = ($) =>
884
- seq(
885
- functionVerb($, spelling),
886
- optional(
887
- prec.right(
888
- seq(
889
- field("separator", alias(" ", $.argument_separator)),
890
- optional(field("label", $.label)),
891
- ),
892
- ),
893
- ),
894
- );
895
- } else if (form === "requiredLabel") {
896
- rules[rule] = ($) =>
821
+ const formArguments = {
822
+ block: ($) => [
823
+ field("commands", alias($._block_commands, $.command_list)),
824
+ field("closing", $.closing_brace),
825
+ optional($._blanks),
826
+ ],
827
+ text: ($) => [
828
+ choice(
897
829
  seq(
898
- functionVerb($, spelling),
830
+ field("introducer", $.text_introducer),
899
831
  choice(
900
- field("label", $.label),
901
- missingAtEndOrBoundary($, "missing_label"),
832
+ field("text", $.text),
833
+ seq(optional(issueField($, "missing_text")), $._text_end),
902
834
  ),
903
- );
904
- } else if (form === "rfile" || form === "wfile") {
905
- const missingFileReason =
906
- form === "rfile" ? "missing_rfile" : "missing_wfile";
907
- rules[rule] = ($) =>
908
- seq(
909
- functionVerb($, spelling),
910
- choice(
911
- seq($._blanks, field(form, $[form])),
912
- seq($._blanks, missingAtEndOrBoundary($, missingFileReason)),
913
- seq(issueField($, "omitted_file_separator"), field(form, $[form])),
914
- missingAtEndOrBoundary($, missingFileReason),
835
+ ),
836
+ missingAtEndOrBoundary($, "missing_text_introducer"),
837
+ ),
838
+ ],
839
+ optionalLabel: ($) => [
840
+ optional(
841
+ prec.right(
842
+ seq(
843
+ field("separator", alias(" ", $.argument_separator)),
844
+ optional(field("label", $.label)),
915
845
  ),
916
- );
917
- } else if (form === "comment") {
918
- rules[rule] = ($) =>
919
- seq(functionVerb($, spelling), optional(field("comment", $.comment)));
846
+ ),
847
+ ),
848
+ ],
849
+ requiredLabel: ($) => [
850
+ choice(
851
+ field("label", $.label),
852
+ missingAtEndOrBoundary($, "missing_label"),
853
+ ),
854
+ ],
855
+ rfile: fileForm("rfile"),
856
+ wfile: fileForm("wfile"),
857
+ comment: ($) => [optional(field("comment", $.comment))],
858
+ };
859
+
860
+ for (const { rule, spelling } of noArgumentDefinitions) {
861
+ rules[rule] = ($) => functionVerb($, spelling);
862
+ }
863
+
864
+ for (const { form, rule, spelling } of functionDefinitions) {
865
+ const argumentParts = formArguments[form];
866
+ if (argumentParts === undefined) {
867
+ continue;
920
868
  }
869
+ rules[rule] = ($) => seq(functionVerb($, spelling), ...argumentParts($));
921
870
  }
922
871
 
923
872
  return rules;
@@ -948,6 +897,33 @@ function editingCommandRules() {
948
897
  return alias($[name], $.function);
949
898
  }
950
899
 
900
+ function addressClauseForMaximum($, maximum) {
901
+ if (maximum === 0) {
902
+ return alias($._max0_address_clause, $.address_clause);
903
+ }
904
+ if (maximum === 1) {
905
+ return alias($._max1_address_clause, $.address_clause);
906
+ }
907
+ return $.address_clause;
908
+ }
909
+
910
+ function addressedCommand($, addresses, selectedFunction) {
911
+ return seq(
912
+ optional($._blanks),
913
+ optional(seq(field("addresses", addresses), optional($._blanks))),
914
+ optional(field("negation", $.negation)),
915
+ field("function", selectedFunction),
916
+ );
917
+ }
918
+
919
+ function addressedFunction($, functionRule) {
920
+ return addressedCommand(
921
+ $,
922
+ $.address_clause,
923
+ alias(functionRule, $.function),
924
+ );
925
+ }
926
+
951
927
  function addressedForms($, definitions, kind) {
952
928
  const forms = [];
953
929
  for (const maximum of [0, 1, 2]) {
@@ -955,98 +931,24 @@ function editingCommandRules() {
955
931
  if (names.length === 0) {
956
932
  continue;
957
933
  }
958
- const selectedFunction = functionWrapper($, wrapperName(kind, maximum));
959
-
960
- function addressedForm(
961
- addresses,
962
- { optionalAddresses = false, reportExcess = false } = {},
963
- ) {
964
- const parts = [optional($._blanks)];
965
- if (addresses !== null) {
966
- const addressed = seq(
967
- field("addresses", addresses),
968
- optional($._blanks),
969
- );
970
- parts.push(optionalAddresses ? optional(addressed) : addressed);
971
- }
972
- parts.push(
973
- optional(field("negation", $.negation)),
974
- field("function", selectedFunction),
975
- );
976
- if (reportExcess) {
977
- parts.push(issueField($, "excess_addresses"));
978
- }
979
- return seq(...parts);
980
- }
981
-
982
- if (maximum === 2) {
983
- forms.push(
984
- addressedForm($.address_clause, { optionalAddresses: true }),
985
- );
986
- continue;
987
- }
988
-
989
- const excessAddressForm = addressedForm(
990
- alias($._address_clause_with_excess, $.address_clause),
991
- { reportExcess: true },
934
+ forms.push(
935
+ addressedCommand(
936
+ $,
937
+ addressClauseForMaximum($, maximum),
938
+ functionWrapper($, wrapperName(kind, maximum)),
939
+ ),
992
940
  );
993
-
994
- if (maximum === 0) {
995
- forms.push(
996
- choice(
997
- addressedForm(null),
998
- addressedForm(
999
- alias(
1000
- choice($._single_address_clause, $._double_address_clause),
1001
- $.address_clause,
1002
- ),
1003
- { reportExcess: true },
1004
- ),
1005
- excessAddressForm,
1006
- ),
1007
- );
1008
- } else {
1009
- forms.push(
1010
- choice(
1011
- addressedForm(alias($._single_address_clause, $.address_clause), {
1012
- optionalAddresses: true,
1013
- }),
1014
- addressedForm(alias($._double_address_clause, $.address_clause), {
1015
- reportExcess: true,
1016
- }),
1017
- excessAddressForm,
1018
- ),
1019
- );
1020
- }
1021
941
  }
1022
942
 
1023
943
  return choice(...forms);
1024
944
  }
1025
945
 
1026
946
  const rules = {
1027
- editing_command: ($) =>
1028
- choice(
1029
- $._chainable_editing_command,
1030
- $._line_terminated_editing_command,
1031
- $._recovered_line_terminated_editing_command,
1032
- $._recovered_editing_command,
1033
- ),
1034
-
1035
947
  _chainable_editing_command: ($) =>
1036
948
  seq(
1037
949
  choice(
1038
950
  addressedForms($, chainable, "chainable"),
1039
- seq(
1040
- optional($._blanks),
1041
- optional(
1042
- seq(field("addresses", $.address_clause), optional($._blanks)),
1043
- ),
1044
- optional(field("negation", $.negation)),
1045
- field(
1046
- "function",
1047
- alias($._chainable_substitute_function, $.function),
1048
- ),
1049
- ),
951
+ addressedFunction($, $._chainable_substitute_function),
1050
952
  ),
1051
953
  optional(issueField($, "unexpected_command_text")),
1052
954
  ),
@@ -1084,28 +986,11 @@ function editingCommandRules() {
1084
986
  addressedForms($, lineTerminated, "line_terminated"),
1085
987
 
1086
988
  _line_terminated_substitute_editing_command_body: ($) =>
1087
- seq(
1088
- optional($._blanks),
1089
- optional(
1090
- seq(field("addresses", $.address_clause), optional($._blanks)),
1091
- ),
1092
- optional(field("negation", $.negation)),
1093
- field(
1094
- "function",
1095
- alias($._line_terminated_substitute_function, $.function),
1096
- ),
1097
- ),
989
+ addressedFunction($, $._line_terminated_substitute_function),
1098
990
 
1099
991
  _recovered_editing_command: ($) =>
1100
992
  choice(
1101
- seq(
1102
- optional($._blanks),
1103
- optional(
1104
- seq(field("addresses", $.address_clause), optional($._blanks)),
1105
- ),
1106
- optional(field("negation", $.negation)),
1107
- field("function", alias($._unknown_function, $.function)),
1108
- ),
993
+ addressedFunction($, $._unknown_function),
1109
994
  seq(
1110
995
  optional($._blanks),
1111
996
  optional(
@@ -1160,7 +1045,6 @@ function editingCommandRules() {
1160
1045
  repeat(
1161
1046
  seq(
1162
1047
  optional(issueField($, "blanks_after_negation")),
1163
- field("operator", alias("!", $.negation_operator)),
1164
1048
  issueField($, "duplicate_negation"),
1165
1049
  ),
1166
1050
  ),
@@ -1197,12 +1081,11 @@ function sedRules(mode) {
1197
1081
  function missingMarkerNames(mode) {
1198
1082
  return [
1199
1083
  "omitted_address",
1084
+ "incomplete_omitted_address",
1200
1085
  "omitted_first_address",
1201
1086
  "empty_subexpression",
1202
1087
  "missing_subexpression",
1203
1088
  ...(mode === "ere" ? ["empty_alternative"] : []),
1204
- "excess_addresses",
1205
- "additional_address",
1206
1089
  "missing_function",
1207
1090
  "missing_label",
1208
1091
  "missing_rfile",
@@ -1211,9 +1094,7 @@ function missingMarkerNames(mode) {
1211
1094
  "missing_text_introducer",
1212
1095
  "missing_text",
1213
1096
  "missing_command_separator",
1214
- "blanks_around_address_separator",
1215
1097
  "missing_address_separator",
1216
- "duplicate_negation",
1217
1098
  "missing_closing_brace",
1218
1099
  "missing_opening_delimiter",
1219
1100
  "missing_separator_before_unmatched_brace",
@@ -1272,12 +1153,25 @@ function issueDefinitions(mode) {
1272
1153
  outcome: "undefined_syntax",
1273
1154
  rule: missing("omitted_address"),
1274
1155
  },
1156
+ {
1157
+ id: "incomplete_omitted_address",
1158
+ reason: "omitted_address",
1159
+ outcome: "incomplete_syntax",
1160
+ rule: missing("incomplete_omitted_address"),
1161
+ },
1275
1162
  {
1276
1163
  id: "omitted_first_address",
1277
1164
  reason: "omitted_address",
1278
1165
  outcome: "undefined_syntax",
1279
1166
  rule: missing("omitted_first_address"),
1280
1167
  },
1168
+ // The marker is shared, but maximum-specific reductions must not merge.
1169
+ ...[0, 1].map((maximum) => ({
1170
+ id: `omitted_first_address_unit${maximum}`,
1171
+ reason: "omitted_address",
1172
+ outcome: "undefined_syntax",
1173
+ rule: missing("omitted_first_address"),
1174
+ })),
1281
1175
  {
1282
1176
  reason: "ordinary_character_escape",
1283
1177
  outcome: "undefined_syntax",
@@ -1298,12 +1192,22 @@ function issueDefinitions(mode) {
1298
1192
  {
1299
1193
  reason: "unmatched_subexpression_close",
1300
1194
  outcome: "undefined_syntax",
1301
- rule: ($) => $._regex_unmatched_group_close,
1195
+ rule: ($) =>
1196
+ namedExternal(
1197
+ $,
1198
+ $._regex_unmatched_group_close,
1199
+ "back_close_parenthesis_token",
1200
+ ),
1302
1201
  },
1303
1202
  {
1304
1203
  reason: "unmatched_interval_close",
1305
1204
  outcome: "undefined_syntax",
1306
- rule: ($) => $._unmatched_interval_close_marker,
1205
+ rule: ($) =>
1206
+ namedExternal(
1207
+ $,
1208
+ $._regex_unmatched_interval_close,
1209
+ "back_close_brace",
1210
+ ),
1307
1211
  },
1308
1212
  ]
1309
1213
  : []),
@@ -1351,6 +1255,11 @@ function issueDefinitions(mode) {
1351
1255
  outcome: "undefined_syntax",
1352
1256
  rule: () => token.immediate(";"),
1353
1257
  },
1258
+ {
1259
+ reason: "flag_after_write_flag",
1260
+ outcome: "undefined_syntax",
1261
+ rule: ($) => postWriteSubstitutionFlagChoice($),
1262
+ },
1354
1263
  {
1355
1264
  reason: "equivalence_class_range_start",
1356
1265
  outcome: "unspecified_syntax",
@@ -1369,7 +1278,7 @@ function issueDefinitions(mode) {
1369
1278
  {
1370
1279
  reason: "blanks_after_negation",
1371
1280
  outcome: "unspecified_syntax",
1372
- rule: () => token.immediate(/[[:blank:]]+/),
1281
+ rule: ($) => alias(token.immediate(/[[:blank:]]+/), $.blank),
1373
1282
  },
1374
1283
  {
1375
1284
  reason: "unspecified_replacement_escape",
@@ -1426,15 +1335,32 @@ function issueDefinitions(mode) {
1426
1335
  rule: missing("omitted_file_separator"),
1427
1336
  },
1428
1337
  {
1429
- reason: "excess_addresses",
1338
+ reason: "excess_address",
1430
1339
  outcome: "nonconforming_syntax",
1431
- rule: missing("excess_addresses"),
1340
+ rule: ($) => field("address", alias($._max0_address, $.address)),
1432
1341
  },
1433
- {
1434
- reason: "additional_address",
1342
+ ...[0, 1, 2].map((maximum) => ({
1343
+ id: `excess_address_unit${maximum}`,
1344
+ reason: "excess_address",
1435
1345
  outcome: "nonconforming_syntax",
1436
- rule: missing("additional_address"),
1437
- },
1346
+ rule: ($) =>
1347
+ choice(
1348
+ seq(
1349
+ field(
1350
+ "separator",
1351
+ alias($._address_separator, $.address_separator),
1352
+ ),
1353
+ field("address", alias($[`_max${maximum}_address`], $.address)),
1354
+ ),
1355
+ seq(
1356
+ field(
1357
+ "separator",
1358
+ alias($._comma_address_separator, $.address_separator),
1359
+ ),
1360
+ $._omitted_second_address_issue,
1361
+ ),
1362
+ ),
1363
+ })),
1438
1364
  {
1439
1365
  reason: "unknown_function",
1440
1366
  outcome: "nonconforming_syntax",
@@ -1463,7 +1389,7 @@ function issueDefinitions(mode) {
1463
1389
  {
1464
1390
  reason: "blanks_around_address_separator",
1465
1391
  outcome: "nonconforming_syntax",
1466
- rule: missing("blanks_around_address_separator"),
1392
+ rule: ($) => alias($._blanks_around_address_separator, $.blank),
1467
1393
  },
1468
1394
  {
1469
1395
  reason: "missing_address_separator",
@@ -1473,7 +1399,7 @@ function issueDefinitions(mode) {
1473
1399
  {
1474
1400
  reason: "duplicate_negation",
1475
1401
  outcome: "nonconforming_syntax",
1476
- rule: missing("duplicate_negation"),
1402
+ rule: ($) => field("operator", alias("!", $.negation_operator)),
1477
1403
  },
1478
1404
  {
1479
1405
  reason: "unmatched_closing_brace",
@@ -1701,7 +1627,6 @@ function externalTokens($, mode) {
1701
1627
  $._bre_question_mark_escape_marker,
1702
1628
  $._bre_plus_escape_marker,
1703
1629
  $._regex_unmatched_interval_close,
1704
- $._unmatched_interval_close_marker,
1705
1630
  ]
1706
1631
  : []),
1707
1632
  ...(mode === "ere" ? [$._regex_alternation_operator] : []),
@@ -1767,6 +1692,7 @@ function externalTokens($, mode) {
1767
1692
  $._translate_unterminated_source,
1768
1693
  $._translate_unterminated_destination,
1769
1694
  $._invalid_substitution_flag,
1695
+ $._flag_after_write_marker,
1770
1696
  $._text_command_start,
1771
1697
  $._text_literal,
1772
1698
  $._text_backslash_escape,
@@ -1790,6 +1716,7 @@ function externalTokens($, mode) {
1790
1716
  $._replacement_line_unterminated,
1791
1717
  $._translate_line_unterminated_source,
1792
1718
  $._translate_line_unterminated_destination,
1719
+ $._blanks_around_address_separator,
1793
1720
  ...missingMarkerNames(mode).map((name) => $[`_${name}_marker`]),
1794
1721
  $._error_sentinel,
1795
1722
  ];
@@ -1808,16 +1735,19 @@ function defineGrammar(name, mode) {
1808
1735
  extras: () => [],
1809
1736
 
1810
1737
  conflicts: ($) => [
1738
+ [$.address_clause, $._recovered_editing_command],
1739
+ [$.address_clause, $._max1_address_clause],
1740
+ [$._double_address_clause, $._excess_address_unit1_reason],
1741
+ [$.address, $._max0_address],
1742
+ [$.address, $._max1_address],
1811
1743
  [
1812
- $.address_clause,
1813
- $._chainable_editing_command,
1814
- $._line_terminated_regular_editing_command_body,
1744
+ $._omitted_first_address_reason,
1745
+ $._omitted_first_address_unit0_reason,
1746
+ $._omitted_first_address_unit1_reason,
1815
1747
  ],
1816
1748
  [
1817
- $.address_clause,
1818
- $._chainable_editing_command,
1819
- $._line_terminated_regular_editing_command_body,
1820
- $._recovered_editing_command,
1749
+ $._omitted_first_address_unit0_reason,
1750
+ $._omitted_first_address_unit1_reason,
1821
1751
  ],
1822
1752
  ],
1823
1753