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 |