From b3a5a519f2c22f1cddd8fab47bb42d03cee85afd Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 22 May 2009 21:48:05 +0000 Subject: [PATCH] Merged aliases branch into trunk, changes r100:150 --- include/exceptions.h | 2 + include/node.h | 7 ++ src/CMakeLists.txt | 2 +- src/alias.cpp | 125 +++++++++++++++++++++++++++++++++ src/alias.h | 42 +++++++++++ src/node.cpp | 70 +++++++++++++----- src/parser.cpp | 3 + src/scanner.cpp | 51 +++++++++++++- src/scanner.h | 10 +++ src/stream.cpp | 1 + yaml-reader/tests.cpp | 1 + yaml-reader/tests/aliased.yaml | 4 ++ yamlcpp.vcproj | 8 +++ 13 files changed, 308 insertions(+), 18 deletions(-) create mode 100644 src/alias.cpp create mode 100644 src/alias.h create mode 100644 yaml-reader/tests/aliased.yaml diff --git a/include/exceptions.h b/include/exceptions.h index ce00bba..8378369 100644 --- a/include/exceptions.h +++ b/include/exceptions.h @@ -38,6 +38,8 @@ namespace YAML const std::string CHAR_IN_ANCHOR = "illegal character found while scanning anchor"; const std::string ZERO_INDENT_IN_BLOCK = "cannot set zero indentation for a block scalar"; const std::string CHAR_IN_BLOCK = "unexpected character in block scalar"; + const std::string AMBIGUOUS_ANCHOR = "cannot assign the same alias to multiple nodes"; + const std::string UNKNOWN_ANCHOR = "the referenced anchor is not defined"; const std::string INVALID_SCALAR = "invalid scalar"; const std::string KEY_NOT_FOUND = "key not found"; diff --git a/include/node.h b/include/node.h index 8900382..0dcac18 100644 --- a/include/node.h +++ b/include/node.h @@ -66,6 +66,11 @@ namespace YAML const Node& operator [] (unsigned u) const; const Node& operator [] (int i) const; + // for anchors/aliases + const Node *Identity() const { return m_pIdentity; } + bool IsAlias() const { return m_alias; } + bool IsReferenced() const { return m_referenced; } + // insertion friend std::ostream& operator << (std::ostream& out, const Node& node); @@ -89,6 +94,8 @@ namespace YAML std::string m_anchor, m_tag; Content *m_pContent; bool m_alias; + const Node *m_pIdentity; + mutable bool m_referenced; }; // templated things we need to keep inline in the header diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2cd7bf..9753189 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -set(FILES content.cpp iterator.cpp node.cpp parserstate.cpp +set(FILES alias.cpp content.cpp iterator.cpp node.cpp parserstate.cpp scalar.cpp scanscalar.cpp sequence.cpp stream.cpp exp.cpp map.cpp parser.cpp regex.cpp scanner.cpp scantoken.cpp simplekey.cpp) diff --git a/src/alias.cpp b/src/alias.cpp new file mode 100644 index 0000000..e8a4560 --- /dev/null +++ b/src/alias.cpp @@ -0,0 +1,125 @@ +#include "crt.h" +#include "alias.h" +#include + +namespace YAML +{ + Alias::Alias(Content* pNodeContent) + : m_pRef(pNodeContent) + { + } + + void Alias::Parse(Scanner *pScanner, const ParserState& state) + { + } + + void Alias::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) + { + out << "\n"; + } + + bool Alias::GetBegin(std::vector ::const_iterator& i) const + { + return m_pRef->GetBegin(i); + } + + bool Alias::GetBegin(std::map ::const_iterator& i) const + { + return m_pRef->GetBegin(i); + } + + bool Alias::GetEnd(std::vector ::const_iterator& i) const + { + return m_pRef->GetEnd(i); + } + + bool Alias::GetEnd(std::map ::const_iterator& i) const + { + return m_pRef->GetEnd(i); + } + + Node* Alias::GetNode(unsigned n) const + { + return m_pRef->GetNode(n); + } + + unsigned Alias::GetSize() const + { + return m_pRef->GetSize(); + } + + bool Alias::IsScalar() const + { + return m_pRef->IsScalar(); + } + + bool Alias::IsMap() const + { + return m_pRef->IsMap(); + } + + bool Alias::IsSequence() const + { + return m_pRef->IsSequence(); + } + + bool Alias::Read(std::string& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(int& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(unsigned& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(long& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(float& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(double& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(char& v) const + { + return m_pRef->Read(v); + } + + bool Alias::Read(bool& v) const + { + return m_pRef->Read(v); + } + + int Alias::Compare(Content *pContent) + { + return m_pRef->Compare(pContent); + } + + int Alias::Compare(Scalar *pScalar) + { + return m_pRef->Compare(pScalar); + } + + int Alias::Compare(Sequence *pSequence) + { + return m_pRef->Compare(pSequence); + } + + int Alias::Compare(Map *pMap) + { + return m_pRef->Compare(pMap); + } +} diff --git a/src/alias.h b/src/alias.h new file mode 100644 index 0000000..2f15d67 --- /dev/null +++ b/src/alias.h @@ -0,0 +1,42 @@ +#pragma once + +#include "content.h" + +namespace YAML +{ + class Alias : public Content + { + public: + Alias(Content *pNodeContent); + + virtual void Parse(Scanner* pScanner, const ParserState& state); + virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine); + + virtual bool GetBegin(std::vector ::const_iterator&) const; + virtual bool GetBegin(std::map ::const_iterator&) const; + virtual bool GetEnd(std::vector ::const_iterator&) const; + virtual bool GetEnd(std::map ::const_iterator&) const; + virtual Node* GetNode(unsigned) const; + virtual unsigned GetSize() const; + virtual bool IsScalar() const; + virtual bool IsMap() const; + virtual bool IsSequence() const; + + virtual bool Read(std::string&) const; + virtual bool Read(int&) const; + virtual bool Read(unsigned&) const; + virtual bool Read(long&) const; + virtual bool Read(float&) const; + virtual bool Read(double&) const; + virtual bool Read(char&) const; + virtual bool Read(bool&) const; + + virtual int Compare(Content *); + virtual int Compare(Scalar *); + virtual int Compare(Sequence *); + virtual int Compare(Map *); + + private: + Content* m_pRef; + }; +} diff --git a/src/node.cpp b/src/node.cpp index 8c86882..bb365eb 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -7,7 +7,9 @@ #include "scalar.h" #include "sequence.h" #include "map.h" +#include "alias.h" #include "iterpriv.h" +#include namespace YAML { @@ -17,7 +19,7 @@ namespace YAML return *pNode1 < *pNode2; } - Node::Node(): m_pContent(0), m_alias(false) + Node::Node(): m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(true) { } @@ -31,6 +33,9 @@ namespace YAML delete m_pContent; m_pContent = 0; m_alias = false; + m_referenced = false; + m_anchor.clear(); + m_tag.clear(); } void Node::Parse(Scanner *pScanner, const ParserState& state) @@ -47,30 +52,50 @@ namespace YAML ParseHeader(pScanner, state); - // is this an alias? if so, it can have no content - if(m_alias) + // is this an alias? if so, its contents are an alias to + // a previously defined anchor + if(m_alias) { + // the scanner throws an exception if it doesn't know this anchor name + const Node *pReferencedNode = pScanner->Retrieve(m_anchor); + m_pIdentity = pReferencedNode; + + // mark the referenced node for the sake of the client code + pReferencedNode->m_referenced = true; + + // use of an Alias object keeps the referenced content from + // being deleted twice + Content *pAliasedContent = pReferencedNode->m_pContent; + if(pAliasedContent) + m_pContent = new Alias(pAliasedContent); + return; + } // now split based on what kind of node we should be switch(pScanner->peek().type) { case TT_SCALAR: m_pContent = new Scalar; - m_pContent->Parse(pScanner, state); break; case TT_FLOW_SEQ_START: case TT_BLOCK_SEQ_START: case TT_BLOCK_ENTRY: m_pContent = new Sequence; - m_pContent->Parse(pScanner, state); break; case TT_FLOW_MAP_START: case TT_BLOCK_MAP_START: m_pContent = new Map; - m_pContent->Parse(pScanner, state); break; default: break; } + + // Have to save anchor before parsing to allow for aliases as + // contained node (recursive structure) + if(!m_anchor.empty()) + pScanner->Save(m_anchor, this); + + if(m_pContent) + m_pContent->Parse(pScanner, state); } // ParseHeader @@ -129,33 +154,46 @@ namespace YAML void Node::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) const { + // If using an anchor or tag for the whole document, document start + // must be explicit + bool indicateDocStart = (indent == 0); + // write anchor/alias if(m_anchor != "") { + if (indicateDocStart) { + out << "--- "; + indicateDocStart = false; + } + if(m_alias) - out << std::string("*"); + out << "*"; else - out << std::string("&"); - out << m_anchor << std::string(" "); + out << "&"; + out << m_anchor << " "; startedLine = true; onlyOneCharOnLine = false; } // write tag if(m_tag != "") { + if (indicateDocStart) { + out << "--- "; + indicateDocStart = false; + } + // put the tag in the "proper" brackets - if(m_tag.substr(0, 2) == "!<" && m_tag.substr(m_tag.size() - 1) == ">") - out << m_tag; + if(m_tag.substr(0, 2) == std::string("!<") && m_tag.substr(m_tag.size() - 1) == std::string(">")) + out << m_tag << " "; else - out << std::string("!<") << m_tag << std::string("> "); + out << "!<" << m_tag << "> "; startedLine = true; onlyOneCharOnLine = false; } - if(!m_pContent) { - out << std::string("\n"); - } else { + if(!m_pContent) + out << "\n"; + else m_pContent->Write(out, indent, startedLine, onlyOneCharOnLine); - } } CONTENT_TYPE Node::GetType() const diff --git a/src/parser.cpp b/src/parser.cpp index 3ec4773..6b49429 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -55,6 +55,9 @@ namespace YAML // and finally eat any doc ends we see while(!m_pScanner->empty() && m_pScanner->peek().type == TT_DOC_END) m_pScanner->pop(); + + // clear anchors from the scanner, which are no longer relevant + m_pScanner->ClearAnchors(); } // ParseDirectives diff --git a/src/scanner.cpp b/src/scanner.cpp index 1d148d9..c89b105 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -29,8 +29,13 @@ namespace YAML void Scanner::pop() { EnsureTokensInQueue(); - if(!m_tokens.empty()) + if(!m_tokens.empty()) { + // Saved anchors shouldn't survive popping the document end marker + if (m_tokens.front().type == TT_DOC_END) { + ClearAnchors(); + } m_tokens.pop(); + } } // peek @@ -217,6 +222,7 @@ namespace YAML m_startedStream = true; m_simpleKeyAllowed = true; m_indents.push(-1); + m_anchors.clear(); } // EndStream @@ -273,4 +279,47 @@ namespace YAML m_tokens.push(Token(TT_BLOCK_END, INPUT.line, INPUT.column)); } } + + // Save + // . Saves a pointer to the Node object referenced by a particular anchor + // name. + void Scanner::Save(const std::string& anchor, Node* value) + { + m_anchors[anchor] = value; + } + + // Retrieve + // . Retrieves a pointer previously saved for an anchor name. + // . Throws an exception if the anchor has not been defined. + const Node *Scanner::Retrieve(const std::string& anchor) const + { + typedef std::map map; + + map::const_iterator itNode = m_anchors.find(anchor); + + if(m_anchors.end() == itNode) + ThrowParserException(ErrorMsg::UNKNOWN_ANCHOR); + + return itNode->second; + } + + // ThrowParserException + // . Throws a ParserException with the current token location + // (if available). + // . Does not parse any more tokens. + void Scanner::ThrowParserException(const std::string& msg) const + { + int line = -1, column = -1; + if(!m_tokens.empty()) { + const Token& token = m_tokens.front(); + line = token.line; + column = token.column; + } + throw ParserException(line, column, msg); + } + + void Scanner::ClearAnchors() + { + m_anchors.clear(); + } } diff --git a/src/scanner.h b/src/scanner.h index e4590ef..f2f7df2 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -5,11 +5,14 @@ #include #include #include +#include #include "stream.h" #include "token.h" namespace YAML { + class Node; + class Scanner { public: @@ -21,6 +24,11 @@ namespace YAML void pop(); Token& peek(); + // anchor management + void Save(const std::string& anchor, Node* value); + const Node *Retrieve(const std::string& anchor) const; + void ClearAnchors(); + private: // scanning void EnsureTokensInQueue(); @@ -35,6 +43,7 @@ namespace YAML void InsertSimpleKey(); bool VerifySimpleKey(bool force = false); void VerifyAllSimpleKeys(); + void ThrowParserException(const std::string& msg) const; bool IsWhitespaceToBeEaten(char ch); @@ -81,5 +90,6 @@ namespace YAML bool m_isLastKeyValid; std::stack m_simpleKeys; std::stack m_indents; + std::map m_anchors; }; } diff --git a/src/stream.cpp b/src/stream.cpp index aa957c3..97da1d4 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -55,6 +55,7 @@ namespace YAML std::string Stream::get(int n) { std::string ret; + ret.reserve(n); for(int i=0;i + + @@ -257,6 +261,10 @@ + +