mirror of
https://github.com/jbeder/yaml-cpp.git
synced 2025-09-09 20:51:16 +00:00
Fixed bug with block maps with null value (the next key was being read as the value)
This commit is contained in:
@@ -32,7 +32,7 @@ namespace YAML
|
|||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
std::auto_ptr<Node> Clone() const;
|
std::auto_ptr<Node> Clone() const;
|
||||||
void Parse(Scanner *pScanner, const ParserState& state);
|
void Parse(Scanner *pScanner, ParserState& state);
|
||||||
|
|
||||||
CONTENT_TYPE GetType() const;
|
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);
|
Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent);
|
||||||
|
|
||||||
// helpers for parsing
|
// helpers for parsing
|
||||||
void ParseHeader(Scanner *pScanner, const ParserState& state);
|
void ParseHeader(Scanner *pScanner, ParserState& state);
|
||||||
void ParseTag(Scanner *pScanner, const ParserState& state);
|
void ParseTag(Scanner *pScanner, ParserState& state);
|
||||||
void ParseAnchor(Scanner *pScanner, const ParserState& state);
|
void ParseAnchor(Scanner *pScanner, ParserState& state);
|
||||||
void ParseAlias(Scanner *pScanner, const ParserState& state);
|
void ParseAlias(Scanner *pScanner, ParserState& state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mark m_mark;
|
Mark m_mark;
|
||||||
|
@@ -12,7 +12,7 @@ namespace YAML
|
|||||||
return 0; // TODO: how to clone an alias?
|
return 0; // TODO: how to clone an alias?
|
||||||
}
|
}
|
||||||
|
|
||||||
void AliasContent::Parse(Scanner * /*pScanner*/, const ParserState& /*state*/)
|
void AliasContent::Parse(Scanner * /*pScanner*/, ParserState& /*state*/)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ namespace YAML
|
|||||||
|
|
||||||
virtual Content *Clone() const;
|
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 void Write(Emitter&) const;
|
||||||
|
|
||||||
virtual bool GetBegin(std::vector <Node *>::const_iterator&) const;
|
virtual bool GetBegin(std::vector <Node *>::const_iterator&) const;
|
||||||
|
@@ -28,7 +28,7 @@ namespace YAML
|
|||||||
|
|
||||||
virtual Content *Clone() const = 0;
|
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 void Write(Emitter& out) const = 0;
|
||||||
|
|
||||||
virtual bool GetBegin(std::vector <Node *>::const_iterator&) const { return false; }
|
virtual bool GetBegin(std::vector <Node *>::const_iterator&) const { return false; }
|
||||||
|
20
src/map.cpp
20
src/map.cpp
@@ -57,7 +57,7 @@ namespace YAML
|
|||||||
return m_data.size();
|
return m_data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::Parse(Scanner *pScanner, const ParserState& state)
|
void Map::Parse(Scanner *pScanner, ParserState& state)
|
||||||
{
|
{
|
||||||
Clear();
|
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
|
// eat start token
|
||||||
pScanner->pop();
|
pScanner->pop();
|
||||||
|
state.PushCollectionType(ParserState::BLOCK_MAP);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
if(pScanner->empty())
|
if(pScanner->empty())
|
||||||
@@ -106,12 +107,15 @@ namespace YAML
|
|||||||
// assign the map with the actual pointers
|
// assign the map with the actual pointers
|
||||||
m_data[pKey.release()] = pValue.release();
|
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
|
// eat start token
|
||||||
pScanner->pop();
|
pScanner->pop();
|
||||||
|
state.PushCollectionType(ParserState::FLOW_MAP);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
if(pScanner->empty())
|
if(pScanner->empty())
|
||||||
@@ -148,12 +152,15 @@ namespace YAML
|
|||||||
// assign the map with the actual pointers
|
// assign the map with the actual pointers
|
||||||
m_data[pKey.release()] = pValue.release();
|
m_data[pKey.release()] = pValue.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.PopCollectionType(ParserState::FLOW_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseCompact
|
// ParseCompact
|
||||||
// . Single "key: value" pair in a flow sequence
|
// . 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 <Node> pKey(new Node), pValue(new Node);
|
std::auto_ptr <Node> pKey(new Node), pValue(new Node);
|
||||||
|
|
||||||
// grab key
|
// grab key
|
||||||
@@ -168,12 +175,14 @@ namespace YAML
|
|||||||
|
|
||||||
// assign the map with the actual pointers
|
// assign the map with the actual pointers
|
||||||
m_data[pKey.release()] = pValue.release();
|
m_data[pKey.release()] = pValue.release();
|
||||||
|
state.PopCollectionType(ParserState::COMPACT_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseCompactWithNoKey
|
// ParseCompactWithNoKey
|
||||||
// . Single ": value" pair in a flow sequence
|
// . 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 <Node> pKey(new Node), pValue(new Node);
|
std::auto_ptr <Node> pKey(new Node), pValue(new Node);
|
||||||
|
|
||||||
// grab value
|
// grab value
|
||||||
@@ -182,6 +191,7 @@ namespace YAML
|
|||||||
|
|
||||||
// assign the map with the actual pointers
|
// assign the map with the actual pointers
|
||||||
m_data[pKey.release()] = pValue.release();
|
m_data[pKey.release()] = pValue.release();
|
||||||
|
state.PopCollectionType(ParserState::COMPACT_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::Write(Emitter& out) const
|
void Map::Write(Emitter& out) const
|
||||||
|
10
src/map.h
10
src/map.h
@@ -27,7 +27,7 @@ namespace YAML
|
|||||||
virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const;
|
virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const;
|
||||||
virtual bool GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const;
|
virtual bool GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const;
|
||||||
virtual std::size_t GetSize() 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 void Write(Emitter& out) const;
|
||||||
|
|
||||||
virtual bool IsMap() const { return true; }
|
virtual bool IsMap() const { return true; }
|
||||||
@@ -39,10 +39,10 @@ namespace YAML
|
|||||||
virtual int Compare(Map *pMap);
|
virtual int Compare(Map *pMap);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ParseBlock(Scanner *pScanner, const ParserState& state);
|
void ParseBlock(Scanner *pScanner, ParserState& state);
|
||||||
void ParseFlow(Scanner *pScanner, const ParserState& state);
|
void ParseFlow(Scanner *pScanner, ParserState& state);
|
||||||
void ParseCompact(Scanner *pScanner, const ParserState& state);
|
void ParseCompact(Scanner *pScanner, ParserState& state);
|
||||||
void ParseCompactWithNoKey(Scanner *pScanner, const ParserState& state);
|
void ParseCompactWithNoKey(Scanner *pScanner, ParserState& state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
node_map m_data;
|
node_map m_data;
|
||||||
|
17
src/node.cpp
17
src/node.cpp
@@ -54,7 +54,7 @@ namespace YAML
|
|||||||
return std::auto_ptr<Node> (new Node(m_mark, m_anchor, m_tag, m_pContent));
|
return std::auto_ptr<Node> (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();
|
Clear();
|
||||||
|
|
||||||
@@ -104,13 +104,14 @@ namespace YAML
|
|||||||
break;
|
break;
|
||||||
case Token::FLOW_MAP_START:
|
case Token::FLOW_MAP_START:
|
||||||
case Token::BLOCK_MAP_START:
|
case Token::BLOCK_MAP_START:
|
||||||
|
m_pContent = new Map;
|
||||||
|
break;
|
||||||
case Token::KEY:
|
case Token::KEY:
|
||||||
|
// compact maps can only go in a flow sequence
|
||||||
|
if(state.GetCurCollectionType() == ParserState::FLOW_SEQ)
|
||||||
m_pContent = new Map;
|
m_pContent = new Map;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// std::stringstream str;
|
|
||||||
// str << TokenNames[pScanner->peek().type];
|
|
||||||
// throw std::runtime_error(str.str());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ namespace YAML
|
|||||||
|
|
||||||
// ParseHeader
|
// ParseHeader
|
||||||
// . Grabs any tag, alias, or anchor tokens and deals with them.
|
// . 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) {
|
while(1) {
|
||||||
if(pScanner->empty())
|
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();
|
Token& token = pScanner->peek();
|
||||||
if(m_tag != "")
|
if(m_tag != "")
|
||||||
@@ -151,7 +152,7 @@ namespace YAML
|
|||||||
pScanner->pop();
|
pScanner->pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::ParseAnchor(Scanner *pScanner, const ParserState& /*state*/)
|
void Node::ParseAnchor(Scanner *pScanner, ParserState& /*state*/)
|
||||||
{
|
{
|
||||||
Token& token = pScanner->peek();
|
Token& token = pScanner->peek();
|
||||||
if(m_anchor != "")
|
if(m_anchor != "")
|
||||||
@@ -162,7 +163,7 @@ namespace YAML
|
|||||||
pScanner->pop();
|
pScanner->pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::ParseAlias(Scanner *pScanner, const ParserState& /*state*/)
|
void Node::ParseAlias(Scanner *pScanner, ParserState& /*state*/)
|
||||||
{
|
{
|
||||||
Token& token = pScanner->peek();
|
Token& token = pScanner->peek();
|
||||||
if(m_anchor != "")
|
if(m_anchor != "")
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <stack>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace YAML
|
namespace YAML
|
||||||
{
|
{
|
||||||
@@ -16,11 +18,19 @@ namespace YAML
|
|||||||
|
|
||||||
struct ParserState
|
struct ParserState
|
||||||
{
|
{
|
||||||
|
enum COLLECTION_TYPE { NONE, BLOCK_MAP, BLOCK_SEQ, FLOW_MAP, FLOW_SEQ, COMPACT_MAP };
|
||||||
|
|
||||||
ParserState();
|
ParserState();
|
||||||
|
|
||||||
const std::string TranslateTagHandle(const std::string& handle) const;
|
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;
|
Version version;
|
||||||
std::map <std::string, std::string> tags;
|
std::map <std::string, std::string> tags;
|
||||||
|
std::stack <COLLECTION_TYPE> collectionStack;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ namespace YAML
|
|||||||
return new Scalar(m_data);
|
return new Scalar(m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scalar::Parse(Scanner *pScanner, const ParserState& /*state*/)
|
void Scalar::Parse(Scanner *pScanner, ParserState& /*state*/)
|
||||||
{
|
{
|
||||||
Token& token = pScanner->peek();
|
Token& token = pScanner->peek();
|
||||||
m_data = token.value;
|
m_data = token.value;
|
||||||
|
@@ -18,7 +18,7 @@ namespace YAML
|
|||||||
|
|
||||||
virtual Content *Clone() const;
|
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 void Write(Emitter& out) const;
|
||||||
|
|
||||||
virtual bool IsScalar() const { return true; }
|
virtual bool IsScalar() const { return true; }
|
||||||
|
@@ -59,7 +59,7 @@ namespace YAML
|
|||||||
return m_data.size();
|
return m_data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sequence::Parse(Scanner *pScanner, const ParserState& state)
|
void Sequence::Parse(Scanner *pScanner, ParserState& state)
|
||||||
{
|
{
|
||||||
Clear();
|
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
|
// eat start token
|
||||||
pScanner->pop();
|
pScanner->pop();
|
||||||
|
state.PushCollectionType(ParserState::BLOCK_SEQ);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
if(pScanner->empty())
|
if(pScanner->empty())
|
||||||
@@ -100,12 +101,15 @@ namespace YAML
|
|||||||
|
|
||||||
pNode->Parse(pScanner, state);
|
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
|
// eat start token
|
||||||
pScanner->pop();
|
pScanner->pop();
|
||||||
|
state.PushCollectionType(ParserState::FLOW_SEQ);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
if(pScanner->empty())
|
if(pScanner->empty())
|
||||||
@@ -129,6 +133,8 @@ namespace YAML
|
|||||||
else if(token.type != Token::FLOW_SEQ_END)
|
else if(token.type != Token::FLOW_SEQ_END)
|
||||||
throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW);
|
throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.PopCollectionType(ParserState::FLOW_SEQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sequence::Write(Emitter& out) const
|
void Sequence::Write(Emitter& out) const
|
||||||
|
@@ -26,7 +26,7 @@ namespace YAML
|
|||||||
virtual Node *GetNode(std::size_t i) const;
|
virtual Node *GetNode(std::size_t i) const;
|
||||||
virtual std::size_t GetSize() 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 void Write(Emitter& out) const;
|
||||||
|
|
||||||
virtual bool IsSequence() const { return true; }
|
virtual bool IsSequence() const { return true; }
|
||||||
@@ -38,8 +38,8 @@ namespace YAML
|
|||||||
virtual int Compare(Map *) { return -1; }
|
virtual int Compare(Map *) { return -1; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ParseBlock(Scanner *pScanner, const ParserState& state);
|
void ParseBlock(Scanner *pScanner, ParserState& state);
|
||||||
void ParseFlow(Scanner *pScanner, const ParserState& state);
|
void ParseFlow(Scanner *pScanner, ParserState& state);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector <Node *> m_data;
|
std::vector <Node *> m_data;
|
||||||
|
@@ -610,6 +610,28 @@ namespace Test
|
|||||||
|
|
||||||
return true;
|
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 {
|
namespace {
|
||||||
@@ -869,6 +891,7 @@ namespace Test
|
|||||||
RunParserTest(&Parser::MultipleDocs, "multiple docs", passed, total);
|
RunParserTest(&Parser::MultipleDocs, "multiple docs", passed, total);
|
||||||
RunParserTest(&Parser::ExplicitEndDoc, "explicit end doc", passed, total);
|
RunParserTest(&Parser::ExplicitEndDoc, "explicit end doc", passed, total);
|
||||||
RunParserTest(&Parser::MultipleDocsWithSomeExplicitIndicators, "multiple docs with some explicit indicators", 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, false, "UTF-8, no BOM", passed, total);
|
||||||
RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed, total);
|
RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed, total);
|
||||||
|
Reference in New Issue
Block a user