tree-sitter-sed 0.6.0 → 0.8.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
@@ -13,6 +13,21 @@ function missingAtEndOrBoundary($, reason) {
13
13
  );
14
14
  }
15
15
 
16
+ function blankSeparatedFileOperand($, name, operand) {
17
+ return seq(
18
+ $._blanks,
19
+ choice(operand, missingAtEndOrBoundary($, `missing_${name}`)),
20
+ );
21
+ }
22
+
23
+ function fileOperandForms($, name, operand) {
24
+ return [
25
+ blankSeparatedFileOperand($, name, operand),
26
+ seq(issueField($, "omitted_file_separator"), operand),
27
+ missingAtEndOrBoundary($, `missing_${name}`),
28
+ ];
29
+ }
30
+
16
31
  function missingCommandSeparator($) {
17
32
  return choice(
18
33
  issueNode($, "missing_command_separator"),
@@ -24,14 +39,6 @@ function functionVerb($, spelling) {
24
39
  return field("verb", alias(spelling, $.function_verb));
25
40
  }
26
41
 
27
- function leadingEmptyCommands($) {
28
- return alias($._leading_empty_commands, $.empty_command);
29
- }
30
-
31
- function withLeadingEmptyCommands($, commands) {
32
- return choice(commands, seq(leadingEmptyCommands($), optional(commands)));
33
- }
34
-
35
42
  const delimiterNames = [
36
43
  "regex_address_start",
37
44
  "escaped_regex_address_start",
@@ -139,16 +146,35 @@ const functionDefinitions = [
139
146
  },
140
147
  ];
141
148
 
142
- const nonWriteSubstitutionFlagRules = [
143
- "occurrence_flag",
144
- "global_flag",
145
- "case_insensitive_flag",
146
- "print_flag",
147
- ];
149
+ // A known verb never becomes an unknown function: address maxima are owned by
150
+ // the excess address clauses, and a token that could also match a verb would
151
+ // let a GLR branch that expects other verbs compete with the valid parse.
152
+ const unknownFunctionCharacter = new RegExp(
153
+ `[^[:blank:][:digit:]$\\/\\\\!;}\\n${functionDefinitions
154
+ .map(({ spelling }) => spelling)
155
+ .join("")}]`,
156
+ );
157
+
158
+ // Letter flags are aliased at their use sites: a rule that is only a string
159
+ // shares its token with the function verbs and would wrap an anonymous leaf.
160
+ const letterSubstitutionFlags = {
161
+ global_flag: "g",
162
+ case_insensitive_flag: "i",
163
+ print_flag: "p",
164
+ };
165
+
166
+ function nonWriteSubstitutionFlags($) {
167
+ return [
168
+ $.occurrence_flag,
169
+ ...Object.entries(letterSubstitutionFlags).map(([rule, spelling]) =>
170
+ alias(spelling, $[rule]),
171
+ ),
172
+ ];
173
+ }
148
174
 
149
175
  function postWriteSubstitutionFlagChoice($) {
150
176
  return choice(
151
- ...nonWriteSubstitutionFlagRules.map((rule) => $[rule]),
177
+ ...nonWriteSubstitutionFlags($),
152
178
  alias("w", $.substitution_flag),
153
179
  );
154
180
  }
@@ -236,22 +262,9 @@ function commandListRules() {
236
262
  _command_list: ($) =>
237
263
  choice($._final_line, seq($._terminated_line, optional($._command_list))),
238
264
 
239
- _final_line: ($) =>
240
- choice($._line_content, alias($._blanks, $.empty_command)),
241
-
242
- _terminated_line: ($) =>
243
- choice(
244
- seq($._line_content, $._newline_separator),
245
- alias($._empty_terminated_line, $.empty_command),
246
- ),
247
-
248
- _empty_terminated_line: ($) =>
249
- seq(optional($._blanks), $._newline_separator),
265
+ _final_line: ($) => choice($._command_sequence, $._blanks),
250
266
 
251
- _line_content: ($) => withLeadingEmptyCommands($, $._command_sequence),
252
-
253
- _leading_empty_commands: ($) =>
254
- seq(optional($._blanks), $._semicolon_separators),
267
+ _terminated_line: ($) => seq($._command_sequence, $._newline_separator),
255
268
 
256
269
  _command_sequence: ($) =>
257
270
  choice(
@@ -261,9 +274,9 @@ function commandListRules() {
261
274
  ),
262
275
  seq(
263
276
  $._recovered_line_terminated_command_item,
264
- optional($._line_content),
277
+ optional(choice($._command_sequence, $._blanks)),
265
278
  ),
266
- seq($._chainable_command_item, optional($._command_sequence_tail)),
279
+ seq($._separable_command_item, optional($._command_sequence_tail)),
267
280
  seq(
268
281
  $._unmatched_closing_brace_item,
269
282
  optional($._unmatched_closing_brace_tail_content),
@@ -272,7 +285,10 @@ function commandListRules() {
272
285
 
273
286
  _command_sequence_tail: ($) =>
274
287
  choice(
275
- seq($._semicolon_separators, optional($._command_sequence)),
288
+ seq(
289
+ $._semicolon_separator,
290
+ optional(choice($._command_sequence, $._blanks)),
291
+ ),
276
292
  $._unmatched_closing_brace_tail,
277
293
  ),
278
294
 
@@ -295,6 +311,9 @@ function commandListRules() {
295
311
  ),
296
312
  ),
297
313
 
314
+ _separable_command_item: ($) =>
315
+ choice($.empty_command, $._chainable_command_item),
316
+
298
317
  _chainable_command_item: ($) =>
299
318
  choice(
300
319
  alias($._chainable_editing_command, $.editing_command),
@@ -312,39 +331,32 @@ function commandListRules() {
312
331
  $._line_terminated_command_item,
313
332
  seq(
314
333
  $._recovered_line_terminated_command_item,
315
- optional($._block_line_content),
334
+ optional($._block_command_sequence),
316
335
  ),
317
336
  seq(
318
- $._chainable_command_item,
319
- optional($._block_command_sequence_tail),
337
+ $._separable_command_item,
338
+ optional(
339
+ seq($._semicolon_separator, optional($._block_command_sequence)),
340
+ ),
320
341
  ),
321
342
  ),
322
343
 
323
- _block_command_sequence_tail: ($) =>
324
- seq($._semicolon_separators, optional($._block_command_sequence)),
325
-
326
- _block_line_content: ($) =>
327
- withLeadingEmptyCommands($, $._block_command_sequence),
328
-
329
- _block_line_content_before_close: ($) =>
330
- withLeadingEmptyCommands($, $._block_command_sequence_before_close),
331
-
332
344
  _block_command_sequence_before_close: ($) =>
333
345
  choice(
334
346
  seq(
335
- $._chainable_command_item,
336
- $._semicolon_separators,
347
+ $._separable_command_item,
348
+ $._semicolon_separator,
337
349
  optional($._block_command_sequence_before_close),
338
350
  ),
339
351
  seq(
340
352
  $._recovered_line_terminated_command_item,
341
- optional($._block_line_content_before_close),
353
+ optional($._block_command_sequence_before_close),
342
354
  ),
343
355
  ),
344
356
 
345
357
  _block_command_list: ($) =>
346
358
  choice(
347
- $._block_line_content_before_close,
359
+ $._block_command_sequence_before_close,
348
360
  seq($._terminated_block_line, optional($._block_command_list)),
349
361
  ),
350
362
 
@@ -354,26 +366,18 @@ function commandListRules() {
354
366
  _block_command_list_missing_separator: ($) =>
355
367
  choice(
356
368
  missingCommandSeparator($),
357
- seq(
358
- optional(leadingEmptyCommands($)),
359
- $._block_command_sequence,
360
- missingCommandSeparator($),
361
- ),
369
+ seq($._block_command_sequence, missingCommandSeparator($)),
362
370
  seq($._terminated_block_line, $._block_command_list_missing_separator),
363
371
  ),
364
372
 
365
373
  _terminated_block_line: ($) =>
366
- choice(
367
- seq($._block_line_content, $._newline_separator),
368
- alias($._empty_terminated_line, $.empty_command),
369
- ),
374
+ seq($._block_command_sequence, $._newline_separator),
370
375
 
371
376
  _newline_separator: ($) => alias("\n", $.command_separator),
372
377
 
373
- _semicolon_separators: ($) => prec.right(repeat1($._semicolon_separator)),
378
+ _semicolon_separator: ($) => alias(";", $.command_separator),
374
379
 
375
- _semicolon_separator: ($) =>
376
- prec.right(seq(alias(";", $.command_separator), optional($._blanks))),
380
+ empty_command: ($) => seq(optional($._blanks), $._empty_command_marker),
377
381
 
378
382
  _blanks: () => token(/[[:blank:]]+/),
379
383
  };
@@ -434,12 +438,10 @@ function addressRules(mode) {
434
438
  field("second", $.address),
435
439
  ),
436
440
  seq(
437
- field("first", $.address),
438
- commaSeparator($),
439
- $._omitted_second_address_issue,
440
- ),
441
- seq(
442
- issueField($, "omitted_first_address"),
441
+ choice(
442
+ field("first", $.address),
443
+ issueField($, "omitted_first_address"),
444
+ ),
443
445
  commaSeparator($),
444
446
  $._omitted_second_address_issue,
445
447
  ),
@@ -541,55 +543,56 @@ function addressRules(mode) {
541
543
  };
542
544
  }
543
545
 
546
+ function delimitedFunction($, spelling, name, first, middleReason, rest) {
547
+ return seq(
548
+ functionVerb($, spelling),
549
+ choice(
550
+ seq(
551
+ field("opening", delimiter($, `${name}_start`)),
552
+ optional(first),
553
+ choice(
554
+ seq(field("middle", delimiter($, `${name}_middle`)), ...rest),
555
+ field("middle", unterminatedDelimiter($, middleReason)),
556
+ ),
557
+ ),
558
+ missingOpeningDelimiter($),
559
+ ),
560
+ );
561
+ }
562
+
544
563
  function operandRules(mode) {
545
564
  const expression = mode === "bre" ? "basic_reg_exp" : "extended_reg_exp";
546
565
 
547
566
  function substitutionFlagChoice($) {
548
- const rules = nonWriteSubstitutionFlagRules.map((rule) => $[rule]);
549
- rules.push(issueField($, "invalid_substitution_flag"));
550
- return choice(...rules);
567
+ return choice(
568
+ ...nonWriteSubstitutionFlags($),
569
+ issueField($, "invalid_substitution_flag"),
570
+ );
551
571
  }
552
572
 
553
573
  return {
554
- _substitute_function: ($) =>
555
- choice(
556
- seq(
557
- $._complete_substitute_function,
558
- optional(
559
- field("flags", alias($._substitution_flags, $.substitution_flags)),
560
- ),
561
- ),
562
- $._incomplete_substitute_function,
563
- ),
564
-
565
- _complete_substitute_function: ($) =>
566
- seq(
567
- functionVerb($, "s"),
568
- field("opening", delimiter($, "substitute_start")),
569
- optional(field("expression", $[expression])),
570
- field("middle", delimiter($, "substitute_middle")),
571
- optional(field("replacement", $.replacement)),
572
- field("closing", delimiter($, "substitute_end")),
573
- ),
574
-
575
- _incomplete_substitute_function: ($) =>
576
- seq(
577
- functionVerb($, "s"),
578
- choice(
579
- missingOpeningDelimiter($),
580
- seq(
581
- field("opening", delimiter($, "substitute_start")),
582
- optional(field("expression", $[expression])),
583
- choice(
584
- seq(
585
- field("middle", delimiter($, "substitute_middle")),
586
- optional(field("replacement", $.replacement)),
587
- field("closing", unterminatedDelimiter($, "replacement")),
574
+ substitute_function: ($) =>
575
+ delimitedFunction(
576
+ $,
577
+ "s",
578
+ "substitute",
579
+ field("expression", $[expression]),
580
+ "regular_expression",
581
+ [
582
+ optional(field("replacement", $.replacement)),
583
+ choice(
584
+ seq(
585
+ field("closing", delimiter($, "substitute_end")),
586
+ optional(
587
+ field(
588
+ "flags",
589
+ alias($._substitution_flags, $.substitution_flags),
590
+ ),
588
591
  ),
589
- field("middle", unterminatedDelimiter($, "regular_expression")),
590
592
  ),
593
+ field("closing", unterminatedDelimiter($, "replacement")),
591
594
  ),
592
- ),
595
+ ],
593
596
  ),
594
597
 
595
598
  replacement: ($) =>
@@ -663,59 +666,38 @@ function operandRules(mode) {
663
666
 
664
667
  occurrence_flag: () => /[[:digit:]]+/,
665
668
 
666
- global_flag: () => "g",
667
-
668
- case_insensitive_flag: () => "i",
669
-
670
- print_flag: () => "p",
671
-
672
- write_flag: ($) =>
673
- seq(
669
+ write_flag: ($) => {
670
+ const wfile = field("wfile", alias($._substitution_wfile, $.wfile));
671
+ return seq(
674
672
  field("verb", alias("w", $.substitution_flag)),
675
673
  choice(
676
674
  seq(
677
675
  $._flag_after_write_marker,
678
676
  repeat1(postWriteSubstitutionFlagChoice($)),
679
- $._blanks,
680
- choice(
681
- field("wfile", alias($._substitution_wfile, $.wfile)),
682
- missingAtEndOrBoundary($, "missing_wfile"),
683
- ),
677
+ blankSeparatedFileOperand($, "wfile", wfile),
684
678
  ),
685
- seq($._blanks, field("wfile", alias($._substitution_wfile, $.wfile))),
686
- seq($._blanks, missingAtEndOrBoundary($, "missing_wfile")),
687
- seq(
688
- issueField($, "omitted_file_separator"),
689
- field("wfile", alias($._substitution_wfile, $.wfile)),
690
- ),
691
- missingAtEndOrBoundary($, "missing_wfile"),
679
+ ...fileOperandForms($, "wfile", wfile),
692
680
  ),
693
- ),
681
+ );
682
+ },
694
683
 
695
684
  translate_function: ($) =>
696
- seq(
697
- functionVerb($, "y"),
698
- choice(
699
- seq(
700
- field("opening", delimiter($, "translate_start")),
701
- optional(field("string1", $.translation_string)),
685
+ delimitedFunction(
686
+ $,
687
+ "y",
688
+ "translate",
689
+ field("string1", $.translation_string),
690
+ "translation",
691
+ [
692
+ optional(field("string2", $.translation_string)),
693
+ field(
694
+ "closing",
702
695
  choice(
703
- seq(
704
- field("middle", delimiter($, "translate_middle")),
705
- optional(field("string2", $.translation_string)),
706
- field(
707
- "closing",
708
- choice(
709
- delimiter($, "translate_end"),
710
- unterminatedDelimiter($, "translation"),
711
- ),
712
- ),
713
- ),
714
- field("middle", unterminatedDelimiter($, "translation")),
696
+ delimiter($, "translate_end"),
697
+ unterminatedDelimiter($, "translation"),
715
698
  ),
716
699
  ),
717
- missingOpeningDelimiter($),
718
- ),
700
+ ],
719
701
  ),
720
702
 
721
703
  translation_string: ($) =>
@@ -760,10 +742,12 @@ function functionRules() {
760
742
  issueField($, "unspecified_text_escape"),
761
743
  ),
762
744
  ),
763
- optional(issueField($, "missing_text")),
764
- $._text_end,
745
+ $._text_tail,
765
746
  ),
766
747
 
748
+ _text_tail: ($) =>
749
+ seq(optional(issueField($, "missing_text")), $._text_end),
750
+
767
751
  _text_end: ($) => choice($._text_line_end, $._text_eof),
768
752
 
769
753
  text_literal: ($) =>
@@ -778,6 +762,9 @@ function functionRules() {
778
762
  text_introducer: ($) =>
779
763
  namedExternal($, $._text_command_start, "text_introducer_token"),
780
764
 
765
+ _incomplete_text_introducer: ($) =>
766
+ issueField($, "incomplete_text_introducer"),
767
+
781
768
  rfile: ($) => namedExternal($, $._file_argument, "rfile_token"),
782
769
 
783
770
  wfile: ($) => namedExternal($, $._file_argument, "wfile_token"),
@@ -797,15 +784,7 @@ function functionRules() {
797
784
  };
798
785
 
799
786
  function fileForm(form) {
800
- const missingFileReason = `missing_${form}`;
801
- return ($) => [
802
- choice(
803
- seq($._blanks, field(form, $[form])),
804
- seq($._blanks, missingAtEndOrBoundary($, missingFileReason)),
805
- seq(issueField($, "omitted_file_separator"), field(form, $[form])),
806
- missingAtEndOrBoundary($, missingFileReason),
807
- ),
808
- ];
787
+ return ($) => [choice(...fileOperandForms($, form, field(form, $[form])))];
809
788
  }
810
789
 
811
790
  const formArguments = {
@@ -818,10 +797,11 @@ function functionRules() {
818
797
  choice(
819
798
  seq(
820
799
  field("introducer", $.text_introducer),
821
- choice(
822
- field("text", $.text),
823
- seq(optional(issueField($, "missing_text")), $._text_end),
824
- ),
800
+ choice(field("text", $.text), $._text_tail),
801
+ ),
802
+ field(
803
+ "introducer",
804
+ alias($._incomplete_text_introducer, $.text_introducer),
825
805
  ),
826
806
  missingAtEndOrBoundary($, "missing_text_introducer"),
827
807
  ),
@@ -830,7 +810,10 @@ function functionRules() {
830
810
  optional(
831
811
  prec.right(
832
812
  seq(
833
- field("separator", alias(" ", $.argument_separator)),
813
+ field(
814
+ "separator",
815
+ namedExternal($, $._argument_separator, "argument_separator"),
816
+ ),
834
817
  optional(field("label", $.label)),
835
818
  ),
836
819
  ),
@@ -863,124 +846,101 @@ function functionRules() {
863
846
  }
864
847
 
865
848
  function editingCommandRules() {
866
- const regularDefinitions = functionDefinitions.filter(
867
- ({ form }) => form !== "substitute",
868
- );
869
- const chainable = regularDefinitions.filter(
870
- ({ lineTerminated }) => !lineTerminated,
871
- );
872
- const lineTerminated = regularDefinitions.filter(
873
- ({ lineTerminated: isLineTerminated }) => isLineTerminated,
874
- );
875
-
876
- function functionsByMaximum(definitions, maximum) {
877
- return definitions
878
- .filter(({ maxAddresses = 2 }) => maxAddresses === maximum)
879
- .map(({ rule }) => rule);
880
- }
881
-
882
- function wrapperName(kind, maximum) {
883
- return `_${kind}_${maximum}_address_function`;
884
- }
849
+ const addressClauses = {
850
+ 0: ($) => alias($._max0_address_clause, $.address_clause),
851
+ 1: ($) => alias($._max1_address_clause, $.address_clause),
852
+ 2: ($) => $.address_clause,
853
+ };
885
854
 
886
- function functionWrapper($, name) {
887
- return alias($[name], $.function);
855
+ // One wrapper rule per (chainable or line-terminated) kind and address
856
+ // maximum keeps the maximum-specific GLR paths distinct until the verb.
857
+ const wrappers = [];
858
+ for (const [kind, isLineTerminated] of [
859
+ ["chainable", false],
860
+ ["line_terminated", true],
861
+ ]) {
862
+ for (const maximum of [0, 1, 2]) {
863
+ const names = functionDefinitions
864
+ .filter(
865
+ ({ lineTerminated = false, maxAddresses = 2 }) =>
866
+ lineTerminated === isLineTerminated && maxAddresses === maximum,
867
+ )
868
+ .map(({ rule }) => rule);
869
+ if (names.length > 0) {
870
+ wrappers.push({
871
+ kind,
872
+ maximum,
873
+ name: `_${kind}_${maximum}_address_function`,
874
+ names,
875
+ });
876
+ }
877
+ }
888
878
  }
889
879
 
890
- function addressClauseForMaximum($, maximum) {
891
- if (maximum === 0) {
892
- return alias($._max0_address_clause, $.address_clause);
893
- }
894
- if (maximum === 1) {
895
- return alias($._max1_address_clause, $.address_clause);
896
- }
897
- return $.address_clause;
880
+ function addressesPrefix($, addresses) {
881
+ return seq(field("addresses", addresses), optional($._blanks));
898
882
  }
899
883
 
900
884
  function addressedCommand($, addresses, selectedFunction) {
901
885
  return seq(
902
886
  optional($._blanks),
903
- optional(seq(field("addresses", addresses), optional($._blanks))),
887
+ optional(addressesPrefix($, addresses)),
904
888
  optional(field("negation", $.negation)),
905
889
  field("function", selectedFunction),
906
890
  );
907
891
  }
908
892
 
909
- function addressedFunction($, functionRule) {
910
- return addressedCommand(
911
- $,
912
- $.address_clause,
913
- alias(functionRule, $.function),
914
- );
915
- }
916
-
917
- function addressedForms($, definitions, kind) {
918
- const forms = [];
919
- for (const maximum of [0, 1, 2]) {
920
- const names = functionsByMaximum(definitions, maximum);
921
- if (names.length === 0) {
922
- continue;
923
- }
924
- forms.push(
925
- addressedCommand(
926
- $,
927
- addressClauseForMaximum($, maximum),
928
- functionWrapper($, wrapperName(kind, maximum)),
893
+ function addressedForms($, kind) {
894
+ return choice(
895
+ ...wrappers
896
+ .filter((wrapper) => wrapper.kind === kind)
897
+ .map(({ maximum, name }) =>
898
+ addressedCommand(
899
+ $,
900
+ addressClauses[maximum]($),
901
+ alias($[name], $.function),
902
+ ),
929
903
  ),
930
- );
931
- }
932
-
933
- return choice(...forms);
904
+ );
934
905
  }
935
906
 
936
907
  const rules = {
937
908
  _chainable_editing_command: ($) =>
938
909
  seq(
939
- choice(
940
- addressedForms($, chainable, "chainable"),
941
- addressedFunction($, $._chainable_substitute_function),
942
- ),
910
+ addressedForms($, "chainable"),
943
911
  optional(issueField($, "unexpected_command_text")),
944
912
  ),
945
913
 
946
914
  _line_terminated_editing_command: ($) =>
947
915
  seq(
948
- addressedForms($, lineTerminated, "line_terminated"),
916
+ addressedForms($, "line_terminated"),
949
917
  optional(issueField($, "unexpected_command_text")),
950
918
  ),
951
919
 
952
920
  _recovered_line_terminated_editing_command: ($) =>
953
- prec.right(
954
- seq(
955
- $._line_terminated_editing_command,
956
- issueField($, "forbidden_command_separator"),
957
- optional($._blanks),
958
- ),
921
+ seq(
922
+ $._line_terminated_editing_command,
923
+ issueField($, "forbidden_command_separator"),
959
924
  ),
960
925
 
961
926
  _recovered_editing_command: ($) =>
962
927
  choice(
963
- addressedFunction($, $._unknown_function),
964
- seq(
965
- optional($._blanks),
966
- optional(
967
- seq(field("addresses", $.address_clause), optional($._blanks)),
968
- ),
969
- field("negation", $.negation),
970
- field("function", alias($._reserved_unknown_function, $.function)),
928
+ addressedCommand(
929
+ $,
930
+ $.address_clause,
931
+ alias($._unknown_function, $.function),
971
932
  ),
972
933
  seq(
973
934
  optional($._blanks),
974
- field("addresses", alias($._double_address_clause, $.address_clause)),
975
- optional($._blanks),
935
+ optional(addressesPrefix($, $.address_clause)),
936
+ field("negation", $.negation),
976
937
  field("function", alias($._reserved_unknown_function, $.function)),
977
938
  ),
978
939
  seq(
979
940
  optional($._blanks),
980
941
  choice(
981
942
  seq(
982
- field("addresses", $.address_clause),
983
- optional($._blanks),
943
+ addressesPrefix($, $.address_clause),
984
944
  optional(field("negation", $.negation)),
985
945
  ),
986
946
  field("negation", $.negation),
@@ -1003,9 +963,6 @@ function editingCommandRules() {
1003
963
 
1004
964
  _missing_function: ($) => missingAtEndOrBoundary($, "missing_function"),
1005
965
 
1006
- _chainable_substitute_function: ($) =>
1007
- alias($._substitute_function, $.substitute_function),
1008
-
1009
966
  negation: ($) =>
1010
967
  seq(
1011
968
  field("operator", alias("!", $.negation_operator)),
@@ -1019,16 +976,8 @@ function editingCommandRules() {
1019
976
  ),
1020
977
  };
1021
978
 
1022
- for (const [kind, definitions] of [
1023
- ["chainable", chainable],
1024
- ["line_terminated", lineTerminated],
1025
- ]) {
1026
- for (const maximum of [0, 1, 2]) {
1027
- const names = functionsByMaximum(definitions, maximum);
1028
- if (names.length > 0) {
1029
- rules[wrapperName(kind, maximum)] = ($) => choiceForRules($, names);
1030
- }
1031
- }
979
+ for (const { name, names } of wrappers) {
980
+ rules[name] = ($) => choiceForRules($, names);
1032
981
  }
1033
982
 
1034
983
  return rules;
@@ -1276,19 +1225,19 @@ function issueDefinitions(mode) {
1276
1225
  },
1277
1226
  {
1278
1227
  reason: "bre_subexpression_left_anchor",
1279
- outcome: "implementation_option_syntax",
1228
+ outcome: "invalid_syntax",
1280
1229
  rule: ($) => $._regex_bre_subexpression_caret,
1281
1230
  },
1282
1231
  {
1283
1232
  reason: "bre_subexpression_right_anchor",
1284
- outcome: "implementation_option_syntax",
1233
+ outcome: "invalid_syntax",
1285
1234
  rule: ($) => $._regex_bre_subexpression_dollar,
1286
1235
  },
1287
1236
  ]
1288
1237
  : []),
1289
1238
  {
1290
1239
  reason: "omitted_file_separator",
1291
- outcome: "implementation_option_syntax",
1240
+ outcome: "invalid_syntax",
1292
1241
  rule: missing("omitted_file_separator"),
1293
1242
  },
1294
1243
  {
@@ -1321,7 +1270,7 @@ function issueDefinitions(mode) {
1321
1270
  {
1322
1271
  reason: "unknown_function",
1323
1272
  outcome: "nonconforming_syntax",
1324
- rule: () => token(prec(-2, /[^[:blank:][:digit:]$\/\\!;{}#\n]/)),
1273
+ rule: () => token(prec(-2, unknownFunctionCharacter)),
1325
1274
  },
1326
1275
  {
1327
1276
  id: "reserved_unknown_function",
@@ -1341,7 +1290,7 @@ function issueDefinitions(mode) {
1341
1290
  {
1342
1291
  reason: "forbidden_command_separator",
1343
1292
  outcome: "nonconforming_syntax",
1344
- rule: () => token.immediate(";"),
1293
+ rule: ($) => alias(token.immediate(";"), $.command_separator),
1345
1294
  },
1346
1295
  {
1347
1296
  reason: "blanks_around_address_separator",
@@ -1388,7 +1337,7 @@ function issueDefinitions(mode) {
1388
1337
  {
1389
1338
  reason: "invalid_delimiter",
1390
1339
  outcome: "nonconforming_syntax",
1391
- rule: () => "\\",
1340
+ rule: ($) => field("token", alias("\\", $.delimiter_token)),
1392
1341
  },
1393
1342
  {
1394
1343
  reason: "unterminated_regular_expression",
@@ -1449,6 +1398,11 @@ function issueDefinitions(mode) {
1449
1398
  outcome: "incomplete_syntax",
1450
1399
  rule: missing("missing_text_introducer"),
1451
1400
  },
1401
+ {
1402
+ reason: "incomplete_text_introducer",
1403
+ outcome: "incomplete_syntax",
1404
+ rule: ($) => $._text_incomplete_introducer,
1405
+ },
1452
1406
  {
1453
1407
  reason: "missing_text",
1454
1408
  outcome: "incomplete_syntax",
@@ -1651,6 +1605,7 @@ function externalTokens($, mode) {
1651
1605
  $._invalid_substitution_flag,
1652
1606
  $._flag_after_write_marker,
1653
1607
  $._text_command_start,
1608
+ $._text_incomplete_introducer,
1654
1609
  $._text_literal,
1655
1610
  $._text_backslash_escape,
1656
1611
  $._text_escaped_newline,
@@ -1662,7 +1617,9 @@ function externalTokens($, mode) {
1662
1617
  $._file_argument,
1663
1618
  $._substitution_wfile_argument,
1664
1619
  $._line_word,
1620
+ $._argument_separator,
1665
1621
  $._right_brace,
1622
+ $._empty_command_marker,
1666
1623
  $._reserved_unknown_function_token,
1667
1624
  $._regex_incomplete_group,
1668
1625
  $._regex_incomplete_bracket_term,
@@ -1692,7 +1649,6 @@ function defineGrammar(name, mode) {
1692
1649
  extras: () => [],
1693
1650
 
1694
1651
  conflicts: ($) => [
1695
- [$.address_clause, $._recovered_editing_command],
1696
1652
  [$.address_clause, $._max1_address_clause],
1697
1653
  [$._double_address_clause, $._excess_address_unit1_reason],
1698
1654
  [$.address, $._max0_address],