1 
2 //          Copyright Ferdinand Majerech 2011-2014.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 /**
8  * YAML parser.
9  * Code based on PyYAML: http://www.pyyaml.org
10  */
11 module dyaml.parser;
12 
13 
14 import std.algorithm;
15 import std.array;
16 import std.container;
17 import std.conv;
18 import std.exception;
19 import std.typecons;
20 
21 import dyaml.anchor;
22 import dyaml.event;
23 import dyaml.exception;
24 import dyaml.scanner;
25 import dyaml.style;
26 import dyaml.token;
27 import dyaml.tag;
28 import dyaml.tagdirective;
29 
30 
31 package:
32 /**
33  * The following YAML grammar is LL(1) and is parsed by a recursive descent
34  * parser.
35  *
36  * stream            ::= STREAM-START implicit_document? explicit_document* STREAM-END
37  * implicit_document ::= block_node DOCUMENT-END*
38  * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
39  * block_node_or_indentless_sequence ::=
40  *                       ALIAS
41  *                       | properties (block_content | indentless_block_sequence)?
42  *                       | block_content
43  *                       | indentless_block_sequence
44  * block_node        ::= ALIAS
45  *                       | properties block_content?
46  *                       | block_content
47  * flow_node         ::= ALIAS
48  *                       | properties flow_content?
49  *                       | flow_content
50  * properties        ::= TAG ANCHOR? | ANCHOR TAG?
51  * block_content     ::= block_collection | flow_collection | SCALAR
52  * flow_content      ::= flow_collection | SCALAR
53  * block_collection  ::= block_sequence | block_mapping
54  * flow_collection   ::= flow_sequence | flow_mapping
55  * block_sequence    ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
56  * indentless_sequence   ::= (BLOCK-ENTRY block_node?)+
57  * block_mapping     ::= BLOCK-MAPPING_START
58  *                       ((KEY block_node_or_indentless_sequence?)?
59  *                       (VALUE block_node_or_indentless_sequence?)?)*
60  *                       BLOCK-END
61  * flow_sequence     ::= FLOW-SEQUENCE-START
62  *                       (flow_sequence_entry FLOW-ENTRY)*
63  *                       flow_sequence_entry?
64  *                       FLOW-SEQUENCE-END
65  * flow_sequence_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
66  * flow_mapping      ::= FLOW-MAPPING-START
67  *                       (flow_mapping_entry FLOW-ENTRY)*
68  *                       flow_mapping_entry?
69  *                       FLOW-MAPPING-END
70  * flow_mapping_entry    ::= flow_node | KEY flow_node? (VALUE flow_node?)?
71  *
72  * FIRST sets:
73  *
74  * stream: { STREAM-START }
75  * explicit_document: { DIRECTIVE DOCUMENT-START }
76  * implicit_document: FIRST(block_node)
77  * block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START }
78  * flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START }
79  * block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
80  * flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
81  * block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START }
82  * flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
83  * block_sequence: { BLOCK-SEQUENCE-START }
84  * block_mapping: { BLOCK-MAPPING-START }
85  * block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY }
86  * indentless_sequence: { ENTRY }
87  * flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
88  * flow_sequence: { FLOW-SEQUENCE-START }
89  * flow_mapping: { FLOW-MAPPING-START }
90  * flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
91  * flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
92  */
93 
94 
95 /**
96  * Marked exception thrown at parser errors.
97  *
98  * See_Also: MarkedYAMLException
99  */
100 class ParserException : MarkedYAMLException
101 {
102     mixin MarkedExceptionCtors;
103 }
104 
105 private alias ParserException Error;
106 
107 /// Generates events from tokens provided by a Scanner.
108 ///
109 /// While Parser receives tokens with non-const character slices, the events it 
110 /// produces are immutable strings, which are usually the same slices, cast to string.
111 /// Parser is the last layer of D:YAML that may possibly do any modifications to these
112 /// slices.
113 final class Parser
114 {
115     private:
116         ///Default tag handle shortcuts and replacements.
117         static TagDirective[] defaultTagDirectives_ = 
118             [TagDirective("!", "!"), TagDirective("!!", "tag:yaml.org,2002:")];
119 
120         ///Scanner providing YAML tokens.
121         Scanner scanner_;
122 
123         ///Event produced by the most recent state.
124         Event currentEvent_;
125 
126         ///YAML version string.
127         string YAMLVersion_ = null;
128         ///Tag handle shortcuts and replacements.
129         TagDirective[] tagDirectives_;
130 
131         ///Stack of states.
132         Array!(Event delegate()) states_;
133         ///Stack of marks used to keep track of extents of e.g. YAML collections.
134         Array!Mark marks_;
135 
136         ///Current state.
137         Event delegate() state_;
138 
139     public:
140         ///Construct a Parser using specified Scanner.
141         this(Scanner scanner) @trusted
142         {
143             state_ = &parseStreamStart;
144             scanner_ = scanner;
145             states_.reserve(32);
146             marks_.reserve(32);
147         }
148 
149         ///Destroy the parser.
150         @trusted ~this()
151         {
152             currentEvent_.destroy();
153             tagDirectives_.destroy();
154             tagDirectives_ = null;
155             states_.destroy();
156             marks_.destroy();
157         }
158 
159         /**
160          * Check if the next event is one of specified types.
161          *
162          * If no types are specified, checks if any events are left.
163          *
164          * Params:  ids = Event IDs to check for.
165          *
166          * Returns: true if the next event is one of specified types,
167          *          or if there are any events left if no types specified.
168          *          false otherwise.
169          */
170         bool checkEvent(EventID[] ids...) @trusted
171         {
172             //Check if the next event is one of specified types.
173             if(currentEvent_.isNull && state_ !is null)
174             {
175                 currentEvent_ = state_();
176             }
177 
178             if(!currentEvent_.isNull)
179             {
180                 if(ids.length == 0){return true;}
181                 else
182                 {
183                     const nextId = currentEvent_.id;
184                     foreach(id; ids)
185                     {
186                         if(nextId == id){return true;}
187                     }
188                 }
189             }
190 
191             return false;
192         }
193 
194         /**
195          * Return the next event, but keep it in the queue.
196          *
197          * Must not be called if there are no events left.
198          */
199         immutable(Event) peekEvent() @trusted
200         {
201             if(currentEvent_.isNull && state_ !is null)
202             {
203                 currentEvent_ = state_();
204             }
205             if(!currentEvent_.isNull){return cast(immutable Event)currentEvent_;}
206             assert(false, "No event left to peek");
207         }
208 
209         /**
210          * Return the next event, removing it from the queue.
211          *
212          * Must not be called if there are no events left.
213          */
214         immutable(Event) getEvent() @trusted
215         {
216             //Get the next event and proceed further.
217             if(currentEvent_.isNull && state_ !is null)
218             {
219                 currentEvent_ = state_();
220             }
221 
222             if(!currentEvent_.isNull)
223             {
224                 immutable Event result = cast(immutable Event)currentEvent_;
225                 currentEvent_.id = EventID.Invalid;
226                 return result;
227             }
228             assert(false, "No event left to get");
229         }
230 
231     private:
232         ///Pop and return the newest state in states_.
233         Event delegate() popState() @trusted
234         {
235             enforce(states_.length > 0,
236                     new YAMLException("Parser: Need to pop state but no states left to pop"));
237             const result = states_.back;
238             states_.length = states_.length - 1;
239             return result;
240         }
241 
242         ///Pop and return the newest mark in marks_.
243         Mark popMark() @trusted
244         {
245             enforce(marks_.length > 0,
246                     new YAMLException("Parser: Need to pop mark but no marks left to pop"));
247             const result = marks_.back;
248             marks_.length = marks_.length - 1;
249             return result;
250         }
251 
252         /**
253          * stream    ::= STREAM-START implicit_document? explicit_document* STREAM-END
254          * implicit_document ::= block_node DOCUMENT-END*
255          * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
256          */
257 
258         ///Parse stream start.
259         Event parseStreamStart() @safe
260         {
261             const token = scanner_.getToken();
262             state_ = &parseImplicitDocumentStart;
263             return streamStartEvent(token.startMark, token.endMark, token.encoding);
264         }
265 
266         /// Parse implicit document start, unless explicit detected: if so, parse explicit.
267         Event parseImplicitDocumentStart() @trusted
268         {
269             // Parse an implicit document.
270             if(!scanner_.checkToken(TokenID.Directive, TokenID.DocumentStart,
271                                     TokenID.StreamEnd))
272             {
273                 tagDirectives_  = defaultTagDirectives_;
274                 const token = scanner_.peekToken();
275 
276                 states_ ~= &parseDocumentEnd;
277                 state_ = &parseBlockNode;
278 
279                 return documentStartEvent(token.startMark, token.endMark, false, null, null);
280             }
281             return parseDocumentStart();
282         }
283 
284         ///Parse explicit document start.
285         Event parseDocumentStart() @trusted
286         {
287             //Parse any extra document end indicators.
288             while(scanner_.checkToken(TokenID.DocumentEnd)){scanner_.getToken();}
289 
290             //Parse an explicit document.
291             if(!scanner_.checkToken(TokenID.StreamEnd))
292             {
293                 const startMark = scanner_.peekToken().startMark;
294 
295                 auto tagDirectives = processDirectives();
296                 enforce(scanner_.checkToken(TokenID.DocumentStart),
297                         new Error("Expected document start but found " ~
298                                   scanner_.peekToken().idString,
299                                   scanner_.peekToken().startMark));
300 
301                 const endMark = scanner_.getToken().endMark;
302                 states_ ~= &parseDocumentEnd;
303                 state_ = &parseDocumentContent;
304                 return documentStartEvent(startMark, endMark, true, YAMLVersion_, tagDirectives);
305             }
306             else
307             {
308                 //Parse the end of the stream.
309                 const token = scanner_.getToken();
310                 assert(states_.length == 0);
311                 assert(marks_.length == 0);
312                 state_ = null;
313                 return streamEndEvent(token.startMark, token.endMark);
314             }
315         }
316 
317         ///Parse document end (explicit or implicit).
318         Event parseDocumentEnd() @safe
319         {
320             Mark startMark = scanner_.peekToken().startMark;
321             const bool explicit = scanner_.checkToken(TokenID.DocumentEnd);
322             Mark endMark = explicit ? scanner_.getToken().endMark : startMark;
323 
324             state_ = &parseDocumentStart;
325 
326             return documentEndEvent(startMark, endMark, explicit);
327         }
328 
329         ///Parse document content.
330         Event parseDocumentContent() @safe
331         {
332             if(scanner_.checkToken(TokenID.Directive,   TokenID.DocumentStart,
333                                    TokenID.DocumentEnd, TokenID.StreamEnd))
334             {
335                 state_ = popState();
336                 return processEmptyScalar(scanner_.peekToken().startMark);
337             }
338             return parseBlockNode();
339         }
340 
341         /// Process directives at the beginning of a document.
342         TagDirective[] processDirectives() @system
343         {
344             // Destroy version and tag handles from previous document.
345             YAMLVersion_ = null;
346             tagDirectives_.length = 0;
347 
348             // Process directives.
349             while(scanner_.checkToken(TokenID.Directive))
350             {
351                 const token = scanner_.getToken();
352                 const value = token.value;
353                 if(token.directive == DirectiveType.YAML)
354                 {
355                     enforce(YAMLVersion_ is null,
356                             new Error("Duplicate YAML directive", token.startMark));
357                     const minor = value.split(".")[0];
358                     enforce(minor == "1",
359                             new Error("Incompatible document (version 1.x is required)",
360                                       token.startMark));
361                     YAMLVersion_ = cast(string)value;
362                 }
363                 else if(token.directive == DirectiveType.TAG)
364                 {
365                     auto handle = cast(string)value[0 .. token.valueDivider];
366 
367                     foreach(ref pair; tagDirectives_)
368                     {
369                         // handle
370                         const h = pair.handle;
371                         enforce(h != handle, new Error("Duplicate tag handle: " ~ handle,
372                                                        token.startMark));
373                     }
374                     tagDirectives_ ~= 
375                         TagDirective(handle, cast(string)value[token.valueDivider .. $]);
376                 }
377                 // Any other directive type is ignored (only YAML and TAG are in YAML
378                 // 1.1/1.2, any other directives are "reserved")
379             }
380 
381             TagDirective[] value = tagDirectives_;
382 
383             //Add any default tag handles that haven't been overridden.
384             foreach(ref defaultPair; defaultTagDirectives_)
385             {
386                 bool found = false;
387                 foreach(ref pair; tagDirectives_) if(defaultPair.handle == pair.handle)
388                 {
389                     found = true;
390                     break;
391                 }
392                 if(!found) {tagDirectives_ ~= defaultPair; }
393             }
394 
395             return value;
396         }
397 
398         /**
399          * block_node_or_indentless_sequence ::= ALIAS
400          *               | properties (block_content | indentless_block_sequence)?
401          *               | block_content
402          *               | indentless_block_sequence
403          * block_node    ::= ALIAS
404          *                   | properties block_content?
405          *                   | block_content
406          * flow_node     ::= ALIAS
407          *                   | properties flow_content?
408          *                   | flow_content
409          * properties    ::= TAG ANCHOR? | ANCHOR TAG?
410          * block_content     ::= block_collection | flow_collection | SCALAR
411          * flow_content      ::= flow_collection | SCALAR
412          * block_collection  ::= block_sequence | block_mapping
413          * flow_collection   ::= flow_sequence | flow_mapping
414          */
415 
416         ///Parse a node.
417         Event parseNode(const Flag!"block" block,
418                         const Flag!"indentlessSequence" indentlessSequence = No.indentlessSequence)
419             @trusted
420         {
421             if(scanner_.checkToken(TokenID.Alias))
422             {
423                 const token = scanner_.getToken();
424                 state_ = popState();
425                 return aliasEvent(token.startMark, token.endMark, 
426                                   Anchor(cast(string)token.value));
427             }
428 
429             string anchor = null;
430             string tag = null;
431             Mark startMark, endMark, tagMark;
432             bool invalidMarks = true;
433             // The index in the tag string where tag handle ends and tag suffix starts.
434             uint tagHandleEnd;
435 
436             //Get anchor/tag if detected. Return false otherwise.
437             bool get(const TokenID id, const Flag!"first" first, ref string target)
438             {
439                 if(!scanner_.checkToken(id)){return false;}
440                 invalidMarks = false;
441                 const token = scanner_.getToken();
442                 if(first){startMark = token.startMark;}
443                 if(id == TokenID.Tag)
444                 {
445                     tagMark = token.startMark;
446                     tagHandleEnd = token.valueDivider;
447                 }
448                 endMark = token.endMark;
449                 target  = cast(string)token.value;
450                 return true;
451             }
452 
453             //Anchor and/or tag can be in any order.
454             if(get(TokenID.Anchor, Yes.first, anchor)){get(TokenID.Tag, No.first, tag);}
455             else if(get(TokenID.Tag, Yes.first, tag)) {get(TokenID.Anchor, No.first, anchor);}
456 
457             if(tag !is null){tag = processTag(tag, tagHandleEnd, startMark, tagMark);}
458 
459             if(invalidMarks)
460             {
461                 startMark = endMark = scanner_.peekToken().startMark;
462             }
463 
464             bool implicit = (tag is null || tag == "!");
465 
466             if(indentlessSequence && scanner_.checkToken(TokenID.BlockEntry))
467             {
468                 state_ = &parseIndentlessSequenceEntry;
469                 return sequenceStartEvent
470                     (startMark, scanner_.peekToken().endMark, Anchor(anchor),
471                      Tag(tag), implicit, CollectionStyle.Block);
472             }
473 
474             if(scanner_.checkToken(TokenID.Scalar))
475             {
476                 auto token = scanner_.getToken();
477                 auto value = token.style == ScalarStyle.DoubleQuoted
478                            ? handleDoubleQuotedScalarEscapes(token.value)
479                            : cast(string)token.value;
480 
481                 implicit = (token.style == ScalarStyle.Plain && tag is null) || tag == "!";
482                 bool implicit_2 = (!implicit) && tag is null;
483                 state_ = popState();
484                 return scalarEvent(startMark, token.endMark, Anchor(anchor), Tag(tag),
485                                    tuple(implicit, implicit_2), value, token.style);
486             }
487 
488             if(scanner_.checkToken(TokenID.FlowSequenceStart))
489             {
490                 endMark = scanner_.peekToken().endMark;
491                 state_ = &parseFlowSequenceEntry!(Yes.first);
492                 return sequenceStartEvent(startMark, endMark, Anchor(anchor), Tag(tag),
493                                           implicit, CollectionStyle.Flow);
494             }
495 
496             if(scanner_.checkToken(TokenID.FlowMappingStart))
497             {
498                 endMark = scanner_.peekToken().endMark;
499                 state_ = &parseFlowMappingKey!(Yes.first);
500                 return mappingStartEvent(startMark, endMark, Anchor(anchor), Tag(tag),
501                                          implicit, CollectionStyle.Flow);
502             }
503 
504             if(block && scanner_.checkToken(TokenID.BlockSequenceStart))
505             {
506                 endMark = scanner_.peekToken().endMark;
507                 state_ = &parseBlockSequenceEntry!(Yes.first);
508                 return sequenceStartEvent(startMark, endMark, Anchor(anchor), Tag(tag),
509                                           implicit, CollectionStyle.Block);
510             }
511 
512             if(block && scanner_.checkToken(TokenID.BlockMappingStart))
513             {
514                 endMark = scanner_.peekToken().endMark;
515                 state_ = &parseBlockMappingKey!(Yes.first);
516                 return mappingStartEvent(startMark, endMark, Anchor(anchor), Tag(tag),
517                                          implicit, CollectionStyle.Block);
518             }
519 
520             if(anchor != null || tag !is null)
521             {
522                 state_ = popState();
523 
524                 //PyYAML uses a tuple(implicit, false) for the second last arg here,
525                 //but the second bool is never used after that - so we don't use it.
526 
527                 //Empty scalars are allowed even if a tag or an anchor is specified.
528                 return scalarEvent(startMark, endMark, Anchor(anchor), Tag(tag),
529                                    tuple(implicit, false) , "");
530             }
531 
532             const token = scanner_.peekToken();
533             throw new Error("While parsing a " ~ (block ? "block" : "flow") ~ " node",
534                             startMark, "expected node content, but found: "
535                             ~ token.idString, token.startMark);
536         }
537 
538         /// Handle escape sequences in a double quoted scalar.
539         ///
540         /// Moved here from scanner as it can't always be done in-place with slices.
541         string handleDoubleQuotedScalarEscapes(char[] tokenValue)
542         {
543             string notInPlace;
544             bool inEscape = false;
545             import dyaml.nogcutil;
546             auto appender = appenderNoGC(cast(char[])tokenValue);
547             for(char[] oldValue = tokenValue; !oldValue.empty();)
548             {
549                 const dchar c = oldValue.front();
550                 oldValue.popFront();
551 
552                 if(!inEscape)
553                 {
554                     if(c != '\\')
555                     {
556                         if(notInPlace is null) { appender.putDChar(c); }
557                         else                   { notInPlace ~= c; }
558                         continue;
559                     }
560                     // Escape sequence starts with a '\'
561                     inEscape = true;
562                     continue;
563                 }
564 
565                 import dyaml.escapes;
566                 scope(exit) { inEscape = false; }
567 
568                 // 'Normal' escape sequence.
569                 if(dyaml.escapes.escapes.canFind(c))
570                 {
571                     if(notInPlace is null)
572                     {
573                         // \L and \C can't be handled in place as the expand into
574                         // many-byte unicode chars
575                         if(c != 'L' && c != 'P')
576                         {
577                             appender.putDChar(dyaml.escapes.fromEscape(c));
578                             continue;
579                         }
580                         // Need to duplicate as we won't fit into
581                         // token.value - which is what appender uses
582                         notInPlace = appender.data.dup;
583                         notInPlace ~= dyaml.escapes.fromEscape(c);
584                         continue;
585                     }
586                     notInPlace ~= dyaml.escapes.fromEscape(c);
587                     continue;
588                 }
589 
590                 // Unicode char written in hexadecimal in an escape sequence.
591                 if(dyaml.escapes.escapeHexCodeList.canFind(c))
592                 {
593                     // Scanner has already checked that the hex string is valid.
594 
595                     const hexLength = dyaml.escapes.escapeHexLength(c);
596                     // Any hex digits are 1-byte so this works.
597                     char[] hex = oldValue[0 .. hexLength];
598                     oldValue = oldValue[hexLength .. $];
599                     assert(!hex.canFind!(d => !d.isHexDigit),
600                             "Scanner must ensure the hex string is valid");
601 
602                     bool overflow;
603                     const decoded = cast(dchar)parseNoGC!int(hex, 16u, overflow);
604                     assert(!overflow, "Scanner must ensure there's no overflow");
605                     if(notInPlace is null) { appender.putDChar(decoded); }
606                     else                   { notInPlace ~= decoded; }
607                     continue;
608                 }
609 
610                 assert(false, "Scanner must handle unsupported escapes");
611             }
612 
613             return notInPlace is null ? cast(string)appender.data : notInPlace;
614         }
615 
616         /**
617          * Process a tag string retrieved from a tag token.
618          *
619          * Params:  tag       = Tag before processing.
620          *          handleEnd = Index in tag where tag handle ends and tag suffix
621          *                      starts.
622          *          startMark = Position of the node the tag belongs to.
623          *          tagMark   = Position of the tag.
624          */
625         string processTag(const string tag, const uint handleEnd,
626                           const Mark startMark, const Mark tagMark)
627             const @trusted
628         {
629             const handle = tag[0 .. handleEnd];
630             const suffix = tag[handleEnd .. $];
631 
632             if(handle.length > 0)
633             {
634                 string replacement = null;
635                 foreach(ref pair; tagDirectives_)
636                 {
637                     if(pair.handle == handle)
638                     {
639                         replacement = pair.prefix;
640                         break;
641                     }
642                 }
643                 //handle must be in tagDirectives_
644                 enforce(replacement !is null,
645                         new Error("While parsing a node", startMark,
646                                   "found undefined tag handle: " ~ handle, tagMark));
647                 return replacement ~ suffix;
648             }
649             return suffix;
650         }
651 
652         ///Wrappers to parse nodes.
653         Event parseBlockNode() @safe {return parseNode(Yes.block);}
654         Event parseFlowNode() @safe {return parseNode(No.block);}
655         Event parseBlockNodeOrIndentlessSequence() @safe {return parseNode(Yes.block, Yes.indentlessSequence);}
656 
657         ///block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
658 
659         ///Parse an entry of a block sequence. If first is true, this is the first entry.
660         Event parseBlockSequenceEntry(Flag!"first" first)() @trusted
661         {
662             static if(first){marks_ ~= scanner_.getToken().startMark;}
663 
664             if(scanner_.checkToken(TokenID.BlockEntry))
665             {
666                 const token = scanner_.getToken();
667                 if(!scanner_.checkToken(TokenID.BlockEntry, TokenID.BlockEnd))
668                 {
669                     states_~= &parseBlockSequenceEntry!(No.first);
670                     return parseBlockNode();
671                 }
672 
673                 state_ = &parseBlockSequenceEntry!(No.first);
674                 return processEmptyScalar(token.endMark);
675             }
676 
677             if(!scanner_.checkToken(TokenID.BlockEnd))
678             {
679                 const token = scanner_.peekToken();
680                 throw new Error("While parsing a block collection", marks_.back,
681                                 "expected block end, but found " ~ token.idString,
682                                 token.startMark);
683             }
684 
685             state_ = popState();
686             popMark();
687             const token = scanner_.getToken();
688             return sequenceEndEvent(token.startMark, token.endMark);
689         }
690 
691         ///indentless_sequence ::= (BLOCK-ENTRY block_node?)+
692 
693         ///Parse an entry of an indentless sequence.
694         Event parseIndentlessSequenceEntry() @trusted
695         {
696             if(scanner_.checkToken(TokenID.BlockEntry))
697             {
698                 const token = scanner_.getToken();
699 
700                 if(!scanner_.checkToken(TokenID.BlockEntry, TokenID.Key,
701                                         TokenID.Value, TokenID.BlockEnd))
702                 {
703                     states_ ~= &parseIndentlessSequenceEntry;
704                     return parseBlockNode();
705                 }
706 
707                 state_ = &parseIndentlessSequenceEntry;
708                 return processEmptyScalar(token.endMark);
709             }
710 
711             state_ = popState();
712             const token = scanner_.peekToken();
713             return sequenceEndEvent(token.startMark, token.endMark);
714         }
715 
716         /**
717          * block_mapping     ::= BLOCK-MAPPING_START
718          *                       ((KEY block_node_or_indentless_sequence?)?
719          *                       (VALUE block_node_or_indentless_sequence?)?)*
720          *                       BLOCK-END
721          */
722 
723         ///Parse a key in a block mapping. If first is true, this is the first key.
724         Event parseBlockMappingKey(Flag!"first" first)() @trusted
725         {
726             static if(first){marks_ ~= scanner_.getToken().startMark;}
727 
728             if(scanner_.checkToken(TokenID.Key))
729             {
730                 const token = scanner_.getToken();
731 
732                 if(!scanner_.checkToken(TokenID.Key, TokenID.Value, TokenID.BlockEnd))
733                 {
734                     states_ ~= &parseBlockMappingValue;
735                     return parseBlockNodeOrIndentlessSequence();
736                 }
737 
738                 state_ = &parseBlockMappingValue;
739                 return processEmptyScalar(token.endMark);
740             }
741 
742             if(!scanner_.checkToken(TokenID.BlockEnd))
743             {
744                 const token = scanner_.peekToken();
745                 throw new Error("While parsing a block mapping", marks_.back,
746                                 "expected block end, but found: " ~ token.idString,
747                                 token.startMark);
748             }
749 
750             state_ = popState();
751             popMark();
752             const token = scanner_.getToken();
753             return mappingEndEvent(token.startMark, token.endMark);
754         }
755 
756         ///Parse a value in a block mapping.
757         Event parseBlockMappingValue() @trusted
758         {
759             if(scanner_.checkToken(TokenID.Value))
760             {
761                 const token = scanner_.getToken();
762 
763                 if(!scanner_.checkToken(TokenID.Key, TokenID.Value, TokenID.BlockEnd))
764                 {
765                     states_ ~= &parseBlockMappingKey!(No.first);
766                     return parseBlockNodeOrIndentlessSequence();
767                 }
768 
769                 state_ = &parseBlockMappingKey!(No.first);
770                 return processEmptyScalar(token.endMark);
771             }
772 
773             state_= &parseBlockMappingKey!(No.first);
774             return processEmptyScalar(scanner_.peekToken().startMark);
775         }
776 
777         /**
778          * flow_sequence     ::= FLOW-SEQUENCE-START
779          *                       (flow_sequence_entry FLOW-ENTRY)*
780          *                       flow_sequence_entry?
781          *                       FLOW-SEQUENCE-END
782          * flow_sequence_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
783          *
784          * Note that while production rules for both flow_sequence_entry and
785          * flow_mapping_entry are equal, their interpretations are different.
786          * For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?`
787          * generate an inline mapping (set syntax).
788          */
789 
790         ///Parse an entry in a flow sequence. If first is true, this is the first entry.
791         Event parseFlowSequenceEntry(Flag!"first" first)() @trusted
792         {
793             static if(first){marks_ ~= scanner_.getToken().startMark;}
794 
795             if(!scanner_.checkToken(TokenID.FlowSequenceEnd))
796             {
797                 static if(!first)
798                 {
799                     if(scanner_.checkToken(TokenID.FlowEntry))
800                     {
801                         scanner_.getToken();
802                     }
803                     else
804                     {
805                         const token = scanner_.peekToken();
806                         throw new Error("While parsing a flow sequence", marks_.back,
807                                         "expected ',' or ']', but got: " ~
808                                         token.idString, token.startMark);
809                     }
810                 }
811 
812                 if(scanner_.checkToken(TokenID.Key))
813                 {
814                     const token = scanner_.peekToken();
815                     state_ = &parseFlowSequenceEntryMappingKey;
816                     return mappingStartEvent(token.startMark, token.endMark,
817                                              Anchor(), Tag(), true, CollectionStyle.Flow);
818                 }
819                 else if(!scanner_.checkToken(TokenID.FlowSequenceEnd))
820                 {
821                     states_ ~= &parseFlowSequenceEntry!(No.first);
822                     return parseFlowNode();
823                 }
824             }
825 
826             const token = scanner_.getToken();
827             state_ = popState();
828             popMark();
829             return sequenceEndEvent(token.startMark, token.endMark);
830         }
831 
832         ///Parse a key in flow context.
833         Event parseFlowKey(in Event delegate() nextState) @trusted
834         {
835             const token = scanner_.getToken();
836 
837             if(!scanner_.checkToken(TokenID.Value, TokenID.FlowEntry,
838                                     TokenID.FlowSequenceEnd))
839             {
840                 states_ ~= nextState;
841                 return parseFlowNode();
842             }
843 
844             state_ = nextState;
845             return processEmptyScalar(token.endMark);
846         }
847 
848         ///Parse a mapping key in an entry in a flow sequence.
849         Event parseFlowSequenceEntryMappingKey() @safe
850         {
851             return parseFlowKey(&parseFlowSequenceEntryMappingValue);
852         }
853 
854         ///Parse a mapping value in a flow context.
855         Event parseFlowValue(TokenID checkId, in Event delegate() nextState)
856             @trusted
857         {
858             if(scanner_.checkToken(TokenID.Value))
859             {
860                 const token = scanner_.getToken();
861                 if(!scanner_.checkToken(TokenID.FlowEntry, checkId))
862                 {
863                     states_ ~= nextState;
864                     return parseFlowNode();
865                 }
866 
867                 state_ = nextState;
868                 return processEmptyScalar(token.endMark);
869             }
870 
871             state_ = nextState;
872             return processEmptyScalar(scanner_.peekToken().startMark);
873         }
874 
875         ///Parse a mapping value in an entry in a flow sequence.
876         Event parseFlowSequenceEntryMappingValue() @safe
877         {
878             return parseFlowValue(TokenID.FlowSequenceEnd,
879                                   &parseFlowSequenceEntryMappingEnd);
880         }
881 
882         ///Parse end of a mapping in a flow sequence entry.
883         Event parseFlowSequenceEntryMappingEnd() @safe
884         {
885             state_ = &parseFlowSequenceEntry!(No.first);
886             const token = scanner_.peekToken();
887             return mappingEndEvent(token.startMark, token.startMark);
888         }
889 
890         /**
891          * flow_mapping  ::= FLOW-MAPPING-START
892          *                   (flow_mapping_entry FLOW-ENTRY)*
893          *                   flow_mapping_entry?
894          *                   FLOW-MAPPING-END
895          * flow_mapping_entry    ::= flow_node | KEY flow_node? (VALUE flow_node?)?
896          */
897 
898         ///Parse a key in a flow mapping.
899         Event parseFlowMappingKey(Flag!"first" first)() @trusted
900         {
901             static if(first){marks_ ~= scanner_.getToken().startMark;}
902 
903             if(!scanner_.checkToken(TokenID.FlowMappingEnd))
904             {
905                 static if(!first)
906                 {
907                     if(scanner_.checkToken(TokenID.FlowEntry))
908                     {
909                         scanner_.getToken();
910                     }
911                     else
912                     {
913                         const token = scanner_.peekToken();
914                         throw new Error("While parsing a flow mapping", marks_.back,
915                                         "expected ',' or '}', but got: " ~
916                                         token.idString, token.startMark);
917                     }
918                 }
919 
920                 if(scanner_.checkToken(TokenID.Key))
921                 {
922                     return parseFlowKey(&parseFlowMappingValue);
923                 }
924 
925                 if(!scanner_.checkToken(TokenID.FlowMappingEnd))
926                 {
927                     states_ ~= &parseFlowMappingEmptyValue;
928                     return parseFlowNode();
929                 }
930             }
931 
932             const token = scanner_.getToken();
933             state_ = popState();
934             popMark();
935             return mappingEndEvent(token.startMark, token.endMark);
936         }
937 
938         ///Parse a value in a flow mapping.
939         Event parseFlowMappingValue()  @safe
940         {
941             return parseFlowValue(TokenID.FlowMappingEnd, &parseFlowMappingKey!(No.first));
942         }
943 
944         ///Parse an empty value in a flow mapping.
945         Event parseFlowMappingEmptyValue() @safe
946         {
947             state_ = &parseFlowMappingKey!(No.first);
948             return processEmptyScalar(scanner_.peekToken().startMark);
949         }
950 
951         ///Return an empty scalar.
952         Event processEmptyScalar(const Mark mark) @safe pure nothrow const @nogc
953         {
954             return scalarEvent(mark, mark, Anchor(), Tag(), tuple(true, false), "");
955         }
956 }