tree-sitter-sed 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1832 @@
1
+ const {
2
+ issueField,
3
+ issueNode,
4
+ issueRuleName,
5
+ namedExternal,
6
+ } = require("./dsl");
7
+ const regularExpressionRules = require("./regex");
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
+ function missingAtEndOrBoundary($, reason) {
71
+ return choice(
72
+ issueField($, reason),
73
+ issueField($, `nonconforming_${reason}`),
74
+ );
75
+ }
76
+
77
+ function missingCommandSeparator($) {
78
+ return choice(
79
+ issueNode($, "missing_command_separator"),
80
+ issueNode($, "incomplete_missing_command_separator"),
81
+ );
82
+ }
83
+
84
+ function functionVerb($, spelling) {
85
+ return field("verb", alias(spelling, $.function_verb));
86
+ }
87
+
88
+ function leadingEmptyCommands($) {
89
+ return alias($._leading_empty_commands, $.empty_command);
90
+ }
91
+
92
+ function withLeadingEmptyCommands($, commands) {
93
+ return choice(commands, seq(leadingEmptyCommands($), optional(commands)));
94
+ }
95
+
96
+ const delimiterNames = [
97
+ "regex_address_start",
98
+ "escaped_regex_address_start",
99
+ "regex_address_end",
100
+ "substitute_start",
101
+ "substitute_middle",
102
+ "substitute_end",
103
+ "translate_start",
104
+ "translate_middle",
105
+ "translate_end",
106
+ ];
107
+
108
+ const missingDelimiterReasons = [
109
+ "incomplete_regular_expression",
110
+ "unterminated_regular_expression",
111
+ "incomplete_replacement",
112
+ "unterminated_replacement",
113
+ "incomplete_translation",
114
+ "unterminated_translation",
115
+ "invalid_delimiter",
116
+ "missing_opening_delimiter",
117
+ "nonconforming_missing_opening_delimiter",
118
+ ];
119
+
120
+ const functionDefinitions = [
121
+ { rule: "block_function", spelling: "{", form: "block" },
122
+ {
123
+ rule: "append_function",
124
+ spelling: "a",
125
+ form: "text",
126
+ maxAddresses: 1,
127
+ lineTerminated: true,
128
+ },
129
+ {
130
+ rule: "branch_function",
131
+ spelling: "b",
132
+ form: "optionalLabel",
133
+ lineTerminated: true,
134
+ },
135
+ {
136
+ rule: "change_function",
137
+ spelling: "c",
138
+ form: "text",
139
+ lineTerminated: true,
140
+ },
141
+ { rule: "delete_function", spelling: "d" },
142
+ { rule: "delete_first_line_function", spelling: "D" },
143
+ { rule: "get_function", spelling: "g" },
144
+ { rule: "get_append_function", spelling: "G" },
145
+ { rule: "hold_function", spelling: "h" },
146
+ { rule: "hold_append_function", spelling: "H" },
147
+ {
148
+ rule: "insert_function",
149
+ spelling: "i",
150
+ form: "text",
151
+ maxAddresses: 1,
152
+ lineTerminated: true,
153
+ },
154
+ { rule: "list_function", spelling: "l" },
155
+ { rule: "next_function", spelling: "n" },
156
+ { rule: "next_append_function", spelling: "N" },
157
+ { rule: "print_function", spelling: "p" },
158
+ { rule: "print_first_line_function", spelling: "P" },
159
+ { rule: "quit_function", spelling: "q", maxAddresses: 1 },
160
+ {
161
+ rule: "read_function",
162
+ spelling: "r",
163
+ form: "rfile",
164
+ maxAddresses: 1,
165
+ lineTerminated: true,
166
+ },
167
+ { rule: "substitute_function", spelling: "s", form: "substitute" },
168
+ {
169
+ rule: "test_function",
170
+ spelling: "t",
171
+ form: "optionalLabel",
172
+ lineTerminated: true,
173
+ },
174
+ {
175
+ rule: "write_function",
176
+ spelling: "w",
177
+ form: "wfile",
178
+ lineTerminated: true,
179
+ },
180
+ { rule: "exchange_function", spelling: "x" },
181
+ { rule: "translate_function", spelling: "y", form: "translate" },
182
+ {
183
+ rule: "label_function",
184
+ spelling: ":",
185
+ form: "requiredLabel",
186
+ maxAddresses: 0,
187
+ lineTerminated: true,
188
+ },
189
+ {
190
+ rule: "line_number_function",
191
+ spelling: "=",
192
+ maxAddresses: 1,
193
+ },
194
+ {
195
+ rule: "comment_function",
196
+ spelling: "#",
197
+ form: "comment",
198
+ maxAddresses: 0,
199
+ lineTerminated: true,
200
+ },
201
+ ];
202
+
203
+ const nonWriteSubstitutionFlagRules = [
204
+ "occurrence_flag",
205
+ "global_flag",
206
+ "case_insensitive_flag",
207
+ "print_flag",
208
+ ];
209
+
210
+ function delimiter($, name) {
211
+ return alias($[`_${name}_delimiter`], $.delimiter);
212
+ }
213
+
214
+ function missingOpeningDelimiter($) {
215
+ return field(
216
+ "opening",
217
+ choice(
218
+ delimiter($, "invalid_delimiter"),
219
+ delimiter($, "missing_opening_delimiter"),
220
+ delimiter($, "nonconforming_missing_opening_delimiter"),
221
+ ),
222
+ );
223
+ }
224
+
225
+ function unterminatedDelimiter($, construct) {
226
+ return choice(
227
+ delimiter($, `incomplete_${construct}`),
228
+ delimiter($, `unterminated_${construct}`),
229
+ );
230
+ }
231
+
232
+ function delimiterRules() {
233
+ const rules = {};
234
+ for (const name of delimiterNames) {
235
+ rules[`_${name}_delimiter`] = ($) =>
236
+ field("token", namedExternal($, $[`_${name}`], "delimiter_token"));
237
+ }
238
+ for (const reason of missingDelimiterReasons) {
239
+ rules[`_${reason}_delimiter`] = ($) => issueField($, reason);
240
+ }
241
+ return rules;
242
+ }
243
+
244
+ function choiceForRules($, names) {
245
+ const rules = names.map((name) => $[name]);
246
+ return rules.length === 1 ? rules[0] : choice(...rules);
247
+ }
248
+
249
+ function commandListRules() {
250
+ return {
251
+ script: ($) => optional(alias($._script_command_list, $.command_list)),
252
+
253
+ command_list: ($) => $._command_list,
254
+
255
+ _script_command_list: ($) =>
256
+ choice($._initial_suppressing_comment_command_list, $._command_list),
257
+
258
+ _initial_suppressing_comment_command_list: ($) =>
259
+ seq(
260
+ alias($._initial_suppressing_comment_command, $.editing_command),
261
+ optional(seq($._newline_separator, optional($._command_list))),
262
+ ),
263
+
264
+ _initial_suppressing_comment_command: ($) =>
265
+ field(
266
+ "function",
267
+ alias($._initial_suppressing_comment_function, $.function),
268
+ ),
269
+
270
+ _initial_suppressing_comment_function: ($) =>
271
+ alias($._initial_suppressing_comment_function_body, $.comment_function),
272
+
273
+ _initial_suppressing_comment_function_body: ($) =>
274
+ seq(
275
+ functionVerb($, "#"),
276
+ field("comment", alias($._initial_suppressing_comment, $.comment)),
277
+ ),
278
+
279
+ _initial_suppressing_comment: ($) =>
280
+ seq(
281
+ field(
282
+ "suppression",
283
+ namedExternal(
284
+ $,
285
+ $._default_output_suppression,
286
+ "default_output_suppression",
287
+ ),
288
+ ),
289
+ optional(namedExternal($, $._comment_text, "comment_text")),
290
+ ),
291
+
292
+ _command_list: ($) =>
293
+ choice($._final_line, seq($._terminated_line, optional($._command_list))),
294
+
295
+ _final_line: ($) =>
296
+ choice($._line_content, alias($._blanks, $.empty_command)),
297
+
298
+ _terminated_line: ($) =>
299
+ choice(
300
+ seq($._line_content, $._newline_separator),
301
+ alias($._empty_terminated_line, $.empty_command),
302
+ ),
303
+
304
+ _empty_terminated_line: ($) =>
305
+ seq(optional($._blanks), $._newline_separator),
306
+
307
+ _line_content: ($) => withLeadingEmptyCommands($, $._command_sequence),
308
+
309
+ _leading_empty_commands: ($) =>
310
+ seq(optional($._blanks), $._semicolon_separators),
311
+
312
+ _command_sequence: ($) =>
313
+ choice(
314
+ seq(
315
+ $._line_terminated_command_item,
316
+ optional($._unmatched_closing_brace_tail),
317
+ ),
318
+ seq(
319
+ $._recovered_line_terminated_command_item,
320
+ optional($._line_content),
321
+ ),
322
+ seq($._chainable_command_item, optional($._command_sequence_tail)),
323
+ seq(
324
+ $._unmatched_closing_brace_item,
325
+ optional($._unmatched_closing_brace_tail_content),
326
+ ),
327
+ ),
328
+
329
+ _command_sequence_tail: ($) =>
330
+ choice(
331
+ seq($._semicolon_separators, optional($._command_sequence)),
332
+ $._unmatched_closing_brace_tail,
333
+ ),
334
+
335
+ _unmatched_closing_brace_tail: ($) =>
336
+ seq(
337
+ issueNode($, "missing_separator_before_unmatched_brace"),
338
+ $._unmatched_closing_brace_item,
339
+ optional($._unmatched_closing_brace_tail_content),
340
+ ),
341
+
342
+ _unmatched_closing_brace_item: ($) =>
343
+ seq(issueNode($, "unmatched_closing_brace"), optional($._blanks)),
344
+
345
+ _unmatched_closing_brace_tail_content: ($) =>
346
+ choice(
347
+ $._command_sequence_tail,
348
+ seq(
349
+ issueNode($, "missing_separator_after_unmatched_brace"),
350
+ $._command_sequence,
351
+ ),
352
+ ),
353
+
354
+ _chainable_command_item: ($) =>
355
+ choice(
356
+ alias($._chainable_editing_command, $.editing_command),
357
+ alias($._recovered_editing_command, $.editing_command),
358
+ ),
359
+
360
+ _line_terminated_command_item: ($) =>
361
+ alias($._line_terminated_editing_command, $.editing_command),
362
+
363
+ _recovered_line_terminated_command_item: ($) =>
364
+ alias($._recovered_line_terminated_editing_command, $.editing_command),
365
+
366
+ _block_command_sequence: ($) =>
367
+ choice(
368
+ $._line_terminated_command_item,
369
+ seq(
370
+ $._recovered_line_terminated_command_item,
371
+ optional($._block_line_content),
372
+ ),
373
+ seq(
374
+ $._chainable_command_item,
375
+ optional($._block_command_sequence_tail),
376
+ ),
377
+ ),
378
+
379
+ _block_command_sequence_tail: ($) =>
380
+ seq($._semicolon_separators, optional($._block_command_sequence)),
381
+
382
+ _block_line_content: ($) =>
383
+ withLeadingEmptyCommands($, $._block_command_sequence),
384
+
385
+ _block_line_content_before_close: ($) =>
386
+ withLeadingEmptyCommands($, $._block_command_sequence_before_close),
387
+
388
+ _block_command_sequence_before_close: ($) =>
389
+ choice(
390
+ seq(
391
+ $._chainable_command_item,
392
+ $._semicolon_separators,
393
+ optional($._block_command_sequence_before_close),
394
+ ),
395
+ seq(
396
+ $._recovered_line_terminated_command_item,
397
+ optional($._block_line_content_before_close),
398
+ ),
399
+ ),
400
+
401
+ _block_command_list: ($) =>
402
+ choice(
403
+ $._block_line_content_before_close,
404
+ seq($._terminated_block_line, optional($._block_command_list)),
405
+ ),
406
+
407
+ _block_commands: ($) =>
408
+ choice($._block_command_list, $._block_command_list_missing_separator),
409
+
410
+ _block_command_list_missing_separator: ($) =>
411
+ choice(
412
+ missingCommandSeparator($),
413
+ seq(
414
+ optional(leadingEmptyCommands($)),
415
+ $._block_command_sequence,
416
+ missingCommandSeparator($),
417
+ ),
418
+ seq($._terminated_block_line, $._block_command_list_missing_separator),
419
+ ),
420
+
421
+ _terminated_block_line: ($) =>
422
+ choice(
423
+ seq($._block_line_content, $._newline_separator),
424
+ alias($._empty_terminated_line, $.empty_command),
425
+ ),
426
+
427
+ _newline_separator: ($) => alias("\n", $.command_separator),
428
+
429
+ _semicolon_separators: ($) => prec.right(repeat1($._semicolon_separator)),
430
+
431
+ _semicolon_separator: ($) =>
432
+ prec.right(seq(alias(";", $.command_separator), optional($._blanks))),
433
+
434
+ _blanks: () => token(/[[:blank:]]+/),
435
+ };
436
+ }
437
+
438
+ function addressRules(mode) {
439
+ const expression = mode === "bre" ? "basic_reg_exp" : "extended_reg_exp";
440
+
441
+ function contextAddress($, opening) {
442
+ return seq(
443
+ field("opening", opening),
444
+ optional(field("expression", $[expression])),
445
+ field(
446
+ "closing",
447
+ choice(
448
+ delimiter($, "regex_address_end"),
449
+ unterminatedDelimiter($, "regular_expression"),
450
+ ),
451
+ ),
452
+ );
453
+ }
454
+
455
+ return {
456
+ address_clause: ($) =>
457
+ choice(
458
+ $._single_address_clause,
459
+ $._double_address_clause,
460
+ $._address_clause_with_excess,
461
+ ),
462
+
463
+ _single_address_clause: ($) => field("first", $.address),
464
+
465
+ _double_address_clause: ($) =>
466
+ 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
+ ),
477
+ ),
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
+ ),
505
+ ),
506
+ ),
507
+
508
+ _address_clause_with_excess: ($) =>
509
+ prec(
510
+ 2,
511
+ seq(
512
+ $._double_address_clause,
513
+ issueField($, "additional_address"),
514
+ repeat1(field("excess", $.excess_address)),
515
+ ),
516
+ ),
517
+
518
+ excess_address: ($) =>
519
+ choice(
520
+ seq(
521
+ field("separator", alias($._address_separator, $.address_separator)),
522
+ field("address", $.address),
523
+ ),
524
+ seq(
525
+ field(
526
+ "separator",
527
+ alias($._comma_address_separator, $.address_separator),
528
+ ),
529
+ issueField($, "omitted_address"),
530
+ ),
531
+ ),
532
+
533
+ _address_separator: ($) =>
534
+ choice(
535
+ $._comma_address_separator,
536
+ prec(
537
+ -1,
538
+ seq(issueField($, "missing_address_separator"), optional($._blanks)),
539
+ ),
540
+ ),
541
+
542
+ _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
+ ),
554
+ $._address_separator_token,
555
+ ),
556
+
557
+ _address_separator_token: ($) =>
558
+ field("token", alias(",", $.address_separator_token)),
559
+
560
+ address: ($) =>
561
+ choice($.line_number_address, $.last_line_address, $.context_address),
562
+
563
+ line_number_address: () => /[[:digit:]]+/,
564
+
565
+ last_line_address: () => "$",
566
+
567
+ context_address: ($) =>
568
+ choice(
569
+ contextAddress($, delimiter($, "regex_address_start")),
570
+ seq(
571
+ field("escape", alias("\\", $.address_escape)),
572
+ choice(
573
+ contextAddress($, delimiter($, "escaped_regex_address_start")),
574
+ missingOpeningDelimiter($),
575
+ ),
576
+ ),
577
+ ),
578
+ };
579
+ }
580
+
581
+ function operandRules(mode) {
582
+ const expression = mode === "bre" ? "basic_reg_exp" : "extended_reg_exp";
583
+
584
+ function substitutionFlagChoice($) {
585
+ const rules = nonWriteSubstitutionFlagRules.map((rule) => $[rule]);
586
+ rules.push(issueField($, "invalid_substitution_flag"));
587
+ return choice(...rules);
588
+ }
589
+
590
+ return {
591
+ substitute_function: ($) =>
592
+ choice(
593
+ $._substitute_function_without_write,
594
+ $._substitute_function_with_write,
595
+ ),
596
+
597
+ _substitute_function_without_write: ($) =>
598
+ choice(
599
+ seq(
600
+ $._complete_substitute_function,
601
+ optional(
602
+ field(
603
+ "flags",
604
+ alias($._substitution_flags_without_write, $.substitution_flags),
605
+ ),
606
+ ),
607
+ ),
608
+ $._incomplete_substitute_function,
609
+ ),
610
+
611
+ _substitute_function_with_write: ($) =>
612
+ seq(
613
+ $._complete_substitute_function,
614
+ field(
615
+ "flags",
616
+ alias($._substitution_flags_with_write, $.substitution_flags),
617
+ ),
618
+ ),
619
+
620
+ _complete_substitute_function: ($) =>
621
+ seq(
622
+ functionVerb($, "s"),
623
+ field("opening", delimiter($, "substitute_start")),
624
+ optional(field("expression", $[expression])),
625
+ field("middle", delimiter($, "substitute_middle")),
626
+ optional(field("replacement", $.replacement)),
627
+ field("closing", delimiter($, "substitute_end")),
628
+ ),
629
+
630
+ _incomplete_substitute_function: ($) =>
631
+ seq(
632
+ functionVerb($, "s"),
633
+ choice(
634
+ missingOpeningDelimiter($),
635
+ seq(
636
+ field("opening", delimiter($, "substitute_start")),
637
+ optional(field("expression", $[expression])),
638
+ choice(
639
+ seq(
640
+ field("middle", delimiter($, "substitute_middle")),
641
+ optional(field("replacement", $.replacement)),
642
+ field("closing", unterminatedDelimiter($, "replacement")),
643
+ ),
644
+ field("middle", unterminatedDelimiter($, "regular_expression")),
645
+ ),
646
+ ),
647
+ ),
648
+ ),
649
+
650
+ replacement: ($) =>
651
+ repeat1(
652
+ choice(
653
+ $.replacement_literal,
654
+ $.matched_text_reference,
655
+ $.replacement_backreference,
656
+ $.replacement_escaped_delimiter,
657
+ $.ambiguous_replacement_delimiter_escape,
658
+ $.replacement_escape,
659
+ $.escaped_newline,
660
+ issueField($, "unspecified_replacement_escape"),
661
+ issueField($, "incomplete_replacement_escape"),
662
+ ),
663
+ ),
664
+
665
+ replacement_literal: ($) =>
666
+ namedExternal($, $._replacement_literal, "replacement_literal_token"),
667
+
668
+ matched_text_reference: ($) =>
669
+ namedExternal(
670
+ $,
671
+ $._replacement_match_reference,
672
+ "matched_text_reference_token",
673
+ ),
674
+
675
+ replacement_backreference: ($) =>
676
+ namedExternal(
677
+ $,
678
+ $._replacement_backreference,
679
+ "replacement_backreference_token",
680
+ ),
681
+
682
+ replacement_escaped_delimiter: ($) =>
683
+ field(
684
+ "token",
685
+ namedExternal(
686
+ $,
687
+ $._replacement_escaped_delimiter,
688
+ "escaped_delimiter_token",
689
+ ),
690
+ ),
691
+
692
+ ambiguous_replacement_delimiter_escape: ($) =>
693
+ issueField($, "replacement_ampersand_delimiter_escape"),
694
+
695
+ replacement_escape: ($) =>
696
+ namedExternal(
697
+ $,
698
+ $._replacement_escape_sequence,
699
+ "replacement_escape_token",
700
+ ),
701
+
702
+ escaped_newline: ($) =>
703
+ namedExternal($, $._replacement_escaped_newline, "escaped_newline_token"),
704
+
705
+ substitution_flags: ($) =>
706
+ choice(
707
+ $._substitution_flags_without_write,
708
+ $._substitution_flags_with_write,
709
+ ),
710
+
711
+ _substitution_flags_without_write: ($) =>
712
+ choice(
713
+ substitutionFlagChoice($),
714
+ prec.left(
715
+ seq($._substitution_flags_without_write, substitutionFlagChoice($)),
716
+ ),
717
+ ),
718
+
719
+ _substitution_flags_with_write: ($) =>
720
+ seq(optional($._substitution_flags_without_write), $.write_flag),
721
+
722
+ occurrence_flag: () => /[[:digit:]]+/,
723
+
724
+ global_flag: () => "g",
725
+
726
+ case_insensitive_flag: () => "i",
727
+
728
+ print_flag: () => "p",
729
+
730
+ write_flag: ($) =>
731
+ seq(
732
+ field("verb", alias("w", $.substitution_flag)),
733
+ choice(
734
+ seq($._blanks, field("wfile", alias($._substitution_wfile, $.wfile))),
735
+ seq($._blanks, missingAtEndOrBoundary($, "missing_wfile")),
736
+ seq(
737
+ issueField($, "omitted_file_separator"),
738
+ field("wfile", alias($._substitution_wfile, $.wfile)),
739
+ ),
740
+ missingAtEndOrBoundary($, "missing_wfile"),
741
+ ),
742
+ ),
743
+
744
+ translate_function: ($) =>
745
+ seq(
746
+ functionVerb($, "y"),
747
+ choice(
748
+ seq(
749
+ field("opening", delimiter($, "translate_start")),
750
+ optional(field("string1", $.translation_string)),
751
+ choice(
752
+ seq(
753
+ field("middle", delimiter($, "translate_middle")),
754
+ optional(field("string2", $.translation_string)),
755
+ field(
756
+ "closing",
757
+ choice(
758
+ delimiter($, "translate_end"),
759
+ unterminatedDelimiter($, "translation"),
760
+ ),
761
+ ),
762
+ ),
763
+ field("middle", unterminatedDelimiter($, "translation")),
764
+ ),
765
+ ),
766
+ missingOpeningDelimiter($),
767
+ ),
768
+ ),
769
+
770
+ translation_string: ($) =>
771
+ repeat1(
772
+ choice(
773
+ $.translation_literal,
774
+ $.translation_escape,
775
+ $.translation_escaped_delimiter,
776
+ issueField($, "undefined_translation_escape"),
777
+ issueField($, "incomplete_translation_escape"),
778
+ ),
779
+ ),
780
+
781
+ translation_literal: ($) =>
782
+ namedExternal($, $._translate_literal, "translation_literal_token"),
783
+
784
+ translation_escape: ($) =>
785
+ namedExternal($, $._translate_escape, "translation_escape_token"),
786
+
787
+ translation_escaped_delimiter: ($) =>
788
+ namedExternal(
789
+ $,
790
+ $._translate_escaped_delimiter,
791
+ "escaped_delimiter_token",
792
+ ),
793
+ };
794
+ }
795
+
796
+ function functionRules() {
797
+ const noArgumentDefinitions = functionDefinitions.filter(
798
+ ({ form }) => form === undefined,
799
+ );
800
+
801
+ const rules = {
802
+ function: ($) => choice(...functionDefinitions.map(({ rule }) => $[rule])),
803
+
804
+ text: ($) =>
805
+ seq(
806
+ repeat1(
807
+ choice(
808
+ $.text_literal,
809
+ $.text_backslash_escape,
810
+ $.text_escaped_newline,
811
+ issueField($, "unspecified_text_escape"),
812
+ ),
813
+ ),
814
+ optional(issueField($, "missing_text")),
815
+ $._text_end,
816
+ ),
817
+
818
+ _text_end: ($) => choice($._text_line_end, $._text_eof),
819
+
820
+ text_literal: ($) =>
821
+ namedExternal($, $._text_literal, "text_literal_token"),
822
+
823
+ text_backslash_escape: ($) =>
824
+ namedExternal($, $._text_backslash_escape, "text_backslash_escape_token"),
825
+
826
+ text_escaped_newline: ($) =>
827
+ namedExternal($, $._text_escaped_newline, "text_escaped_newline_token"),
828
+
829
+ text_introducer: ($) =>
830
+ namedExternal($, $._text_command_start, "text_introducer_token"),
831
+
832
+ rfile: ($) => namedExternal($, $._file_argument, "rfile_token"),
833
+
834
+ wfile: ($) => namedExternal($, $._file_argument, "wfile_token"),
835
+
836
+ _substitution_wfile: ($) =>
837
+ namedExternal($, $._substitution_wfile_argument, "wfile_token"),
838
+
839
+ label: ($) => namedExternal($, $._line_word, "label_token"),
840
+
841
+ comment: ($) => namedExternal($, $._comment_text, "comment_text"),
842
+
843
+ closing_brace: ($) =>
844
+ choice(
845
+ namedExternal($, $._right_brace, "closing_brace_token"),
846
+ issueField($, "missing_closing_brace"),
847
+ ),
848
+ };
849
+
850
+ for (const { rule, spelling } of noArgumentDefinitions) {
851
+ rules[rule] = ($) => functionVerb($, spelling);
852
+ }
853
+
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] = ($) =>
897
+ seq(
898
+ functionVerb($, spelling),
899
+ choice(
900
+ field("label", $.label),
901
+ missingAtEndOrBoundary($, "missing_label"),
902
+ ),
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),
915
+ ),
916
+ );
917
+ } else if (form === "comment") {
918
+ rules[rule] = ($) =>
919
+ seq(functionVerb($, spelling), optional(field("comment", $.comment)));
920
+ }
921
+ }
922
+
923
+ return rules;
924
+ }
925
+
926
+ function editingCommandRules() {
927
+ const regularDefinitions = functionDefinitions.filter(
928
+ ({ form }) => form !== "substitute",
929
+ );
930
+ const chainable = regularDefinitions.filter(
931
+ ({ lineTerminated }) => !lineTerminated,
932
+ );
933
+ const lineTerminated = regularDefinitions.filter(
934
+ ({ lineTerminated: isLineTerminated }) => isLineTerminated,
935
+ );
936
+
937
+ function functionsByMaximum(definitions, maximum) {
938
+ return definitions
939
+ .filter(({ maxAddresses = 2 }) => maxAddresses === maximum)
940
+ .map(({ rule }) => rule);
941
+ }
942
+
943
+ function wrapperName(kind, maximum) {
944
+ return `_${kind}_${maximum}_address_function`;
945
+ }
946
+
947
+ function functionWrapper($, name) {
948
+ return alias($[name], $.function);
949
+ }
950
+
951
+ function addressedForms($, definitions, kind) {
952
+ const forms = [];
953
+ for (const maximum of [0, 1, 2]) {
954
+ const names = functionsByMaximum(definitions, maximum);
955
+ if (names.length === 0) {
956
+ continue;
957
+ }
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 },
992
+ );
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
+ }
1022
+
1023
+ return choice(...forms);
1024
+ }
1025
+
1026
+ 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
+ _chainable_editing_command: ($) =>
1036
+ seq(
1037
+ choice(
1038
+ 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
+ ),
1050
+ ),
1051
+ optional(issueField($, "unexpected_command_text")),
1052
+ ),
1053
+
1054
+ _line_terminated_editing_command: ($) =>
1055
+ seq(
1056
+ choice(
1057
+ $._line_terminated_regular_editing_command_body,
1058
+ $._line_terminated_substitute_editing_command_body,
1059
+ ),
1060
+ optional(issueField($, "unexpected_command_text")),
1061
+ ),
1062
+
1063
+ _recovered_line_terminated_editing_command: ($) =>
1064
+ choice(
1065
+ prec.right(
1066
+ seq(
1067
+ $._line_terminated_regular_editing_command_body,
1068
+ optional(issueField($, "unexpected_command_text")),
1069
+ issueField($, "forbidden_command_separator"),
1070
+ optional($._blanks),
1071
+ ),
1072
+ ),
1073
+ prec.right(
1074
+ seq(
1075
+ $._line_terminated_substitute_editing_command_body,
1076
+ optional(issueField($, "unexpected_command_text")),
1077
+ issueField($, "command_after_write_flag"),
1078
+ optional($._blanks),
1079
+ ),
1080
+ ),
1081
+ ),
1082
+
1083
+ _line_terminated_regular_editing_command_body: ($) =>
1084
+ addressedForms($, lineTerminated, "line_terminated"),
1085
+
1086
+ _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
+ ),
1098
+
1099
+ _recovered_editing_command: ($) =>
1100
+ 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
+ ),
1109
+ seq(
1110
+ optional($._blanks),
1111
+ optional(
1112
+ seq(field("addresses", $.address_clause), optional($._blanks)),
1113
+ ),
1114
+ field("negation", $.negation),
1115
+ field("function", alias($._reserved_unknown_function, $.function)),
1116
+ ),
1117
+ seq(
1118
+ optional($._blanks),
1119
+ field("addresses", alias($._double_address_clause, $.address_clause)),
1120
+ optional($._blanks),
1121
+ field("function", alias($._reserved_unknown_function, $.function)),
1122
+ ),
1123
+ seq(
1124
+ optional($._blanks),
1125
+ choice(
1126
+ seq(
1127
+ field("addresses", $.address_clause),
1128
+ optional($._blanks),
1129
+ optional(field("negation", $.negation)),
1130
+ ),
1131
+ field("negation", $.negation),
1132
+ ),
1133
+ field("function", alias($._missing_function, $.function)),
1134
+ ),
1135
+ ),
1136
+
1137
+ _unknown_function: ($) =>
1138
+ seq(
1139
+ issueField($, "unknown_function"),
1140
+ optional(issueField($, "unexpected_command_text")),
1141
+ ),
1142
+
1143
+ _reserved_unknown_function: ($) =>
1144
+ seq(
1145
+ issueField($, "reserved_unknown_function"),
1146
+ optional(issueField($, "unexpected_command_text")),
1147
+ ),
1148
+
1149
+ _missing_function: ($) => missingAtEndOrBoundary($, "missing_function"),
1150
+
1151
+ _chainable_substitute_function: ($) =>
1152
+ alias($._substitute_function_without_write, $.substitute_function),
1153
+
1154
+ _line_terminated_substitute_function: ($) =>
1155
+ alias($._substitute_function_with_write, $.substitute_function),
1156
+
1157
+ negation: ($) =>
1158
+ seq(
1159
+ field("operator", alias("!", $.negation_operator)),
1160
+ repeat(
1161
+ seq(
1162
+ optional(issueField($, "blanks_after_negation")),
1163
+ field("operator", alias("!", $.negation_operator)),
1164
+ issueField($, "duplicate_negation"),
1165
+ ),
1166
+ ),
1167
+ optional(issueField($, "blanks_after_negation")),
1168
+ ),
1169
+ };
1170
+
1171
+ for (const [kind, definitions] of [
1172
+ ["chainable", chainable],
1173
+ ["line_terminated", lineTerminated],
1174
+ ]) {
1175
+ for (const maximum of [0, 1, 2]) {
1176
+ const names = functionsByMaximum(definitions, maximum);
1177
+ if (names.length > 0) {
1178
+ rules[wrapperName(kind, maximum)] = ($) => choiceForRules($, names);
1179
+ }
1180
+ }
1181
+ }
1182
+
1183
+ return rules;
1184
+ }
1185
+
1186
+ function sedRules(mode) {
1187
+ return {
1188
+ ...commandListRules(),
1189
+ ...delimiterRules(),
1190
+ ...addressRules(mode),
1191
+ ...operandRules(mode),
1192
+ ...functionRules(),
1193
+ ...editingCommandRules(),
1194
+ };
1195
+ }
1196
+
1197
+ function missingMarkerNames(mode) {
1198
+ return [
1199
+ "omitted_address",
1200
+ "omitted_first_address",
1201
+ "empty_subexpression",
1202
+ "missing_subexpression",
1203
+ ...(mode === "ere" ? ["empty_alternative"] : []),
1204
+ "excess_addresses",
1205
+ "additional_address",
1206
+ "missing_function",
1207
+ "missing_label",
1208
+ "missing_rfile",
1209
+ "missing_wfile",
1210
+ "omitted_file_separator",
1211
+ "missing_text_introducer",
1212
+ "missing_text",
1213
+ "missing_command_separator",
1214
+ "blanks_around_address_separator",
1215
+ "missing_address_separator",
1216
+ "duplicate_negation",
1217
+ "missing_closing_brace",
1218
+ "missing_opening_delimiter",
1219
+ "missing_separator_before_unmatched_brace",
1220
+ "missing_separator_after_unmatched_brace",
1221
+ "nonconforming_missing_function",
1222
+ "nonconforming_missing_label",
1223
+ "nonconforming_missing_rfile",
1224
+ "nonconforming_missing_wfile",
1225
+ "nonconforming_missing_text_introducer",
1226
+ "nonconforming_missing_opening_delimiter",
1227
+ "missing_subexpression_placeholder",
1228
+ "incomplete_bracket_list",
1229
+ "incomplete_bracket_expression",
1230
+ ...(mode === "ere" ? ["incomplete_alternative"] : []),
1231
+ "incomplete_command_separator",
1232
+ ];
1233
+ }
1234
+
1235
+ function issueDefinitions(mode) {
1236
+ function missing(reason) {
1237
+ return ($) => $[`_${reason}_marker`];
1238
+ }
1239
+
1240
+ const boundaryMissingReasons = [
1241
+ "missing_function",
1242
+ "missing_label",
1243
+ "missing_rfile",
1244
+ "missing_wfile",
1245
+ "missing_text_introducer",
1246
+ "missing_opening_delimiter",
1247
+ ];
1248
+
1249
+ return [
1250
+ {
1251
+ reason: "malformed_bracket_term",
1252
+ outcome: "undefined_syntax",
1253
+ rule: ($) => $._regex_malformed_bracket_term,
1254
+ },
1255
+ {
1256
+ reason: "character_class_range_start",
1257
+ outcome: "undefined_syntax",
1258
+ rule: ($) => $._nonportable_range_start_marker,
1259
+ },
1260
+ {
1261
+ reason: "character_class_range_end",
1262
+ outcome: "undefined_syntax",
1263
+ rule: ($) => $._nonportable_range_end_marker,
1264
+ },
1265
+ {
1266
+ reason: "shared_range_endpoint",
1267
+ outcome: "undefined_syntax",
1268
+ rule: ($) => $._regex_shared_range_endpoint,
1269
+ },
1270
+ {
1271
+ reason: "omitted_address",
1272
+ outcome: "undefined_syntax",
1273
+ rule: missing("omitted_address"),
1274
+ },
1275
+ {
1276
+ id: "omitted_first_address",
1277
+ reason: "omitted_address",
1278
+ outcome: "undefined_syntax",
1279
+ rule: missing("omitted_first_address"),
1280
+ },
1281
+ {
1282
+ reason: "ordinary_character_escape",
1283
+ outcome: "undefined_syntax",
1284
+ rule: ($) => $._regex_nonportable_escape,
1285
+ },
1286
+ {
1287
+ reason: "empty_subexpression",
1288
+ outcome: "undefined_syntax",
1289
+ rule: missing("empty_subexpression"),
1290
+ },
1291
+ {
1292
+ reason: "unclosed_subexpression",
1293
+ outcome: "undefined_syntax",
1294
+ rule: ($) => $._regex_unclosed_group,
1295
+ },
1296
+ ...(mode === "bre"
1297
+ ? [
1298
+ {
1299
+ reason: "unmatched_subexpression_close",
1300
+ outcome: "undefined_syntax",
1301
+ rule: ($) => $._regex_unmatched_group_close,
1302
+ },
1303
+ {
1304
+ reason: "unmatched_interval_close",
1305
+ outcome: "undefined_syntax",
1306
+ rule: ($) => $._unmatched_interval_close_marker,
1307
+ },
1308
+ ]
1309
+ : []),
1310
+ {
1311
+ reason: "malformed_interval",
1312
+ outcome: "undefined_syntax",
1313
+ rule: ($) => $._regex_invalid_interval,
1314
+ },
1315
+ {
1316
+ reason: "leading_duplication_symbol",
1317
+ outcome: "undefined_syntax",
1318
+ rule: ($) => $._regex_leading_duplication_marker,
1319
+ },
1320
+ {
1321
+ reason: "adjacent_duplication_symbol",
1322
+ outcome: "undefined_syntax",
1323
+ rule: ($) => $._regex_adjacent_duplication_marker,
1324
+ },
1325
+ ...(mode === "ere"
1326
+ ? [
1327
+ {
1328
+ reason: "empty_alternative",
1329
+ outcome: "undefined_syntax",
1330
+ rule: missing("empty_alternative"),
1331
+ },
1332
+ ]
1333
+ : []),
1334
+ {
1335
+ reason: "missing_bracket_list",
1336
+ outcome: "undefined_syntax",
1337
+ rule: ($) => $._missing_bracket_list_marker,
1338
+ },
1339
+ {
1340
+ reason: "unclosed_bracket_expression",
1341
+ outcome: "undefined_syntax",
1342
+ rule: ($) => $._unclosed_bracket_expression_marker,
1343
+ },
1344
+ {
1345
+ reason: "undefined_translation_escape",
1346
+ outcome: "undefined_syntax",
1347
+ rule: ($) => $._translate_nonportable_escape,
1348
+ },
1349
+ {
1350
+ reason: "command_after_write_flag",
1351
+ outcome: "undefined_syntax",
1352
+ rule: () => token.immediate(";"),
1353
+ },
1354
+ {
1355
+ reason: "equivalence_class_range_start",
1356
+ outcome: "unspecified_syntax",
1357
+ rule: ($) => $._nonportable_range_start_marker,
1358
+ },
1359
+ {
1360
+ reason: "equivalence_class_range_end",
1361
+ outcome: "unspecified_syntax",
1362
+ rule: ($) => $._nonportable_range_end_marker,
1363
+ },
1364
+ {
1365
+ reason: "ambiguous_bracket_expression",
1366
+ outcome: "unspecified_syntax",
1367
+ rule: ($) => $._ambiguous_bracket_expression_marker,
1368
+ },
1369
+ {
1370
+ reason: "blanks_after_negation",
1371
+ outcome: "unspecified_syntax",
1372
+ rule: () => token.immediate(/[[:blank:]]+/),
1373
+ },
1374
+ {
1375
+ reason: "unspecified_replacement_escape",
1376
+ outcome: "unspecified_syntax",
1377
+ rule: ($) => $._replacement_nonportable_escape,
1378
+ },
1379
+ {
1380
+ reason: "special_delimiter_escape",
1381
+ outcome: "unspecified_syntax",
1382
+ rule: ($) => $._regex_special_escaped_delimiter,
1383
+ },
1384
+ {
1385
+ reason: "replacement_ampersand_delimiter_escape",
1386
+ outcome: "unspecified_syntax",
1387
+ rule: ($) => $._replacement_ampersand_escaped_delimiter,
1388
+ },
1389
+ {
1390
+ reason: "unspecified_text_escape",
1391
+ outcome: "unspecified_syntax",
1392
+ rule: ($) => $._text_unspecified_escape,
1393
+ },
1394
+ ...(mode === "bre"
1395
+ ? [
1396
+ {
1397
+ reason: "bre_vertical_line_escape",
1398
+ outcome: "implementation_defined_syntax",
1399
+ rule: ($) => $._bre_vertical_line_escape_marker,
1400
+ },
1401
+ {
1402
+ reason: "bre_question_mark_escape",
1403
+ outcome: "implementation_defined_syntax",
1404
+ rule: ($) => $._bre_question_mark_escape_marker,
1405
+ },
1406
+ {
1407
+ reason: "bre_plus_escape",
1408
+ outcome: "implementation_defined_syntax",
1409
+ rule: ($) => $._bre_plus_escape_marker,
1410
+ },
1411
+ {
1412
+ reason: "bre_subexpression_left_anchor",
1413
+ outcome: "implementation_option_syntax",
1414
+ rule: ($) => $._regex_bre_subexpression_caret,
1415
+ },
1416
+ {
1417
+ reason: "bre_subexpression_right_anchor",
1418
+ outcome: "implementation_option_syntax",
1419
+ rule: ($) => $._regex_bre_subexpression_dollar,
1420
+ },
1421
+ ]
1422
+ : []),
1423
+ {
1424
+ reason: "omitted_file_separator",
1425
+ outcome: "implementation_option_syntax",
1426
+ rule: missing("omitted_file_separator"),
1427
+ },
1428
+ {
1429
+ reason: "excess_addresses",
1430
+ outcome: "nonconforming_syntax",
1431
+ rule: missing("excess_addresses"),
1432
+ },
1433
+ {
1434
+ reason: "additional_address",
1435
+ outcome: "nonconforming_syntax",
1436
+ rule: missing("additional_address"),
1437
+ },
1438
+ {
1439
+ reason: "unknown_function",
1440
+ outcome: "nonconforming_syntax",
1441
+ rule: () => token(prec(-2, /[^[:blank:][:digit:]$\/\\!;{}#\n]/)),
1442
+ },
1443
+ {
1444
+ id: "reserved_unknown_function",
1445
+ reason: "unknown_function",
1446
+ outcome: "nonconforming_syntax",
1447
+ rule: ($) => $._reserved_unknown_function_token,
1448
+ },
1449
+ {
1450
+ reason: "unexpected_command_text",
1451
+ outcome: "nonconforming_syntax",
1452
+ rule: () =>
1453
+ choice(
1454
+ token.immediate(prec(-10, /[[:blank:]]+/)),
1455
+ token.immediate(prec(-10, /[[:blank:]]*[^[:blank:];}\n][^;}\n]*/)),
1456
+ ),
1457
+ },
1458
+ {
1459
+ reason: "forbidden_command_separator",
1460
+ outcome: "nonconforming_syntax",
1461
+ rule: () => token.immediate(";"),
1462
+ },
1463
+ {
1464
+ reason: "blanks_around_address_separator",
1465
+ outcome: "nonconforming_syntax",
1466
+ rule: missing("blanks_around_address_separator"),
1467
+ },
1468
+ {
1469
+ reason: "missing_address_separator",
1470
+ outcome: "nonconforming_syntax",
1471
+ rule: missing("missing_address_separator"),
1472
+ },
1473
+ {
1474
+ reason: "duplicate_negation",
1475
+ outcome: "nonconforming_syntax",
1476
+ rule: missing("duplicate_negation"),
1477
+ },
1478
+ {
1479
+ reason: "unmatched_closing_brace",
1480
+ outcome: "nonconforming_syntax",
1481
+ rule: ($) => $._right_brace,
1482
+ },
1483
+ {
1484
+ reason: "missing_command_separator",
1485
+ outcome: "nonconforming_syntax",
1486
+ rule: missing("missing_command_separator"),
1487
+ },
1488
+ {
1489
+ id: "missing_separator_before_unmatched_brace",
1490
+ reason: "missing_command_separator",
1491
+ outcome: "nonconforming_syntax",
1492
+ rule: missing("missing_separator_before_unmatched_brace"),
1493
+ },
1494
+ {
1495
+ id: "missing_separator_after_unmatched_brace",
1496
+ reason: "missing_command_separator",
1497
+ outcome: "nonconforming_syntax",
1498
+ rule: missing("missing_separator_after_unmatched_brace"),
1499
+ },
1500
+ {
1501
+ reason: "invalid_substitution_flag",
1502
+ outcome: "nonconforming_syntax",
1503
+ rule: ($) => $._invalid_substitution_flag,
1504
+ },
1505
+ {
1506
+ reason: "invalid_delimiter",
1507
+ outcome: "nonconforming_syntax",
1508
+ rule: () => "\\",
1509
+ },
1510
+ {
1511
+ reason: "unterminated_regular_expression",
1512
+ outcome: "nonconforming_syntax",
1513
+ rule: ($) =>
1514
+ choice(
1515
+ $._regex_line_unterminated_address,
1516
+ $._regex_line_unterminated_substitute,
1517
+ ),
1518
+ },
1519
+ {
1520
+ reason: "forbidden_regular_expression_newline",
1521
+ outcome: "nonconforming_syntax",
1522
+ rule: ($) => $._regex_forbidden_newline_escape,
1523
+ },
1524
+ {
1525
+ reason: "unterminated_replacement",
1526
+ outcome: "nonconforming_syntax",
1527
+ rule: ($) => $._replacement_line_unterminated,
1528
+ },
1529
+ {
1530
+ reason: "unterminated_translation",
1531
+ outcome: "nonconforming_syntax",
1532
+ rule: ($) =>
1533
+ choice(
1534
+ $._translate_line_unterminated_source,
1535
+ $._translate_line_unterminated_destination,
1536
+ ),
1537
+ },
1538
+ ...boundaryMissingReasons.map((reason) => ({
1539
+ id: `nonconforming_${reason}`,
1540
+ reason,
1541
+ outcome: "nonconforming_syntax",
1542
+ rule: missing(`nonconforming_${reason}`),
1543
+ })),
1544
+ {
1545
+ reason: "missing_function",
1546
+ outcome: "incomplete_syntax",
1547
+ rule: missing("missing_function"),
1548
+ },
1549
+ {
1550
+ reason: "missing_label",
1551
+ outcome: "incomplete_syntax",
1552
+ rule: missing("missing_label"),
1553
+ },
1554
+ {
1555
+ reason: "missing_rfile",
1556
+ outcome: "incomplete_syntax",
1557
+ rule: missing("missing_rfile"),
1558
+ },
1559
+ {
1560
+ reason: "missing_wfile",
1561
+ outcome: "incomplete_syntax",
1562
+ rule: missing("missing_wfile"),
1563
+ },
1564
+ {
1565
+ reason: "missing_text_introducer",
1566
+ outcome: "incomplete_syntax",
1567
+ rule: missing("missing_text_introducer"),
1568
+ },
1569
+ {
1570
+ reason: "missing_text",
1571
+ outcome: "incomplete_syntax",
1572
+ rule: missing("missing_text"),
1573
+ },
1574
+ {
1575
+ reason: "missing_closing_brace",
1576
+ outcome: "incomplete_syntax",
1577
+ rule: missing("missing_closing_brace"),
1578
+ },
1579
+ {
1580
+ reason: "missing_opening_delimiter",
1581
+ outcome: "incomplete_syntax",
1582
+ rule: missing("missing_opening_delimiter"),
1583
+ },
1584
+ {
1585
+ reason: "missing_subexpression",
1586
+ outcome: "incomplete_syntax",
1587
+ rule: missing("missing_subexpression"),
1588
+ },
1589
+ {
1590
+ id: "incomplete_unclosed_subexpression",
1591
+ reason: "unclosed_subexpression",
1592
+ outcome: "incomplete_syntax",
1593
+ rule: ($) => $._regex_incomplete_group,
1594
+ },
1595
+ {
1596
+ reason: "incomplete_bracket_term",
1597
+ outcome: "incomplete_syntax",
1598
+ rule: ($) => $._regex_incomplete_bracket_term,
1599
+ },
1600
+ {
1601
+ id: "incomplete_missing_bracket_list",
1602
+ reason: "missing_bracket_list",
1603
+ outcome: "incomplete_syntax",
1604
+ rule: ($) => $._incomplete_bracket_list_marker,
1605
+ },
1606
+ {
1607
+ id: "incomplete_unclosed_bracket_expression",
1608
+ reason: "unclosed_bracket_expression",
1609
+ outcome: "incomplete_syntax",
1610
+ rule: ($) => $._incomplete_bracket_expression_marker,
1611
+ },
1612
+ ...(mode === "ere"
1613
+ ? [
1614
+ {
1615
+ reason: "incomplete_alternative",
1616
+ outcome: "incomplete_syntax",
1617
+ rule: ($) => $._incomplete_alternative_marker,
1618
+ },
1619
+ ]
1620
+ : []),
1621
+ {
1622
+ reason: "incomplete_interval",
1623
+ outcome: "incomplete_syntax",
1624
+ rule: ($) => $._regex_incomplete_interval,
1625
+ },
1626
+ {
1627
+ id: "incomplete_missing_command_separator",
1628
+ reason: "missing_command_separator",
1629
+ outcome: "incomplete_syntax",
1630
+ rule: ($) => $._incomplete_command_separator_marker,
1631
+ },
1632
+ {
1633
+ reason: "incomplete_regular_expression",
1634
+ outcome: "incomplete_syntax",
1635
+ rule: ($) =>
1636
+ choice($._regex_unterminated_address, $._regex_unterminated_substitute),
1637
+ },
1638
+ {
1639
+ reason: "incomplete_regular_expression_escape",
1640
+ outcome: "incomplete_syntax",
1641
+ rule: ($) => $._regex_incomplete_escape,
1642
+ },
1643
+ {
1644
+ reason: "incomplete_replacement",
1645
+ outcome: "incomplete_syntax",
1646
+ rule: ($) => $._replacement_unterminated,
1647
+ },
1648
+ {
1649
+ reason: "incomplete_replacement_escape",
1650
+ outcome: "incomplete_syntax",
1651
+ rule: ($) => $._replacement_incomplete_escape,
1652
+ },
1653
+ {
1654
+ reason: "incomplete_translation",
1655
+ outcome: "incomplete_syntax",
1656
+ rule: ($) =>
1657
+ choice(
1658
+ $._translate_unterminated_source,
1659
+ $._translate_unterminated_destination,
1660
+ ),
1661
+ },
1662
+ {
1663
+ reason: "incomplete_translation_escape",
1664
+ outcome: "incomplete_syntax",
1665
+ rule: ($) => $._translate_incomplete_escape,
1666
+ },
1667
+ ];
1668
+ }
1669
+
1670
+ function externalTokens($, mode) {
1671
+ return [
1672
+ $._regex_address_start,
1673
+ $._escaped_regex_address_start,
1674
+ $._regex_address_end,
1675
+ $._substitute_start,
1676
+ $._substitute_middle,
1677
+ $._substitute_end,
1678
+ $._translate_start,
1679
+ $._translate_middle,
1680
+ $._translate_end,
1681
+ $._regex_literal,
1682
+ $._regex_beginning_anchor,
1683
+ $._regex_end_anchor,
1684
+ $._regex_period,
1685
+ $._regex_quoted_escape,
1686
+ $._regex_newline_escape,
1687
+ $._regex_escaped_delimiter,
1688
+ $._regex_special_escaped_delimiter,
1689
+ $._regex_group_open,
1690
+ $._regex_group_close,
1691
+ $._regex_unclosed_group,
1692
+ ...(mode === "bre"
1693
+ ? [
1694
+ $._regex_unmatched_group_close,
1695
+ $._regex_bre_vertical_line_escape,
1696
+ $._regex_bre_question_mark_escape,
1697
+ $._regex_bre_plus_escape,
1698
+ $._regex_bre_subexpression_caret,
1699
+ $._regex_bre_subexpression_dollar,
1700
+ $._bre_vertical_line_escape_marker,
1701
+ $._bre_question_mark_escape_marker,
1702
+ $._bre_plus_escape_marker,
1703
+ $._regex_unmatched_interval_close,
1704
+ $._unmatched_interval_close_marker,
1705
+ ]
1706
+ : []),
1707
+ ...(mode === "ere" ? [$._regex_alternation_operator] : []),
1708
+ $._regex_leading_duplication_marker,
1709
+ $._regex_adjacent_duplication_marker,
1710
+ $._regex_zero_or_more,
1711
+ ...(mode === "ere"
1712
+ ? [
1713
+ $._regex_one_or_more,
1714
+ $._regex_zero_or_one,
1715
+ $._regex_repetition_modifier,
1716
+ ]
1717
+ : []),
1718
+ $._regex_interval_open,
1719
+ $._regex_dup_count,
1720
+ $._regex_interval_separator,
1721
+ $._regex_interval_close,
1722
+ ...(mode === "bre" ? [$._regex_backreference] : []),
1723
+ $._regex_invalid_interval,
1724
+ $._regex_nonportable_escape,
1725
+ $._regex_incomplete_escape,
1726
+ $._regex_bracket_open,
1727
+ $._regex_bracket_close,
1728
+ $._regex_bracket_literal,
1729
+ $._regex_bracket_negation,
1730
+ $._regex_bracket_hyphen,
1731
+ $._regex_bracket_range_end_hyphen,
1732
+ $._regex_bracket_trailing_hyphen,
1733
+ $._regex_open_colon,
1734
+ $._regex_class_name,
1735
+ $._regex_colon_close,
1736
+ $._regex_open_dot,
1737
+ $._regex_coll_elem_single,
1738
+ $._regex_coll_elem_multi,
1739
+ $._regex_meta_char,
1740
+ $._regex_dot_close,
1741
+ $._regex_open_equal,
1742
+ $._regex_equal_close,
1743
+ $._regex_malformed_bracket_term,
1744
+ $._regex_shared_range_endpoint,
1745
+ $._ambiguous_bracket_expression_marker,
1746
+ $._missing_bracket_list_marker,
1747
+ $._unclosed_bracket_expression_marker,
1748
+ $._nonportable_range_start_marker,
1749
+ $._nonportable_range_end_marker,
1750
+ $._regex_unterminated_address,
1751
+ $._regex_unterminated_substitute,
1752
+ $._replacement_literal,
1753
+ $._replacement_match_reference,
1754
+ $._replacement_backreference,
1755
+ $._replacement_escaped_delimiter,
1756
+ $._replacement_ampersand_escaped_delimiter,
1757
+ $._replacement_escape_sequence,
1758
+ $._replacement_nonportable_escape,
1759
+ $._replacement_escaped_newline,
1760
+ $._replacement_incomplete_escape,
1761
+ $._replacement_unterminated,
1762
+ $._translate_literal,
1763
+ $._translate_escaped_delimiter,
1764
+ $._translate_escape,
1765
+ $._translate_nonportable_escape,
1766
+ $._translate_incomplete_escape,
1767
+ $._translate_unterminated_source,
1768
+ $._translate_unterminated_destination,
1769
+ $._invalid_substitution_flag,
1770
+ $._text_command_start,
1771
+ $._text_literal,
1772
+ $._text_backslash_escape,
1773
+ $._text_escaped_newline,
1774
+ $._text_unspecified_escape,
1775
+ $._text_line_end,
1776
+ $._text_eof,
1777
+ $._default_output_suppression,
1778
+ $._comment_text,
1779
+ $._file_argument,
1780
+ $._substitution_wfile_argument,
1781
+ $._line_word,
1782
+ $._right_brace,
1783
+ $._reserved_unknown_function_token,
1784
+ $._regex_incomplete_group,
1785
+ $._regex_incomplete_bracket_term,
1786
+ $._regex_incomplete_interval,
1787
+ $._regex_forbidden_newline_escape,
1788
+ $._regex_line_unterminated_address,
1789
+ $._regex_line_unterminated_substitute,
1790
+ $._replacement_line_unterminated,
1791
+ $._translate_line_unterminated_source,
1792
+ $._translate_line_unterminated_destination,
1793
+ ...missingMarkerNames(mode).map((name) => $[`_${name}_marker`]),
1794
+ $._error_sentinel,
1795
+ ];
1796
+ }
1797
+
1798
+ function defineGrammar(name, mode) {
1799
+ if (mode !== "bre" && mode !== "ere") {
1800
+ throw new Error(`Unsupported regular-expression mode: ${mode}`);
1801
+ }
1802
+
1803
+ return grammar({
1804
+ name,
1805
+
1806
+ externals: ($) => externalTokens($, mode),
1807
+
1808
+ extras: () => [],
1809
+
1810
+ conflicts: ($) => [
1811
+ [
1812
+ $.address_clause,
1813
+ $._chainable_editing_command,
1814
+ $._line_terminated_regular_editing_command_body,
1815
+ ],
1816
+ [
1817
+ $.address_clause,
1818
+ $._chainable_editing_command,
1819
+ $._line_terminated_regular_editing_command_body,
1820
+ $._recovered_editing_command,
1821
+ ],
1822
+ ],
1823
+
1824
+ rules: {
1825
+ ...sedRules(mode),
1826
+ ...regularExpressionRules(mode),
1827
+ ...defineIssueRules(issueDefinitions(mode)),
1828
+ },
1829
+ });
1830
+ }
1831
+
1832
+ module.exports = defineGrammar;