Merged aliases branch into trunk, changes r100:150

This commit is contained in:
jbeder
2009-05-22 21:48:05 +00:00
parent 7297387015
commit 5abf31b991
13 changed files with 308 additions and 18 deletions

View File

@@ -38,6 +38,8 @@ namespace YAML
const std::string CHAR_IN_ANCHOR = "illegal character found while scanning anchor"; 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 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 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 INVALID_SCALAR = "invalid scalar";
const std::string KEY_NOT_FOUND = "key not found"; const std::string KEY_NOT_FOUND = "key not found";

View File

@@ -66,6 +66,11 @@ namespace YAML
const Node& operator [] (unsigned u) const; const Node& operator [] (unsigned u) const;
const Node& operator [] (int i) 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 // insertion
friend std::ostream& operator << (std::ostream& out, const Node& node); friend std::ostream& operator << (std::ostream& out, const Node& node);
@@ -89,6 +94,8 @@ namespace YAML
std::string m_anchor, m_tag; std::string m_anchor, m_tag;
Content *m_pContent; Content *m_pContent;
bool m_alias; bool m_alias;
const Node *m_pIdentity;
mutable bool m_referenced;
}; };
// templated things we need to keep inline in the header // templated things we need to keep inline in the header

View File

@@ -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 scalar.cpp scanscalar.cpp sequence.cpp stream.cpp
exp.cpp map.cpp parser.cpp regex.cpp scanner.cpp exp.cpp map.cpp parser.cpp regex.cpp scanner.cpp
scantoken.cpp simplekey.cpp) scantoken.cpp simplekey.cpp)

125
src/alias.cpp Normal file
View File

@@ -0,0 +1,125 @@
#include "crt.h"
#include "alias.h"
#include <iostream>
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 <Node *>::const_iterator& i) const
{
return m_pRef->GetBegin(i);
}
bool Alias::GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& i) const
{
return m_pRef->GetBegin(i);
}
bool Alias::GetEnd(std::vector <Node *>::const_iterator& i) const
{
return m_pRef->GetEnd(i);
}
bool Alias::GetEnd(std::map <Node *, Node *, ltnode>::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);
}
}

42
src/alias.h Normal file
View File

@@ -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 <Node *>::const_iterator&) const;
virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator&) const;
virtual bool GetEnd(std::vector <Node *>::const_iterator&) const;
virtual bool GetEnd(std::map <Node *, Node *, ltnode>::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;
};
}

View File

@@ -7,7 +7,9 @@
#include "scalar.h" #include "scalar.h"
#include "sequence.h" #include "sequence.h"
#include "map.h" #include "map.h"
#include "alias.h"
#include "iterpriv.h" #include "iterpriv.h"
#include <iostream>
namespace YAML namespace YAML
{ {
@@ -17,7 +19,7 @@ namespace YAML
return *pNode1 < *pNode2; 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; delete m_pContent;
m_pContent = 0; m_pContent = 0;
m_alias = false; m_alias = false;
m_referenced = false;
m_anchor.clear();
m_tag.clear();
} }
void Node::Parse(Scanner *pScanner, const ParserState& state) void Node::Parse(Scanner *pScanner, const ParserState& state)
@@ -47,30 +52,50 @@ namespace YAML
ParseHeader(pScanner, state); ParseHeader(pScanner, state);
// is this an alias? if so, it can have no content // is this an alias? if so, its contents are an alias to
if(m_alias) // 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; return;
}
// now split based on what kind of node we should be // now split based on what kind of node we should be
switch(pScanner->peek().type) { switch(pScanner->peek().type) {
case TT_SCALAR: case TT_SCALAR:
m_pContent = new Scalar; m_pContent = new Scalar;
m_pContent->Parse(pScanner, state);
break; break;
case TT_FLOW_SEQ_START: case TT_FLOW_SEQ_START:
case TT_BLOCK_SEQ_START: case TT_BLOCK_SEQ_START:
case TT_BLOCK_ENTRY: case TT_BLOCK_ENTRY:
m_pContent = new Sequence; m_pContent = new Sequence;
m_pContent->Parse(pScanner, state);
break; break;
case TT_FLOW_MAP_START: case TT_FLOW_MAP_START:
case TT_BLOCK_MAP_START: case TT_BLOCK_MAP_START:
m_pContent = new Map; m_pContent = new Map;
m_pContent->Parse(pScanner, state);
break; break;
default: default:
break; 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 // ParseHeader
@@ -129,33 +154,46 @@ namespace YAML
void Node::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) const 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 // write anchor/alias
if(m_anchor != "") { if(m_anchor != "") {
if (indicateDocStart) {
out << "--- ";
indicateDocStart = false;
}
if(m_alias) if(m_alias)
out << std::string("*"); out << "*";
else else
out << std::string("&"); out << "&";
out << m_anchor << std::string(" "); out << m_anchor << " ";
startedLine = true; startedLine = true;
onlyOneCharOnLine = false; onlyOneCharOnLine = false;
} }
// write tag // write tag
if(m_tag != "") { if(m_tag != "") {
if (indicateDocStart) {
out << "--- ";
indicateDocStart = false;
}
// put the tag in the "proper" brackets // put the tag in the "proper" brackets
if(m_tag.substr(0, 2) == "!<" && m_tag.substr(m_tag.size() - 1) == ">") if(m_tag.substr(0, 2) == std::string("!<") && m_tag.substr(m_tag.size() - 1) == std::string(">"))
out << m_tag; out << m_tag << " ";
else else
out << std::string("!<") << m_tag << std::string("> "); out << "!<" << m_tag << "> ";
startedLine = true; startedLine = true;
onlyOneCharOnLine = false; onlyOneCharOnLine = false;
} }
if(!m_pContent) { if(!m_pContent)
out << std::string("\n"); out << "\n";
} else { else
m_pContent->Write(out, indent, startedLine, onlyOneCharOnLine); m_pContent->Write(out, indent, startedLine, onlyOneCharOnLine);
}
} }
CONTENT_TYPE Node::GetType() const CONTENT_TYPE Node::GetType() const

View File

@@ -55,6 +55,9 @@ namespace YAML
// and finally eat any doc ends we see // and finally eat any doc ends we see
while(!m_pScanner->empty() && m_pScanner->peek().type == TT_DOC_END) while(!m_pScanner->empty() && m_pScanner->peek().type == TT_DOC_END)
m_pScanner->pop(); m_pScanner->pop();
// clear anchors from the scanner, which are no longer relevant
m_pScanner->ClearAnchors();
} }
// ParseDirectives // ParseDirectives

View File

@@ -29,8 +29,13 @@ namespace YAML
void Scanner::pop() void Scanner::pop()
{ {
EnsureTokensInQueue(); 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(); m_tokens.pop();
}
} }
// peek // peek
@@ -217,6 +222,7 @@ namespace YAML
m_startedStream = true; m_startedStream = true;
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_indents.push(-1); m_indents.push(-1);
m_anchors.clear();
} }
// EndStream // EndStream
@@ -273,4 +279,47 @@ namespace YAML
m_tokens.push(Token(TT_BLOCK_END, INPUT.line, INPUT.column)); 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<std::string, const Node *> 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();
}
} }

View File

@@ -5,11 +5,14 @@
#include <queue> #include <queue>
#include <stack> #include <stack>
#include <set> #include <set>
#include <map>
#include "stream.h" #include "stream.h"
#include "token.h" #include "token.h"
namespace YAML namespace YAML
{ {
class Node;
class Scanner class Scanner
{ {
public: public:
@@ -21,6 +24,11 @@ namespace YAML
void pop(); void pop();
Token& peek(); Token& peek();
// anchor management
void Save(const std::string& anchor, Node* value);
const Node *Retrieve(const std::string& anchor) const;
void ClearAnchors();
private: private:
// scanning // scanning
void EnsureTokensInQueue(); void EnsureTokensInQueue();
@@ -35,6 +43,7 @@ namespace YAML
void InsertSimpleKey(); void InsertSimpleKey();
bool VerifySimpleKey(bool force = false); bool VerifySimpleKey(bool force = false);
void VerifyAllSimpleKeys(); void VerifyAllSimpleKeys();
void ThrowParserException(const std::string& msg) const;
bool IsWhitespaceToBeEaten(char ch); bool IsWhitespaceToBeEaten(char ch);
@@ -81,5 +90,6 @@ namespace YAML
bool m_isLastKeyValid; bool m_isLastKeyValid;
std::stack <SimpleKey> m_simpleKeys; std::stack <SimpleKey> m_simpleKeys;
std::stack <int> m_indents; std::stack <int> m_indents;
std::map <std::string, const Node *> m_anchors;
}; };
} }

View File

@@ -55,6 +55,7 @@ namespace YAML
std::string Stream::get(int n) std::string Stream::get(int n)
{ {
std::string ret; std::string ret;
ret.reserve(n);
for(int i=0;i<n;i++) for(int i=0;i<n;i++)
ret += get(); ret += get();
return ret; return ret;

View File

@@ -16,6 +16,7 @@ namespace Test
files.push_back("tests/mixed.yaml"); files.push_back("tests/mixed.yaml");
files.push_back("tests/scalars.yaml"); files.push_back("tests/scalars.yaml");
files.push_back("tests/directives.yaml"); files.push_back("tests/directives.yaml");
files.push_back("tests/aliased.yaml");
bool passed = true; bool passed = true;
for(unsigned i=0;i<files.size();i++) { for(unsigned i=0;i<files.size();i++) {

View File

@@ -0,0 +1,4 @@
--- &list
- This document contains a recursive list.
- *list
...

View File

@@ -167,6 +167,10 @@
<Filter <Filter
Name="Representation" Name="Representation"
> >
<File
RelativePath=".\src\alias.cpp"
>
</File>
<File <File
RelativePath=".\src\content.cpp" RelativePath=".\src\content.cpp"
> >
@@ -257,6 +261,10 @@
<Filter <Filter
Name="Representation" Name="Representation"
> >
<File
RelativePath=".\include\alias.h"
>
</File>
<File <File
RelativePath=".\src\content.h" RelativePath=".\src\content.h"
> >