@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/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "konomanoasa-tree-sitter-yaml"
3
- version = "0.1.0"
3
+ version = "0.3.0"
4
4
  edition = "2024"
5
5
  description = "Tree-sitter grammar for YAML."
6
6
  readme = "README.md"
package/README.md CHANGED
@@ -7,22 +7,20 @@
7
7
  [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) grammar for
8
8
  YAML Ain't Markup Language 1.2.2.
9
9
 
10
+ ## Syntax Issues
11
+
12
+ The parser detects syntax issues, including missing syntax, while preserving the surrounding structure.
13
+ These issues are represented as `syntax_issue` nodes.
14
+
10
15
  ## Installation
11
16
 
12
17
  ```sh
13
18
  npm install @konomanoasa/tree-sitter-yaml
14
19
  ```
15
20
 
16
- For Rust, add this dependency to your `Cargo.toml`:
17
-
18
- ```toml
19
- [dependencies]
20
- konomanoasa-tree-sitter-yaml = "0.1.0"
21
- ```
22
-
23
21
  ## Development
24
22
 
25
- Development uses Node.js 24 or later.
23
+ Development uses Node.js 24.21.0 or later.
26
24
 
27
25
  ```sh
28
26
  npm install
package/grammar.js CHANGED
@@ -2,7 +2,7 @@ const problems = [
2
2
  ["invalid_encoding", "invalid_syntax", "invalid_encoding"],
3
3
  ["invalid_character", "invalid_syntax", "invalid_character"],
4
4
  ["invalid_escape", "invalid_syntax", "invalid_escape"],
5
- ["incomplete_escape", "incomplete_syntax", "incomplete_escape"],
5
+ ["incomplete_escape", "incomplete_syntax", "invalid_escape"],
6
6
  [
7
7
  "invalid_line_continuation",
8
8
  "invalid_syntax",
@@ -10,16 +10,19 @@ const problems = [
10
10
  "escape_indicator",
11
11
  ],
12
12
  ["invalid_scalar_start", "invalid_syntax", "invalid_scalar_start"],
13
+ ["incomplete_scalar_start", "incomplete_syntax", "invalid_scalar_start"],
13
14
  ["missing_flow_separator", "invalid_syntax", "missing_flow_separator"],
14
15
  ["unexpected_flow_separator", "invalid_syntax", "unexpected_flow_separator"],
15
16
  ["invalid_indentation", "invalid_syntax", "invalid_indentation"],
17
+ ["tab_in_indentation", "invalid_syntax", "tab_in_indentation"],
16
18
  ["missing_indentation", "invalid_syntax", "missing_indentation"],
17
19
  ["missing_separation", "invalid_syntax", "missing_separation"],
18
- ["invalid_block_header", "invalid_syntax", "invalid_block_header"],
20
+ ["invalid_block_header", "invalid_syntax", "invalid_header_character"],
19
21
  ["invalid_tag_handle", "invalid_syntax", "invalid_tag_handle"],
20
22
  ["invalid_tag_character", "invalid_syntax", "invalid_tag_character"],
23
+ ["invalid_tag_prefix_start", "invalid_syntax", "invalid_tag_prefix_start"],
21
24
  ["invalid_uri_escape", "invalid_syntax", "invalid_uri_escape"],
22
- ["incomplete_uri_escape", "incomplete_syntax", "incomplete_uri_escape"],
25
+ ["incomplete_uri_escape", "incomplete_syntax", "invalid_uri_escape"],
23
26
  [
24
27
  "invalid_yaml_version",
25
28
  "invalid_syntax",
@@ -29,7 +32,7 @@ const problems = [
29
32
  [
30
33
  "unfinished_yaml_version",
31
34
  "incomplete_syntax",
32
- "incomplete_yaml_version",
35
+ "invalid_yaml_version",
33
36
  "yaml_version",
34
37
  ],
35
38
  [
@@ -46,13 +49,13 @@ const problems = [
46
49
  [
47
50
  "invalid_header_indentation",
48
51
  "invalid_syntax",
49
- "invalid_block_header",
52
+ "unexpected_indentation_indicator",
50
53
  "indentation_indicator",
51
54
  ],
52
55
  [
53
56
  "invalid_header_chomping",
54
57
  "invalid_syntax",
55
- "invalid_block_header",
58
+ "unexpected_chomping_indicator",
56
59
  "chomping_indicator",
57
60
  ],
58
61
  ["missing_document_end", "invalid_syntax", "missing_document_end"],
@@ -76,13 +79,65 @@ const problems = [
76
79
  [`missing_${name}`, "invalid_syntax", `missing_${name}`],
77
80
  [`incomplete_${name}`, "incomplete_syntax", `missing_${name}`],
78
81
  ]),
82
+ ...[
83
+ "invalid_indentation",
84
+ "tab_in_indentation",
85
+ "missing_indentation",
86
+ "missing_flow_separator",
87
+ ].map((name) => [`unfinished_${name}`, "incomplete_syntax", name]),
79
88
  ];
80
- const issue = ($, name) =>
81
- field("issue", alias($[`_${name}_issue`], $.syntax_issue));
82
- const missing = ($, name) =>
83
- choice(issue($, `missing_${name}`), issue($, `incomplete_${name}`));
84
- const invalid = ($) =>
85
- choice(issue($, "invalid_encoding"), issue($, "invalid_character"));
89
+
90
+ const structuredProblems = [
91
+ {
92
+ name: "unexpected_document_content",
93
+ reason: "unexpected_document_content",
94
+ start: "_unexpected_content_start",
95
+ body: ($) => seq(field("content", $._node), $._document_content_end),
96
+ },
97
+ {
98
+ name: "invalid_document_collection",
99
+ reason: "invalid_compact_collection",
100
+ start: "_document_compact_start",
101
+ body: ($) => field("content", $._node),
102
+ },
103
+ {
104
+ name: "duplicate_anchor",
105
+ reason: "duplicate_anchor",
106
+ start: "_duplicate_anchor_start",
107
+ body: ($) => field("property", $.anchor),
108
+ },
109
+ {
110
+ name: "duplicate_tag",
111
+ reason: "duplicate_tag",
112
+ start: "_duplicate_tag_start",
113
+ body: ($) => field("property", $._tag),
114
+ },
115
+ {
116
+ name: "properties_on_alias",
117
+ reason: "properties_on_alias",
118
+ start: "_property_alias_start",
119
+ body: ($) => field("content", $.alias),
120
+ },
121
+ {
122
+ name: "invalid_property_collection",
123
+ reason: "invalid_compact_collection",
124
+ start: "_property_compact_start",
125
+ body: ($) => field("content", $._node_content),
126
+ },
127
+ {
128
+ name: "invalid_compact_collection",
129
+ reason: "invalid_compact_collection",
130
+ start: "_invalid_compact_start",
131
+ body: ($) => field("value", $._node),
132
+ },
133
+ {
134
+ name: "invalid_implicit_key",
135
+ reason: "invalid_implicit_key",
136
+ end: ["_invalid_key_end", "_unfinished_invalid_key_end"],
137
+ body: ($) => seq($._implicit_key_start, field("key", $._flow_node)),
138
+ },
139
+ ];
140
+
86
141
  const problemRules = Object.fromEntries(
87
142
  problems.flatMap(([name, outcome, reason, leaf]) => [
88
143
  ...(leaf
@@ -95,65 +150,45 @@ const problemRules = Object.fromEntries(
95
150
  [`_${name}_issue`, ($) => alias($[`_${name}_outcome`], $[outcome])],
96
151
  ]),
97
152
  );
98
- const structuredProblems = [
99
- [
100
- "unexpected_document_content",
101
- "unexpected_document_content",
102
- ($) =>
103
- seq(
104
- $._unexpected_content_start,
105
- field("content", $._node),
106
- $._document_content_end,
107
- ),
108
- ],
109
- [
110
- "invalid_document_collection",
111
- "invalid_compact_collection",
112
- ($) => seq($._document_compact_start, field("content", $._node)),
113
- ],
114
- [
115
- "duplicate_anchor",
116
- "duplicate_anchor",
117
- ($) => seq($._duplicate_anchor_start, field("property", $.anchor)),
118
- ],
119
- [
120
- "duplicate_tag",
121
- "duplicate_tag",
122
- ($) => seq($._duplicate_tag_start, field("property", $._tag)),
123
- ],
124
- [
125
- "properties_on_alias",
126
- "properties_on_alias",
127
- ($) => seq($._property_alias_start, field("content", $.alias)),
128
- ],
129
- [
130
- "invalid_property_collection",
131
- "invalid_compact_collection",
132
- ($) => seq($._property_compact_start, field("content", $._node_content)),
133
- ],
134
- [
135
- "invalid_compact_collection",
136
- "invalid_compact_collection",
137
- ($) => seq($._invalid_compact_start, field("value", $._node)),
138
- ],
139
- [
140
- "invalid_implicit_key",
141
- "invalid_implicit_key",
142
- ($) =>
143
- seq(
144
- $._implicit_key_start,
145
- field("key", $._flow_node),
146
- $._invalid_key_end,
147
- ),
148
- ],
149
- ];
150
- for (const [name, reason, body] of structuredProblems) {
151
- problemRules[`_${name}_reason`] = body;
152
- problemRules[`_${name}_outcome`] = ($) =>
153
- alias($[`_${name}_reason`], $[reason]);
154
- problemRules[`_${name}_issue`] = ($) =>
155
- alias($[`_${name}_outcome`], $.invalid_syntax);
153
+
154
+ for (const { name, reason, start, end, body } of structuredProblems) {
155
+ for (const unfinished of [false, true]) {
156
+ const id = unfinished ? `unfinished_${name}` : name;
157
+ problemRules[`_${id}_reason`] = ($) =>
158
+ end
159
+ ? seq(body($), $[end[Number(unfinished)]])
160
+ : seq($[unfinished ? `_${id}_start` : start], body($));
161
+ problemRules[`_${id}_outcome`] = ($) =>
162
+ alias($[`_${id}_reason`], $[reason]);
163
+ problemRules[`_${id}_issue`] = ($) =>
164
+ alias(
165
+ $[`_${id}_outcome`],
166
+ unfinished ? $.incomplete_syntax : $.invalid_syntax,
167
+ );
168
+ }
156
169
  }
170
+
171
+ const issue = ($, name) =>
172
+ field("issue", alias($[`_${name}_issue`], $.syntax_issue));
173
+
174
+ const missing = ($, name) =>
175
+ choice(issue($, `missing_${name}`), issue($, `incomplete_${name}`));
176
+
177
+ const closing = ($, name) =>
178
+ choice(field("closing", $[name]), missing($, name));
179
+
180
+ const invalid = ($) =>
181
+ choice(issue($, "invalid_encoding"), issue($, "invalid_character"));
182
+
183
+ const unfinishedIssue = ($, name) =>
184
+ choice(issue($, name), issue($, `unfinished_${name}`));
185
+
186
+ const indentationIssue = ($) =>
187
+ choice(
188
+ unfinishedIssue($, "invalid_indentation"),
189
+ unfinishedIssue($, "tab_in_indentation"),
190
+ );
191
+
157
192
  export default grammar({
158
193
  name: "yaml",
159
194
  externals: ($) => [
@@ -191,6 +226,7 @@ export default grammar({
191
226
  $._implicit_key_start,
192
227
  $._key_end,
193
228
  $._invalid_key_end,
229
+ $._unfinished_invalid_key_end,
194
230
  $._flow_node_end,
195
231
  $._plain_start,
196
232
  $._plain_end,
@@ -239,6 +275,9 @@ export default grammar({
239
275
  $._unexpected_content_start,
240
276
  $._properties_start,
241
277
  $._properties_end,
278
+ $._block_properties_end,
279
+ $._empty_properties_end,
280
+ $._property_continue,
242
281
  $._property_content_start,
243
282
  $._block_property_content_start,
244
283
  $._block_key_content_start,
@@ -281,7 +320,11 @@ export default grammar({
281
320
  $._tag_prefix_start,
282
321
  $._tag_prefix_end,
283
322
  $.directive_parameter,
323
+ ...structuredProblems
324
+ .filter(({ start }) => start)
325
+ .map(({ name }) => $[`_unfinished_${name}_start`]),
284
326
  ...problems.map(([name]) => $[`_${name}`]),
327
+ $._undecodable_escape_prefix,
285
328
  $._error_sentinel,
286
329
  ],
287
330
  extras: ($) => [
@@ -324,12 +367,12 @@ export default grammar({
324
367
  seq(
325
368
  choice(
326
369
  field("content", $._node),
327
- issue($, "invalid_document_collection"),
370
+ unfinishedIssue($, "invalid_document_collection"),
328
371
  ),
329
372
  $._document_content_end,
330
373
  ),
331
374
  ),
332
- repeat(issue($, "unexpected_document_content")),
375
+ repeat(unfinishedIssue($, "unexpected_document_content")),
333
376
  $._document_close,
334
377
  ),
335
378
  yaml_directive: ($) =>
@@ -374,24 +417,11 @@ export default grammar({
374
417
  $._directive_end,
375
418
  ),
376
419
  _directive_named_handle: ($) =>
377
- seq(
378
- field("opening", $.tag_handle_open),
379
- repeat1(
380
- choice(
381
- field("name", $.tag_handle_name),
382
- issue($, "invalid_tag_handle"),
383
- invalid($),
384
- ),
385
- ),
386
- choice(
387
- field("closing", $.tag_handle_close),
388
- missing($, "tag_handle_close"),
389
- ),
390
- ),
420
+ namedTagHandle($, closing($, "tag_handle_close")),
391
421
  tag_prefix: ($) =>
392
422
  seq(
393
423
  $._tag_prefix_start,
394
- repeat1(uriPart($, $.uri_text)),
424
+ repeat1(uriPart($, $.uri_text, true)),
395
425
  $._tag_prefix_end,
396
426
  ),
397
427
  _node: ($) => choice($._node_content, $.alias, $.node_with_properties),
@@ -423,6 +453,7 @@ export default grammar({
423
453
  propertyNode(
424
454
  $,
425
455
  seq($._block_property_content_start, field("content", $._node_content)),
456
+ true,
426
457
  ),
427
458
  _flow_properties: ($) =>
428
459
  propertyNode(
@@ -432,7 +463,6 @@ export default grammar({
432
463
  seq($._block_key_content_start, field("content", $._node_content)),
433
464
  ),
434
465
  ),
435
- _property: ($) => choice($.anchor, $._tag),
436
466
  _tag: ($) => choice($.non_specific_tag, $.shorthand_tag, $.verbatim_tag),
437
467
  anchor: ($) =>
438
468
  seq(
@@ -469,26 +499,13 @@ export default grammar({
469
499
  $._tag_end,
470
500
  ),
471
501
  named_tag_handle: ($) =>
472
- seq(
473
- field("opening", $.tag_handle_open),
474
- repeat1(
475
- choice(
476
- field("name", $.tag_handle_name),
477
- issue($, "invalid_tag_handle"),
478
- invalid($),
479
- ),
480
- ),
481
- field("closing", $.tag_handle_close),
482
- ),
502
+ namedTagHandle($, field("closing", $.tag_handle_close)),
483
503
  verbatim_tag: ($) =>
484
504
  seq(
485
505
  $._verbatim_tag_start,
486
506
  field("opening", $.verbatim_tag_open),
487
507
  choice(field("uri", $.tag_uri), missing($, "tag_uri")),
488
- choice(
489
- field("closing", $.verbatim_tag_close),
490
- missing($, "verbatim_tag_close"),
491
- ),
508
+ closing($, "verbatim_tag_close"),
492
509
  ),
493
510
  tag_uri: ($) =>
494
511
  seq($._tag_uri_start, repeat1(uriPart($, $.uri_text)), $._tag_uri_end),
@@ -537,7 +554,7 @@ export default grammar({
537
554
  field("indicator", $.value_indicator),
538
555
  choice(
539
556
  seq($._value_start, field("value", $._node)),
540
- issue($, "invalid_compact_collection"),
557
+ unfinishedIssue($, "invalid_compact_collection"),
541
558
  $._empty_block_node,
542
559
  ),
543
560
  ),
@@ -562,19 +579,39 @@ export default grammar({
562
579
  block_scalar_header: ($) =>
563
580
  seq(
564
581
  field("style", choice($.literal_indicator, $.folded_indicator)),
565
- repeat(
582
+ repeat($._block_header_issue),
583
+ optional(
566
584
  choice(
567
- field("indentation", $.indentation_indicator),
568
- field("chomping", $.chomping_indicator),
569
- issue($, "invalid_block_header"),
570
- issue($, "invalid_header_indentation"),
571
- issue($, "invalid_header_chomping"),
572
- invalid($),
585
+ seq(
586
+ $._block_header_indentation,
587
+ optional($._block_header_chomping),
588
+ ),
589
+ seq(
590
+ $._block_header_chomping,
591
+ optional($._block_header_indentation),
592
+ ),
573
593
  ),
574
594
  ),
575
595
  optional($.block_header_break),
576
596
  $._block_header_end,
577
597
  ),
598
+ _block_header_issue: ($) =>
599
+ choice(
600
+ issue($, "invalid_block_header"),
601
+ issue($, "invalid_header_indentation"),
602
+ issue($, "invalid_header_chomping"),
603
+ invalid($),
604
+ ),
605
+ _block_header_indentation: ($) =>
606
+ seq(
607
+ field("indentation", $.indentation_indicator),
608
+ repeat($._block_header_issue),
609
+ ),
610
+ _block_header_chomping: ($) =>
611
+ seq(
612
+ field("chomping", $.chomping_indicator),
613
+ repeat($._block_header_issue),
614
+ ),
578
615
  block_scalar_line: ($) => blockLine($, $._block_text_line_start),
579
616
  block_scalar_spaced_line: ($) => blockLine($, $._block_spaced_line_start),
580
617
  block_scalar_empty_line: ($) => blockLine($, $._block_empty_line_start),
@@ -588,10 +625,11 @@ export default grammar({
588
625
  $.scalar_line_break,
589
626
  $.scalar_line_prefix,
590
627
  $.scalar_line_suffix,
591
- issue($, "invalid_indentation"),
592
- issue($, "missing_indentation"),
628
+ indentationIssue($),
629
+ unfinishedIssue($, "missing_indentation"),
593
630
  invalid($),
594
631
  issue($, "invalid_scalar_start"),
632
+ issue($, "incomplete_scalar_start"),
595
633
  ),
596
634
  ),
597
635
  $._plain_end,
@@ -604,6 +642,7 @@ export default grammar({
604
642
  $.line_continuation,
605
643
  issue($, "invalid_escape"),
606
644
  issue($, "incomplete_escape"),
645
+ seq($._undecodable_escape_prefix, issue($, "invalid_encoding")),
607
646
  ]),
608
647
  line_continuation: ($) =>
609
648
  prec.right(
@@ -624,20 +663,14 @@ export default grammar({
624
663
  alias($._flow_sequence_pair, $.flow_mapping_pair),
625
664
  ),
626
665
  ),
627
- choice(
628
- field("closing", $.flow_sequence_close),
629
- missing($, "flow_sequence_close"),
630
- ),
666
+ closing($, "flow_sequence_close"),
631
667
  ),
632
668
  flow_mapping: ($) =>
633
669
  seq(
634
670
  $._flow_mapping_start,
635
671
  field("opening", $.flow_mapping_open),
636
672
  flowContent($, $.flow_mapping_pair),
637
- choice(
638
- field("closing", $.flow_mapping_close),
639
- missing($, "flow_mapping_close"),
640
- ),
673
+ closing($, "flow_mapping_close"),
641
674
  ),
642
675
  flow_mapping_pair: ($) => seq($._flow_map_pair_start, $._flow_pair_body),
643
676
  _flow_sequence_pair: ($) =>
@@ -648,7 +681,7 @@ export default grammar({
648
681
  _implicit_key: ($) =>
649
682
  choice(
650
683
  seq($._implicit_key_start, field("key", $._flow_node), $._key_end),
651
- issue($, "invalid_implicit_key"),
684
+ unfinishedIssue($, "invalid_implicit_key"),
652
685
  ),
653
686
  _flow_pair_body: ($) =>
654
687
  choice(
@@ -671,38 +704,56 @@ export default grammar({
671
704
  choice(
672
705
  $.indentation,
673
706
  $.separation,
674
- issue($, "invalid_indentation"),
675
- issue($, "missing_indentation"),
707
+ indentationIssue($),
708
+ unfinishedIssue($, "missing_indentation"),
676
709
  ),
677
710
  ),
678
711
  $._line_prefix_end,
679
712
  ),
680
- // A nonmatching internal token keeps lexer fallback from accepting EOF mid-input.
713
+ // Prevents lexer fallback from accepting EOF mid-input.
681
714
  _unmatchable: () => token(seq(/[\s\S]/, /[^\s\S]/)),
682
715
  ...problemRules,
683
716
  },
684
717
  });
685
718
 
686
719
  function flowContent($, entry) {
687
- const extra = () => repeat(issue($, "unexpected_flow_separator"));
720
+ const unexpectedSeparators = repeat(issue($, "unexpected_flow_separator"));
688
721
  return seq(
689
- extra(),
722
+ unexpectedSeparators,
690
723
  optional(
691
724
  seq(
692
725
  entry,
693
726
  repeat(
694
727
  seq(
695
- choice($.flow_separator, issue($, "missing_flow_separator")),
696
- extra(),
728
+ choice(
729
+ $.flow_separator,
730
+ unfinishedIssue($, "missing_flow_separator"),
731
+ ),
732
+ unexpectedSeparators,
697
733
  entry,
698
734
  ),
699
735
  ),
700
- optional(seq($.flow_separator, extra())),
736
+ optional(seq($.flow_separator, unexpectedSeparators)),
701
737
  ),
702
738
  ),
703
739
  );
704
740
  }
705
741
 
742
+ function explicitFlowPair($) {
743
+ return seq(
744
+ field("key_indicator", $.key_indicator),
745
+ optional(field("key", $._flow_node)),
746
+ optional(flowValue($)),
747
+ );
748
+ }
749
+ function flowValue($) {
750
+ return seq(
751
+ field("indicator", $.value_indicator),
752
+ optional(issue($, "missing_separation")),
753
+ optional(field("value", $._flow_node)),
754
+ );
755
+ }
756
+
706
757
  function blockScalar($, start) {
707
758
  return seq(
708
759
  start,
@@ -725,8 +776,8 @@ function blockLine($, start) {
725
776
  repeat(
726
777
  choice(
727
778
  $.scalar_text,
728
- issue($, "missing_indentation"),
729
- issue($, "invalid_indentation"),
779
+ unfinishedIssue($, "missing_indentation"),
780
+ indentationIssue($),
730
781
  invalid($),
731
782
  ),
732
783
  ),
@@ -735,24 +786,38 @@ function blockLine($, start) {
735
786
  );
736
787
  }
737
788
 
738
- function propertyNode($, content) {
789
+ function propertyNode($, content, block = false) {
790
+ const duplicate = (name) => unfinishedIssue($, `duplicate_${name}`);
791
+ const separation = issue($, "missing_separation");
792
+ const next = (rule) => seq($._property_continue, optional(separation), rule);
793
+ const both = repeat(next(choice(duplicate("anchor"), duplicate("tag"))));
794
+ const ordered = (first, second, rule) =>
795
+ seq(
796
+ field("property", rule),
797
+ repeat(next(duplicate(first))),
798
+ optional(
799
+ seq(
800
+ next(field("property", second === "anchor" ? $.anchor : $._tag)),
801
+ both,
802
+ ),
803
+ ),
804
+ );
739
805
  return seq(
740
806
  $._properties_start,
741
- repeat1(
742
- choice(
743
- field("property", $._property),
744
- issue($, "duplicate_anchor"),
745
- issue($, "duplicate_tag"),
746
- issue($, "missing_separation"),
747
- ),
807
+ choice(
808
+ ordered("anchor", "tag", $.anchor),
809
+ ordered("tag", "anchor", $._tag),
748
810
  ),
749
- $._properties_end,
750
- optional(issue($, "missing_separation")),
751
- optional(
752
- choice(
753
- content,
754
- issue($, "properties_on_alias"),
755
- issue($, "invalid_property_collection"),
811
+ choice(
812
+ $._empty_properties_end,
813
+ seq(
814
+ block ? $._block_properties_end : $._properties_end,
815
+ optional(separation),
816
+ choice(
817
+ content,
818
+ unfinishedIssue($, "properties_on_alias"),
819
+ unfinishedIssue($, "invalid_property_collection"),
820
+ ),
756
821
  ),
757
822
  ),
758
823
  );
@@ -774,31 +839,33 @@ function directiveTail($) {
774
839
  $._directive_end,
775
840
  );
776
841
  }
777
- function uriPart($, text) {
842
+ function uriPart($, text, prefix = false) {
778
843
  return choice(
779
844
  text,
780
845
  $.uri_escape,
781
846
  issue($, "invalid_uri_escape"),
782
847
  issue($, "incomplete_uri_escape"),
848
+ seq($._undecodable_escape_prefix, issue($, "invalid_encoding")),
783
849
  issue($, "invalid_tag_character"),
850
+ ...(prefix ? [issue($, "invalid_tag_prefix_start")] : []),
784
851
  invalid($),
785
852
  );
786
853
  }
787
854
 
788
- function explicitFlowPair($) {
855
+ function namedTagHandle($, end) {
789
856
  return seq(
790
- field("key_indicator", $.key_indicator),
791
- optional(field("key", $._flow_node)),
792
- optional(flowValue($)),
793
- );
794
- }
795
- function flowValue($) {
796
- return seq(
797
- field("indicator", $.value_indicator),
798
- optional(issue($, "missing_separation")),
799
- optional(field("value", $._flow_node)),
857
+ field("opening", $.tag_handle_open),
858
+ repeat1(
859
+ choice(
860
+ field("name", $.tag_handle_name),
861
+ issue($, "invalid_tag_handle"),
862
+ invalid($),
863
+ ),
864
+ ),
865
+ end,
800
866
  );
801
867
  }
868
+
802
869
  function quotedScalar($, start, opening, escapes) {
803
870
  return seq(
804
871
  start,
@@ -809,12 +876,12 @@ function quotedScalar($, start, opening, escapes) {
809
876
  $.scalar_line_break,
810
877
  $.scalar_line_prefix,
811
878
  $.scalar_line_suffix,
812
- issue($, "invalid_indentation"),
813
- issue($, "missing_indentation"),
879
+ indentationIssue($),
880
+ unfinishedIssue($, "missing_indentation"),
814
881
  invalid($),
815
882
  ...escapes,
816
883
  ),
817
884
  ),
818
- choice(field("closing", $.quote_close), missing($, "quote_close")),
885
+ closing($, "quote_close"),
819
886
  );
820
887
  }