| 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 |