Merged emitter branch into trunk, changes r105:r151

This commit is contained in:
jbeder
2009-05-22 21:52:31 +00:00
parent 5abf31b991
commit cba20711b0
20 changed files with 2478 additions and 37 deletions

85
include/emitter.h Normal file
View File

@@ -0,0 +1,85 @@
#pragma once
#include "emittermanip.h"
#include "ostream.h"
#include <memory>
#include <string>
namespace YAML
{
class EmitterState;
class Emitter
{
public:
Emitter();
~Emitter();
bool WriteToStream(std::ostream& out) const;
bool WriteToFile(const std::string& fileName) const;
// state checking
bool good() const;
const std::string GetLastError() const;
// global setters
bool SetStringFormat(EMITTER_MANIP value);
bool SetBoolFormat(EMITTER_MANIP value);
bool SetIntBase(EMITTER_MANIP value);
bool SetSeqFormat(EMITTER_MANIP value);
bool SetMapFormat(EMITTER_MANIP value);
bool SetIndent(unsigned n);
bool SetPreCommentIndent(unsigned n);
bool SetPostCommentIndent(unsigned n);
// local setters
Emitter& SetLocalValue(EMITTER_MANIP value);
Emitter& SetLocalIndent(const _Indent& indent);
// overloads of write
Emitter& Write(const std::string& str);
Emitter& Write(const char *str);
Emitter& Write(int i);
Emitter& Write(bool b);
Emitter& Write(float f);
Emitter& Write(double d);
Emitter& Write(const _Alias& alias);
Emitter& Write(const _Anchor& anchor);
Emitter& Write(const _Comment& comment);
private:
enum ATOMIC_TYPE { AT_SCALAR, AT_SEQ, AT_BLOCK_SEQ, AT_FLOW_SEQ, AT_MAP, AT_BLOCK_MAP, AT_FLOW_MAP };
void PreAtomicWrite();
bool GotoNextPreAtomicState();
void PostAtomicWrite();
void EmitSeparationIfNecessary();
void EmitBeginSeq();
void EmitEndSeq();
void EmitBeginMap();
void EmitEndMap();
void EmitKey();
void EmitValue();
private:
ostream m_stream;
std::auto_ptr <EmitterState> m_pState;
};
// overloads of insertion
template <typename T>
inline Emitter& operator << (Emitter& emitter, T v) {
return emitter.Write(v);
}
template <>
inline Emitter& operator << (Emitter& emitter, EMITTER_MANIP value) {
return emitter.SetLocalValue(value);
}
template <>
inline Emitter& operator << (Emitter& emitter, _Indent indent) {
return emitter.SetLocalIndent(indent);
}
}

84
include/emittermanip.h Normal file
View File

@@ -0,0 +1,84 @@
#pragma once
#include <string>
namespace YAML
{
enum EMITTER_MANIP {
// general manipulators
Auto,
// string manipulators
// Auto, // duplicate
SingleQuoted,
DoubleQuoted,
Literal,
// bool manipulators
YesNoBool, // yes, no
TrueFalseBool, // true, false
OnOffBool, // on, off
UpperCase, // TRUE, N
LowerCase, // f, yes
CamelCase, // No, Off
LongBool, // yes, On
ShortBool, // y, t
// int manipulators
Dec,
Hex,
Oct,
// sequence manipulators
BeginSeq,
EndSeq,
Flow,
Block,
// map manipulators
BeginMap,
EndMap,
Key,
Value,
// Flow, // duplicate
// Block, // duplicate
// Auto, // duplicate
LongKey,
};
struct _Indent {
_Indent(int value_): value(value_) {}
int value;
};
inline _Indent Indent(int value) {
return _Indent(value);
}
struct _Alias {
_Alias(const std::string& content_): content(content_) {}
std::string content;
};
inline _Alias Alias(const std::string content) {
return _Alias(content);
}
struct _Anchor {
_Anchor(const std::string& content_): content(content_) {}
std::string content;
};
inline _Anchor Anchor(const std::string content) {
return _Anchor(content);
}
struct _Comment {
_Comment(const std::string& content_): content(content_) {}
std::string content;
};
inline _Comment Comment(const std::string content) {
return _Comment(content);
}
}

View File

@@ -8,42 +8,53 @@ namespace YAML
// error messages // error messages
namespace ErrorMsg namespace ErrorMsg
{ {
const std::string YAML_DIRECTIVE_ARGS = "YAML directives must have exactly one argument"; const std::string YAML_DIRECTIVE_ARGS = "YAML directives must have exactly one argument";
const std::string YAML_VERSION = "bad YAML version: "; const std::string YAML_VERSION = "bad YAML version: ";
const std::string YAML_MAJOR_VERSION = "YAML major version too large"; const std::string YAML_MAJOR_VERSION = "YAML major version too large";
const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments"; const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments";
const std::string END_OF_MAP = "end of map not found"; const std::string END_OF_MAP = "end of map not found";
const std::string END_OF_MAP_FLOW = "end of map flow not found"; const std::string END_OF_MAP_FLOW = "end of map flow not found";
const std::string END_OF_SEQ = "end of sequence not found"; const std::string END_OF_SEQ = "end of sequence not found";
const std::string END_OF_SEQ_FLOW = "end of sequence flow not found"; const std::string END_OF_SEQ_FLOW = "end of sequence flow not found";
const std::string MULTIPLE_TAGS = "cannot assign multiple tags to the same node"; const std::string MULTIPLE_TAGS = "cannot assign multiple tags to the same node";
const std::string MULTIPLE_ANCHORS = "cannot assign multiple anchors to the same node"; const std::string MULTIPLE_ANCHORS = "cannot assign multiple anchors to the same node";
const std::string MULTIPLE_ALIASES = "cannot assign multiple aliases to the same node"; const std::string MULTIPLE_ALIASES = "cannot assign multiple aliases to the same node";
const std::string ALIAS_CONTENT = "aliases can't have any content, *including* tags"; const std::string ALIAS_CONTENT = "aliases can't have any content, *including* tags";
const std::string INVALID_HEX = "bad character found while scanning hex number"; const std::string INVALID_HEX = "bad character found while scanning hex number";
const std::string INVALID_UNICODE = "invalid unicode: "; const std::string INVALID_UNICODE = "invalid unicode: ";
const std::string INVALID_ESCAPE = "unknown escape character: "; const std::string INVALID_ESCAPE = "unknown escape character: ";
const std::string UNKNOWN_TOKEN = "unknown token"; const std::string UNKNOWN_TOKEN = "unknown token";
const std::string DOC_IN_SCALAR = "illegal document indicator in scalar"; const std::string DOC_IN_SCALAR = "illegal document indicator in scalar";
const std::string EOF_IN_SCALAR = "illegal EOF in scalar"; const std::string EOF_IN_SCALAR = "illegal EOF in scalar";
const std::string CHAR_IN_SCALAR = "illegal character in scalar"; const std::string CHAR_IN_SCALAR = "illegal character in scalar";
const std::string TAB_IN_INDENTATION = "illegal tab when looking for indentation"; const std::string TAB_IN_INDENTATION = "illegal tab when looking for indentation";
const std::string FLOW_END = "illegal flow end"; const std::string FLOW_END = "illegal flow end";
const std::string BLOCK_ENTRY = "illegal block entry"; const std::string BLOCK_ENTRY = "illegal block entry";
const std::string MAP_KEY = "illegal map key"; const std::string MAP_KEY = "illegal map key";
const std::string MAP_VALUE = "illegal map value"; const std::string MAP_VALUE = "illegal map value";
const std::string ALIAS_NOT_FOUND = "alias not found after *"; const std::string ALIAS_NOT_FOUND = "alias not found after *";
const std::string ANCHOR_NOT_FOUND = "anchor not found after &"; const std::string ANCHOR_NOT_FOUND = "anchor not found after &";
const std::string CHAR_IN_ALIAS = "illegal character found while scanning alias"; const std::string CHAR_IN_ALIAS = "illegal character found while scanning alias";
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 AMBIGUOUS_ANCHOR = "cannot assign the same alias to multiple nodes";
const std::string UNKNOWN_ANCHOR = "the referenced anchor is not defined"; 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";
const std::string BAD_DEREFERENCE = "bad dereference"; const std::string BAD_DEREFERENCE = "bad dereference";
const std::string UNMATCHED_GROUP_TAG = "unmatched group tag";
const std::string UNEXPECTED_END_SEQ = "unexpected end sequence token";
const std::string UNEXPECTED_END_MAP = "unexpected end map token";
const std::string SINGLE_QUOTED_CHAR = "invalid character in single-quoted string";
const std::string INVALID_ANCHOR = "invalid anchor";
const std::string INVALID_ALIAS = "invalid alias";
const std::string EXPECTED_KEY_TOKEN = "expected key token";
const std::string EXPECTED_VALUE_TOKEN = "expected value token";
const std::string UNEXPECTED_KEY_TOKEN = "unexpected key token";
const std::string UNEXPECTED_VALUE_TOKEN = "unexpected value token";
} }
class Exception: public std::exception { class Exception: public std::exception {
@@ -101,4 +112,10 @@ namespace YAML
BadDereference() BadDereference()
: RepresentationException(-1, -1, ErrorMsg::BAD_DEREFERENCE) {} : RepresentationException(-1, -1, ErrorMsg::BAD_DEREFERENCE) {}
}; };
class EmitterException: public Exception {
public:
EmitterException(const std::string& msg_)
: Exception(-1, -1, msg_) {}
};
} }

16
include/noncopyable.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
namespace YAML
{
// this is basically boost::noncopyable
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private:
noncopyable(const noncopyable&);
const noncopyable& operator = (const noncopyable&);
};
}

31
include/ostream.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <string>
namespace YAML
{
class ostream
{
public:
ostream();
~ostream();
void reserve(unsigned size);
void put(char ch);
const char *str() const { return m_buffer; }
unsigned row() const { return m_row; }
unsigned col() const { return m_col; }
private:
char *m_buffer;
unsigned m_pos;
unsigned m_size;
unsigned m_row, m_col;
};
ostream& operator << (ostream& out, const char *str);
ostream& operator << (ostream& out, const std::string& str);
ostream& operator << (ostream& out, char ch);
}

38
include/stlemitter.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <vector>
#include <list>
#include <map>
namespace YAML
{
template <typename T>
inline Emitter& operator << (Emitter& emitter, const std::vector <T>& v) {
typedef typename std::vector <T> vec;
emitter << BeginSeq;
for(typename vec::const_iterator it=v.begin();it!=v.end();++it)
emitter << *it;
emitter << EndSeq;
return emitter;
}
template <typename T>
inline Emitter& operator << (Emitter& emitter, const std::list <T>& v) {
typedef typename std::list <T> list;
emitter << BeginSeq;
for(typename list::const_iterator it=v.begin();it!=v.end();++it)
emitter << *it;
emitter << EndSeq;
return emitter;
}
template <typename K, typename V>
inline Emitter& operator << (Emitter& emitter, const std::map <K, V>& m) {
typedef typename std::map <K, V> map;
emitter << BeginMap;
for(typename map::const_iterator it=m.begin();it!=m.end();++it)
emitter << Key << it->first << Value << it->second;
emitter << EndMap;
return emitter;
}
}

View File

@@ -1,7 +1,9 @@
set(FILES alias.cpp 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
emitter.cpp emitterstate.h emitterstate.cpp emitterutils.h emitterutils.cpp
ostream.cpp)
include_directories(${YAML_CPP_SOURCE_DIR}/include) include_directories(${YAML_CPP_SOURCE_DIR}/include)
add_library(yaml-cpp ${FILES}) add_library(yaml-cpp ${FILES})

697
src/emitter.cpp Normal file
View File

@@ -0,0 +1,697 @@
#include "emitter.h"
#include "emitterstate.h"
#include "emitterutils.h"
#include "indentation.h"
#include "exceptions.h"
#include <fstream>
#include <sstream>
namespace YAML
{
Emitter::Emitter(): m_pState(new EmitterState)
{
}
Emitter::~Emitter()
{
}
bool Emitter::WriteToStream(std::ostream& out) const
{
out << m_stream.str();
return true;
}
bool Emitter::WriteToFile(const std::string& fileName) const
{
std::ofstream fout(fileName.c_str());
if(!fout)
return false;
return WriteToStream(fout);
}
// state checking
bool Emitter::good() const
{
return m_pState->good();
}
const std::string Emitter::GetLastError() const
{
return m_pState->GetLastError();
}
// global setters
bool Emitter::SetStringFormat(EMITTER_MANIP value)
{
return m_pState->SetStringFormat(value, GLOBAL);
}
bool Emitter::SetBoolFormat(EMITTER_MANIP value)
{
bool ok = false;
if(m_pState->SetBoolFormat(value, GLOBAL))
ok = true;
if(m_pState->SetBoolCaseFormat(value, GLOBAL))
ok = true;
if(m_pState->SetBoolLengthFormat(value, GLOBAL))
ok = true;
return ok;
}
bool Emitter::SetIntBase(EMITTER_MANIP value)
{
return m_pState->SetIntFormat(value, GLOBAL);
}
bool Emitter::SetSeqFormat(EMITTER_MANIP value)
{
return m_pState->SetFlowType(GT_SEQ, value, GLOBAL);
}
bool Emitter::SetMapFormat(EMITTER_MANIP value)
{
bool ok = false;
if(m_pState->SetFlowType(GT_MAP, value, GLOBAL))
ok = true;
if(m_pState->SetMapKeyFormat(value, GLOBAL))
ok = true;
return ok;
}
bool Emitter::SetIndent(unsigned n)
{
return m_pState->SetIndent(n, GLOBAL);
}
bool Emitter::SetPreCommentIndent(unsigned n)
{
return m_pState->SetPreCommentIndent(n, GLOBAL);
}
bool Emitter::SetPostCommentIndent(unsigned n)
{
return m_pState->SetPostCommentIndent(n, GLOBAL);
}
// SetLocalValue
// . Either start/end a group, or set a modifier locally
Emitter& Emitter::SetLocalValue(EMITTER_MANIP value)
{
if(!good())
return *this;
switch(value) {
case BeginSeq:
EmitBeginSeq();
break;
case EndSeq:
EmitEndSeq();
break;
case BeginMap:
EmitBeginMap();
break;
case EndMap:
EmitEndMap();
break;
case Key:
EmitKey();
break;
case Value:
EmitValue();
break;
default:
m_pState->SetLocalValue(value);
break;
}
return *this;
}
Emitter& Emitter::SetLocalIndent(const _Indent& indent)
{
m_pState->SetIndent(indent.value, LOCAL);
return *this;
}
// GotoNextPreAtomicState
// . Runs the state machine, emitting if necessary, and returns 'true' if done (i.e., ready to emit an atom)
bool Emitter::GotoNextPreAtomicState()
{
if(!good())
return true;
unsigned curIndent = m_pState->GetCurIndent();
EMITTER_STATE curState = m_pState->GetCurState();
switch(curState) {
// document-level
case ES_WAITING_FOR_DOC:
std::cerr << "waiting for doc (pre)\n";
m_pState->SwitchState(ES_WRITING_DOC);
return true;
case ES_WRITING_DOC:
std::cerr << "writing doc (pre)\n";
return true;
// block sequence
case ES_WAITING_FOR_BLOCK_SEQ_ENTRY:
std::cerr << "waiting for block seq entry (pre)\n";
m_stream << IndentTo(curIndent) << "-";
m_pState->RequireSeparation();
m_pState->SwitchState(ES_WRITING_BLOCK_SEQ_ENTRY);
return true;
case ES_WRITING_BLOCK_SEQ_ENTRY:
std::cerr << "writing block seq entry (pre)\n";
return true;
case ES_DONE_WITH_BLOCK_SEQ_ENTRY:
std::cerr << "done with block seq entry (pre)\n";
m_stream << '\n';
m_pState->SwitchState(ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
return false;
// flow sequence
case ES_WAITING_FOR_FLOW_SEQ_ENTRY:
std::cerr << "waiting for flow seq entry (pre)\n";
m_pState->SwitchState(ES_WRITING_FLOW_SEQ_ENTRY);
return true;
case ES_WRITING_FLOW_SEQ_ENTRY:
std::cerr << "writing flow seq entry (pre)\n";
return true;
case ES_DONE_WITH_FLOW_SEQ_ENTRY:
std::cerr << "done with flow seq entry (pre)\n";
m_stream << ',';
m_pState->RequireSeparation();
m_pState->SwitchState(ES_WAITING_FOR_FLOW_SEQ_ENTRY);
return false;
// block map
case ES_WAITING_FOR_BLOCK_MAP_ENTRY:
std::cerr << "waiting for block map entry (pre)\n";
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
case ES_WAITING_FOR_BLOCK_MAP_KEY:
std::cerr << "waiting for block map key (pre)\n";
if(m_pState->CurrentlyInLongKey()) {
m_stream << IndentTo(curIndent) << '?';
m_pState->RequireSeparation();
}
m_pState->SwitchState(ES_WRITING_BLOCK_MAP_KEY);
return true;
case ES_WRITING_BLOCK_MAP_KEY:
std::cerr << "writing block map key (pre)\n";
return true;
case ES_DONE_WITH_BLOCK_MAP_KEY:
std::cerr << "done with block map key (pre)\n";
m_pState->SetError(ErrorMsg::EXPECTED_VALUE_TOKEN);
return true;
case ES_WAITING_FOR_BLOCK_MAP_VALUE:
std::cerr << "waiting for block map value (pre)\n";
if(m_pState->CurrentlyInLongKey())
m_stream << IndentTo(curIndent);
m_stream << ':';
m_pState->RequireSeparation();
m_pState->SwitchState(ES_WRITING_BLOCK_MAP_VALUE);
return true;
case ES_WRITING_BLOCK_MAP_VALUE:
std::cerr << "writing block map value (pre)\n";
return true;
case ES_DONE_WITH_BLOCK_MAP_VALUE:
std::cerr << "done with block map value (pre)\n";
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
// flow map
case ES_WAITING_FOR_FLOW_MAP_ENTRY:
std::cerr << "waiting for flow map entry (pre)\n";
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
case ES_WAITING_FOR_FLOW_MAP_KEY:
std::cerr << "waiting for flow map key (pre)\n";
m_pState->SwitchState(ES_WRITING_FLOW_MAP_KEY);
if(m_pState->CurrentlyInLongKey()) {
EmitSeparationIfNecessary();
m_stream << '?';
m_pState->RequireSeparation();
}
return true;
case ES_WRITING_FLOW_MAP_KEY:
std::cerr << "writing flow map key (pre)\n";
return true;
case ES_DONE_WITH_FLOW_MAP_KEY:
std::cerr << "done with flow map key (pre)\n";
m_pState->SetError(ErrorMsg::EXPECTED_VALUE_TOKEN);
return true;
case ES_WAITING_FOR_FLOW_MAP_VALUE:
std::cerr << "waiting for flow map value (pre)\n";
m_stream << ':';
m_pState->RequireSeparation();
m_pState->SwitchState(ES_WRITING_FLOW_MAP_VALUE);
return true;
case ES_WRITING_FLOW_MAP_VALUE:
std::cerr << "writing flow map value (pre)\n";
return true;
case ES_DONE_WITH_FLOW_MAP_VALUE:
std::cerr << "done with flow map value (pre)\n";
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
default:
assert(false);
}
assert(false);
return true;
}
// PreAtomicWrite
// . Depending on the emitter state, write to the stream to get it
// in position to do an atomic write (e.g., scalar, sequence, or map)
void Emitter::PreAtomicWrite()
{
if(!good())
return;
while(!GotoNextPreAtomicState())
;
}
// PostAtomicWrite
// . Clean up
void Emitter::PostAtomicWrite()
{
if(!good())
return;
EMITTER_STATE curState = m_pState->GetCurState();
switch(curState) {
// document-level
case ES_WRITING_DOC:
m_pState->SwitchState(ES_DONE_WITH_DOC);
break;
// block seq
case ES_WRITING_BLOCK_SEQ_ENTRY:
m_pState->SwitchState(ES_DONE_WITH_BLOCK_SEQ_ENTRY);
break;
// flow seq
case ES_WRITING_FLOW_SEQ_ENTRY:
m_pState->SwitchState(ES_DONE_WITH_FLOW_SEQ_ENTRY);
break;
// block map
case ES_WRITING_BLOCK_MAP_KEY:
m_pState->SwitchState(ES_DONE_WITH_BLOCK_MAP_KEY);
break;
case ES_WRITING_BLOCK_MAP_VALUE:
m_pState->SwitchState(ES_DONE_WITH_BLOCK_MAP_VALUE);
break;
// flow map
case ES_WRITING_FLOW_MAP_KEY:
m_pState->SwitchState(ES_DONE_WITH_FLOW_MAP_KEY);
break;
case ES_WRITING_FLOW_MAP_VALUE:
m_pState->SwitchState(ES_DONE_WITH_FLOW_MAP_VALUE);
break;
default:
assert(false);
};
m_pState->ClearModifiedSettings();
}
// EmitSeparationIfNecessary
void Emitter::EmitSeparationIfNecessary()
{
if(!good())
return;
if(m_pState->RequiresSeparation())
m_stream << ' ';
m_pState->UnsetSeparation();
}
// EmitBeginSeq
void Emitter::EmitBeginSeq()
{
if(!good())
return;
// must have a long key if we're emitting a sequence
m_pState->StartLongKey();
PreAtomicWrite();
EMITTER_STATE curState = m_pState->GetCurState();
EMITTER_MANIP flowType = m_pState->GetFlowType(GT_SEQ);
if(flowType == Block) {
if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE) {
m_stream << "\n";
m_pState->UnsetSeparation();
}
m_pState->PushState(ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
} else if(flowType == Flow) {
EmitSeparationIfNecessary();
m_stream << "[";
m_pState->PushState(ES_WAITING_FOR_FLOW_SEQ_ENTRY);
} else
assert(false);
m_pState->BeginGroup(GT_SEQ);
}
// EmitEndSeq
void Emitter::EmitEndSeq()
{
if(!good())
return;
if(m_pState->GetCurGroupType() != GT_SEQ)
return m_pState->SetError(ErrorMsg::UNEXPECTED_END_SEQ);
EMITTER_STATE curState = m_pState->GetCurState();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(flowType == FT_BLOCK)
assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY);
else if(flowType == FT_FLOW) {
m_stream << "]";
// Note: flow sequences are allowed to be empty
assert(curState == ES_DONE_WITH_FLOW_SEQ_ENTRY || curState == ES_WAITING_FOR_FLOW_SEQ_ENTRY);
} else
assert(false);
m_pState->PopState();
m_pState->EndGroup(GT_SEQ);
PostAtomicWrite();
}
// EmitBeginMap
void Emitter::EmitBeginMap()
{
if(!good())
return;
// must have a long key if we're emitting a map
m_pState->StartLongKey();
PreAtomicWrite();
EMITTER_STATE curState = m_pState->GetCurState();
EMITTER_MANIP flowType = m_pState->GetFlowType(GT_MAP);
if(flowType == Block) {
if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE) {
m_stream << "\n";
m_pState->UnsetSeparation();
}
m_pState->PushState(ES_WAITING_FOR_BLOCK_MAP_ENTRY);
} else if(flowType == Flow) {
EmitSeparationIfNecessary();
m_stream << "{";
m_pState->PushState(ES_WAITING_FOR_FLOW_MAP_ENTRY);
} else
assert(false);
m_pState->BeginGroup(GT_MAP);
}
// EmitEndMap
void Emitter::EmitEndMap()
{
if(!good())
return;
if(m_pState->GetCurGroupType() != GT_MAP)
return m_pState->SetError(ErrorMsg::UNEXPECTED_END_MAP);
EMITTER_STATE curState = m_pState->GetCurState();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(flowType == FT_BLOCK)
assert(curState == ES_DONE_WITH_BLOCK_MAP_VALUE);
else if(flowType == FT_FLOW) {
m_stream << "}";
// Note: flow maps are allowed to be empty
assert(curState == ES_DONE_WITH_FLOW_MAP_VALUE || curState == ES_WAITING_FOR_FLOW_MAP_ENTRY);
} else
assert(false);
m_pState->PopState();
m_pState->EndGroup(GT_MAP);
PostAtomicWrite();
}
// EmitKey
void Emitter::EmitKey()
{
if(!good())
return;
EMITTER_STATE curState = m_pState->GetCurState();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(curState != ES_WAITING_FOR_BLOCK_MAP_ENTRY && curState != ES_DONE_WITH_BLOCK_MAP_VALUE
&& curState != ES_WAITING_FOR_FLOW_MAP_ENTRY && curState != ES_DONE_WITH_FLOW_MAP_VALUE)
return m_pState->SetError(ErrorMsg::UNEXPECTED_KEY_TOKEN);
if(flowType == FT_BLOCK) {
if(curState == ES_DONE_WITH_BLOCK_MAP_VALUE)
m_stream << '\n';
unsigned curIndent = m_pState->GetCurIndent();
m_stream << IndentTo(curIndent);
m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_KEY);
} else if(flowType == FT_FLOW) {
if(curState == ES_DONE_WITH_FLOW_MAP_VALUE) {
m_stream << ',';
m_pState->RequireSeparation();
}
m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_KEY);
} else
assert(false);
if(m_pState->GetMapKeyFormat() == LongKey)
m_pState->StartLongKey();
else if(m_pState->GetMapKeyFormat() == Auto)
m_pState->StartSimpleKey();
else
assert(false);
}
// EmitValue
void Emitter::EmitValue()
{
if(!good())
return;
EMITTER_STATE curState = m_pState->GetCurState();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(curState != ES_DONE_WITH_BLOCK_MAP_KEY && curState != ES_DONE_WITH_FLOW_MAP_KEY)
return m_pState->SetError(ErrorMsg::UNEXPECTED_VALUE_TOKEN);
if(flowType == FT_BLOCK) {
if(m_pState->CurrentlyInLongKey())
m_stream << '\n';
m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_VALUE);
} else if(flowType == FT_FLOW) {
m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_VALUE);
} else
assert(false);
}
// *******************************************************************************************
// overloads of Write
Emitter& Emitter::Write(const std::string& str)
{
if(!good())
return *this;
// literal scalars must use long keys
if(m_pState->GetStringFormat() == Literal && m_pState->GetCurGroupFlowType() != FT_FLOW)
m_pState->StartLongKey();
PreAtomicWrite();
EmitSeparationIfNecessary();
EMITTER_MANIP strFmt = m_pState->GetStringFormat();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
unsigned curIndent = m_pState->GetCurIndent();
switch(strFmt) {
case Auto:
Utils::WriteString(m_stream, str, flowType == FT_FLOW);
break;
case SingleQuoted:
if(!Utils::WriteSingleQuotedString(m_stream, str)) {
m_pState->SetError(ErrorMsg::SINGLE_QUOTED_CHAR);
return *this;
}
break;
case DoubleQuoted:
Utils::WriteDoubleQuotedString(m_stream, str);
break;
case Literal:
if(flowType == FT_FLOW)
Utils::WriteString(m_stream, str, flowType == FT_FLOW);
else
Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent());
break;
default:
assert(false);
}
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(const char *str)
{
if(!good())
return *this;
return Write(std::string(str));
}
Emitter& Emitter::Write(int i)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
EMITTER_MANIP intFmt = m_pState->GetIntFormat();
std::stringstream str;
switch(intFmt) {
case Dec:
str << std::dec;
break;
case Hex:
str << std::hex;
break;
case Oct:
str << std::oct;
break;
default:
assert(false);
}
str << i;
m_stream << str.str();
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(bool b)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
// set up all possible bools to write
struct BoolName { std::string trueName, falseName; };
struct BoolFormatNames { BoolName upper, lower, camel; };
struct BoolTypes { BoolFormatNames yesNo, trueFalse, onOff; };
static const BoolTypes boolTypes = {
{ { "YES", "NO" }, { "yes", "no" }, { "Yes", "No" } },
{ { "TRUE", "FALSE" }, { "true", "false" }, { "True", "False" } },
{ { "ON", "OFF" }, { "on", "off" }, { "On", "Off" } }
};
// select the right one
EMITTER_MANIP boolFmt = m_pState->GetBoolFormat();
EMITTER_MANIP boolLengthFmt = m_pState->GetBoolLengthFormat();
EMITTER_MANIP boolCaseFmt = m_pState->GetBoolCaseFormat();
const BoolFormatNames& fmtNames = (boolFmt == YesNoBool ? boolTypes.yesNo : boolFmt == TrueFalseBool ? boolTypes.trueFalse : boolTypes.onOff);
const BoolName& boolName = (boolCaseFmt == UpperCase ? fmtNames.upper : boolCaseFmt == LowerCase ? fmtNames.lower : fmtNames.camel);
const std::string& name = (b ? boolName.trueName : boolName.falseName);
// and say it!
// TODO: should we disallow writing OnOffBool with ShortBool? (it'll just print "o" for both, which is silly)
if(boolLengthFmt == ShortBool)
m_stream << name[0];
else
m_stream << name;
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(float f)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
std::stringstream str;
str << f;
m_stream << str.str();
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(double d)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
std::stringstream str;
str << d;
m_stream << str.str();
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(const _Alias& alias)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
if(!Utils::WriteAlias(m_stream, alias.content)) {
m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this;
}
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(const _Anchor& anchor)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
if(!Utils::WriteAnchor(m_stream, anchor.content)) {
m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this;
}
m_pState->RequireSeparation();
// Note: no PostAtomicWrite() because we need another value for this node
return *this;
}
Emitter& Emitter::Write(const _Comment& comment)
{
if(!good())
return *this;
m_stream << Indentation(m_pState->GetPreCommentIndent());
Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent());
return *this;
}
}

263
src/emitterstate.cpp Normal file
View File

@@ -0,0 +1,263 @@
#include "emitterstate.h"
#include "exceptions.h"
namespace YAML
{
EmitterState::EmitterState(): m_isGood(true), m_curIndent(0), m_requiresSeparation(false)
{
// start up
m_stateStack.push(ES_WAITING_FOR_DOC);
// set default global manipulators
m_strFmt.set(Auto);
m_boolFmt.set(TrueFalseBool);
m_boolLengthFmt.set(LongBool);
m_boolCaseFmt.set(LowerCase);
m_intFmt.set(Dec);
m_indent.set(2);
m_preCommentIndent.set(2);
m_postCommentIndent.set(1);
m_seqFmt.set(Block);
m_mapFmt.set(Block);
m_mapKeyFmt.set(Auto);
}
EmitterState::~EmitterState()
{
while(!m_groups.empty())
_PopGroup();
}
std::auto_ptr <EmitterState::Group> EmitterState::_PopGroup()
{
if(m_groups.empty())
return std::auto_ptr <Group> (0);
std::auto_ptr <Group> pGroup(m_groups.top());
m_groups.pop();
return pGroup;
}
// SetLocalValue
// . We blindly tries to set all possible formatters to this value
// . Only the ones that make sense will be accepted
void EmitterState::SetLocalValue(EMITTER_MANIP value)
{
SetStringFormat(value, LOCAL);
SetBoolFormat(value, LOCAL);
SetBoolCaseFormat(value, LOCAL);
SetBoolLengthFormat(value, LOCAL);
SetIntFormat(value, LOCAL);
SetFlowType(GT_SEQ, value, LOCAL);
SetFlowType(GT_MAP, value, LOCAL);
SetMapKeyFormat(value, LOCAL);
}
void EmitterState::BeginGroup(GROUP_TYPE type)
{
unsigned lastIndent = (m_groups.empty() ? 0 : m_groups.top()->indent);
m_curIndent += lastIndent;
std::auto_ptr <Group> pGroup(new Group(type));
// transfer settings (which last until this group is done)
pGroup->modifiedSettings = m_modifiedSettings;
// set up group
pGroup->flow = GetFlowType(type);
pGroup->indent = GetIndent();
pGroup->usingLongKey = (GetMapKeyFormat() == LongKey ? true : false);
m_groups.push(pGroup.release());
}
void EmitterState::EndGroup(GROUP_TYPE type)
{
if(m_groups.empty())
return SetError(ErrorMsg::UNMATCHED_GROUP_TAG);
// get rid of the current group
{
std::auto_ptr <Group> pFinishedGroup = _PopGroup();
if(pFinishedGroup->type != type)
return SetError(ErrorMsg::UNMATCHED_GROUP_TAG);
}
// reset old settings
unsigned lastIndent = (m_groups.empty() ? 0 : m_groups.top()->indent);
assert(m_curIndent >= lastIndent);
m_curIndent -= lastIndent;
// some global settings that we changed may have been overridden
// by a local setting we just popped, so we need to restore them
m_globalModifiedSettings.restore();
}
GROUP_TYPE EmitterState::GetCurGroupType() const
{
if(m_groups.empty())
return GT_NONE;
return m_groups.top()->type;
}
FLOW_TYPE EmitterState::GetCurGroupFlowType() const
{
if(m_groups.empty())
return FT_NONE;
return (m_groups.top()->flow == Flow ? FT_FLOW : FT_BLOCK);
}
bool EmitterState::CurrentlyInLongKey()
{
if(m_groups.empty())
return false;
return m_groups.top()->usingLongKey;
}
void EmitterState::StartLongKey()
{
if(!m_groups.empty())
m_groups.top()->usingLongKey = true;
}
void EmitterState::StartSimpleKey()
{
if(!m_groups.empty())
m_groups.top()->usingLongKey = false;
}
void EmitterState::ClearModifiedSettings()
{
m_modifiedSettings.clear();
}
bool EmitterState::SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case Auto:
case SingleQuoted:
case DoubleQuoted:
case Literal:
_Set(m_strFmt, value, scope);
return true;
default:
return false;
}
}
bool EmitterState::SetBoolFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case OnOffBool:
case TrueFalseBool:
case YesNoBool:
_Set(m_boolFmt, value, scope);
return true;
default:
return false;
}
}
bool EmitterState::SetBoolLengthFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case LongBool:
case ShortBool:
_Set(m_boolLengthFmt, value, scope);
return true;
default:
return false;
}
}
bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case UpperCase:
case LowerCase:
case CamelCase:
_Set(m_boolCaseFmt, value, scope);
return true;
default:
return false;
}
}
bool EmitterState::SetIntFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case Dec:
case Hex:
case Oct:
_Set(m_intFmt, value, scope);
return true;
default:
return false;
}
}
bool EmitterState::SetIndent(unsigned value, FMT_SCOPE scope)
{
if(value == 0)
return false;
_Set(m_indent, value, scope);
return true;
}
bool EmitterState::SetPreCommentIndent(unsigned value, FMT_SCOPE scope)
{
if(value == 0)
return false;
_Set(m_preCommentIndent, value, scope);
return true;
}
bool EmitterState::SetPostCommentIndent(unsigned value, FMT_SCOPE scope)
{
if(value == 0)
return false;
_Set(m_postCommentIndent, value, scope);
return true;
}
bool EmitterState::SetFlowType(GROUP_TYPE groupType, EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case Block:
case Flow:
_Set(groupType == GT_SEQ ? m_seqFmt : m_mapFmt, value, scope);
return true;
default:
return false;
}
}
EMITTER_MANIP EmitterState::GetFlowType(GROUP_TYPE groupType) const
{
// force flow style if we're currently in a flow
FLOW_TYPE flowType = GetCurGroupFlowType();
if(flowType == FT_FLOW)
return Flow;
// otherwise, go with what's asked of use
return (groupType == GT_SEQ ? m_seqFmt.get() : m_mapFmt.get());
}
bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case Auto:
case LongKey:
_Set(m_mapKeyFmt, value, scope);
return true;
default:
return false;
}
}
}

195
src/emitterstate.h Normal file
View File

@@ -0,0 +1,195 @@
#pragma once
#include "setting.h"
#include "emittermanip.h"
#include <cassert>
#include <vector>
#include <stack>
#include <memory>
namespace YAML
{
enum FMT_SCOPE {
LOCAL,
GLOBAL
};
enum GROUP_TYPE {
GT_NONE,
GT_SEQ,
GT_MAP
};
enum FLOW_TYPE {
FT_NONE,
FT_FLOW,
FT_BLOCK
};
enum NODE_STATE {
NS_START,
NS_READY_FOR_ATOM,
NS_END
};
enum EMITTER_STATE {
ES_WAITING_FOR_DOC,
ES_WRITING_DOC,
ES_DONE_WITH_DOC,
// block seq
ES_WAITING_FOR_BLOCK_SEQ_ENTRY,
ES_WRITING_BLOCK_SEQ_ENTRY,
ES_DONE_WITH_BLOCK_SEQ_ENTRY,
// flow seq
ES_WAITING_FOR_FLOW_SEQ_ENTRY,
ES_WRITING_FLOW_SEQ_ENTRY,
ES_DONE_WITH_FLOW_SEQ_ENTRY,
// block map
ES_WAITING_FOR_BLOCK_MAP_ENTRY,
ES_WAITING_FOR_BLOCK_MAP_KEY,
ES_WRITING_BLOCK_MAP_KEY,
ES_DONE_WITH_BLOCK_MAP_KEY,
ES_WAITING_FOR_BLOCK_MAP_VALUE,
ES_WRITING_BLOCK_MAP_VALUE,
ES_DONE_WITH_BLOCK_MAP_VALUE,
// flow map
ES_WAITING_FOR_FLOW_MAP_ENTRY,
ES_WAITING_FOR_FLOW_MAP_KEY,
ES_WRITING_FLOW_MAP_KEY,
ES_DONE_WITH_FLOW_MAP_KEY,
ES_WAITING_FOR_FLOW_MAP_VALUE,
ES_WRITING_FLOW_MAP_VALUE,
ES_DONE_WITH_FLOW_MAP_VALUE,
};
class EmitterState
{
public:
EmitterState();
~EmitterState();
// basic state checking
bool good() const { return m_isGood; }
const std::string GetLastError() const { return m_lastError; }
void SetError(const std::string& error) { m_isGood = false; m_lastError = error; }
// main state of the machine
EMITTER_STATE GetCurState() const { return m_stateStack.top(); }
void SwitchState(EMITTER_STATE state) { PopState(); PushState(state); }
void PushState(EMITTER_STATE state) { m_stateStack.push(state); }
void PopState() { m_stateStack.pop(); }
void SetLocalValue(EMITTER_MANIP value);
// group handling
void BeginGroup(GROUP_TYPE type);
void EndGroup(GROUP_TYPE type);
GROUP_TYPE GetCurGroupType() const;
FLOW_TYPE GetCurGroupFlowType() const;
int GetCurIndent() const { return m_curIndent; }
bool CurrentlyInLongKey();
void StartLongKey();
void StartSimpleKey();
bool RequiresSeparation() const { return m_requiresSeparation; }
void RequireSeparation() { m_requiresSeparation = true; }
void UnsetSeparation() { m_requiresSeparation = false; }
void ClearModifiedSettings();
// formatters
bool SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetStringFormat() const { return m_strFmt.get(); }
bool SetBoolFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetBoolFormat() const { return m_boolFmt.get(); }
bool SetBoolLengthFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetBoolLengthFormat() const { return m_boolLengthFmt.get(); }
bool SetBoolCaseFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetBoolCaseFormat() const { return m_boolCaseFmt.get(); }
bool SetIntFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetIntFormat() const { return m_intFmt.get(); }
bool SetIndent(unsigned value, FMT_SCOPE scope);
int GetIndent() const { return m_indent.get(); }
bool SetPreCommentIndent(unsigned value, FMT_SCOPE scope);
int GetPreCommentIndent() const { return m_preCommentIndent.get(); }
bool SetPostCommentIndent(unsigned value, FMT_SCOPE scope);
int GetPostCommentIndent() const { return m_postCommentIndent.get(); }
bool SetFlowType(GROUP_TYPE groupType, EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetFlowType(GROUP_TYPE groupType) const;
bool SetMapKeyFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetMapKeyFormat() const { return m_mapKeyFmt.get(); }
private:
template <typename T>
void _Set(Setting<T>& fmt, T value, FMT_SCOPE scope);
private:
// basic state ok?
bool m_isGood;
std::string m_lastError;
// other state
std::stack <EMITTER_STATE> m_stateStack;
Setting <EMITTER_MANIP> m_strFmt;
Setting <EMITTER_MANIP> m_boolFmt;
Setting <EMITTER_MANIP> m_boolLengthFmt;
Setting <EMITTER_MANIP> m_boolCaseFmt;
Setting <EMITTER_MANIP> m_intFmt;
Setting <unsigned> m_indent;
Setting <unsigned> m_preCommentIndent, m_postCommentIndent;
Setting <EMITTER_MANIP> m_seqFmt;
Setting <EMITTER_MANIP> m_mapFmt;
Setting <EMITTER_MANIP> m_mapKeyFmt;
SettingChanges m_modifiedSettings;
SettingChanges m_globalModifiedSettings;
struct Group {
Group(GROUP_TYPE type_): type(type_), usingLongKey(false), indent(0) {}
GROUP_TYPE type;
EMITTER_MANIP flow;
bool usingLongKey;
int indent;
SettingChanges modifiedSettings;
};
std::auto_ptr <Group> _PopGroup();
std::stack <Group *> m_groups;
unsigned m_curIndent;
bool m_requiresSeparation;
};
template <typename T>
void EmitterState::_Set(Setting<T>& fmt, T value, FMT_SCOPE scope) {
switch(scope) {
case LOCAL:
m_modifiedSettings.push(fmt.set(value));
break;
case GLOBAL:
fmt.set(value);
m_globalModifiedSettings.push(fmt.set(value)); // this pushes an identity set, so when we restore,
// it restores to the value here, and not the previous one
break;
default:
assert(false);
}
}
}

143
src/emitterutils.cpp Normal file
View File

@@ -0,0 +1,143 @@
#include "emitterutils.h"
#include "exp.h"
#include "indentation.h"
#include "exceptions.h"
#include <sstream>
namespace YAML
{
namespace Utils
{
namespace {
bool IsPrintable(char ch) {
return (0x20 <= ch && ch <= 0x7E);
}
bool IsValidPlainScalar(const std::string& str, bool inFlow) {
// first check the start
const RegEx& start = (inFlow ? Exp::PlainScalarInFlow : Exp::PlainScalar);
if(!start.Matches(str))
return false;
// and check the end for plain whitespace (which can't be faithfully kept in a plain scalar)
if(!str.empty() && *str.rbegin() == ' ')
return false;
// then check until something is disallowed
const RegEx& disallowed = (inFlow ? Exp::EndScalarInFlow : Exp::EndScalar)
|| (Exp::BlankOrBreak + Exp::Comment)
|| (!Exp::Printable)
|| Exp::Break
|| Exp::Tab;
Buffer buffer(&str[0], str.size());
while(buffer.size) {
if(disallowed.Matches(buffer))
return false;
++buffer;
}
return true;
}
}
bool WriteString(ostream& out, const std::string& str, bool inFlow)
{
if(IsValidPlainScalar(str, inFlow)) {
out << str;
return true;
} else
return WriteDoubleQuotedString(out, str);
}
bool WriteSingleQuotedString(ostream& out, const std::string& str)
{
out << "'";
for(unsigned i=0;i<str.size();i++) {
char ch = str[i];
if(!IsPrintable(ch))
return false;
if(ch == '\'')
out << "''";
else
out << ch;
}
out << "'";
return true;
}
bool WriteDoubleQuotedString(ostream& out, const std::string& str)
{
out << "\"";
for(unsigned i=0;i<str.size();i++) {
char ch = str[i];
if(IsPrintable(ch)) {
if(ch == '\"')
out << "\\\"";
else if(ch == '\\')
out << "\\\\";
else
out << ch;
} else {
// TODO: for the common escaped characters, give their usual symbol
std::stringstream str;
str << "\\x" << std::hex << static_cast <int>(ch);
out << str.str();
}
}
out << "\"";
return true;
}
bool WriteLiteralString(ostream& out, const std::string& str, int indent)
{
out << "|\n";
out << IndentTo(indent);
for(unsigned i=0;i<str.size();i++) {
if(str[i] == '\n')
out << "\n" << IndentTo(indent);
else
out << str[i];
}
return true;
}
bool WriteComment(ostream& out, const std::string& str, int postCommentIndent)
{
unsigned curIndent = out.col();
out << "#" << Indentation(postCommentIndent);
for(unsigned i=0;i<str.size();i++) {
if(str[i] == '\n')
out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
else
out << str[i];
}
return true;
}
bool WriteAlias(ostream& out, const std::string& str)
{
out << "*";
for(unsigned i=0;i<str.size();i++) {
if(!IsPrintable(str[i]) || str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\r')
return false;
out << str[i];
}
return true;
}
bool WriteAnchor(ostream& out, const std::string& str)
{
out << "&";
for(unsigned i=0;i<str.size();i++) {
if(!IsPrintable(str[i]) || str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\r')
return false;
out << str[i];
}
return true;
}
}
}

18
src/emitterutils.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include "ostream.h"
#include <string>
namespace YAML
{
namespace Utils
{
bool WriteString(ostream& out, const std::string& str, bool inFlow);
bool WriteSingleQuotedString(ostream& out, const std::string& str);
bool WriteDoubleQuotedString(ostream& out, const std::string& str);
bool WriteLiteralString(ostream& out, const std::string& str, int indent);
bool WriteComment(ostream& out, const std::string& str, int postCommentIndent);
bool WriteAlias(ostream& out, const std::string& str);
bool WriteAnchor(ostream& out, const std::string& str);
}
}

View File

@@ -13,13 +13,16 @@ namespace YAML
namespace Exp namespace Exp
{ {
// misc // misc
const RegEx Blank = RegEx(' ') || RegEx('\t'); const RegEx Space = RegEx(' ');
const RegEx Tab = RegEx('\t');
const RegEx Blank = Space || Tab;
const RegEx Break = RegEx('\n') || RegEx("\r\n"); const RegEx Break = RegEx('\n') || RegEx("\r\n");
const RegEx BlankOrBreak = Blank || Break; const RegEx BlankOrBreak = Blank || Break;
const RegEx Digit = RegEx('0', '9'); const RegEx Digit = RegEx('0', '9');
const RegEx Alpha = RegEx('a', 'z') || RegEx('A', 'Z'); const RegEx Alpha = RegEx('a', 'z') || RegEx('A', 'Z');
const RegEx AlphaNumeric = Alpha || Digit; const RegEx AlphaNumeric = Alpha || Digit;
const RegEx Hex = Digit || RegEx('A', 'F') || RegEx('a', 'f'); const RegEx Hex = Digit || RegEx('A', 'F') || RegEx('a', 'f');
const RegEx Printable = RegEx(0x20, 0x7E);
// actual tags // actual tags

30
src/indentation.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include "ostream.h"
#include <iostream>
namespace YAML
{
struct Indentation {
Indentation(unsigned n_): n(n_) {}
unsigned n;
};
inline ostream& operator << (ostream& out, const Indentation& indent) {
for(unsigned i=0;i<indent.n;i++)
out << ' ';
return out;
}
struct IndentTo {
IndentTo(unsigned n_): n(n_) {}
unsigned n;
};
inline ostream& operator << (ostream& out, const IndentTo& indent) {
while(out.col() < indent.n)
out << ' ';
return out;
}
}

63
src/ostream.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "ostream.h"
#include <cstring>
namespace YAML
{
ostream::ostream(): m_buffer(0), m_pos(0), m_size(0), m_row(0), m_col(0)
{
reserve(1024);
}
ostream::~ostream()
{
delete [] m_buffer;
}
void ostream::reserve(unsigned size)
{
if(size <= m_size)
return;
char *newBuffer = new char[size];
std::memset(newBuffer, 0, size * sizeof(char));
std::memcpy(newBuffer, m_buffer, m_size * sizeof(char));
delete [] m_buffer;
m_buffer = newBuffer;
m_size = size;
}
void ostream::put(char ch)
{
if(m_pos >= m_size - 1) // an extra space for the NULL terminator
reserve(m_size * 2);
m_buffer[m_pos] = ch;
m_pos++;
if(ch == '\n') {
m_row++;
m_col = 0;
} else
m_col++;
}
ostream& operator << (ostream& out, const char *str)
{
unsigned length = std::strlen(str);
for(unsigned i=0;i<length;i++)
out.put(str[i]);
return out;
}
ostream& operator << (ostream& out, const std::string& str)
{
out << str.c_str();
return out;
}
ostream& operator << (ostream& out, char ch)
{
out.put(ch);
return out;
}
}

97
src/setting.h Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#include <memory>
#include <vector>
#include "noncopyable.h"
namespace YAML
{
class SettingChangeBase;
template <typename T>
class Setting
{
public:
Setting(): m_value() {}
const T get() const { return m_value; }
std::auto_ptr <SettingChangeBase> set(const T& value);
void restore(const Setting<T>& oldSetting) {
m_value = oldSetting.get();
}
private:
T m_value;
};
class SettingChangeBase
{
public:
virtual ~SettingChangeBase() {}
virtual void pop() = 0;
};
template <typename T>
class SettingChange: public SettingChangeBase
{
public:
SettingChange(Setting<T> *pSetting): m_pCurSetting(pSetting) {
// copy old setting to save its state
m_oldSetting = *pSetting;
}
virtual void pop() {
m_pCurSetting->restore(m_oldSetting);
}
private:
Setting<T> *m_pCurSetting;
Setting<T> m_oldSetting;
};
template <typename T>
inline std::auto_ptr <SettingChangeBase> Setting<T>::set(const T& value) {
std::auto_ptr <SettingChangeBase> pChange(new SettingChange<T> (this));
m_value = value;
return pChange;
}
class SettingChanges: private noncopyable
{
public:
SettingChanges() {}
~SettingChanges() { clear(); }
void clear() {
restore();
for(setting_changes::const_iterator it=m_settingChanges.begin();it!=m_settingChanges.end();++it)
delete *it;
m_settingChanges.clear();
}
void restore() {
for(setting_changes::const_iterator it=m_settingChanges.begin();it!=m_settingChanges.end();++it)
(*it)->pop();
}
void push(std::auto_ptr <SettingChangeBase> pSettingChange) {
m_settingChanges.push_back(pSettingChange.release());
}
// like std::auto_ptr - assignment is transfer of ownership
SettingChanges& operator = (SettingChanges& rhs) {
if(this == &rhs)
return *this;
clear();
m_settingChanges = rhs.m_settingChanges;
rhs.m_settingChanges.clear();
return *this;
}
private:
typedef std::vector <SettingChangeBase *> setting_changes;
setting_changes m_settingChanges;
};
}

View File

@@ -7,14 +7,15 @@ namespace YAML
{ {
// a simple buffer wrapper that knows how big it is // a simple buffer wrapper that knows how big it is
struct Buffer { struct Buffer {
Buffer(char *b, int s): buffer(b), size(s) {} Buffer(const char *b, int s): buffer(b), size(s) {}
operator bool() const { return size > 0; } operator bool() const { return size > 0; }
bool operator !() const { return !static_cast <bool> (*this); } bool operator !() const { return !static_cast <bool> (*this); }
char operator [] (int i) const { return buffer[i]; } char operator [] (int i) const { return buffer[i]; }
const Buffer operator + (int offset) const { return Buffer(buffer + offset, size - offset); } const Buffer operator + (int offset) const { return Buffer(buffer + offset, size - offset); }
Buffer& operator ++ () { ++buffer; --size; return *this; }
char *buffer; const char *buffer;
int size; int size;
}; };

View File

@@ -4,6 +4,9 @@
#include <iostream> #include <iostream>
#include <cstring> #include <cstring>
#include "emitter.h"
#include "stlemitter.h"
void run() void run()
{ {
std::ifstream fin("tests/test.yaml"); std::ifstream fin("tests/test.yaml");
@@ -15,6 +18,9 @@ void run()
parser.GetNextDocument(doc); parser.GetNextDocument(doc);
std::cout << doc; std::cout << doc;
} }
// try some output
YAML::Emitter out;
} }
int main(int argc, char **argv) int main(int argc, char **argv)

View File

@@ -1,6 +1,9 @@
#include "yaml.h" #include "yaml.h"
#include "tests.h" #include "tests.h"
#include "parser.h" #include "parser.h"
#include "emitter.h"
#include "stlemitter.h"
#include "exceptions.h"
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
@@ -26,6 +29,9 @@ namespace Test
} else } else
std::cout << "Inout test passed: " << files[i] << "\n"; std::cout << "Inout test passed: " << files[i] << "\n";
} }
if(!RunEmitterTests())
passed = false;
if(passed) if(passed)
std::cout << "All tests passed!\n"; std::cout << "All tests passed!\n";
@@ -86,4 +92,601 @@ namespace Test
return true; return true;
} }
////////////////////////////////////////////////////////////////////////////////////////
// Emitter tests
namespace {
void RunTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
YAML::Emitter out;
std::string desiredOutput;
test(out, desiredOutput);
std::stringstream output;
out.WriteToStream(output);
if(output.str() == desiredOutput) {
std::cout << "Test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Test failed: " << name << "\n";
std::cout << "Output:\n";
std::cout << output.str() << "<<<\n";
std::cout << "Desired output:\n";
std::cout << desiredOutput << "<<<\n";
}
}
void RunErrorTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
YAML::Emitter out;
std::string desiredError;
test(out, desiredError);
std::string lastError = out.GetLastError();
if(!out.good() && lastError == desiredError) {
std::cout << "Test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Test failed: " << name << "\n";
if(out.good())
std::cout << "No error detected\n";
else
std::cout << "Detected error: " << lastError << "\n";
std::cout << "Expected error: " << desiredError << "\n";
}
}
}
bool RunEmitterTests()
{
bool passed = true;
RunTest(&Emitter::SimpleScalar, "simple scalar", passed);
RunTest(&Emitter::SimpleSeq, "simple seq", passed);
RunTest(&Emitter::SimpleFlowSeq, "simple flow seq", passed);
RunTest(&Emitter::EmptyFlowSeq, "empty flow seq", passed);
RunTest(&Emitter::NestedBlockSeq, "nested block seq", passed);
RunTest(&Emitter::NestedFlowSeq, "nested flow seq", passed);
RunTest(&Emitter::SimpleMap, "simple map", passed);
RunTest(&Emitter::SimpleFlowMap, "simple flow map", passed);
RunTest(&Emitter::MapAndList, "map and list", passed);
RunTest(&Emitter::ListAndMap, "list and map", passed);
RunTest(&Emitter::NestedBlockMap, "nested block map", passed);
RunTest(&Emitter::NestedFlowMap, "nested flow map", passed);
RunTest(&Emitter::MapListMix, "map list mix", passed);
RunTest(&Emitter::SimpleLongKey, "simple long key", passed);
RunTest(&Emitter::SingleLongKey, "single long key", passed);
RunTest(&Emitter::ComplexLongKey, "complex long key", passed);
RunTest(&Emitter::AutoLongKey, "auto long key", passed);
RunTest(&Emitter::ScalarFormat, "scalar format", passed);
RunTest(&Emitter::AutoLongKeyScalar, "auto long key scalar", passed);
RunTest(&Emitter::LongKeyFlowMap, "long key flow map", passed);
RunTest(&Emitter::BlockMapAsKey, "block map as key", passed);
RunTest(&Emitter::AliasAndAnchor, "alias and anchor", passed);
RunTest(&Emitter::ComplexDoc, "complex doc", passed);
RunTest(&Emitter::STLContainers, "STL containers", passed);
RunTest(&Emitter::SimpleComment, "simple comment", passed);
RunTest(&Emitter::MultiLineComment, "multi-line comment", passed);
RunTest(&Emitter::ComplexComments, "complex comments", passed);
RunTest(&Emitter::Indentation, "indentation", passed);
RunTest(&Emitter::SimpleGlobalSettings, "simple global settings", passed);
RunTest(&Emitter::ComplexGlobalSettings, "complex global settings", passed);
RunErrorTest(&Emitter::ExtraEndSeq, "extra EndSeq", passed);
RunErrorTest(&Emitter::ExtraEndMap, "extra EndMap", passed);
RunErrorTest(&Emitter::BadSingleQuoted, "bad single quoted string", passed);
RunErrorTest(&Emitter::InvalidAnchor, "invalid anchor", passed);
RunErrorTest(&Emitter::InvalidAlias, "invalid alias", passed);
RunErrorTest(&Emitter::MissingKey, "missing key", passed);
RunErrorTest(&Emitter::MissingValue, "missing value", passed);
RunErrorTest(&Emitter::UnexpectedKey, "unexpected key", passed);
RunErrorTest(&Emitter::UnexpectedValue, "unexpected value", passed);
return passed;
}
namespace Emitter {
////////////////////////////////////////////////////////////////////////////////////////////////////////
// correct emitting
void SimpleScalar(YAML::Emitter& out, std::string& desiredOutput) {
out << "Hello, World!";
desiredOutput = "Hello, World!";
}
void SimpleSeq(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginSeq;
out << "eggs";
out << "bread";
out << "milk";
out << YAML::EndSeq;
desiredOutput = "- eggs\n- bread\n- milk";
}
void SimpleFlowSeq(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::Flow;
out << YAML::BeginSeq;
out << "Larry";
out << "Curly";
out << "Moe";
out << YAML::EndSeq;
desiredOutput = "[Larry, Curly, Moe]";
}
void EmptyFlowSeq(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::Flow;
out << YAML::BeginSeq;
out << YAML::EndSeq;
desiredOutput = "[]";
}
void NestedBlockSeq(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginSeq;
out << "item 1";
out << YAML::BeginSeq << "subitem 1" << "subitem 2" << YAML::EndSeq;
out << YAML::EndSeq;
desiredOutput = "- item 1\n-\n - subitem 1\n - subitem 2";
}
void NestedFlowSeq(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginSeq;
out << "one";
out << YAML::Flow << YAML::BeginSeq << "two" << "three" << YAML::EndSeq;
out << YAML::EndSeq;
desiredOutput = "- one\n- [two, three]";
}
void SimpleMap(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Ryan Braun";
out << YAML::Key << "position";
out << YAML::Value << "3B";
out << YAML::EndMap;
desiredOutput = "name: Ryan Braun\nposition: 3B";
}
void SimpleFlowMap(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::Flow;
out << YAML::BeginMap;
out << YAML::Key << "shape";
out << YAML::Value << "square";
out << YAML::Key << "color";
out << YAML::Value << "blue";
out << YAML::EndMap;
desiredOutput = "{shape: square, color: blue}";
}
void MapAndList(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Barack Obama";
out << YAML::Key << "children";
out << YAML::Value << YAML::BeginSeq << "Sasha" << "Malia" << YAML::EndSeq;
out << YAML::EndMap;
desiredOutput = "name: Barack Obama\nchildren:\n - Sasha\n - Malia";
}
void ListAndMap(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginSeq;
out << "item 1";
out << YAML::BeginMap;
out << YAML::Key << "pens" << YAML::Value << 8;
out << YAML::Key << "pencils" << YAML::Value << 14;
out << YAML::EndMap;
out << "item 2";
out << YAML::EndSeq;
desiredOutput = "- item 1\n-\n pens: 8\n pencils: 14\n- item 2";
}
void NestedBlockMap(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Fred";
out << YAML::Key << "grades";
out << YAML::Value;
out << YAML::BeginMap;
out << YAML::Key << "algebra" << YAML::Value << "A";
out << YAML::Key << "physics" << YAML::Value << "C+";
out << YAML::Key << "literature" << YAML::Value << "B";
out << YAML::EndMap;
out << YAML::EndMap;
desiredOutput = "name: Fred\ngrades:\n algebra: A\n physics: C+\n literature: B";
}
void NestedFlowMap(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::Flow;
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Fred";
out << YAML::Key << "grades";
out << YAML::Value;
out << YAML::BeginMap;
out << YAML::Key << "algebra" << YAML::Value << "A";
out << YAML::Key << "physics" << YAML::Value << "C+";
out << YAML::Key << "literature" << YAML::Value << "B";
out << YAML::EndMap;
out << YAML::EndMap;
desiredOutput = "{name: Fred, grades: {algebra: A, physics: C+, literature: B}}";
}
void MapListMix(YAML::Emitter& out, std::string& desiredOutput) {
out << YAML::BeginMap;
out << YAML::Key << "name";
out << YAML::Value << "Bob";
out << YAML::Key << "position";
out << YAML::Value;
out << YAML::Flow << YAML::BeginSeq << 2 << 4 << YAML::EndSeq;
out << YAML::Key << "invincible" << YAML::Value << YAML::OnOffBool << false;
out << YAML::EndMap;
desiredOutput = "name: Bob\nposition: [2, 4]\ninvincible: off";
}
void SimpleLongKey(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::LongKey;
out << YAML::BeginMap;
out << YAML::Key << "height";
out << YAML::Value << "5'9\"";
out << YAML::Key << "weight";
out << YAML::Value << 145;
out << YAML::EndMap;
desiredOutput = "? height\n: 5'9\"\n? weight\n: 145";
}
void SingleLongKey(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key << "age";
out << YAML::Value << "24";
out << YAML::LongKey << YAML::Key << "height";
out << YAML::Value << "5'9\"";
out << YAML::Key << "weight";
out << YAML::Value << 145;
out << YAML::EndMap;
desiredOutput = "age: 24\n? height\n: 5'9\"\nweight: 145";
}
void ComplexLongKey(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::LongKey;
out << YAML::BeginMap;
out << YAML::Key << YAML::BeginSeq << 1 << 3 << YAML::EndSeq;
out << YAML::Value << "monster";
out << YAML::Key << YAML::Flow << YAML::BeginSeq << 2 << 0 << YAML::EndSeq;
out << YAML::Value << "demon";
out << YAML::EndMap;
desiredOutput = "?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon";
}
void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key << YAML::BeginSeq << 1 << 3 << YAML::EndSeq;
out << YAML::Value << "monster";
out << YAML::Key << YAML::Flow << YAML::BeginSeq << 2 << 0 << YAML::EndSeq;
out << YAML::Value << "demon";
out << YAML::Key << "the origin";
out << YAML::Value << "angel";
out << YAML::EndMap;
desiredOutput = "?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon\nthe origin: angel";
}
void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << "simple scalar";
out << YAML::SingleQuoted << "explicit single-quoted scalar";
out << YAML::DoubleQuoted << "explicit double-quoted scalar";
out << "auto-detected\ndouble-quoted scalar";
out << "a non-\"auto-detected\" double-quoted scalar";
out << YAML::Literal << "literal scalar\nthat may span\nmany, many\nlines and have \"whatever\" crazy\tsymbols that we like";
out << YAML::EndSeq;
desiredOutput = "- simple scalar\n- 'explicit single-quoted scalar'\n- \"explicit double-quoted scalar\"\n- \"auto-detected\\xadouble-quoted scalar\"\n- a non-\"auto-detected\" double-quoted scalar\n- |\n literal scalar\n that may span\n many, many\n lines and have \"whatever\" crazy\tsymbols that we like";
}
void AutoLongKeyScalar(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key << YAML::Literal << "multi-line\nscalar";
out << YAML::Value << "and its value";
out << YAML::EndMap;
desiredOutput = "? |\n multi-line\n scalar\n: and its value";
}
void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Flow;
out << YAML::BeginMap;
out << YAML::Key << "simple key";
out << YAML::Value << "and value";
out << YAML::LongKey << YAML::Key << "long key";
out << YAML::Value << "and its value";
out << YAML::EndMap;
desiredOutput = "{simple key: and value, ? long key: and its value}";
}
void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key;
out << YAML::BeginMap;
out << YAML::Key << "key" << YAML::Value << "value";
out << YAML::Key << "next key" << YAML::Value << "next value";
out << YAML::EndMap;
out << YAML::Value;
out << "total value";
out << YAML::EndMap;
desiredOutput = "?\n key: value\n next key: next value\n: total value";
}
void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << YAML::Anchor("fred");
out << YAML::BeginMap;
out << YAML::Key << "name" << YAML::Value << "Fred";
out << YAML::Key << "age" << YAML::Value << 42;
out << YAML::EndMap;
out << YAML::Alias("fred");
out << YAML::EndSeq;
desiredOutput = "- &fred\n name: Fred\n age: 42\n- *fred";
}
void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key << "receipt";
out << YAML::Value << "Oz-Ware Purchase Invoice";
out << YAML::Key << "date";
out << YAML::Value << "2007-08-06";
out << YAML::Key << "customer";
out << YAML::Value;
out << YAML::BeginMap;
out << YAML::Key << "given";
out << YAML::Value << "Dorothy";
out << YAML::Key << "family";
out << YAML::Value << "Gale";
out << YAML::EndMap;
out << YAML::Key << "items";
out << YAML::Value;
out << YAML::BeginSeq;
out << YAML::BeginMap;
out << YAML::Key << "part_no";
out << YAML::Value << "A4786";
out << YAML::Key << "descrip";
out << YAML::Value << "Water Bucket (Filled)";
out << YAML::Key << "price";
out << YAML::Value << 1.47;
out << YAML::Key << "quantity";
out << YAML::Value << 4;
out << YAML::EndMap;
out << YAML::BeginMap;
out << YAML::Key << "part_no";
out << YAML::Value << "E1628";
out << YAML::Key << "descrip";
out << YAML::Value << "High Heeled \"Ruby\" Slippers";
out << YAML::Key << "price";
out << YAML::Value << 100.27;
out << YAML::Key << "quantity";
out << YAML::Value << 1;
out << YAML::EndMap;
out << YAML::EndSeq;
out << YAML::Key << "bill-to";
out << YAML::Value << YAML::Anchor("id001");
out << YAML::BeginMap;
out << YAML::Key << "street";
out << YAML::Value << YAML::Literal << "123 Tornado Alley\nSuite 16";
out << YAML::Key << "city";
out << YAML::Value << "East Westville";
out << YAML::Key << "state";
out << YAML::Value << "KS";
out << YAML::EndMap;
out << YAML::Key << "ship-to";
out << YAML::Value << YAML::Alias("id001");
out << YAML::EndMap;
desiredOutput = "receipt: Oz-Ware Purchase Invoice\ndate: 2007-08-06\ncustomer:\n given: Dorothy\n family: Gale\nitems:\n -\n part_no: A4786\n descrip: Water Bucket (Filled)\n price: 1.47\n quantity: 4\n -\n part_no: E1628\n descrip: High Heeled \"Ruby\" Slippers\n price: 100.27\n quantity: 1\nbill-to: &id001\n street: |\n 123 Tornado Alley\n Suite 16\n city: East Westville\n state: KS\nship-to: *id001";
}
void STLContainers(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
std::vector <int> primes;
primes.push_back(2);
primes.push_back(3);
primes.push_back(5);
primes.push_back(7);
primes.push_back(11);
primes.push_back(13);
out << YAML::Flow << primes;
std::map <std::string, int> ages;
ages["Daniel"] = 26;
ages["Jesse"] = 24;
out << ages;
out << YAML::EndSeq;
desiredOutput = "- [2, 3, 5, 7, 11, 13]\n-\n Daniel: 26\n Jesse: 24";
}
void SimpleComment(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key << "method";
out << YAML::Value << "least squares" << YAML::Comment("should we change this method?");
out << YAML::EndMap;
desiredOutput = "method: least squares # should we change this method?";
}
void MultiLineComment(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << "item 1" << YAML::Comment("really really long\ncomment that couldn't possibly\nfit on one line");
out << "item 2";
out << YAML::EndSeq;
desiredOutput = "- item 1 # really really long\n # comment that couldn't possibly\n # fit on one line\n- item 2";
}
void ComplexComments(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::LongKey << YAML::Key << "long key" << YAML::Comment("long key");
out << YAML::Value << "value";
out << YAML::EndMap;
desiredOutput = "? long key # long key\n: value";
}
void Indentation(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Indent(4);
out << YAML::BeginSeq;
out << YAML::BeginMap;
out << YAML::Key << "key 1" << YAML::Value << "value 1";
out << YAML::Key << "key 2" << YAML::Value << YAML::BeginSeq << "a" << "b" << "c" << YAML::EndSeq;
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "-\n key 1: value 1\n key 2:\n - a\n - b\n - c";
}
void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput)
{
out.SetIndent(4);
out.SetMapFormat(YAML::LongKey);
out << YAML::BeginSeq;
out << YAML::BeginMap;
out << YAML::Key << "key 1" << YAML::Value << "value 1";
out << YAML::Key << "key 2" << YAML::Value << YAML::Flow << YAML::BeginSeq << "a" << "b" << "c" << YAML::EndSeq;
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "-\n ? key 1\n : value 1\n ? key 2\n : [a, b, c]";
}
void ComplexGlobalSettings(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << YAML::Block;
out << YAML::BeginMap;
out << YAML::Key << "key 1" << YAML::Value << "value 1";
out << YAML::Key << "key 2" << YAML::Value;
out.SetSeqFormat(YAML::Flow);
out << YAML::BeginSeq << "a" << "b" << "c" << YAML::EndSeq;
out << YAML::EndMap;
out << YAML::BeginMap;
out << YAML::Key << YAML::BeginSeq << 1 << 2 << YAML::EndSeq;
out << YAML::Value << YAML::BeginMap << YAML::Key << "a" << YAML::Value << "b" << YAML::EndMap;
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "-\n key 1: value 1\n key 2: [a, b, c]\n-\n ? [1, 2]\n :\n a: b";
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// incorrect emitting
void ExtraEndSeq(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::UNEXPECTED_END_SEQ;
out << YAML::BeginSeq;
out << "Hello";
out << "World";
out << YAML::EndSeq;
out << YAML::EndSeq;
}
void ExtraEndMap(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::UNEXPECTED_END_MAP;
out << YAML::BeginMap;
out << YAML::Key << "Hello" << YAML::Value << "World";
out << YAML::EndMap;
out << YAML::EndMap;
}
void BadSingleQuoted(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::SINGLE_QUOTED_CHAR;
out << YAML::SingleQuoted << "Hello\nWorld";
}
void InvalidAnchor(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::INVALID_ANCHOR;
out << YAML::BeginSeq;
out << YAML::Anchor("new\nline") << "Test";
out << YAML::EndSeq;
}
void InvalidAlias(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::INVALID_ALIAS;
out << YAML::BeginSeq;
out << YAML::Alias("new\nline");
out << YAML::EndSeq;
}
void MissingKey(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::EXPECTED_KEY_TOKEN;
out << YAML::BeginMap;
out << YAML::Key << "key" << YAML::Value << "value";
out << "missing key" << YAML::Value << "value";
out << YAML::EndMap;
}
void MissingValue(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::EXPECTED_VALUE_TOKEN;
out << YAML::BeginMap;
out << YAML::Key << "key" << "value";
out << YAML::EndMap;
}
void UnexpectedKey(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::UNEXPECTED_KEY_TOKEN;
out << YAML::BeginSeq;
out << YAML::Key << "hi";
out << YAML::EndSeq;
}
void UnexpectedValue(YAML::Emitter& out, std::string& desiredError)
{
desiredError = YAML::ErrorMsg::UNEXPECTED_VALUE_TOKEN;
out << YAML::BeginSeq;
out << YAML::Value << "hi";
out << YAML::EndSeq;
}
}
} }

View File

@@ -1,6 +1,55 @@
#include <string> #include <string>
namespace YAML { class Emitter; }
namespace Test { namespace Test {
void RunAll(bool verbose); void RunAll(bool verbose);
bool Inout(const std::string& file, bool verbose); bool Inout(const std::string& file, bool verbose);
bool RunEmitterTests();
namespace Emitter {
// correct emitting
void SimpleScalar(YAML::Emitter& out, std::string& desiredOutput);
void SimpleSeq(YAML::Emitter& out, std::string& desiredOutput);
void SimpleFlowSeq(YAML::Emitter& ouptut, std::string& desiredOutput);
void EmptyFlowSeq(YAML::Emitter& out, std::string& desiredOutput);
void NestedBlockSeq(YAML::Emitter& out, std::string& desiredOutput);
void NestedFlowSeq(YAML::Emitter& out, std::string& desiredOutput);
void SimpleMap(YAML::Emitter& out, std::string& desiredOutput);
void SimpleFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void MapAndList(YAML::Emitter& out, std::string& desiredOutput);
void ListAndMap(YAML::Emitter& out, std::string& desiredOutput);
void NestedBlockMap(YAML::Emitter& out, std::string& desiredOutput);
void NestedFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void MapListMix(YAML::Emitter& out, std::string& desiredOutput);
void SimpleLongKey(YAML::Emitter& out, std::string& desiredOutput);
void SingleLongKey(YAML::Emitter& out, std::string& desiredOutput);
void ComplexLongKey(YAML::Emitter& out, std::string& desiredOutput);
void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput);
void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput);
void AutoLongKeyScalar(YAML::Emitter& out, std::string& desiredOutput);
void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput);
void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput);
void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput);
void STLContainers(YAML::Emitter& out, std::string& desiredOutput);
void SimpleComment(YAML::Emitter& out, std::string& desiredOutput);
void MultiLineComment(YAML::Emitter& out, std::string& desiredOutput);
void ComplexComments(YAML::Emitter& out, std::string& desiredOutput);
void Indentation(YAML::Emitter& out, std::string& desiredOutput);
void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput);
void ComplexGlobalSettings(YAML::Emitter& out, std::string& desiredOutput);
// incorrect emitting
void ExtraEndSeq(YAML::Emitter& out, std::string& desiredError);
void ExtraEndMap(YAML::Emitter& out, std::string& desiredError);
void BadSingleQuoted(YAML::Emitter& out, std::string& desiredError);
void InvalidAnchor(YAML::Emitter& out, std::string& desiredError);
void InvalidAlias(YAML::Emitter& out, std::string& desiredError);
void MissingKey(YAML::Emitter& out, std::string& desiredError);
void MissingValue(YAML::Emitter& out, std::string& desiredError);
void UnexpectedKey(YAML::Emitter& out, std::string& desiredError);
void UnexpectedValue(YAML::Emitter& out, std::string& desiredError);
}
} }