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