436 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			436 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								#include <algorithm>
							 | 
						||
| 
								 | 
							
								#include <cstdio>
							 | 
						||
| 
								 | 
							
								#include <sstream>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "collectionstack.h"  // IWYU pragma: keep
							 | 
						||
| 
								 | 
							
								#include "scanner.h"
							 | 
						||
| 
								 | 
							
								#include "singledocparser.h"
							 | 
						||
| 
								 | 
							
								#include "tag.h"
							 | 
						||
| 
								 | 
							
								#include "token.h"
							 | 
						||
| 
								 | 
							
								#include "yaml-cpp/depthguard.h"
							 | 
						||
| 
								 | 
							
								#include "yaml-cpp/emitterstyle.h"
							 | 
						||
| 
								 | 
							
								#include "yaml-cpp/eventhandler.h"
							 | 
						||
| 
								 | 
							
								#include "yaml-cpp/exceptions.h"  // IWYU pragma: keep
							 | 
						||
| 
								 | 
							
								#include "yaml-cpp/mark.h"
							 | 
						||
| 
								 | 
							
								#include "yaml-cpp/null.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace YAML {
							 | 
						||
| 
								 | 
							
								SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives)
							 | 
						||
| 
								 | 
							
								    : m_scanner(scanner),
							 | 
						||
| 
								 | 
							
								      m_directives(directives),
							 | 
						||
| 
								 | 
							
								      m_pCollectionStack(new CollectionStack),
							 | 
						||
| 
								 | 
							
								      m_anchors{},
							 | 
						||
| 
								 | 
							
								      m_curAnchor(0) {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SingleDocParser::~SingleDocParser() = default;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// HandleDocument
							 | 
						||
| 
								 | 
							
								// . Handles the next document
							 | 
						||
| 
								 | 
							
								// . Throws a ParserException on error.
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleDocument(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  assert(!m_scanner.empty());  // guaranteed that there are tokens
							 | 
						||
| 
								 | 
							
								  assert(!m_curAnchor);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  eventHandler.OnDocumentStart(m_scanner.peek().mark);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // eat doc start
							 | 
						||
| 
								 | 
							
								  if (m_scanner.peek().type == Token::DOC_START)
							 | 
						||
| 
								 | 
							
								    m_scanner.pop();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // recurse!
							 | 
						||
| 
								 | 
							
								  HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  eventHandler.OnDocumentEnd();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // and finally eat any doc ends we see
							 | 
						||
| 
								 | 
							
								  while (!m_scanner.empty() && m_scanner.peek().type == Token::DOC_END)
							 | 
						||
| 
								 | 
							
								    m_scanner.pop();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleNode(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  DepthGuard<500> depthguard(depth, m_scanner.mark(), ErrorMsg::BAD_FILE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // an empty node *is* a possibility
							 | 
						||
| 
								 | 
							
								  if (m_scanner.empty()) {
							 | 
						||
| 
								 | 
							
								    eventHandler.OnNull(m_scanner.mark(), NullAnchor);
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // save location
							 | 
						||
| 
								 | 
							
								  Mark mark = m_scanner.peek().mark;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // special case: a value node by itself must be a map, with no header
							 | 
						||
| 
								 | 
							
								  if (m_scanner.peek().type == Token::VALUE) {
							 | 
						||
| 
								 | 
							
								    eventHandler.OnMapStart(mark, "?", NullAnchor, EmitterStyle::Default);
							 | 
						||
| 
								 | 
							
								    HandleMap(eventHandler);
							 | 
						||
| 
								 | 
							
								    eventHandler.OnMapEnd();
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // special case: an alias node
							 | 
						||
| 
								 | 
							
								  if (m_scanner.peek().type == Token::ALIAS) {
							 | 
						||
| 
								 | 
							
								    eventHandler.OnAlias(mark, LookupAnchor(mark, m_scanner.peek().value));
							 | 
						||
| 
								 | 
							
								    m_scanner.pop();
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  std::string tag;
							 | 
						||
| 
								 | 
							
								  std::string anchor_name;
							 | 
						||
| 
								 | 
							
								  anchor_t anchor;
							 | 
						||
| 
								 | 
							
								  ParseProperties(tag, anchor, anchor_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!anchor_name.empty())
							 | 
						||
| 
								 | 
							
								    eventHandler.OnAnchor(mark, anchor_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // after parsing properties, an empty node is again a possibility
							 | 
						||
| 
								 | 
							
								  if (m_scanner.empty()) {
							 | 
						||
| 
								 | 
							
								    eventHandler.OnNull(mark, anchor);
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const Token& token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // add non-specific tags
							 | 
						||
| 
								 | 
							
								  if (tag.empty())
							 | 
						||
| 
								 | 
							
								    tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?");
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  if (token.type == Token::PLAIN_SCALAR 
							 | 
						||
| 
								 | 
							
								      && tag.compare("?") == 0 && IsNullString(token.value)) {
							 | 
						||
| 
								 | 
							
								    eventHandler.OnNull(mark, anchor);
							 | 
						||
| 
								 | 
							
								    m_scanner.pop();
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // now split based on what kind of node we should be
							 | 
						||
| 
								 | 
							
								  switch (token.type) {
							 | 
						||
| 
								 | 
							
								    case Token::PLAIN_SCALAR:
							 | 
						||
| 
								 | 
							
								    case Token::NON_PLAIN_SCALAR:
							 | 
						||
| 
								 | 
							
								      eventHandler.OnScalar(mark, tag, anchor, token.value);
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    case Token::FLOW_SEQ_START:
							 | 
						||
| 
								 | 
							
								      eventHandler.OnSequenceStart(mark, tag, anchor, EmitterStyle::Flow);
							 | 
						||
| 
								 | 
							
								      HandleSequence(eventHandler);
							 | 
						||
| 
								 | 
							
								      eventHandler.OnSequenceEnd();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    case Token::BLOCK_SEQ_START:
							 | 
						||
| 
								 | 
							
								      eventHandler.OnSequenceStart(mark, tag, anchor, EmitterStyle::Block);
							 | 
						||
| 
								 | 
							
								      HandleSequence(eventHandler);
							 | 
						||
| 
								 | 
							
								      eventHandler.OnSequenceEnd();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    case Token::FLOW_MAP_START:
							 | 
						||
| 
								 | 
							
								      eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Flow);
							 | 
						||
| 
								 | 
							
								      HandleMap(eventHandler);
							 | 
						||
| 
								 | 
							
								      eventHandler.OnMapEnd();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    case Token::BLOCK_MAP_START:
							 | 
						||
| 
								 | 
							
								      eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Block);
							 | 
						||
| 
								 | 
							
								      HandleMap(eventHandler);
							 | 
						||
| 
								 | 
							
								      eventHandler.OnMapEnd();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    case Token::KEY:
							 | 
						||
| 
								 | 
							
								      // compact maps can only go in a flow sequence
							 | 
						||
| 
								 | 
							
								      if (m_pCollectionStack->GetCurCollectionType() ==
							 | 
						||
| 
								 | 
							
								          CollectionType::FlowSeq) {
							 | 
						||
| 
								 | 
							
								        eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Flow);
							 | 
						||
| 
								 | 
							
								        HandleMap(eventHandler);
							 | 
						||
| 
								 | 
							
								        eventHandler.OnMapEnd();
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (tag == "?")
							 | 
						||
| 
								 | 
							
								    eventHandler.OnNull(mark, anchor);
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    eventHandler.OnScalar(mark, tag, anchor, "");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleSequence(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  // split based on start token
							 | 
						||
| 
								 | 
							
								  switch (m_scanner.peek().type) {
							 | 
						||
| 
								 | 
							
								    case Token::BLOCK_SEQ_START:
							 | 
						||
| 
								 | 
							
								      HandleBlockSequence(eventHandler);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case Token::FLOW_SEQ_START:
							 | 
						||
| 
								 | 
							
								      HandleFlowSequence(eventHandler);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  // eat start token
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PushCollectionType(CollectionType::BlockSeq);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Token token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								    if (token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END)
							 | 
						||
| 
								 | 
							
								      throw ParserException(token.mark, ErrorMsg::END_OF_SEQ);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    m_scanner.pop();
							 | 
						||
| 
								 | 
							
								    if (token.type == Token::BLOCK_SEQ_END)
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // check for null
							 | 
						||
| 
								 | 
							
								    if (!m_scanner.empty()) {
							 | 
						||
| 
								 | 
							
								      const Token& nextToken = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								      if (nextToken.type == Token::BLOCK_ENTRY ||
							 | 
						||
| 
								 | 
							
								          nextToken.type == Token::BLOCK_SEQ_END) {
							 | 
						||
| 
								 | 
							
								        eventHandler.OnNull(nextToken.mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PopCollectionType(CollectionType::BlockSeq);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  // eat start token
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PushCollectionType(CollectionType::FlowSeq);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // first check for end
							 | 
						||
| 
								 | 
							
								    if (m_scanner.peek().type == Token::FLOW_SEQ_END) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // then read the node
							 | 
						||
| 
								 | 
							
								    HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // now eat the separator (or could be a sequence end, which we ignore - but
							 | 
						||
| 
								 | 
							
								    // if it's neither, then it's a bad node)
							 | 
						||
| 
								 | 
							
								    Token& token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								    if (token.type == Token::FLOW_ENTRY)
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								    else if (token.type != Token::FLOW_SEQ_END)
							 | 
						||
| 
								 | 
							
								      throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PopCollectionType(CollectionType::FlowSeq);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleMap(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  // split based on start token
							 | 
						||
| 
								 | 
							
								  switch (m_scanner.peek().type) {
							 | 
						||
| 
								 | 
							
								    case Token::BLOCK_MAP_START:
							 | 
						||
| 
								 | 
							
								      HandleBlockMap(eventHandler);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case Token::FLOW_MAP_START:
							 | 
						||
| 
								 | 
							
								      HandleFlowMap(eventHandler);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case Token::KEY:
							 | 
						||
| 
								 | 
							
								      HandleCompactMap(eventHandler);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case Token::VALUE:
							 | 
						||
| 
								 | 
							
								      HandleCompactMapWithNoKey(eventHandler);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleBlockMap(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  // eat start token
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PushCollectionType(CollectionType::BlockMap);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Token token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								    if (token.type != Token::KEY && token.type != Token::VALUE &&
							 | 
						||
| 
								 | 
							
								        token.type != Token::BLOCK_MAP_END)
							 | 
						||
| 
								 | 
							
								      throw ParserException(token.mark, ErrorMsg::END_OF_MAP);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (token.type == Token::BLOCK_MAP_END) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // grab key (if non-null)
							 | 
						||
| 
								 | 
							
								    if (token.type == Token::KEY) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      eventHandler.OnNull(token.mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // now grab value (optional)
							 | 
						||
| 
								 | 
							
								    if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      eventHandler.OnNull(token.mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PopCollectionType(CollectionType::BlockMap);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  // eat start token
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PushCollectionType(CollectionType::FlowMap);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Token& token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								    const Mark mark = token.mark;
							 | 
						||
| 
								 | 
							
								    // first check for end
							 | 
						||
| 
								 | 
							
								    if (token.type == Token::FLOW_MAP_END) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // grab key (if non-null)
							 | 
						||
| 
								 | 
							
								    if (token.type == Token::KEY) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      eventHandler.OnNull(mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // now grab value (optional)
							 | 
						||
| 
								 | 
							
								    if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								      HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      eventHandler.OnNull(mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // now eat the separator (or could be a map end, which we ignore - but if
							 | 
						||
| 
								 | 
							
								    // it's neither, then it's a bad node)
							 | 
						||
| 
								 | 
							
								    Token& nextToken = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								    if (nextToken.type == Token::FLOW_ENTRY)
							 | 
						||
| 
								 | 
							
								      m_scanner.pop();
							 | 
						||
| 
								 | 
							
								    else if (nextToken.type != Token::FLOW_MAP_END)
							 | 
						||
| 
								 | 
							
								      throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PopCollectionType(CollectionType::FlowMap);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// . Single "key: value" pair in a flow sequence
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleCompactMap(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PushCollectionType(CollectionType::CompactMap);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // grab key
							 | 
						||
| 
								 | 
							
								  Mark mark = m_scanner.peek().mark;
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								  HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // now grab value (optional)
							 | 
						||
| 
								 | 
							
								  if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
							 | 
						||
| 
								 | 
							
								    m_scanner.pop();
							 | 
						||
| 
								 | 
							
								    HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    eventHandler.OnNull(mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PopCollectionType(CollectionType::CompactMap);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// . Single ": value" pair in a flow sequence
							 | 
						||
| 
								 | 
							
								void SingleDocParser::HandleCompactMapWithNoKey(EventHandler& eventHandler) {
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PushCollectionType(CollectionType::CompactMap);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // null key
							 | 
						||
| 
								 | 
							
								  eventHandler.OnNull(m_scanner.peek().mark, NullAnchor);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // grab value
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								  HandleNode(eventHandler);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  m_pCollectionStack->PopCollectionType(CollectionType::CompactMap);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ParseProperties
							 | 
						||
| 
								 | 
							
								// . Grabs any tag or anchor tokens and deals with them.
							 | 
						||
| 
								 | 
							
								void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor,
							 | 
						||
| 
								 | 
							
								                                      std::string& anchor_name) {
							 | 
						||
| 
								 | 
							
								  tag.clear();
							 | 
						||
| 
								 | 
							
								  anchor_name.clear();
							 | 
						||
| 
								 | 
							
								  anchor = NullAnchor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    if (m_scanner.empty())
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    switch (m_scanner.peek().type) {
							 | 
						||
| 
								 | 
							
								      case Token::TAG:
							 | 
						||
| 
								 | 
							
								        ParseTag(tag);
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								      case Token::ANCHOR:
							 | 
						||
| 
								 | 
							
								        ParseAnchor(anchor, anchor_name);
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								      default:
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::ParseTag(std::string& tag) {
							 | 
						||
| 
								 | 
							
								  Token& token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								  if (!tag.empty())
							 | 
						||
| 
								 | 
							
								    throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Tag tagInfo(token);
							 | 
						||
| 
								 | 
							
								  tag = tagInfo.Translate(m_directives);
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SingleDocParser::ParseAnchor(anchor_t& anchor, std::string& anchor_name) {
							 | 
						||
| 
								 | 
							
								  Token& token = m_scanner.peek();
							 | 
						||
| 
								 | 
							
								  if (anchor)
							 | 
						||
| 
								 | 
							
								    throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  anchor_name = token.value;
							 | 
						||
| 
								 | 
							
								  anchor = RegisterAnchor(token.value);
							 | 
						||
| 
								 | 
							
								  m_scanner.pop();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								anchor_t SingleDocParser::RegisterAnchor(const std::string& name) {
							 | 
						||
| 
								 | 
							
								  if (name.empty())
							 | 
						||
| 
								 | 
							
								    return NullAnchor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return m_anchors[name] = ++m_curAnchor;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								anchor_t SingleDocParser::LookupAnchor(const Mark& mark,
							 | 
						||
| 
								 | 
							
								                                       const std::string& name) const {
							 | 
						||
| 
								 | 
							
								  auto it = m_anchors.find(name);
							 | 
						||
| 
								 | 
							
								  if (it == m_anchors.end()) {
							 | 
						||
| 
								 | 
							
								    std::stringstream ss;
							 | 
						||
| 
								 | 
							
								    ss << ErrorMsg::UNKNOWN_ANCHOR << name;
							 | 
						||
| 
								 | 
							
								    throw ParserException(mark, ss.str());
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return it->second;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								}  // namespace YAML
							 |