tree-sitter-sed 0.7.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/scanner.h CHANGED
@@ -110,6 +110,7 @@ enum TokenType {
110
110
  INVALID_SUBSTITUTION_FLAG,
111
111
  FLAG_AFTER_WRITE_MARKER,
112
112
  TEXT_COMMAND_START,
113
+ TEXT_INCOMPLETE_INTRODUCER,
113
114
  TEXT_LITERAL,
114
115
  TEXT_BACKSLASH_ESCAPE,
115
116
  TEXT_ESCAPED_NEWLINE,
@@ -121,7 +122,9 @@ enum TokenType {
121
122
  FILE_ARGUMENT,
122
123
  SUBSTITUTION_WFILE_ARGUMENT,
123
124
  LINE_WORD,
125
+ ARGUMENT_SEPARATOR,
124
126
  RIGHT_BRACE,
127
+ EMPTY_COMMAND_MARKER,
125
128
  RESERVED_UNKNOWN_FUNCTION_TOKEN,
126
129
  REGEX_INCOMPLETE_GROUP,
127
130
  REGEX_INCOMPLETE_BRACKET_TERM,
@@ -193,14 +196,6 @@ enum TextState {
193
196
  TEXT_AFTER_ESCAPED_NEWLINE,
194
197
  };
195
198
 
196
- enum RegexIntervalState {
197
- REGEX_INTERVAL_NONE,
198
- REGEX_INTERVAL_EXPECT_MINIMUM,
199
- REGEX_INTERVAL_AFTER_MINIMUM,
200
- REGEX_INTERVAL_EXPECT_MAXIMUM,
201
- REGEX_INTERVAL_AFTER_MAXIMUM,
202
- };
203
-
204
199
  enum RegexDuplicationState {
205
200
  REGEX_DUPLICATION_NONE,
206
201
  REGEX_AFTER_DUPLICATION_SYMBOL,
@@ -231,7 +226,7 @@ typedef struct {
231
226
  bool regex_after_alternation;
232
227
  bool regex_after_anchor;
233
228
  enum TextState text_state;
234
- enum RegexIntervalState regex_interval_state;
229
+ bool regex_in_interval;
235
230
  enum RegexBracketTermState regex_bracket_term_state;
236
231
  enum RegexBracketPendingElement regex_bracket_pending_element;
237
232
  enum RegexBracketPendingElement regex_bracket_first_element;
@@ -242,7 +237,7 @@ typedef struct {
242
237
  } ScannerState;
243
238
 
244
239
  enum {
245
- SCANNER_SERIALIZATION_VERSION = 14,
240
+ SCANNER_SERIALIZATION_VERSION = 15,
246
241
  SCANNER_SERIALIZED_STATE_SIZE = 17,
247
242
  };
248
243
 
@@ -286,7 +281,7 @@ static void reset_mode_tracking(ScannerState *state) {
286
281
  state->regex_after_alternation = false;
287
282
  state->regex_after_anchor = false;
288
283
  state->text_state = TEXT_EMPTY;
289
- state->regex_interval_state = REGEX_INTERVAL_NONE;
284
+ state->regex_in_interval = false;
290
285
  reset_bracket_tracking(state);
291
286
  state->regex_group_depth = 0;
292
287
  }
@@ -336,7 +331,7 @@ static unsigned sed_scanner_serialize(void *payload, char *buffer) {
336
331
  buffer[6] = (char)((delimiter >> 16) & UINT32_C(0xff));
337
332
  buffer[7] = (char)((delimiter >> 24) & UINT32_C(0xff));
338
333
  serialize_uint16(buffer, 8, state->regex_group_depth);
339
- buffer[10] = (char)state->regex_interval_state;
334
+ buffer[10] = state->regex_in_interval ? 1 : 0;
340
335
  buffer[11] = (char)state->regex_bracket_term_state;
341
336
  buffer[12] = (char)state->regex_bracket_pending_element;
342
337
  buffer[13] = (char)state->regex_bracket_first_element;
@@ -371,15 +366,18 @@ static bool regex_position_is_reset(const ScannerState *state) {
371
366
  !state->regex_after_anchor;
372
367
  }
373
368
 
374
- static bool deserialized_delimiter_is_valid(uint32_t delimiter) {
375
- return delimiter !=
369
+ // Reject NUL and decode errors before emission: they cannot survive delimiter
370
+ // serialization.
371
+ static bool delimiter_character_is_valid(int32_t character) {
372
+ const uint32_t code_point = (uint32_t)character;
373
+ return code_point !=
376
374
  0 &&
377
- delimiter <=
375
+ code_point <=
378
376
  UINT32_C(0x10ffff) &&
379
- !(delimiter >= UINT32_C(0xd800) && delimiter <= UINT32_C(0xdfff)) &&
380
- delimiter !=
381
- (uint32_t)'\\' &&
382
- delimiter != (uint32_t)'\n';
377
+ !(code_point >= UINT32_C(0xd800) && code_point <= UINT32_C(0xdfff)) &&
378
+ character !=
379
+ '\\' &&
380
+ character != '\n';
383
381
  }
384
382
 
385
383
  static bool deserialized_state_is_valid(const ScannerState *candidate) {
@@ -395,12 +393,11 @@ static bool deserialized_state_is_valid(const ScannerState *candidate) {
395
393
  regex_position_is_reset(candidate) &&
396
394
  candidate->regex_group_depth ==
397
395
  0 &&
398
- candidate->regex_interval_state ==
399
- REGEX_INTERVAL_NONE &&
396
+ !candidate->regex_in_interval &&
400
397
  bracket_tracking_is_reset(candidate);
401
398
  }
402
399
 
403
- if (!deserialized_delimiter_is_valid((uint32_t)candidate->delimiter)) {
400
+ if (!delimiter_character_is_valid(candidate->delimiter)) {
404
401
  return false;
405
402
  }
406
403
 
@@ -410,8 +407,7 @@ static bool deserialized_state_is_valid(const ScannerState *candidate) {
410
407
  regex_position_is_reset(candidate) &&
411
408
  candidate->text_state ==
412
409
  TEXT_EMPTY &&
413
- candidate->regex_interval_state ==
414
- REGEX_INTERVAL_NONE &&
410
+ !candidate->regex_in_interval &&
415
411
  bracket_tracking_is_reset(candidate) &&
416
412
  candidate->regex_group_depth == 0;
417
413
  }
@@ -471,7 +467,7 @@ sed_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
471
467
  .regex_after_anchor = (mode_flags & SERIALIZED_AFTER_ANCHOR_FLAG) != 0,
472
468
  .text_state =
473
469
  (enum TextState)((mode_flags >> SERIALIZED_TEXT_STATE_SHIFT) & 3U),
474
- .regex_interval_state = (enum RegexIntervalState)(unsigned char)buffer[10],
470
+ .regex_in_interval = buffer[10] != 0,
475
471
  .regex_bracket_term_state =
476
472
  (enum RegexBracketTermState)(unsigned char)buffer[11],
477
473
  .regex_bracket_pending_element =
@@ -506,7 +502,7 @@ sed_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
506
502
  }
507
503
  #endif
508
504
 
509
- if (candidate.regex_interval_state > REGEX_INTERVAL_AFTER_MAXIMUM) {
505
+ if ((unsigned char)buffer[10] > 1U) {
510
506
  return;
511
507
  }
512
508
  if (candidate.regex_bracket_term_state > REGEX_BRACKET_TERM_EQUAL) {
@@ -558,6 +554,11 @@ static void advance(TSLexer *lexer) {
558
554
  lexer->advance(lexer, false);
559
555
  }
560
556
 
557
+ static void consume(TSLexer *lexer) {
558
+ advance(lexer);
559
+ lexer->mark_end(lexer);
560
+ }
561
+
561
562
  static bool is_blank(int32_t character) {
562
563
  return character == ' ' || character == '\t';
563
564
  }
@@ -572,12 +573,6 @@ static void advance_past_blanks(TSLexer *lexer) {
572
573
  }
573
574
  }
574
575
 
575
- static void advance_past_escaped_character(TSLexer *lexer) {
576
- if (!lexer->eof(lexer) && lexer->lookahead != '\n') {
577
- advance(lexer);
578
- }
579
- }
580
-
581
576
  static void skip_blanks(TSLexer *lexer) {
582
577
  while (is_blank(lexer->lookahead)) {
583
578
  lexer->advance(lexer, true);
@@ -585,12 +580,7 @@ static void skip_blanks(TSLexer *lexer) {
585
580
  }
586
581
 
587
582
  static bool invalid_delimiter(TSLexer *lexer) {
588
- return lexer->eof(lexer) ||
589
- lexer->lookahead ==
590
- 0 ||
591
- lexer->lookahead ==
592
- '\\' ||
593
- lexer->lookahead == '\n';
583
+ return lexer->eof(lexer) || !delimiter_character_is_valid(lexer->lookahead);
594
584
  }
595
585
 
596
586
  static bool delimiter_is_missing(TSLexer *lexer) {
@@ -609,32 +599,41 @@ static bool scan_simple_delimiter(
609
599
  state->delimiter = lexer->lookahead;
610
600
  state->mode = next_mode;
611
601
  reset_mode_tracking(state);
612
- state->regex_at_branch_start =
613
- next_mode == MODE_REGEX_ADDRESS || next_mode == MODE_SUBSTITUTE_PATTERN;
614
- advance(lexer);
615
- lexer->mark_end(lexer);
602
+ state->regex_at_branch_start = is_regex_mode(next_mode);
603
+ consume(lexer);
616
604
  return true;
617
605
  }
618
606
 
619
- static bool scan_mode_delimiter(
620
- TSLexer *lexer,
621
- ScannerState *state,
622
- enum ScannerMode next_mode
623
- ) {
624
- if (lexer->lookahead != state->delimiter) {
607
+ static bool
608
+ emit_symbol(const bool *valid_symbols, TSSymbol candidate, TSSymbol *symbol) {
609
+ if (!valid_symbols[candidate]) {
625
610
  return false;
626
611
  }
627
612
 
628
- state->mode = next_mode;
629
- reset_mode_tracking(state);
630
- if (next_mode == MODE_NONE) {
631
- state->delimiter = 0;
632
- }
633
- advance(lexer);
634
- lexer->mark_end(lexer);
613
+ *symbol = candidate;
635
614
  return true;
636
615
  }
637
616
 
617
+ static bool consume_as(
618
+ TSLexer *lexer,
619
+ const bool *valid_symbols,
620
+ TSSymbol candidate,
621
+ TSSymbol *symbol
622
+ ) {
623
+ consume(lexer);
624
+ return emit_symbol(valid_symbols, candidate, symbol);
625
+ }
626
+
627
+ static bool end_mode(
628
+ ScannerState *state,
629
+ const bool *valid_symbols,
630
+ TSSymbol candidate,
631
+ TSSymbol *symbol
632
+ ) {
633
+ reset_state(state);
634
+ return emit_symbol(valid_symbols, candidate, symbol);
635
+ }
636
+
638
637
  static bool scan_active_mode_delimiter(
639
638
  TSLexer *lexer,
640
639
  ScannerState *state,
@@ -670,26 +669,15 @@ static bool scan_active_mode_delimiter(
670
669
  return false;
671
670
  }
672
671
 
673
- if (
674
- !valid_symbols[candidate] || !scan_mode_delimiter(lexer, state, next_mode)
675
- ) {
676
- return false;
672
+ state->mode = next_mode;
673
+ reset_mode_tracking(state);
674
+ if (next_mode == MODE_NONE) {
675
+ state->delimiter = 0;
677
676
  }
678
-
679
- *symbol = candidate;
680
- return true;
681
- }
682
-
683
- static void consume(TSLexer *lexer) {
684
- advance(lexer);
685
- lexer->mark_end(lexer);
677
+ return consume_as(lexer, valid_symbols, candidate, symbol);
686
678
  }
687
679
 
688
680
  static bool scan_text_command_start(TSLexer *lexer, ScannerState *state) {
689
- if (state->mode != MODE_NONE || lexer->lookahead != '\\') {
690
- return false;
691
- }
692
-
693
681
  lexer->mark_end(lexer);
694
682
  advance(lexer);
695
683
 
@@ -702,47 +690,23 @@ static bool scan_text_command_start(TSLexer *lexer, ScannerState *state) {
702
690
  return true;
703
691
  }
704
692
 
705
- static bool scan_to_physical_line_end(TSLexer *lexer, bool consumed) {
706
- while (!lexer->eof(lexer)) {
707
- if (lexer->lookahead == '\n') {
708
- return consumed;
709
- }
710
-
693
+ static bool scan_line_rest(TSLexer *lexer, bool ends_at_semicolon) {
694
+ bool consumed = false;
695
+ while (
696
+ !lexer->eof(lexer) &&
697
+ lexer->lookahead !=
698
+ '\n' &&
699
+ !(ends_at_semicolon && lexer->lookahead == ';')
700
+ ) {
711
701
  consume(lexer);
712
702
  consumed = true;
713
703
  }
714
-
715
704
  return consumed;
716
705
  }
717
706
 
718
- static bool scan_file_argument(TSLexer *lexer) {
719
- if (
720
- lexer->eof(lexer) || is_blank(lexer->lookahead) || lexer->lookahead == '\n'
721
- ) {
722
- return false;
723
- }
724
-
725
- consume(lexer);
726
- return scan_to_physical_line_end(lexer, true);
727
- }
728
-
729
- static bool scan_substitution_wfile_argument(TSLexer *lexer) {
730
- if (
731
- lexer->eof(lexer) ||
732
- is_blank(lexer->lookahead) ||
733
- lexer->lookahead ==
734
- '\n' ||
735
- lexer->lookahead == ';'
736
- ) {
737
- return false;
738
- }
739
-
740
- do {
741
- consume(lexer);
742
- } while (
743
- !lexer->eof(lexer) && lexer->lookahead != '\n' && lexer->lookahead != ';'
744
- );
745
- return true;
707
+ static bool scan_file_argument(TSLexer *lexer, bool ends_at_semicolon) {
708
+ return !is_blank(lexer->lookahead) &&
709
+ scan_line_rest(lexer, ends_at_semicolon);
746
710
  }
747
711
 
748
712
  enum FlagAfterWriteScan {
@@ -781,55 +745,13 @@ static enum FlagAfterWriteScan scan_flag_after_write_marker(TSLexer *lexer) {
781
745
  : FLAG_AFTER_WRITE_FALLBACK;
782
746
  }
783
747
 
784
- static bool scan_right_brace(TSLexer *lexer, ScannerState *state) {
785
- if (state->mode != MODE_NONE) {
786
- return false;
787
- }
788
-
789
- skip_blanks(lexer);
790
-
791
- if (lexer->lookahead != '}') {
792
- return false;
793
- }
794
-
795
- consume(lexer);
796
- return true;
797
- }
798
-
799
- static bool scan_reserved_unknown_function(TSLexer *lexer) {
800
- const int32_t character = lexer->lookahead;
801
- if (
802
- !is_digit(character) &&
803
- character !=
804
- '$' &&
805
- character !=
806
- '/' &&
807
- character != '\\'
808
- ) {
809
- return false;
810
- }
811
-
812
- consume(lexer);
813
- return true;
814
- }
815
-
816
748
  enum LiteralScanResult {
817
749
  LITERAL_SCAN_NONE,
818
750
  LITERAL_SCAN_TOKEN,
819
751
  LITERAL_SCAN_LINE_END,
820
752
  };
821
753
 
822
- static bool
823
- emit_symbol(const bool *valid_symbols, TSSymbol candidate, TSSymbol *symbol) {
824
- if (!valid_symbols[candidate]) {
825
- return false;
826
- }
827
-
828
- *symbol = candidate;
829
- return true;
830
- }
831
-
832
- static bool emit_missing_marker(
754
+ static bool emit_marker(
833
755
  TSLexer *lexer,
834
756
  const bool *valid_symbols,
835
757
  TSSymbol candidate,
@@ -844,6 +766,15 @@ static bool emit_missing_marker(
844
766
  return true;
845
767
  }
846
768
 
769
+ static bool can_start_address(TSLexer *lexer) {
770
+ return is_digit(lexer->lookahead) ||
771
+ lexer->lookahead ==
772
+ '$' ||
773
+ lexer->lookahead ==
774
+ '/' ||
775
+ lexer->lookahead == '\\';
776
+ }
777
+
847
778
  static bool at_command_boundary(TSLexer *lexer) {
848
779
  return lexer->eof(lexer) ||
849
780
  lexer->lookahead ==
@@ -853,13 +784,23 @@ static bool at_command_boundary(TSLexer *lexer) {
853
784
  lexer->lookahead == '}';
854
785
  }
855
786
 
856
- static bool can_start_address(TSLexer *lexer) {
857
- return is_digit(lexer->lookahead) ||
858
- lexer->lookahead ==
859
- '$' ||
860
- lexer->lookahead ==
861
- '/' ||
862
- lexer->lookahead == '\\';
787
+ static TSSymbol missing_command_separator_marker(
788
+ const TSLexer *lexer,
789
+ const bool *valid_symbols
790
+ ) {
791
+ if (lexer->lookahead == '}') {
792
+ if (valid_symbols[MISSING_SEPARATOR_BEFORE_UNMATCHED_BRACE_MARKER]) {
793
+ return MISSING_SEPARATOR_BEFORE_UNMATCHED_BRACE_MARKER;
794
+ }
795
+ if (valid_symbols[MISSING_COMMAND_SEPARATOR_MARKER]) {
796
+ return MISSING_COMMAND_SEPARATOR_MARKER;
797
+ }
798
+ } else if (
799
+ lexer->eof(lexer) && valid_symbols[INCOMPLETE_COMMAND_SEPARATOR_MARKER]
800
+ ) {
801
+ return INCOMPLETE_COMMAND_SEPARATOR_MARKER;
802
+ }
803
+ return ERROR_SENTINEL;
863
804
  }
864
805
 
865
806
  enum PostBlankRecoveryScan {
@@ -913,22 +854,10 @@ static enum PostBlankRecoveryScan scan_post_blank_recovery(
913
854
  *symbol = MISSING_ADDRESS_SEPARATOR_MARKER;
914
855
  return POST_BLANK_RECOVERY_TOKEN;
915
856
  }
916
- if (lexer->lookahead == '}') {
917
- if (valid_symbols[MISSING_SEPARATOR_BEFORE_UNMATCHED_BRACE_MARKER]) {
918
- *symbol = MISSING_SEPARATOR_BEFORE_UNMATCHED_BRACE_MARKER;
919
- return POST_BLANK_RECOVERY_TOKEN;
920
- }
921
- if (valid_symbols[MISSING_COMMAND_SEPARATOR_MARKER]) {
922
- *symbol = MISSING_COMMAND_SEPARATOR_MARKER;
923
- return POST_BLANK_RECOVERY_TOKEN;
924
- }
925
- return POST_BLANK_RECOVERY_FAILED;
926
- }
927
- if (lexer->eof(lexer) && valid_symbols[INCOMPLETE_COMMAND_SEPARATOR_MARKER]) {
928
- *symbol = INCOMPLETE_COMMAND_SEPARATOR_MARKER;
929
- return POST_BLANK_RECOVERY_TOKEN;
930
- }
931
- return POST_BLANK_RECOVERY_FAILED;
857
+ // The blanks precede the marker as padding of the brace or the source end.
858
+ *symbol = missing_command_separator_marker(lexer, valid_symbols);
859
+ return *symbol == ERROR_SENTINEL ? POST_BLANK_RECOVERY_FAILED
860
+ : POST_BLANK_RECOVERY_TOKEN;
932
861
  }
933
862
 
934
863
  static bool scan_text_token(
@@ -938,48 +867,25 @@ static bool scan_text_token(
938
867
  TSSymbol *symbol
939
868
  ) {
940
869
  if (lexer->eof(lexer)) {
941
- if (state->text_state == TEXT_AFTER_ESCAPED_NEWLINE) {
942
- if (!emit_missing_marker(
943
- lexer,
944
- valid_symbols,
945
- MISSING_TEXT_MARKER,
946
- symbol
947
- )) {
948
- return false;
949
- }
950
- state->text_state = TEXT_HAS_CONTENT;
951
- return true;
952
- }
953
870
  if (
954
- state->text_state ==
955
- TEXT_EMPTY &&
956
- emit_missing_marker(lexer, valid_symbols, MISSING_TEXT_MARKER, symbol)
871
+ state->text_state !=
872
+ TEXT_HAS_CONTENT &&
873
+ emit_marker(lexer, valid_symbols, MISSING_TEXT_MARKER, symbol)
957
874
  ) {
958
875
  state->text_state = TEXT_HAS_CONTENT;
959
876
  return true;
960
877
  }
961
- if (!valid_symbols[TEXT_EOF]) {
962
- return false;
963
- }
964
878
  lexer->mark_end(lexer);
965
- reset_state(state);
966
- *symbol = TEXT_EOF;
967
- return true;
879
+ return end_mode(state, valid_symbols, TEXT_EOF, symbol);
968
880
  }
969
881
 
970
882
  if (lexer->lookahead == '\n') {
971
- if (!valid_symbols[TEXT_LINE_END]) {
972
- return false;
973
- }
974
883
  lexer->mark_end(lexer);
975
- reset_state(state);
976
- *symbol = TEXT_LINE_END;
977
- return true;
884
+ return end_mode(state, valid_symbols, TEXT_LINE_END, symbol);
978
885
  }
979
886
 
980
887
  if (lexer->lookahead == '\\') {
981
- advance(lexer);
982
- lexer->mark_end(lexer);
888
+ consume(lexer);
983
889
 
984
890
  if (lexer->eof(lexer)) {
985
891
  state->text_state = TEXT_HAS_CONTENT;
@@ -1001,17 +907,13 @@ static bool scan_text_token(
1001
907
  return emit_symbol(valid_symbols, TEXT_UNSPECIFIED_ESCAPE, symbol);
1002
908
  }
1003
909
 
1004
- if (!valid_symbols[TEXT_LITERAL]) {
1005
- return false;
1006
- }
1007
910
  do {
1008
911
  consume(lexer);
1009
912
  } while (
1010
913
  !lexer->eof(lexer) && lexer->lookahead != '\\' && lexer->lookahead != '\n'
1011
914
  );
1012
915
  state->text_state = TEXT_HAS_CONTENT;
1013
- *symbol = TEXT_LITERAL;
1014
- return true;
916
+ return emit_symbol(valid_symbols, TEXT_LITERAL, symbol);
1015
917
  }
1016
918
 
1017
919
  static bool regex_is_inside_bracket(const ScannerState *state) {
@@ -1101,16 +1003,6 @@ static void finish_first_bracket_element(ScannerState *state) {
1101
1003
  }
1102
1004
  }
1103
1005
 
1104
- static void
1105
- update_bracket_state_after_literal(ScannerState *state, int32_t character) {
1106
- if (state->regex_state == REGEX_BRACKET_FIRST) {
1107
- state->regex_state =
1108
- character == '^' ? REGEX_BRACKET_AFTER_CARET : REGEX_BRACKET_BODY;
1109
- } else if (state->regex_state == REGEX_BRACKET_AFTER_CARET) {
1110
- state->regex_state = REGEX_BRACKET_BODY;
1111
- }
1112
- }
1113
-
1114
1006
  static bool
1115
1007
  regex_literal_boundary(const ScannerState *state, int32_t character) {
1116
1008
  if (state->regex_state == REGEX_OUTSIDE_BRACKET) {
@@ -1162,33 +1054,57 @@ scan_regex_literal(TSLexer *lexer, ScannerState *state) {
1162
1054
  consume(lexer);
1163
1055
  if (regex_is_inside_bracket(state)) {
1164
1056
  begin_single_bracket_element(state, character);
1057
+ finish_first_bracket_element(state);
1165
1058
  }
1166
- update_bracket_state_after_literal(state, character);
1167
1059
  return LITERAL_SCAN_TOKEN;
1168
1060
  }
1169
1061
 
1170
1062
  static TSSymbol
1171
- regex_unterminated_symbol(enum ScannerMode mode, bool at_end_of_source) {
1172
- if (mode == MODE_REGEX_ADDRESS) {
1063
+ unterminated_mode_symbol(enum ScannerMode mode, bool at_end_of_source) {
1064
+ switch (mode) {
1065
+ case MODE_REGEX_ADDRESS:
1173
1066
  return at_end_of_source ? REGEX_UNTERMINATED_ADDRESS
1174
1067
  : REGEX_LINE_UNTERMINATED_ADDRESS;
1068
+ case MODE_SUBSTITUTE_PATTERN:
1069
+ return at_end_of_source ? REGEX_UNTERMINATED_SUBSTITUTE
1070
+ : REGEX_LINE_UNTERMINATED_SUBSTITUTE;
1071
+ case MODE_SUBSTITUTE_REPLACEMENT:
1072
+ return at_end_of_source ? REPLACEMENT_UNTERMINATED
1073
+ : REPLACEMENT_LINE_UNTERMINATED;
1074
+ case MODE_TRANSLATE_SOURCE:
1075
+ return at_end_of_source ? TRANSLATE_UNTERMINATED_SOURCE
1076
+ : TRANSLATE_LINE_UNTERMINATED_SOURCE;
1077
+ case MODE_TRANSLATE_DESTINATION:
1078
+ return at_end_of_source ? TRANSLATE_UNTERMINATED_DESTINATION
1079
+ : TRANSLATE_LINE_UNTERMINATED_DESTINATION;
1080
+ case MODE_NONE:
1081
+ case MODE_TEXT:
1082
+ break;
1175
1083
  }
1176
- return at_end_of_source ? REGEX_UNTERMINATED_SUBSTITUTE
1177
- : REGEX_LINE_UNTERMINATED_SUBSTITUTE;
1084
+ return ERROR_SENTINEL;
1178
1085
  }
1179
1086
 
1180
- static bool emit_unterminated(
1181
- ScannerState *state,
1182
- const bool *valid_symbols,
1183
- TSSymbol candidate,
1184
- TSSymbol *symbol
1185
- ) {
1186
- if (!emit_symbol(valid_symbols, candidate, symbol)) {
1187
- return false;
1188
- }
1087
+ static const struct {
1088
+ int32_t marker;
1089
+ TSSymbol open_symbol;
1090
+ TSSymbol close_symbol;
1091
+ } bracket_terms[] = {
1092
+ [REGEX_BRACKET_TERM_COLON] = {':', REGEX_OPEN_COLON, REGEX_COLON_CLOSE},
1093
+ [REGEX_BRACKET_TERM_DOT] = {'.', REGEX_OPEN_DOT, REGEX_DOT_CLOSE},
1094
+ [REGEX_BRACKET_TERM_EQUAL] = {'=', REGEX_OPEN_EQUAL, REGEX_EQUAL_CLOSE},
1095
+ };
1189
1096
 
1190
- reset_state(state);
1191
- return true;
1097
+ static enum RegexBracketTermState bracket_term_for_marker(int32_t character) {
1098
+ for (
1099
+ enum RegexBracketTermState term_state = REGEX_BRACKET_TERM_COLON;
1100
+ term_state <= REGEX_BRACKET_TERM_EQUAL;
1101
+ term_state++
1102
+ ) {
1103
+ if (bracket_terms[term_state].marker == character) {
1104
+ return term_state;
1105
+ }
1106
+ }
1107
+ return REGEX_BRACKET_TERM_NONE;
1192
1108
  }
1193
1109
 
1194
1110
  static bool scan_regex_bracket_opener(
@@ -1210,35 +1126,22 @@ static bool scan_regex_bracket_opener(
1210
1126
 
1211
1127
  lexer->mark_end(lexer);
1212
1128
 
1213
- TSSymbol candidate;
1214
- enum RegexBracketTermState term_state;
1215
- switch (lexer->lookahead) {
1216
- case ':':
1217
- candidate = REGEX_OPEN_COLON;
1218
- term_state = REGEX_BRACKET_TERM_COLON;
1219
- break;
1220
- case '.':
1221
- candidate = REGEX_OPEN_DOT;
1222
- term_state = REGEX_BRACKET_TERM_DOT;
1223
- break;
1224
- case '=':
1225
- candidate = REGEX_OPEN_EQUAL;
1226
- term_state = REGEX_BRACKET_TERM_EQUAL;
1227
- break;
1228
- default:
1129
+ const enum RegexBracketTermState term_state =
1130
+ bracket_term_for_marker(lexer->lookahead);
1131
+ if (term_state == REGEX_BRACKET_TERM_NONE) {
1229
1132
  begin_single_bracket_element(state, '[');
1230
1133
  finish_first_bracket_element(state);
1231
1134
  return emit_symbol(valid_symbols, REGEX_BRACKET_LITERAL, symbol);
1232
1135
  }
1233
1136
 
1234
- if (!valid_symbols[candidate]) {
1235
- return false;
1236
- }
1237
1137
  begin_compound_bracket_element(state);
1238
1138
  finish_first_bracket_element(state);
1239
1139
  state->regex_bracket_term_state = term_state;
1240
- *symbol = candidate;
1241
- return true;
1140
+ return emit_symbol(
1141
+ valid_symbols,
1142
+ bracket_terms[term_state].open_symbol,
1143
+ symbol
1144
+ );
1242
1145
  }
1243
1146
 
1244
1147
  static void record_bracket_term_character(
@@ -1255,19 +1158,7 @@ static void record_bracket_term_character(
1255
1158
  }
1256
1159
 
1257
1160
  static bool bracket_meta_character(int32_t character) {
1258
- return character == '^' || character == '-' || character == ']';
1259
- }
1260
-
1261
- static int32_t
1262
- bracket_term_close_marker(enum RegexBracketTermState term_state) {
1263
- switch (term_state) {
1264
- case REGEX_BRACKET_TERM_COLON:
1265
- return ':';
1266
- case REGEX_BRACKET_TERM_DOT:
1267
- return '.';
1268
- default:
1269
- return '=';
1270
- }
1161
+ return character == '-' || character == ']';
1271
1162
  }
1272
1163
 
1273
1164
  static TSSymbol regex_bracket_term_content_symbol(
@@ -1281,45 +1172,45 @@ static TSSymbol regex_bracket_term_content_symbol(
1281
1172
  if (character_count > 1U) {
1282
1173
  return REGEX_COLL_ELEM_MULTI;
1283
1174
  }
1284
- if (
1285
- state->regex_bracket_term_state ==
1286
- REGEX_BRACKET_TERM_DOT &&
1287
- bracket_meta_character(single_character)
1288
- ) {
1289
- return REGEX_META_CHAR;
1175
+ if (!bracket_meta_character(single_character)) {
1176
+ return REGEX_COLL_ELEM_SINGLE;
1290
1177
  }
1291
- return REGEX_COLL_ELEM_SINGLE;
1178
+ return state->regex_bracket_term_state == REGEX_BRACKET_TERM_DOT
1179
+ ? REGEX_META_CHAR
1180
+ : REGEX_MALFORMED_BRACKET_TERM;
1292
1181
  }
1293
1182
 
1294
1183
  static bool scan_regex_bracket_term_content(
1295
1184
  TSLexer *lexer,
1296
1185
  ScannerState *state,
1297
- int32_t close_marker,
1298
1186
  const bool *valid_symbols,
1299
1187
  TSSymbol *symbol
1300
1188
  ) {
1189
+ const int32_t close_marker =
1190
+ bracket_terms[state->regex_bracket_term_state].marker;
1301
1191
  uint8_t character_count = 0;
1302
1192
  int32_t single_character = 0;
1303
1193
  bool stopped_at_close = false;
1194
+ bool leading_bracket_is_content =
1195
+ state->regex_bracket_term_state != REGEX_BRACKET_TERM_COLON;
1304
1196
  lexer->mark_end(lexer);
1305
1197
 
1306
- if (
1307
- lexer->lookahead ==
1308
- ']' &&
1309
- state->regex_bracket_term_state != REGEX_BRACKET_TERM_COLON
1310
- ) {
1311
- advance(lexer);
1312
- lexer->mark_end(lexer);
1313
- record_bracket_term_character(&character_count, &single_character, ']');
1198
+ for (;;) {
1199
+ if (
1200
+ lexer->eof(lexer) ||
1201
+ lexer->lookahead ==
1202
+ '\n' ||
1203
+ (lexer->lookahead == ']' && !leading_bracket_is_content)
1204
+ ) {
1205
+ break;
1206
+ }
1207
+ leading_bracket_is_content = false;
1208
+
1314
1209
  if (lexer->lookahead == close_marker) {
1315
1210
  advance(lexer);
1316
1211
  if (lexer->lookahead == ']') {
1317
- const TSSymbol candidate = regex_bracket_term_content_symbol(
1318
- state,
1319
- character_count,
1320
- single_character
1321
- );
1322
- return emit_symbol(valid_symbols, candidate, symbol);
1212
+ stopped_at_close = true;
1213
+ break;
1323
1214
  }
1324
1215
  lexer->mark_end(lexer);
1325
1216
  record_bracket_term_character(
@@ -1327,29 +1218,7 @@ static bool scan_regex_bracket_term_content(
1327
1218
  &single_character,
1328
1219
  close_marker
1329
1220
  );
1330
- }
1331
- }
1332
-
1333
- for (;;) {
1334
- if (
1335
- lexer->eof(lexer) || lexer->lookahead == '\n' || lexer->lookahead == ']'
1336
- ) {
1337
- break;
1338
- }
1339
-
1340
- if (lexer->lookahead == close_marker) {
1341
- advance(lexer);
1342
- if (lexer->lookahead == ']') {
1343
- stopped_at_close = true;
1344
- break;
1345
- }
1346
- lexer->mark_end(lexer);
1347
- record_bracket_term_character(
1348
- &character_count,
1349
- &single_character,
1350
- close_marker
1351
- );
1352
- continue;
1221
+ continue;
1353
1222
  }
1354
1223
 
1355
1224
  const int32_t character = lexer->lookahead;
@@ -1376,154 +1245,112 @@ static bool scan_regex_bracket_term_content(
1376
1245
  static bool scan_regex_bracket_term_close(
1377
1246
  TSLexer *lexer,
1378
1247
  ScannerState *state,
1379
- int32_t marker,
1380
- TSSymbol candidate,
1381
1248
  const bool *valid_symbols,
1382
1249
  TSSymbol *symbol
1383
1250
  ) {
1384
- if (lexer->lookahead != marker) {
1385
- return false;
1386
- }
1387
-
1388
1251
  consume(lexer);
1389
1252
  if (lexer->lookahead != ']') {
1390
1253
  return false;
1391
1254
  }
1255
+ const TSSymbol candidate =
1256
+ bracket_terms[state->regex_bracket_term_state].close_symbol;
1392
1257
  state->regex_bracket_term_state = REGEX_BRACKET_TERM_NONE;
1393
1258
  finish_compound_bracket_element(state);
1394
1259
  return emit_symbol(valid_symbols, candidate, symbol);
1395
1260
  }
1396
1261
 
1397
- enum RegexIntervalPartResult {
1398
- REGEX_INTERVAL_PART_END,
1399
- REGEX_INTERVAL_PART_ESCAPED_CLOSE,
1400
- REGEX_INTERVAL_PART_INVALID,
1401
- };
1402
-
1403
- static enum RegexIntervalPartResult scan_regex_interval_digits(
1404
- TSLexer *lexer,
1405
- const ScannerState *state,
1406
- bool escaped_close,
1407
- bool *has_digits
1408
- ) {
1409
- for (;;) {
1410
- if (is_digit(lexer->lookahead) && lexer->lookahead != state->delimiter) {
1411
- advance(lexer);
1412
- if (has_digits != NULL) {
1413
- *has_digits = true;
1414
- }
1415
- continue;
1416
- }
1417
-
1418
- if (escaped_close && lexer->lookahead == '\\') {
1419
- advance(lexer);
1420
- if (lexer->lookahead == '}' && state->delimiter != '}') {
1421
- return REGEX_INTERVAL_PART_ESCAPED_CLOSE;
1422
- }
1423
- advance_past_escaped_character(lexer);
1424
- return REGEX_INTERVAL_PART_INVALID;
1425
- }
1426
-
1427
- return REGEX_INTERVAL_PART_END;
1428
- }
1429
- }
1430
-
1431
- static bool
1432
- scan_regex_interval_separator(TSLexer *lexer, const ScannerState *state) {
1433
- if (lexer->lookahead != ',' || state->delimiter == ',') {
1434
- return false;
1435
- }
1436
-
1437
- advance(lexer);
1438
- return true;
1439
- }
1440
-
1441
- static bool scan_regex_interval_tail(
1262
+ static bool scan_regex_interval_open(
1442
1263
  TSLexer *lexer,
1443
- const ScannerState *state,
1444
- bool escaped_close
1264
+ ScannerState *state,
1265
+ const bool *valid_symbols,
1266
+ TSSymbol *symbol
1445
1267
  ) {
1446
- bool has_minimum = false;
1447
- enum RegexIntervalPartResult result =
1448
- scan_regex_interval_digits(lexer, state, escaped_close, &has_minimum);
1449
- if (result == REGEX_INTERVAL_PART_INVALID) {
1450
- return false;
1451
- }
1452
- if (result == REGEX_INTERVAL_PART_ESCAPED_CLOSE) {
1453
- advance(lexer);
1454
- return has_minimum;
1455
- }
1456
-
1457
- const bool has_separator = scan_regex_interval_separator(lexer, state);
1458
- if (!has_minimum) {
1459
- return false;
1460
- }
1461
-
1462
- if (has_separator) {
1463
- result = scan_regex_interval_digits(lexer, state, escaped_close, NULL);
1464
- if (result == REGEX_INTERVAL_PART_INVALID) {
1465
- return false;
1466
- }
1467
- if (result == REGEX_INTERVAL_PART_ESCAPED_CLOSE) {
1468
- advance(lexer);
1469
- return true;
1470
- }
1471
- }
1472
-
1473
- if (escaped_close) {
1474
- if (lexer->lookahead != '\\') {
1475
- return false;
1476
- }
1477
- advance(lexer);
1478
- if (lexer->lookahead != '}' || state->delimiter == '}') {
1479
- advance_past_escaped_character(lexer);
1480
- return false;
1481
- }
1482
- }
1483
-
1484
- if (lexer->lookahead != '}' || state->delimiter == '}') {
1485
- return false;
1486
- }
1487
-
1488
- advance(lexer);
1489
- return true;
1268
+ consume(lexer);
1269
+ state->regex_in_interval = true;
1270
+ return emit_symbol(valid_symbols, REGEX_INTERVAL_OPEN, symbol);
1490
1271
  }
1491
1272
 
1492
- static bool scan_regex_interval(
1493
- TSLexer *lexer,
1273
+ static bool end_interval(
1494
1274
  ScannerState *state,
1495
1275
  const bool *valid_symbols,
1276
+ TSSymbol candidate,
1496
1277
  TSSymbol *symbol
1497
1278
  ) {
1498
- advance(lexer);
1499
- lexer->mark_end(lexer);
1500
- state->regex_interval_state = REGEX_INTERVAL_EXPECT_MINIMUM;
1501
- return emit_symbol(valid_symbols, REGEX_INTERVAL_OPEN, symbol);
1279
+ state->regex_in_interval = false;
1280
+ return emit_symbol(valid_symbols, candidate, symbol);
1502
1281
  }
1503
1282
 
1504
- static bool emit_interval_issue(
1283
+ static bool scan_regex_interval_close(
1505
1284
  TSLexer *lexer,
1506
1285
  ScannerState *state,
1507
- TSSymbol candidate,
1286
+ const bool *valid_symbols,
1508
1287
  TSSymbol *symbol
1509
1288
  ) {
1510
- lexer->mark_end(lexer);
1511
- state->regex_interval_state = REGEX_INTERVAL_NONE;
1512
- *symbol = candidate;
1513
- return true;
1289
+ consume(lexer);
1290
+ return end_interval(state, valid_symbols, REGEX_INTERVAL_CLOSE, symbol);
1514
1291
  }
1515
1292
 
1293
+ #if !SED_REGEX_EXTENDED
1294
+ // A BRE escape that ends malformed interval content: the interval close and
1295
+ // the subexpression delimiters own source of their own, unless the escaped
1296
+ // character is the RE delimiter and the escape is that literal character.
1516
1297
  static bool
1517
- scan_invalid_interval_remainder(TSLexer *lexer, ScannerState *state);
1298
+ interval_escape_ends_content(const ScannerState *state, int32_t escaped) {
1299
+ return (escaped == '}' || escaped == '(' || escaped == ')') &&
1300
+ escaped != state->delimiter;
1301
+ }
1302
+ #endif
1303
+
1304
+ static void finish_malformed_escape(TSLexer *lexer) {
1305
+ if (!lexer->eof(lexer) && lexer->lookahead != '\n') {
1306
+ advance(lexer);
1307
+ }
1308
+ lexer->mark_end(lexer);
1309
+ }
1310
+
1311
+ // Leave independently parsed constructs outside the malformed interval token
1312
+ // so recovery preserves their source ownership.
1313
+ static void
1314
+ scan_malformed_interval_content(TSLexer *lexer, const ScannerState *state) {
1315
+ for (;;) {
1316
+ const int32_t character = lexer->lookahead;
1317
+ if (
1318
+ lexer->eof(lexer) ||
1319
+ character ==
1320
+ '\n' ||
1321
+ character ==
1322
+ state->delimiter ||
1323
+ character == '['
1324
+ ) {
1325
+ return;
1326
+ }
1327
+ #if SED_REGEX_EXTENDED
1328
+ if (
1329
+ character ==
1330
+ '}' ||
1331
+ character ==
1332
+ '(' ||
1333
+ character ==
1334
+ ')' ||
1335
+ character == '|'
1336
+ ) {
1337
+ return;
1338
+ }
1339
+ #endif
1340
+ if (character != '\\') {
1341
+ consume(lexer);
1342
+ continue;
1343
+ }
1518
1344
 
1345
+ advance(lexer);
1519
1346
  #if !SED_REGEX_EXTENDED
1520
- static bool scan_regex_after_backslash(
1521
- TSLexer *lexer,
1522
- ScannerState *state,
1523
- const bool *valid_symbols,
1524
- TSSymbol *symbol
1525
- );
1347
+ if (interval_escape_ends_content(state, lexer->lookahead)) {
1348
+ return;
1349
+ }
1526
1350
  #endif
1351
+ finish_malformed_escape(lexer);
1352
+ }
1353
+ }
1527
1354
 
1528
1355
  static bool raw_duplication_symbol_follows(
1529
1356
  const TSLexer *lexer,
@@ -1541,84 +1368,28 @@ static bool raw_duplication_symbol_follows(
1541
1368
  #endif
1542
1369
  }
1543
1370
 
1544
- enum DuplicationContextScan {
1545
- DUPLICATION_CONTEXT_SKIPPED,
1546
- DUPLICATION_CONTEXT_TOKEN,
1547
- DUPLICATION_CONTEXT_CONSUMED,
1548
- };
1549
-
1550
- static enum DuplicationContextScan scan_regex_duplication_context_marker(
1551
- TSLexer *lexer,
1552
- ScannerState *state,
1553
- const bool *valid_symbols,
1554
- TSSymbol *symbol
1371
+ static TSSymbol regex_duplication_context_marker(
1372
+ const TSLexer *lexer,
1373
+ const ScannerState *state
1555
1374
  ) {
1556
- TSSymbol candidate;
1557
1375
  if (state->regex_at_branch_start) {
1558
- candidate = REGEX_LEADING_DUPLICATION_MARKER;
1559
- } else if (state->regex_duplication_state != REGEX_DUPLICATION_NONE) {
1560
- #if SED_REGEX_EXTENDED
1561
- if (
1562
- state->regex_duplication_state ==
1563
- REGEX_AFTER_DUPLICATION_SYMBOL &&
1564
- lexer->lookahead == '?'
1565
- ) {
1566
- return DUPLICATION_CONTEXT_SKIPPED;
1567
- }
1568
- #endif
1569
- candidate = REGEX_ADJACENT_DUPLICATION_MARKER;
1570
- } else {
1571
- return DUPLICATION_CONTEXT_SKIPPED;
1572
- }
1573
-
1574
- if (!valid_symbols[candidate]) {
1575
- return DUPLICATION_CONTEXT_SKIPPED;
1376
+ return REGEX_LEADING_DUPLICATION_MARKER;
1576
1377
  }
1577
-
1578
- lexer->mark_end(lexer);
1579
- if (raw_duplication_symbol_follows(lexer, state)) {
1580
- *symbol = candidate;
1581
- return DUPLICATION_CONTEXT_TOKEN;
1378
+ if (state->regex_duplication_state == REGEX_DUPLICATION_NONE) {
1379
+ return ERROR_SENTINEL;
1582
1380
  }
1583
-
1584
1381
  #if SED_REGEX_EXTENDED
1585
- if (lexer->lookahead != '{' || state->delimiter == '{') {
1586
- return DUPLICATION_CONTEXT_SKIPPED;
1587
- }
1588
- advance(lexer);
1589
- if (scan_regex_interval_tail(lexer, state, false)) {
1590
- *symbol = candidate;
1591
- return DUPLICATION_CONTEXT_TOKEN;
1382
+ if (
1383
+ state->regex_duplication_state ==
1384
+ REGEX_AFTER_DUPLICATION_SYMBOL &&
1385
+ lexer->lookahead == '?'
1386
+ ) {
1387
+ return ERROR_SENTINEL;
1592
1388
  }
1593
1389
  #else
1594
- if (lexer->lookahead != '\\') {
1595
- return DUPLICATION_CONTEXT_SKIPPED;
1596
- }
1597
- advance(lexer);
1598
- if (lexer->lookahead != '{' || state->delimiter == '{') {
1599
- return scan_regex_after_backslash(lexer, state, valid_symbols, symbol)
1600
- ? DUPLICATION_CONTEXT_TOKEN
1601
- : DUPLICATION_CONTEXT_CONSUMED;
1602
- }
1603
- advance(lexer);
1604
- if (scan_regex_interval_tail(lexer, state, true)) {
1605
- *symbol = candidate;
1606
- return DUPLICATION_CONTEXT_TOKEN;
1607
- }
1390
+ (void)lexer;
1608
1391
  #endif
1609
-
1610
- if (lexer->eof(lexer) && valid_symbols[REGEX_INCOMPLETE_INTERVAL]) {
1611
- emit_interval_issue(lexer, state, REGEX_INCOMPLETE_INTERVAL, symbol);
1612
- return DUPLICATION_CONTEXT_TOKEN;
1613
- }
1614
-
1615
- if (!valid_symbols[REGEX_INVALID_INTERVAL]) {
1616
- *symbol = candidate;
1617
- return DUPLICATION_CONTEXT_TOKEN;
1618
- }
1619
- scan_invalid_interval_remainder(lexer, state);
1620
- emit_interval_issue(lexer, state, REGEX_INVALID_INTERVAL, symbol);
1621
- return DUPLICATION_CONTEXT_TOKEN;
1392
+ return REGEX_ADJACENT_DUPLICATION_MARKER;
1622
1393
  }
1623
1394
 
1624
1395
  static bool raw_regex_operator_character(int32_t character) {
@@ -1646,10 +1417,9 @@ static bool scan_raw_regex_operator(
1646
1417
 
1647
1418
  if (character == '{') {
1648
1419
  #if SED_REGEX_EXTENDED
1649
- return scan_regex_interval(lexer, state, valid_symbols, symbol);
1420
+ return scan_regex_interval_open(lexer, state, valid_symbols, symbol);
1650
1421
  #else
1651
- consume(lexer);
1652
- return emit_symbol(valid_symbols, REGEX_LITERAL, symbol);
1422
+ return consume_as(lexer, valid_symbols, REGEX_LITERAL, symbol);
1653
1423
  #endif
1654
1424
  }
1655
1425
 
@@ -1724,10 +1494,17 @@ static bool regex_delimiter_is_special(int32_t character) {
1724
1494
  }
1725
1495
  }
1726
1496
 
1497
+ // The QUOTED_CHAR sequences of XBD 9.5.1 for the selected RE.
1727
1498
  static bool escape_quotes_regex_syntax(int32_t character) {
1728
1499
  switch (character) {
1729
1500
  #if SED_REGEX_EXTENDED
1501
+ case '(':
1502
+ case ')':
1503
+ case '{':
1730
1504
  case '}':
1505
+ case '+':
1506
+ case '?':
1507
+ case '|':
1731
1508
  #endif
1732
1509
  case '.':
1733
1510
  case '*':
@@ -1747,11 +1524,12 @@ static bool scan_regex_escaped_delimiter(
1747
1524
  const bool *valid_symbols,
1748
1525
  TSSymbol *symbol
1749
1526
  ) {
1750
- const bool special = regex_delimiter_is_special(lexer->lookahead);
1751
- consume(lexer);
1752
- return emit_symbol(
1527
+ return consume_as(
1528
+ lexer,
1753
1529
  valid_symbols,
1754
- special ? REGEX_SPECIAL_ESCAPED_DELIMITER : REGEX_ESCAPED_DELIMITER,
1530
+ regex_delimiter_is_special(lexer->lookahead)
1531
+ ? REGEX_SPECIAL_ESCAPED_DELIMITER
1532
+ : REGEX_ESCAPED_DELIMITER,
1755
1533
  symbol
1756
1534
  );
1757
1535
  }
@@ -1777,103 +1555,89 @@ static bool scan_regex_escape_after_backslash(
1777
1555
  #if !SED_REGEX_EXTENDED
1778
1556
  if (lexer->lookahead == '}') {
1779
1557
  if (valid_symbols[REGEX_INTERVAL_CLOSE]) {
1780
- consume(lexer);
1781
- state->regex_interval_state = REGEX_INTERVAL_NONE;
1782
- return emit_symbol(valid_symbols, REGEX_INTERVAL_CLOSE, symbol);
1558
+ return scan_regex_interval_close(lexer, state, valid_symbols, symbol);
1783
1559
  }
1784
1560
  if (valid_symbols[REGEX_UNMATCHED_INTERVAL_CLOSE]) {
1785
- consume(lexer);
1786
- return emit_symbol(valid_symbols, REGEX_UNMATCHED_INTERVAL_CLOSE, symbol);
1561
+ return consume_as(
1562
+ lexer,
1563
+ valid_symbols,
1564
+ REGEX_UNMATCHED_INTERVAL_CLOSE,
1565
+ symbol
1566
+ );
1787
1567
  }
1788
1568
  }
1789
- #endif
1790
1569
 
1791
- if (lexer->lookahead == '(' || lexer->lookahead == ')') {
1792
- #if !SED_REGEX_EXTENDED
1793
- const int32_t character = lexer->lookahead;
1794
- #endif
1795
- consume(lexer);
1796
- #if SED_REGEX_EXTENDED
1797
- return emit_symbol(valid_symbols, REGEX_QUOTED_ESCAPE, symbol);
1798
- #else
1799
- return character == '('
1800
- ? emit_symbol(valid_symbols, REGEX_GROUP_OPEN, symbol)
1801
- : emit_symbol(
1802
- valid_symbols,
1803
- state->regex_group_depth > 0 ? REGEX_GROUP_CLOSE
1804
- : REGEX_UNMATCHED_GROUP_CLOSE,
1805
- symbol
1806
- );
1807
- #endif
1570
+ if (lexer->lookahead == '(') {
1571
+ return consume_as(lexer, valid_symbols, REGEX_GROUP_OPEN, symbol);
1572
+ }
1573
+
1574
+ if (lexer->lookahead == ')') {
1575
+ return consume_as(
1576
+ lexer,
1577
+ valid_symbols,
1578
+ state->regex_group_depth > 0 ? REGEX_GROUP_CLOSE
1579
+ : REGEX_UNMATCHED_GROUP_CLOSE,
1580
+ symbol
1581
+ );
1808
1582
  }
1809
1583
 
1810
1584
  if (lexer->lookahead >= '1' && lexer->lookahead <= '9') {
1811
- #if SED_REGEX_EXTENDED
1812
- consume(lexer);
1813
- return emit_symbol(valid_symbols, REGEX_NONPORTABLE_ESCAPE, symbol);
1814
- #else
1815
- consume(lexer);
1816
- return emit_symbol(valid_symbols, REGEX_BACKREFERENCE, symbol);
1817
- #endif
1585
+ return consume_as(lexer, valid_symbols, REGEX_BACKREFERENCE, symbol);
1818
1586
  }
1819
1587
 
1820
1588
  if (lexer->lookahead == '{') {
1821
- #if SED_REGEX_EXTENDED
1822
- consume(lexer);
1823
- return emit_symbol(valid_symbols, REGEX_QUOTED_ESCAPE, symbol);
1824
- #else
1825
- return scan_regex_interval(lexer, state, valid_symbols, symbol);
1826
- #endif
1589
+ return scan_regex_interval_open(lexer, state, valid_symbols, symbol);
1827
1590
  }
1828
1591
 
1829
- if (
1830
- lexer->lookahead ==
1831
- '+' ||
1832
- lexer->lookahead ==
1833
- '?' ||
1834
- lexer->lookahead == '|'
1835
- ) {
1836
- #if SED_REGEX_EXTENDED
1837
- consume(lexer);
1838
- return emit_symbol(valid_symbols, REGEX_QUOTED_ESCAPE, symbol);
1839
- #else
1840
- const int32_t character = lexer->lookahead;
1841
- consume(lexer);
1842
- if (character == '|') {
1843
- return emit_symbol(valid_symbols, REGEX_BRE_VERTICAL_LINE_ESCAPE, symbol);
1844
- }
1845
- return emit_symbol(
1592
+ if (lexer->lookahead == '|') {
1593
+ return consume_as(
1594
+ lexer,
1846
1595
  valid_symbols,
1847
- character == '?' ? REGEX_BRE_QUESTION_MARK_ESCAPE : REGEX_BRE_PLUS_ESCAPE,
1596
+ REGEX_BRE_VERTICAL_LINE_ESCAPE,
1597
+ symbol
1598
+ );
1599
+ }
1600
+
1601
+ if (lexer->lookahead == '?' || lexer->lookahead == '+') {
1602
+ return consume_as(
1603
+ lexer,
1604
+ valid_symbols,
1605
+ lexer->lookahead == '?' ? REGEX_BRE_QUESTION_MARK_ESCAPE
1606
+ : REGEX_BRE_PLUS_ESCAPE,
1848
1607
  symbol
1849
1608
  );
1850
- #endif
1851
1609
  }
1610
+ #endif
1852
1611
 
1853
1612
  if (lexer->lookahead == 'n') {
1854
- consume(lexer);
1855
- return emit_symbol(valid_symbols, REGEX_NEWLINE_ESCAPE, symbol);
1613
+ return consume_as(lexer, valid_symbols, REGEX_NEWLINE_ESCAPE, symbol);
1856
1614
  }
1857
1615
 
1858
- const TSSymbol candidate = escape_quotes_regex_syntax(lexer->lookahead)
1859
- ? REGEX_QUOTED_ESCAPE
1860
- : REGEX_NONPORTABLE_ESCAPE;
1861
- consume(lexer);
1862
- return emit_symbol(valid_symbols, candidate, symbol);
1616
+ return consume_as(
1617
+ lexer,
1618
+ valid_symbols,
1619
+ escape_quotes_regex_syntax(lexer->lookahead) ? REGEX_QUOTED_ESCAPE
1620
+ : REGEX_NONPORTABLE_ESCAPE,
1621
+ symbol
1622
+ );
1863
1623
  }
1864
1624
 
1865
1625
  #if !SED_REGEX_EXTENDED
1626
+ // Scans the escape after a backslash whose token end is marked before it:
1627
+ // "\)" first closes an empty subexpression unless ')' is the delimiter.
1866
1628
  static bool scan_regex_after_backslash(
1867
1629
  TSLexer *lexer,
1868
1630
  ScannerState *state,
1869
1631
  const bool *valid_symbols,
1870
1632
  TSSymbol *symbol
1871
1633
  ) {
1872
- if (lexer->lookahead == state->delimiter) {
1873
- return scan_regex_escaped_delimiter(lexer, valid_symbols, symbol);
1874
- }
1875
-
1876
- if (lexer->lookahead == ')' && valid_symbols[EMPTY_SUBEXPRESSION_MARKER]) {
1634
+ if (
1635
+ lexer->lookahead ==
1636
+ ')' &&
1637
+ state->delimiter !=
1638
+ ')' &&
1639
+ valid_symbols[EMPTY_SUBEXPRESSION_MARKER]
1640
+ ) {
1877
1641
  *symbol = EMPTY_SUBEXPRESSION_MARKER;
1878
1642
  return true;
1879
1643
  }
@@ -1885,7 +1649,7 @@ static bool scan_regex_after_backslash(
1885
1649
 
1886
1650
  static bool scan_regex_dup_count(
1887
1651
  TSLexer *lexer,
1888
- ScannerState *state,
1652
+ const ScannerState *state,
1889
1653
  const bool *valid_symbols,
1890
1654
  TSSymbol *symbol
1891
1655
  ) {
@@ -1893,45 +1657,9 @@ static bool scan_regex_dup_count(
1893
1657
  consume(lexer);
1894
1658
  } while (is_digit(lexer->lookahead) && lexer->lookahead != state->delimiter);
1895
1659
 
1896
- if (state->regex_interval_state == REGEX_INTERVAL_EXPECT_MINIMUM) {
1897
- state->regex_interval_state = REGEX_INTERVAL_AFTER_MINIMUM;
1898
- } else if (state->regex_interval_state == REGEX_INTERVAL_EXPECT_MAXIMUM) {
1899
- state->regex_interval_state = REGEX_INTERVAL_AFTER_MAXIMUM;
1900
- }
1901
-
1902
1660
  return emit_symbol(valid_symbols, REGEX_DUP_COUNT, symbol);
1903
1661
  }
1904
1662
 
1905
- static bool
1906
- scan_invalid_interval_remainder(TSLexer *lexer, ScannerState *state) {
1907
- bool consumed_character = false;
1908
- while (
1909
- !lexer->eof(lexer) &&
1910
- lexer->lookahead !=
1911
- '\n' &&
1912
- lexer->lookahead != state->delimiter
1913
- ) {
1914
- const int32_t character = lexer->lookahead;
1915
- consume(lexer);
1916
- consumed_character = true;
1917
- if (character == '}') {
1918
- break;
1919
- }
1920
- if (character != '\\' || lexer->eof(lexer) || lexer->lookahead == '\n') {
1921
- continue;
1922
- }
1923
- const int32_t escaped = lexer->lookahead;
1924
- consume(lexer);
1925
- if (!SED_REGEX_EXTENDED && escaped == '}') {
1926
- break;
1927
- }
1928
- }
1929
- if (consumed_character) {
1930
- state->regex_interval_state = REGEX_INTERVAL_NONE;
1931
- }
1932
- return consumed_character;
1933
- }
1934
-
1935
1663
  #if SED_REGEX_EXTENDED
1936
1664
  static bool scan_empty_regex_construct(
1937
1665
  TSLexer *lexer,
@@ -1946,7 +1674,7 @@ static bool scan_empty_regex_construct(
1946
1674
  ')' &&
1947
1675
  valid_symbols[EMPTY_SUBEXPRESSION_MARKER]
1948
1676
  ) {
1949
- return emit_missing_marker(
1677
+ return emit_marker(
1950
1678
  lexer,
1951
1679
  valid_symbols,
1952
1680
  EMPTY_SUBEXPRESSION_MARKER,
@@ -1959,365 +1687,343 @@ static bool scan_empty_regex_construct(
1959
1687
  '|' ||
1960
1688
  (state->regex_group_depth > 0 && lexer->lookahead == ')'))
1961
1689
  ) {
1962
- return emit_missing_marker(
1963
- lexer,
1964
- valid_symbols,
1965
- EMPTY_ALTERNATIVE_MARKER,
1966
- symbol
1967
- );
1690
+ return emit_marker(lexer, valid_symbols, EMPTY_ALTERNATIVE_MARKER, symbol);
1968
1691
  }
1969
1692
  return false;
1970
1693
  }
1971
1694
  #endif
1972
1695
 
1973
- static bool scan_regex_token(
1696
+ static bool scan_regex_special_token(
1974
1697
  TSLexer *lexer,
1975
1698
  ScannerState *state,
1976
1699
  const bool *valid_symbols,
1977
1700
  TSSymbol *symbol
1978
1701
  ) {
1979
- switch (scan_regex_duplication_context_marker(
1980
- lexer,
1981
- state,
1982
- valid_symbols,
1983
- symbol
1984
- )) {
1985
- case DUPLICATION_CONTEXT_TOKEN:
1986
- return true;
1987
- case DUPLICATION_CONTEXT_CONSUMED:
1988
- return false;
1989
- case DUPLICATION_CONTEXT_SKIPPED:
1990
- break;
1702
+ if (lexer->lookahead == '^') {
1703
+ consume(lexer);
1704
+ #if !SED_REGEX_EXTENDED
1705
+ if (!state->regex_at_branch_start || state->regex_after_anchor) {
1706
+ return emit_symbol(valid_symbols, REGEX_LITERAL, symbol);
1707
+ }
1708
+ if (state->regex_group_depth > 0) {
1709
+ return emit_symbol(valid_symbols, REGEX_BRE_SUBEXPRESSION_CARET, symbol);
1710
+ }
1711
+ #endif
1712
+ return emit_symbol(valid_symbols, REGEX_BEGINNING_ANCHOR, symbol);
1991
1713
  }
1992
1714
 
1715
+ if (lexer->lookahead == '$') {
1716
+ consume(lexer);
1993
1717
  #if !SED_REGEX_EXTENDED
1994
- if (lexer->lookahead == '\\' && valid_symbols[EMPTY_SUBEXPRESSION_MARKER]) {
1995
- lexer->mark_end(lexer);
1996
- advance(lexer);
1997
- return scan_regex_after_backslash(lexer, state, valid_symbols, symbol);
1718
+ if (lexer->lookahead == '\\') {
1719
+ // A dollar sign anchors a subexpression before "\)", unless ')'
1720
+ // delimits the RE and "\)" is the escaped delimiter.
1721
+ advance(lexer);
1722
+ const bool closes_subexpression = state->regex_group_depth >
1723
+ 0 &&
1724
+ lexer->lookahead ==
1725
+ ')' &&
1726
+ state->delimiter != ')';
1727
+ return emit_symbol(
1728
+ valid_symbols,
1729
+ closes_subexpression ? REGEX_BRE_SUBEXPRESSION_DOLLAR : REGEX_LITERAL,
1730
+ symbol
1731
+ );
1732
+ }
1733
+ const bool at_branch_end = lexer->eof(lexer) ||
1734
+ lexer->lookahead ==
1735
+ '\n' ||
1736
+ lexer->lookahead == state->delimiter;
1737
+ if (!at_branch_end) {
1738
+ return emit_symbol(valid_symbols, REGEX_LITERAL, symbol);
1739
+ }
1740
+ #endif
1741
+ return emit_symbol(valid_symbols, REGEX_END_ANCHOR, symbol);
1998
1742
  }
1999
- #else
2000
- if (scan_empty_regex_construct(lexer, state, valid_symbols, symbol)) {
2001
- return true;
1743
+
1744
+ if (lexer->lookahead == '.') {
1745
+ return consume_as(lexer, valid_symbols, REGEX_PERIOD, symbol);
2002
1746
  }
2003
- #endif
2004
1747
 
2005
- if (
2006
- valid_symbols[REGEX_DUP_COUNT] &&
2007
- is_digit(lexer->lookahead) &&
2008
- lexer->lookahead != state->delimiter
2009
- ) {
2010
- return scan_regex_dup_count(lexer, state, valid_symbols, symbol);
1748
+ if (lexer->lookahead == '\\') {
1749
+ consume(lexer);
1750
+ return scan_regex_escape_after_backslash(
1751
+ lexer,
1752
+ state,
1753
+ valid_symbols,
1754
+ symbol
1755
+ );
2011
1756
  }
2012
1757
 
1758
+ if (raw_regex_operator_character(lexer->lookahead)) {
1759
+ return scan_raw_regex_operator(lexer, state, valid_symbols, symbol);
1760
+ }
1761
+
1762
+ if (lexer->lookahead == '[') {
1763
+ consume(lexer);
1764
+ reset_bracket_tracking(state);
1765
+ state->regex_state = REGEX_BRACKET_FIRST;
1766
+ return emit_symbol(valid_symbols, REGEX_BRACKET_OPEN, symbol);
1767
+ }
1768
+
1769
+ return false;
1770
+ }
1771
+
1772
+ static bool scan_regex_bracket_close(
1773
+ TSLexer *lexer,
1774
+ ScannerState *state,
1775
+ const bool *valid_symbols,
1776
+ TSSymbol *symbol
1777
+ ) {
1778
+ commit_pending_bracket_element(state);
2013
1779
  if (
2014
- valid_symbols[REGEX_INTERVAL_SEPARATOR] &&
2015
- lexer->lookahead ==
2016
- ',' &&
2017
- lexer->lookahead != state->delimiter
1780
+ bracket_expression_is_ambiguous(state) &&
1781
+ valid_symbols[AMBIGUOUS_BRACKET_EXPRESSION_MARKER]
2018
1782
  ) {
2019
- consume(lexer);
2020
- state->regex_interval_state = REGEX_INTERVAL_EXPECT_MAXIMUM;
2021
- *symbol = REGEX_INTERVAL_SEPARATOR;
1783
+ lexer->mark_end(lexer);
1784
+ *symbol = AMBIGUOUS_BRACKET_EXPRESSION_MARKER;
2022
1785
  return true;
2023
1786
  }
2024
1787
 
2025
- #if SED_REGEX_EXTENDED
1788
+ consume(lexer);
1789
+ state->regex_state = REGEX_OUTSIDE_BRACKET;
1790
+ reset_bracket_tracking(state);
1791
+ return emit_symbol(valid_symbols, REGEX_BRACKET_CLOSE, symbol);
1792
+ }
1793
+
1794
+ static bool scan_regex_bracket_hyphen(
1795
+ TSLexer *lexer,
1796
+ ScannerState *state,
1797
+ const bool *valid_symbols,
1798
+ TSSymbol *symbol
1799
+ ) {
1800
+ lexer->mark_end(lexer);
1801
+ advance(lexer);
2026
1802
  if (
2027
- valid_symbols[REGEX_INTERVAL_CLOSE] &&
2028
- lexer->lookahead ==
2029
- '}' &&
2030
- lexer->lookahead != state->delimiter
1803
+ valid_symbols[NONPORTABLE_RANGE_START_MARKER] &&
1804
+ !lexer->eof(lexer) &&
1805
+ lexer->lookahead !=
1806
+ '\n' &&
1807
+ lexer->lookahead != ']'
2031
1808
  ) {
2032
- consume(lexer);
2033
- state->regex_interval_state = REGEX_INTERVAL_NONE;
2034
- *symbol = REGEX_INTERVAL_CLOSE;
1809
+ *symbol = NONPORTABLE_RANGE_START_MARKER;
2035
1810
  return true;
2036
1811
  }
2037
- #else
2038
- if (
2039
- state->regex_interval_state !=
2040
- REGEX_INTERVAL_NONE &&
2041
- lexer->lookahead == '\\'
1812
+ lexer->mark_end(lexer);
1813
+
1814
+ TSSymbol candidate;
1815
+ if (valid_symbols[REGEX_BRACKET_RANGE_END_HYPHEN]) {
1816
+ candidate = REGEX_BRACKET_RANGE_END_HYPHEN;
1817
+ } else if (
1818
+ lexer->eof(lexer) || lexer->lookahead == '\n' || lexer->lookahead == ']'
2042
1819
  ) {
2043
- advance(lexer);
2044
- if (
2045
- valid_symbols[REGEX_INTERVAL_CLOSE] &&
2046
- lexer->lookahead ==
2047
- '}' &&
2048
- lexer->lookahead != state->delimiter
2049
- ) {
2050
- consume(lexer);
2051
- state->regex_interval_state = REGEX_INTERVAL_NONE;
2052
- *symbol = REGEX_INTERVAL_CLOSE;
2053
- return true;
2054
- }
2055
- if (lexer->eof(lexer) && valid_symbols[REGEX_INCOMPLETE_INTERVAL]) {
2056
- return emit_interval_issue(
2057
- lexer,
2058
- state,
2059
- REGEX_INCOMPLETE_INTERVAL,
2060
- symbol
2061
- );
2062
- }
2063
- if (!valid_symbols[REGEX_INVALID_INTERVAL]) {
2064
- return false;
2065
- }
2066
- if (lexer->lookahead == '}') {
2067
- advance(lexer);
1820
+ candidate = REGEX_BRACKET_TRAILING_HYPHEN;
1821
+ } else {
1822
+ candidate = REGEX_BRACKET_HYPHEN;
1823
+ }
1824
+ if (emit_symbol(valid_symbols, candidate, symbol)) {
1825
+ if (candidate == REGEX_BRACKET_RANGE_END_HYPHEN) {
1826
+ state->regex_bracket_pending_element = REGEX_BRACKET_PENDING_NONE;
1827
+ state->regex_bracket_range_pending = false;
1828
+ record_bracket_element(state, REGEX_BRACKET_PENDING_OTHER);
1829
+ } else if (candidate == REGEX_BRACKET_TRAILING_HYPHEN) {
1830
+ commit_pending_bracket_element(state);
1831
+ record_bracket_element(state, REGEX_BRACKET_PENDING_OTHER);
2068
1832
  } else {
2069
- advance_past_escaped_character(lexer);
2070
- scan_invalid_interval_remainder(lexer, state);
1833
+ state->regex_bracket_pending_element = REGEX_BRACKET_PENDING_NONE;
1834
+ state->regex_bracket_range_pending = true;
2071
1835
  }
2072
- return emit_interval_issue(lexer, state, REGEX_INVALID_INTERVAL, symbol);
1836
+ return true;
2073
1837
  }
2074
- #endif
2075
-
2076
- if (
2077
- state->regex_interval_state !=
2078
- REGEX_INTERVAL_NONE &&
2079
- valid_symbols[REGEX_INVALID_INTERVAL] &&
2080
- scan_invalid_interval_remainder(lexer, state)
2081
- ) {
2082
- *symbol = REGEX_INVALID_INTERVAL;
1838
+ if (emit_symbol(valid_symbols, REGEX_SHARED_RANGE_ENDPOINT, symbol)) {
1839
+ commit_pending_bracket_element(state);
1840
+ state->regex_bracket_range_pending = false;
2083
1841
  return true;
2084
1842
  }
1843
+ return false;
1844
+ }
2085
1845
 
2086
- const bool inside_bracket = regex_is_inside_bracket(state);
2087
- if (inside_bracket) {
2088
- if (
2089
- valid_symbols[REGEX_CLASS_NAME] ||
2090
- valid_symbols[REGEX_COLL_ELEM_SINGLE] ||
2091
- valid_symbols[REGEX_COLL_ELEM_MULTI] ||
2092
- valid_symbols[REGEX_META_CHAR]
2093
- ) {
2094
- return scan_regex_bracket_term_content(
2095
- lexer,
2096
- state,
2097
- bracket_term_close_marker(state->regex_bracket_term_state),
2098
- valid_symbols,
2099
- symbol
2100
- );
2101
- }
1846
+ static bool scan_regex_bracket_special_token(
1847
+ TSLexer *lexer,
1848
+ ScannerState *state,
1849
+ const bool *valid_symbols,
1850
+ TSSymbol *symbol
1851
+ ) {
1852
+ if (state->regex_state == REGEX_BRACKET_FIRST && lexer->lookahead == '^') {
1853
+ consume(lexer);
1854
+ state->regex_state = REGEX_BRACKET_AFTER_CARET;
1855
+ return emit_symbol(valid_symbols, REGEX_BRACKET_NEGATION, symbol);
1856
+ }
1857
+
1858
+ if (state->regex_state == REGEX_BRACKET_BODY && lexer->lookahead == ']') {
1859
+ return scan_regex_bracket_close(lexer, state, valid_symbols, symbol);
1860
+ }
1861
+
1862
+ if (state->regex_state == REGEX_BRACKET_BODY && lexer->lookahead == '-') {
1863
+ return scan_regex_bracket_hyphen(lexer, state, valid_symbols, symbol);
1864
+ }
1865
+
1866
+ if (lexer->lookahead == '[') {
1867
+ return scan_regex_bracket_opener(lexer, state, valid_symbols, symbol);
1868
+ }
1869
+
1870
+ return false;
1871
+ }
1872
+
1873
+ static bool scan_regex_token(
1874
+ TSLexer *lexer,
1875
+ ScannerState *state,
1876
+ const bool *valid_symbols,
1877
+ TSSymbol *symbol
1878
+ ) {
1879
+ // The marker precedes the duplication symbol, whose interval is then
1880
+ // scanned like any other so that malformed content keeps its structure.
1881
+ const TSSymbol marker = regex_duplication_context_marker(lexer, state);
1882
+ if (marker != ERROR_SENTINEL && valid_symbols[marker]) {
1883
+ lexer->mark_end(lexer);
1884
+ #if SED_REGEX_EXTENDED
2102
1885
  if (
2103
- state->regex_bracket_term_state !=
2104
- REGEX_BRACKET_TERM_NONE &&
2105
- lexer->lookahead ==
2106
- ']' &&
2107
- valid_symbols[REGEX_MALFORMED_BRACKET_TERM]
1886
+ raw_duplication_symbol_follows(lexer, state) || lexer->lookahead == '{'
2108
1887
  ) {
2109
- lexer->mark_end(lexer);
2110
- state->regex_bracket_term_state = REGEX_BRACKET_TERM_NONE;
2111
- finish_compound_bracket_element(state);
2112
- *symbol = REGEX_MALFORMED_BRACKET_TERM;
1888
+ *symbol = marker;
2113
1889
  return true;
2114
1890
  }
2115
- if (valid_symbols[REGEX_COLON_CLOSE] && lexer->lookahead == ':') {
2116
- return scan_regex_bracket_term_close(
2117
- lexer,
2118
- state,
2119
- ':',
2120
- REGEX_COLON_CLOSE,
2121
- valid_symbols,
2122
- symbol
2123
- );
2124
- }
2125
- if (valid_symbols[REGEX_DOT_CLOSE] && lexer->lookahead == '.') {
2126
- return scan_regex_bracket_term_close(
2127
- lexer,
2128
- state,
2129
- '.',
2130
- REGEX_DOT_CLOSE,
2131
- valid_symbols,
2132
- symbol
2133
- );
1891
+ #else
1892
+ if (raw_duplication_symbol_follows(lexer, state)) {
1893
+ *symbol = marker;
1894
+ return true;
2134
1895
  }
2135
- if (valid_symbols[REGEX_EQUAL_CLOSE] && lexer->lookahead == '=') {
2136
- return scan_regex_bracket_term_close(
2137
- lexer,
2138
- state,
2139
- '=',
2140
- REGEX_EQUAL_CLOSE,
2141
- valid_symbols,
2142
- symbol
2143
- );
1896
+ if (lexer->lookahead == '\\') {
1897
+ advance(lexer);
1898
+ if (lexer->lookahead == '{' && state->delimiter != '{') {
1899
+ *symbol = marker;
1900
+ return true;
1901
+ }
1902
+ return scan_regex_after_backslash(lexer, state, valid_symbols, symbol);
2144
1903
  }
1904
+ #endif
2145
1905
  }
2146
1906
 
2147
- const enum LiteralScanResult literal_result =
2148
- scan_regex_literal(lexer, state);
2149
- if (literal_result == LITERAL_SCAN_TOKEN) {
2150
- return emit_symbol(
2151
- valid_symbols,
2152
- inside_bracket ? REGEX_BRACKET_LITERAL : REGEX_LITERAL,
2153
- symbol
2154
- );
2155
- }
2156
- if (literal_result == LITERAL_SCAN_LINE_END) {
2157
- return emit_unterminated(
2158
- state,
2159
- valid_symbols,
2160
- regex_unterminated_symbol(state->mode, lexer->eof(lexer)),
2161
- symbol
2162
- );
1907
+ #if SED_REGEX_EXTENDED
1908
+ if (scan_empty_regex_construct(lexer, state, valid_symbols, symbol)) {
1909
+ return true;
2163
1910
  }
2164
-
2165
- if (state->regex_state == REGEX_OUTSIDE_BRACKET) {
2166
- if (lexer->lookahead == '^') {
2167
- consume(lexer);
2168
- #if !SED_REGEX_EXTENDED
2169
- if (
2170
- state->regex_group_depth >
2171
- 0 &&
2172
- state->regex_at_branch_start &&
2173
- !state->regex_after_anchor
2174
- ) {
2175
- return emit_symbol(
2176
- valid_symbols,
2177
- REGEX_BRE_SUBEXPRESSION_CARET,
2178
- symbol
2179
- );
2180
- }
2181
1911
  #endif
2182
- const bool is_beginning_anchor = SED_REGEX_EXTENDED ||
2183
- (state->regex_at_branch_start && !state->regex_after_anchor);
2184
- const TSSymbol candidate =
2185
- is_beginning_anchor ? REGEX_BEGINNING_ANCHOR : REGEX_LITERAL;
2186
- return emit_symbol(valid_symbols, candidate, symbol);
2187
- }
2188
1912
 
2189
- if (lexer->lookahead == '$') {
2190
- bool inspected_bre_escape = false;
2191
- consume(lexer);
2192
- #if !SED_REGEX_EXTENDED
2193
- if (lexer->lookahead == '\\') {
2194
- inspected_bre_escape = true;
2195
- advance(lexer);
2196
- if (state->regex_group_depth > 0 && lexer->lookahead == ')') {
2197
- return emit_symbol(
2198
- valid_symbols,
2199
- REGEX_BRE_SUBEXPRESSION_DOLLAR,
2200
- symbol
2201
- );
2202
- }
2203
- }
2204
- #endif
2205
- const bool at_branch_end = SED_REGEX_EXTENDED ||
2206
- (!inspected_bre_escape &&
2207
- (lexer->eof(lexer) ||
2208
- lexer->lookahead ==
2209
- '\n' ||
2210
- lexer->lookahead == state->delimiter));
2211
- const TSSymbol candidate =
2212
- at_branch_end ? REGEX_END_ANCHOR : REGEX_LITERAL;
2213
- return emit_symbol(valid_symbols, candidate, symbol);
2214
- }
1913
+ // The delimiter never reaches this point outside a bracket expression, so
1914
+ // the interval tokens compare only against their own characters.
1915
+ if (valid_symbols[REGEX_DUP_COUNT] && is_digit(lexer->lookahead)) {
1916
+ return scan_regex_dup_count(lexer, state, valid_symbols, symbol);
1917
+ }
2215
1918
 
2216
- if (lexer->lookahead == '.') {
2217
- consume(lexer);
2218
- return emit_symbol(valid_symbols, REGEX_PERIOD, symbol);
2219
- }
1919
+ if (valid_symbols[REGEX_INTERVAL_SEPARATOR] && lexer->lookahead == ',') {
1920
+ consume(lexer);
1921
+ *symbol = REGEX_INTERVAL_SEPARATOR;
1922
+ return true;
1923
+ }
2220
1924
 
2221
- if (lexer->lookahead == '\\') {
2222
- advance(lexer);
1925
+ #if SED_REGEX_EXTENDED
1926
+ if (valid_symbols[REGEX_INTERVAL_CLOSE] && lexer->lookahead == '}') {
1927
+ return scan_regex_interval_close(lexer, state, valid_symbols, symbol);
1928
+ }
1929
+ #else
1930
+ if (state->regex_in_interval && lexer->lookahead == '\\') {
1931
+ lexer->mark_end(lexer);
1932
+ advance(lexer);
1933
+ if (
1934
+ valid_symbols[REGEX_INTERVAL_CLOSE] &&
1935
+ lexer->lookahead ==
1936
+ '}' &&
1937
+ lexer->lookahead != state->delimiter
1938
+ ) {
1939
+ return scan_regex_interval_close(lexer, state, valid_symbols, symbol);
1940
+ }
1941
+ if (lexer->eof(lexer) && valid_symbols[REGEX_INCOMPLETE_INTERVAL]) {
2223
1942
  lexer->mark_end(lexer);
2224
- return scan_regex_escape_after_backslash(
2225
- lexer,
1943
+ return end_interval(
2226
1944
  state,
2227
1945
  valid_symbols,
1946
+ REGEX_INCOMPLETE_INTERVAL,
2228
1947
  symbol
2229
1948
  );
2230
1949
  }
2231
-
2232
- if (raw_regex_operator_character(lexer->lookahead)) {
2233
- return scan_raw_regex_operator(lexer, state, valid_symbols, symbol);
2234
- }
2235
-
2236
- if (lexer->lookahead == '[') {
2237
- consume(lexer);
2238
- reset_bracket_tracking(state);
2239
- state->regex_state = REGEX_BRACKET_FIRST;
2240
- return emit_symbol(valid_symbols, REGEX_BRACKET_OPEN, symbol);
1950
+ // The escape is the first unit of malformed content unless it ends the
1951
+ // content, in which case the issue is empty and the escape follows it.
1952
+ if (!interval_escape_ends_content(state, lexer->lookahead)) {
1953
+ finish_malformed_escape(lexer);
1954
+ scan_malformed_interval_content(lexer, state);
2241
1955
  }
2242
-
2243
- return false;
1956
+ return end_interval(state, valid_symbols, REGEX_INVALID_INTERVAL, symbol);
2244
1957
  }
1958
+ #endif
2245
1959
 
2246
- if (state->regex_state == REGEX_BRACKET_FIRST && lexer->lookahead == '^') {
2247
- consume(lexer);
2248
- state->regex_state = REGEX_BRACKET_AFTER_CARET;
2249
- return emit_symbol(valid_symbols, REGEX_BRACKET_NEGATION, symbol);
1960
+ if (state->regex_in_interval && valid_symbols[REGEX_INVALID_INTERVAL]) {
1961
+ lexer->mark_end(lexer);
1962
+ scan_malformed_interval_content(lexer, state);
1963
+ return end_interval(state, valid_symbols, REGEX_INVALID_INTERVAL, symbol);
2250
1964
  }
2251
1965
 
2252
- if (state->regex_state == REGEX_BRACKET_BODY && lexer->lookahead == ']') {
2253
- commit_pending_bracket_element(state);
1966
+ const bool inside_bracket = regex_is_inside_bracket(state);
1967
+ if (inside_bracket) {
2254
1968
  if (
2255
- bracket_expression_is_ambiguous(state) &&
2256
- valid_symbols[AMBIGUOUS_BRACKET_EXPRESSION_MARKER]
1969
+ valid_symbols[REGEX_CLASS_NAME] ||
1970
+ valid_symbols[REGEX_COLL_ELEM_SINGLE] ||
1971
+ valid_symbols[REGEX_COLL_ELEM_MULTI] ||
1972
+ valid_symbols[REGEX_META_CHAR]
2257
1973
  ) {
2258
- lexer->mark_end(lexer);
2259
- *symbol = AMBIGUOUS_BRACKET_EXPRESSION_MARKER;
2260
- return true;
1974
+ return scan_regex_bracket_term_content(
1975
+ lexer,
1976
+ state,
1977
+ valid_symbols,
1978
+ symbol
1979
+ );
2261
1980
  }
2262
-
2263
- consume(lexer);
2264
- state->regex_state = REGEX_OUTSIDE_BRACKET;
2265
- reset_bracket_tracking(state);
2266
- return emit_symbol(valid_symbols, REGEX_BRACKET_CLOSE, symbol);
2267
- }
2268
-
2269
- if (state->regex_state == REGEX_BRACKET_BODY && lexer->lookahead == '-') {
2270
- lexer->mark_end(lexer);
2271
- advance(lexer);
2272
1981
  if (
2273
- valid_symbols[NONPORTABLE_RANGE_START_MARKER] &&
2274
- !lexer->eof(lexer) &&
2275
- lexer->lookahead !=
2276
- '\n' &&
2277
- lexer->lookahead != ']'
1982
+ state->regex_bracket_term_state !=
1983
+ REGEX_BRACKET_TERM_NONE &&
1984
+ lexer->lookahead ==
1985
+ ']' &&
1986
+ valid_symbols[REGEX_MALFORMED_BRACKET_TERM]
2278
1987
  ) {
2279
- *symbol = NONPORTABLE_RANGE_START_MARKER;
1988
+ lexer->mark_end(lexer);
1989
+ state->regex_bracket_term_state = REGEX_BRACKET_TERM_NONE;
1990
+ finish_compound_bracket_element(state);
1991
+ *symbol = REGEX_MALFORMED_BRACKET_TERM;
2280
1992
  return true;
2281
1993
  }
2282
- lexer->mark_end(lexer);
2283
-
2284
- TSSymbol candidate;
2285
- if (valid_symbols[REGEX_BRACKET_RANGE_END_HYPHEN]) {
2286
- candidate = REGEX_BRACKET_RANGE_END_HYPHEN;
2287
- } else if (
2288
- lexer->eof(lexer) || lexer->lookahead == '\n' || lexer->lookahead == ']'
1994
+ if (
1995
+ state->regex_bracket_term_state !=
1996
+ REGEX_BRACKET_TERM_NONE &&
1997
+ valid_symbols[bracket_terms[state->regex_bracket_term_state]
1998
+ .close_symbol] &&
1999
+ lexer->lookahead == bracket_terms[state->regex_bracket_term_state].marker
2289
2000
  ) {
2290
- candidate = REGEX_BRACKET_TRAILING_HYPHEN;
2291
- } else {
2292
- candidate = REGEX_BRACKET_HYPHEN;
2293
- }
2294
- if (emit_symbol(valid_symbols, candidate, symbol)) {
2295
- if (candidate == REGEX_BRACKET_RANGE_END_HYPHEN) {
2296
- state->regex_bracket_pending_element = REGEX_BRACKET_PENDING_NONE;
2297
- state->regex_bracket_range_pending = false;
2298
- record_bracket_element(state, REGEX_BRACKET_PENDING_OTHER);
2299
- } else if (candidate == REGEX_BRACKET_TRAILING_HYPHEN) {
2300
- commit_pending_bracket_element(state);
2301
- record_bracket_element(state, REGEX_BRACKET_PENDING_OTHER);
2302
- } else {
2303
- state->regex_bracket_pending_element = REGEX_BRACKET_PENDING_NONE;
2304
- state->regex_bracket_range_pending = true;
2305
- }
2306
- return true;
2307
- }
2308
- if (emit_symbol(valid_symbols, REGEX_SHARED_RANGE_ENDPOINT, symbol)) {
2309
- commit_pending_bracket_element(state);
2310
- state->regex_bracket_range_pending = false;
2311
- return true;
2001
+ return scan_regex_bracket_term_close(lexer, state, valid_symbols, symbol);
2312
2002
  }
2313
- return false;
2314
2003
  }
2315
2004
 
2316
- if (lexer->lookahead == '[') {
2317
- return scan_regex_bracket_opener(lexer, state, valid_symbols, symbol);
2005
+ const enum LiteralScanResult literal_result =
2006
+ scan_regex_literal(lexer, state);
2007
+ if (literal_result == LITERAL_SCAN_TOKEN) {
2008
+ return emit_symbol(
2009
+ valid_symbols,
2010
+ inside_bracket ? REGEX_BRACKET_LITERAL : REGEX_LITERAL,
2011
+ symbol
2012
+ );
2013
+ }
2014
+ if (literal_result == LITERAL_SCAN_LINE_END) {
2015
+ return end_mode(
2016
+ state,
2017
+ valid_symbols,
2018
+ unterminated_mode_symbol(state->mode, lexer->eof(lexer)),
2019
+ symbol
2020
+ );
2318
2021
  }
2319
2022
 
2320
- return false;
2023
+ if (state->regex_state == REGEX_OUTSIDE_BRACKET) {
2024
+ return scan_regex_special_token(lexer, state, valid_symbols, symbol);
2025
+ }
2026
+ return scan_regex_bracket_special_token(lexer, state, valid_symbols, symbol);
2321
2027
  }
2322
2028
 
2323
2029
  static enum LiteralScanResult scan_operand_literal(
@@ -2354,16 +2060,19 @@ static bool scan_replacement_escape(
2354
2060
  const bool *valid_symbols,
2355
2061
  TSSymbol *symbol
2356
2062
  ) {
2357
- advance(lexer);
2358
- lexer->mark_end(lexer);
2063
+ consume(lexer);
2359
2064
 
2360
2065
  if (lexer->eof(lexer)) {
2361
2066
  return emit_symbol(valid_symbols, REPLACEMENT_INCOMPLETE_ESCAPE, symbol);
2362
2067
  }
2363
2068
 
2364
2069
  if (lexer->lookahead == '\n') {
2365
- consume(lexer);
2366
- return emit_symbol(valid_symbols, REPLACEMENT_ESCAPED_NEWLINE, symbol);
2070
+ return consume_as(
2071
+ lexer,
2072
+ valid_symbols,
2073
+ REPLACEMENT_ESCAPED_NEWLINE,
2074
+ symbol
2075
+ );
2367
2076
  }
2368
2077
 
2369
2078
  TSSymbol candidate = REPLACEMENT_ESCAPE_SEQUENCE;
@@ -2376,8 +2085,12 @@ static bool scan_replacement_escape(
2376
2085
  '\\' &&
2377
2086
  !is_digit(lexer->lookahead)
2378
2087
  ) {
2379
- consume(lexer);
2380
- return emit_symbol(valid_symbols, REPLACEMENT_NONPORTABLE_ESCAPE, symbol);
2088
+ return consume_as(
2089
+ lexer,
2090
+ valid_symbols,
2091
+ REPLACEMENT_NONPORTABLE_ESCAPE,
2092
+ symbol
2093
+ );
2381
2094
  }
2382
2095
  if (lexer->lookahead == state->delimiter) {
2383
2096
  candidate = state->delimiter == '&'
@@ -2387,51 +2100,7 @@ static bool scan_replacement_escape(
2387
2100
  candidate = REPLACEMENT_BACKREFERENCE;
2388
2101
  }
2389
2102
 
2390
- consume(lexer);
2391
- return emit_symbol(valid_symbols, candidate, symbol);
2392
- }
2393
-
2394
- static bool scan_replacement_token(
2395
- TSLexer *lexer,
2396
- ScannerState *state,
2397
- const bool *valid_symbols,
2398
- TSSymbol *symbol
2399
- ) {
2400
- const enum LiteralScanResult literal_result =
2401
- scan_operand_literal(lexer, state, true);
2402
- if (literal_result == LITERAL_SCAN_TOKEN) {
2403
- return emit_symbol(valid_symbols, REPLACEMENT_LITERAL, symbol);
2404
- }
2405
- if (literal_result == LITERAL_SCAN_LINE_END) {
2406
- return emit_unterminated(
2407
- state,
2408
- valid_symbols,
2409
- lexer->eof(lexer) ? REPLACEMENT_UNTERMINATED
2410
- : REPLACEMENT_LINE_UNTERMINATED,
2411
- symbol
2412
- );
2413
- }
2414
-
2415
- if (lexer->lookahead == '\\') {
2416
- return scan_replacement_escape(lexer, state, valid_symbols, symbol);
2417
- }
2418
-
2419
- if (lexer->lookahead == '&') {
2420
- consume(lexer);
2421
- return emit_symbol(valid_symbols, REPLACEMENT_MATCH_REFERENCE, symbol);
2422
- }
2423
-
2424
- return false;
2425
- }
2426
-
2427
- static TSSymbol
2428
- translate_unterminated_symbol(enum ScannerMode mode, bool at_end_of_source) {
2429
- if (mode == MODE_TRANSLATE_SOURCE) {
2430
- return at_end_of_source ? TRANSLATE_UNTERMINATED_SOURCE
2431
- : TRANSLATE_LINE_UNTERMINATED_SOURCE;
2432
- }
2433
- return at_end_of_source ? TRANSLATE_UNTERMINATED_DESTINATION
2434
- : TRANSLATE_LINE_UNTERMINATED_DESTINATION;
2103
+ return consume_as(lexer, valid_symbols, candidate, symbol);
2435
2104
  }
2436
2105
 
2437
2106
  static bool scan_translate_escape(
@@ -2440,8 +2109,7 @@ static bool scan_translate_escape(
2440
2109
  const bool *valid_symbols,
2441
2110
  TSSymbol *symbol
2442
2111
  ) {
2443
- advance(lexer);
2444
- lexer->mark_end(lexer);
2112
+ consume(lexer);
2445
2113
 
2446
2114
  if (lexer->eof(lexer)) {
2447
2115
  return emit_symbol(valid_symbols, TRANSLATE_INCOMPLETE_ESCAPE, symbol);
@@ -2451,45 +2119,56 @@ static bool scan_translate_escape(
2451
2119
  return emit_symbol(valid_symbols, TRANSLATE_NONPORTABLE_ESCAPE, symbol);
2452
2120
  }
2453
2121
 
2454
- if (
2455
- lexer->lookahead !=
2456
- 'n' &&
2457
- lexer->lookahead !=
2458
- '\\' &&
2459
- (lexer->lookahead != state->delimiter || state->delimiter == 'n')
2460
- ) {
2461
- consume(lexer);
2462
- return emit_symbol(valid_symbols, TRANSLATE_NONPORTABLE_ESCAPE, symbol);
2122
+ // "\n" is a newline even when 'n' is the delimiter.
2123
+ TSSymbol candidate;
2124
+ if (lexer->lookahead == 'n' || lexer->lookahead == '\\') {
2125
+ candidate = TRANSLATE_ESCAPE;
2126
+ } else if (lexer->lookahead == state->delimiter) {
2127
+ candidate = TRANSLATE_ESCAPED_DELIMITER;
2128
+ } else {
2129
+ candidate = TRANSLATE_NONPORTABLE_ESCAPE;
2463
2130
  }
2464
- const TSSymbol candidate = lexer->lookahead == 'n' || lexer->lookahead == '\\'
2465
- ? TRANSLATE_ESCAPE
2466
- : TRANSLATE_ESCAPED_DELIMITER;
2467
- consume(lexer);
2468
- return emit_symbol(valid_symbols, candidate, symbol);
2131
+ return consume_as(lexer, valid_symbols, candidate, symbol);
2469
2132
  }
2470
2133
 
2471
- static bool scan_translate_token(
2134
+ static bool scan_operand_token(
2472
2135
  TSLexer *lexer,
2473
2136
  ScannerState *state,
2474
2137
  const bool *valid_symbols,
2475
2138
  TSSymbol *symbol
2476
2139
  ) {
2140
+ const bool is_replacement = state->mode == MODE_SUBSTITUTE_REPLACEMENT;
2477
2141
  const enum LiteralScanResult literal_result =
2478
- scan_operand_literal(lexer, state, false);
2142
+ scan_operand_literal(lexer, state, is_replacement);
2479
2143
  if (literal_result == LITERAL_SCAN_TOKEN) {
2480
- return emit_symbol(valid_symbols, TRANSLATE_LITERAL, symbol);
2144
+ return emit_symbol(
2145
+ valid_symbols,
2146
+ is_replacement ? REPLACEMENT_LITERAL : TRANSLATE_LITERAL,
2147
+ symbol
2148
+ );
2481
2149
  }
2482
2150
  if (literal_result == LITERAL_SCAN_LINE_END) {
2483
- return emit_unterminated(
2151
+ return end_mode(
2484
2152
  state,
2485
2153
  valid_symbols,
2486
- translate_unterminated_symbol(state->mode, lexer->eof(lexer)),
2154
+ unterminated_mode_symbol(state->mode, lexer->eof(lexer)),
2487
2155
  symbol
2488
2156
  );
2489
2157
  }
2490
2158
 
2491
2159
  if (lexer->lookahead == '\\') {
2492
- return scan_translate_escape(lexer, state, valid_symbols, symbol);
2160
+ return is_replacement
2161
+ ? scan_replacement_escape(lexer, state, valid_symbols, symbol)
2162
+ : scan_translate_escape(lexer, state, valid_symbols, symbol);
2163
+ }
2164
+
2165
+ if (is_replacement && lexer->lookahead == '&') {
2166
+ return consume_as(
2167
+ lexer,
2168
+ valid_symbols,
2169
+ REPLACEMENT_MATCH_REFERENCE,
2170
+ symbol
2171
+ );
2493
2172
  }
2494
2173
 
2495
2174
  return false;
@@ -2503,19 +2182,19 @@ static bool scan_regex_recovery_marker(
2503
2182
  ) {
2504
2183
  #if !SED_REGEX_EXTENDED
2505
2184
  if (
2506
- emit_missing_marker(
2185
+ emit_marker(
2507
2186
  lexer,
2508
2187
  valid_symbols,
2509
2188
  BRE_VERTICAL_LINE_ESCAPE_MARKER,
2510
2189
  symbol
2511
2190
  ) ||
2512
- emit_missing_marker(
2191
+ emit_marker(
2513
2192
  lexer,
2514
2193
  valid_symbols,
2515
2194
  BRE_QUESTION_MARK_ESCAPE_MARKER,
2516
2195
  symbol
2517
2196
  ) ||
2518
- emit_missing_marker(lexer, valid_symbols, BRE_PLUS_ESCAPE_MARKER, symbol)
2197
+ emit_marker(lexer, valid_symbols, BRE_PLUS_ESCAPE_MARKER, symbol)
2519
2198
  ) {
2520
2199
  return true;
2521
2200
  }
@@ -2532,7 +2211,7 @@ static bool scan_regex_recovery_marker(
2532
2211
  0 &&
2533
2212
  !state->regex_after_alternation &&
2534
2213
  at_regex_end &&
2535
- emit_missing_marker(
2214
+ emit_marker(
2536
2215
  lexer,
2537
2216
  valid_symbols,
2538
2217
  lexer->eof(lexer) ? MISSING_SUBEXPRESSION_MARKER
@@ -2548,7 +2227,7 @@ static bool scan_regex_recovery_marker(
2548
2227
  REGEX_BRACKET_FIRST ||
2549
2228
  state->regex_state == REGEX_BRACKET_AFTER_CARET) &&
2550
2229
  at_line_boundary &&
2551
- emit_missing_marker(
2230
+ emit_marker(
2552
2231
  lexer,
2553
2232
  valid_symbols,
2554
2233
  lexer->eof(lexer) ? INCOMPLETE_BRACKET_LIST_MARKER
@@ -2563,7 +2242,7 @@ static bool scan_regex_recovery_marker(
2563
2242
  state->regex_bracket_term_state !=
2564
2243
  REGEX_BRACKET_TERM_NONE &&
2565
2244
  at_line_boundary &&
2566
- emit_missing_marker(
2245
+ emit_marker(
2567
2246
  lexer,
2568
2247
  valid_symbols,
2569
2248
  lexer->eof(lexer) ? REGEX_INCOMPLETE_BRACKET_TERM
@@ -2579,7 +2258,7 @@ static bool scan_regex_recovery_marker(
2579
2258
  if (
2580
2259
  regex_is_inside_bracket(state) &&
2581
2260
  at_line_boundary &&
2582
- emit_missing_marker(
2261
+ emit_marker(
2583
2262
  lexer,
2584
2263
  valid_symbols,
2585
2264
  lexer->eof(lexer) ? INCOMPLETE_BRACKET_EXPRESSION_MARKER
@@ -2598,7 +2277,7 @@ static bool scan_regex_recovery_marker(
2598
2277
  REGEX_OUTSIDE_BRACKET &&
2599
2278
  state->regex_after_alternation &&
2600
2279
  at_regex_end &&
2601
- emit_missing_marker(
2280
+ emit_marker(
2602
2281
  lexer,
2603
2282
  valid_symbols,
2604
2283
  lexer->eof(lexer) ? INCOMPLETE_ALTERNATIVE_MARKER
@@ -2612,21 +2291,16 @@ static bool scan_regex_recovery_marker(
2612
2291
  #endif
2613
2292
 
2614
2293
  if (
2615
- state->regex_interval_state !=
2616
- REGEX_INTERVAL_NONE &&
2617
- (at_regex_end
2618
- #if !SED_REGEX_EXTENDED
2619
- || (state->delimiter == '}' && lexer->lookahead == '\\')
2620
- #endif
2621
- ) &&
2622
- emit_missing_marker(
2294
+ state->regex_in_interval &&
2295
+ at_regex_end &&
2296
+ emit_marker(
2623
2297
  lexer,
2624
2298
  valid_symbols,
2625
2299
  lexer->eof(lexer) ? REGEX_INCOMPLETE_INTERVAL : REGEX_INVALID_INTERVAL,
2626
2300
  symbol
2627
2301
  )
2628
2302
  ) {
2629
- state->regex_interval_state = REGEX_INTERVAL_NONE;
2303
+ state->regex_in_interval = false;
2630
2304
  return true;
2631
2305
  }
2632
2306
 
@@ -2636,7 +2310,7 @@ static bool scan_regex_recovery_marker(
2636
2310
  state->regex_group_depth >
2637
2311
  0 &&
2638
2312
  at_line_boundary &&
2639
- emit_missing_marker(
2313
+ emit_marker(
2640
2314
  lexer,
2641
2315
  valid_symbols,
2642
2316
  lexer->eof(lexer) ? REGEX_INCOMPLETE_GROUP : REGEX_UNCLOSED_GROUP,
@@ -2655,60 +2329,86 @@ static TSSymbol missing_text_introducer_symbol(const TSLexer *lexer) {
2655
2329
  : NONCONFORMING_MISSING_TEXT_INTRODUCER_MARKER;
2656
2330
  }
2657
2331
 
2332
+ // Blanks before a closing brace are padding of the brace token, or of the
2333
+ // missing-brace marker when the source ends after them. Any other
2334
+ // continuation leaves the blank run to the internal lexer.
2335
+ static bool
2336
+ scan_right_brace(TSLexer *lexer, const bool *valid_symbols, TSSymbol *symbol) {
2337
+ skip_blanks(lexer);
2338
+
2339
+ if (lexer->lookahead == '}') {
2340
+ consume(lexer);
2341
+ *symbol = RIGHT_BRACE;
2342
+ return true;
2343
+ }
2344
+ if (lexer->eof(lexer)) {
2345
+ return emit_marker(
2346
+ lexer,
2347
+ valid_symbols,
2348
+ MISSING_CLOSING_BRACE_MARKER,
2349
+ symbol
2350
+ );
2351
+ }
2352
+ return false;
2353
+ }
2354
+
2658
2355
  static bool scan_command_token(
2659
2356
  TSLexer *lexer,
2660
2357
  ScannerState *state,
2661
2358
  const bool *valid_symbols,
2662
2359
  TSSymbol *symbol
2663
2360
  ) {
2664
- if (valid_symbols[LINE_WORD] && scan_to_physical_line_end(lexer, false)) {
2361
+ if (
2362
+ valid_symbols[EMPTY_COMMAND_MARKER] &&
2363
+ (lexer->lookahead == ';' || lexer->lookahead == '\n')
2364
+ ) {
2365
+ return emit_marker(lexer, valid_symbols, EMPTY_COMMAND_MARKER, symbol);
2366
+ }
2367
+
2368
+ if (valid_symbols[LINE_WORD] && scan_line_rest(lexer, false)) {
2665
2369
  *symbol = LINE_WORD;
2666
2370
  return true;
2667
2371
  }
2668
2372
 
2373
+ // The label separator of b and t is exactly one space. It is read before
2374
+ // the post-blank recovery markers, which would otherwise claim the blank in
2375
+ // front of a label that starts with a closing brace.
2376
+ if (valid_symbols[ARGUMENT_SEPARATOR] && lexer->lookahead == ' ') {
2377
+ consume(lexer);
2378
+ *symbol = ARGUMENT_SEPARATOR;
2379
+ return true;
2380
+ }
2381
+
2669
2382
  if (valid_symbols[FLAG_AFTER_WRITE_MARKER]) {
2670
- const enum FlagAfterWriteScan scan = scan_flag_after_write_marker(lexer);
2671
- if (scan == FLAG_AFTER_WRITE_MATCH) {
2383
+ switch (scan_flag_after_write_marker(lexer)) {
2384
+ case FLAG_AFTER_WRITE_MATCH:
2672
2385
  *symbol = FLAG_AFTER_WRITE_MARKER;
2673
2386
  return true;
2674
- }
2675
- if (
2676
- scan ==
2677
- FLAG_AFTER_WRITE_FALLBACK &&
2678
- valid_symbols[OMITTED_FILE_SEPARATOR_MARKER]
2679
- ) {
2680
- *symbol = OMITTED_FILE_SEPARATOR_MARKER;
2681
- return true;
2682
- }
2683
- if (scan == FLAG_AFTER_WRITE_FALLBACK) {
2684
- return false;
2387
+ case FLAG_AFTER_WRITE_FALLBACK:
2388
+ return emit_symbol(valid_symbols, OMITTED_FILE_SEPARATOR_MARKER, symbol);
2389
+ case FLAG_AFTER_WRITE_NONE:
2390
+ break;
2685
2391
  }
2686
2392
  }
2687
2393
 
2688
2394
  if (
2689
2395
  valid_symbols[SUBSTITUTION_WFILE_ARGUMENT] &&
2690
- scan_substitution_wfile_argument(lexer)
2396
+ scan_file_argument(lexer, true)
2691
2397
  ) {
2692
2398
  *symbol = SUBSTITUTION_WFILE_ARGUMENT;
2693
2399
  return true;
2694
2400
  }
2695
2401
 
2696
- if (valid_symbols[FILE_ARGUMENT] && scan_file_argument(lexer)) {
2402
+ if (valid_symbols[FILE_ARGUMENT] && scan_file_argument(lexer, false)) {
2697
2403
  *symbol = FILE_ARGUMENT;
2698
2404
  return true;
2699
2405
  }
2700
2406
 
2701
2407
  if (
2702
2408
  valid_symbols[INVALID_SUBSTITUTION_FLAG] &&
2409
+ !at_command_boundary(lexer) &&
2703
2410
  !is_blank(lexer->lookahead) &&
2704
- !is_substitution_flag_character(lexer->lookahead) &&
2705
- lexer->lookahead !=
2706
- ';' &&
2707
- lexer->lookahead !=
2708
- '}' &&
2709
- lexer->lookahead !=
2710
- '\n' &&
2711
- !lexer->eof(lexer)
2411
+ !is_substitution_flag_character(lexer->lookahead)
2712
2412
  ) {
2713
2413
  consume(lexer);
2714
2414
  *symbol = INVALID_SUBSTITUTION_FLAG;
@@ -2720,9 +2420,13 @@ static bool scan_command_token(
2720
2420
  *symbol = TEXT_COMMAND_START;
2721
2421
  return true;
2722
2422
  }
2423
+ if (lexer->eof(lexer)) {
2424
+ lexer->mark_end(lexer);
2425
+ return emit_symbol(valid_symbols, TEXT_INCOMPLETE_INTRODUCER, symbol);
2426
+ }
2723
2427
  return emit_symbol(
2724
2428
  valid_symbols,
2725
- missing_text_introducer_symbol(lexer),
2429
+ NONCONFORMING_MISSING_TEXT_INTRODUCER_MARKER,
2726
2430
  symbol
2727
2431
  );
2728
2432
  }
@@ -2733,77 +2437,64 @@ static bool scan_command_token(
2733
2437
  return true;
2734
2438
  }
2735
2439
 
2736
- if (valid_symbols[COMMENT_TEXT] && scan_to_physical_line_end(lexer, false)) {
2440
+ if (valid_symbols[COMMENT_TEXT] && scan_line_rest(lexer, false)) {
2737
2441
  *symbol = COMMENT_TEXT;
2738
2442
  return true;
2739
2443
  }
2740
2444
 
2741
2445
  // Preserve an address interpretation before reserved-function recovery.
2742
2446
  if (
2743
- valid_symbols[MISSING_ADDRESS_SEPARATOR_MARKER] && can_start_address(lexer)
2447
+ can_start_address(lexer) &&
2448
+ emit_marker(lexer, valid_symbols, MISSING_ADDRESS_SEPARATOR_MARKER, symbol)
2744
2449
  ) {
2745
- return emit_missing_marker(
2746
- lexer,
2747
- valid_symbols,
2748
- MISSING_ADDRESS_SEPARATOR_MARKER,
2749
- symbol
2750
- );
2450
+ return true;
2751
2451
  }
2752
2452
 
2753
2453
  if (
2754
- valid_symbols[RESERVED_UNKNOWN_FUNCTION_TOKEN] &&
2755
- scan_reserved_unknown_function(lexer)
2454
+ valid_symbols[RESERVED_UNKNOWN_FUNCTION_TOKEN] && can_start_address(lexer)
2756
2455
  ) {
2456
+ consume(lexer);
2757
2457
  *symbol = RESERVED_UNKNOWN_FUNCTION_TOKEN;
2758
2458
  return true;
2759
2459
  }
2760
2460
 
2761
- if (
2762
- valid_symbols[REGEX_ADDRESS_START] &&
2763
- lexer->lookahead ==
2764
- '/' &&
2765
- scan_simple_delimiter(lexer, state, MODE_REGEX_ADDRESS)
2461
+ static const struct {
2462
+ TSSymbol symbol;
2463
+ enum ScannerMode mode;
2464
+ int32_t required_delimiter;
2465
+ } delimited_openers[] = {
2466
+ {REGEX_ADDRESS_START, MODE_REGEX_ADDRESS, '/'},
2467
+ {ESCAPED_REGEX_ADDRESS_START, MODE_REGEX_ADDRESS, 0},
2468
+ {SUBSTITUTE_START, MODE_SUBSTITUTE_PATTERN, 0},
2469
+ {TRANSLATE_START, MODE_TRANSLATE_SOURCE, 0},
2470
+ };
2471
+ for (
2472
+ unsigned index = 0;
2473
+ index < sizeof(delimited_openers) / sizeof(delimited_openers[0]);
2474
+ index++
2766
2475
  ) {
2767
- *symbol = REGEX_ADDRESS_START;
2768
- return true;
2769
- }
2770
-
2771
- if (valid_symbols[ESCAPED_REGEX_ADDRESS_START]) {
2772
- if (scan_simple_delimiter(lexer, state, MODE_REGEX_ADDRESS)) {
2773
- *symbol = ESCAPED_REGEX_ADDRESS_START;
2774
- return true;
2775
- }
2776
- }
2777
-
2778
- if (valid_symbols[SUBSTITUTE_START]) {
2779
- if (scan_simple_delimiter(lexer, state, MODE_SUBSTITUTE_PATTERN)) {
2780
- *symbol = SUBSTITUTE_START;
2781
- return true;
2782
- }
2783
- }
2784
-
2785
- if (valid_symbols[TRANSLATE_START]) {
2786
- if (scan_simple_delimiter(lexer, state, MODE_TRANSLATE_SOURCE)) {
2787
- *symbol = TRANSLATE_START;
2476
+ const int32_t required = delimited_openers[index].required_delimiter;
2477
+ if (
2478
+ valid_symbols[delimited_openers[index].symbol] &&
2479
+ (required == 0 || lexer->lookahead == required) &&
2480
+ scan_simple_delimiter(lexer, state, delimited_openers[index].mode)
2481
+ ) {
2482
+ *symbol = delimited_openers[index].symbol;
2788
2483
  return true;
2789
2484
  }
2790
2485
  }
2791
2486
 
2792
- if (valid_symbols[RIGHT_BRACE] && scan_right_brace(lexer, state)) {
2793
- *symbol = RIGHT_BRACE;
2794
- return true;
2487
+ if (
2488
+ valid_symbols[RIGHT_BRACE] &&
2489
+ (is_blank(lexer->lookahead) || lexer->lookahead == '}')
2490
+ ) {
2491
+ return scan_right_brace(lexer, valid_symbols, symbol);
2795
2492
  }
2796
2493
 
2797
2494
  if (
2798
- valid_symbols[OMITTED_FIRST_ADDRESS_MARKER] &&
2799
2495
  lexer->lookahead ==
2800
2496
  ',' &&
2801
- emit_missing_marker(
2802
- lexer,
2803
- valid_symbols,
2804
- OMITTED_FIRST_ADDRESS_MARKER,
2805
- symbol
2806
- )
2497
+ emit_marker(lexer, valid_symbols, OMITTED_FIRST_ADDRESS_MARKER, symbol)
2807
2498
  ) {
2808
2499
  return true;
2809
2500
  }
@@ -2814,7 +2505,7 @@ static bool scan_command_token(
2814
2505
  !can_start_address(lexer) &&
2815
2506
  !is_blank(lexer->lookahead)
2816
2507
  ) {
2817
- return emit_missing_marker(
2508
+ return emit_marker(
2818
2509
  lexer,
2819
2510
  valid_symbols,
2820
2511
  lexer->eof(lexer) ? INCOMPLETE_OMITTED_ADDRESS_MARKER
@@ -2837,13 +2528,7 @@ static bool scan_command_token(
2837
2528
  if (
2838
2529
  !at_command_boundary(lexer) &&
2839
2530
  !is_blank(lexer->lookahead) &&
2840
- valid_symbols[OMITTED_FILE_SEPARATOR_MARKER] &&
2841
- emit_missing_marker(
2842
- lexer,
2843
- valid_symbols,
2844
- OMITTED_FILE_SEPARATOR_MARKER,
2845
- symbol
2846
- )
2531
+ emit_marker(lexer, valid_symbols, OMITTED_FILE_SEPARATOR_MARKER, symbol)
2847
2532
  ) {
2848
2533
  return true;
2849
2534
  }
@@ -2862,7 +2547,7 @@ static bool scan_command_token(
2862
2547
  index++
2863
2548
  ) {
2864
2549
  if (
2865
- emit_missing_marker(
2550
+ emit_marker(
2866
2551
  lexer,
2867
2552
  valid_symbols,
2868
2553
  boundary_markers[index][variant],
@@ -2875,7 +2560,7 @@ static bool scan_command_token(
2875
2560
  }
2876
2561
 
2877
2562
  if (
2878
- emit_missing_marker(
2563
+ emit_marker(
2879
2564
  lexer,
2880
2565
  valid_symbols,
2881
2566
  missing_text_introducer_symbol(lexer),
@@ -2885,19 +2570,17 @@ static bool scan_command_token(
2885
2570
  return true;
2886
2571
  }
2887
2572
 
2888
- if (valid_symbols[MISSING_CLOSING_BRACE_MARKER] && lexer->eof(lexer)) {
2889
- return emit_missing_marker(
2890
- lexer,
2891
- valid_symbols,
2892
- MISSING_CLOSING_BRACE_MARKER,
2893
- symbol
2894
- );
2573
+ if (
2574
+ lexer->eof(lexer) &&
2575
+ emit_marker(lexer, valid_symbols, MISSING_CLOSING_BRACE_MARKER, symbol)
2576
+ ) {
2577
+ return true;
2895
2578
  }
2896
2579
 
2897
2580
  if (
2898
2581
  !at_command_boundary(lexer) &&
2899
2582
  !is_blank(lexer->lookahead) &&
2900
- emit_missing_marker(
2583
+ emit_marker(
2901
2584
  lexer,
2902
2585
  valid_symbols,
2903
2586
  MISSING_SEPARATOR_AFTER_UNMATCHED_BRACE_MARKER,
@@ -2907,38 +2590,10 @@ static bool scan_command_token(
2907
2590
  return true;
2908
2591
  }
2909
2592
 
2910
- if (lexer->lookahead == '}') {
2911
- if (
2912
- emit_missing_marker(
2913
- lexer,
2914
- valid_symbols,
2915
- MISSING_SEPARATOR_BEFORE_UNMATCHED_BRACE_MARKER,
2916
- symbol
2917
- )
2918
- ) {
2919
- return true;
2920
- }
2921
- if (
2922
- emit_missing_marker(
2923
- lexer,
2924
- valid_symbols,
2925
- MISSING_COMMAND_SEPARATOR_MARKER,
2926
- symbol
2927
- )
2928
- ) {
2929
- return true;
2930
- }
2931
- } else if (lexer->eof(lexer)) {
2932
- if (
2933
- emit_missing_marker(
2934
- lexer,
2935
- valid_symbols,
2936
- INCOMPLETE_COMMAND_SEPARATOR_MARKER,
2937
- symbol
2938
- )
2939
- ) {
2940
- return true;
2941
- }
2593
+ const TSSymbol separator_marker =
2594
+ missing_command_separator_marker(lexer, valid_symbols);
2595
+ if (separator_marker != ERROR_SENTINEL) {
2596
+ return emit_marker(lexer, valid_symbols, separator_marker, symbol);
2942
2597
  }
2943
2598
 
2944
2599
  const TSSymbol missing_opening_delimiter = lexer->eof(lexer)
@@ -2946,7 +2601,7 @@ static bool scan_command_token(
2946
2601
  : NONCONFORMING_MISSING_OPENING_DELIMITER_MARKER;
2947
2602
  if (
2948
2603
  delimiter_is_missing(lexer) &&
2949
- emit_missing_marker(lexer, valid_symbols, missing_opening_delimiter, symbol)
2604
+ emit_marker(lexer, valid_symbols, missing_opening_delimiter, symbol)
2950
2605
  ) {
2951
2606
  return true;
2952
2607
  }
@@ -2995,10 +2650,9 @@ static bool sed_scanner_scan_impl(
2995
2650
  case MODE_SUBSTITUTE_PATTERN:
2996
2651
  return scan_regex_token(lexer, state, valid_symbols, symbol);
2997
2652
  case MODE_SUBSTITUTE_REPLACEMENT:
2998
- return scan_replacement_token(lexer, state, valid_symbols, symbol);
2999
2653
  case MODE_TRANSLATE_SOURCE:
3000
2654
  case MODE_TRANSLATE_DESTINATION:
3001
- return scan_translate_token(lexer, state, valid_symbols, symbol);
2655
+ return scan_operand_token(lexer, state, valid_symbols, symbol);
3002
2656
  case MODE_TEXT:
3003
2657
  return false;
3004
2658
  case MODE_NONE: