1 /** 2 * Copyright (c) 2008, SnakeYAML 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package org.yaml.snakeyaml.parser; 15 16 import java.util.HashMap; 17 import java.util.LinkedList; 18 import java.util.List; 19 import java.util.Map; 20 import org.yaml.snakeyaml.DumperOptions; 21 import org.yaml.snakeyaml.DumperOptions.Version; 22 import org.yaml.snakeyaml.LoaderOptions; 23 import org.yaml.snakeyaml.comments.CommentType; 24 import org.yaml.snakeyaml.error.Mark; 25 import org.yaml.snakeyaml.error.YAMLException; 26 import org.yaml.snakeyaml.events.AliasEvent; 27 import org.yaml.snakeyaml.events.CommentEvent; 28 import org.yaml.snakeyaml.events.DocumentEndEvent; 29 import org.yaml.snakeyaml.events.DocumentStartEvent; 30 import org.yaml.snakeyaml.events.Event; 31 import org.yaml.snakeyaml.events.ImplicitTuple; 32 import org.yaml.snakeyaml.events.MappingEndEvent; 33 import org.yaml.snakeyaml.events.MappingStartEvent; 34 import org.yaml.snakeyaml.events.ScalarEvent; 35 import org.yaml.snakeyaml.events.SequenceEndEvent; 36 import org.yaml.snakeyaml.events.SequenceStartEvent; 37 import org.yaml.snakeyaml.events.StreamEndEvent; 38 import org.yaml.snakeyaml.events.StreamStartEvent; 39 import org.yaml.snakeyaml.nodes.Tag; 40 import org.yaml.snakeyaml.reader.StreamReader; 41 import org.yaml.snakeyaml.scanner.Scanner; 42 import org.yaml.snakeyaml.scanner.ScannerImpl; 43 import org.yaml.snakeyaml.tokens.AliasToken; 44 import org.yaml.snakeyaml.tokens.AnchorToken; 45 import org.yaml.snakeyaml.tokens.BlockEntryToken; 46 import org.yaml.snakeyaml.tokens.CommentToken; 47 import org.yaml.snakeyaml.tokens.DirectiveToken; 48 import org.yaml.snakeyaml.tokens.ScalarToken; 49 import org.yaml.snakeyaml.tokens.StreamEndToken; 50 import org.yaml.snakeyaml.tokens.StreamStartToken; 51 import org.yaml.snakeyaml.tokens.TagToken; 52 import org.yaml.snakeyaml.tokens.TagTuple; 53 import org.yaml.snakeyaml.tokens.Token; 54 import org.yaml.snakeyaml.util.ArrayStack; 55 56 /** 57 * <pre> 58 * # The following YAML grammar is LL(1) and is parsed by a recursive descent 59 * parser. 60 * stream ::= STREAM-START implicit_document? explicit_document* STREAM-END 61 * implicit_document ::= block_node DOCUMENT-END* 62 * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* 63 * block_node_or_indentless_sequence ::= 64 * ALIAS 65 * | properties (block_content | indentless_block_sequence)? 66 * | block_content 67 * | indentless_block_sequence 68 * block_node ::= ALIAS 69 * | properties block_content? 70 * | block_content 71 * flow_node ::= ALIAS 72 * | properties flow_content? 73 * | flow_content 74 * properties ::= TAG ANCHOR? | ANCHOR TAG? 75 * block_content ::= block_collection | flow_collection | SCALAR 76 * flow_content ::= flow_collection | SCALAR 77 * block_collection ::= block_sequence | block_mapping 78 * flow_collection ::= flow_sequence | flow_mapping 79 * block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END 80 * indentless_sequence ::= (BLOCK-ENTRY block_node?)+ 81 * block_mapping ::= BLOCK-MAPPING_START 82 * ((KEY block_node_or_indentless_sequence?)? 83 * (VALUE block_node_or_indentless_sequence?)?)* 84 * BLOCK-END 85 * flow_sequence ::= FLOW-SEQUENCE-START 86 * (flow_sequence_entry FLOW-ENTRY)* 87 * flow_sequence_entry? 88 * FLOW-SEQUENCE-END 89 * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? 90 * flow_mapping ::= FLOW-MAPPING-START 91 * (flow_mapping_entry FLOW-ENTRY)* 92 * flow_mapping_entry? 93 * FLOW-MAPPING-END 94 * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? 95 * FIRST sets: 96 * stream: { STREAM-START } 97 * explicit_document: { DIRECTIVE DOCUMENT-START } 98 * implicit_document: FIRST(block_node) 99 * block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } 100 * flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } 101 * block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } 102 * flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } 103 * block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } 104 * flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } 105 * block_sequence: { BLOCK-SEQUENCE-START } 106 * block_mapping: { BLOCK-MAPPING-START } 107 * block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } 108 * indentless_sequence: { ENTRY } 109 * flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } 110 * flow_sequence: { FLOW-SEQUENCE-START } 111 * flow_mapping: { FLOW-MAPPING-START } 112 * flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } 113 * flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } 114 * </pre> 115 * 116 * Since writing a recursive-descendant parser is a straightforward task, we do not give many 117 * comments here. 118 */ 119 public class ParserImpl implements Parser { 120 121 private static final Map<String, String> DEFAULT_TAGS = new HashMap<String, String>(); 122 123 static { 124 DEFAULT_TAGS.put("!", "!"); 125 DEFAULT_TAGS.put("!!", Tag.PREFIX); 126 } 127 128 protected final Scanner scanner; 129 private Event currentEvent; 130 private final ArrayStack<Production> states; 131 private final ArrayStack<Mark> marks; 132 private Production state; 133 private VersionTagsTuple directives; 134 ParserImpl(StreamReader reader)135 public ParserImpl(StreamReader reader) { 136 this(new ScannerImpl(reader)); 137 } 138 139 @Deprecated ParserImpl(StreamReader reader, boolean parseComments)140 public ParserImpl(StreamReader reader, boolean parseComments) { 141 this(new ScannerImpl(reader, new LoaderOptions().setProcessComments(parseComments))); 142 } 143 ParserImpl(StreamReader reader, LoaderOptions options)144 public ParserImpl(StreamReader reader, LoaderOptions options) { 145 this(new ScannerImpl(reader, options)); 146 } 147 ParserImpl(Scanner scanner)148 public ParserImpl(Scanner scanner) { 149 this.scanner = scanner; 150 currentEvent = null; 151 directives = new VersionTagsTuple(null, new HashMap<String, String>(DEFAULT_TAGS)); 152 states = new ArrayStack<Production>(100); 153 marks = new ArrayStack<Mark>(10); 154 state = new ParseStreamStart(); 155 } 156 157 /** 158 * Check the type of the next event. 159 */ checkEvent(Event.ID choice)160 public boolean checkEvent(Event.ID choice) { 161 peekEvent(); 162 return currentEvent != null && currentEvent.is(choice); 163 } 164 165 /** 166 * Get the next event. 167 */ peekEvent()168 public Event peekEvent() { 169 if (currentEvent == null) { 170 if (state != null) { 171 currentEvent = state.produce(); 172 } 173 } 174 return currentEvent; 175 } 176 177 /** 178 * Get the next event and proceed further. 179 */ getEvent()180 public Event getEvent() { 181 peekEvent(); 182 Event value = currentEvent; 183 currentEvent = null; 184 return value; 185 } 186 produceCommentEvent(CommentToken token)187 private CommentEvent produceCommentEvent(CommentToken token) { 188 Mark startMark = token.getStartMark(); 189 Mark endMark = token.getEndMark(); 190 String value = token.getValue(); 191 CommentType type = token.getCommentType(); 192 193 // state = state, that no change in state 194 195 return new CommentEvent(type, value, startMark, endMark); 196 } 197 198 /** 199 * <pre> 200 * stream ::= STREAM-START implicit_document? explicit_document* STREAM-END 201 * implicit_document ::= block_node DOCUMENT-END* 202 * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* 203 * </pre> 204 */ 205 private class ParseStreamStart implements Production { 206 produce()207 public Event produce() { 208 // Parse the stream start. 209 StreamStartToken token = (StreamStartToken) scanner.getToken(); 210 Event event = new StreamStartEvent(token.getStartMark(), token.getEndMark()); 211 // Prepare the next state. 212 state = new ParseImplicitDocumentStart(); 213 return event; 214 } 215 } 216 217 private class ParseImplicitDocumentStart implements Production { 218 produce()219 public Event produce() { 220 // Parse an implicit document. 221 if (scanner.checkToken(Token.ID.Comment)) { 222 state = new ParseImplicitDocumentStart(); 223 return produceCommentEvent((CommentToken) scanner.getToken()); 224 } 225 if (!scanner.checkToken(Token.ID.Directive, Token.ID.DocumentStart, Token.ID.StreamEnd)) { 226 Token token = scanner.peekToken(); 227 Mark startMark = token.getStartMark(); 228 Mark endMark = startMark; 229 Event event = new DocumentStartEvent(startMark, endMark, false, null, null); 230 // Prepare the next state. 231 states.push(new ParseDocumentEnd()); 232 state = new ParseBlockNode(); 233 return event; 234 } 235 return new ParseDocumentStart().produce(); 236 } 237 } 238 239 private class ParseDocumentStart implements Production { 240 produce()241 public Event produce() { 242 // Parse any extra document end indicators. 243 while (scanner.checkToken(Token.ID.DocumentEnd)) { 244 scanner.getToken(); 245 } 246 // Parse an explicit document. 247 Event event; 248 if (!scanner.checkToken(Token.ID.StreamEnd)) { 249 Token token = scanner.peekToken(); 250 Mark startMark = token.getStartMark(); 251 VersionTagsTuple tuple = processDirectives(); 252 while (scanner.checkToken(Token.ID.Comment)) { 253 // TODO: till we figure out what todo with the comments 254 scanner.getToken(); 255 } 256 if (!scanner.checkToken(Token.ID.StreamEnd)) { 257 if (!scanner.checkToken(Token.ID.DocumentStart)) { 258 throw new ParserException(null, null, 259 "expected '<document start>', but found '" + scanner.peekToken().getTokenId() + "'", 260 scanner.peekToken().getStartMark()); 261 } 262 token = scanner.getToken(); 263 Mark endMark = token.getEndMark(); 264 event = 265 new DocumentStartEvent(startMark, endMark, true, tuple.getVersion(), tuple.getTags()); 266 states.push(new ParseDocumentEnd()); 267 state = new ParseDocumentContent(); 268 return event; 269 } 270 } 271 // Parse the end of the stream. 272 StreamEndToken token = (StreamEndToken) scanner.getToken(); 273 event = new StreamEndEvent(token.getStartMark(), token.getEndMark()); 274 if (!states.isEmpty()) { 275 throw new YAMLException("Unexpected end of stream. States left: " + states); 276 } 277 if (!marks.isEmpty()) { 278 throw new YAMLException("Unexpected end of stream. Marks left: " + marks); 279 } 280 state = null; 281 return event; 282 } 283 } 284 285 private class ParseDocumentEnd implements Production { 286 produce()287 public Event produce() { 288 // Parse the document end. 289 Token token = scanner.peekToken(); 290 Mark startMark = token.getStartMark(); 291 Mark endMark = startMark; 292 boolean explicit = false; 293 if (scanner.checkToken(Token.ID.DocumentEnd)) { 294 token = scanner.getToken(); 295 endMark = token.getEndMark(); 296 explicit = true; 297 } 298 Event event = new DocumentEndEvent(startMark, endMark, explicit); 299 // Prepare the next state. 300 state = new ParseDocumentStart(); 301 return event; 302 } 303 } 304 305 private class ParseDocumentContent implements Production { 306 produce()307 public Event produce() { 308 if (scanner.checkToken(Token.ID.Comment)) { 309 state = new ParseDocumentContent(); 310 return produceCommentEvent((CommentToken) scanner.getToken()); 311 } 312 if (scanner.checkToken(Token.ID.Directive, Token.ID.DocumentStart, Token.ID.DocumentEnd, 313 Token.ID.StreamEnd)) { 314 Event event = processEmptyScalar(scanner.peekToken().getStartMark()); 315 state = states.pop(); 316 return event; 317 } 318 return new ParseBlockNode().produce(); 319 } 320 } 321 322 /** 323 * https://yaml.org/spec/1.1/#id898785 says "If the document specifies no directives, it is parsed 324 * using the same settings as the previous document. If the document does specify any directives, 325 * all directives of previous documents, if any, are ignored." TODO the last statement is not 326 * respected (as in PyYAML, to work the same) 327 * 328 * @return directives to be applied for the current document 329 */ 330 @SuppressWarnings("unchecked") processDirectives()331 private VersionTagsTuple processDirectives() { 332 HashMap<String, String> tagHandles = new HashMap<String, String>(directives.getTags()); 333 for (String key : DEFAULT_TAGS.keySet()) { 334 tagHandles.remove(key); 335 } 336 // keep only added tag handlers 337 directives = new VersionTagsTuple(null, tagHandles); 338 while (scanner.checkToken(Token.ID.Directive)) { 339 @SuppressWarnings("rawtypes") 340 DirectiveToken token = (DirectiveToken) scanner.getToken(); 341 if (token.getName().equals("YAML")) { 342 if (directives.getVersion() != null) { 343 throw new ParserException(null, null, "found duplicate YAML directive", 344 token.getStartMark()); 345 } 346 List<Integer> value = (List<Integer>) token.getValue(); 347 Integer major = value.get(0); 348 if (major != 1) { 349 throw new ParserException(null, null, 350 "found incompatible YAML document (version 1.* is required)", token.getStartMark()); 351 } 352 Integer minor = value.get(1); 353 // TODO refactor with ternary 354 if (minor == 0) { 355 directives = new VersionTagsTuple(Version.V1_0, tagHandles); 356 } else { 357 directives = new VersionTagsTuple(Version.V1_1, tagHandles); 358 } 359 } else if (token.getName().equals("TAG")) { 360 List<String> value = (List<String>) token.getValue(); 361 String handle = value.get(0); 362 String prefix = value.get(1); 363 if (tagHandles.containsKey(handle)) { 364 throw new ParserException(null, null, "duplicate tag handle " + handle, 365 token.getStartMark()); 366 } 367 tagHandles.put(handle, prefix); 368 } 369 } 370 HashMap<String, String> detectedTagHandles = new HashMap<String, String>(); 371 if (!tagHandles.isEmpty()) { 372 // copy from tagHandles 373 detectedTagHandles = new HashMap<String, String>(tagHandles); 374 } 375 // add default tag handlers to resolve tags 376 for (String key : DEFAULT_TAGS.keySet()) { 377 // do not overwrite re-defined tags 378 if (!tagHandles.containsKey(key)) { 379 tagHandles.put(key, DEFAULT_TAGS.get(key)); 380 } 381 } 382 // data for the events (no default tags added) 383 return new VersionTagsTuple(directives.getVersion(), detectedTagHandles); 384 } 385 386 /** 387 * <pre> 388 * block_node_or_indentless_sequence ::= ALIAS 389 * | properties (block_content | indentless_block_sequence)? 390 * | block_content 391 * | indentless_block_sequence 392 * block_node ::= ALIAS 393 * | properties block_content? 394 * | block_content 395 * flow_node ::= ALIAS 396 * | properties flow_content? 397 * | flow_content 398 * properties ::= TAG ANCHOR? | ANCHOR TAG? 399 * block_content ::= block_collection | flow_collection | SCALAR 400 * flow_content ::= flow_collection | SCALAR 401 * block_collection ::= block_sequence | block_mapping 402 * flow_collection ::= flow_sequence | flow_mapping 403 * </pre> 404 */ 405 406 private class ParseBlockNode implements Production { 407 produce()408 public Event produce() { 409 return parseNode(true, false); 410 } 411 } 412 parseFlowNode()413 private Event parseFlowNode() { 414 return parseNode(false, false); 415 } 416 parseBlockNodeOrIndentlessSequence()417 private Event parseBlockNodeOrIndentlessSequence() { 418 return parseNode(true, true); 419 } 420 parseNode(boolean block, boolean indentlessSequence)421 private Event parseNode(boolean block, boolean indentlessSequence) { 422 Event event; 423 Mark startMark = null; 424 Mark endMark = null; 425 Mark tagMark = null; 426 if (scanner.checkToken(Token.ID.Alias)) { 427 AliasToken token = (AliasToken) scanner.getToken(); 428 event = new AliasEvent(token.getValue(), token.getStartMark(), token.getEndMark()); 429 state = states.pop(); 430 } else { 431 String anchor = null; 432 TagTuple tagTokenTag = null; 433 if (scanner.checkToken(Token.ID.Anchor)) { 434 AnchorToken token = (AnchorToken) scanner.getToken(); 435 startMark = token.getStartMark(); 436 endMark = token.getEndMark(); 437 anchor = token.getValue(); 438 if (scanner.checkToken(Token.ID.Tag)) { 439 TagToken tagToken = (TagToken) scanner.getToken(); 440 tagMark = tagToken.getStartMark(); 441 endMark = tagToken.getEndMark(); 442 tagTokenTag = tagToken.getValue(); 443 } 444 } else if (scanner.checkToken(Token.ID.Tag)) { 445 TagToken tagToken = (TagToken) scanner.getToken(); 446 startMark = tagToken.getStartMark(); 447 tagMark = startMark; 448 endMark = tagToken.getEndMark(); 449 tagTokenTag = tagToken.getValue(); 450 if (scanner.checkToken(Token.ID.Anchor)) { 451 AnchorToken token = (AnchorToken) scanner.getToken(); 452 endMark = token.getEndMark(); 453 anchor = token.getValue(); 454 } 455 } 456 String tag = null; 457 if (tagTokenTag != null) { 458 String handle = tagTokenTag.getHandle(); 459 String suffix = tagTokenTag.getSuffix(); 460 if (handle != null) { 461 if (!directives.getTags().containsKey(handle)) { 462 throw new ParserException("while parsing a node", startMark, 463 "found undefined tag handle " + handle, tagMark); 464 } 465 tag = directives.getTags().get(handle) + suffix; 466 } else { 467 tag = suffix; 468 } 469 } 470 if (startMark == null) { 471 startMark = scanner.peekToken().getStartMark(); 472 endMark = startMark; 473 } 474 event = null; 475 boolean implicit = tag == null || tag.equals("!"); 476 if (indentlessSequence && scanner.checkToken(Token.ID.BlockEntry)) { 477 endMark = scanner.peekToken().getEndMark(); 478 event = new SequenceStartEvent(anchor, tag, implicit, startMark, endMark, 479 DumperOptions.FlowStyle.BLOCK); 480 state = new ParseIndentlessSequenceEntryKey(); 481 } else { 482 if (scanner.checkToken(Token.ID.Scalar)) { 483 ScalarToken token = (ScalarToken) scanner.getToken(); 484 endMark = token.getEndMark(); 485 ImplicitTuple implicitValues; 486 if ((token.getPlain() && tag == null) || "!".equals(tag)) { 487 implicitValues = new ImplicitTuple(true, false); 488 } else if (tag == null) { 489 implicitValues = new ImplicitTuple(false, true); 490 } else { 491 implicitValues = new ImplicitTuple(false, false); 492 } 493 event = new ScalarEvent(anchor, tag, implicitValues, token.getValue(), startMark, endMark, 494 token.getStyle()); 495 state = states.pop(); 496 } else if (scanner.checkToken(Token.ID.FlowSequenceStart)) { 497 endMark = scanner.peekToken().getEndMark(); 498 event = new SequenceStartEvent(anchor, tag, implicit, startMark, endMark, 499 DumperOptions.FlowStyle.FLOW); 500 state = new ParseFlowSequenceFirstEntry(); 501 } else if (scanner.checkToken(Token.ID.FlowMappingStart)) { 502 endMark = scanner.peekToken().getEndMark(); 503 event = new MappingStartEvent(anchor, tag, implicit, startMark, endMark, 504 DumperOptions.FlowStyle.FLOW); 505 state = new ParseFlowMappingFirstKey(); 506 } else if (block && scanner.checkToken(Token.ID.BlockSequenceStart)) { 507 endMark = scanner.peekToken().getStartMark(); 508 event = new SequenceStartEvent(anchor, tag, implicit, startMark, endMark, 509 DumperOptions.FlowStyle.BLOCK); 510 state = new ParseBlockSequenceFirstEntry(); 511 } else if (block && scanner.checkToken(Token.ID.BlockMappingStart)) { 512 endMark = scanner.peekToken().getStartMark(); 513 event = new MappingStartEvent(anchor, tag, implicit, startMark, endMark, 514 DumperOptions.FlowStyle.BLOCK); 515 state = new ParseBlockMappingFirstKey(); 516 } else if (anchor != null || tag != null) { 517 // Empty scalars are allowed even if a tag or an anchor is 518 // specified. 519 event = new ScalarEvent(anchor, tag, new ImplicitTuple(implicit, false), "", startMark, 520 endMark, DumperOptions.ScalarStyle.PLAIN); 521 state = states.pop(); 522 } else { 523 Token token = scanner.peekToken(); 524 throw new ParserException("while parsing a " + (block ? "block" : "flow") + " node", 525 startMark, "expected the node content, but found '" + token.getTokenId() + "'", 526 token.getStartMark()); 527 } 528 } 529 } 530 return event; 531 } 532 533 // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* 534 // BLOCK-END 535 536 private class ParseBlockSequenceFirstEntry implements Production { 537 produce()538 public Event produce() { 539 Token token = scanner.getToken(); 540 marks.push(token.getStartMark()); 541 return new ParseBlockSequenceEntryKey().produce(); 542 } 543 } 544 545 private class ParseBlockSequenceEntryKey implements Production { 546 produce()547 public Event produce() { 548 if (scanner.checkToken(Token.ID.Comment)) { 549 state = new ParseBlockSequenceEntryKey(); 550 return produceCommentEvent((CommentToken) scanner.getToken()); 551 } 552 if (scanner.checkToken(Token.ID.BlockEntry)) { 553 BlockEntryToken token = (BlockEntryToken) scanner.getToken(); 554 return new ParseBlockSequenceEntryValue(token).produce(); 555 } 556 if (!scanner.checkToken(Token.ID.BlockEnd)) { 557 Token token = scanner.peekToken(); 558 throw new ParserException("while parsing a block collection", marks.pop(), 559 "expected <block end>, but found '" + token.getTokenId() + "'", token.getStartMark()); 560 } 561 Token token = scanner.getToken(); 562 Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark()); 563 state = states.pop(); 564 marks.pop(); 565 return event; 566 } 567 } 568 569 private class ParseBlockSequenceEntryValue implements Production { 570 571 BlockEntryToken token; 572 ParseBlockSequenceEntryValue(final BlockEntryToken token)573 public ParseBlockSequenceEntryValue(final BlockEntryToken token) { 574 this.token = token; 575 } 576 produce()577 public Event produce() { 578 if (scanner.checkToken(Token.ID.Comment)) { 579 state = new ParseBlockSequenceEntryValue(token); 580 return produceCommentEvent((CommentToken) scanner.getToken()); 581 } 582 if (!scanner.checkToken(Token.ID.BlockEntry, Token.ID.BlockEnd)) { 583 states.push(new ParseBlockSequenceEntryKey()); 584 return new ParseBlockNode().produce(); 585 } else { 586 state = new ParseBlockSequenceEntryKey(); 587 return processEmptyScalar(token.getEndMark()); 588 } 589 } 590 } 591 592 // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ 593 594 private class ParseIndentlessSequenceEntryKey implements Production { 595 produce()596 public Event produce() { 597 if (scanner.checkToken(Token.ID.Comment)) { 598 state = new ParseIndentlessSequenceEntryKey(); 599 return produceCommentEvent((CommentToken) scanner.getToken()); 600 } 601 if (scanner.checkToken(Token.ID.BlockEntry)) { 602 BlockEntryToken token = (BlockEntryToken) scanner.getToken(); 603 return new ParseIndentlessSequenceEntryValue(token).produce(); 604 } 605 Token token = scanner.peekToken(); 606 Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark()); 607 state = states.pop(); 608 return event; 609 } 610 } 611 612 private class ParseIndentlessSequenceEntryValue implements Production { 613 614 BlockEntryToken token; 615 ParseIndentlessSequenceEntryValue(final BlockEntryToken token)616 public ParseIndentlessSequenceEntryValue(final BlockEntryToken token) { 617 this.token = token; 618 } 619 produce()620 public Event produce() { 621 if (scanner.checkToken(Token.ID.Comment)) { 622 state = new ParseIndentlessSequenceEntryValue(token); 623 return produceCommentEvent((CommentToken) scanner.getToken()); 624 } 625 if (!scanner.checkToken(Token.ID.BlockEntry, Token.ID.Key, Token.ID.Value, 626 Token.ID.BlockEnd)) { 627 states.push(new ParseIndentlessSequenceEntryKey()); 628 return new ParseBlockNode().produce(); 629 } else { 630 state = new ParseIndentlessSequenceEntryKey(); 631 return processEmptyScalar(token.getEndMark()); 632 } 633 } 634 } 635 636 private class ParseBlockMappingFirstKey implements Production { 637 produce()638 public Event produce() { 639 Token token = scanner.getToken(); 640 marks.push(token.getStartMark()); 641 return new ParseBlockMappingKey().produce(); 642 } 643 } 644 645 private class ParseBlockMappingKey implements Production { 646 produce()647 public Event produce() { 648 if (scanner.checkToken(Token.ID.Comment)) { 649 state = new ParseBlockMappingKey(); 650 return produceCommentEvent((CommentToken) scanner.getToken()); 651 } 652 if (scanner.checkToken(Token.ID.Key)) { 653 Token token = scanner.getToken(); 654 if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) { 655 states.push(new ParseBlockMappingValue()); 656 return parseBlockNodeOrIndentlessSequence(); 657 } else { 658 state = new ParseBlockMappingValue(); 659 return processEmptyScalar(token.getEndMark()); 660 } 661 } 662 if (!scanner.checkToken(Token.ID.BlockEnd)) { 663 Token token = scanner.peekToken(); 664 throw new ParserException("while parsing a block mapping", marks.pop(), 665 "expected <block end>, but found '" + token.getTokenId() + "'", token.getStartMark()); 666 } 667 Token token = scanner.getToken(); 668 Event event = new MappingEndEvent(token.getStartMark(), token.getEndMark()); 669 state = states.pop(); 670 marks.pop(); 671 return event; 672 } 673 } 674 675 private class ParseBlockMappingValue implements Production { 676 produce()677 public Event produce() { 678 if (scanner.checkToken(Token.ID.Value)) { 679 Token token = scanner.getToken(); 680 if (scanner.checkToken(Token.ID.Comment)) { 681 state = new ParseBlockMappingValueComment(); 682 return state.produce(); 683 } else if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) { 684 states.push(new ParseBlockMappingKey()); 685 return parseBlockNodeOrIndentlessSequence(); 686 } else { 687 state = new ParseBlockMappingKey(); 688 return processEmptyScalar(token.getEndMark()); 689 } 690 } else if (scanner.checkToken(Token.ID.Scalar)) { 691 states.push(new ParseBlockMappingKey()); 692 return parseBlockNodeOrIndentlessSequence(); 693 } 694 state = new ParseBlockMappingKey(); 695 Token token = scanner.peekToken(); 696 return processEmptyScalar(token.getStartMark()); 697 } 698 } 699 700 private class ParseBlockMappingValueComment implements Production { 701 702 List<CommentToken> tokens = new LinkedList<>(); 703 produce()704 public Event produce() { 705 if (scanner.checkToken(Token.ID.Comment)) { 706 tokens.add((CommentToken) scanner.getToken()); 707 return produce(); 708 } else if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) { 709 if (!tokens.isEmpty()) { 710 return produceCommentEvent(tokens.remove(0)); 711 } 712 states.push(new ParseBlockMappingKey()); 713 return parseBlockNodeOrIndentlessSequence(); 714 } else { 715 state = new ParseBlockMappingValueCommentList(tokens); 716 return processEmptyScalar(scanner.peekToken().getStartMark()); 717 } 718 } 719 } 720 721 private class ParseBlockMappingValueCommentList implements Production { 722 723 List<CommentToken> tokens; 724 ParseBlockMappingValueCommentList(final List<CommentToken> tokens)725 public ParseBlockMappingValueCommentList(final List<CommentToken> tokens) { 726 this.tokens = tokens; 727 } 728 produce()729 public Event produce() { 730 if (!tokens.isEmpty()) { 731 return produceCommentEvent(tokens.remove(0)); 732 } 733 return new ParseBlockMappingKey().produce(); 734 } 735 } 736 737 /** 738 * <pre> 739 * flow_sequence ::= FLOW-SEQUENCE-START 740 * (flow_sequence_entry FLOW-ENTRY)* 741 * flow_sequence_entry? 742 * FLOW-SEQUENCE-END 743 * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? 744 * Note that while production rules for both flow_sequence_entry and 745 * flow_mapping_entry are equal, their interpretations are different. 746 * For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?` 747 * generate an inline mapping (set syntax). 748 * </pre> 749 */ 750 private class ParseFlowSequenceFirstEntry implements Production { 751 produce()752 public Event produce() { 753 Token token = scanner.getToken(); 754 marks.push(token.getStartMark()); 755 return new ParseFlowSequenceEntry(true).produce(); 756 } 757 } 758 759 private class ParseFlowSequenceEntry implements Production { 760 761 private final boolean first; 762 ParseFlowSequenceEntry(boolean first)763 public ParseFlowSequenceEntry(boolean first) { 764 this.first = first; 765 } 766 produce()767 public Event produce() { 768 if (scanner.checkToken(Token.ID.Comment)) { 769 state = new ParseFlowSequenceEntry(first); 770 return produceCommentEvent((CommentToken) scanner.getToken()); 771 } 772 if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) { 773 if (!first) { 774 if (scanner.checkToken(Token.ID.FlowEntry)) { 775 scanner.getToken(); 776 if (scanner.checkToken(Token.ID.Comment)) { 777 state = new ParseFlowSequenceEntry(true); 778 return produceCommentEvent((CommentToken) scanner.getToken()); 779 } 780 } else { 781 Token token = scanner.peekToken(); 782 throw new ParserException("while parsing a flow sequence", marks.pop(), 783 "expected ',' or ']', but got " + token.getTokenId(), token.getStartMark()); 784 } 785 } 786 if (scanner.checkToken(Token.ID.Key)) { 787 Token token = scanner.peekToken(); 788 Event event = new MappingStartEvent(null, null, true, token.getStartMark(), 789 token.getEndMark(), DumperOptions.FlowStyle.FLOW); 790 state = new ParseFlowSequenceEntryMappingKey(); 791 return event; 792 } else if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) { 793 states.push(new ParseFlowSequenceEntry(false)); 794 return parseFlowNode(); 795 } 796 } 797 Token token = scanner.getToken(); 798 Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark()); 799 if (!scanner.checkToken(Token.ID.Comment)) { 800 state = states.pop(); 801 } else { 802 state = new ParseFlowEndComment(); 803 } 804 marks.pop(); 805 return event; 806 } 807 } 808 809 private class ParseFlowEndComment implements Production { 810 produce()811 public Event produce() { 812 Event event = produceCommentEvent((CommentToken) scanner.getToken()); 813 if (!scanner.checkToken(Token.ID.Comment)) { 814 state = states.pop(); 815 } 816 return event; 817 } 818 } 819 820 private class ParseFlowSequenceEntryMappingKey implements Production { 821 produce()822 public Event produce() { 823 Token token = scanner.getToken(); 824 if (!scanner.checkToken(Token.ID.Value, Token.ID.FlowEntry, Token.ID.FlowSequenceEnd)) { 825 states.push(new ParseFlowSequenceEntryMappingValue()); 826 return parseFlowNode(); 827 } else { 828 state = new ParseFlowSequenceEntryMappingValue(); 829 return processEmptyScalar(token.getEndMark()); 830 } 831 } 832 } 833 834 private class ParseFlowSequenceEntryMappingValue implements Production { 835 produce()836 public Event produce() { 837 if (scanner.checkToken(Token.ID.Value)) { 838 Token token = scanner.getToken(); 839 if (!scanner.checkToken(Token.ID.FlowEntry, Token.ID.FlowSequenceEnd)) { 840 states.push(new ParseFlowSequenceEntryMappingEnd()); 841 return parseFlowNode(); 842 } else { 843 state = new ParseFlowSequenceEntryMappingEnd(); 844 return processEmptyScalar(token.getEndMark()); 845 } 846 } else { 847 state = new ParseFlowSequenceEntryMappingEnd(); 848 Token token = scanner.peekToken(); 849 return processEmptyScalar(token.getStartMark()); 850 } 851 } 852 } 853 854 private class ParseFlowSequenceEntryMappingEnd implements Production { 855 produce()856 public Event produce() { 857 state = new ParseFlowSequenceEntry(false); 858 Token token = scanner.peekToken(); 859 return new MappingEndEvent(token.getStartMark(), token.getEndMark()); 860 } 861 } 862 863 /** 864 * <pre> 865 * flow_mapping ::= FLOW-MAPPING-START 866 * (flow_mapping_entry FLOW-ENTRY)* 867 * flow_mapping_entry? 868 * FLOW-MAPPING-END 869 * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? 870 * </pre> 871 */ 872 private class ParseFlowMappingFirstKey implements Production { 873 produce()874 public Event produce() { 875 Token token = scanner.getToken(); 876 marks.push(token.getStartMark()); 877 return new ParseFlowMappingKey(true).produce(); 878 } 879 } 880 881 private class ParseFlowMappingKey implements Production { 882 883 private final boolean first; 884 ParseFlowMappingKey(boolean first)885 public ParseFlowMappingKey(boolean first) { 886 this.first = first; 887 } 888 produce()889 public Event produce() { 890 if (scanner.checkToken(Token.ID.Comment)) { 891 state = new ParseFlowMappingKey(first); 892 return produceCommentEvent((CommentToken) scanner.getToken()); 893 } 894 if (!scanner.checkToken(Token.ID.FlowMappingEnd)) { 895 if (!first) { 896 if (scanner.checkToken(Token.ID.FlowEntry)) { 897 scanner.getToken(); 898 if (scanner.checkToken(Token.ID.Comment)) { 899 state = new ParseFlowMappingKey(true); 900 return produceCommentEvent((CommentToken) scanner.getToken()); 901 } 902 } else { 903 Token token = scanner.peekToken(); 904 throw new ParserException("while parsing a flow mapping", marks.pop(), 905 "expected ',' or '}', but got " + token.getTokenId(), token.getStartMark()); 906 } 907 } 908 if (scanner.checkToken(Token.ID.Key)) { 909 Token token = scanner.getToken(); 910 if (!scanner.checkToken(Token.ID.Value, Token.ID.FlowEntry, Token.ID.FlowMappingEnd)) { 911 states.push(new ParseFlowMappingValue()); 912 return parseFlowNode(); 913 } else { 914 state = new ParseFlowMappingValue(); 915 return processEmptyScalar(token.getEndMark()); 916 } 917 } else if (!scanner.checkToken(Token.ID.FlowMappingEnd)) { 918 states.push(new ParseFlowMappingEmptyValue()); 919 return parseFlowNode(); 920 } 921 } 922 Token token = scanner.getToken(); 923 Event event = new MappingEndEvent(token.getStartMark(), token.getEndMark()); 924 marks.pop(); 925 if (!scanner.checkToken(Token.ID.Comment)) { 926 state = states.pop(); 927 } else { 928 state = new ParseFlowEndComment(); 929 } 930 return event; 931 } 932 } 933 934 private class ParseFlowMappingValue implements Production { 935 produce()936 public Event produce() { 937 if (scanner.checkToken(Token.ID.Value)) { 938 Token token = scanner.getToken(); 939 if (!scanner.checkToken(Token.ID.FlowEntry, Token.ID.FlowMappingEnd)) { 940 states.push(new ParseFlowMappingKey(false)); 941 return parseFlowNode(); 942 } else { 943 state = new ParseFlowMappingKey(false); 944 return processEmptyScalar(token.getEndMark()); 945 } 946 } else { 947 state = new ParseFlowMappingKey(false); 948 Token token = scanner.peekToken(); 949 return processEmptyScalar(token.getStartMark()); 950 } 951 } 952 } 953 954 private class ParseFlowMappingEmptyValue implements Production { 955 produce()956 public Event produce() { 957 state = new ParseFlowMappingKey(false); 958 return processEmptyScalar(scanner.peekToken().getStartMark()); 959 } 960 } 961 962 /** 963 * <pre> 964 * block_mapping ::= BLOCK-MAPPING_START 965 * ((KEY block_node_or_indentless_sequence?)? 966 * (VALUE block_node_or_indentless_sequence?)?)* 967 * BLOCK-END 968 * </pre> 969 */ processEmptyScalar(Mark mark)970 private Event processEmptyScalar(Mark mark) { 971 return new ScalarEvent(null, null, new ImplicitTuple(true, false), "", mark, mark, 972 DumperOptions.ScalarStyle.PLAIN); 973 } 974 } 975