OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library yaml.parser; | |
6 | |
7 import 'package:source_span/source_span.dart'; | |
8 import 'package:string_scanner/string_scanner.dart'; | |
9 | |
10 import 'event.dart'; | |
11 import 'scanner.dart'; | |
12 import 'style.dart'; | |
13 import 'token.dart'; | |
14 import 'utils.dart'; | |
15 import 'yaml_document.dart'; | |
16 import 'yaml_exception.dart'; | |
17 | |
18 /// A parser that reads [Token]s emitted by a [Scanner] and emits [Event]s. | |
19 /// | |
20 /// This is based on the libyaml parser, available at | |
21 /// https://github.com/yaml/libyaml/blob/master/src/parser.c. The license for | |
22 /// that is available in ../../libyaml-license.txt. | |
23 class Parser { | |
24 /// The underlying [Scanner] that generates [Token]s. | |
25 final Scanner _scanner; | |
26 | |
27 /// The stack of parse states for nested contexts. | |
28 final _states = new List<_State>(); | |
29 | |
30 /// The current parse state. | |
31 var _state = _State.STREAM_START; | |
32 | |
33 /// The custom tag directives, by tag handle. | |
34 final _tagDirectives = new Map<String, TagDirective>(); | |
35 | |
36 /// Whether the parser has finished parsing. | |
37 bool get isDone => _state == _State.END; | |
38 | |
39 /// Creates a parser that parses [source]. | |
40 /// | |
41 /// [sourceUrl] can be a String or a [Uri]. | |
42 Parser(String source, {sourceUrl}) | |
43 : _scanner = new Scanner(source, sourceUrl: sourceUrl); | |
44 | |
45 /// Consumes and returns the next event. | |
46 Event parse() { | |
47 try { | |
48 if (isDone) throw new StateError("No more events."); | |
49 var event = _stateMachine(); | |
50 return event; | |
51 } on StringScannerException catch (error) { | |
52 throw new YamlException(error.message, error.span); | |
53 } | |
54 } | |
55 | |
56 /// Dispatches parsing based on the current state. | |
57 Event _stateMachine() { | |
58 switch (_state) { | |
59 case _State.STREAM_START: | |
60 return _parseStreamStart(); | |
61 case _State.DOCUMENT_START: | |
62 return _parseDocumentStart(); | |
63 case _State.DOCUMENT_CONTENT: | |
64 return _parseDocumentContent(); | |
65 case _State.DOCUMENT_END: | |
66 return _parseDocumentEnd(); | |
67 case _State.BLOCK_NODE: | |
68 return _parseNode(block: true); | |
69 case _State.BLOCK_NODE_OR_INDENTLESS_SEQUENCE: | |
70 return _parseNode(block: true, indentlessSequence: true); | |
71 case _State.FLOW_NODE: | |
72 return _parseNode(); | |
73 case _State.BLOCK_SEQUENCE_FIRST_ENTRY: | |
74 // Scan past the `BLOCK-SEQUENCE-FIRST-ENTRY` token to the | |
75 // `BLOCK-SEQUENCE-ENTRY` token. | |
76 _scanner.scan(); | |
77 return _parseBlockSequenceEntry(); | |
78 case _State.BLOCK_SEQUENCE_ENTRY: | |
79 return _parseBlockSequenceEntry(); | |
80 case _State.INDENTLESS_SEQUENCE_ENTRY: | |
81 return _parseIndentlessSequenceEntry(); | |
82 case _State.BLOCK_MAPPING_FIRST_KEY: | |
83 // Scan past the `BLOCK-MAPPING-FIRST-KEY` token to the | |
84 // `BLOCK-MAPPING-KEY` token. | |
85 _scanner.scan(); | |
86 return _parseBlockMappingKey(); | |
87 case _State.BLOCK_MAPPING_KEY: | |
88 return _parseBlockMappingKey(); | |
89 case _State.BLOCK_MAPPING_VALUE: | |
90 return _parseBlockMappingValue(); | |
91 case _State.FLOW_SEQUENCE_FIRST_ENTRY: | |
92 return _parseFlowSequenceEntry(first: true); | |
93 case _State.FLOW_SEQUENCE_ENTRY: | |
94 return _parseFlowSequenceEntry(); | |
95 case _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY: | |
96 return _parseFlowSequenceEntryMappingKey(); | |
97 case _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE: | |
98 return _parseFlowSequenceEntryMappingValue(); | |
99 case _State.FLOW_SEQUENCE_ENTRY_MAPPING_END: | |
100 return _parseFlowSequenceEntryMappingEnd(); | |
101 case _State.FLOW_MAPPING_FIRST_KEY: | |
102 return _parseFlowMappingKey(first: true); | |
103 case _State.FLOW_MAPPING_KEY: | |
104 return _parseFlowMappingKey(); | |
105 case _State.FLOW_MAPPING_VALUE: | |
106 return _parseFlowMappingValue(); | |
107 case _State.FLOW_MAPPING_EMPTY_VALUE: | |
108 return _parseFlowMappingValue(empty: true); | |
109 default: | |
110 throw "Unreachable"; | |
111 } | |
112 } | |
113 | |
114 /// Parses the production: | |
115 /// | |
116 /// stream ::= | |
117 /// STREAM-START implicit_document? explicit_document* STREAM-END | |
118 /// ************ | |
119 Event _parseStreamStart() { | |
120 var token = _scanner.scan(); | |
121 assert(token.type == TokenType.STREAM_START); | |
122 | |
123 _state = _State.DOCUMENT_START; | |
124 return new Event(EventType.STREAM_START, token.span); | |
125 } | |
126 | |
127 /// Parses the productions: | |
128 /// | |
129 /// implicit_document ::= block_node DOCUMENT-END* | |
130 /// * | |
131 /// explicit_document ::= | |
132 /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* | |
133 /// ************************* | |
134 Event _parseDocumentStart() { | |
135 var token = _scanner.peek(); | |
136 | |
137 // libyaml requires any document beyond the first in the stream to have an | |
138 // explicit document start indicator, but the spec allows it to be omitted | |
139 // as long as there was an end indicator. | |
140 | |
141 // Parse extra document end indicators. | |
142 while (token.type == TokenType.DOCUMENT_END) { | |
143 token = _scanner.advance(); | |
144 } | |
145 | |
146 if (token.type != TokenType.VERSION_DIRECTIVE && | |
147 token.type != TokenType.TAG_DIRECTIVE && | |
148 token.type != TokenType.DOCUMENT_START && | |
149 token.type != TokenType.STREAM_END) { | |
150 // Parse an implicit document. | |
151 _processDirectives(); | |
152 _states.add(_State.DOCUMENT_END); | |
153 _state = _State.BLOCK_NODE; | |
154 return new DocumentStartEvent(token.span.start.pointSpan()); | |
155 } | |
156 | |
157 if (token.type == TokenType.STREAM_END) { | |
158 _state = _State.END; | |
159 _scanner.scan(); | |
160 return new Event(EventType.STREAM_END, token.span); | |
161 } | |
162 | |
163 // Parse an explicit document. | |
164 var start = token.span; | |
165 var pair = _processDirectives(); | |
166 var versionDirective = pair.first; | |
167 var tagDirectives = pair.last; | |
168 token = _scanner.peek(); | |
169 if (token.type != TokenType.DOCUMENT_START) { | |
170 throw new YamlException("Expected document start.", token.span); | |
171 } | |
172 | |
173 _states.add(_State.DOCUMENT_END); | |
174 _state = _State.DOCUMENT_CONTENT; | |
175 _scanner.scan(); | |
176 return new DocumentStartEvent(start.expand(token.span), | |
177 versionDirective: versionDirective, | |
178 tagDirectives: tagDirectives, | |
179 isImplicit: false); | |
180 } | |
181 | |
182 /// Parses the productions: | |
183 /// | |
184 /// explicit_document ::= | |
185 /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* | |
186 /// *********** | |
187 Event _parseDocumentContent() { | |
188 var token = _scanner.peek(); | |
189 | |
190 switch (token.type) { | |
191 case TokenType.VERSION_DIRECTIVE: | |
192 case TokenType.TAG_DIRECTIVE: | |
193 case TokenType.DOCUMENT_START: | |
194 case TokenType.DOCUMENT_END: | |
195 case TokenType.STREAM_END: | |
196 _state = _states.removeLast(); | |
197 return _processEmptyScalar(token.span.start); | |
198 default: | |
199 return _parseNode(block: true); | |
200 } | |
201 } | |
202 | |
203 /// Parses the productions: | |
204 /// | |
205 /// implicit_document ::= block_node DOCUMENT-END* | |
206 /// ************* | |
207 /// explicit_document ::= | |
208 /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* | |
209 /// ************* | |
210 Event _parseDocumentEnd() { | |
211 _tagDirectives.clear(); | |
212 _state = _State.DOCUMENT_START; | |
213 | |
214 var token = _scanner.peek(); | |
215 if (token.type == TokenType.DOCUMENT_END) { | |
216 _scanner.scan(); | |
217 return new DocumentEndEvent(token.span, isImplicit: false); | |
218 } else { | |
219 return new DocumentEndEvent( | |
220 token.span.start.pointSpan(), isImplicit: true); | |
221 } | |
222 } | |
223 | |
224 /// Parses the productions: | |
225 /// | |
226 /// block_node_or_indentless_sequence ::= | |
227 /// ALIAS | |
228 /// ***** | |
229 /// | properties (block_content | indentless_block_sequence)? | |
230 /// ********** * | |
231 /// | block_content | indentless_block_sequence | |
232 /// * | |
233 /// block_node ::= ALIAS | |
234 /// ***** | |
235 /// | properties block_content? | |
236 /// ********** * | |
237 /// | block_content | |
238 /// * | |
239 /// flow_node ::= ALIAS | |
240 /// ***** | |
241 /// | properties flow_content? | |
242 /// ********** * | |
243 /// | flow_content | |
244 /// * | |
245 /// properties ::= TAG ANCHOR? | ANCHOR TAG? | |
246 /// ************************* | |
247 /// block_content ::= block_collection | flow_collection | SCALAR | |
248 /// ****** | |
249 /// flow_content ::= flow_collection | SCALAR | |
250 /// ****** | |
251 Event _parseNode({bool block: false, bool indentlessSequence: false}) { | |
252 var token = _scanner.peek(); | |
253 | |
254 if (token is AliasToken) { | |
255 _scanner.scan(); | |
256 _state = _states.removeLast(); | |
257 return new AliasEvent(token.span, token.name); | |
258 } | |
259 | |
260 var anchor; | |
261 var tagToken; | |
262 var span = token.span.start.pointSpan(); | |
263 parseAnchor() { | |
264 anchor = token.name; | |
265 span = span.expand(token.span); | |
266 token = _scanner.advance(); | |
267 } | |
268 | |
269 parseTag() { | |
270 tagToken = token; | |
271 span = span.expand(token.span); | |
272 token = _scanner.advance(); | |
273 } | |
274 | |
275 if (token is AnchorToken) { | |
276 parseAnchor(); | |
277 if (token is TagToken) parseTag(); | |
278 } else if (token is TagToken) { | |
279 parseTag(); | |
280 if (token is AnchorToken) parseAnchor(); | |
281 } | |
282 | |
283 var tag; | |
284 if (tagToken != null) { | |
285 if (tagToken.handle == null) { | |
286 tag = tagToken.suffix; | |
287 } else { | |
288 var tagDirective = _tagDirectives[tagToken.handle]; | |
289 if (tagDirective == null) { | |
290 throw new YamlException("Undefined tag handle.", tagToken.span); | |
291 } | |
292 | |
293 tag = tagDirective.prefix + tagToken.suffix; | |
294 } | |
295 } | |
296 | |
297 if (indentlessSequence && token.type == TokenType.BLOCK_ENTRY) { | |
298 _state = _State.INDENTLESS_SEQUENCE_ENTRY; | |
299 return new SequenceStartEvent( | |
300 span.expand(token.span), CollectionStyle.BLOCK, | |
301 anchor: anchor, tag: tag); | |
302 } | |
303 | |
304 if (token is ScalarToken) { | |
305 // All non-plain scalars have the "!" tag by default. | |
306 if (tag == null && token.style != ScalarStyle.PLAIN) tag = "!"; | |
307 | |
308 _state = _states.removeLast(); | |
309 _scanner.scan(); | |
310 return new ScalarEvent( | |
311 span.expand(token.span), token.value, token.style, | |
312 anchor: anchor, tag: tag); | |
313 } | |
314 | |
315 if (token.type == TokenType.FLOW_SEQUENCE_START) { | |
316 _state = _State.FLOW_SEQUENCE_FIRST_ENTRY; | |
317 return new SequenceStartEvent( | |
318 span.expand(token.span), CollectionStyle.FLOW, | |
319 anchor: anchor, tag: tag); | |
320 } | |
321 | |
322 if (token.type == TokenType.FLOW_MAPPING_START) { | |
323 _state = _State.FLOW_MAPPING_FIRST_KEY; | |
324 return new MappingStartEvent( | |
325 span.expand(token.span), CollectionStyle.FLOW, | |
326 anchor: anchor, tag: tag); | |
327 } | |
328 | |
329 if (block && token.type == TokenType.BLOCK_SEQUENCE_START) { | |
330 _state = _State.BLOCK_SEQUENCE_FIRST_ENTRY; | |
331 return new SequenceStartEvent( | |
332 span.expand(token.span), CollectionStyle.BLOCK, | |
333 anchor: anchor, tag: tag); | |
334 } | |
335 | |
336 | |
337 if (block && token.type == TokenType.BLOCK_MAPPING_START) { | |
338 _state = _State.BLOCK_MAPPING_FIRST_KEY; | |
339 return new MappingStartEvent( | |
340 span.expand(token.span), CollectionStyle.BLOCK, | |
341 anchor: anchor, tag: tag); | |
342 } | |
343 | |
344 if (anchor != null || tag != null) { | |
345 _state = _states.removeLast(); | |
346 return new ScalarEvent( | |
347 span, '', ScalarStyle.PLAIN, | |
348 anchor: anchor, tag: tag); | |
349 } | |
350 | |
351 throw new YamlException("Expected node content.", span); | |
352 } | |
353 | |
354 /// Parses the productions: | |
355 /// | |
356 /// block_sequence ::= | |
357 /// BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END | |
358 /// ******************** *********** * ********* | |
359 Event _parseBlockSequenceEntry() { | |
360 var token = _scanner.peek(); | |
361 | |
362 if (token.type == TokenType.BLOCK_ENTRY) { | |
363 token = _scanner.advance(); | |
364 | |
365 if (token.type == TokenType.BLOCK_ENTRY || | |
366 token.type == TokenType.BLOCK_END) { | |
367 _state = _State.BLOCK_SEQUENCE_ENTRY; | |
368 return _processEmptyScalar(token.span.end); | |
369 } else { | |
370 _states.add(_State.BLOCK_SEQUENCE_ENTRY); | |
371 return _parseNode(block: true); | |
372 } | |
373 } | |
374 | |
375 if (token.type == TokenType.BLOCK_END) { | |
376 _scanner.scan(); | |
377 _state = _states.removeLast(); | |
378 return new Event(EventType.SEQUENCE_END, token.span); | |
379 } | |
380 | |
381 throw new YamlException("While parsing a block collection, expected '-'.", | |
382 token.span.start.pointSpan()); | |
383 } | |
384 | |
385 /// Parses the productions: | |
386 /// | |
387 /// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ | |
388 /// *********** * | |
389 Event _parseIndentlessSequenceEntry() { | |
390 var token = _scanner.peek(); | |
391 | |
392 if (token.type != TokenType.BLOCK_ENTRY) { | |
393 _state = _states.removeLast(); | |
394 return new Event(EventType.SEQUENCE_END, token.span.start.pointSpan()); | |
395 } | |
396 | |
397 var start = token.span.start; | |
398 token = _scanner.advance(); | |
399 | |
400 if (token.type == TokenType.BLOCK_ENTRY || | |
401 token.type == TokenType.KEY || | |
402 token.type == TokenType.VALUE || | |
403 token.type == TokenType.BLOCK_END) { | |
404 _state = _State.INDENTLESS_SEQUENCE_ENTRY; | |
405 return _processEmptyScalar(start); | |
406 } else { | |
407 _states.add(_State.INDENTLESS_SEQUENCE_ENTRY); | |
408 return _parseNode(block: true); | |
409 } | |
410 } | |
411 | |
412 /// Parses the productions: | |
413 /// | |
414 /// block_mapping ::= BLOCK-MAPPING_START | |
415 /// ******************* | |
416 /// ((KEY block_node_or_indentless_sequence?)? | |
417 /// *** * | |
418 /// (VALUE block_node_or_indentless_sequence?)?)* | |
419 /// | |
420 /// BLOCK-END | |
421 /// ********* | |
422 Event _parseBlockMappingKey() { | |
423 var token = _scanner.peek(); | |
424 if (token.type == TokenType.KEY) { | |
425 var start = token.span.start; | |
426 token = _scanner.advance(); | |
427 | |
428 if (token.type == TokenType.KEY || | |
429 token.type == TokenType.VALUE || | |
430 token.type == TokenType.BLOCK_END) { | |
431 _state = _State.BLOCK_MAPPING_VALUE; | |
432 return _processEmptyScalar(start); | |
433 } else { | |
434 _states.add(_State.BLOCK_MAPPING_VALUE); | |
435 return _parseNode(block: true, indentlessSequence: true); | |
436 } | |
437 } | |
438 | |
439 // libyaml doesn't allow empty keys without an explicit key indicator, but | |
440 // the spec does. See example 8.18: | |
441 // http://yaml.org/spec/1.2/spec.html#id2798896. | |
442 if (token.type == TokenType.VALUE) { | |
443 _state = _State.BLOCK_MAPPING_VALUE; | |
444 return _processEmptyScalar(token.span.start); | |
445 } | |
446 | |
447 if (token.type == TokenType.BLOCK_END) { | |
448 _scanner.scan(); | |
449 _state = _states.removeLast(); | |
450 return new Event(EventType.MAPPING_END, token.span); | |
451 } | |
452 | |
453 throw new YamlException("Expected a key while parsing a block mapping.", | |
454 token.span.start.pointSpan()); | |
455 } | |
456 | |
457 /// Parses the productions: | |
458 /// | |
459 /// block_mapping ::= BLOCK-MAPPING_START | |
460 /// | |
461 /// ((KEY block_node_or_indentless_sequence?)? | |
462 /// | |
463 /// (VALUE block_node_or_indentless_sequence?)?)* | |
464 /// ***** * | |
465 /// BLOCK-END | |
466 /// | |
467 Event _parseBlockMappingValue() { | |
468 var token = _scanner.peek(); | |
469 | |
470 if (token.type != TokenType.VALUE) { | |
471 _state = _State.BLOCK_MAPPING_KEY; | |
472 return _processEmptyScalar(token.span.start); | |
473 } | |
474 | |
475 var start = token.span.start; | |
476 token = _scanner.advance(); | |
477 if (token.type == TokenType.KEY || | |
478 token.type == TokenType.VALUE || | |
479 token.type == TokenType.BLOCK_END) { | |
480 _state = _State.BLOCK_MAPPING_KEY; | |
481 return _processEmptyScalar(start); | |
482 } else { | |
483 _states.add(_State.BLOCK_MAPPING_KEY); | |
484 return _parseNode(block: true, indentlessSequence: true); | |
485 } | |
486 } | |
487 | |
488 /// Parses the productions: | |
489 /// | |
490 /// flow_sequence ::= FLOW-SEQUENCE-START | |
491 /// ******************* | |
492 /// (flow_sequence_entry FLOW-ENTRY)* | |
493 /// * ********** | |
494 /// flow_sequence_entry? | |
495 /// * | |
496 /// FLOW-SEQUENCE-END | |
497 /// ***************** | |
498 /// flow_sequence_entry ::= | |
499 /// flow_node | KEY flow_node? (VALUE flow_node?)? | |
500 /// * | |
501 Event _parseFlowSequenceEntry({bool first: false}) { | |
502 if (first) _scanner.scan(); | |
503 var token = _scanner.peek(); | |
504 | |
505 if (token.type != TokenType.FLOW_SEQUENCE_END) { | |
506 if (!first) { | |
507 if (token.type != TokenType.FLOW_ENTRY) { | |
508 throw new YamlException( | |
509 "While parsing a flow sequence, expected ',' or ']'.", | |
510 token.span.start.pointSpan()); | |
511 } | |
512 | |
513 token = _scanner.advance(); | |
514 } | |
515 | |
516 if (token.type == TokenType.KEY) { | |
517 _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY; | |
518 _scanner.scan(); | |
519 return new MappingStartEvent( | |
520 token.span, CollectionStyle.FLOW); | |
521 } else if (token.type != TokenType.FLOW_SEQUENCE_END) { | |
522 _states.add(_State.FLOW_SEQUENCE_ENTRY); | |
523 return _parseNode(); | |
524 } | |
525 } | |
526 | |
527 _scanner.scan(); | |
528 _state = _states.removeLast(); | |
529 return new Event(EventType.SEQUENCE_END, token.span); | |
530 } | |
531 | |
532 /// Parses the productions: | |
533 /// | |
534 /// flow_sequence_entry ::= | |
535 /// flow_node | KEY flow_node? (VALUE flow_node?)? | |
536 /// *** * | |
537 Event _parseFlowSequenceEntryMappingKey() { | |
538 var token = _scanner.peek(); | |
539 | |
540 if (token.type == TokenType.VALUE || | |
541 token.type == TokenType.FLOW_ENTRY || | |
542 token.type == TokenType.FLOW_SEQUENCE_END) { | |
543 // libyaml consumes the token here, but that seems like a bug, since it | |
544 // always causes [_parseFlowSequenceEntryMappingValue] to emit an empty | |
545 // scalar. | |
546 | |
547 var start = token.span.start; | |
548 _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE; | |
549 return _processEmptyScalar(start); | |
550 } else { | |
551 _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE); | |
552 return _parseNode(); | |
553 } | |
554 } | |
555 | |
556 /// Parses the productions: | |
557 /// | |
558 /// flow_sequence_entry ::= | |
559 /// flow_node | KEY flow_node? (VALUE flow_node?)? | |
560 /// ***** * | |
561 Event _parseFlowSequenceEntryMappingValue() { | |
562 var token = _scanner.peek(); | |
563 | |
564 if (token.type == TokenType.VALUE) { | |
565 token = _scanner.advance(); | |
566 if (token.type != TokenType.FLOW_ENTRY && | |
567 token.type != TokenType.FLOW_SEQUENCE_END) { | |
568 _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_END); | |
569 return _parseNode(); | |
570 } | |
571 } | |
572 | |
573 _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_END; | |
574 return _processEmptyScalar(token.span.start); | |
575 } | |
576 | |
577 /// Parses the productions: | |
578 /// | |
579 /// flow_sequence_entry ::= | |
580 /// flow_node | KEY flow_node? (VALUE flow_node?)? | |
581 /// * | |
582 Event _parseFlowSequenceEntryMappingEnd() { | |
583 _state = _State.FLOW_SEQUENCE_ENTRY; | |
584 return new Event(EventType.MAPPING_END, | |
585 _scanner.peek().span.start.pointSpan()); | |
586 } | |
587 | |
588 /// Parses the productions: | |
589 /// | |
590 /// flow_mapping ::= FLOW-MAPPING-START | |
591 /// ****************** | |
592 /// (flow_mapping_entry FLOW-ENTRY)* | |
593 /// * ********** | |
594 /// flow_mapping_entry? | |
595 /// ****************** | |
596 /// FLOW-MAPPING-END | |
597 /// **************** | |
598 /// flow_mapping_entry ::= | |
599 /// flow_node | KEY flow_node? (VALUE flow_node?)? | |
600 /// * *** * | |
601 Event _parseFlowMappingKey({bool first: false}) { | |
602 if (first) _scanner.scan(); | |
603 var token = _scanner.peek(); | |
604 | |
605 if (token.type != TokenType.FLOW_MAPPING_END) { | |
606 if (!first) { | |
607 if (token.type != TokenType.FLOW_ENTRY) { | |
608 throw new YamlException( | |
609 "While parsing a flow mapping, expected ',' or '}'.", | |
610 token.span.start.pointSpan()); | |
611 } | |
612 | |
613 token = _scanner.advance(); | |
614 } | |
615 | |
616 if (token.type == TokenType.KEY) { | |
617 token = _scanner.advance(); | |
618 if (token.type != TokenType.VALUE && | |
619 token.type != TokenType.FLOW_ENTRY && | |
620 token.type != TokenType.FLOW_MAPPING_END) { | |
621 _states.add(_State.FLOW_MAPPING_VALUE); | |
622 return _parseNode(); | |
623 } else { | |
624 _state = _State.FLOW_MAPPING_VALUE; | |
625 return _processEmptyScalar(token.span.start); | |
626 } | |
627 } else if (token.type != TokenType.FLOW_MAPPING_END) { | |
628 _states.add(_State.FLOW_MAPPING_EMPTY_VALUE); | |
629 return _parseNode(); | |
630 } | |
631 } | |
632 | |
633 _scanner.scan(); | |
634 _state = _states.removeLast(); | |
635 return new Event(EventType.MAPPING_END, token.span); | |
636 } | |
637 | |
638 /// Parses the productions: | |
639 /// | |
640 /// flow_mapping_entry ::= | |
641 /// flow_node | KEY flow_node? (VALUE flow_node?)? | |
642 /// * ***** * | |
643 Event _parseFlowMappingValue({bool empty: false}) { | |
644 var token = _scanner.peek(); | |
645 | |
646 if (empty) { | |
647 _state = _State.FLOW_MAPPING_KEY; | |
648 return _processEmptyScalar(token.span.start); | |
649 } | |
650 | |
651 if (token.type == TokenType.VALUE) { | |
652 token = _scanner.advance(); | |
653 if (token.type != TokenType.FLOW_ENTRY && | |
654 token.type != TokenType.FLOW_MAPPING_END) { | |
655 _states.add(_State.FLOW_MAPPING_KEY); | |
656 return _parseNode(); | |
657 } | |
658 } | |
659 | |
660 _state = _State.FLOW_MAPPING_KEY; | |
661 return _processEmptyScalar(token.span.start); | |
662 } | |
663 | |
664 /// Generate an empty scalar event. | |
665 Event _processEmptyScalar(SourceLocation location) => | |
666 new ScalarEvent(location.pointSpan(), '', ScalarStyle.PLAIN); | |
667 | |
668 /// Parses directives. | |
669 Pair<VersionDirective, List<TagDirective>> _processDirectives() { | |
670 var token = _scanner.peek(); | |
671 | |
672 var versionDirective; | |
673 var tagDirectives = []; | |
674 while (token.type == TokenType.VERSION_DIRECTIVE || | |
675 token.type == TokenType.TAG_DIRECTIVE) { | |
676 if (token is VersionDirectiveToken) { | |
677 if (versionDirective != null) { | |
678 throw new YamlException("Duplicate %YAML directive.", token.span); | |
679 } | |
680 | |
681 if (token.major != 1 || token.minor == 0) { | |
682 throw new YamlException( | |
683 "Incompatible YAML document. This parser only supports YAML 1.1 " | |
684 "and 1.2.", | |
685 token.span); | |
686 } else if (token.minor > 2) { | |
687 // TODO(nweiz): Print to stderr when issue 6943 is fixed and dart:io | |
688 // is available. | |
689 warn("Warning: this parser only supports YAML 1.1 and 1.2.", | |
690 token.span); | |
691 } | |
692 | |
693 versionDirective = new VersionDirective(token.major, token.minor); | |
694 } else if (token is TagDirectiveToken) { | |
695 var tagDirective = new TagDirective(token.handle, token.prefix); | |
696 _appendTagDirective(tagDirective, token.span); | |
697 tagDirectives.add(tagDirective); | |
698 } | |
699 | |
700 token = _scanner.advance(); | |
701 } | |
702 | |
703 _appendTagDirective( | |
704 new TagDirective("!", "!"), | |
705 token.span.start.pointSpan(), | |
706 allowDuplicates: true); | |
707 _appendTagDirective( | |
708 new TagDirective("!!", "tag:yaml.org,2002:"), | |
709 token.span.start.pointSpan(), | |
710 allowDuplicates: true); | |
711 | |
712 return new Pair(versionDirective, tagDirectives); | |
713 } | |
714 | |
715 /// Adds a tag directive to the directives stack. | |
716 void _appendTagDirective(TagDirective newDirective, FileSpan span, | |
717 {bool allowDuplicates: false}) { | |
718 if (_tagDirectives.containsKey(newDirective.handle)) { | |
719 if (allowDuplicates) return; | |
720 throw new YamlException("Duplicate %TAG directive.", span); | |
721 } | |
722 | |
723 _tagDirectives[newDirective.handle] = newDirective; | |
724 } | |
725 } | |
726 | |
727 /// The possible states for the parser. | |
728 class _State { | |
729 /// Expect [TokenType.STREAM_START]. | |
730 static const STREAM_START = const _State("STREAM_START"); | |
731 | |
732 /// Expect the beginning of an implicit document. | |
733 static const IMPLICIT_DOCUMENT_START = | |
734 const _State("IMPLICIT_DOCUMENT_START"); | |
735 | |
736 /// Expect [TokenType.DOCUMENT_START]. | |
737 static const DOCUMENT_START = const _State("DOCUMENT_START"); | |
738 | |
739 /// Expect the content of a document. | |
740 static const DOCUMENT_CONTENT = const _State("DOCUMENT_CONTENT"); | |
741 | |
742 /// Expect [TokenType.DOCUMENT_END]. | |
743 static const DOCUMENT_END = const _State("DOCUMENT_END"); | |
744 | |
745 /// Expect a block node. | |
746 static const BLOCK_NODE = const _State("BLOCK_NODE"); | |
747 | |
748 /// Expect a block node or indentless sequence. | |
749 static const BLOCK_NODE_OR_INDENTLESS_SEQUENCE = | |
750 const _State("BLOCK_NODE_OR_INDENTLESS_SEQUENCE"); | |
751 | |
752 /// Expect a flow node. | |
753 static const FLOW_NODE = const _State("FLOW_NODE"); | |
754 | |
755 /// Expect the first entry of a block sequence. | |
756 static const BLOCK_SEQUENCE_FIRST_ENTRY = | |
757 const _State("BLOCK_SEQUENCE_FIRST_ENTRY"); | |
758 | |
759 /// Expect an entry of a block sequence. | |
760 static const BLOCK_SEQUENCE_ENTRY = const _State("BLOCK_SEQUENCE_ENTRY"); | |
761 | |
762 /// Expect an entry of an indentless sequence. | |
763 static const INDENTLESS_SEQUENCE_ENTRY = | |
764 const _State("INDENTLESS_SEQUENCE_ENTRY"); | |
765 | |
766 /// Expect the first key of a block mapping. | |
767 static const BLOCK_MAPPING_FIRST_KEY = | |
768 const _State("BLOCK_MAPPING_FIRST_KEY"); | |
769 | |
770 /// Expect a block mapping key. | |
771 static const BLOCK_MAPPING_KEY = const _State("BLOCK_MAPPING_KEY"); | |
772 | |
773 /// Expect a block mapping value. | |
774 static const BLOCK_MAPPING_VALUE = const _State("BLOCK_MAPPING_VALUE"); | |
775 | |
776 /// Expect the first entry of a flow sequence. | |
777 static const FLOW_SEQUENCE_FIRST_ENTRY = | |
778 const _State("FLOW_SEQUENCE_FIRST_ENTRY"); | |
779 | |
780 /// Expect an entry of a flow sequence. | |
781 static const FLOW_SEQUENCE_ENTRY = const _State("FLOW_SEQUENCE_ENTRY"); | |
782 | |
783 /// Expect a key of an ordered mapping. | |
784 static const FLOW_SEQUENCE_ENTRY_MAPPING_KEY = | |
785 const _State("FLOW_SEQUENCE_ENTRY_MAPPING_KEY"); | |
786 | |
787 /// Expect a value of an ordered mapping. | |
788 static const FLOW_SEQUENCE_ENTRY_MAPPING_VALUE = | |
789 const _State("FLOW_SEQUENCE_ENTRY_MAPPING_VALUE"); | |
790 | |
791 /// Expect the and of an ordered mapping entry. | |
792 static const FLOW_SEQUENCE_ENTRY_MAPPING_END = | |
793 const _State("FLOW_SEQUENCE_ENTRY_MAPPING_END"); | |
794 | |
795 /// Expect the first key of a flow mapping. | |
796 static const FLOW_MAPPING_FIRST_KEY = const _State("FLOW_MAPPING_FIRST_KEY"); | |
797 | |
798 /// Expect a key of a flow mapping. | |
799 static const FLOW_MAPPING_KEY = const _State("FLOW_MAPPING_KEY"); | |
800 | |
801 /// Expect a value of a flow mapping. | |
802 static const FLOW_MAPPING_VALUE = const _State("FLOW_MAPPING_VALUE"); | |
803 | |
804 /// Expect an empty value of a flow mapping. | |
805 static const FLOW_MAPPING_EMPTY_VALUE = | |
806 const _State("FLOW_MAPPING_EMPTY_VALUE"); | |
807 | |
808 /// Expect nothing. | |
809 static const END = const _State("END"); | |
810 | |
811 final String name; | |
812 | |
813 const _State(this.name); | |
814 | |
815 String toString() => name; | |
816 } | |
OLD | NEW |