@konomanoasa/tree-sitter-yaml 0.1.0 → 0.3.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/src/scanner.c CHANGED
@@ -38,6 +38,7 @@ enum Token {
38
38
  IMPLICIT_KEY_START,
39
39
  KEY_END,
40
40
  INVALID_KEY_END,
41
+ UNFINISHED_INVALID_KEY_END,
41
42
  FLOW_NODE_END,
42
43
  PLAIN_START,
43
44
  PLAIN_END,
@@ -86,6 +87,9 @@ enum Token {
86
87
  UNEXPECTED_CONTENT_START,
87
88
  PROPERTIES_START,
88
89
  PROPERTIES_END,
90
+ BLOCK_PROPERTIES_END,
91
+ EMPTY_PROPERTIES_END,
92
+ PROPERTY_CONTINUE,
89
93
  PROPERTY_CONTENT_START,
90
94
  BLOCK_PROPERTY_CONTENT_START,
91
95
  BLOCK_KEY_CONTENT_START,
@@ -128,20 +132,30 @@ enum Token {
128
132
  TAG_PREFIX_START,
129
133
  TAG_PREFIX_END,
130
134
  DIRECTIVE_PARAMETER,
135
+ UNFINISHED_UNEXPECTED_DOCUMENT_CONTENT_START,
136
+ UNFINISHED_INVALID_DOCUMENT_COLLECTION_START,
137
+ UNFINISHED_DUPLICATE_ANCHOR_START,
138
+ UNFINISHED_DUPLICATE_TAG_START,
139
+ UNFINISHED_PROPERTIES_ON_ALIAS_START,
140
+ UNFINISHED_INVALID_PROPERTY_COLLECTION_START,
141
+ UNFINISHED_INVALID_COMPACT_COLLECTION_START,
131
142
  INVALID_ENCODING,
132
143
  INVALID_CHARACTER,
133
144
  INVALID_ESCAPE,
134
145
  INCOMPLETE_ESCAPE,
135
146
  INVALID_LINE_CONTINUATION,
136
147
  INVALID_SCALAR_START,
148
+ INCOMPLETE_SCALAR_START,
137
149
  MISSING_FLOW_SEPARATOR,
138
150
  UNEXPECTED_FLOW_SEPARATOR,
139
151
  INVALID_INDENTATION,
152
+ TAB_IN_INDENTATION,
140
153
  MISSING_INDENTATION,
141
154
  MISSING_SEPARATION,
142
155
  INVALID_BLOCK_HEADER,
143
156
  INVALID_TAG_HANDLE,
144
157
  INVALID_TAG_CHARACTER,
158
+ INVALID_TAG_PREFIX_START,
145
159
  INVALID_URI_ESCAPE,
146
160
  INCOMPLETE_URI_ESCAPE,
147
161
  INVALID_YAML_VERSION,
@@ -181,6 +195,11 @@ enum Token {
181
195
  INCOMPLETE_TAG_PREFIX,
182
196
  MISSING_DOCUMENT_START,
183
197
  INCOMPLETE_DOCUMENT_START,
198
+ UNFINISHED_INVALID_INDENTATION,
199
+ UNFINISHED_TAB_IN_INDENTATION,
200
+ UNFINISHED_MISSING_INDENTATION,
201
+ UNFINISHED_MISSING_FLOW_SEPARATOR,
202
+ UNDECODABLE_ESCAPE_PREFIX,
184
203
  ERROR_SENTINEL
185
204
  };
186
205
 
@@ -215,19 +234,10 @@ enum Mode {
215
234
  DIRECTIVE_PREFIX,
216
235
  DIRECTIVE_PARAMETERS
217
236
  };
218
- enum HeaderFlag {
219
- HEADER_INDENTATION = 1,
220
- HEADER_CHOMPING = 2,
221
- HEADER_SEPARATION = 4
222
- };
223
- enum PropertyFlag {
224
- PROPERTY_ANCHOR = 1,
225
- PROPERTY_TAG = 2,
226
- PROPERTY_COLON_CONTENT = 4
227
- };
228
237
  enum HandleForm { PRIMARY_HANDLE = 1, SECONDARY_HANDLE, NAMED_HANDLE };
238
+ /* Appending ":" can move these continued properties into an implicit key. */
239
+ enum PropertyKey { ANCHOR_KEY = 1, TAG_KEY = 2 };
229
240
  enum DirectiveKind { YAML_DIRECTIVE = 1, TAG_DIRECTIVE, RESERVED_DIRECTIVE };
230
- /* Line starts that end the open constructs of a document. */
231
241
  enum Boundary {
232
242
  NO_BOUNDARY,
233
243
  DOCUMENT_START_LINE,
@@ -261,28 +271,35 @@ enum Boundary {
261
271
  F(bool, separated, 1, 0) \
262
272
  F(bool, key_done, 1, 0) \
263
273
  F(int64_t, block_indent, 8, 1) \
264
- F(uint8_t, header_flags, 1, 0) \
274
+ F(bool, block_explicit_indent, 1, 0) \
275
+ F(bool, header_separated, 1, 0) \
265
276
  F(uint8_t, return_mode, 1, 0) \
266
277
  F(bool, block_leading, 1, 0) \
267
278
  F(bool, block_empty, 1, 0) \
268
279
  F(bool, block_prefix, 1, 0) \
280
+ F(uint32_t, block_layout_lines, 4, 0) \
269
281
  F(uint8_t, boundary, 1, 0) \
270
282
  F(bool, boundary_checked, 1, 0) \
283
+ F(uint8_t, boundary_pending, 1, 0) \
271
284
  F(bool, document_started, 1, 0) \
272
285
  F(bool, document_content, 1, 0) \
273
286
  F(bool, after_document_end, 1, 0) \
274
287
  F(bool, block_out, 1, 0) \
275
- F(uint8_t, property_flags, 1, 0) \
288
+ F(bool, property_colon_content, 1, 0) \
276
289
  F(uint8_t, handle_form, 1, 0) \
277
290
  F(bool, properties_ready, 1, 0) \
278
- F(bool, duplicate_pending, 1, 0) \
279
291
  F(bool, property_separation_notified, 1, 0) \
292
+ F(uint8_t, property_keys, 1, 0) \
280
293
  F(uint8_t, directive_kind, 1, 0) \
281
294
  F(bool, directive_handle, 1, 0) \
282
295
  F(bool, value_separation, 1, 0) \
283
296
  F(uint32_t, scalar_spaces, 4, 0) \
284
297
  F(bool, scalar_tab, 1, 0) \
285
298
  F(bool, explicit_document_required, 1, 0) \
299
+ F(bool, eof_continuation, 1, 0) \
300
+ F(bool, block_eof, 1, 0) \
301
+ F(bool, prefix_eof, 1, 0) \
302
+ F(int64_t, provisional_indent, 8, 2) \
286
303
  F(uint8_t, block_kind, 1, 0)
287
304
 
288
305
  #define FIELD_BYTES(type, name, width, bias) +width
@@ -464,8 +481,7 @@ close_flow(Scanner *s, TSLexer *l, const bool *valid, enum Token token) {
464
481
  if (closed) {
465
482
  s->flow--;
466
483
  s->json = true;
467
- /* A mismatched closer ends the collection mid-line without consuming
468
- * source, so the rest of the line is content rather than layout. */
484
+ /* Missing closers must also mark the rest of this line as content. */
469
485
  s->line = false;
470
486
  }
471
487
  return closed;
@@ -490,6 +506,8 @@ static bool emit_closing(
490
506
  case DEDENT:
491
507
  case OUTDENT:
492
508
  s->indent--;
509
+ if (s->indent <= s->provisional_indent)
510
+ s->provisional_indent = -2;
493
511
  break;
494
512
  case RESTORE_INDENT:
495
513
  case INDENT:
@@ -505,23 +523,32 @@ static bool emit_closing(
505
523
  static bool start_plain(Scanner *s, TSLexer *l, const bool *valid) {
506
524
  if (!valid[PLAIN_START])
507
525
  return false;
526
+ s->eof_continuation = false;
508
527
  s->mode = PLAIN;
509
528
  s->first = true;
510
529
  s->json = false;
511
530
  return emit(l, valid, PLAIN_START);
512
531
  }
513
- static bool scan_invalid_character(TSLexer *l, const bool *valid) {
514
- if (l->eof(l) || !invalid_unquoted(l->lookahead))
532
+ static bool scan_invalid_character(TSLexer *l, const bool *valid, bool quoted) {
533
+ bool (*invalid)(int32_t) = quoted ? invalid_quoted : invalid_unquoted;
534
+ if (l->eof(l) || !invalid(l->lookahead))
515
535
  return false;
516
- return take(
517
- l,
518
- valid,
519
- invalid_encoding(l->lookahead) ? INVALID_ENCODING : INVALID_CHARACTER
536
+ bool undecodable = invalid_encoding(l->lookahead);
537
+ enum Token token = undecodable ? INVALID_ENCODING : INVALID_CHARACTER;
538
+ if (!valid[token])
539
+ return false;
540
+ do {
541
+ step(l);
542
+ } while (
543
+ !l->eof(l) &&
544
+ invalid(l->lookahead) &&
545
+ invalid_encoding(l->lookahead) == undecodable
520
546
  );
547
+ return finish(l, valid, token);
521
548
  }
522
549
 
523
550
  static bool scan_line_text(TSLexer *l, const bool *valid, enum Token token) {
524
- if (scan_invalid_character(l, valid))
551
+ if (scan_invalid_character(l, valid, false))
525
552
  return true;
526
553
  do {
527
554
  step(l);
@@ -531,11 +558,8 @@ static bool scan_line_text(TSLexer *l, const bool *valid, enum Token token) {
531
558
  return finish(l, valid, token);
532
559
  }
533
560
 
534
- static enum Boundary document_marker(TSLexer *l) {
535
- if (l->get_column(l) != 0 || (l->lookahead != '-' && l->lookahead != '.'))
536
- return NO_BOUNDARY;
537
- int32_t marker = l->lookahead;
538
- for (unsigned i = 0; i < 3; i++) {
561
+ static enum Boundary document_marker_tail(TSLexer *l, int32_t marker) {
562
+ for (unsigned i = 0; i < 2; i++) {
539
563
  if (l->lookahead != marker)
540
564
  return NO_BOUNDARY;
541
565
  step(l);
@@ -544,8 +568,13 @@ static enum Boundary document_marker(TSLexer *l) {
544
568
  return NO_BOUNDARY;
545
569
  return marker == '-' ? DOCUMENT_START_LINE : DOCUMENT_END_LINE;
546
570
  }
547
- /* Outside flow collections and scalars, a directive indicator or a BOM at
548
- * the start of a line also ends the open constructs of the document. */
571
+ static enum Boundary document_marker(TSLexer *l) {
572
+ if (l->get_column(l) != 0 || (l->lookahead != '-' && l->lookahead != '.'))
573
+ return NO_BOUNDARY;
574
+ int32_t marker = l->lookahead;
575
+ step(l);
576
+ return document_marker_tail(l, marker);
577
+ }
549
578
  static enum Boundary line_boundary(const Scanner *s, TSLexer *l) {
550
579
  if (l->lookahead == '-' || l->lookahead == '.')
551
580
  return document_marker(l);
@@ -582,7 +611,6 @@ static bool block_collection(enum Token kind) {
582
611
  return kind == MAPPING_START || kind == SEQUENCE_START;
583
612
  }
584
613
 
585
- /* Only starts within the same line's last 1024 characters can remain valid. */
586
614
  static void advance_keys(Scanner *s, uint64_t characters, bool broken) {
587
615
  if (broken || characters > IMPLICIT_KEY_LIMIT) {
588
616
  memset(s->implicit_keys, 0, sizeof(s->implicit_keys));
@@ -614,11 +642,7 @@ static unsigned end_key(Scanner *s) {
614
642
  return IMPLICIT_KEY_LIMIT + 1;
615
643
  }
616
644
 
617
- /* Flow entries and values are optional, so the parser reduces a flow node only
618
- * at the token that follows the layout after it. Reading that layout keeps an
619
- * edit inside it from leaving a reused node's reduction unrevised. The key
620
- * lookahead reads the same layout because the colon may follow comment lines.
621
- */
645
+ /* Layout edits must invalidate preceding node reductions. */
622
646
  static void skip_layout(TSLexer *l) {
623
647
  for (;;) {
624
648
  while (space(l->lookahead))
@@ -647,6 +671,11 @@ static bool scan_key_end(Scanner *s, TSLexer *l, const bool *valid) {
647
671
  return emit(l, valid, FLOW_NODE_END);
648
672
  }
649
673
  s->key_done = true;
674
+ if (key_invalid && !s->flow && l->lookahead == ':') {
675
+ step(l);
676
+ if (l->eof(l))
677
+ return emit(l, valid, UNFINISHED_INVALID_KEY_END);
678
+ }
650
679
  return emit(l, valid, key_invalid ? INVALID_KEY_END : KEY_END);
651
680
  }
652
681
 
@@ -665,10 +694,14 @@ static void skip_property_or_alias(TSLexer *l) {
665
694
  }
666
695
  }
667
696
 
668
- static enum Token classify_block_node(TSLexer *l) {
697
+ static enum Token classify_block_node(TSLexer *l, bool *unfinished) {
698
+ if (unfinished)
699
+ *unfinished = false;
669
700
  bool properties = l->lookahead == '&' || l->lookahead == '!';
701
+ bool attached = false;
670
702
  while (l->lookahead == '&' || l->lookahead == '!') {
671
703
  skip_property_or_alias(l);
704
+ attached = !blank(l->lookahead);
672
705
  skip_blanks(l);
673
706
  if (l->eof(l) || newline(l->lookahead) || l->lookahead == '#')
674
707
  return PROPERTIES_START;
@@ -692,10 +725,13 @@ static enum Token classify_block_node(TSLexer *l) {
692
725
  }
693
726
  if (first == '-' || first == '?') {
694
727
  step(l);
695
- if (space(l->lookahead) || l->eof(l))
728
+ if (space(l->lookahead) || l->eof(l)) {
729
+ if (unfinished)
730
+ *unfinished = l->eof(l);
696
731
  return properties ? PROPERTIES_START
697
732
  : first == '-' ? SEQUENCE_START
698
733
  : MAPPING_START;
734
+ }
699
735
  }
700
736
  int32_t quote = 0;
701
737
  char initial_closings[32];
@@ -704,7 +740,7 @@ static enum Token classify_block_node(TSLexer *l) {
704
740
  bool separated = false;
705
741
  bool complete = content == ALIAS_START;
706
742
  bool plain = content == PLAIN_START;
707
- bool adjacent = false;
743
+ bool adjacent = attached;
708
744
  while (!l->eof(l)) {
709
745
  if (
710
746
  l->get_column(l) ==
@@ -788,6 +824,8 @@ static enum Token classify_block_node(TSLexer *l) {
788
824
  bool indicator = colon_boundary(l, flow);
789
825
  if (!flow && indicator) {
790
826
  fallback = MAPPING_START;
827
+ if (unfinished)
828
+ *unfinished = l->eof(l);
791
829
  break;
792
830
  }
793
831
  if (!flow && complete)
@@ -816,14 +854,21 @@ static enum Token classify_block_node(TSLexer *l) {
816
854
 
817
855
  static enum Token classify_block_start(Scanner *s, TSLexer *l) {
818
856
  if (!s->block_kind)
819
- s->block_kind = (uint8_t)(classify_block_node(l) + 1);
857
+ s->block_kind = (uint8_t)(classify_block_node(l, &s->block_eof) + 1);
820
858
  return (enum Token)(s->block_kind - 1);
821
859
  }
822
860
 
823
861
  static enum Token
824
862
  property_content_start(Scanner *s, TSLexer *l, const bool *valid) {
825
- if (l->lookahead == '*' && !(s->line && s->line_mapping_key))
863
+ if (l->lookahead == '*' && !(s->line && s->line_mapping_key)) {
864
+ if (s->line && !s->flow) {
865
+ skip_property_or_alias(l);
866
+ skip_blanks(l);
867
+ if (l->eof(l))
868
+ return UNFINISHED_PROPERTIES_ON_ALIAS_START;
869
+ }
826
870
  return PROPERTY_ALIAS_START;
871
+ }
827
872
  if (!s->flow) {
828
873
  enum Token kind = classify_block_start(s, l);
829
874
  if (
@@ -839,7 +884,8 @@ property_content_start(Scanner *s, TSLexer *l, const bool *valid) {
839
884
  SEQUENCE_START ||
840
885
  (kind == MAPPING_START && valid[BLOCK_PROPERTY_CONTENT_START]))
841
886
  )
842
- return PROPERTY_COMPACT_START;
887
+ return s->block_eof ? UNFINISHED_INVALID_PROPERTY_COLLECTION_START
888
+ : PROPERTY_COMPACT_START;
843
889
  }
844
890
  return valid[BLOCK_PROPERTY_CONTENT_START] ? BLOCK_PROPERTY_CONTENT_START
845
891
  : PROPERTY_CONTENT_START;
@@ -851,9 +897,12 @@ emit_indicator(Scanner *s, TSLexer *l, const bool *valid, enum Token token) {
851
897
  if (!s->flow && blank(l->lookahead)) {
852
898
  skip_blanks(l);
853
899
  if (!l->eof(l) && !newline(l->lookahead) && l->lookahead != '#') {
854
- enum Token kind = classify_block_node(l);
855
- if (block_collection(kind))
900
+ bool unfinished;
901
+ enum Token kind = classify_block_node(l, &unfinished);
902
+ if (block_collection(kind)) {
903
+ s->prefix_eof = unfinished;
856
904
  s->mode = COMPACT_PREFIX;
905
+ }
857
906
  }
858
907
  }
859
908
  return emit(l, valid, token);
@@ -907,10 +956,6 @@ static bool uri_char(int32_t c, bool suffix) {
907
956
  strchr("#;/?:@&=+$,_.!~*'()[]", (int)c) &&
908
957
  (!suffix || (c != '!' && !flow_indicator(c))));
909
958
  }
910
- static void finish_tag(Scanner *s) {
911
- s->mode = NORMAL;
912
- s->property_flags |= PROPERTY_TAG;
913
- }
914
959
  static bool scan_name(Scanner *s, TSLexer *l, const bool *valid) {
915
960
  bool anchor = s->mode == ANCHOR_BODY;
916
961
  if (name_boundary(l)) {
@@ -921,12 +966,10 @@ static bool scan_name(Scanner *s, TSLexer *l, const bool *valid) {
921
966
  : emit_missing(l, valid, MISSING_ALIAS_NAME, INCOMPLETE_ALIAS_NAME);
922
967
  }
923
968
  s->mode = NORMAL;
924
- if (anchor)
925
- s->property_flags |= PROPERTY_ANCHOR;
926
969
  return emit(l, valid, NAME_END);
927
970
  }
928
971
  s->first = false;
929
- if (scan_invalid_character(l, valid))
972
+ if (scan_invalid_character(l, valid, false))
930
973
  return true;
931
974
  do {
932
975
  step(l);
@@ -934,7 +977,6 @@ static bool scan_name(Scanner *s, TSLexer *l, const bool *valid) {
934
977
  return finish(l, valid, anchor ? ANCHOR_NAME : ALIAS_NAME);
935
978
  }
936
979
 
937
- /* The starting lookahead has already selected the tag handle form. */
938
980
  static bool scan_handle(Scanner *s, TSLexer *l, const bool *valid) {
939
981
  if (s->mode == HANDLE_SELECT) {
940
982
  step(l);
@@ -962,17 +1004,27 @@ static bool scan_handle(Scanner *s, TSLexer *l, const bool *valid) {
962
1004
  s->mode = s->directive_handle ? DIRECTIVE_PREFIX : SUFFIX_BEGIN;
963
1005
  return take(l, valid, TAG_HANDLE_CLOSE);
964
1006
  }
965
- if (scan_invalid_character(l, valid))
1007
+ if (scan_invalid_character(l, valid, false))
966
1008
  return true;
967
- if (!word_char(l->lookahead))
968
- return take(l, valid, INVALID_TAG_HANDLE);
1009
+ if (!word_char(l->lookahead)) {
1010
+ do {
1011
+ step(l);
1012
+ } while (
1013
+ !l->eof(l) &&
1014
+ !space(l->lookahead) &&
1015
+ l->lookahead !=
1016
+ '!' &&
1017
+ !invalid_unquoted(l->lookahead) &&
1018
+ !word_char(l->lookahead)
1019
+ );
1020
+ return finish(l, valid, INVALID_TAG_HANDLE);
1021
+ }
969
1022
  do {
970
1023
  step(l);
971
1024
  } while (word_char(l->lookahead));
972
1025
  return finish(l, valid, TAG_HANDLE_NAME);
973
1026
  }
974
1027
 
975
- /* The zero-width boundaries around a verbatim URI or a tag suffix. */
976
1028
  static bool scan_tag_boundary(Scanner *s, TSLexer *l, const bool *valid) {
977
1029
  if (s->mode == URI_BEGIN) {
978
1030
  s->mode = URI_BODY;
@@ -983,7 +1035,7 @@ static bool scan_tag_boundary(Scanner *s, TSLexer *l, const bool *valid) {
983
1035
  return emit(l, valid, TAG_URI_START);
984
1036
  }
985
1037
  if (s->mode == VERBATIM_CLOSE) {
986
- finish_tag(s);
1038
+ s->mode = NORMAL;
987
1039
  if (l->lookahead == '>')
988
1040
  return take(l, valid, VERBATIM_TAG_CLOSE);
989
1041
  return emit_missing(
@@ -999,37 +1051,41 @@ static bool scan_tag_boundary(Scanner *s, TSLexer *l, const bool *valid) {
999
1051
  return emit(l, valid, TAG_SUFFIX_START);
1000
1052
  }
1001
1053
 
1054
+ static bool uri_boundary(TSLexer *l, bool prefix, bool suffix) {
1055
+ return l->eof(l) ||
1056
+ space(l->lookahead) ||
1057
+ (suffix ? flow_indicator(l->lookahead) : !prefix && l->lookahead == '>');
1058
+ }
1059
+
1002
1060
  static bool scan_uri(Scanner *s, TSLexer *l, const bool *valid) {
1003
1061
  bool prefix = s->mode == PREFIX_BODY;
1004
1062
  bool suffix = s->mode == SUFFIX_BODY;
1005
- if (
1006
- l->eof(l) ||
1007
- space(l->lookahead) ||
1008
- (suffix ? flow_indicator(l->lookahead) : !prefix && l->lookahead == '>')
1009
- ) {
1063
+ if (uri_boundary(l, prefix, suffix)) {
1010
1064
  if (prefix) {
1011
1065
  s->mode = DIRECTIVE_PARAMETERS;
1012
1066
  return emit(l, valid, TAG_PREFIX_END);
1013
1067
  }
1014
1068
  if (suffix) {
1015
- finish_tag(s);
1069
+ s->mode = NORMAL;
1016
1070
  return emit(l, valid, TAG_END);
1017
1071
  }
1018
1072
  s->mode = VERBATIM_CLOSE;
1019
1073
  return emit(l, valid, TAG_URI_END);
1020
1074
  }
1021
- if (scan_invalid_character(l, valid))
1075
+ if (scan_invalid_character(l, valid, false))
1022
1076
  return true;
1023
1077
  if (prefix && s->first) {
1024
1078
  s->first = false;
1025
- if (flow_indicator(l->lookahead))
1026
- return take(l, valid, INVALID_TAG_CHARACTER);
1079
+ if (flow_indicator(l->lookahead) && uri_char(l->lookahead, false))
1080
+ return take(l, valid, INVALID_TAG_PREFIX_START);
1027
1081
  }
1028
1082
  if (l->lookahead == '%') {
1029
1083
  step(l);
1030
1084
  l->mark_end(l);
1031
1085
  uint32_t value;
1032
1086
  unsigned remaining = hex_digits(l, 2, &value);
1087
+ if (remaining && invalid_encoding(l->lookahead))
1088
+ return emit(l, valid, UNDECODABLE_ESCAPE_PREFIX);
1033
1089
  return emit(
1034
1090
  l,
1035
1091
  valid,
@@ -1038,8 +1094,18 @@ static bool scan_uri(Scanner *s, TSLexer *l, const bool *valid) {
1038
1094
  : INVALID_URI_ESCAPE
1039
1095
  );
1040
1096
  }
1041
- if (!uri_char(l->lookahead, suffix))
1042
- return take(l, valid, INVALID_TAG_CHARACTER);
1097
+ if (!uri_char(l->lookahead, suffix)) {
1098
+ do {
1099
+ step(l);
1100
+ } while (
1101
+ !uri_boundary(l, prefix, suffix) &&
1102
+ !invalid_unquoted(l->lookahead) &&
1103
+ l->lookahead !=
1104
+ '%' &&
1105
+ !uri_char(l->lookahead, suffix)
1106
+ );
1107
+ return finish(l, valid, INVALID_TAG_CHARACTER);
1108
+ }
1043
1109
  do {
1044
1110
  step(l);
1045
1111
  } while (uri_char(l->lookahead, suffix));
@@ -1056,10 +1122,7 @@ scan_directive_word(TSLexer *l, const bool *valid, enum Token token) {
1056
1122
  } while (!directive_boundary(l));
1057
1123
  return finish(l, valid, token);
1058
1124
  }
1059
- /* The zero-width start token reads to the end of the line so that any edit
1060
- * inside the comment invalidates the comment's first leaf. Tree-sitter then
1061
- * breaks down the reused node before it, whose reduction may have depended on
1062
- * the content that follows the comment. */
1125
+ /* Comment edits must also invalidate preceding node reductions. */
1063
1126
  static bool start_comment(
1064
1127
  Scanner *s,
1065
1128
  TSLexer *l,
@@ -1093,13 +1156,13 @@ static bool scan_directive_name(Scanner *s, TSLexer *l, const bool *valid) {
1093
1156
  return emit(l, valid, DIRECTIVE_NAME_END);
1094
1157
  }
1095
1158
  s->first = false;
1096
- if (scan_invalid_character(l, valid))
1159
+ if (scan_invalid_character(l, valid, false))
1097
1160
  return true;
1098
1161
  return scan_directive_word(l, valid, DIRECTIVE_NAME);
1099
1162
  }
1100
1163
  static bool
1101
1164
  scan_yaml_version(Scanner *s, TSLexer *l, const bool *valid, bool end) {
1102
- if (scan_invalid_character(l, valid))
1165
+ if (scan_invalid_character(l, valid, false))
1103
1166
  return true;
1104
1167
  s->mode = DIRECTIVE_PARAMETERS;
1105
1168
  if (end)
@@ -1132,7 +1195,7 @@ scan_yaml_version(Scanner *s, TSLexer *l, const bool *valid, bool end) {
1132
1195
  }
1133
1196
  static bool
1134
1197
  scan_directive_handle(Scanner *s, TSLexer *l, const bool *valid, bool end) {
1135
- if (scan_invalid_character(l, valid))
1198
+ if (scan_invalid_character(l, valid, false))
1136
1199
  return true;
1137
1200
  s->mode = DIRECTIVE_PREFIX;
1138
1201
  s->property_separation_notified = false;
@@ -1186,7 +1249,7 @@ static bool scan_directive(Scanner *s, TSLexer *l, const bool *valid) {
1186
1249
  s->mode = NORMAL;
1187
1250
  return emit(l, valid, DIRECTIVE_END);
1188
1251
  }
1189
- if (scan_invalid_character(l, valid))
1252
+ if (scan_invalid_character(l, valid, false))
1190
1253
  return true;
1191
1254
  return scan_directive_word(
1192
1255
  l,
@@ -1205,8 +1268,6 @@ static bool scalar_break(Scanner *s, TSLexer *l, const bool *valid) {
1205
1268
  s->scalar_tab = false;
1206
1269
  return line_break(l, valid, SCALAR_LINE_BREAK);
1207
1270
  }
1208
- /* Content resumes after the prefix of a continuation line; its first token
1209
- * must be indented past the enclosing block indentation. */
1210
1271
  static bool resume_scalar_line(Scanner *s) {
1211
1272
  s->prefix = false;
1212
1273
  s->continuation = false;
@@ -1215,10 +1276,23 @@ static bool resume_scalar_line(Scanner *s) {
1215
1276
  s->scalar_indent = false;
1216
1277
  return s->scalar_spaces > s->indent;
1217
1278
  }
1279
+ static bool provisional_indentation(const Scanner *s, uint32_t spaces) {
1280
+ return s->boundary_pending ||
1281
+ (s->provisional_indent >= -1 && spaces > s->provisional_indent);
1282
+ }
1218
1283
  static bool scalar_prefix(Scanner *s, TSLexer *l, const bool *valid) {
1219
1284
  if (l->lookahead == '\t' && s->scalar_spaces <= s->indent) {
1220
1285
  s->scalar_tab = true;
1221
- return take(l, valid, INVALID_INDENTATION);
1286
+ do {
1287
+ step(l);
1288
+ } while (l->lookahead == '\t');
1289
+ return finish(
1290
+ l,
1291
+ valid,
1292
+ provisional_indentation(s, s->scalar_spaces)
1293
+ ? UNFINISHED_TAB_IN_INDENTATION
1294
+ : TAB_IN_INDENTATION
1295
+ );
1222
1296
  }
1223
1297
  do {
1224
1298
  if (l->lookahead == '\t')
@@ -1247,8 +1321,14 @@ static bool scan_quote(Scanner *s, TSLexer *l, const bool *valid) {
1247
1321
  if (s->prefix && blank(l->lookahead))
1248
1322
  return scalar_prefix(s, l, valid);
1249
1323
  if (!resume_scalar_line(s))
1250
- return emit(l, valid, MISSING_INDENTATION);
1251
- if (invalid_quoted(l->lookahead) && scan_invalid_character(l, valid))
1324
+ return emit(
1325
+ l,
1326
+ valid,
1327
+ provisional_indentation(s, s->scalar_spaces)
1328
+ ? UNFINISHED_MISSING_INDENTATION
1329
+ : MISSING_INDENTATION
1330
+ );
1331
+ if (scan_invalid_character(l, valid, true))
1252
1332
  return true;
1253
1333
  if (l->lookahead == quote) {
1254
1334
  step(l);
@@ -1275,9 +1355,12 @@ static bool scan_quote(Scanner *s, TSLexer *l, const bool *valid) {
1275
1355
  if (l->eof(l))
1276
1356
  return emit(l, valid, INCOMPLETE_ESCAPE);
1277
1357
  if (invalid_quoted(l->lookahead)) {
1278
- /* The prefix belongs to the scalar; decoding failure owns its own bytes.
1279
- */
1280
- return emit(l, valid, INVALID_ESCAPE);
1358
+ return emit(
1359
+ l,
1360
+ valid,
1361
+ invalid_encoding(l->lookahead) ? UNDECODABLE_ESCAPE_PREFIX
1362
+ : INVALID_ESCAPE
1363
+ );
1281
1364
  }
1282
1365
  int32_t c = l->lookahead;
1283
1366
  const char *simple = "0abtnvfre \t\"/\\N_LP";
@@ -1291,6 +1374,8 @@ static bool scan_quote(Scanner *s, TSLexer *l, const bool *valid) {
1291
1374
  uint64_t lower = (uint64_t)value << (remaining * 4);
1292
1375
  uint64_t upper = lower | ((UINT64_C(1) << (remaining * 4)) - 1);
1293
1376
  ok = lower <= 0x10ffff && (lower < 0xd800 || upper > 0xdfff);
1377
+ if (remaining && ok && invalid_encoding(l->lookahead))
1378
+ return emit(l, valid, UNDECODABLE_ESCAPE_PREFIX);
1294
1379
  if (remaining)
1295
1380
  return emit(
1296
1381
  l,
@@ -1345,12 +1430,12 @@ static bool scan_plain(Scanner *s, TSLexer *l, const bool *valid) {
1345
1430
  return take(l, valid, INVALID_SCALAR_START);
1346
1431
  if (l->lookahead == '-' || l->lookahead == '?' || l->lookahead == ':') {
1347
1432
  step(l);
1348
- if (
1349
- space(l->lookahead) ||
1350
- l->eof(l) ||
1351
- (s->flow && flow_indicator(l->lookahead))
1352
- ) {
1353
- return finish(l, valid, INVALID_SCALAR_START);
1433
+ if (colon_boundary(l, s->flow)) {
1434
+ return finish(
1435
+ l,
1436
+ valid,
1437
+ l->eof(l) ? INCOMPLETE_SCALAR_START : INVALID_SCALAR_START
1438
+ );
1354
1439
  }
1355
1440
  text = true;
1356
1441
  }
@@ -1366,7 +1451,13 @@ static bool scan_plain(Scanner *s, TSLexer *l, const bool *valid) {
1366
1451
  return finish(l, valid, SCALAR_LINE_SUFFIX);
1367
1452
  }
1368
1453
  if (!resume_scalar_line(s))
1369
- return emit(l, valid, MISSING_INDENTATION);
1454
+ return emit(
1455
+ l,
1456
+ valid,
1457
+ provisional_indentation(s, s->scalar_spaces)
1458
+ ? UNFINISHED_MISSING_INDENTATION
1459
+ : MISSING_INDENTATION
1460
+ );
1370
1461
  } else if (space(l->lookahead)) {
1371
1462
  bool broken = false;
1372
1463
  uint32_t indentation = 0;
@@ -1389,6 +1480,7 @@ static bool scan_plain(Scanner *s, TSLexer *l, const bool *valid) {
1389
1480
  (!broken || s->flow || indentation > s->indent);
1390
1481
  if (more && l->lookahead == ':') {
1391
1482
  step(l);
1483
+ s->eof_continuation = l->eof(l);
1392
1484
  more = !colon_boundary(l, s->flow);
1393
1485
  }
1394
1486
  if (
@@ -1411,7 +1503,7 @@ static bool scan_plain(Scanner *s, TSLexer *l, const bool *valid) {
1411
1503
  return true;
1412
1504
  }
1413
1505
  }
1414
- if (!text && scan_invalid_character(l, valid))
1506
+ if (!text && scan_invalid_character(l, valid, false))
1415
1507
  return true;
1416
1508
  l->mark_end(l);
1417
1509
  while (!l->eof(l)) {
@@ -1420,8 +1512,10 @@ static bool scan_plain(Scanner *s, TSLexer *l, const bool *valid) {
1420
1512
  break;
1421
1513
  if (c == ':') {
1422
1514
  step(l);
1423
- if (colon_boundary(l, s->flow))
1515
+ if (colon_boundary(l, s->flow)) {
1516
+ s->eof_continuation = l->eof(l);
1424
1517
  break;
1518
+ }
1425
1519
  text = true;
1426
1520
  l->mark_end(l);
1427
1521
  continue;
@@ -1455,40 +1549,34 @@ static bool scan_block_header(Scanner *s, TSLexer *l, const bool *valid) {
1455
1549
  s->mode = BLOCK_DETECT;
1456
1550
  return emit(l, valid, BLOCK_HEADER_END);
1457
1551
  }
1458
- if (scan_invalid_character(l, valid))
1552
+ if (scan_invalid_character(l, valid, false))
1459
1553
  return true;
1460
1554
  if (newline(l->lookahead)) {
1461
1555
  s->mode = BLOCK_HEADER_DONE;
1462
1556
  return line_break(l, valid, BLOCK_HEADER_BREAK);
1463
1557
  }
1464
1558
  if (blank(l->lookahead)) {
1465
- s->header_flags |= HEADER_SEPARATION;
1559
+ s->header_separated = true;
1466
1560
  return scan_separation(l, valid);
1467
1561
  }
1468
1562
  if (l->lookahead == '#' && valid[COMMENT_START])
1469
1563
  return start_comment(s, l, valid, BLOCK_HEADER, !s->separated);
1470
- if (
1471
- l->lookahead >=
1472
- '1' &&
1473
- l->lookahead <=
1474
- '9' &&
1475
- !(s->header_flags & (HEADER_INDENTATION | HEADER_SEPARATION))
1476
- ) {
1477
- s->block_indent = s->indent + l->lookahead - '0';
1478
- s->header_flags |= HEADER_INDENTATION;
1479
- return take(l, valid, INDENTATION_INDICATOR);
1480
- }
1481
- if (
1482
- (l->lookahead == '+' || l->lookahead == '-') &&
1483
- !(s->header_flags & (HEADER_CHOMPING | HEADER_SEPARATION))
1484
- ) {
1485
- s->header_flags |= HEADER_CHOMPING;
1486
- return take(l, valid, CHOMPING_INDICATOR);
1487
- }
1488
- if (l->lookahead >= '1' && l->lookahead <= '9')
1564
+ if (l->lookahead >= '1' && l->lookahead <= '9') {
1565
+ if (valid[INDENTATION_INDICATOR] && !s->header_separated) {
1566
+ s->block_indent = s->indent + l->lookahead - '0';
1567
+ s->block_explicit_indent = true;
1568
+ return take(l, valid, INDENTATION_INDICATOR);
1569
+ }
1489
1570
  return take(l, valid, INVALID_HEADER_INDENTATION);
1571
+ }
1490
1572
  if (l->lookahead == '+' || l->lookahead == '-')
1491
- return take(l, valid, INVALID_HEADER_CHOMPING);
1573
+ return take(
1574
+ l,
1575
+ valid,
1576
+ valid[CHOMPING_INDICATOR] && !s->header_separated
1577
+ ? CHOMPING_INDICATOR
1578
+ : INVALID_HEADER_CHOMPING
1579
+ );
1492
1580
  step(l);
1493
1581
  while (
1494
1582
  !l->eof(l) &&
@@ -1496,7 +1584,7 @@ static bool scan_block_header(Scanner *s, TSLexer *l, const bool *valid) {
1496
1584
  l->lookahead !=
1497
1585
  '#' &&
1498
1586
  !invalid_unquoted(l->lookahead) &&
1499
- !(l->lookahead >= '0' && l->lookahead <= '9') &&
1587
+ !(l->lookahead >= '1' && l->lookahead <= '9') &&
1500
1588
  l->lookahead !=
1501
1589
  '+' &&
1502
1590
  l->lookahead != '-'
@@ -1515,8 +1603,6 @@ static void reset_line(Scanner *s) {
1515
1603
  s->after_document_end = false;
1516
1604
  }
1517
1605
 
1518
- /* A document that ends without "..." must be followed by an explicit
1519
- * document; a directive document then also lacks its document end marker. */
1520
1606
  static void close_document(Scanner *s) {
1521
1607
  s->document = false;
1522
1608
  s->document_content = false;
@@ -1526,19 +1612,23 @@ static void open_document(Scanner *s, bool started) {
1526
1612
  s->document = true;
1527
1613
  s->document_started = started;
1528
1614
  s->document_content = false;
1615
+ s->eof_continuation = false;
1529
1616
  s->flow = 0;
1530
1617
  s->indent = -1;
1618
+ s->provisional_indent = -2;
1531
1619
  s->block_out = false;
1532
1620
  }
1533
1621
 
1534
- /* Close open constructs before a document marker; directives and stream BOMs
1535
- * are left for normal scanning. */
1536
1622
  static bool scan_boundary(Scanner *s, TSLexer *l, const bool *valid) {
1537
1623
  if (s->mode == SINGLE || s->mode == DOUBLE) {
1538
1624
  s->mode = NORMAL;
1539
1625
  s->prefix = false;
1540
1626
  s->scalar_indent = false;
1541
- return emit(l, valid, MISSING_QUOTE_CLOSE);
1627
+ return emit(
1628
+ l,
1629
+ valid,
1630
+ s->boundary_pending ? INCOMPLETE_QUOTE_CLOSE : MISSING_QUOTE_CLOSE
1631
+ );
1542
1632
  }
1543
1633
  if (s->mode == PLAIN) {
1544
1634
  s->mode = NORMAL;
@@ -1549,10 +1639,12 @@ static bool scan_boundary(Scanner *s, TSLexer *l, const bool *valid) {
1549
1639
  reset_line(s);
1550
1640
  return emit(l, valid, BLOCK_SCALAR_END);
1551
1641
  }
1552
- static const enum Token closings[] = {
1642
+ const enum Token closings[] = {
1553
1643
  EMPTY_BLOCK_NODE,
1554
- MISSING_FLOW_SEQUENCE_CLOSE,
1555
- MISSING_FLOW_MAPPING_CLOSE,
1644
+ s->boundary_pending ? INCOMPLETE_FLOW_SEQUENCE_CLOSE
1645
+ : MISSING_FLOW_SEQUENCE_CLOSE,
1646
+ s->boundary_pending ? INCOMPLETE_FLOW_MAPPING_CLOSE
1647
+ : MISSING_FLOW_MAPPING_CLOSE,
1556
1648
  MAPPING_END,
1557
1649
  SEQUENCE_END,
1558
1650
  DEDENT,
@@ -1595,8 +1687,6 @@ static bool scan_boundary(Scanner *s, TSLexer *l, const bool *valid) {
1595
1687
  return emit(l, valid, token);
1596
1688
  }
1597
1689
 
1598
- /* Auto-detects the content indentation from the first non-empty body line,
1599
- * or from the longest leading empty line when no content follows. */
1600
1690
  static bool detect_block_indent(Scanner *s, TSLexer *l, const bool *valid) {
1601
1691
  if (s->block_indent < 0) {
1602
1692
  uint32_t longest = 0;
@@ -1612,8 +1702,9 @@ static bool detect_block_indent(Scanner *s, TSLexer *l, const bool *valid) {
1612
1702
  content && !spaces && (l->lookahead == 0xfeff || document_marker(l))
1613
1703
  )
1614
1704
  content = false;
1615
- s->block_indent =
1616
- content ? spaces : (spaces > longest ? spaces : longest);
1705
+ s->block_indent = content ? spaces
1706
+ : longest > s->indent ? longest
1707
+ : s->indent + 1;
1617
1708
  break;
1618
1709
  }
1619
1710
  }
@@ -1622,37 +1713,58 @@ static bool detect_block_indent(Scanner *s, TSLexer *l, const bool *valid) {
1622
1713
  s->mode = BLOCK_BODY;
1623
1714
  return emit(l, valid, BLOCK_BODY_START);
1624
1715
  }
1716
+ /* Cache the layout run to avoid repeated lookahead on shallow tab lines. */
1717
+ static bool block_stream_layout_follows(Scanner *s, TSLexer *l) {
1718
+ uint32_t lines = 1;
1719
+ for (;;) {
1720
+ skip_blanks(l);
1721
+ if (l->lookahead == '#') {
1722
+ do {
1723
+ step(l);
1724
+ } while (!l->eof(l) && !newline(l->lookahead));
1725
+ }
1726
+ if (l->eof(l))
1727
+ return true;
1728
+ if (!newline(l->lookahead)) {
1729
+ s->block_layout_lines = lines;
1730
+ return l->get_column(l) ==
1731
+ 0 &&
1732
+ (l->lookahead == 0xfeff || l->lookahead == '%' || document_marker(l));
1733
+ }
1734
+ skip_line_break(l);
1735
+ lines++;
1736
+ }
1737
+ }
1625
1738
  static bool scan_block_body(Scanner *s, TSLexer *l, const bool *valid) {
1626
1739
  if (l->eof(l)) {
1627
1740
  s->mode = NORMAL;
1741
+ s->block_layout_lines = 0;
1628
1742
  reset_line(s);
1629
1743
  return emit(l, valid, BLOCK_SCALAR_END);
1630
1744
  }
1631
1745
  uint32_t spaces = count_spaces(l);
1632
- bool empty = newline(l->lookahead) || l->eof(l);
1633
- bool spaced = spaces > s->block_indent || l->lookahead == '\t';
1634
- /* A less indented line that holds only blanks and a comment is a
1635
- * separation line of the enclosing construct, not scalar content. */
1636
- bool separation = false;
1637
- if (!empty && spaces < s->block_indent && l->lookahead == '\t') {
1638
- skip_blanks(l);
1639
- separation = l->eof(l) || newline(l->lookahead) || l->lookahead == '#';
1640
- }
1746
+ int32_t first = l->lookahead;
1747
+ bool empty = newline(first);
1748
+ bool spaced = spaces > s->block_indent || first == '\t';
1749
+ bool shallow_tab = spaces < s->block_indent && first == '\t';
1750
+ bool leading = s->block_leading && !s->block_explicit_indent;
1751
+ bool end = l->eof(l) && (spaces <= s->block_indent || leading);
1752
+ if (shallow_tab && !s->block_layout_lines)
1753
+ end = block_stream_layout_follows(s, l);
1641
1754
  if (
1642
- !empty &&
1643
- (spaces <=
1644
- s->indent ||
1645
- separation ||
1646
- (spaces < s->block_indent && l->lookahead == '#'))
1755
+ end ||
1756
+ (!empty &&
1757
+ ((spaces <= s->indent && !shallow_tab) ||
1758
+ (spaces < s->block_indent && first == '#')))
1647
1759
  ) {
1648
1760
  s->mode = NORMAL;
1761
+ s->block_layout_lines = 0;
1649
1762
  reset_line(s);
1650
1763
  return emit(l, valid, BLOCK_SCALAR_END);
1651
1764
  }
1652
- s->block_empty = empty &&
1653
- (spaces <=
1654
- s->block_indent ||
1655
- (s->block_leading && !(s->header_flags & HEADER_INDENTATION)));
1765
+ if (s->block_layout_lines)
1766
+ s->block_layout_lines--;
1767
+ s->block_empty = empty && (spaces <= s->block_indent || leading);
1656
1768
  if (!s->block_empty)
1657
1769
  s->block_leading = false;
1658
1770
  s->block_prefix = true;
@@ -1689,7 +1801,12 @@ static bool scan_block_line(Scanner *s, TSLexer *l, const bool *valid) {
1689
1801
  return finish(l, valid, INVALID_INDENTATION);
1690
1802
  }
1691
1803
  if (!s->block_empty && l->get_column(l) < s->block_indent)
1692
- return emit(l, valid, MISSING_INDENTATION);
1804
+ return emit(
1805
+ l,
1806
+ valid,
1807
+ s->boundary_pending ? UNFINISHED_MISSING_INDENTATION
1808
+ : MISSING_INDENTATION
1809
+ );
1693
1810
  }
1694
1811
  if (newline(l->lookahead)) {
1695
1812
  s->mode = BLOCK_LINE_DONE;
@@ -1745,12 +1862,28 @@ static bool scan_prefix(Scanner *s, TSLexer *l, const bool *valid) {
1745
1862
  ' ' &&
1746
1863
  (excess || !s->prefix_invalid || l->get_column(l) < s->indent)
1747
1864
  );
1748
- return finish(l, valid, excess ? INVALID_INDENTATION : INDENTATION);
1865
+ return finish(
1866
+ l,
1867
+ valid,
1868
+ excess
1869
+ ? s->prefix_eof ? UNFINISHED_INVALID_INDENTATION : INVALID_INDENTATION
1870
+ : INDENTATION
1871
+ );
1749
1872
  }
1750
1873
  if (blank(l->lookahead)) {
1751
1874
  s->prefix_tab = true;
1752
- if (s->tab_invalid && l->lookahead == '\t')
1753
- return take(l, valid, INVALID_INDENTATION);
1875
+ if (s->tab_invalid && l->lookahead == '\t') {
1876
+ do {
1877
+ step(l);
1878
+ } while (l->lookahead == '\t');
1879
+ return finish(
1880
+ l,
1881
+ valid,
1882
+ s->prefix_eof || provisional_indentation(s, s->line_indent)
1883
+ ? UNFINISHED_TAB_IN_INDENTATION
1884
+ : TAB_IN_INDENTATION
1885
+ );
1886
+ }
1754
1887
  do {
1755
1888
  step(l);
1756
1889
  } while (blank(l->lookahead) && !(s->tab_invalid && l->lookahead == '\t'));
@@ -1766,7 +1899,13 @@ static bool scan_prefix(Scanner *s, TSLexer *l, const bool *valid) {
1766
1899
  s->line_indent <= s->indent
1767
1900
  ) {
1768
1901
  s->indent_checked = true;
1769
- return emit(l, valid, MISSING_INDENTATION);
1902
+ return emit(
1903
+ l,
1904
+ valid,
1905
+ provisional_indentation(s, s->line_indent)
1906
+ ? UNFINISHED_MISSING_INDENTATION
1907
+ : MISSING_INDENTATION
1908
+ );
1770
1909
  }
1771
1910
  s->mode = NORMAL;
1772
1911
  return emit(l, valid, LINE_PREFIX_END);
@@ -1777,7 +1916,12 @@ scan_node_start(Scanner *s, TSLexer *l, const bool *valid, int64_t column) {
1777
1916
  int32_t first = l->lookahead;
1778
1917
  enum Token kind = s->flow ? node_start(first) : classify_block_start(s, l);
1779
1918
  if (!s->line && valid[DOCUMENT_COMPACT_START] && block_collection(kind))
1780
- return emit(l, valid, DOCUMENT_COMPACT_START);
1919
+ return emit(
1920
+ l,
1921
+ valid,
1922
+ s->block_eof ? UNFINISHED_INVALID_DOCUMENT_COLLECTION_START
1923
+ : DOCUMENT_COMPACT_START
1924
+ );
1781
1925
  if (
1782
1926
  !s->flow &&
1783
1927
  s->line &&
@@ -1795,8 +1939,10 @@ scan_node_start(Scanner *s, TSLexer *l, const bool *valid, int64_t column) {
1795
1939
  kind ==
1796
1940
  SEQUENCE_START &&
1797
1941
  valid[MAPPING_END]
1798
- )
1942
+ ) {
1943
+ s->eof_continuation = s->block_eof;
1799
1944
  return emit(l, valid, MAPPING_END);
1945
+ }
1800
1946
  if (
1801
1947
  !s->flow &&
1802
1948
  s->line &&
@@ -1818,6 +1964,8 @@ scan_node_start(Scanner *s, TSLexer *l, const bool *valid, int64_t column) {
1818
1964
  if ((kind == LITERAL_START || kind == FOLDED_START) && !valid[kind])
1819
1965
  kind = PLAIN_START;
1820
1966
  if (!s->flow && block_collection(kind)) {
1967
+ if (s->block_eof && s->provisional_indent == -2 && column > s->indent)
1968
+ s->provisional_indent = s->indent;
1821
1969
  enum Token shift = column < s->indent ? OUTDENT : INDENT;
1822
1970
  if (column != s->indent && emit_closing(s, l, valid, &shift, 1))
1823
1971
  return true;
@@ -1836,10 +1984,10 @@ scan_node_start(Scanner *s, TSLexer *l, const bool *valid, int64_t column) {
1836
1984
  return start_plain(s, l, valid);
1837
1985
  if (kind == PROPERTIES_START) {
1838
1986
  s->json = false;
1839
- s->property_flags = 0;
1987
+ s->property_colon_content = false;
1840
1988
  s->properties_ready = false;
1841
- s->duplicate_pending = false;
1842
1989
  s->property_separation_notified = false;
1990
+ s->property_keys = 0;
1843
1991
  }
1844
1992
  return !block_collection(kind) && emit(l, valid, kind);
1845
1993
  }
@@ -1869,24 +2017,14 @@ static bool scan_node_open(Scanner *s, TSLexer *l, const bool *valid) {
1869
2017
  bool literal = l->lookahead == '|';
1870
2018
  s->mode = BLOCK_HEADER;
1871
2019
  s->block_indent = -1;
1872
- s->header_flags = 0;
2020
+ s->block_layout_lines = 0;
2021
+ s->block_explicit_indent = false;
2022
+ s->header_separated = false;
1873
2023
  return take(l, valid, literal ? LITERAL_INDICATOR : FOLDED_INDICATOR);
1874
2024
  }
1875
2025
  return false;
1876
2026
  }
1877
2027
 
1878
- static bool scan(Scanner *s, TSLexer *l, const bool *valid);
1879
-
1880
- static bool scan_content(Scanner *s, TSLexer *l, const bool *valid);
1881
- static bool
1882
- scan_entry(Scanner *s, TSLexer *l, const bool *valid, int64_t column);
1883
- static bool scan_properties(
1884
- Scanner *s,
1885
- TSLexer *l,
1886
- const bool *valid,
1887
- bool property_pending
1888
- );
1889
-
1890
2028
  static bool scan_line_context(Scanner *s, TSLexer *l, const bool *valid) {
1891
2029
  s->line_indent = count_spaces(l);
1892
2030
  skip_blanks(l);
@@ -1917,160 +2055,238 @@ static bool begin_line_prefix(Scanner *s, TSLexer *l, const bool *valid) {
1917
2055
  !valid[INDENT] &&
1918
2056
  (valid[PAIR_START] || valid[ENTRY_START]);
1919
2057
  bool tab_invalid = !s->layout_only && spaces <= s->indent;
1920
- if (!s->layout_only && !s->flow)
1921
- tab_invalid |= block_collection(classify_block_node(l));
2058
+ s->prefix_eof = prefix_invalid && s->eof_continuation;
2059
+ if (!s->layout_only && !s->flow) {
2060
+ bool unfinished;
2061
+ bool collection = block_collection(classify_block_node(l, &unfinished));
2062
+ s->prefix_eof |= !tab_invalid && collection && unfinished;
2063
+ tab_invalid |= collection;
2064
+ }
1922
2065
  s->line_prefixed = true;
1923
2066
  return start_line_prefix(s, l, valid, prefix_invalid, tab_invalid, false);
1924
2067
  }
1925
2068
 
1926
2069
  static bool
1927
- scan_normal(Scanner *s, TSLexer *l, const bool *valid, bool property_pending) {
2070
+ scan_entry(Scanner *s, TSLexer *l, const bool *valid, int64_t column) {
1928
2071
  if (
1929
- !property_pending &&
1930
- (valid[KEY_END] || valid[INVALID_KEY_END] || valid[FLOW_NODE_END])
2072
+ l->lookahead ==
2073
+ ',' &&
2074
+ !valid[FLOW_SEPARATOR] &&
2075
+ valid[UNEXPECTED_FLOW_SEPARATOR]
1931
2076
  )
1932
- return scan_key_end(s, l, valid);
1933
- if (
1934
- s->key_done &&
1935
- (valid[MISSING_VALUE_INDICATOR] || valid[INCOMPLETE_VALUE_INDICATOR])
1936
- ) {
1937
- s->key_done = false;
1938
- if (l->lookahead == ':') {
2077
+ return take(l, valid, UNEXPECTED_FLOW_SEPARATOR);
2078
+ if (valid[FLOW_MAP_PAIR_START] && !flow_entry_end(l->lookahead)) {
2079
+ s->json = false;
2080
+ return emit(l, valid, FLOW_MAP_PAIR_START);
2081
+ }
2082
+ if (valid[IMPLICIT_KEY_START] && !(s->flow && flow_entry_end(l->lookahead))) {
2083
+ int32_t first = l->lookahead;
2084
+ bool indicator = false;
2085
+ if (l->lookahead == '?' || l->lookahead == ':') {
1939
2086
  step(l);
1940
- if (colon_boundary(l, false))
1941
- return emit_indicator(s, l, valid, VALUE_INDICATOR);
2087
+ indicator = colon_boundary(l, first == ':' && s->flow);
1942
2088
  }
1943
- return emit_missing(
1944
- l,
1945
- valid,
1946
- MISSING_VALUE_INDICATOR,
1947
- INCOMPLETE_VALUE_INDICATOR
1948
- );
2089
+ if (indicator) {
2090
+ if (valid[FLOW_SEQ_PAIR_START]) {
2091
+ s->json = false;
2092
+ return emit(l, valid, FLOW_SEQ_PAIR_START);
2093
+ }
2094
+ return emit_indicator(
2095
+ s,
2096
+ l,
2097
+ valid,
2098
+ first == '?' ? KEY_INDICATOR : VALUE_INDICATOR
2099
+ );
2100
+ }
2101
+ s->implicit_keys[0] |= 1;
2102
+ s->block_out = false;
2103
+ return emit(l, valid, IMPLICIT_KEY_START);
1949
2104
  }
1950
- if (s->line && !s->line_context && valid[LINE_CONTEXT])
1951
- return scan_line_context(s, l, valid);
1952
- if (s->line_context && !s->layout_only && !property_line_follows(s)) {
1953
- s->properties_ready = false;
1954
- property_pending = false;
1955
- if (valid[PROPERTIES_END])
1956
- return emit(l, valid, PROPERTIES_END);
2105
+ if (l->lookahead == '?' && valid[KEY_INDICATOR]) {
2106
+ step(l);
2107
+ if (space(l->lookahead) || l->eof(l)) {
2108
+ return emit_indicator(s, l, valid, KEY_INDICATOR);
2109
+ }
2110
+ return start_plain(s, l, valid);
1957
2111
  }
1958
- static const enum Token restorations[] = {DEDENT, RESTORE_INDENT};
2112
+ if (l->lookahead == ':' && valid[VALUE_INDICATOR]) {
2113
+ bool adjacent = s->flow && s->json;
2114
+ step(l);
2115
+ if (adjacent || colon_boundary(l, s->flow)) {
2116
+ s->value_separation =
2117
+ s->flow && !adjacent && (l->lookahead == '[' || l->lookahead == '{');
2118
+ s->json = false;
2119
+ return emit_indicator(s, l, valid, VALUE_INDICATOR);
2120
+ }
2121
+ bool unfinished = s->eof_continuation;
2122
+ return start_plain(s, l, valid) ||
2123
+ emit(
2124
+ l,
2125
+ valid,
2126
+ unfinished ? UNFINISHED_MISSING_FLOW_SEPARATOR : MISSING_FLOW_SEPARATOR
2127
+ );
2128
+ }
2129
+ if (l->lookahead == '-' && valid[SEQUENCE_INDICATOR]) {
2130
+ step(l);
2131
+ return emit_indicator(s, l, valid, SEQUENCE_INDICATOR);
2132
+ }
2133
+ if (s->value_separation && valid[MISSING_SEPARATION]) {
2134
+ s->value_separation = false;
2135
+ return emit(l, valid, MISSING_SEPARATION);
2136
+ }
2137
+ if (l->lookahead == ',' && valid[FLOW_SEPARATOR])
2138
+ return take(l, valid, FLOW_SEPARATOR);
2139
+ if (l->lookahead == ']' && valid[FLOW_SEQUENCE_CLOSE])
2140
+ return close_flow(s, l, valid, FLOW_SEQUENCE_CLOSE);
2141
+ if (l->lookahead == '}' && valid[FLOW_MAPPING_CLOSE])
2142
+ return close_flow(s, l, valid, FLOW_MAPPING_CLOSE);
2143
+ if (l->lookahead == ']' && valid[MISSING_FLOW_MAPPING_CLOSE])
2144
+ return close_flow(s, l, valid, MISSING_FLOW_MAPPING_CLOSE);
2145
+ if (l->lookahead == '}' && valid[MISSING_FLOW_SEQUENCE_CLOSE])
2146
+ return close_flow(s, l, valid, MISSING_FLOW_SEQUENCE_CLOSE);
1959
2147
  if (
1960
- !property_pending &&
1961
- emit_closing(
1962
- s,
2148
+ valid[MISSING_FLOW_SEPARATOR] &&
2149
+ !valid[PLAIN_START] &&
2150
+ !valid[SINGLE_START] &&
2151
+ !valid[DOUBLE_START] &&
2152
+ !valid[FLOW_SEQUENCE_START] &&
2153
+ !valid[FLOW_MAPPING_START] &&
2154
+ !flow_entry_end(l->lookahead)
2155
+ )
2156
+ return emit(
1963
2157
  l,
1964
2158
  valid,
1965
- restorations,
1966
- sizeof(restorations) / sizeof(*restorations)
1967
- )
1968
- )
1969
- return true;
2159
+ s->eof_continuation ? UNFINISHED_MISSING_FLOW_SEPARATOR
2160
+ : MISSING_FLOW_SEPARATOR
2161
+ );
1970
2162
  if (
1971
- !property_pending &&
1972
- s->line &&
1973
- s->line_context &&
1974
- !s->layout_only &&
1975
- !s->flow
1976
- ) {
1977
- if (valid[EMPTY_BLOCK_NODE] && !block_value_follows(s, l, valid))
1978
- return emit(l, valid, EMPTY_BLOCK_NODE);
1979
- if (
1980
- valid[MAPPING_END] &&
1981
- (s->line_indent <
1982
- s->indent ||
1983
- (s->line_indent ==
1984
- s->indent &&
1985
- s->line_head ==
1986
- '-' &&
1987
- !valid[VALUE_START]))
1988
- )
1989
- return emit(l, valid, MAPPING_END);
1990
- if (
1991
- valid[SEQUENCE_END] && (s->line_indent < s->indent || s->line_head != '-')
1992
- )
1993
- return emit(l, valid, SEQUENCE_END);
2163
+ valid[PAIR_START] ||
2164
+ valid[ENTRY_START] ||
2165
+ valid[MAPPING_START] ||
2166
+ valid[SEQUENCE_START] ||
2167
+ valid[PLAIN_START] ||
2168
+ valid[INDENT] ||
2169
+ valid[OUTDENT] ||
2170
+ valid[SINGLE_START] ||
2171
+ valid[DOUBLE_START] ||
2172
+ valid[FLOW_SEQUENCE_START] ||
2173
+ valid[FLOW_MAPPING_START] ||
2174
+ valid[LITERAL_START] ||
2175
+ valid[FOLDED_START] ||
2176
+ valid[PROPERTIES_START] ||
2177
+ valid[ALIAS_START]
2178
+ ) {
2179
+ return scan_node_start(s, l, valid, column);
1994
2180
  }
2181
+ return scan_node_open(s, l, valid);
2182
+ }
2183
+
2184
+ static bool scan_content(Scanner *s, TSLexer *l, const bool *valid) {
1995
2185
  if (
1996
- s->line &&
1997
- !s->line_prefixed &&
1998
- valid[LINE_PREFIX_START] &&
1999
- (blank(l->lookahead) ||
2000
- (s->flow &&
2001
- s->indent >=
2002
- 0 &&
2003
- !newline(l->lookahead) &&
2004
- !l->eof(l) &&
2005
- l->lookahead != '#'))
2006
- )
2007
- return begin_line_prefix(s, l, valid);
2008
- if (newline(l->lookahead) && valid[LINE_BREAK]) {
2186
+ l->lookahead == 0xfeff && l->get_column(l) == 0 && valid[BYTE_ORDER_MARK]
2187
+ ) {
2188
+ step(l);
2189
+ cursor_start_line(l);
2009
2190
  reset_line(s);
2010
- return line_break(l, valid, LINE_BREAK);
2191
+ return finish(l, valid, BYTE_ORDER_MARK);
2011
2192
  }
2012
- if (blank(l->lookahead) && valid[SEPARATION])
2013
- return scan_separation(l, valid);
2014
- if (l->lookahead == '#' && valid[COMMENT_START])
2015
- return start_comment(
2016
- s,
2017
- l,
2018
- valid,
2019
- NORMAL,
2020
- !s->separated && l->get_column(l) != 0
2193
+ if (scan_invalid_character(l, valid, false))
2194
+ return true;
2195
+ bool end = l->eof(l);
2196
+ if (s->after_document_end && !end) {
2197
+ do {
2198
+ step(l);
2199
+ } while (
2200
+ !l->eof(l) && !space(l->lookahead) && !invalid_unquoted(l->lookahead)
2021
2201
  );
2022
- if (valid[DIRECTIVE_INDICATOR]) {
2023
- s->mode = DIRECTIVE_NAME_BODY;
2024
- s->first = true;
2025
- return take(l, valid, DIRECTIVE_INDICATOR);
2202
+ return finish(l, valid, UNEXPECTED_DOCUMENT_END_CONTENT);
2026
2203
  }
2204
+ int64_t column = s->line ? s->line_indent : l->get_column(l);
2027
2205
  if (
2206
+ valid[EXPLICIT_VALUE_START] &&
2028
2207
  l->lookahead ==
2029
- '%' &&
2030
- l->get_column(l) ==
2031
- 0 &&
2032
- valid[RESERVED_DIRECTIVE_START]
2208
+ ':' &&
2209
+ s->line_head ==
2210
+ ':' &&
2211
+ s->line &&
2212
+ column == s->indent
2213
+ )
2214
+ return emit(l, valid, EXPLICIT_VALUE_START);
2215
+ if (
2216
+ valid[VALUE_START] || valid[SEQUENCE_VALUE_START] || valid[EMPTY_BLOCK_NODE]
2033
2217
  ) {
2034
- char name[5] = {0};
2035
- unsigned length = 0;
2036
- step(l);
2037
- while (!directive_boundary(l)) {
2038
- if (length < 4 && l->lookahead < 128)
2039
- name[length] = (char)l->lookahead;
2040
- if (length < 5)
2041
- length++;
2042
- step(l);
2218
+ if (block_value_follows(s, l, valid)) {
2219
+ if (!s->line && valid[INVALID_COMPACT_START]) {
2220
+ bool unfinished;
2221
+ enum Token kind = classify_block_node(l, &unfinished);
2222
+ if (block_collection(kind))
2223
+ return emit(
2224
+ l,
2225
+ valid,
2226
+ unfinished ? UNFINISHED_INVALID_COMPACT_COLLECTION_START
2227
+ : INVALID_COMPACT_START
2228
+ );
2229
+ }
2230
+ s->block_out = valid[VALUE_START];
2231
+ return emit(l, valid, s->block_out ? VALUE_START : SEQUENCE_VALUE_START);
2043
2232
  }
2044
- s->directive_kind = length == 4 && !strcmp(name, "YAML") ? YAML_DIRECTIVE
2045
- : length == 3 && !strcmp(name, "TAG") ? TAG_DIRECTIVE
2046
- : RESERVED_DIRECTIVE;
2233
+ return emit(l, valid, EMPTY_BLOCK_NODE);
2234
+ }
2235
+ if (valid[DOCUMENT_CLOSE] && end) {
2236
+ close_document(s);
2237
+ return emit(l, valid, DOCUMENT_CLOSE);
2238
+ }
2239
+ if (valid[DOCUMENT_OPEN] && !end && !s->document) {
2240
+ open_document(s, !s->explicit_document_required);
2241
+ return emit(l, valid, DOCUMENT_OPEN);
2242
+ }
2243
+ if (valid[UNEXPECTED_CONTENT_START] && s->document_content && !end) {
2244
+ s->document_content = false;
2047
2245
  return emit(
2048
2246
  l,
2049
2247
  valid,
2050
- s->directive_kind == YAML_DIRECTIVE ? YAML_DIRECTIVE_START
2051
- : s->directive_kind == TAG_DIRECTIVE ? TAG_DIRECTIVE_START
2052
- : RESERVED_DIRECTIVE_START
2248
+ s->eof_continuation || s->boundary_pending
2249
+ ? UNFINISHED_UNEXPECTED_DOCUMENT_CONTENT_START
2250
+ : UNEXPECTED_CONTENT_START
2053
2251
  );
2054
2252
  }
2055
- if (
2056
- !s->document_started &&
2057
- (valid[MISSING_DOCUMENT_START] || valid[INCOMPLETE_DOCUMENT_START])
2058
- ) {
2059
- s->document_started = true;
2060
- s->explicit_document_required = false;
2061
- return emit_missing(
2253
+ if (!s->flow && !s->line) {
2254
+ if (valid[MAPPING_END])
2255
+ return emit(l, valid, MAPPING_END);
2256
+ if (valid[SEQUENCE_END])
2257
+ return emit(l, valid, SEQUENCE_END);
2258
+ }
2259
+ if (end) {
2260
+ static const enum Token endings[] = {
2261
+ MAPPING_END,
2262
+ SEQUENCE_END,
2263
+ INCOMPLETE_FLOW_SEQUENCE_CLOSE,
2264
+ INCOMPLETE_FLOW_MAPPING_CLOSE,
2265
+ END_OF_FILE
2266
+ };
2267
+ return emit_closing(
2268
+ s,
2062
2269
  l,
2063
2270
  valid,
2064
- MISSING_DOCUMENT_START,
2065
- INCOMPLETE_DOCUMENT_START
2271
+ endings,
2272
+ sizeof(endings) / sizeof(*endings)
2066
2273
  );
2067
2274
  }
2068
- return scan_properties(s, l, valid, property_pending);
2275
+ if (valid[MAPPING_END] && column < s->indent)
2276
+ return emit(l, valid, MAPPING_END);
2277
+ if (
2278
+ valid[SEQUENCE_END] &&
2279
+ (column < s->indent || (s->line && l->lookahead != '-'))
2280
+ )
2281
+ return emit(l, valid, SEQUENCE_END);
2282
+ return scan_entry(s, l, valid, column);
2069
2283
  }
2070
2284
 
2071
- static void begin_property(Scanner *s) {
2072
- s->duplicate_pending = false;
2285
+ static bool scan(Scanner *s, TSLexer *l, const bool *valid);
2286
+
2287
+ static void begin_property(Scanner *s, unsigned key) {
2073
2288
  s->property_separation_notified = false;
2289
+ s->property_keys &= (uint8_t)~key;
2074
2290
  }
2075
2291
  static bool scan_properties(
2076
2292
  Scanner *s,
@@ -2078,33 +2294,12 @@ static bool scan_properties(
2078
2294
  const bool *valid,
2079
2295
  bool property_pending
2080
2296
  ) {
2081
- /* Properties at the start of a line end the node's own properties when
2082
- * they belong to the first key of a block mapping that is its content.
2083
- * The line context records whether the leading property or alias of the
2084
- * line is such a key. */
2085
- if (
2086
- valid[PROPERTIES_END] &&
2087
- ((l->lookahead != '&' && l->lookahead != '!') ||
2088
- (s->property_flags &&
2089
- !s->flow &&
2090
- s->line &&
2091
- (s->line_indent <= s->indent || s->line_mapping_key)))
2092
- ) {
2093
- s->properties_ready = true;
2094
- s->property_separation_notified = false;
2095
- if (l->lookahead == ':') {
2096
- step(l);
2097
- if (!colon_boundary(l, s->flow))
2098
- s->property_flags |= PROPERTY_COLON_CONTENT;
2099
- }
2100
- return emit(l, valid, PROPERTIES_END);
2101
- }
2102
2297
  if (property_pending) {
2103
2298
  bool content = !l->eof(l) && !(s->flow && flow_entry_end(l->lookahead));
2104
2299
  if (
2105
2300
  l->lookahead ==
2106
2301
  ':' &&
2107
- !(s->property_flags & PROPERTY_COLON_CONTENT) &&
2302
+ !s->property_colon_content &&
2108
2303
  !(s->line && !s->flow && valid[BLOCK_PROPERTY_CONTENT_START])
2109
2304
  )
2110
2305
  content = false;
@@ -2126,31 +2321,40 @@ static bool scan_properties(
2126
2321
  return emit(l, valid, property_content_start(s, l, valid));
2127
2322
  }
2128
2323
  if (
2129
- s->property_flags &&
2130
2324
  !s->separated &&
2131
2325
  !s->property_separation_notified &&
2132
2326
  valid[MISSING_SEPARATION] &&
2133
- ((l->lookahead == '&' && valid[ANCHOR_INDICATOR]) ||
2134
- (l->lookahead == '!' && valid[SHORTHAND_TAG_START]))
2327
+ ((l->lookahead ==
2328
+ '&' &&
2329
+ (valid[ANCHOR_INDICATOR] || valid[DUPLICATE_ANCHOR_START])) ||
2330
+ (l->lookahead ==
2331
+ '!' &&
2332
+ (valid[SHORTHAND_TAG_START] || valid[DUPLICATE_TAG_START])))
2135
2333
  ) {
2136
2334
  s->property_separation_notified = true;
2137
2335
  return emit(l, valid, MISSING_SEPARATION);
2138
2336
  }
2337
+ if (
2338
+ (l->lookahead == '&' && valid[DUPLICATE_ANCHOR_START]) ||
2339
+ (l->lookahead == '!' && valid[DUPLICATE_TAG_START])
2340
+ ) {
2341
+ bool anchor = l->lookahead == '&';
2342
+ bool unfinished = s->property_keys & (anchor ? ANCHOR_KEY : TAG_KEY);
2343
+ return emit(
2344
+ l,
2345
+ valid,
2346
+ anchor
2347
+ ? (unfinished ? UNFINISHED_DUPLICATE_ANCHOR_START
2348
+ : DUPLICATE_ANCHOR_START)
2349
+ : (unfinished ? UNFINISHED_DUPLICATE_TAG_START : DUPLICATE_TAG_START)
2350
+ );
2351
+ }
2139
2352
  if (
2140
2353
  (l->lookahead == '&' && valid[ANCHOR_INDICATOR]) ||
2141
2354
  (l->lookahead == '*' && valid[ALIAS_INDICATOR])
2142
2355
  ) {
2143
2356
  bool anchor = l->lookahead == '&';
2144
- if (
2145
- anchor &&
2146
- (s->property_flags & PROPERTY_ANCHOR) &&
2147
- !s->duplicate_pending &&
2148
- valid[DUPLICATE_ANCHOR_START]
2149
- ) {
2150
- s->duplicate_pending = true;
2151
- return emit(l, valid, DUPLICATE_ANCHOR_START);
2152
- }
2153
- begin_property(s);
2357
+ begin_property(s, anchor ? ANCHOR_KEY : 0);
2154
2358
  s->mode = anchor ? ANCHOR_BODY : ALIAS_BODY;
2155
2359
  s->first = true;
2156
2360
  s->json = false;
@@ -2163,29 +2367,20 @@ static bool scan_properties(
2163
2367
  valid[SHORTHAND_TAG_START] ||
2164
2368
  valid[NON_SPECIFIC_TAG])
2165
2369
  ) {
2166
- if (
2167
- (s->property_flags & PROPERTY_TAG) &&
2168
- !s->duplicate_pending &&
2169
- valid[DUPLICATE_TAG_START]
2170
- ) {
2171
- s->duplicate_pending = true;
2172
- return emit(l, valid, DUPLICATE_TAG_START);
2173
- }
2174
2370
  step(l);
2175
2371
  if (l->lookahead == '<')
2176
2372
  return emit(l, valid, VERBATIM_TAG_START);
2177
2373
  if (!name_boundary(l))
2178
2374
  return emit(l, valid, SHORTHAND_TAG_START);
2179
2375
  l->mark_end(l);
2180
- begin_property(s);
2181
- s->property_flags |= PROPERTY_TAG;
2376
+ begin_property(s, TAG_KEY);
2182
2377
  return emit(l, valid, NON_SPECIFIC_TAG);
2183
2378
  }
2184
2379
  if (valid[VERBATIM_TAG_OPEN]) {
2185
2380
  step(l);
2186
2381
  step(l);
2187
2382
  l->mark_end(l);
2188
- begin_property(s);
2383
+ begin_property(s, TAG_KEY);
2189
2384
  s->mode = URI_BEGIN;
2190
2385
  return emit(l, valid, VERBATIM_TAG_OPEN);
2191
2386
  }
@@ -2205,212 +2400,230 @@ static bool scan_properties(
2205
2400
  }
2206
2401
  }
2207
2402
  s->mode = HANDLE_SELECT;
2208
- begin_property(s);
2403
+ begin_property(s, TAG_KEY);
2209
2404
  return emit(l, valid, TAG_HANDLE_START);
2210
2405
  }
2211
2406
  return scan_content(s, l, valid);
2212
2407
  }
2213
2408
 
2214
- static bool scan_content(Scanner *s, TSLexer *l, const bool *valid) {
2409
+ static bool
2410
+ scan_normal(Scanner *s, TSLexer *l, const bool *valid, bool property_pending) {
2215
2411
  if (
2216
- l->lookahead == 0xfeff && l->get_column(l) == 0 && valid[BYTE_ORDER_MARK]
2412
+ !property_pending &&
2413
+ (valid[KEY_END] || valid[INVALID_KEY_END] || valid[FLOW_NODE_END])
2414
+ )
2415
+ return scan_key_end(s, l, valid);
2416
+ if (
2417
+ s->key_done &&
2418
+ (valid[MISSING_VALUE_INDICATOR] || valid[INCOMPLETE_VALUE_INDICATOR])
2217
2419
  ) {
2218
- step(l);
2219
- /* The line starts after the BOM; its layout is measured from there. */
2220
- cursor_start_line(l);
2221
- reset_line(s);
2222
- return finish(l, valid, BYTE_ORDER_MARK);
2223
- }
2224
- if (scan_invalid_character(l, valid))
2225
- return true;
2226
- bool end = l->eof(l);
2227
- if (s->after_document_end && !end) {
2228
- do {
2420
+ s->key_done = false;
2421
+ if (l->lookahead == ':') {
2229
2422
  step(l);
2230
- } while (
2231
- !l->eof(l) && !space(l->lookahead) && !invalid_unquoted(l->lookahead)
2423
+ if (colon_boundary(l, false))
2424
+ return emit_indicator(s, l, valid, VALUE_INDICATOR);
2425
+ }
2426
+ return emit_missing(
2427
+ l,
2428
+ valid,
2429
+ MISSING_VALUE_INDICATOR,
2430
+ INCOMPLETE_VALUE_INDICATOR
2232
2431
  );
2233
- return finish(l, valid, UNEXPECTED_DOCUMENT_END_CONTENT);
2234
2432
  }
2235
- int64_t column = s->line ? s->line_indent : l->get_column(l);
2433
+ if (s->line && !s->line_context && valid[LINE_CONTEXT])
2434
+ return scan_line_context(s, l, valid);
2435
+ if (s->line_context && !s->layout_only && !property_line_follows(s)) {
2436
+ s->properties_ready = false;
2437
+ property_pending = false;
2438
+ }
2439
+ static const enum Token restorations[] = {DEDENT, RESTORE_INDENT};
2236
2440
  if (
2237
- valid[EXPLICIT_VALUE_START] &&
2238
- l->lookahead ==
2239
- ':' &&
2240
- s->line_head ==
2241
- ':' &&
2242
- s->line &&
2243
- column == s->indent
2441
+ !property_pending &&
2442
+ emit_closing(
2443
+ s,
2444
+ l,
2445
+ valid,
2446
+ restorations,
2447
+ sizeof(restorations) / sizeof(*restorations)
2448
+ )
2244
2449
  )
2245
- return emit(l, valid, EXPLICIT_VALUE_START);
2450
+ return true;
2246
2451
  if (
2247
- valid[VALUE_START] || valid[SEQUENCE_VALUE_START] || valid[EMPTY_BLOCK_NODE]
2452
+ !property_pending &&
2453
+ s->line &&
2454
+ s->line_context &&
2455
+ !s->layout_only &&
2456
+ !s->flow
2248
2457
  ) {
2249
- if (block_value_follows(s, l, valid)) {
2250
- if (!s->line && valid[INVALID_COMPACT_START]) {
2251
- enum Token kind = classify_block_node(l);
2252
- if (block_collection(kind))
2253
- return emit(l, valid, INVALID_COMPACT_START);
2458
+ if (valid[EMPTY_BLOCK_NODE] && !block_value_follows(s, l, valid))
2459
+ return emit(l, valid, EMPTY_BLOCK_NODE);
2460
+ if (
2461
+ valid[MAPPING_END] &&
2462
+ (s->line_indent <
2463
+ s->indent ||
2464
+ (s->line_indent ==
2465
+ s->indent &&
2466
+ s->line_head ==
2467
+ '-' &&
2468
+ !valid[VALUE_START]))
2469
+ ) {
2470
+ if (s->line_indent == s->indent) {
2471
+ skip_blanks(l);
2472
+ step(l);
2473
+ s->eof_continuation = l->eof(l);
2254
2474
  }
2255
- s->block_out = valid[VALUE_START];
2256
- return emit(l, valid, s->block_out ? VALUE_START : SEQUENCE_VALUE_START);
2475
+ return emit(l, valid, MAPPING_END);
2257
2476
  }
2258
- return emit(l, valid, EMPTY_BLOCK_NODE);
2259
- }
2260
- if (valid[DOCUMENT_CLOSE] && end) {
2261
- close_document(s);
2262
- return emit(l, valid, DOCUMENT_CLOSE);
2263
- }
2264
- if (valid[DOCUMENT_OPEN] && !end && !s->document) {
2265
- open_document(s, !s->explicit_document_required);
2266
- return emit(l, valid, DOCUMENT_OPEN);
2267
- }
2268
- if (valid[UNEXPECTED_CONTENT_START] && s->document_content && !end) {
2269
- s->document_content = false;
2270
- return emit(l, valid, UNEXPECTED_CONTENT_START);
2477
+ if (
2478
+ valid[SEQUENCE_END] && (s->line_indent < s->indent || s->line_head != '-')
2479
+ )
2480
+ return emit(l, valid, SEQUENCE_END);
2271
2481
  }
2272
2482
  if (
2273
- !s->flow && !s->line && !valid[VALUE_START] && !valid[SEQUENCE_VALUE_START]
2274
- ) {
2275
- if (valid[MAPPING_END])
2276
- return emit(l, valid, MAPPING_END);
2277
- if (valid[SEQUENCE_END])
2278
- return emit(l, valid, SEQUENCE_END);
2483
+ s->line &&
2484
+ !s->line_prefixed &&
2485
+ valid[LINE_PREFIX_START] &&
2486
+ (blank(l->lookahead) ||
2487
+ (s->flow &&
2488
+ s->indent >=
2489
+ 0 &&
2490
+ !newline(l->lookahead) &&
2491
+ !l->eof(l) &&
2492
+ l->lookahead != '#'))
2493
+ )
2494
+ return begin_line_prefix(s, l, valid);
2495
+ if (newline(l->lookahead) && valid[LINE_BREAK]) {
2496
+ reset_line(s);
2497
+ return line_break(l, valid, LINE_BREAK);
2279
2498
  }
2280
- if (end) {
2281
- static const enum Token endings[] = {
2282
- MAPPING_END,
2283
- SEQUENCE_END,
2284
- INCOMPLETE_FLOW_SEQUENCE_CLOSE,
2285
- INCOMPLETE_FLOW_MAPPING_CLOSE,
2286
- END_OF_FILE
2287
- };
2288
- return emit_closing(
2499
+ if (blank(l->lookahead) && valid[SEPARATION])
2500
+ return scan_separation(l, valid);
2501
+ if (l->lookahead == '#' && valid[COMMENT_START])
2502
+ return start_comment(
2289
2503
  s,
2290
2504
  l,
2291
2505
  valid,
2292
- endings,
2293
- sizeof(endings) / sizeof(*endings)
2506
+ NORMAL,
2507
+ !s->separated && l->get_column(l) != 0
2294
2508
  );
2509
+ if (valid[DIRECTIVE_INDICATOR]) {
2510
+ s->mode = DIRECTIVE_NAME_BODY;
2511
+ s->first = true;
2512
+ return take(l, valid, DIRECTIVE_INDICATOR);
2295
2513
  }
2296
- if (valid[MAPPING_END] && column < s->indent)
2297
- return emit(l, valid, MAPPING_END);
2298
- if (
2299
- valid[SEQUENCE_END] &&
2300
- (column < s->indent || (s->line && l->lookahead != '-'))
2301
- )
2302
- return emit(l, valid, SEQUENCE_END);
2303
- return scan_entry(s, l, valid, column);
2304
- }
2305
-
2306
- static bool
2307
- scan_entry(Scanner *s, TSLexer *l, const bool *valid, int64_t column) {
2308
2514
  if (
2309
2515
  l->lookahead ==
2310
- ',' &&
2311
- !valid[FLOW_SEPARATOR] &&
2312
- valid[UNEXPECTED_FLOW_SEPARATOR]
2313
- )
2314
- return take(l, valid, UNEXPECTED_FLOW_SEPARATOR);
2315
- if (valid[FLOW_MAP_PAIR_START] && !flow_entry_end(l->lookahead)) {
2316
- s->json = false;
2317
- return emit(l, valid, FLOW_MAP_PAIR_START);
2318
- }
2319
- if (valid[IMPLICIT_KEY_START] && !(s->flow && flow_entry_end(l->lookahead))) {
2320
- int32_t first = l->lookahead;
2321
- bool indicator = false;
2322
- if (l->lookahead == '?' || l->lookahead == ':') {
2516
+ '%' &&
2517
+ l->get_column(l) ==
2518
+ 0 &&
2519
+ valid[RESERVED_DIRECTIVE_START]
2520
+ ) {
2521
+ char name[5] = {0};
2522
+ unsigned length = 0;
2523
+ step(l);
2524
+ while (!directive_boundary(l)) {
2525
+ if (length < 4 && l->lookahead < 128)
2526
+ name[length] = (char)l->lookahead;
2527
+ if (length < 5)
2528
+ length++;
2323
2529
  step(l);
2324
- indicator = colon_boundary(l, first == ':' && s->flow);
2325
- }
2326
- if (indicator) {
2327
- if (valid[FLOW_SEQ_PAIR_START]) {
2328
- s->json = false;
2329
- return emit(l, valid, FLOW_SEQ_PAIR_START);
2330
- }
2331
- return emit_indicator(
2332
- s,
2333
- l,
2334
- valid,
2335
- first == '?' ? KEY_INDICATOR : VALUE_INDICATOR
2336
- );
2337
2530
  }
2338
- s->implicit_keys[0] |= 1;
2339
- s->block_out = false;
2340
- return emit(l, valid, IMPLICIT_KEY_START);
2531
+ s->directive_kind = length == 4 && !strcmp(name, "YAML") ? YAML_DIRECTIVE
2532
+ : length == 3 && !strcmp(name, "TAG") ? TAG_DIRECTIVE
2533
+ : RESERVED_DIRECTIVE;
2534
+ return emit(
2535
+ l,
2536
+ valid,
2537
+ s->directive_kind == YAML_DIRECTIVE ? YAML_DIRECTIVE_START
2538
+ : s->directive_kind == TAG_DIRECTIVE ? TAG_DIRECTIVE_START
2539
+ : RESERVED_DIRECTIVE_START
2540
+ );
2341
2541
  }
2342
- if (l->lookahead == '?' && valid[KEY_INDICATOR]) {
2343
- step(l);
2344
- if (space(l->lookahead) || l->eof(l)) {
2345
- return emit_indicator(s, l, valid, KEY_INDICATOR);
2346
- }
2347
- return start_plain(s, l, valid);
2542
+ if (
2543
+ !s->document_started &&
2544
+ (valid[MISSING_DOCUMENT_START] || valid[INCOMPLETE_DOCUMENT_START])
2545
+ ) {
2546
+ s->document_started = true;
2547
+ s->explicit_document_required = false;
2548
+ if (s->boundary_pending == DOCUMENT_START_LINE)
2549
+ return emit(l, valid, INCOMPLETE_DOCUMENT_START);
2550
+ return emit_missing(
2551
+ l,
2552
+ valid,
2553
+ MISSING_DOCUMENT_START,
2554
+ INCOMPLETE_DOCUMENT_START
2555
+ );
2348
2556
  }
2349
- if (l->lookahead == ':' && valid[VALUE_INDICATOR]) {
2350
- bool adjacent = s->flow && s->json;
2351
- step(l);
2352
- if (adjacent || colon_boundary(l, s->flow)) {
2353
- s->value_separation =
2354
- s->flow && !adjacent && (l->lookahead == '[' || l->lookahead == '{');
2355
- s->json = false;
2356
- return emit_indicator(s, l, valid, VALUE_INDICATOR);
2557
+ return scan_properties(s, l, valid, property_pending);
2558
+ }
2559
+
2560
+ static bool scan_property_tail(Scanner *s, TSLexer *l, const bool *valid) {
2561
+ Scanner context = *s;
2562
+ for (;;) {
2563
+ if (context.line) {
2564
+ context.line_indent = count_spaces(l);
2565
+ }
2566
+ skip_blanks(l);
2567
+ if (l->lookahead == '#') {
2568
+ while (!l->eof(l) && !newline(l->lookahead))
2569
+ step(l);
2357
2570
  }
2358
- return start_plain(s, l, valid) || emit(l, valid, MISSING_FLOW_SEPARATOR);
2571
+ if (!newline(l->lookahead))
2572
+ break;
2573
+ skip_line_break(l);
2574
+ context.line = true;
2359
2575
  }
2360
- if (l->lookahead == '-' && valid[SEQUENCE_INDICATOR]) {
2576
+ bool end = l->eof(l);
2577
+ int32_t first = l->lookahead;
2578
+ context.line_head = first;
2579
+ bool boundary = false;
2580
+ if (first == '-' && context.line && !s->flow) {
2581
+ bool column_zero = l->get_column(l) == 0;
2361
2582
  step(l);
2362
- return emit_indicator(s, l, valid, SEQUENCE_INDICATOR);
2363
- }
2364
- if (s->value_separation && valid[MISSING_SEPARATION]) {
2365
- s->value_separation = false;
2366
- return emit(l, valid, MISSING_SEPARATION);
2367
- }
2368
- if (l->lookahead == ',' && valid[FLOW_SEPARATOR])
2369
- return take(l, valid, FLOW_SEPARATOR);
2370
- if (l->lookahead == ']' && valid[FLOW_SEQUENCE_CLOSE])
2371
- return close_flow(s, l, valid, FLOW_SEQUENCE_CLOSE);
2372
- if (l->lookahead == '}' && valid[FLOW_MAPPING_CLOSE])
2373
- return close_flow(s, l, valid, FLOW_MAPPING_CLOSE);
2374
- if (l->lookahead == ']' && valid[MISSING_FLOW_MAPPING_CLOSE])
2375
- return close_flow(s, l, valid, MISSING_FLOW_MAPPING_CLOSE);
2376
- if (l->lookahead == '}' && valid[MISSING_FLOW_SEQUENCE_CLOSE])
2377
- return close_flow(s, l, valid, MISSING_FLOW_SEQUENCE_CLOSE);
2378
- if (
2379
- valid[MISSING_FLOW_SEPARATOR] &&
2380
- !valid[PLAIN_START] &&
2381
- !valid[SINGLE_START] &&
2382
- !valid[DOUBLE_START] &&
2383
- !valid[FLOW_SEQUENCE_START] &&
2384
- !valid[FLOW_MAPPING_START] &&
2385
- !flow_entry_end(l->lookahead)
2386
- )
2387
- return emit(l, valid, MISSING_FLOW_SEPARATOR);
2583
+ if (!space(l->lookahead) && !l->eof(l))
2584
+ context.line_head = 0;
2585
+ if (column_zero)
2586
+ boundary = document_marker_tail(l, first) != NO_BOUNDARY;
2587
+ } else if (l->get_column(l) == 0) {
2588
+ boundary = line_boundary(&context, l) != NO_BOUNDARY;
2589
+ }
2590
+ enum Token content_end =
2591
+ valid[BLOCK_PROPERTIES_END] ? BLOCK_PROPERTIES_END : PROPERTIES_END;
2592
+ enum Token token = content_end;
2388
2593
  if (
2389
- valid[PAIR_START] ||
2390
- valid[ENTRY_START] ||
2391
- valid[MAPPING_START] ||
2392
- valid[SEQUENCE_START] ||
2393
- valid[PLAIN_START] ||
2394
- valid[INDENT] ||
2395
- valid[OUTDENT] ||
2396
- valid[SINGLE_START] ||
2397
- valid[DOUBLE_START] ||
2398
- valid[FLOW_SEQUENCE_START] ||
2399
- valid[FLOW_MAPPING_START] ||
2400
- valid[LITERAL_START] ||
2401
- valid[FOLDED_START] ||
2402
- valid[PROPERTIES_START] ||
2403
- valid[ALIAS_START]
2594
+ boundary ||
2595
+ end ||
2596
+ (s->flow && flow_entry_end(first)) ||
2597
+ !property_line_follows(&context)
2404
2598
  ) {
2405
- return scan_node_start(s, l, valid, column);
2599
+ token = EMPTY_PROPERTIES_END;
2600
+ } else if (first == ':') {
2601
+ step(l);
2602
+ if (colon_boundary(l, s->flow)) {
2603
+ if (s->flow || !context.line || !valid[BLOCK_PROPERTIES_END])
2604
+ token = EMPTY_PROPERTIES_END;
2605
+ } else {
2606
+ s->property_colon_content = true;
2607
+ }
2608
+ } else if (first == '&' || first == '!') {
2609
+ token = PROPERTY_CONTINUE;
2610
+ if (context.line && !s->flow) {
2611
+ if (classify_block_node(l, NULL) == MAPPING_START)
2612
+ token = content_end;
2613
+ s->property_keys = l->eof(l) ? ANCHOR_KEY | TAG_KEY : 0;
2614
+ }
2406
2615
  }
2407
- return scan_node_open(s, l, valid);
2616
+ s->properties_ready = token == content_end;
2617
+ s->property_separation_notified = false;
2618
+ return emit(l, valid, token);
2408
2619
  }
2409
2620
 
2410
2621
  static bool scan(Scanner *s, TSLexer *l, const bool *valid) {
2411
2622
  if (valid[ERROR_SENTINEL])
2412
2623
  return false;
2413
2624
  l->mark_end(l);
2625
+ if (s->mode == NORMAL && valid[EMPTY_PROPERTIES_END])
2626
+ return scan_property_tail(s, l, valid);
2414
2627
  bool boundary_mode = s->mode ==
2415
2628
  NORMAL ||
2416
2629
  s->mode ==
@@ -2431,14 +2644,13 @@ static bool scan(Scanner *s, TSLexer *l, const bool *valid) {
2431
2644
  enum Boundary boundary = line_boundary(s, l);
2432
2645
  if (boundary || first == '-' || first == '.') {
2433
2646
  s->boundary = (uint8_t)boundary;
2647
+ s->boundary_pending = l->eof(l)
2648
+ ? first == '-' ? DOCUMENT_START_LINE : DOCUMENT_END_LINE
2649
+ : NO_BOUNDARY;
2434
2650
  s->boundary_checked = true;
2435
2651
  return emit(l, valid, BOUNDARY_CHECK);
2436
2652
  }
2437
2653
  }
2438
- if (s->mode == NORMAL && s->boundary && valid[PROPERTIES_END]) {
2439
- s->properties_ready = true;
2440
- return emit(l, valid, PROPERTIES_END);
2441
- }
2442
2654
  if (s->boundary)
2443
2655
  s->properties_ready = false;
2444
2656
  bool property_pending = s->properties_ready &&
@@ -2534,6 +2746,9 @@ static bool layout_token(TSSymbol token) {
2534
2746
  case LINE_PREFIX_END:
2535
2747
  case INDENTATION:
2536
2748
  case INVALID_INDENTATION:
2749
+ case TAB_IN_INDENTATION:
2750
+ case UNFINISHED_INVALID_INDENTATION:
2751
+ case UNFINISHED_TAB_IN_INDENTATION:
2537
2752
  case BYTE_ORDER_MARK:
2538
2753
  case LINE_CONTEXT:
2539
2754
  case BOUNDARY_CHECK:
@@ -2545,6 +2760,7 @@ static bool layout_token(TSSymbol token) {
2545
2760
  static void reset_scanner(Scanner *s) {
2546
2761
  *s = (Scanner){
2547
2762
  .indent = -1,
2763
+ .provisional_indent = -2,
2548
2764
  .block_indent = -1,
2549
2765
  .line = true,
2550
2766
  .separated = true
@@ -2646,6 +2862,7 @@ bool tree_sitter_yaml_external_scanner_scan(
2646
2862
  advance_keys(&next, cursor.marked_characters, cursor.marked_broken);
2647
2863
  next.boundary = 0;
2648
2864
  next.boundary_checked = false;
2865
+ next.boundary_pending = 0;
2649
2866
  if (!layout_token(cursor.lexer.result_symbol))
2650
2867
  next.line = false;
2651
2868
  }