Index: mojo/public/dart/third_party/yaml/lib/src/parser.dart |
diff --git a/mojo/public/dart/third_party/yaml/lib/src/parser.dart b/mojo/public/dart/third_party/yaml/lib/src/parser.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8bb131f8346bb1e6d2df1d498c537131b5bc9f36 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/yaml/lib/src/parser.dart |
@@ -0,0 +1,816 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library yaml.parser; |
+ |
+import 'package:source_span/source_span.dart'; |
+import 'package:string_scanner/string_scanner.dart'; |
+ |
+import 'event.dart'; |
+import 'scanner.dart'; |
+import 'style.dart'; |
+import 'token.dart'; |
+import 'utils.dart'; |
+import 'yaml_document.dart'; |
+import 'yaml_exception.dart'; |
+ |
+/// A parser that reads [Token]s emitted by a [Scanner] and emits [Event]s. |
+/// |
+/// This is based on the libyaml parser, available at |
+/// https://github.com/yaml/libyaml/blob/master/src/parser.c. The license for |
+/// that is available in ../../libyaml-license.txt. |
+class Parser { |
+ /// The underlying [Scanner] that generates [Token]s. |
+ final Scanner _scanner; |
+ |
+ /// The stack of parse states for nested contexts. |
+ final _states = new List<_State>(); |
+ |
+ /// The current parse state. |
+ var _state = _State.STREAM_START; |
+ |
+ /// The custom tag directives, by tag handle. |
+ final _tagDirectives = new Map<String, TagDirective>(); |
+ |
+ /// Whether the parser has finished parsing. |
+ bool get isDone => _state == _State.END; |
+ |
+ /// Creates a parser that parses [source]. |
+ /// |
+ /// [sourceUrl] can be a String or a [Uri]. |
+ Parser(String source, {sourceUrl}) |
+ : _scanner = new Scanner(source, sourceUrl: sourceUrl); |
+ |
+ /// Consumes and returns the next event. |
+ Event parse() { |
+ try { |
+ if (isDone) throw new StateError("No more events."); |
+ var event = _stateMachine(); |
+ return event; |
+ } on StringScannerException catch (error) { |
+ throw new YamlException(error.message, error.span); |
+ } |
+ } |
+ |
+ /// Dispatches parsing based on the current state. |
+ Event _stateMachine() { |
+ switch (_state) { |
+ case _State.STREAM_START: |
+ return _parseStreamStart(); |
+ case _State.DOCUMENT_START: |
+ return _parseDocumentStart(); |
+ case _State.DOCUMENT_CONTENT: |
+ return _parseDocumentContent(); |
+ case _State.DOCUMENT_END: |
+ return _parseDocumentEnd(); |
+ case _State.BLOCK_NODE: |
+ return _parseNode(block: true); |
+ case _State.BLOCK_NODE_OR_INDENTLESS_SEQUENCE: |
+ return _parseNode(block: true, indentlessSequence: true); |
+ case _State.FLOW_NODE: |
+ return _parseNode(); |
+ case _State.BLOCK_SEQUENCE_FIRST_ENTRY: |
+ // Scan past the `BLOCK-SEQUENCE-FIRST-ENTRY` token to the |
+ // `BLOCK-SEQUENCE-ENTRY` token. |
+ _scanner.scan(); |
+ return _parseBlockSequenceEntry(); |
+ case _State.BLOCK_SEQUENCE_ENTRY: |
+ return _parseBlockSequenceEntry(); |
+ case _State.INDENTLESS_SEQUENCE_ENTRY: |
+ return _parseIndentlessSequenceEntry(); |
+ case _State.BLOCK_MAPPING_FIRST_KEY: |
+ // Scan past the `BLOCK-MAPPING-FIRST-KEY` token to the |
+ // `BLOCK-MAPPING-KEY` token. |
+ _scanner.scan(); |
+ return _parseBlockMappingKey(); |
+ case _State.BLOCK_MAPPING_KEY: |
+ return _parseBlockMappingKey(); |
+ case _State.BLOCK_MAPPING_VALUE: |
+ return _parseBlockMappingValue(); |
+ case _State.FLOW_SEQUENCE_FIRST_ENTRY: |
+ return _parseFlowSequenceEntry(first: true); |
+ case _State.FLOW_SEQUENCE_ENTRY: |
+ return _parseFlowSequenceEntry(); |
+ case _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY: |
+ return _parseFlowSequenceEntryMappingKey(); |
+ case _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE: |
+ return _parseFlowSequenceEntryMappingValue(); |
+ case _State.FLOW_SEQUENCE_ENTRY_MAPPING_END: |
+ return _parseFlowSequenceEntryMappingEnd(); |
+ case _State.FLOW_MAPPING_FIRST_KEY: |
+ return _parseFlowMappingKey(first: true); |
+ case _State.FLOW_MAPPING_KEY: |
+ return _parseFlowMappingKey(); |
+ case _State.FLOW_MAPPING_VALUE: |
+ return _parseFlowMappingValue(); |
+ case _State.FLOW_MAPPING_EMPTY_VALUE: |
+ return _parseFlowMappingValue(empty: true); |
+ default: |
+ throw "Unreachable"; |
+ } |
+ } |
+ |
+ /// Parses the production: |
+ /// |
+ /// stream ::= |
+ /// STREAM-START implicit_document? explicit_document* STREAM-END |
+ /// ************ |
+ Event _parseStreamStart() { |
+ var token = _scanner.scan(); |
+ assert(token.type == TokenType.STREAM_START); |
+ |
+ _state = _State.DOCUMENT_START; |
+ return new Event(EventType.STREAM_START, token.span); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// implicit_document ::= block_node DOCUMENT-END* |
+ /// * |
+ /// explicit_document ::= |
+ /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* |
+ /// ************************* |
+ Event _parseDocumentStart() { |
+ var token = _scanner.peek(); |
+ |
+ // libyaml requires any document beyond the first in the stream to have an |
+ // explicit document start indicator, but the spec allows it to be omitted |
+ // as long as there was an end indicator. |
+ |
+ // Parse extra document end indicators. |
+ while (token.type == TokenType.DOCUMENT_END) { |
+ token = _scanner.advance(); |
+ } |
+ |
+ if (token.type != TokenType.VERSION_DIRECTIVE && |
+ token.type != TokenType.TAG_DIRECTIVE && |
+ token.type != TokenType.DOCUMENT_START && |
+ token.type != TokenType.STREAM_END) { |
+ // Parse an implicit document. |
+ _processDirectives(); |
+ _states.add(_State.DOCUMENT_END); |
+ _state = _State.BLOCK_NODE; |
+ return new DocumentStartEvent(token.span.start.pointSpan()); |
+ } |
+ |
+ if (token.type == TokenType.STREAM_END) { |
+ _state = _State.END; |
+ _scanner.scan(); |
+ return new Event(EventType.STREAM_END, token.span); |
+ } |
+ |
+ // Parse an explicit document. |
+ var start = token.span; |
+ var pair = _processDirectives(); |
+ var versionDirective = pair.first; |
+ var tagDirectives = pair.last; |
+ token = _scanner.peek(); |
+ if (token.type != TokenType.DOCUMENT_START) { |
+ throw new YamlException("Expected document start.", token.span); |
+ } |
+ |
+ _states.add(_State.DOCUMENT_END); |
+ _state = _State.DOCUMENT_CONTENT; |
+ _scanner.scan(); |
+ return new DocumentStartEvent(start.expand(token.span), |
+ versionDirective: versionDirective, |
+ tagDirectives: tagDirectives, |
+ isImplicit: false); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// explicit_document ::= |
+ /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* |
+ /// *********** |
+ Event _parseDocumentContent() { |
+ var token = _scanner.peek(); |
+ |
+ switch (token.type) { |
+ case TokenType.VERSION_DIRECTIVE: |
+ case TokenType.TAG_DIRECTIVE: |
+ case TokenType.DOCUMENT_START: |
+ case TokenType.DOCUMENT_END: |
+ case TokenType.STREAM_END: |
+ _state = _states.removeLast(); |
+ return _processEmptyScalar(token.span.start); |
+ default: |
+ return _parseNode(block: true); |
+ } |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// implicit_document ::= block_node DOCUMENT-END* |
+ /// ************* |
+ /// explicit_document ::= |
+ /// DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* |
+ /// ************* |
+ Event _parseDocumentEnd() { |
+ _tagDirectives.clear(); |
+ _state = _State.DOCUMENT_START; |
+ |
+ var token = _scanner.peek(); |
+ if (token.type == TokenType.DOCUMENT_END) { |
+ _scanner.scan(); |
+ return new DocumentEndEvent(token.span, isImplicit: false); |
+ } else { |
+ return new DocumentEndEvent( |
+ token.span.start.pointSpan(), isImplicit: true); |
+ } |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// block_node_or_indentless_sequence ::= |
+ /// ALIAS |
+ /// ***** |
+ /// | properties (block_content | indentless_block_sequence)? |
+ /// ********** * |
+ /// | block_content | indentless_block_sequence |
+ /// * |
+ /// block_node ::= ALIAS |
+ /// ***** |
+ /// | properties block_content? |
+ /// ********** * |
+ /// | block_content |
+ /// * |
+ /// flow_node ::= ALIAS |
+ /// ***** |
+ /// | properties flow_content? |
+ /// ********** * |
+ /// | flow_content |
+ /// * |
+ /// properties ::= TAG ANCHOR? | ANCHOR TAG? |
+ /// ************************* |
+ /// block_content ::= block_collection | flow_collection | SCALAR |
+ /// ****** |
+ /// flow_content ::= flow_collection | SCALAR |
+ /// ****** |
+ Event _parseNode({bool block: false, bool indentlessSequence: false}) { |
+ var token = _scanner.peek(); |
+ |
+ if (token is AliasToken) { |
+ _scanner.scan(); |
+ _state = _states.removeLast(); |
+ return new AliasEvent(token.span, token.name); |
+ } |
+ |
+ var anchor; |
+ var tagToken; |
+ var span = token.span.start.pointSpan(); |
+ parseAnchor() { |
+ anchor = token.name; |
+ span = span.expand(token.span); |
+ token = _scanner.advance(); |
+ } |
+ |
+ parseTag() { |
+ tagToken = token; |
+ span = span.expand(token.span); |
+ token = _scanner.advance(); |
+ } |
+ |
+ if (token is AnchorToken) { |
+ parseAnchor(); |
+ if (token is TagToken) parseTag(); |
+ } else if (token is TagToken) { |
+ parseTag(); |
+ if (token is AnchorToken) parseAnchor(); |
+ } |
+ |
+ var tag; |
+ if (tagToken != null) { |
+ if (tagToken.handle == null) { |
+ tag = tagToken.suffix; |
+ } else { |
+ var tagDirective = _tagDirectives[tagToken.handle]; |
+ if (tagDirective == null) { |
+ throw new YamlException("Undefined tag handle.", tagToken.span); |
+ } |
+ |
+ tag = tagDirective.prefix + tagToken.suffix; |
+ } |
+ } |
+ |
+ if (indentlessSequence && token.type == TokenType.BLOCK_ENTRY) { |
+ _state = _State.INDENTLESS_SEQUENCE_ENTRY; |
+ return new SequenceStartEvent( |
+ span.expand(token.span), CollectionStyle.BLOCK, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ if (token is ScalarToken) { |
+ // All non-plain scalars have the "!" tag by default. |
+ if (tag == null && token.style != ScalarStyle.PLAIN) tag = "!"; |
+ |
+ _state = _states.removeLast(); |
+ _scanner.scan(); |
+ return new ScalarEvent( |
+ span.expand(token.span), token.value, token.style, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ if (token.type == TokenType.FLOW_SEQUENCE_START) { |
+ _state = _State.FLOW_SEQUENCE_FIRST_ENTRY; |
+ return new SequenceStartEvent( |
+ span.expand(token.span), CollectionStyle.FLOW, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ if (token.type == TokenType.FLOW_MAPPING_START) { |
+ _state = _State.FLOW_MAPPING_FIRST_KEY; |
+ return new MappingStartEvent( |
+ span.expand(token.span), CollectionStyle.FLOW, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ if (block && token.type == TokenType.BLOCK_SEQUENCE_START) { |
+ _state = _State.BLOCK_SEQUENCE_FIRST_ENTRY; |
+ return new SequenceStartEvent( |
+ span.expand(token.span), CollectionStyle.BLOCK, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ |
+ if (block && token.type == TokenType.BLOCK_MAPPING_START) { |
+ _state = _State.BLOCK_MAPPING_FIRST_KEY; |
+ return new MappingStartEvent( |
+ span.expand(token.span), CollectionStyle.BLOCK, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ if (anchor != null || tag != null) { |
+ _state = _states.removeLast(); |
+ return new ScalarEvent( |
+ span, '', ScalarStyle.PLAIN, |
+ anchor: anchor, tag: tag); |
+ } |
+ |
+ throw new YamlException("Expected node content.", span); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// block_sequence ::= |
+ /// BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END |
+ /// ******************** *********** * ********* |
+ Event _parseBlockSequenceEntry() { |
+ var token = _scanner.peek(); |
+ |
+ if (token.type == TokenType.BLOCK_ENTRY) { |
+ token = _scanner.advance(); |
+ |
+ if (token.type == TokenType.BLOCK_ENTRY || |
+ token.type == TokenType.BLOCK_END) { |
+ _state = _State.BLOCK_SEQUENCE_ENTRY; |
+ return _processEmptyScalar(token.span.end); |
+ } else { |
+ _states.add(_State.BLOCK_SEQUENCE_ENTRY); |
+ return _parseNode(block: true); |
+ } |
+ } |
+ |
+ if (token.type == TokenType.BLOCK_END) { |
+ _scanner.scan(); |
+ _state = _states.removeLast(); |
+ return new Event(EventType.SEQUENCE_END, token.span); |
+ } |
+ |
+ throw new YamlException("While parsing a block collection, expected '-'.", |
+ token.span.start.pointSpan()); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ |
+ /// *********** * |
+ Event _parseIndentlessSequenceEntry() { |
+ var token = _scanner.peek(); |
+ |
+ if (token.type != TokenType.BLOCK_ENTRY) { |
+ _state = _states.removeLast(); |
+ return new Event(EventType.SEQUENCE_END, token.span.start.pointSpan()); |
+ } |
+ |
+ var start = token.span.start; |
+ token = _scanner.advance(); |
+ |
+ if (token.type == TokenType.BLOCK_ENTRY || |
+ token.type == TokenType.KEY || |
+ token.type == TokenType.VALUE || |
+ token.type == TokenType.BLOCK_END) { |
+ _state = _State.INDENTLESS_SEQUENCE_ENTRY; |
+ return _processEmptyScalar(start); |
+ } else { |
+ _states.add(_State.INDENTLESS_SEQUENCE_ENTRY); |
+ return _parseNode(block: true); |
+ } |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// block_mapping ::= BLOCK-MAPPING_START |
+ /// ******************* |
+ /// ((KEY block_node_or_indentless_sequence?)? |
+ /// *** * |
+ /// (VALUE block_node_or_indentless_sequence?)?)* |
+ /// |
+ /// BLOCK-END |
+ /// ********* |
+ Event _parseBlockMappingKey() { |
+ var token = _scanner.peek(); |
+ if (token.type == TokenType.KEY) { |
+ var start = token.span.start; |
+ token = _scanner.advance(); |
+ |
+ if (token.type == TokenType.KEY || |
+ token.type == TokenType.VALUE || |
+ token.type == TokenType.BLOCK_END) { |
+ _state = _State.BLOCK_MAPPING_VALUE; |
+ return _processEmptyScalar(start); |
+ } else { |
+ _states.add(_State.BLOCK_MAPPING_VALUE); |
+ return _parseNode(block: true, indentlessSequence: true); |
+ } |
+ } |
+ |
+ // libyaml doesn't allow empty keys without an explicit key indicator, but |
+ // the spec does. See example 8.18: |
+ // http://yaml.org/spec/1.2/spec.html#id2798896. |
+ if (token.type == TokenType.VALUE) { |
+ _state = _State.BLOCK_MAPPING_VALUE; |
+ return _processEmptyScalar(token.span.start); |
+ } |
+ |
+ if (token.type == TokenType.BLOCK_END) { |
+ _scanner.scan(); |
+ _state = _states.removeLast(); |
+ return new Event(EventType.MAPPING_END, token.span); |
+ } |
+ |
+ throw new YamlException("Expected a key while parsing a block mapping.", |
+ token.span.start.pointSpan()); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// block_mapping ::= BLOCK-MAPPING_START |
+ /// |
+ /// ((KEY block_node_or_indentless_sequence?)? |
+ /// |
+ /// (VALUE block_node_or_indentless_sequence?)?)* |
+ /// ***** * |
+ /// BLOCK-END |
+ /// |
+ Event _parseBlockMappingValue() { |
+ var token = _scanner.peek(); |
+ |
+ if (token.type != TokenType.VALUE) { |
+ _state = _State.BLOCK_MAPPING_KEY; |
+ return _processEmptyScalar(token.span.start); |
+ } |
+ |
+ var start = token.span.start; |
+ token = _scanner.advance(); |
+ if (token.type == TokenType.KEY || |
+ token.type == TokenType.VALUE || |
+ token.type == TokenType.BLOCK_END) { |
+ _state = _State.BLOCK_MAPPING_KEY; |
+ return _processEmptyScalar(start); |
+ } else { |
+ _states.add(_State.BLOCK_MAPPING_KEY); |
+ return _parseNode(block: true, indentlessSequence: true); |
+ } |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// flow_sequence ::= FLOW-SEQUENCE-START |
+ /// ******************* |
+ /// (flow_sequence_entry FLOW-ENTRY)* |
+ /// * ********** |
+ /// flow_sequence_entry? |
+ /// * |
+ /// FLOW-SEQUENCE-END |
+ /// ***************** |
+ /// flow_sequence_entry ::= |
+ /// flow_node | KEY flow_node? (VALUE flow_node?)? |
+ /// * |
+ Event _parseFlowSequenceEntry({bool first: false}) { |
+ if (first) _scanner.scan(); |
+ var token = _scanner.peek(); |
+ |
+ if (token.type != TokenType.FLOW_SEQUENCE_END) { |
+ if (!first) { |
+ if (token.type != TokenType.FLOW_ENTRY) { |
+ throw new YamlException( |
+ "While parsing a flow sequence, expected ',' or ']'.", |
+ token.span.start.pointSpan()); |
+ } |
+ |
+ token = _scanner.advance(); |
+ } |
+ |
+ if (token.type == TokenType.KEY) { |
+ _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY; |
+ _scanner.scan(); |
+ return new MappingStartEvent( |
+ token.span, CollectionStyle.FLOW); |
+ } else if (token.type != TokenType.FLOW_SEQUENCE_END) { |
+ _states.add(_State.FLOW_SEQUENCE_ENTRY); |
+ return _parseNode(); |
+ } |
+ } |
+ |
+ _scanner.scan(); |
+ _state = _states.removeLast(); |
+ return new Event(EventType.SEQUENCE_END, token.span); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// flow_sequence_entry ::= |
+ /// flow_node | KEY flow_node? (VALUE flow_node?)? |
+ /// *** * |
+ Event _parseFlowSequenceEntryMappingKey() { |
+ var token = _scanner.peek(); |
+ |
+ if (token.type == TokenType.VALUE || |
+ token.type == TokenType.FLOW_ENTRY || |
+ token.type == TokenType.FLOW_SEQUENCE_END) { |
+ // libyaml consumes the token here, but that seems like a bug, since it |
+ // always causes [_parseFlowSequenceEntryMappingValue] to emit an empty |
+ // scalar. |
+ |
+ var start = token.span.start; |
+ _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE; |
+ return _processEmptyScalar(start); |
+ } else { |
+ _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE); |
+ return _parseNode(); |
+ } |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// flow_sequence_entry ::= |
+ /// flow_node | KEY flow_node? (VALUE flow_node?)? |
+ /// ***** * |
+ Event _parseFlowSequenceEntryMappingValue() { |
+ var token = _scanner.peek(); |
+ |
+ if (token.type == TokenType.VALUE) { |
+ token = _scanner.advance(); |
+ if (token.type != TokenType.FLOW_ENTRY && |
+ token.type != TokenType.FLOW_SEQUENCE_END) { |
+ _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_END); |
+ return _parseNode(); |
+ } |
+ } |
+ |
+ _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_END; |
+ return _processEmptyScalar(token.span.start); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// flow_sequence_entry ::= |
+ /// flow_node | KEY flow_node? (VALUE flow_node?)? |
+ /// * |
+ Event _parseFlowSequenceEntryMappingEnd() { |
+ _state = _State.FLOW_SEQUENCE_ENTRY; |
+ return new Event(EventType.MAPPING_END, |
+ _scanner.peek().span.start.pointSpan()); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// flow_mapping ::= FLOW-MAPPING-START |
+ /// ****************** |
+ /// (flow_mapping_entry FLOW-ENTRY)* |
+ /// * ********** |
+ /// flow_mapping_entry? |
+ /// ****************** |
+ /// FLOW-MAPPING-END |
+ /// **************** |
+ /// flow_mapping_entry ::= |
+ /// flow_node | KEY flow_node? (VALUE flow_node?)? |
+ /// * *** * |
+ Event _parseFlowMappingKey({bool first: false}) { |
+ if (first) _scanner.scan(); |
+ var token = _scanner.peek(); |
+ |
+ if (token.type != TokenType.FLOW_MAPPING_END) { |
+ if (!first) { |
+ if (token.type != TokenType.FLOW_ENTRY) { |
+ throw new YamlException( |
+ "While parsing a flow mapping, expected ',' or '}'.", |
+ token.span.start.pointSpan()); |
+ } |
+ |
+ token = _scanner.advance(); |
+ } |
+ |
+ if (token.type == TokenType.KEY) { |
+ token = _scanner.advance(); |
+ if (token.type != TokenType.VALUE && |
+ token.type != TokenType.FLOW_ENTRY && |
+ token.type != TokenType.FLOW_MAPPING_END) { |
+ _states.add(_State.FLOW_MAPPING_VALUE); |
+ return _parseNode(); |
+ } else { |
+ _state = _State.FLOW_MAPPING_VALUE; |
+ return _processEmptyScalar(token.span.start); |
+ } |
+ } else if (token.type != TokenType.FLOW_MAPPING_END) { |
+ _states.add(_State.FLOW_MAPPING_EMPTY_VALUE); |
+ return _parseNode(); |
+ } |
+ } |
+ |
+ _scanner.scan(); |
+ _state = _states.removeLast(); |
+ return new Event(EventType.MAPPING_END, token.span); |
+ } |
+ |
+ /// Parses the productions: |
+ /// |
+ /// flow_mapping_entry ::= |
+ /// flow_node | KEY flow_node? (VALUE flow_node?)? |
+ /// * ***** * |
+ Event _parseFlowMappingValue({bool empty: false}) { |
+ var token = _scanner.peek(); |
+ |
+ if (empty) { |
+ _state = _State.FLOW_MAPPING_KEY; |
+ return _processEmptyScalar(token.span.start); |
+ } |
+ |
+ if (token.type == TokenType.VALUE) { |
+ token = _scanner.advance(); |
+ if (token.type != TokenType.FLOW_ENTRY && |
+ token.type != TokenType.FLOW_MAPPING_END) { |
+ _states.add(_State.FLOW_MAPPING_KEY); |
+ return _parseNode(); |
+ } |
+ } |
+ |
+ _state = _State.FLOW_MAPPING_KEY; |
+ return _processEmptyScalar(token.span.start); |
+ } |
+ |
+ /// Generate an empty scalar event. |
+ Event _processEmptyScalar(SourceLocation location) => |
+ new ScalarEvent(location.pointSpan(), '', ScalarStyle.PLAIN); |
+ |
+ /// Parses directives. |
+ Pair<VersionDirective, List<TagDirective>> _processDirectives() { |
+ var token = _scanner.peek(); |
+ |
+ var versionDirective; |
+ var tagDirectives = []; |
+ while (token.type == TokenType.VERSION_DIRECTIVE || |
+ token.type == TokenType.TAG_DIRECTIVE) { |
+ if (token is VersionDirectiveToken) { |
+ if (versionDirective != null) { |
+ throw new YamlException("Duplicate %YAML directive.", token.span); |
+ } |
+ |
+ if (token.major != 1 || token.minor == 0) { |
+ throw new YamlException( |
+ "Incompatible YAML document. This parser only supports YAML 1.1 " |
+ "and 1.2.", |
+ token.span); |
+ } else if (token.minor > 2) { |
+ // TODO(nweiz): Print to stderr when issue 6943 is fixed and dart:io |
+ // is available. |
+ warn("Warning: this parser only supports YAML 1.1 and 1.2.", |
+ token.span); |
+ } |
+ |
+ versionDirective = new VersionDirective(token.major, token.minor); |
+ } else if (token is TagDirectiveToken) { |
+ var tagDirective = new TagDirective(token.handle, token.prefix); |
+ _appendTagDirective(tagDirective, token.span); |
+ tagDirectives.add(tagDirective); |
+ } |
+ |
+ token = _scanner.advance(); |
+ } |
+ |
+ _appendTagDirective( |
+ new TagDirective("!", "!"), |
+ token.span.start.pointSpan(), |
+ allowDuplicates: true); |
+ _appendTagDirective( |
+ new TagDirective("!!", "tag:yaml.org,2002:"), |
+ token.span.start.pointSpan(), |
+ allowDuplicates: true); |
+ |
+ return new Pair(versionDirective, tagDirectives); |
+ } |
+ |
+ /// Adds a tag directive to the directives stack. |
+ void _appendTagDirective(TagDirective newDirective, FileSpan span, |
+ {bool allowDuplicates: false}) { |
+ if (_tagDirectives.containsKey(newDirective.handle)) { |
+ if (allowDuplicates) return; |
+ throw new YamlException("Duplicate %TAG directive.", span); |
+ } |
+ |
+ _tagDirectives[newDirective.handle] = newDirective; |
+ } |
+} |
+ |
+/// The possible states for the parser. |
+class _State { |
+ /// Expect [TokenType.STREAM_START]. |
+ static const STREAM_START = const _State("STREAM_START"); |
+ |
+ /// Expect the beginning of an implicit document. |
+ static const IMPLICIT_DOCUMENT_START = |
+ const _State("IMPLICIT_DOCUMENT_START"); |
+ |
+ /// Expect [TokenType.DOCUMENT_START]. |
+ static const DOCUMENT_START = const _State("DOCUMENT_START"); |
+ |
+ /// Expect the content of a document. |
+ static const DOCUMENT_CONTENT = const _State("DOCUMENT_CONTENT"); |
+ |
+ /// Expect [TokenType.DOCUMENT_END]. |
+ static const DOCUMENT_END = const _State("DOCUMENT_END"); |
+ |
+ /// Expect a block node. |
+ static const BLOCK_NODE = const _State("BLOCK_NODE"); |
+ |
+ /// Expect a block node or indentless sequence. |
+ static const BLOCK_NODE_OR_INDENTLESS_SEQUENCE = |
+ const _State("BLOCK_NODE_OR_INDENTLESS_SEQUENCE"); |
+ |
+ /// Expect a flow node. |
+ static const FLOW_NODE = const _State("FLOW_NODE"); |
+ |
+ /// Expect the first entry of a block sequence. |
+ static const BLOCK_SEQUENCE_FIRST_ENTRY = |
+ const _State("BLOCK_SEQUENCE_FIRST_ENTRY"); |
+ |
+ /// Expect an entry of a block sequence. |
+ static const BLOCK_SEQUENCE_ENTRY = const _State("BLOCK_SEQUENCE_ENTRY"); |
+ |
+ /// Expect an entry of an indentless sequence. |
+ static const INDENTLESS_SEQUENCE_ENTRY = |
+ const _State("INDENTLESS_SEQUENCE_ENTRY"); |
+ |
+ /// Expect the first key of a block mapping. |
+ static const BLOCK_MAPPING_FIRST_KEY = |
+ const _State("BLOCK_MAPPING_FIRST_KEY"); |
+ |
+ /// Expect a block mapping key. |
+ static const BLOCK_MAPPING_KEY = const _State("BLOCK_MAPPING_KEY"); |
+ |
+ /// Expect a block mapping value. |
+ static const BLOCK_MAPPING_VALUE = const _State("BLOCK_MAPPING_VALUE"); |
+ |
+ /// Expect the first entry of a flow sequence. |
+ static const FLOW_SEQUENCE_FIRST_ENTRY = |
+ const _State("FLOW_SEQUENCE_FIRST_ENTRY"); |
+ |
+ /// Expect an entry of a flow sequence. |
+ static const FLOW_SEQUENCE_ENTRY = const _State("FLOW_SEQUENCE_ENTRY"); |
+ |
+ /// Expect a key of an ordered mapping. |
+ static const FLOW_SEQUENCE_ENTRY_MAPPING_KEY = |
+ const _State("FLOW_SEQUENCE_ENTRY_MAPPING_KEY"); |
+ |
+ /// Expect a value of an ordered mapping. |
+ static const FLOW_SEQUENCE_ENTRY_MAPPING_VALUE = |
+ const _State("FLOW_SEQUENCE_ENTRY_MAPPING_VALUE"); |
+ |
+ /// Expect the and of an ordered mapping entry. |
+ static const FLOW_SEQUENCE_ENTRY_MAPPING_END = |
+ const _State("FLOW_SEQUENCE_ENTRY_MAPPING_END"); |
+ |
+ /// Expect the first key of a flow mapping. |
+ static const FLOW_MAPPING_FIRST_KEY = const _State("FLOW_MAPPING_FIRST_KEY"); |
+ |
+ /// Expect a key of a flow mapping. |
+ static const FLOW_MAPPING_KEY = const _State("FLOW_MAPPING_KEY"); |
+ |
+ /// Expect a value of a flow mapping. |
+ static const FLOW_MAPPING_VALUE = const _State("FLOW_MAPPING_VALUE"); |
+ |
+ /// Expect an empty value of a flow mapping. |
+ static const FLOW_MAPPING_EMPTY_VALUE = |
+ const _State("FLOW_MAPPING_EMPTY_VALUE"); |
+ |
+ /// Expect nothing. |
+ static const END = const _State("END"); |
+ |
+ final String name; |
+ |
+ const _State(this.name); |
+ |
+ String toString() => name; |
+} |