Commit 4116d89f authored by Jesse Beder's avatar Jesse Beder
Browse files

Merged emitter refactor from core

parents f17cfacd 2dd1cf45
......@@ -8,6 +8,7 @@
#include "yaml-cpp/dll.h"
#include "yaml-cpp/binary.h"
#include "yaml-cpp/emitterdef.h"
#include "yaml-cpp/emittermanip.h"
#include "yaml-cpp/ostream.h"
#include "yaml-cpp/noncopyable.h"
......@@ -70,33 +71,45 @@ namespace YAML
Emitter& WriteStreamable(T value);
private:
void PreWriteIntegralType(std::stringstream& str);
void PreWriteStreamable(std::stringstream& str);
void PostWriteIntegralType(const std::stringstream& str);
void PostWriteStreamable(const std::stringstream& str);
template<typename T> void SetStreamablePrecision(std::stringstream&) {}
unsigned GetFloatPrecision() const;
unsigned GetDoublePrecision() const;
void PrepareIntegralStream(std::stringstream& stream) const;
void StartedScalar();
private:
void PreAtomicWrite();
bool GotoNextPreAtomicState();
void PostAtomicWrite();
void EmitSeparationIfNecessary();
void EmitBeginDoc();
void EmitEndDoc();
void EmitBeginSeq();
void EmitEndSeq();
void EmitBeginMap();
void EmitEndMap();
void EmitKey();
void EmitValue();
void EmitNewline();
void EmitKindTag();
void EmitTag(bool verbatim, const _Tag& tag);
void PrepareNode(EmitterNodeType::value child);
void PrepareTopNode(EmitterNodeType::value child);
void FlowSeqPrepareNode(EmitterNodeType::value child);
void BlockSeqPrepareNode(EmitterNodeType::value child);
void FlowMapPrepareNode(EmitterNodeType::value child);
void FlowMapPrepareLongKey(EmitterNodeType::value child);
void FlowMapPrepareLongKeyValue(EmitterNodeType::value child);
void FlowMapPrepareSimpleKey(EmitterNodeType::value child);
void FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child);
void BlockMapPrepareNode(EmitterNodeType::value child);
void BlockMapPrepareLongKey(EmitterNodeType::value child);
void BlockMapPrepareLongKeyValue(EmitterNodeType::value child);
void BlockMapPrepareSimpleKey(EmitterNodeType::value child);
void BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child);
void SpaceOrIndentTo(bool requireSpace, unsigned indent);
const char *ComputeFullBoolName(bool b) const;
bool CanEmitNewline() const;
......@@ -111,10 +124,15 @@ namespace YAML
if(!good())
return *this;
std::stringstream str;
PreWriteIntegralType(str);
str << value;
PostWriteIntegralType(str);
PrepareNode(EmitterNodeType::Scalar);
std::stringstream stream;
PrepareIntegralStream(stream);
stream << value;
m_stream << stream.str();
StartedScalar();
return *this;
}
......@@ -124,24 +142,28 @@ namespace YAML
if(!good())
return *this;
std::stringstream str;
PreWriteStreamable(str);
SetStreamablePrecision<T>(str);
str << value;
PostWriteStreamable(str);
PrepareNode(EmitterNodeType::Scalar);
std::stringstream stream;
SetStreamablePrecision<T>(stream);
stream << value;
m_stream << stream.str();
StartedScalar();
return *this;
}
template<>
inline void Emitter::SetStreamablePrecision<float>(std::stringstream& str)
inline void Emitter::SetStreamablePrecision<float>(std::stringstream& stream)
{
str.precision(GetFloatPrecision());
stream.precision(GetFloatPrecision());
}
template<>
inline void Emitter::SetStreamablePrecision<double>(std::stringstream& str)
inline void Emitter::SetStreamablePrecision<double>(std::stringstream& stream)
{
str.precision(GetDoublePrecision());
stream.precision(GetDoublePrecision());
}
// overloads of insertion
......
#ifndef EMITTERDEF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define EMITTERDEF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#if defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
#pragma once
#endif
namespace YAML
{
struct EmitterNodeType { enum value { None, Property, Scalar, FlowSeq, BlockSeq, FlowMap, BlockMap }; };
}
#endif // EMITTERDEF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
......@@ -69,10 +69,6 @@ namespace YAML
const char * const INVALID_ANCHOR = "invalid anchor";
const char * const INVALID_ALIAS = "invalid alias";
const char * const INVALID_TAG = "invalid tag";
const char * const EXPECTED_KEY_TOKEN = "expected key token";
const char * const EXPECTED_VALUE_TOKEN = "expected value token";
const char * const UNEXPECTED_KEY_TOKEN = "unexpected key token";
const char * const UNEXPECTED_VALUE_TOKEN = "unexpected value token";
template <typename T>
inline const std::string KEY_NOT_FOUND_WITH_KEY(const T&, typename disable_if<is_numeric<T> >::type * = 0) {
......
......@@ -18,11 +18,13 @@ namespace YAML
void reserve(unsigned size);
void put(char ch);
void set_comment() { m_comment = true; }
const char *str() const { return m_buffer; }
unsigned row() const { return m_row; }
unsigned col() const { return m_col; }
unsigned pos() const { return m_pos; }
bool comment() const { return m_comment; }
private:
char *m_buffer;
......@@ -30,6 +32,7 @@ namespace YAML
unsigned m_size;
unsigned m_row, m_col;
bool m_comment;
};
ostream& operator << (ostream& out, const char *str);
......
......@@ -39,69 +39,69 @@ namespace YAML
// global setters
bool Emitter::SetOutputCharset(EMITTER_MANIP value)
{
return m_pState->SetOutputCharset(value, GLOBAL);
return m_pState->SetOutputCharset(value, FmtScope::Global);
}
bool Emitter::SetStringFormat(EMITTER_MANIP value)
{
return m_pState->SetStringFormat(value, GLOBAL);
return m_pState->SetStringFormat(value, FmtScope::Global);
}
bool Emitter::SetBoolFormat(EMITTER_MANIP value)
{
bool ok = false;
if(m_pState->SetBoolFormat(value, GLOBAL))
if(m_pState->SetBoolFormat(value, FmtScope::Global))
ok = true;
if(m_pState->SetBoolCaseFormat(value, GLOBAL))
if(m_pState->SetBoolCaseFormat(value, FmtScope::Global))
ok = true;
if(m_pState->SetBoolLengthFormat(value, GLOBAL))
if(m_pState->SetBoolLengthFormat(value, FmtScope::Global))
ok = true;
return ok;
}
bool Emitter::SetIntBase(EMITTER_MANIP value)
{
return m_pState->SetIntFormat(value, GLOBAL);
return m_pState->SetIntFormat(value, FmtScope::Global);
}
bool Emitter::SetSeqFormat(EMITTER_MANIP value)
{
return m_pState->SetFlowType(GT_SEQ, value, GLOBAL);
return m_pState->SetFlowType(GroupType::Seq, value, FmtScope::Global);
}
bool Emitter::SetMapFormat(EMITTER_MANIP value)
{
bool ok = false;
if(m_pState->SetFlowType(GT_MAP, value, GLOBAL))
if(m_pState->SetFlowType(GroupType::Map, value, FmtScope::Global))
ok = true;
if(m_pState->SetMapKeyFormat(value, GLOBAL))
if(m_pState->SetMapKeyFormat(value, FmtScope::Global))
ok = true;
return ok;
}
bool Emitter::SetIndent(unsigned n)
{
return m_pState->SetIndent(n, GLOBAL);
return m_pState->SetIndent(n, FmtScope::Global);
}
bool Emitter::SetPreCommentIndent(unsigned n)
{
return m_pState->SetPreCommentIndent(n, GLOBAL);
return m_pState->SetPreCommentIndent(n, FmtScope::Global);
}
bool Emitter::SetPostCommentIndent(unsigned n)
{
return m_pState->SetPostCommentIndent(n, GLOBAL);
return m_pState->SetPostCommentIndent(n, FmtScope::Global);
}
bool Emitter::SetFloatPrecision(unsigned n)
{
return m_pState->SetFloatPrecision(n, GLOBAL);
return m_pState->SetFloatPrecision(n, FmtScope::Global);
}
bool Emitter::SetDoublePrecision(unsigned n)
{
return m_pState->SetDoublePrecision(n, GLOBAL);
return m_pState->SetDoublePrecision(n, FmtScope::Global);
}
// SetLocalValue
......@@ -131,10 +131,8 @@ namespace YAML
EmitEndMap();
break;
case Key:
EmitKey();
break;
case Value:
EmitValue();
// deprecated (these can be deduced by the parity of nodes in a map)
break;
case TagByKind:
EmitKindTag();
......@@ -151,220 +149,40 @@ namespace YAML
Emitter& Emitter::SetLocalIndent(const _Indent& indent)
{
m_pState->SetIndent(indent.value, LOCAL);
m_pState->SetIndent(indent.value, FmtScope::Local);
return *this;
}
Emitter& Emitter::SetLocalPrecision(const _Precision& precision)
{
if(precision.floatPrecision >= 0)
m_pState->SetFloatPrecision(precision.floatPrecision, LOCAL);
m_pState->SetFloatPrecision(precision.floatPrecision, FmtScope::Local);
if(precision.doublePrecision >= 0)
m_pState->SetDoublePrecision(precision.doublePrecision, LOCAL);
m_pState->SetDoublePrecision(precision.doublePrecision, FmtScope::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:
m_pState->SwitchState(ES_WRITING_DOC);
return true;
case ES_WRITING_DOC:
return true;
case ES_DONE_WITH_DOC:
EmitBeginDoc();
return false;
// block sequence
case ES_WAITING_FOR_BLOCK_SEQ_ENTRY:
m_stream << IndentTo(curIndent) << "-";
m_pState->RequireSoftSeparation();
m_pState->SwitchState(ES_WRITING_BLOCK_SEQ_ENTRY);
return true;
case ES_WRITING_BLOCK_SEQ_ENTRY:
return true;
case ES_DONE_WITH_BLOCK_SEQ_ENTRY:
m_stream << '\n';
m_pState->SwitchState(ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
return false;
// flow sequence
case ES_WAITING_FOR_FLOW_SEQ_ENTRY:
m_pState->SwitchState(ES_WRITING_FLOW_SEQ_ENTRY);
return true;
case ES_WRITING_FLOW_SEQ_ENTRY:
return true;
case ES_DONE_WITH_FLOW_SEQ_ENTRY:
EmitSeparationIfNecessary();
m_stream << ',';
m_pState->RequireSoftSeparation();
m_pState->SwitchState(ES_WAITING_FOR_FLOW_SEQ_ENTRY);
return false;
// block map
case ES_WAITING_FOR_BLOCK_MAP_ENTRY:
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
case ES_WAITING_FOR_BLOCK_MAP_KEY:
if(m_pState->CurrentlyInLongKey()) {
m_stream << IndentTo(curIndent) << '?';
m_pState->RequireSoftSeparation();
}
m_pState->SwitchState(ES_WRITING_BLOCK_MAP_KEY);
return true;
case ES_WRITING_BLOCK_MAP_KEY:
return true;
case ES_DONE_WITH_BLOCK_MAP_KEY:
m_pState->SetError(ErrorMsg::EXPECTED_VALUE_TOKEN);
return true;
case ES_WAITING_FOR_BLOCK_MAP_VALUE:
m_pState->SwitchState(ES_WRITING_BLOCK_MAP_VALUE);
return true;
case ES_WRITING_BLOCK_MAP_VALUE:
return true;
case ES_DONE_WITH_BLOCK_MAP_VALUE:
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
// flow map
case ES_WAITING_FOR_FLOW_MAP_ENTRY:
m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN);
return true;
case ES_WAITING_FOR_FLOW_MAP_KEY:
EmitSeparationIfNecessary();
m_pState->SwitchState(ES_WRITING_FLOW_MAP_KEY);
if(m_pState->CurrentlyInLongKey()) {
m_stream << '?';
m_pState->RequireSoftSeparation();
}
return true;
case ES_WRITING_FLOW_MAP_KEY:
return true;
case ES_DONE_WITH_FLOW_MAP_KEY:
m_pState->SetError(ErrorMsg::EXPECTED_VALUE_TOKEN);
return true;
case ES_WAITING_FOR_FLOW_MAP_VALUE:
EmitSeparationIfNecessary();
m_stream << ':';
m_pState->RequireSoftSeparation();
m_pState->SwitchState(ES_WRITING_FLOW_MAP_VALUE);
return true;
case ES_WRITING_FLOW_MAP_VALUE:
return true;
case ES_DONE_WITH_FLOW_MAP_VALUE:
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:
if(!m_pState->CurrentlyInLongKey()) {
m_stream << ':';
m_pState->RequireSoftSeparation();
}
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->RequiresSoftSeparation())
m_stream << ' ';
else if(m_pState->RequiresHardSeparation())
m_stream << '\n';
m_pState->UnsetSeparation();
}
// EmitBeginDoc
void Emitter::EmitBeginDoc()
{
if(!good())
return;
EMITTER_STATE curState = m_pState->GetCurState();
if(curState != ES_WAITING_FOR_DOC && curState != ES_WRITING_DOC && curState != ES_DONE_WITH_DOC) {
if(m_pState->CurGroupType() != GroupType::None) {
m_pState->SetError("Unexpected begin document");
return;
}
if(curState == ES_WRITING_DOC || curState == ES_DONE_WITH_DOC)
m_stream << '\n';
m_stream << "---\n";
m_pState->UnsetSeparation();
m_pState->SwitchState(ES_WAITING_FOR_DOC);
}
if(m_pState->HasAnchor() || m_pState->HasTag()) {
m_pState->SetError("Unexpected begin document");
return;
}
if(m_stream.col() > 0)
m_stream << "\n";
m_stream << "---\n";
m_pState->StartedDoc();
}
// EmitEndDoc
......@@ -372,20 +190,20 @@ namespace YAML
{
if(!good())
return;
EMITTER_STATE curState = m_pState->GetCurState();
if(curState != ES_WAITING_FOR_DOC && curState != ES_WRITING_DOC && curState != ES_DONE_WITH_DOC) {
m_pState->SetError("Unexpected end document");
if(m_pState->CurGroupType() != GroupType::None) {
m_pState->SetError("Unexpected begin document");
return;
}
if(curState == ES_WRITING_DOC || curState == ES_DONE_WITH_DOC)
m_stream << '\n';
m_stream << "...\n";
m_pState->UnsetSeparation();
m_pState->SwitchState(ES_WAITING_FOR_DOC);
}
if(m_pState->HasAnchor() || m_pState->HasTag()) {
m_pState->SetError("Unexpected begin document");
return;
}
if(m_stream.col() > 0)
m_stream << "\n";
m_stream << "...\n";
}
// EmitBeginSeq
......@@ -393,33 +211,10 @@ namespace YAML
{
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 ||
curState == ES_WRITING_DOC
) {
if(m_pState->RequiresHardSeparation() || curState != ES_WRITING_DOC) {
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);
PrepareNode(m_pState->NextGroupType(GroupType::Seq));
m_pState->StartedGroup(GroupType::Seq);
}
// EmitEndSeq
......@@ -427,35 +222,20 @@ namespace YAML
{
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) {
// Note: block sequences are *not* allowed to be empty, but we convert it
// to a flow sequence if it is
assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY || curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
if(curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY) {
// Note: only one of these will actually output anything for a given situation
EmitSeparationIfNecessary();
unsigned curIndent = m_pState->GetCurIndent();
m_stream << IndentTo(curIndent);
m_stream << "[]";
}
} else if(flowType == FT_FLOW) {
// Note: flow sequences are allowed to be empty
assert(curState == ES_DONE_WITH_FLOW_SEQ_ENTRY || curState == ES_WAITING_FOR_FLOW_SEQ_ENTRY);
m_stream << "]";
} else
assert(false);
m_pState->PopState();
m_pState->EndGroup(GT_SEQ);
PostAtomicWrite();
if(m_pState->CurGroupChildCount() == 0)
m_pState->ForceFlow();
if(m_pState->CurGroupFlowType() == FlowType::Flow) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(m_pState->CurIndent());
if(m_pState->CurGroupChildCount() == 0)
m_stream << "[";
m_stream << "]";
}
m_pState->EndedGroup(GroupType::Seq);
}
// EmitBeginMap
......@@ -463,229 +243,507 @@ namespace YAML
{
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 ||
curState == ES_WRITING_DOC
) {
if(m_pState->RequiresHardSeparation() || (curState != ES_WRITING_DOC && curState != ES_WRITING_BLOCK_SEQ_ENTRY)) {
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);
PrepareNode(m_pState->NextGroupType(GroupType::Map));
m_pState->StartedGroup(GroupType::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) {
// Note: block sequences are *not* allowed to be empty, but we convert it
// to a flow sequence if it is
assert(curState == ES_DONE_WITH_BLOCK_MAP_VALUE || curState == ES_WAITING_FOR_BLOCK_MAP_ENTRY);
if(curState == ES_WAITING_FOR_BLOCK_MAP_ENTRY) {
// Note: only one of these will actually output anything for a given situation
EmitSeparationIfNecessary();
unsigned curIndent = m_pState->GetCurIndent();
m_stream << IndentTo(curIndent);
m_stream << "{}";
}
} else if(flowType == FT_FLOW) {
// Note: flow maps are allowed to be empty
assert(curState == ES_DONE_WITH_FLOW_MAP_VALUE || curState == ES_WAITING_FOR_FLOW_MAP_ENTRY);
EmitSeparationIfNecessary();
m_stream << "}";
} 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->UnsetSeparation();
m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_KEY);
} else if(flowType == FT_FLOW) {
EmitSeparationIfNecessary();
if(curState == ES_DONE_WITH_FLOW_MAP_VALUE) {
m_stream << ',';
m_pState->RequireSoftSeparation();
}
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_stream << IndentTo(m_pState->GetCurIndent());
m_stream << ':';
m_pState->RequireSoftSeparation();
}
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);
}
if(m_pState->CurGroupChildCount() == 0)
m_pState->ForceFlow();
if(m_pState->CurGroupFlowType() == FlowType::Flow) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(m_pState->CurIndent());
if(m_pState->CurGroupChildCount() == 0)
m_stream << "{";
m_stream << "}";
}
m_pState->EndedGroup(GroupType::Map);
}
// EmitNewline
void Emitter::EmitNewline()
{
if(!good())
return;
if(CanEmitNewline()) {
m_stream << '\n';
m_pState->UnsetSeparation();
}
PrepareNode(EmitterNodeType::None);
m_stream << "\n";
m_pState->SetNonContent();
}
bool Emitter::CanEmitNewline() const
{
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(flowType == FT_BLOCK && m_pState->CurrentlyInLongKey())
return true;
EMITTER_STATE curState = m_pState->GetCurState();
return curState != ES_DONE_WITH_BLOCK_MAP_KEY && curState != ES_WAITING_FOR_BLOCK_MAP_VALUE && curState != ES_WRITING_BLOCK_MAP_VALUE;
return true;
}
// *******************************************************************************************
// 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();
bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii;
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, escapeNonAscii);
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, escapeNonAscii);
break;
case Literal:
if(flowType == FT_FLOW)
Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii);
else
Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent());
break;
default:
assert(false);
}
PostAtomicWrite();
return *this;
}
// Put the stream in a state so we can simply write the next node
// E.g., if we're in a sequence, write the "- "
void Emitter::PrepareNode(EmitterNodeType::value child)
{
switch(m_pState->CurGroupNodeType()) {
case EmitterNodeType::None:
PrepareTopNode(child);
break;
case EmitterNodeType::FlowSeq:
FlowSeqPrepareNode(child);
break;
case EmitterNodeType::BlockSeq:
BlockSeqPrepareNode(child);
break;
case EmitterNodeType::FlowMap:
FlowMapPrepareNode(child);
break;
case EmitterNodeType::BlockMap:
BlockMapPrepareNode(child);
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
assert(false);
break;
}
}
void Emitter::PrepareTopNode(EmitterNodeType::value child)
{
if(child == EmitterNodeType::None)
return;
if(m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0) {
if(child != EmitterNodeType::None)
EmitBeginDoc();
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
// TODO: if we were writing null, and
// we wanted it blank, we wouldn't want a space
SpaceOrIndentTo(m_pState->HasBegunContent(), 0);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
if(m_pState->HasBegunNode())
m_stream << "\n";
break;
}
}
void Emitter::FlowSeqPrepareNode(EmitterNodeType::value child)
{
const unsigned lastIndent = m_pState->LastIndent();
if(!m_pState->HasBegunNode()) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(lastIndent);
if(m_pState->CurGroupChildCount() == 0)
m_stream << "[";
else
m_stream << ",";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
assert(false);
break;
}
}
void Emitter::PreWriteIntegralType(std::stringstream& str)
{
PreAtomicWrite();
EmitSeparationIfNecessary();
void Emitter::BlockSeqPrepareNode(EmitterNodeType::value child)
{
const unsigned curIndent = m_pState->CurIndent();
const unsigned nextIndent = curIndent + m_pState->CurGroupIndent();
if(child == EmitterNodeType::None)
return;
if(!m_pState->HasBegunContent()) {
if(m_pState->CurGroupChildCount() > 0 || m_stream.comment()) {
m_stream << "\n";
}
m_stream << IndentTo(curIndent);
m_stream << "-";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent(), nextIndent);
break;
case EmitterNodeType::BlockSeq:
m_stream << "\n";
break;
case EmitterNodeType::BlockMap:
if(m_pState->HasBegunContent() || m_stream.comment())
m_stream << "\n";
break;
}
}
void Emitter::FlowMapPrepareNode(EmitterNodeType::value child)
{
if(m_pState->CurGroupChildCount() % 2 == 0) {
if(m_pState->GetMapKeyFormat() == LongKey)
m_pState->SetLongKey();
if(m_pState->CurGroupLongKey())
FlowMapPrepareLongKey(child);
else
FlowMapPrepareSimpleKey(child);
} else {
if(m_pState->CurGroupLongKey())
FlowMapPrepareLongKeyValue(child);
else
FlowMapPrepareSimpleKeyValue(child);
}
}
void Emitter::FlowMapPrepareLongKey(EmitterNodeType::value child)
{
const unsigned lastIndent = m_pState->LastIndent();
if(!m_pState->HasBegunNode()) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(lastIndent);
if(m_pState->CurGroupChildCount() == 0)
m_stream << "{ ?";
else
m_stream << ", ?";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
assert(false);
break;
}
}
void Emitter::FlowMapPrepareLongKeyValue(EmitterNodeType::value child)
{
const unsigned lastIndent = m_pState->LastIndent();
if(!m_pState->HasBegunNode()) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(lastIndent);
m_stream << ":";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
assert(false);
break;
}
}
void Emitter::FlowMapPrepareSimpleKey(EmitterNodeType::value child)
{
const unsigned lastIndent = m_pState->LastIndent();
if(!m_pState->HasBegunNode()) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(lastIndent);
if(m_pState->CurGroupChildCount() == 0)
m_stream << "{";
else
m_stream << ",";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
assert(false);
break;
}
}
void Emitter::FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child)
{
const unsigned lastIndent = m_pState->LastIndent();
if(!m_pState->HasBegunNode()) {
if(m_stream.comment())
m_stream << "\n";
m_stream << IndentTo(lastIndent);
m_stream << ":";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
assert(false);
break;
}
}
void Emitter::BlockMapPrepareNode(EmitterNodeType::value child)
{
if(m_pState->CurGroupChildCount() % 2 == 0) {
if(m_pState->GetMapKeyFormat() == LongKey)
m_pState->SetLongKey();
if(child == EmitterNodeType::BlockSeq || child == EmitterNodeType::BlockMap)
m_pState->SetLongKey();
if(m_pState->CurGroupLongKey())
BlockMapPrepareLongKey(child);
else
BlockMapPrepareSimpleKey(child);
} else {
if(m_pState->CurGroupLongKey())
BlockMapPrepareLongKeyValue(child);
else
BlockMapPrepareSimpleKeyValue(child);
}
}
void Emitter::BlockMapPrepareLongKey(EmitterNodeType::value child)
{
const unsigned curIndent = m_pState->CurIndent();
const std::size_t childCount = m_pState->CurGroupChildCount();
if(child == EmitterNodeType::None)
return;
if(!m_pState->HasBegunContent()) {
if(childCount > 0) {
m_stream << "\n";
}
if(m_stream.comment()) {
m_stream << "\n";
}
m_stream << IndentTo(curIndent);
m_stream << "?";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(true, curIndent + 1);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
break;
}
}
void Emitter::BlockMapPrepareLongKeyValue(EmitterNodeType::value child)
{
const unsigned curIndent = m_pState->CurIndent();
if(child == EmitterNodeType::None)
return;
if(!m_pState->HasBegunContent()) {
m_stream << "\n";
m_stream << IndentTo(curIndent);
m_stream << ":";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
SpaceOrIndentTo(true, curIndent + 1);
break;
}
}
void Emitter::BlockMapPrepareSimpleKey(EmitterNodeType::value child)
{
const unsigned curIndent = m_pState->CurIndent();
const std::size_t childCount = m_pState->CurGroupChildCount();
if(child == EmitterNodeType::None)
return;
if(!m_pState->HasBegunNode()) {
if(childCount > 0) {
m_stream << "\n";
}
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(m_pState->HasBegunContent(), curIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
break;
}
}
void Emitter::BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child)
{
const unsigned curIndent = m_pState->CurIndent();
const unsigned nextIndent = curIndent + m_pState->CurGroupIndent();
if(!m_pState->HasBegunNode()) {
m_stream << ":";
}
switch(child) {
case EmitterNodeType::None:
break;
case EmitterNodeType::Property:
case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
case EmitterNodeType::FlowMap:
SpaceOrIndentTo(true, nextIndent);
break;
case EmitterNodeType::BlockSeq:
case EmitterNodeType::BlockMap:
m_stream << "\n";
break;
}
}
// SpaceOrIndentTo
// . Prepares for some more content by proper spacing
void Emitter::SpaceOrIndentTo(bool requireSpace, unsigned indent)
{
if(m_stream.comment())
m_stream << "\n";
if(m_stream.col() > 0 && requireSpace)
m_stream << " ";
m_stream << IndentTo(indent);
}
void Emitter::PrepareIntegralStream(std::stringstream& stream) const
{
EMITTER_MANIP intFmt = m_pState->GetIntFormat();
switch(intFmt) {
switch(m_pState->GetIntFormat()) {
case Dec:
str << std::dec;
stream << std::dec;
break;
case Hex:
str << "0x";
str << std::hex;
stream << "0x";
stream << std::hex;
break;
case Oct:
str << "0";
str << std::oct;
stream << "0";
stream << std::oct;
break;
default:
assert(false);
}
}
}
void Emitter::StartedScalar()
{
m_pState->StartedScalar();
}
void Emitter::PreWriteStreamable(std::stringstream&)
// *******************************************************************************************
// overloads of Write
Emitter& Emitter::Write(const std::string& str)
{
PreAtomicWrite();
EmitSeparationIfNecessary();
if(!good())
return *this;
const bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii;
const StringFormat::value strFormat = Utils::ComputeStringFormat(str, m_pState->GetStringFormat(), m_pState->CurGroupFlowType(), escapeNonAscii);
if(strFormat == StringFormat::Literal)
m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local);
PrepareNode(EmitterNodeType::Scalar);
switch(strFormat) {
case StringFormat::Plain:
m_stream << str;
break;
case StringFormat::SingleQuoted:
Utils::WriteSingleQuotedString(m_stream, str);
break;
case StringFormat::DoubleQuoted:
Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii);
break;
case StringFormat::Literal:
Utils::WriteLiteralString(m_stream, str, m_pState->CurIndent() + m_pState->GetIndent());
break;
}
StartedScalar();
return *this;
}
unsigned Emitter::GetFloatPrecision() const
......@@ -698,18 +756,6 @@ namespace YAML
return m_pState->GetDoublePrecision();
}
void Emitter::PostWriteIntegralType(const std::stringstream& str)
{
m_stream << str.str();
PostAtomicWrite();
}
void Emitter::PostWriteStreamable(const std::stringstream& str)
{
m_stream << str.str();
PostAtomicWrite();
}
const char *Emitter::ComputeFullBoolName(bool b) const
{
const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool ? YesNoBool : m_pState->GetBoolFormat());
......@@ -749,17 +795,17 @@ namespace YAML
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
PrepareNode(EmitterNodeType::Scalar);
const char *name = ComputeFullBoolName(b);
if(m_pState->GetBoolLengthFormat() == ShortBool)
m_stream << name[0];
else
m_stream << name;
PostAtomicWrite();
StartedScalar();
return *this;
}
......@@ -767,13 +813,11 @@ namespace YAML
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
Utils::WriteChar(m_stream, ch);
PostAtomicWrite();
PrepareNode(EmitterNodeType::Scalar);
Utils::WriteChar(m_stream, ch);
StartedScalar();
return *this;
}
......@@ -781,14 +825,21 @@ namespace YAML
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
if(m_pState->HasAnchor() || m_pState->HasTag()) {
m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this;
}
PrepareNode(EmitterNodeType::Scalar);
if(!Utils::WriteAlias(m_stream, alias.content)) {
m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this;
}
PostAtomicWrite();
StartedScalar();
return *this;
}
......@@ -796,15 +847,21 @@ namespace YAML
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
if(m_pState->HasAnchor()) {
m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this;
}
PrepareNode(EmitterNodeType::Property);
if(!Utils::WriteAnchor(m_stream, anchor.content)) {
m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this;
}
m_pState->RequireHardSeparation();
// Note: no PostAtomicWrite() because we need another value for this node
m_pState->SetAnchor();
return *this;
}
......@@ -812,10 +869,14 @@ namespace YAML
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
if(m_pState->HasTag()) {
m_pState->SetError(ErrorMsg::INVALID_TAG);
return *this;
}
PrepareNode(EmitterNodeType::Property);
bool success = false;
if(tag.type == _Tag::Type::Verbatim)
success = Utils::WriteTag(m_stream, tag.content, true);
......@@ -828,9 +889,9 @@ namespace YAML
m_pState->SetError(ErrorMsg::INVALID_TAG);
return *this;
}
m_pState->RequireHardSeparation();
// Note: no PostAtomicWrite() because we need another value for this node
m_pState->SetTag();
return *this;
}
......@@ -843,13 +904,15 @@ namespace YAML
{
if(!good())
return *this;
PrepareNode(EmitterNodeType::None);
if(m_stream.col() > 0)
m_stream << Indentation(m_pState->GetPreCommentIndent());
Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent());
m_pState->RequireHardSeparation();
m_pState->ForceHardSeparation();
m_pState->SetNonContent();
return *this;
}
......@@ -857,11 +920,13 @@ namespace YAML
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
m_stream << "~";
PostAtomicWrite();
PrepareNode(EmitterNodeType::Scalar);
m_stream << "~";
StartedScalar();
return *this;
}
......@@ -871,11 +936,11 @@ namespace YAML
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
PrepareNode(EmitterNodeType::Scalar);
Utils::WriteBinary(m_stream, binary);
PostAtomicWrite();
StartedScalar();
return *this;
}
}
......
......@@ -4,11 +4,8 @@
namespace YAML
{
EmitterState::EmitterState(): m_isGood(true), m_curIndent(0), m_requiresSoftSeparation(false), m_requiresHardSeparation(false)
EmitterState::EmitterState(): m_isGood(true), m_curIndent(0), m_hasAnchor(false), m_hasTag(false), m_hasNonContent(false), m_docCount(0)
{
// start up
m_stateStack.push(ES_WAITING_FOR_DOC);
// set default global manipulators
m_charset.set(EmitNonAscii);
m_strFmt.set(Auto);
......@@ -35,21 +32,111 @@ namespace YAML
// . Only the ones that make sense will be accepted
void EmitterState::SetLocalValue(EMITTER_MANIP value)
{
SetOutputCharset(value, LOCAL);
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);
SetOutputCharset(value, FmtScope::Local);
SetStringFormat(value, FmtScope::Local);
SetBoolFormat(value, FmtScope::Local);
SetBoolCaseFormat(value, FmtScope::Local);
SetBoolLengthFormat(value, FmtScope::Local);
SetIntFormat(value, FmtScope::Local);
SetFlowType(GroupType::Seq, value, FmtScope::Local);
SetFlowType(GroupType::Map, value, FmtScope::Local);
SetMapKeyFormat(value, FmtScope::Local);
}
void EmitterState::BeginGroup(GROUP_TYPE type)
void EmitterState::SetAnchor()
{
m_hasAnchor = true;
}
void EmitterState::SetTag()
{
m_hasTag = true;
}
void EmitterState::SetNonContent()
{
m_hasNonContent = true;
}
void EmitterState::SetLongKey()
{
assert(!m_groups.empty());
if(m_groups.empty())
return;
assert(m_groups.top().type == GroupType::Map);
m_groups.top().longKey = true;
}
void EmitterState::ForceFlow()
{
assert(!m_groups.empty());
if(m_groups.empty())
return;
m_groups.top().flowType = FlowType::Flow;
}
void EmitterState::StartedNode()
{
if(m_groups.empty()) {
m_docCount++;
} else {
m_groups.top().childCount++;
if(m_groups.top().childCount % 2 == 0)
m_groups.top().longKey = false;
}
m_hasAnchor = false;
m_hasTag = false;
m_hasNonContent = false;
}
EmitterNodeType::value EmitterState::NextGroupType(GroupType::value type) const
{
if(type == GroupType::Seq) {
if(GetFlowType(type) == Block)
return EmitterNodeType::BlockSeq;
else
return EmitterNodeType::FlowSeq;
} else {
if(GetFlowType(type) == Block)
return EmitterNodeType::BlockMap;
else
return EmitterNodeType::FlowMap;
}
// can't happen
assert(false);
return EmitterNodeType::None;
}
void EmitterState::StartedDoc()
{
m_hasAnchor = false;
m_hasTag = false;
m_hasNonContent = false;
}
void EmitterState::EndedDoc()
{
m_hasAnchor = false;
m_hasTag = false;
m_hasNonContent = false;
}
void EmitterState::StartedScalar()
{
StartedNode();
ClearModifiedSettings();
}
void EmitterState::StartedGroup(GroupType::value type)
{
unsigned lastIndent = (m_groups.empty() ? 0 : m_groups.top().indent);
m_curIndent += lastIndent;
StartedNode();
const int lastGroupIndent = (m_groups.empty() ? 0 : m_groups.top().indent);
m_curIndent += lastGroupIndent;
std::auto_ptr<Group> pGroup(new Group(type));
......@@ -57,17 +144,23 @@ namespace YAML
pGroup->modifiedSettings = m_modifiedSettings;
// set up group
pGroup->flow = GetFlowType(type);
if(GetFlowType(type) == Block)
pGroup->flowType = FlowType::Block;
else
pGroup->flowType = FlowType::Flow;
pGroup->indent = GetIndent();
pGroup->usingLongKey = (GetMapKeyFormat() == LongKey ? true : false);
m_groups.push(pGroup);
}
void EmitterState::EndGroup(GROUP_TYPE type)
void EmitterState::EndedGroup(GroupType::value type)
{
if(m_groups.empty())
return SetError(ErrorMsg::UNMATCHED_GROUP_TAG);
if(m_groups.empty()) {
if(type == GroupType::Seq)
return SetError(ErrorMsg::UNEXPECTED_END_SEQ);
else
return SetError(ErrorMsg::UNEXPECTED_END_MAP);
}
// get rid of the current group
{
......@@ -84,49 +177,57 @@ namespace YAML
// 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();
ClearModifiedSettings();
}
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()
EmitterNodeType::value EmitterState::CurGroupNodeType() const
{
if(m_groups.empty())
return EmitterNodeType::None;
return m_groups.top().NodeType();
}
GroupType::value EmitterState::CurGroupType() const
{
if(!m_groups.empty())
m_groups.top().usingLongKey = true;
return m_groups.empty() ? GroupType::None : m_groups.top().type;
}
void EmitterState::StartSimpleKey()
FlowType::value EmitterState::CurGroupFlowType() const
{
if(!m_groups.empty())
m_groups.top().usingLongKey = false;
return m_groups.empty() ? FlowType::None : m_groups.top().flowType;
}
int EmitterState::CurGroupIndent() const
{
return m_groups.empty() ? 0 : m_groups.top().indent;
}
std::size_t EmitterState::CurGroupChildCount() const
{
return m_groups.empty() ? m_docCount : m_groups.top().childCount;
}
bool EmitterState::CurGroupLongKey() const
{
return m_groups.empty() ? false : m_groups.top().longKey;
}
int EmitterState::LastIndent() const
{
if(m_groups.size() <= 1)
return 0;
return m_curIndent - m_groups.top(-1).indent;
}
void EmitterState::ClearModifiedSettings()
{
m_modifiedSettings.clear();
}
bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case EmitNonAscii:
......@@ -138,7 +239,7 @@ namespace YAML
}
}
bool EmitterState::SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetStringFormat(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case Auto:
......@@ -152,7 +253,7 @@ namespace YAML
}
}
bool EmitterState::SetBoolFormat(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetBoolFormat(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case OnOffBool:
......@@ -165,7 +266,7 @@ namespace YAML
}
}
bool EmitterState::SetBoolLengthFormat(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetBoolLengthFormat(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case LongBool:
......@@ -177,7 +278,7 @@ namespace YAML
}
}
bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case UpperCase:
......@@ -190,7 +291,7 @@ namespace YAML
}
}
bool EmitterState::SetIntFormat(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetIntFormat(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case Dec:
......@@ -203,16 +304,16 @@ namespace YAML
}
}
bool EmitterState::SetIndent(unsigned value, FMT_SCOPE scope)
bool EmitterState::SetIndent(unsigned value, FmtScope::value scope)
{
if(value == 0)
if(value <= 1)
return false;
_Set(m_indent, value, scope);
return true;
}
bool EmitterState::SetPreCommentIndent(unsigned value, FMT_SCOPE scope)
bool EmitterState::SetPreCommentIndent(unsigned value, FmtScope::value scope)
{
if(value == 0)
return false;
......@@ -221,7 +322,7 @@ namespace YAML
return true;
}
bool EmitterState::SetPostCommentIndent(unsigned value, FMT_SCOPE scope)
bool EmitterState::SetPostCommentIndent(unsigned value, FmtScope::value scope)
{
if(value == 0)
return false;
......@@ -230,30 +331,29 @@ namespace YAML
return true;
}
bool EmitterState::SetFlowType(GROUP_TYPE groupType, EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetFlowType(GroupType::value groupType, EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case Block:
case Flow:
_Set(groupType == GT_SEQ ? m_seqFmt : m_mapFmt, value, scope);
_Set(groupType == GroupType::Seq ? m_seqFmt : m_mapFmt, value, scope);
return true;
default:
return false;
}
}
EMITTER_MANIP EmitterState::GetFlowType(GROUP_TYPE groupType) const
EMITTER_MANIP EmitterState::GetFlowType(GroupType::value groupType) const
{
// force flow style if we're currently in a flow
FLOW_TYPE flowType = GetCurGroupFlowType();
if(flowType == FT_FLOW)
if(CurGroupFlowType() == FlowType::Flow)
return Flow;
// otherwise, go with what's asked of use
return (groupType == GT_SEQ ? m_seqFmt.get() : m_mapFmt.get());
// otherwise, go with what's asked of us
return (groupType == GroupType::Seq ? m_seqFmt.get() : m_mapFmt.get());
}
bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FMT_SCOPE scope)
bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope)
{
switch(value) {
case Auto:
......@@ -265,7 +365,7 @@ namespace YAML
}
}
bool EmitterState::SetFloatPrecision(int value, FMT_SCOPE scope)
bool EmitterState::SetFloatPrecision(int value, FmtScope::value scope)
{
if(value < 0 || value > std::numeric_limits<float>::digits10)
return false;
......@@ -273,7 +373,7 @@ namespace YAML
return true;
}
bool EmitterState::SetDoublePrecision(int value, FMT_SCOPE scope)
bool EmitterState::SetDoublePrecision(int value, FmtScope::value scope)
{
if(value < 0 || value > std::numeric_limits<double>::digits10)
return false;
......
......@@ -8,71 +8,20 @@
#include "ptr_stack.h"
#include "setting.h"
#include "yaml-cpp/emitterdef.h"
#include "yaml-cpp/emittermanip.h"
#include <cassert>
#include <vector>
#include <stack>
#include <memory>
#include <stdexcept>
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
};
struct FmtScope { enum value { Local, Global }; };
struct GroupType { enum value { None, Seq, Map }; };
struct FlowType { enum value { None, Flow, Block }; };
class EmitterState
{
public:
......@@ -84,76 +33,81 @@ namespace YAML
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();
// node handling
void SetAnchor();
void SetTag();
void SetNonContent();
void SetLongKey();
void ForceFlow();
void StartedDoc();
void EndedDoc();
void StartedScalar();
void StartedGroup(GroupType::value type);
void EndedGroup(GroupType::value type);
EmitterNodeType::value NextGroupType(GroupType::value type) const;
EmitterNodeType::value CurGroupNodeType() const;
GroupType::value CurGroupType() const;
FlowType::value CurGroupFlowType() const;
int CurGroupIndent() const;
std::size_t CurGroupChildCount() const;
bool CurGroupLongKey() const;
bool RequiresSoftSeparation() const { return m_requiresSoftSeparation; }
bool RequiresHardSeparation() const { return m_requiresHardSeparation; }
void RequireSoftSeparation() { m_requiresSoftSeparation = true; }
void RequireHardSeparation() { m_requiresSoftSeparation = true; m_requiresHardSeparation = true; }
void ForceHardSeparation() { m_requiresSoftSeparation = false; }
void UnsetSeparation() { m_requiresSoftSeparation = false; m_requiresHardSeparation = false; }
int LastIndent() const;
int CurIndent() const { return m_curIndent; }
bool HasAnchor() const { return m_hasAnchor; }
bool HasTag() const { return m_hasTag; }
bool HasBegunNode() const { return m_hasAnchor || m_hasTag || m_hasNonContent; }
bool HasBegunContent() const { return m_hasAnchor || m_hasTag; }
void ClearModifiedSettings();
// formatters
bool SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope);
void SetLocalValue(EMITTER_MANIP value);
bool SetOutputCharset(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetOutputCharset() const { return m_charset.get(); }
bool SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope);
bool SetStringFormat(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetStringFormat() const { return m_strFmt.get(); }
bool SetBoolFormat(EMITTER_MANIP value, FMT_SCOPE scope);
bool SetBoolFormat(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetBoolFormat() const { return m_boolFmt.get(); }
bool SetBoolLengthFormat(EMITTER_MANIP value, FMT_SCOPE scope);
bool SetBoolLengthFormat(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetBoolLengthFormat() const { return m_boolLengthFmt.get(); }
bool SetBoolCaseFormat(EMITTER_MANIP value, FMT_SCOPE scope);
bool SetBoolCaseFormat(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetBoolCaseFormat() const { return m_boolCaseFmt.get(); }
bool SetIntFormat(EMITTER_MANIP value, FMT_SCOPE scope);
bool SetIntFormat(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetIntFormat() const { return m_intFmt.get(); }
bool SetIndent(unsigned value, FMT_SCOPE scope);
bool SetIndent(unsigned value, FmtScope::value scope);
int GetIndent() const { return m_indent.get(); }
bool SetPreCommentIndent(unsigned value, FMT_SCOPE scope);
bool SetPreCommentIndent(unsigned value, FmtScope::value scope);
int GetPreCommentIndent() const { return m_preCommentIndent.get(); }
bool SetPostCommentIndent(unsigned value, FMT_SCOPE scope);
bool SetPostCommentIndent(unsigned value, FmtScope::value 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 SetFlowType(GroupType::value groupType, EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetFlowType(GroupType::value groupType) const;
bool SetMapKeyFormat(EMITTER_MANIP value, FMT_SCOPE scope);
bool SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetMapKeyFormat() const { return m_mapKeyFmt.get(); }
bool SetFloatPrecision(int value, FMT_SCOPE scope);
bool SetFloatPrecision(int value, FmtScope::value scope);
unsigned GetFloatPrecision() const { return m_floatPrecision.get(); }
bool SetDoublePrecision(int value, FMT_SCOPE scope);
bool SetDoublePrecision(int value, FmtScope::value scope);
unsigned GetDoublePrecision() const { return m_doublePrecision.get(); }
private:
template <typename T>
void _Set(Setting<T>& fmt, T value, FMT_SCOPE scope);
void _Set(Setting<T>& fmt, T value, FmtScope::value scope);
void StartedNode();
private:
// basic state ok?
......@@ -161,8 +115,6 @@ namespace YAML
std::string m_lastError;
// other state
std::stack<EMITTER_STATE> m_stateStack;
Setting<EMITTER_MANIP> m_charset;
Setting<EMITTER_MANIP> m_strFmt;
Setting<EMITTER_MANIP> m_boolFmt;
......@@ -181,29 +133,50 @@ namespace YAML
SettingChanges m_globalModifiedSettings;
struct Group {
Group(GROUP_TYPE type_): type(type_), usingLongKey(false), indent(0) {}
explicit Group(GroupType::value type_): type(type_), indent(0), childCount(0), longKey(false) {}
GROUP_TYPE type;
EMITTER_MANIP flow;
bool usingLongKey;
GroupType::value type;
FlowType::value flowType;
int indent;
std::size_t childCount;
bool longKey;
SettingChanges modifiedSettings;
EmitterNodeType::value NodeType() const {
if(type == GroupType::Seq) {
if(flowType == FlowType::Flow)
return EmitterNodeType::FlowSeq;
else
return EmitterNodeType::BlockSeq;
} else {
if(flowType == FlowType::Flow)
return EmitterNodeType::FlowMap;
else
return EmitterNodeType::BlockMap;
}
// can't get here
assert(false);
return EmitterNodeType::None;
}
};
ptr_stack<Group> m_groups;
unsigned m_curIndent;
bool m_requiresSoftSeparation;
bool m_requiresHardSeparation;
bool m_hasAnchor;
bool m_hasTag;
bool m_hasNonContent;
std::size_t m_docCount;
};
template <typename T>
void EmitterState::_Set(Setting<T>& fmt, T value, FMT_SCOPE scope) {
void EmitterState::_Set(Setting<T>& fmt, T value, FmtScope::value scope) {
switch(scope) {
case LOCAL:
case FmtScope::Local:
m_modifiedSettings.push(fmt.set(value));
break;
case GLOBAL:
case FmtScope::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
......
......@@ -128,12 +128,12 @@ namespace YAML
}
}
bool IsValidPlainScalar(const std::string& str, bool inFlow, bool allowOnlyAscii) {
bool IsValidPlainScalar(const std::string& str, FlowType::value flowType, bool allowOnlyAscii) {
if(str.empty())
return false;
// first check the start
const RegEx& start = (inFlow ? Exp::PlainScalarInFlow() : Exp::PlainScalar());
const RegEx& start = (flowType == FlowType::Flow ? Exp::PlainScalarInFlow() : Exp::PlainScalar());
if(!start.Matches(str))
return false;
......@@ -142,7 +142,7 @@ namespace YAML
return false;
// then check until something is disallowed
const RegEx& disallowed = (inFlow ? Exp::EndScalarInFlow() : Exp::EndScalar())
const RegEx& disallowed = (flowType == FlowType::Flow ? Exp::EndScalarInFlow() : Exp::EndScalar())
|| (Exp::BlankOrBreak() + Exp::Comment())
|| Exp::NotPrintable()
|| Exp::Utf8_ByteOrderMark()
......@@ -152,7 +152,7 @@ namespace YAML
while(buffer) {
if(disallowed.Matches(buffer))
return false;
if(allowOnlyAscii && (0x7F < static_cast<unsigned char>(buffer[0])))
if(allowOnlyAscii && (0x80 <= static_cast<unsigned char>(buffer[0])))
return false;
++buffer;
}
......@@ -160,7 +160,32 @@ namespace YAML
return true;
}
void WriteDoubleQuoteEscapeSequence(ostream& out, int codePoint) {
bool IsValidSingleQuotedScalar(const std::string& str, bool escapeNonAscii)
{
// TODO: check for non-printable characters?
for(std::size_t i=0;i<str.size();i++) {
if(escapeNonAscii && (0x80 <= static_cast<unsigned char>(str[i])))
return false;
if(str[i] == '\n')
return false;
}
return true;
}
bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType, bool escapeNonAscii)
{
if(flowType == FlowType::Flow)
return false;
// TODO: check for non-printable characters?
for(std::size_t i=0;i<str.size();i++) {
if(escapeNonAscii && (0x80 <= static_cast<unsigned char>(str[i])))
return false;
}
return true;
}
void WriteDoubleQuoteEscapeSequence(ostream& out, int codePoint) {
static const char hexDigits[] = "0123456789abcdef";
char escSeq[] = "\\U00000000";
......@@ -198,15 +223,30 @@ namespace YAML
}
}
bool WriteString(ostream& out, const std::string& str, bool inFlow, bool escapeNonAscii)
{
if(IsValidPlainScalar(str, inFlow, escapeNonAscii)) {
out << str;
return true;
} else
return WriteDoubleQuotedString(out, str, escapeNonAscii);
}
StringFormat::value ComputeStringFormat(const std::string& str, EMITTER_MANIP strFormat, FlowType::value flowType, bool escapeNonAscii)
{
switch(strFormat) {
case Auto:
if(IsValidPlainScalar(str, flowType, escapeNonAscii))
return StringFormat::Plain;
return StringFormat::DoubleQuoted;
case SingleQuoted:
if(IsValidSingleQuotedScalar(str, escapeNonAscii))
return StringFormat::SingleQuoted;
return StringFormat::DoubleQuoted;
case DoubleQuoted:
return StringFormat::DoubleQuoted;
case Literal:
if(IsValidLiteralScalar(str, flowType, escapeNonAscii))
return StringFormat::Literal;
return StringFormat::DoubleQuoted;
default:
break;
}
return StringFormat::DoubleQuoted;
}
bool WriteSingleQuotedString(ostream& out, const std::string& str)
{
out << "'";
......@@ -235,18 +275,23 @@ namespace YAML
GetNextCodePointAndAdvance(codePoint, i, str.end());
)
{
if (codePoint == '\"')
out << "\\\"";
else if (codePoint == '\\')
out << "\\\\";
else if (codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) // Control characters and non-breaking space
WriteDoubleQuoteEscapeSequence(out, codePoint);
else if (codePoint == 0xFEFF) // Byte order marks (ZWNS) should be escaped (YAML 1.2, sec. 5.2)
WriteDoubleQuoteEscapeSequence(out, codePoint);
else if (escapeNonAscii && codePoint > 0x7E)
WriteDoubleQuoteEscapeSequence(out, codePoint);
else
WriteCodePoint(out, codePoint);
switch(codePoint) {
case '\"': out << "\\\""; break;
case '\\': out << "\\\\"; break;
case '\n': out << "\\n"; break;
case '\t': out << "\\t"; break;
case '\r': out << "\\r"; break;
case '\b': out << "\\b"; break;
default:
if(codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) // Control characters and non-breaking space
WriteDoubleQuoteEscapeSequence(out, codePoint);
else if (codePoint == 0xFEFF) // Byte order marks (ZWNS) should be escaped (YAML 1.2, sec. 5.2)
WriteDoubleQuoteEscapeSequence(out, codePoint);
else if (escapeNonAscii && codePoint > 0x7E)
WriteDoubleQuoteEscapeSequence(out, codePoint);
else
WriteCodePoint(out, codePoint);
}
}
out << "\"";
return true;
......@@ -293,15 +338,18 @@ namespace YAML
{
const unsigned curIndent = out.col();
out << "#" << Indentation(postCommentIndent);
out.set_comment();
int codePoint;
for(std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());
)
{
if(codePoint == '\n')
if(codePoint == '\n') {
out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
else
out.set_comment();
} else {
WriteCodePoint(out, codePoint);
}
}
return true;
}
......
......@@ -6,6 +6,7 @@
#endif
#include "emitterstate.h"
#include "yaml-cpp/ostream.h"
#include <string>
......@@ -13,9 +14,12 @@ namespace YAML
{
class Binary;
struct StringFormat { enum value { Plain, SingleQuoted, DoubleQuoted, Literal }; };
namespace Utils
{
bool WriteString(ostream& out, const std::string& str, bool inFlow, bool escapeNonAscii);
StringFormat::value ComputeStringFormat(const std::string& str, EMITTER_MANIP strFormat, FlowType::value flowType, bool escapeNonAscii);
bool WriteSingleQuotedString(ostream& out, const std::string& str);
bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii);
bool WriteLiteralString(ostream& out, const std::string& str, int indent);
......
......@@ -3,7 +3,7 @@
namespace YAML
{
ostream::ostream(): m_buffer(0), m_pos(0), m_size(0), m_row(0), m_col(0)
ostream::ostream(): m_buffer(0), m_pos(0), m_size(0), m_row(0), m_col(0), m_comment(false)
{
reserve(1024);
}
......@@ -37,6 +37,7 @@ namespace YAML
if(ch == '\n') {
m_row++;
m_col = 0;
m_comment = false;
} else
m_col++;
}
......
......@@ -38,7 +38,10 @@ public:
}
T& top() { return *m_data.back(); }
const T& top() const { return *m_data.back(); }
T& top(std::ptrdiff_t diff) { return **(m_data.end() - 1 + diff); }
const T& top(std::ptrdiff_t diff) const { return **(m_data.end() - 1 + diff); }
private:
std::vector<T*> m_data;
};
......
#include "spectests.h"
#include "handlermacros.h"
#include "specexamples.h"
#include "yaml-cpp/yaml.h"
#include "yaml-cpp/eventhandler.h"
#include <cassert>
#define YAML_ASSERT(cond) do { if(!(cond)) return " Assert failed: " #cond; } while(false)
namespace Test {
struct Event {
enum Type { DocStart, DocEnd, Null, Alias, Scalar, SeqStart, SeqEnd, MapStart, MapEnd };
typedef YAML::Mark Mark;
typedef YAML::anchor_t anchor_t;
Event(Type type_, const std::string& tag_, anchor_t anchor_, const std::string& scalar_): type(type_), tag(tag_), anchor(anchor_), scalar(scalar_) {}
Type type;
std::string tag;
anchor_t anchor;
std::string scalar;
std::ostream& write(std::ostream& out) const {
switch(type) {
case DocStart:
return out << "DocStart";
case DocEnd:
return out << "DocEnd";
case Null:
return out << "Null(" << anchor << ")";
case Alias:
return out << "Alias(" << anchor << ")";
case Scalar:
return out << "Scalar(" << tag << ", " << anchor << ", " << scalar << ")";
case SeqStart:
return out << "SeqStart(" << tag << ", " << anchor << ")";
case SeqEnd:
return out << "SeqEnd";
case MapStart:
return out << "MapStart(" << tag << ", " << anchor << ")";
case MapEnd:
return out << "MapEnd";
}
assert(false);
return out;
}
};
std::ostream& operator << (std::ostream& out, const Event& event) {
return event.write(out);
}
bool operator == (const Event& a, const Event& b) {
return a.type == b.type && a.tag == b.tag && a.anchor == b.anchor && a.scalar == b.scalar;
}
bool operator != (const Event& a, const Event& b) {
return !(a == b);
}
class MockEventHandler: public YAML::EventHandler
{
public:
typedef YAML::Mark Mark;
typedef YAML::anchor_t anchor_t;
MockEventHandler() {}
virtual void OnDocumentStart(const Mark&) {
m_actualEvents.push_back(Event(Event::DocStart, "", 0, ""));
}
virtual void OnDocumentEnd() {
m_actualEvents.push_back(Event(Event::DocEnd, "", 0, ""));
}
virtual void OnNull(const Mark&, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::Null, "", anchor, ""));
}
virtual void OnAlias(const Mark&, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::Alias, "", anchor, ""));
}
virtual void OnScalar(const Mark&, const std::string& tag, anchor_t anchor, const std::string& value) {
m_actualEvents.push_back(Event(Event::Scalar, tag, anchor, value));
}
virtual void OnSequenceStart(const Mark&, const std::string& tag, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::SeqStart, tag, anchor, ""));
}
virtual void OnSequenceEnd() {
m_actualEvents.push_back(Event(Event::SeqEnd, "", 0, ""));
}
virtual void OnMapStart(const Mark&, const std::string& tag, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::MapStart, tag, anchor, ""));
}
virtual void OnMapEnd() {
m_actualEvents.push_back(Event(Event::MapEnd, "", 0, ""));
}
void Expect(const Event& event) { m_expectedEvents.push_back(event); }
Test::TEST Check() const {
std::size_t N = std::max(m_expectedEvents.size(), m_actualEvents.size());
for(std::size_t i=0;i<N;i++) {
if(i >= m_expectedEvents.size()) {
std::stringstream out;
for(std::size_t j=0;j<i;j++) {
out << m_expectedEvents[j] << "\n";
}
out << "EXPECTED: (no event expected)\n";
out << "ACTUAL : " << m_actualEvents[i] << "\n";
return out.str().c_str();
}
if(i >= m_actualEvents.size()) {
std::stringstream out;
for(std::size_t j=0;j<i;j++) {
out << m_expectedEvents[j] << "\n";
}
out << "EXPECTED: " << m_expectedEvents[i] << "\n";
out << "ACTUAL : (no event recorded)\n";
return out.str().c_str();
}
if(m_expectedEvents[i] != m_actualEvents[i]) {
std::stringstream out;
for(std::size_t j=0;j<i;j++) {
out << m_expectedEvents[j] << "\n";
}
out << "EXPECTED: " << m_expectedEvents[i] << "\n";
out << "ACTUAL : " << m_actualEvents[i] << "\n";
return out.str().c_str();
}
}
return true;
}
std::vector<Event> m_expectedEvents;
std::vector<Event> m_actualEvents;
};
#define HANDLE(ex)\
MockEventHandler handler;\
std::stringstream stream(ex);\
YAML::Parser parser(stream);\
while(parser.HandleNextDocument(handler))\
#define EXPECT_DOC_START()\
do {\
handler.Expect(Event(Event::DocStart, "", 0, ""));\
} while(false)
#define EXPECT_DOC_END()\
do {\
handler.Expect(Event(Event::DocEnd, "", 0, ""));\
} while(false)
#define EXPECT_NULL(anchor)\
do {\
handler.Expect(Event(Event::Null, "", anchor, ""));\
} while(false)
#define EXPECT_ALIAS(anchor)\
do {\
handler.Expect(Event(Event::Alias, "", anchor, ""));\
} while(false)
#define EXPECT_SCALAR(tag, anchor, value)\
do {\
handler.Expect(Event(Event::Scalar, tag, anchor, value));\
} while(false)
#define EXPECT_SEQ_START(tag, anchor)\
do {\
handler.Expect(Event(Event::SeqStart, tag, anchor, ""));\
} while(false)
#define EXPECT_SEQ_END()\
do {\
handler.Expect(Event(Event::SeqEnd, "", 0, ""));\
} while(false)
#define EXPECT_MAP_START(tag, anchor)\
do {\
handler.Expect(Event(Event::MapStart, tag, anchor, ""));\
} while(false)
#define EXPECT_MAP_END()\
do {\
handler.Expect(Event(Event::MapEnd, "", 0, ""));\
} while(false)
#define DONE()\
do {\
return handler.Check();\
} while(false)
namespace Spec {
// 2.1
TEST SeqScalars()
......
import sys
import yaml
import hashlib
NS = 'Emitter'
DEFINE = 'YAML_GEN_TESTS'
EVENT_COUNT = 5
def encode_stream(line):
for c in line:
if c == '\n':
yield '\\n'
elif c == '"':
yield '\\"'
elif c == '\t':
yield '\\t'
elif ord(c) < 0x20:
yield '\\x' + hex(ord(c))
else:
yield c
def encode(line):
return ''.join(encode_stream(line))
def doc_start(implicit=False):
if implicit:
return {'emit': '', 'handle': 'DOC_START()'}
else:
return {'emit': 'YAML::BeginDoc', 'handle': 'DOC_START()'}
def doc_end(implicit=False):
if implicit:
return {'emit': '', 'handle': 'DOC_END()'}
else:
return {'emit': 'YAML::EndDoc', 'handle': 'DOC_END()'}
def scalar(value, tag='', anchor='', anchor_id=0):
emit = []
if tag:
emit += ['YAML::VerbatimTag("%s")' % encode(tag)]
if anchor:
emit += ['YAML::Anchor("%s")' % encode(anchor)]
if tag:
out_tag = encode(tag)
else:
if value == encode(value):
out_tag = '?'
else:
out_tag = '!'
emit += ['"%s"' % encode(value)]
return {'emit': emit, 'handle': 'SCALAR("%s", %s, "%s")' % (out_tag, anchor_id, encode(value))}
def comment(value):
return {'emit': 'YAML::Comment("%s")' % value, 'handle': ''}
def seq_start(tag='', anchor='', anchor_id=0):
emit = []
if tag:
emit += ['YAML::VerbatimTag("%s")' % encode(tag)]
if anchor:
emit += ['YAML::Anchor("%s")' % encode(anchor)]
if tag:
out_tag = encode(tag)
else:
out_tag = '?'
emit += ['YAML::BeginSeq']
return {'emit': emit, 'handle': 'SEQ_START("%s", %s)' % (out_tag, anchor_id)}
def seq_end():
return {'emit': 'YAML::EndSeq', 'handle': 'SEQ_END()'}
def map_start(tag='', anchor='', anchor_id=0):
emit = []
if tag:
emit += ['YAML::VerbatimTag("%s")' % encode(tag)]
if anchor:
emit += ['YAML::Anchor("%s")' % encode(anchor)]
if tag:
out_tag = encode(tag)
else:
out_tag = '?'
emit += ['YAML::BeginMap']
return {'emit': emit, 'handle': 'MAP_START("%s", %s)' % (out_tag, anchor_id)}
def map_end():
return {'emit': 'YAML::EndMap', 'handle': 'MAP_END()'}
def gen_templates():
yield [[doc_start(), doc_start(True)],
[scalar('foo'), scalar('foo\n'), scalar('foo', 'tag'), scalar('foo', '', 'anchor', 1)],
[doc_end(), doc_end(True)]]
yield [[doc_start(), doc_start(True)],
[seq_start()],
[[], [scalar('foo')], [scalar('foo', 'tag')], [scalar('foo', '', 'anchor', 1)], [scalar('foo', 'tag', 'anchor', 1)], [scalar('foo'), scalar('bar')], [scalar('foo', 'tag', 'anchor', 1), scalar('bar', 'tag', 'other', 2)]],
[seq_end()],
[doc_end(), doc_end(True)]]
yield [[doc_start(), doc_start(True)],
[map_start()],
[[], [scalar('foo'), scalar('bar')], [scalar('foo', 'tag', 'anchor', 1), scalar('bar', 'tag', 'other', 2)]],
[map_end()],
[doc_end(), doc_end(True)]]
yield [[doc_start(True)],
[map_start()],
[[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
[[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
[map_end()],
[doc_end(True)]]
yield [[doc_start(True)],
[seq_start()],
[[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
[[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
[seq_end()],
[doc_end(True)]]
def expand(template):
if len(template) == 0:
pass
elif len(template) == 1:
for item in template[0]:
if isinstance(item, list):
yield item
else:
yield [item]
else:
for car in expand(template[:1]):
for cdr in expand(template[1:]):
yield car + cdr
def gen_events():
for template in gen_templates():
for events in expand(template):
base = list(events)
for i in range(0, len(base)+1):
cpy = list(base)
cpy.insert(i, comment('comment'))
yield cpy
def gen_tests():
for events in gen_events():
name = 'test' + hashlib.sha1(''.join(yaml.dump(event) for event in events)).hexdigest()[:20]
yield {'name': name, 'events': events}
def create_emitter_tests(out):
out.write('#ifdef %s\n' % DEFINE)
out.write('namespace %s {\n' % NS)
tests = list(gen_tests())
for test in tests:
out.write('TEST %s(YAML::Emitter& out)\n' % test['name'])
out.write('{\n')
for event in test['events']:
emit = event['emit']
if isinstance(emit, list):
for e in emit:
out.write(' out << %s;\n' % e)
elif emit:
out.write(' out << %s;\n' % emit)
out.write('\n')
out.write(' HANDLE(out.c_str());\n')
for event in test['events']:
handle = event['handle']
if handle:
out.write(' EXPECT_%s;\n' % handle)
out.write(' DONE();\n')
out.write('}\n')
out.write('}\n')
out.write('#endif // %s\n\n' % DEFINE)
out.write('void RunGenEmitterTests(int& passed, int& total)\n')
out.write('{\n')
out.write('#ifdef %s\n' % DEFINE)
for test in tests:
out.write(' RunGenEmitterTest(&Emitter::%s, "%s", passed, total);\n' % (test['name'], encode(test['name'])))
out.write('#else // %s\n' % DEFINE)
out.write(' (void)passed; (void)total;\n')
out.write('#endif // %s\n' % DEFINE)
out.write('}\n')
if __name__ == '__main__':
create_emitter_tests(sys.stdout)
#include "tests.h"
#include "handlermacros.h"
#include "yaml-cpp/yaml.h"
#include <iostream>
......@@ -190,7 +191,7 @@ namespace Test
out << YAML::Value << "demon";
out << YAML::EndMap;
desiredOutput = "?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon";
desiredOutput = "? - 1\n - 3\n: monster\n? [2, 0]\n: demon";
}
void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput)
......@@ -204,7 +205,7 @@ namespace Test
out << YAML::Value << "angel";
out << YAML::EndMap;
desiredOutput = "?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon\nthe origin: angel";
desiredOutput = "? - 1\n - 3\n: monster\n[2, 0]: demon\nthe origin: angel";
}
void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput)
......@@ -218,7 +219,7 @@ namespace Test
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\\x0adouble-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";
desiredOutput = "- simple scalar\n- 'explicit single-quoted scalar'\n- \"explicit double-quoted scalar\"\n- \"auto-detected\\ndouble-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)
......@@ -256,7 +257,7 @@ namespace Test
out << "total value";
out << YAML::EndMap;
desiredOutput = "?\n key: value\n next key: next value\n: total value";
desiredOutput = "? key: value\n next key: next value\n: total value";
}
void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput)
......@@ -539,7 +540,7 @@ namespace Test
{
out << YAML::Flow << YAML::BeginSeq << "foo" << YAML::Comment("foo!") << "bar" << YAML::EndSeq;
desiredOutput = "[foo # foo!\n, bar]";
desiredOutput = "[foo, # foo!\nbar]";
}
void CommentInFlowMap(YAML::Emitter& out, std::string& desiredOutput)
......@@ -550,7 +551,7 @@ namespace Test
out << YAML::Key << "baz" << YAML::Value << "baz value" << YAML::Comment("baz!");
out << YAML::EndMap;
desiredOutput = "{foo: foo value, bar: bar value # bar!\n, baz: baz value # baz!\n}";
desiredOutput = "{foo: foo value, bar: bar value, # bar!\nbaz: baz value, # baz!\n}";
}
void Indentation(YAML::Emitter& out, std::string& desiredOutput)
......@@ -563,7 +564,7 @@ namespace Test
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "- key 1: value 1\n key 2:\n - a\n - b\n - c";
desiredOutput = "- key 1: value 1\n key 2:\n - a\n - b\n - c";
}
void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput)
......@@ -597,7 +598,7 @@ namespace Test
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "- key 1: value 1\n key 2: [a, b, c]\n- ? [1, 2]\n :\n a: b";
desiredOutput = "- key 1: value 1\n key 2: [a, b, c]\n- [1, 2]:\n a: b";
}
void Null(YAML::Emitter& out, std::string& desiredOutput)
......@@ -720,7 +721,7 @@ namespace Test
out << YAML::Flow << YAML::BeginSeq;
out << "a" << YAML::Newline << "b" << "c" << YAML::Newline << "d";
out << YAML::EndSeq;
desiredOutput = "[a\n, b, c\n, d]";
desiredOutput = "[a,\nb, c,\nd]";
}
void NewlineInBlockMap(YAML::Emitter& out, std::string& desiredOutput)
......@@ -730,7 +731,7 @@ namespace Test
out << YAML::Key << "b" << YAML::Newline << YAML::Value << "bar";
out << YAML::LongKey << YAML::Key << "c" << YAML::Newline << YAML::Value << "car";
out << YAML::EndMap;
desiredOutput = "a: foo\n\nb: bar\n? c\n\n: car";
desiredOutput = "a: foo\nb:\n bar\n? c\n\n: car";
}
void NewlineInFlowMap(YAML::Emitter& out, std::string& desiredOutput)
......@@ -739,7 +740,7 @@ namespace Test
out << YAML::Key << "a" << YAML::Value << "foo" << YAML::Newline;
out << YAML::Key << "b" << YAML::Value << "bar";
out << YAML::EndMap;
desiredOutput = "{a: foo\n, b: bar}";
desiredOutput = "{a: foo,\nb: bar}";
}
void LotsOfNewlines(YAML::Emitter& out, std::string& desiredOutput)
......@@ -755,7 +756,7 @@ namespace Test
out << YAML::LongKey << YAML::Key << "f" << YAML::Newline << YAML::Value << "foo";
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "- a\n\n-\n - b\n - c\n\n\n-\n d: e\n ? f\n\n : foo";
desiredOutput = "- a\n\n-\n - b\n - c\n\n\n-\n d:\n e\n ? f\n\n : foo";
}
void Binary(YAML::Emitter& out, std::string& desiredOutput)
......@@ -911,7 +912,40 @@ namespace Test
desiredOutput = "[31, 0x1f, 037]";
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
void CompactMapWithNewline(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Comment("Characteristics");
out << YAML::BeginSeq;
out << YAML::BeginMap;
out << YAML::Key << "color" << YAML::Value << "blue";
out << YAML::Key << "height" << YAML::Value << 120;
out << YAML::EndMap;
out << YAML::Newline << YAML::Newline;
out << YAML::Comment("Skills");
out << YAML::BeginMap;
out << YAML::Key << "attack" << YAML::Value << 23;
out << YAML::Key << "intelligence" << YAML::Value << 56;
out << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput =
"# Characteristics\n"
"- color: blue\n"
" height: 120\n"
"\n"
"# Skills\n"
"- attack: 23\n"
" intelligence: 56";
}
void ForceSingleQuotedToDouble(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::SingleQuoted << "Hello\nWorld";
desiredOutput = "\"Hello\\nWorld\"";
}
////////////////////////////////////////////////////////////////////////////////
// incorrect emitting
void ExtraEndSeq(YAML::Emitter& out, std::string& desiredError)
......@@ -935,13 +969,6 @@ namespace Test
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;
......@@ -959,43 +986,6 @@ namespace Test
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;
}
}
namespace {
......@@ -1043,7 +1033,37 @@ namespace Test
}
total++;
}
void RunGenEmitterTest(TEST (*test)(YAML::Emitter&), const std::string& name, int& passed, int& total) {
YAML::Emitter out;
TEST ret;
try {
ret = test(out);
} catch(const YAML::Exception& e) {
ret.ok = false;
ret.error = std::string(" Exception caught: ") + e.what();
}
if(!out.good()) {
ret.ok = false;
ret.error = out.GetLastError();
}
if(!ret.ok) {
std::cout << "Generated emitter test failed: " << name << "\n";
std::cout << "Output:\n";
std::cout << out.c_str() << "<<<\n";
std::cout << ret.error << "\n";
}
if(ret.ok)
passed++;
total++;
}
}
#include "genemittertests.h"
bool RunEmitterTests()
{
......@@ -1126,17 +1146,16 @@ namespace Test
RunEmitterTest(&Emitter::SetPrecision, "set precision", passed, total);
RunEmitterTest(&Emitter::DashInBlockContext, "dash in block context", passed, total);
RunEmitterTest(&Emitter::HexAndOct, "hex and oct", passed, total);
RunEmitterTest(&Emitter::CompactMapWithNewline, "compact map with newline", passed, total);
RunEmitterTest(&Emitter::ForceSingleQuotedToDouble, "force single quoted to double", passed, total);
RunEmitterErrorTest(&Emitter::ExtraEndSeq, "extra EndSeq", passed, total);
RunEmitterErrorTest(&Emitter::ExtraEndMap, "extra EndMap", passed, total);
RunEmitterErrorTest(&Emitter::BadSingleQuoted, "bad single quoted string", passed, total);
RunEmitterErrorTest(&Emitter::InvalidAnchor, "invalid anchor", passed, total);
RunEmitterErrorTest(&Emitter::InvalidAlias, "invalid alias", passed, total);
RunEmitterErrorTest(&Emitter::MissingKey, "missing key", passed, total);
RunEmitterErrorTest(&Emitter::MissingValue, "missing value", passed, total);
RunEmitterErrorTest(&Emitter::UnexpectedKey, "unexpected key", passed, total);
RunEmitterErrorTest(&Emitter::UnexpectedValue, "unexpected value", passed, total);
RunEmitterErrorTest(&Emitter::BadLocalTag, "bad local tag", passed, total);
RunGenEmitterTests(passed, total);
std::cout << "Emitter tests: " << passed << "/" << total << " passed\n";
return passed == total;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
#include "teststruct.h"
#pragma once
#include "yaml-cpp/yaml.h"
#include "yaml-cpp/eventhandler.h"
#include <string>
#include <cassert>
namespace Test {
inline std::string Quote(const std::string& text) {
YAML::Emitter out;
out << YAML::DoubleQuoted << text;
return out.c_str();
}
struct Event {
enum Type { DocStart, DocEnd, Null, Alias, Scalar, SeqStart, SeqEnd, MapStart, MapEnd };
typedef YAML::Mark Mark;
typedef YAML::anchor_t anchor_t;
Event(Type type_, const std::string& tag_, anchor_t anchor_, const std::string& scalar_): type(type_), tag(tag_), anchor(anchor_), scalar(scalar_) {}
Type type;
std::string tag;
anchor_t anchor;
std::string scalar;
std::ostream& write(std::ostream& out) const {
switch(type) {
case DocStart:
return out << "DocStart";
case DocEnd:
return out << "DocEnd";
case Null:
return out << "Null(" << anchor << ")";
case Alias:
return out << "Alias(" << anchor << ")";
case Scalar:
return out << "Scalar(" << Quote(tag) << ", " << anchor << ", " << Quote(scalar) << ")";
case SeqStart:
return out << "SeqStart(" << Quote(tag) << ", " << anchor << ")";
case SeqEnd:
return out << "SeqEnd";
case MapStart:
return out << "MapStart(" << Quote(tag) << ", " << anchor << ")";
case MapEnd:
return out << "MapEnd";
}
assert(false);
return out;
}
};
inline std::ostream& operator << (std::ostream& out, const Event& event) {
return event.write(out);
}
inline bool operator == (const Event& a, const Event& b) {
return a.type == b.type && a.tag == b.tag && a.anchor == b.anchor && a.scalar == b.scalar;
}
inline bool operator != (const Event& a, const Event& b) {
return !(a == b);
}
class MockEventHandler: public YAML::EventHandler
{
public:
typedef YAML::Mark Mark;
typedef YAML::anchor_t anchor_t;
MockEventHandler() {}
virtual void OnDocumentStart(const Mark&) {
m_actualEvents.push_back(Event(Event::DocStart, "", 0, ""));
}
virtual void OnDocumentEnd() {
m_actualEvents.push_back(Event(Event::DocEnd, "", 0, ""));
}
virtual void OnNull(const Mark&, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::Null, "", anchor, ""));
}
virtual void OnAlias(const Mark&, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::Alias, "", anchor, ""));
}
virtual void OnScalar(const Mark&, const std::string& tag, anchor_t anchor, const std::string& value) {
m_actualEvents.push_back(Event(Event::Scalar, tag, anchor, value));
}
virtual void OnSequenceStart(const Mark&, const std::string& tag, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::SeqStart, tag, anchor, ""));
}
virtual void OnSequenceEnd() {
m_actualEvents.push_back(Event(Event::SeqEnd, "", 0, ""));
}
virtual void OnMapStart(const Mark&, const std::string& tag, anchor_t anchor) {
m_actualEvents.push_back(Event(Event::MapStart, tag, anchor, ""));
}
virtual void OnMapEnd() {
m_actualEvents.push_back(Event(Event::MapEnd, "", 0, ""));
}
void Expect(const Event& event) { m_expectedEvents.push_back(event); }
Test::TEST Check() const {
std::size_t N = std::max(m_expectedEvents.size(), m_actualEvents.size());
for(std::size_t i=0;i<N;i++) {
if(i >= m_expectedEvents.size()) {
std::stringstream out;
for(std::size_t j=0;j<i;j++) {
out << " " << m_expectedEvents[j] << "\n";
}
out << " EXPECTED: (no event expected)\n";
out << " ACTUAL : " << m_actualEvents[i] << "\n";
return out.str().c_str();
}
if(i >= m_actualEvents.size()) {
std::stringstream out;
for(std::size_t j=0;j<i;j++) {
out << " " << m_expectedEvents[j] << "\n";
}
out << " EXPECTED: " << m_expectedEvents[i] << "\n";
out << " ACTUAL : (no event recorded)\n";
return out.str().c_str();
}
if(m_expectedEvents[i] != m_actualEvents[i]) {
std::stringstream out;
for(std::size_t j=0;j<i;j++) {
out << " " << m_expectedEvents[j] << "\n";
}
out << " EXPECTED: " << m_expectedEvents[i] << "\n";
out << " ACTUAL : " << m_actualEvents[i] << "\n";
return out.str().c_str();
}
}
return true;
}
std::vector<Event> m_expectedEvents;
std::vector<Event> m_actualEvents;
};
#define HANDLE(ex)\
MockEventHandler handler;\
std::stringstream stream(ex);\
YAML::Parser parser(stream);\
while(parser.HandleNextDocument(handler)) {}
#define EXPECT_DOC_START()\
handler.Expect(Event(Event::DocStart, "", 0, ""))
#define EXPECT_DOC_END()\
handler.Expect(Event(Event::DocEnd, "", 0, ""))
#define EXPECT_NULL(anchor)\
handler.Expect(Event(Event::Null, "", anchor, ""))
#define EXPECT_ALIAS(anchor)\
handler.Expect(Event(Event::Alias, "", anchor, ""))
#define EXPECT_SCALAR(tag, anchor, value)\
handler.Expect(Event(Event::Scalar, tag, anchor, value))
#define EXPECT_SEQ_START(tag, anchor)\
handler.Expect(Event(Event::SeqStart, tag, anchor, ""))
#define EXPECT_SEQ_END()\
handler.Expect(Event(Event::SeqEnd, "", 0, ""))
#define EXPECT_MAP_START(tag, anchor)\
handler.Expect(Event(Event::MapStart, tag, anchor, ""))
#define EXPECT_MAP_END()\
handler.Expect(Event(Event::MapEnd, "", 0, ""))
#define DONE()\
return handler.Check()
}
......@@ -5,18 +5,9 @@
#pragma once
#endif
#include <string>
#include "teststruct.h"
namespace Test {
struct TEST {
TEST(): ok(false) {}
TEST(bool ok_): ok(ok_) {}
TEST(const char *error_): ok(false), error(error_) {}
bool ok;
std::string error;
};
namespace Spec {
// 2.1
TEST SeqScalars();
......
#pragma once
#include <string>
#define YAML_ASSERT(cond) do { if(!(cond)) return " Assert failed: " #cond; } while(false)
namespace Test
{
struct TEST {
TEST(): ok(false) {}
TEST(bool ok_): ok(ok_) {}
TEST(const char *error_): ok(false), error(error_) {}
bool ok;
std::string error;
};
}
add_executable(parse parse.cpp)
target_link_libraries(parse yaml-cpp)
add_executable(sandbox sandbox.cpp)
target_link_libraries(sandbox yaml-cpp)
#include "yaml-cpp/yaml.h"
#include <iostream>
int main()
{
YAML::Emitter out;
out << YAML::BeginSeq;
out << ':';
out << YAML::EndSeq;
std::cout << out.c_str() << "\n";
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment