From e04be7890a1d4ed0cbf2e4516ce76090b814b3d7 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 30 Oct 2009 01:06:19 +0000 Subject: [PATCH] Fixed bug with block maps with null value (the next key was being read as the value) --- include/node.h | 10 +++++----- src/aliascontent.cpp | 2 +- src/aliascontent.h | 2 +- src/content.h | 2 +- src/map.cpp | 20 +++++++++++++++----- src/map.h | 10 +++++----- src/node.cpp | 19 ++++++++++--------- src/parserstate.h | 10 ++++++++++ src/scalar.cpp | 2 +- src/scalar.h | 2 +- src/sequence.cpp | 12 +++++++++--- src/sequence.h | 6 +++--- test/parsertests.cpp | 23 +++++++++++++++++++++++ 13 files changed, 85 insertions(+), 35 deletions(-) diff --git a/include/node.h b/include/node.h index 4218444..097ccd8 100644 --- a/include/node.h +++ b/include/node.h @@ -32,7 +32,7 @@ namespace YAML void Clear(); std::auto_ptr Clone() const; - void Parse(Scanner *pScanner, const ParserState& state); + void Parse(Scanner *pScanner, ParserState& state); CONTENT_TYPE GetType() const; @@ -102,10 +102,10 @@ namespace YAML Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent); // helpers for parsing - void ParseHeader(Scanner *pScanner, const ParserState& state); - void ParseTag(Scanner *pScanner, const ParserState& state); - void ParseAnchor(Scanner *pScanner, const ParserState& state); - void ParseAlias(Scanner *pScanner, const ParserState& state); + void ParseHeader(Scanner *pScanner, ParserState& state); + void ParseTag(Scanner *pScanner, ParserState& state); + void ParseAnchor(Scanner *pScanner, ParserState& state); + void ParseAlias(Scanner *pScanner, ParserState& state); private: Mark m_mark; diff --git a/src/aliascontent.cpp b/src/aliascontent.cpp index 305abdc..111938b 100644 --- a/src/aliascontent.cpp +++ b/src/aliascontent.cpp @@ -12,7 +12,7 @@ namespace YAML return 0; // TODO: how to clone an alias? } - void AliasContent::Parse(Scanner * /*pScanner*/, const ParserState& /*state*/) + void AliasContent::Parse(Scanner * /*pScanner*/, ParserState& /*state*/) { } diff --git a/src/aliascontent.h b/src/aliascontent.h index d4cbb56..a69c28b 100644 --- a/src/aliascontent.h +++ b/src/aliascontent.h @@ -15,7 +15,7 @@ namespace YAML virtual Content *Clone() const; - virtual void Parse(Scanner* pScanner, const ParserState& state); + virtual void Parse(Scanner* pScanner, ParserState& state); virtual void Write(Emitter&) const; virtual bool GetBegin(std::vector ::const_iterator&) const; diff --git a/src/content.h b/src/content.h index 90e265c..d6e0e8c 100644 --- a/src/content.h +++ b/src/content.h @@ -28,7 +28,7 @@ namespace YAML virtual Content *Clone() const = 0; - virtual void Parse(Scanner *pScanner, const ParserState& state) = 0; + virtual void Parse(Scanner *pScanner, ParserState& state) = 0; virtual void Write(Emitter& out) const = 0; virtual bool GetBegin(std::vector ::const_iterator&) const { return false; } diff --git a/src/map.cpp b/src/map.cpp index 5d33385..3a6a299 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -57,7 +57,7 @@ namespace YAML return m_data.size(); } - void Map::Parse(Scanner *pScanner, const ParserState& state) + void Map::Parse(Scanner *pScanner, ParserState& state) { Clear(); @@ -71,10 +71,11 @@ namespace YAML } } - void Map::ParseBlock(Scanner *pScanner, const ParserState& state) + void Map::ParseBlock(Scanner *pScanner, ParserState& state) { // eat start token pScanner->pop(); + state.PushCollectionType(ParserState::BLOCK_MAP); while(1) { if(pScanner->empty()) @@ -106,12 +107,15 @@ namespace YAML // assign the map with the actual pointers m_data[pKey.release()] = pValue.release(); } + + state.PopCollectionType(ParserState::BLOCK_MAP); } - void Map::ParseFlow(Scanner *pScanner, const ParserState& state) + void Map::ParseFlow(Scanner *pScanner, ParserState& state) { // eat start token pScanner->pop(); + state.PushCollectionType(ParserState::FLOW_MAP); while(1) { if(pScanner->empty()) @@ -148,12 +152,15 @@ namespace YAML // assign the map with the actual pointers m_data[pKey.release()] = pValue.release(); } + + state.PopCollectionType(ParserState::FLOW_MAP); } // ParseCompact // . Single "key: value" pair in a flow sequence - void Map::ParseCompact(Scanner *pScanner, const ParserState& state) + void Map::ParseCompact(Scanner *pScanner, ParserState& state) { + state.PushCollectionType(ParserState::COMPACT_MAP); std::auto_ptr pKey(new Node), pValue(new Node); // grab key @@ -168,12 +175,14 @@ namespace YAML // assign the map with the actual pointers m_data[pKey.release()] = pValue.release(); + state.PopCollectionType(ParserState::COMPACT_MAP); } // ParseCompactWithNoKey // . Single ": value" pair in a flow sequence - void Map::ParseCompactWithNoKey(Scanner *pScanner, const ParserState& state) + void Map::ParseCompactWithNoKey(Scanner *pScanner, ParserState& state) { + state.PushCollectionType(ParserState::COMPACT_MAP); std::auto_ptr pKey(new Node), pValue(new Node); // grab value @@ -182,6 +191,7 @@ namespace YAML // assign the map with the actual pointers m_data[pKey.release()] = pValue.release(); + state.PopCollectionType(ParserState::COMPACT_MAP); } void Map::Write(Emitter& out) const diff --git a/src/map.h b/src/map.h index d411010..bb8f949 100644 --- a/src/map.h +++ b/src/map.h @@ -27,7 +27,7 @@ namespace YAML virtual bool GetBegin(std::map ::const_iterator& it) const; virtual bool GetEnd(std::map ::const_iterator& it) const; virtual std::size_t GetSize() const; - virtual void Parse(Scanner *pScanner, const ParserState& state); + virtual void Parse(Scanner *pScanner, ParserState& state); virtual void Write(Emitter& out) const; virtual bool IsMap() const { return true; } @@ -39,10 +39,10 @@ namespace YAML virtual int Compare(Map *pMap); private: - void ParseBlock(Scanner *pScanner, const ParserState& state); - void ParseFlow(Scanner *pScanner, const ParserState& state); - void ParseCompact(Scanner *pScanner, const ParserState& state); - void ParseCompactWithNoKey(Scanner *pScanner, const ParserState& state); + void ParseBlock(Scanner *pScanner, ParserState& state); + void ParseFlow(Scanner *pScanner, ParserState& state); + void ParseCompact(Scanner *pScanner, ParserState& state); + void ParseCompactWithNoKey(Scanner *pScanner, ParserState& state); private: node_map m_data; diff --git a/src/node.cpp b/src/node.cpp index 8f66f3b..198bb71 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -54,7 +54,7 @@ namespace YAML return std::auto_ptr (new Node(m_mark, m_anchor, m_tag, m_pContent)); } - void Node::Parse(Scanner *pScanner, const ParserState& state) + void Node::Parse(Scanner *pScanner, ParserState& state) { Clear(); @@ -104,13 +104,14 @@ namespace YAML break; case Token::FLOW_MAP_START: case Token::BLOCK_MAP_START: - case Token::KEY: m_pContent = new Map; break; + case Token::KEY: + // compact maps can only go in a flow sequence + if(state.GetCurCollectionType() == ParserState::FLOW_SEQ) + m_pContent = new Map; + break; default: -// std::stringstream str; -// str << TokenNames[pScanner->peek().type]; -// throw std::runtime_error(str.str()); break; } @@ -125,7 +126,7 @@ namespace YAML // ParseHeader // . Grabs any tag, alias, or anchor tokens and deals with them. - void Node::ParseHeader(Scanner *pScanner, const ParserState& state) + void Node::ParseHeader(Scanner *pScanner, ParserState& state) { while(1) { if(pScanner->empty()) @@ -140,7 +141,7 @@ namespace YAML } } - void Node::ParseTag(Scanner *pScanner, const ParserState& state) + void Node::ParseTag(Scanner *pScanner, ParserState& state) { Token& token = pScanner->peek(); if(m_tag != "") @@ -151,7 +152,7 @@ namespace YAML pScanner->pop(); } - void Node::ParseAnchor(Scanner *pScanner, const ParserState& /*state*/) + void Node::ParseAnchor(Scanner *pScanner, ParserState& /*state*/) { Token& token = pScanner->peek(); if(m_anchor != "") @@ -162,7 +163,7 @@ namespace YAML pScanner->pop(); } - void Node::ParseAlias(Scanner *pScanner, const ParserState& /*state*/) + void Node::ParseAlias(Scanner *pScanner, ParserState& /*state*/) { Token& token = pScanner->peek(); if(m_anchor != "") diff --git a/src/parserstate.h b/src/parserstate.h index 09b4afd..0b849c3 100644 --- a/src/parserstate.h +++ b/src/parserstate.h @@ -6,6 +6,8 @@ #include #include +#include +#include namespace YAML { @@ -16,11 +18,19 @@ namespace YAML struct ParserState { + enum COLLECTION_TYPE { NONE, BLOCK_MAP, BLOCK_SEQ, FLOW_MAP, FLOW_SEQ, COMPACT_MAP }; + ParserState(); + const std::string TranslateTagHandle(const std::string& handle) const; + COLLECTION_TYPE GetCurCollectionType() const { if(collectionStack.empty()) return NONE; return collectionStack.top(); } + + void PushCollectionType(COLLECTION_TYPE type) { collectionStack.push(type); } + void PopCollectionType(COLLECTION_TYPE type) { assert(type == GetCurCollectionType()); collectionStack.pop(); } Version version; std::map tags; + std::stack collectionStack; }; } diff --git a/src/scalar.cpp b/src/scalar.cpp index dbf45b3..415bace 100644 --- a/src/scalar.cpp +++ b/src/scalar.cpp @@ -24,7 +24,7 @@ namespace YAML return new Scalar(m_data); } - void Scalar::Parse(Scanner *pScanner, const ParserState& /*state*/) + void Scalar::Parse(Scanner *pScanner, ParserState& /*state*/) { Token& token = pScanner->peek(); m_data = token.value; diff --git a/src/scalar.h b/src/scalar.h index fe9a84b..1baecaa 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -18,7 +18,7 @@ namespace YAML virtual Content *Clone() const; - virtual void Parse(Scanner *pScanner, const ParserState& state); + virtual void Parse(Scanner *pScanner, ParserState& state); virtual void Write(Emitter& out) const; virtual bool IsScalar() const { return true; } diff --git a/src/sequence.cpp b/src/sequence.cpp index 467826f..0f96e27 100644 --- a/src/sequence.cpp +++ b/src/sequence.cpp @@ -59,7 +59,7 @@ namespace YAML return m_data.size(); } - void Sequence::Parse(Scanner *pScanner, const ParserState& state) + void Sequence::Parse(Scanner *pScanner, ParserState& state) { Clear(); @@ -71,10 +71,11 @@ namespace YAML } } - void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state) + void Sequence::ParseBlock(Scanner *pScanner, ParserState& state) { // eat start token pScanner->pop(); + state.PushCollectionType(ParserState::BLOCK_SEQ); while(1) { if(pScanner->empty()) @@ -100,12 +101,15 @@ namespace YAML pNode->Parse(pScanner, state); } + + state.PopCollectionType(ParserState::BLOCK_SEQ); } - void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state) + void Sequence::ParseFlow(Scanner *pScanner, ParserState& state) { // eat start token pScanner->pop(); + state.PushCollectionType(ParserState::FLOW_SEQ); while(1) { if(pScanner->empty()) @@ -129,6 +133,8 @@ namespace YAML else if(token.type != Token::FLOW_SEQ_END) throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW); } + + state.PopCollectionType(ParserState::FLOW_SEQ); } void Sequence::Write(Emitter& out) const diff --git a/src/sequence.h b/src/sequence.h index ad4fde5..49bdc83 100644 --- a/src/sequence.h +++ b/src/sequence.h @@ -26,7 +26,7 @@ namespace YAML virtual Node *GetNode(std::size_t i) const; virtual std::size_t GetSize() const; - virtual void Parse(Scanner *pScanner, const ParserState& state); + virtual void Parse(Scanner *pScanner, ParserState& state); virtual void Write(Emitter& out) const; virtual bool IsSequence() const { return true; } @@ -38,8 +38,8 @@ namespace YAML virtual int Compare(Map *) { return -1; } private: - void ParseBlock(Scanner *pScanner, const ParserState& state); - void ParseFlow(Scanner *pScanner, const ParserState& state); + void ParseBlock(Scanner *pScanner, ParserState& state); + void ParseFlow(Scanner *pScanner, ParserState& state); protected: std::vector m_data; diff --git a/test/parsertests.cpp b/test/parsertests.cpp index 138143c..4c159bc 100644 --- a/test/parsertests.cpp +++ b/test/parsertests.cpp @@ -610,6 +610,28 @@ namespace Test return true; } + + bool BlockKeyWithNullValue() + { + std::string input = + "key:\n" + "just a key: value"; + + std::stringstream stream(input); + YAML::Parser parser(stream); + YAML::Node doc; + std::string output; + + parser.GetNextDocument(doc); + if(doc.size() != 2) + return false; + if(!IsNull(doc["key"])) + return false; + if(doc["just a key"] != "value") + return false; + + return true; + } } namespace { @@ -869,6 +891,7 @@ namespace Test RunParserTest(&Parser::MultipleDocs, "multiple docs", passed, total); RunParserTest(&Parser::ExplicitEndDoc, "explicit end doc", passed, total); RunParserTest(&Parser::MultipleDocsWithSomeExplicitIndicators, "multiple docs with some explicit indicators", passed, total); + RunParserTest(&Parser::BlockKeyWithNullValue, "block key with null value", passed, total); RunEncodingTest(&EncodeToUtf8, false, "UTF-8, no BOM", passed, total); RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed, total);