#include "singledocparser.h" #include "collectionstack.h" #include "directives.h" #include "yaml-cpp/eventhandler.h" #include "yaml-cpp/exceptions.h" #include "scanner.h" #include "tag.h" #include "token.h" #include #include #include namespace YAML { SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives): m_scanner(scanner), m_directives(directives), m_pCollectionStack(new CollectionStack), m_curAnchor(0) { } SingleDocParser::~SingleDocParser() { } // 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) { // an empty node *is* a possibility if(m_scanner.empty()) { eventHandler.OnNull(Mark::null(), 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); 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; anchor_t anchor; ParseProperties(tag, anchor); const Token& token = m_scanner.peek(); // add non-specific tags if(tag.empty()) tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?"); // 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: case Token::BLOCK_SEQ_START: eventHandler.OnSequenceStart(mark, tag, anchor); HandleSequence(eventHandler); eventHandler.OnSequenceEnd(); return; case Token::FLOW_MAP_START: case Token::BLOCK_MAP_START: eventHandler.OnMapStart(mark, tag, anchor); 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); 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(1) { if(m_scanner.empty()) throw ParserException(Mark::null(), 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& token = m_scanner.peek(); if(token.type == Token::BLOCK_ENTRY || token.type == Token::BLOCK_SEQ_END) { eventHandler.OnNull(token.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(1) { if(m_scanner.empty()) throw ParserException(Mark::null(), 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); // 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(1) { if(m_scanner.empty()) throw ParserException(Mark::null(), 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(1) { if(m_scanner.empty()) throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP_FLOW); Token& token = m_scanner.peek(); // 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(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); } // 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) { tag.clear(); anchor = NullAnchor; while(1) { if(m_scanner.empty()) return; switch(m_scanner.peek().type) { case Token::TAG: ParseTag(tag); break; case Token::ANCHOR: ParseAnchor(anchor); 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) { Token& token = m_scanner.peek(); if(anchor) throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS); 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 { Anchors::const_iterator it = m_anchors.find(name); if(it == m_anchors.end()) throw ParserException(mark, ErrorMsg::UNKNOWN_ANCHOR); return it->second; } }