"docs/source/en/installation.md" did not exist on "f46b22ba134f21a3a62154bf0528ecda290029fd"
Commit 4116d89f authored by Jesse Beder's avatar Jesse Beder
Browse files

Merged emitter refactor from core

parents f17cfacd 2dd1cf45
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "yaml-cpp/dll.h" #include "yaml-cpp/dll.h"
#include "yaml-cpp/binary.h" #include "yaml-cpp/binary.h"
#include "yaml-cpp/emitterdef.h"
#include "yaml-cpp/emittermanip.h" #include "yaml-cpp/emittermanip.h"
#include "yaml-cpp/ostream.h" #include "yaml-cpp/ostream.h"
#include "yaml-cpp/noncopyable.h" #include "yaml-cpp/noncopyable.h"
...@@ -70,33 +71,45 @@ namespace YAML ...@@ -70,33 +71,45 @@ namespace YAML
Emitter& WriteStreamable(T value); Emitter& WriteStreamable(T value);
private: 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&) {} template<typename T> void SetStreamablePrecision(std::stringstream&) {}
unsigned GetFloatPrecision() const; unsigned GetFloatPrecision() const;
unsigned GetDoublePrecision() const; unsigned GetDoublePrecision() const;
void PrepareIntegralStream(std::stringstream& stream) const;
void StartedScalar();
private: private:
void PreAtomicWrite();
bool GotoNextPreAtomicState();
void PostAtomicWrite();
void EmitSeparationIfNecessary();
void EmitBeginDoc(); void EmitBeginDoc();
void EmitEndDoc(); void EmitEndDoc();
void EmitBeginSeq(); void EmitBeginSeq();
void EmitEndSeq(); void EmitEndSeq();
void EmitBeginMap(); void EmitBeginMap();
void EmitEndMap(); void EmitEndMap();
void EmitKey();
void EmitValue();
void EmitNewline(); void EmitNewline();
void EmitKindTag(); void EmitKindTag();
void EmitTag(bool verbatim, const _Tag& tag); 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; const char *ComputeFullBoolName(bool b) const;
bool CanEmitNewline() const; bool CanEmitNewline() const;
...@@ -111,10 +124,15 @@ namespace YAML ...@@ -111,10 +124,15 @@ namespace YAML
if(!good()) if(!good())
return *this; return *this;
std::stringstream str; PrepareNode(EmitterNodeType::Scalar);
PreWriteIntegralType(str);
str << value; std::stringstream stream;
PostWriteIntegralType(str); PrepareIntegralStream(stream);
stream << value;
m_stream << stream.str();
StartedScalar();
return *this; return *this;
} }
...@@ -124,24 +142,28 @@ namespace YAML ...@@ -124,24 +142,28 @@ namespace YAML
if(!good()) if(!good())
return *this; return *this;
std::stringstream str; PrepareNode(EmitterNodeType::Scalar);
PreWriteStreamable(str);
SetStreamablePrecision<T>(str); std::stringstream stream;
str << value; SetStreamablePrecision<T>(stream);
PostWriteStreamable(str); stream << value;
m_stream << stream.str();
StartedScalar();
return *this; return *this;
} }
template<> template<>
inline void Emitter::SetStreamablePrecision<float>(std::stringstream& str) inline void Emitter::SetStreamablePrecision<float>(std::stringstream& stream)
{ {
str.precision(GetFloatPrecision()); stream.precision(GetFloatPrecision());
} }
template<> 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 // 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 ...@@ -69,10 +69,6 @@ namespace YAML
const char * const INVALID_ANCHOR = "invalid anchor"; const char * const INVALID_ANCHOR = "invalid anchor";
const char * const INVALID_ALIAS = "invalid alias"; const char * const INVALID_ALIAS = "invalid alias";
const char * const INVALID_TAG = "invalid tag"; 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> template <typename T>
inline const std::string KEY_NOT_FOUND_WITH_KEY(const T&, typename disable_if<is_numeric<T> >::type * = 0) { 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 ...@@ -18,11 +18,13 @@ namespace YAML
void reserve(unsigned size); void reserve(unsigned size);
void put(char ch); void put(char ch);
void set_comment() { m_comment = true; }
const char *str() const { return m_buffer; } const char *str() const { return m_buffer; }
unsigned row() const { return m_row; } unsigned row() const { return m_row; }
unsigned col() const { return m_col; } unsigned col() const { return m_col; }
unsigned pos() const { return m_pos; } unsigned pos() const { return m_pos; }
bool comment() const { return m_comment; }
private: private:
char *m_buffer; char *m_buffer;
...@@ -30,6 +32,7 @@ namespace YAML ...@@ -30,6 +32,7 @@ namespace YAML
unsigned m_size; unsigned m_size;
unsigned m_row, m_col; unsigned m_row, m_col;
bool m_comment;
}; };
ostream& operator << (ostream& out, const char *str); ostream& operator << (ostream& out, const char *str);
......
...@@ -39,69 +39,69 @@ namespace YAML ...@@ -39,69 +39,69 @@ namespace YAML
// global setters // global setters
bool Emitter::SetOutputCharset(EMITTER_MANIP value) 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) 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 Emitter::SetBoolFormat(EMITTER_MANIP value)
{ {
bool ok = false; bool ok = false;
if(m_pState->SetBoolFormat(value, GLOBAL)) if(m_pState->SetBoolFormat(value, FmtScope::Global))
ok = true; ok = true;
if(m_pState->SetBoolCaseFormat(value, GLOBAL)) if(m_pState->SetBoolCaseFormat(value, FmtScope::Global))
ok = true; ok = true;
if(m_pState->SetBoolLengthFormat(value, GLOBAL)) if(m_pState->SetBoolLengthFormat(value, FmtScope::Global))
ok = true; ok = true;
return ok; return ok;
} }
bool Emitter::SetIntBase(EMITTER_MANIP value) 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) 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 Emitter::SetMapFormat(EMITTER_MANIP value)
{ {
bool ok = false; bool ok = false;
if(m_pState->SetFlowType(GT_MAP, value, GLOBAL)) if(m_pState->SetFlowType(GroupType::Map, value, FmtScope::Global))
ok = true; ok = true;
if(m_pState->SetMapKeyFormat(value, GLOBAL)) if(m_pState->SetMapKeyFormat(value, FmtScope::Global))
ok = true; ok = true;
return ok; return ok;
} }
bool Emitter::SetIndent(unsigned n) bool Emitter::SetIndent(unsigned n)
{ {
return m_pState->SetIndent(n, GLOBAL); return m_pState->SetIndent(n, FmtScope::Global);
} }
bool Emitter::SetPreCommentIndent(unsigned n) bool Emitter::SetPreCommentIndent(unsigned n)
{ {
return m_pState->SetPreCommentIndent(n, GLOBAL); return m_pState->SetPreCommentIndent(n, FmtScope::Global);
} }
bool Emitter::SetPostCommentIndent(unsigned n) bool Emitter::SetPostCommentIndent(unsigned n)
{ {
return m_pState->SetPostCommentIndent(n, GLOBAL); return m_pState->SetPostCommentIndent(n, FmtScope::Global);
} }
bool Emitter::SetFloatPrecision(unsigned n) bool Emitter::SetFloatPrecision(unsigned n)
{ {
return m_pState->SetFloatPrecision(n, GLOBAL); return m_pState->SetFloatPrecision(n, FmtScope::Global);
} }
bool Emitter::SetDoublePrecision(unsigned n) bool Emitter::SetDoublePrecision(unsigned n)
{ {
return m_pState->SetDoublePrecision(n, GLOBAL); return m_pState->SetDoublePrecision(n, FmtScope::Global);
} }
// SetLocalValue // SetLocalValue
...@@ -131,10 +131,8 @@ namespace YAML ...@@ -131,10 +131,8 @@ namespace YAML
EmitEndMap(); EmitEndMap();
break; break;
case Key: case Key:
EmitKey();
break;
case Value: case Value:
EmitValue(); // deprecated (these can be deduced by the parity of nodes in a map)
break; break;
case TagByKind: case TagByKind:
EmitKindTag(); EmitKindTag();
...@@ -151,220 +149,40 @@ namespace YAML ...@@ -151,220 +149,40 @@ namespace YAML
Emitter& Emitter::SetLocalIndent(const _Indent& indent) Emitter& Emitter::SetLocalIndent(const _Indent& indent)
{ {
m_pState->SetIndent(indent.value, LOCAL); m_pState->SetIndent(indent.value, FmtScope::Local);
return *this; return *this;
} }
Emitter& Emitter::SetLocalPrecision(const _Precision& precision) Emitter& Emitter::SetLocalPrecision(const _Precision& precision)
{ {
if(precision.floatPrecision >= 0) if(precision.floatPrecision >= 0)
m_pState->SetFloatPrecision(precision.floatPrecision, LOCAL); m_pState->SetFloatPrecision(precision.floatPrecision, FmtScope::Local);
if(precision.doublePrecision >= 0) if(precision.doublePrecision >= 0)
m_pState->SetDoublePrecision(precision.doublePrecision, LOCAL); m_pState->SetDoublePrecision(precision.doublePrecision, FmtScope::Local);
return *this; 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 // EmitBeginDoc
void Emitter::EmitBeginDoc() void Emitter::EmitBeginDoc()
{ {
if(!good()) if(!good())
return; return;
EMITTER_STATE curState = m_pState->GetCurState(); if(m_pState->CurGroupType() != GroupType::None) {
if(curState != ES_WAITING_FOR_DOC && curState != ES_WRITING_DOC && curState != ES_DONE_WITH_DOC) {
m_pState->SetError("Unexpected begin document"); m_pState->SetError("Unexpected begin document");
return; return;
} }
if(curState == ES_WRITING_DOC || curState == ES_DONE_WITH_DOC) if(m_pState->HasAnchor() || m_pState->HasTag()) {
m_stream << '\n'; m_pState->SetError("Unexpected begin document");
m_stream << "---\n"; return;
}
m_pState->UnsetSeparation();
m_pState->SwitchState(ES_WAITING_FOR_DOC); if(m_stream.col() > 0)
m_stream << "\n";
m_stream << "---\n";
m_pState->StartedDoc();
} }
// EmitEndDoc // EmitEndDoc
...@@ -372,20 +190,20 @@ namespace YAML ...@@ -372,20 +190,20 @@ namespace YAML
{ {
if(!good()) if(!good())
return; return;
if(m_pState->CurGroupType() != GroupType::None) {
EMITTER_STATE curState = m_pState->GetCurState(); m_pState->SetError("Unexpected begin document");
if(curState != ES_WAITING_FOR_DOC && curState != ES_WRITING_DOC && curState != ES_DONE_WITH_DOC) {
m_pState->SetError("Unexpected end document");
return; return;
} }
if(curState == ES_WRITING_DOC || curState == ES_DONE_WITH_DOC) if(m_pState->HasAnchor() || m_pState->HasTag()) {
m_stream << '\n'; m_pState->SetError("Unexpected begin document");
m_stream << "...\n"; return;
}
m_pState->UnsetSeparation();
m_pState->SwitchState(ES_WAITING_FOR_DOC); if(m_stream.col() > 0)
m_stream << "\n";
m_stream << "...\n";
} }
// EmitBeginSeq // EmitBeginSeq
...@@ -393,33 +211,10 @@ namespace YAML ...@@ -393,33 +211,10 @@ namespace YAML
{ {
if(!good()) if(!good())
return; return;
// must have a long key if we're emitting a sequence PrepareNode(m_pState->NextGroupType(GroupType::Seq));
m_pState->StartLongKey();
m_pState->StartedGroup(GroupType::Seq);
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);
} }
// EmitEndSeq // EmitEndSeq
...@@ -427,35 +222,20 @@ namespace YAML ...@@ -427,35 +222,20 @@ namespace YAML
{ {
if(!good()) if(!good())
return; return;
if(m_pState->GetCurGroupType() != GT_SEQ) if(m_pState->CurGroupChildCount() == 0)
return m_pState->SetError(ErrorMsg::UNEXPECTED_END_SEQ); m_pState->ForceFlow();
EMITTER_STATE curState = m_pState->GetCurState(); if(m_pState->CurGroupFlowType() == FlowType::Flow) {
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); if(m_stream.comment())
if(flowType == FT_BLOCK) { m_stream << "\n";
// Note: block sequences are *not* allowed to be empty, but we convert it m_stream << IndentTo(m_pState->CurIndent());
// to a flow sequence if it is if(m_pState->CurGroupChildCount() == 0)
assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY || curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY); m_stream << "[";
if(curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY) { m_stream << "]";
// Note: only one of these will actually output anything for a given situation }
EmitSeparationIfNecessary();
unsigned curIndent = m_pState->GetCurIndent(); m_pState->EndedGroup(GroupType::Seq);
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();
} }
// EmitBeginMap // EmitBeginMap
...@@ -463,229 +243,507 @@ namespace YAML ...@@ -463,229 +243,507 @@ namespace YAML
{ {
if(!good()) if(!good())
return; return;
// must have a long key if we're emitting a map PrepareNode(m_pState->NextGroupType(GroupType::Map));
m_pState->StartLongKey();
m_pState->StartedGroup(GroupType::Map);
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);
} }
// EmitEndMap // EmitEndMap
void Emitter::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()) if(!good())
return; return;
EMITTER_STATE curState = m_pState->GetCurState(); if(m_pState->CurGroupChildCount() == 0)
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); m_pState->ForceFlow();
if(curState != ES_DONE_WITH_BLOCK_MAP_KEY && curState != ES_DONE_WITH_FLOW_MAP_KEY)
return m_pState->SetError(ErrorMsg::UNEXPECTED_VALUE_TOKEN); if(m_pState->CurGroupFlowType() == FlowType::Flow) {
if(m_stream.comment())
if(flowType == FT_BLOCK) { m_stream << "\n";
if(m_pState->CurrentlyInLongKey()) { m_stream << IndentTo(m_pState->CurIndent());
m_stream << '\n'; if(m_pState->CurGroupChildCount() == 0)
m_stream << IndentTo(m_pState->GetCurIndent()); m_stream << "{";
m_stream << ':'; m_stream << "}";
m_pState->RequireSoftSeparation(); }
}
m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_VALUE); m_pState->EndedGroup(GroupType::Map);
} else if(flowType == FT_FLOW) { }
m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_VALUE);
} else
assert(false);
}
// EmitNewline // EmitNewline
void Emitter::EmitNewline() void Emitter::EmitNewline()
{ {
if(!good()) if(!good())
return; return;
if(CanEmitNewline()) { PrepareNode(EmitterNodeType::None);
m_stream << '\n'; m_stream << "\n";
m_pState->UnsetSeparation(); m_pState->SetNonContent();
}
} }
bool Emitter::CanEmitNewline() const bool Emitter::CanEmitNewline() const
{ {
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); return true;
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;
} }
// ******************************************************************************************* // Put the stream in a state so we can simply write the next node
// overloads of Write // E.g., if we're in a sequence, write the "- "
void Emitter::PrepareNode(EmitterNodeType::value child)
Emitter& Emitter::Write(const std::string& str) {
{ switch(m_pState->CurGroupNodeType()) {
if(!good()) case EmitterNodeType::None:
return *this; PrepareTopNode(child);
break;
// literal scalars must use long keys case EmitterNodeType::FlowSeq:
if(m_pState->GetStringFormat() == Literal && m_pState->GetCurGroupFlowType() != FT_FLOW) FlowSeqPrepareNode(child);
m_pState->StartLongKey(); break;
case EmitterNodeType::BlockSeq:
PreAtomicWrite(); BlockSeqPrepareNode(child);
EmitSeparationIfNecessary(); break;
case EmitterNodeType::FlowMap:
bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii; FlowMapPrepareNode(child);
EMITTER_MANIP strFmt = m_pState->GetStringFormat(); break;
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); case EmitterNodeType::BlockMap:
unsigned curIndent = m_pState->GetCurIndent(); BlockMapPrepareNode(child);
break;
switch(strFmt) { case EmitterNodeType::Property:
case Auto: case EmitterNodeType::Scalar:
Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii); assert(false);
break; break;
case SingleQuoted: }
if(!Utils::WriteSingleQuotedString(m_stream, str)) { }
m_pState->SetError(ErrorMsg::SINGLE_QUOTED_CHAR);
return *this; void Emitter::PrepareTopNode(EmitterNodeType::value child)
} {
break; if(child == EmitterNodeType::None)
case DoubleQuoted: return;
Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii);
break; if(m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0) {
case Literal: if(child != EmitterNodeType::None)
if(flowType == FT_FLOW) EmitBeginDoc();
Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii); }
else
Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent()); switch(child) {
break; case EmitterNodeType::None:
default: break;
assert(false); case EmitterNodeType::Property:
} case EmitterNodeType::Scalar:
case EmitterNodeType::FlowSeq:
PostAtomicWrite(); case EmitterNodeType::FlowMap:
return *this; // 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) void Emitter::BlockSeqPrepareNode(EmitterNodeType::value child)
{ {
PreAtomicWrite(); const unsigned curIndent = m_pState->CurIndent();
EmitSeparationIfNecessary(); 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(m_pState->GetIntFormat()) {
switch(intFmt) {
case Dec: case Dec:
str << std::dec; stream << std::dec;
break; break;
case Hex: case Hex:
str << "0x"; stream << "0x";
str << std::hex; stream << std::hex;
break; break;
case Oct: case Oct:
str << "0"; stream << "0";
str << std::oct; stream << std::oct;
break; break;
default: default:
assert(false); assert(false);
} }
} }
void Emitter::StartedScalar()
{
m_pState->StartedScalar();
}
void Emitter::PreWriteStreamable(std::stringstream&) // *******************************************************************************************
// overloads of Write
Emitter& Emitter::Write(const std::string& str)
{ {
PreAtomicWrite(); if(!good())
EmitSeparationIfNecessary(); 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 unsigned Emitter::GetFloatPrecision() const
...@@ -698,18 +756,6 @@ namespace YAML ...@@ -698,18 +756,6 @@ namespace YAML
return m_pState->GetDoublePrecision(); 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 char *Emitter::ComputeFullBoolName(bool b) const
{ {
const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool ? YesNoBool : m_pState->GetBoolFormat()); const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool ? YesNoBool : m_pState->GetBoolFormat());
...@@ -749,17 +795,17 @@ namespace YAML ...@@ -749,17 +795,17 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); PrepareNode(EmitterNodeType::Scalar);
EmitSeparationIfNecessary();
const char *name = ComputeFullBoolName(b); const char *name = ComputeFullBoolName(b);
if(m_pState->GetBoolLengthFormat() == ShortBool) if(m_pState->GetBoolLengthFormat() == ShortBool)
m_stream << name[0]; m_stream << name[0];
else else
m_stream << name; m_stream << name;
PostAtomicWrite(); StartedScalar();
return *this; return *this;
} }
...@@ -767,13 +813,11 @@ namespace YAML ...@@ -767,13 +813,11 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); PrepareNode(EmitterNodeType::Scalar);
EmitSeparationIfNecessary(); Utils::WriteChar(m_stream, ch);
StartedScalar();
Utils::WriteChar(m_stream, ch);
PostAtomicWrite();
return *this; return *this;
} }
...@@ -781,14 +825,21 @@ namespace YAML ...@@ -781,14 +825,21 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); if(m_pState->HasAnchor() || m_pState->HasTag()) {
EmitSeparationIfNecessary(); m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this;
}
PrepareNode(EmitterNodeType::Scalar);
if(!Utils::WriteAlias(m_stream, alias.content)) { if(!Utils::WriteAlias(m_stream, alias.content)) {
m_pState->SetError(ErrorMsg::INVALID_ALIAS); m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this; return *this;
} }
PostAtomicWrite();
StartedScalar();
return *this; return *this;
} }
...@@ -796,15 +847,21 @@ namespace YAML ...@@ -796,15 +847,21 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); if(m_pState->HasAnchor()) {
EmitSeparationIfNecessary(); m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this;
}
PrepareNode(EmitterNodeType::Property);
if(!Utils::WriteAnchor(m_stream, anchor.content)) { if(!Utils::WriteAnchor(m_stream, anchor.content)) {
m_pState->SetError(ErrorMsg::INVALID_ANCHOR); m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this; return *this;
} }
m_pState->RequireHardSeparation();
// Note: no PostAtomicWrite() because we need another value for this node m_pState->SetAnchor();
return *this; return *this;
} }
...@@ -812,10 +869,14 @@ namespace YAML ...@@ -812,10 +869,14 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); if(m_pState->HasTag()) {
EmitSeparationIfNecessary(); m_pState->SetError(ErrorMsg::INVALID_TAG);
return *this;
}
PrepareNode(EmitterNodeType::Property);
bool success = false; bool success = false;
if(tag.type == _Tag::Type::Verbatim) if(tag.type == _Tag::Type::Verbatim)
success = Utils::WriteTag(m_stream, tag.content, true); success = Utils::WriteTag(m_stream, tag.content, true);
...@@ -828,9 +889,9 @@ namespace YAML ...@@ -828,9 +889,9 @@ namespace YAML
m_pState->SetError(ErrorMsg::INVALID_TAG); m_pState->SetError(ErrorMsg::INVALID_TAG);
return *this; return *this;
} }
m_pState->RequireHardSeparation(); m_pState->SetTag();
// Note: no PostAtomicWrite() because we need another value for this node
return *this; return *this;
} }
...@@ -843,13 +904,15 @@ namespace YAML ...@@ -843,13 +904,15 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PrepareNode(EmitterNodeType::None);
if(m_stream.col() > 0) if(m_stream.col() > 0)
m_stream << Indentation(m_pState->GetPreCommentIndent()); m_stream << Indentation(m_pState->GetPreCommentIndent());
Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent()); Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent());
m_pState->RequireHardSeparation();
m_pState->ForceHardSeparation(); m_pState->SetNonContent();
return *this; return *this;
} }
...@@ -857,11 +920,13 @@ namespace YAML ...@@ -857,11 +920,13 @@ namespace YAML
{ {
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); PrepareNode(EmitterNodeType::Scalar);
EmitSeparationIfNecessary();
m_stream << "~"; m_stream << "~";
PostAtomicWrite();
StartedScalar();
return *this; return *this;
} }
...@@ -871,11 +936,11 @@ namespace YAML ...@@ -871,11 +936,11 @@ namespace YAML
if(!good()) if(!good())
return *this; return *this;
PreAtomicWrite(); PrepareNode(EmitterNodeType::Scalar);
EmitSeparationIfNecessary();
Utils::WriteBinary(m_stream, binary); Utils::WriteBinary(m_stream, binary);
PostAtomicWrite(); StartedScalar();
return *this; return *this;
} }
} }
......
...@@ -4,11 +4,8 @@ ...@@ -4,11 +4,8 @@
namespace YAML 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 // set default global manipulators
m_charset.set(EmitNonAscii); m_charset.set(EmitNonAscii);
m_strFmt.set(Auto); m_strFmt.set(Auto);
...@@ -35,21 +32,111 @@ namespace YAML ...@@ -35,21 +32,111 @@ namespace YAML
// . Only the ones that make sense will be accepted // . Only the ones that make sense will be accepted
void EmitterState::SetLocalValue(EMITTER_MANIP value) void EmitterState::SetLocalValue(EMITTER_MANIP value)
{ {
SetOutputCharset(value, LOCAL); SetOutputCharset(value, FmtScope::Local);
SetStringFormat(value, LOCAL); SetStringFormat(value, FmtScope::Local);
SetBoolFormat(value, LOCAL); SetBoolFormat(value, FmtScope::Local);
SetBoolCaseFormat(value, LOCAL); SetBoolCaseFormat(value, FmtScope::Local);
SetBoolLengthFormat(value, LOCAL); SetBoolLengthFormat(value, FmtScope::Local);
SetIntFormat(value, LOCAL); SetIntFormat(value, FmtScope::Local);
SetFlowType(GT_SEQ, value, LOCAL); SetFlowType(GroupType::Seq, value, FmtScope::Local);
SetFlowType(GT_MAP, value, LOCAL); SetFlowType(GroupType::Map, value, FmtScope::Local);
SetMapKeyFormat(value, 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); StartedNode();
m_curIndent += lastIndent;
const int lastGroupIndent = (m_groups.empty() ? 0 : m_groups.top().indent);
m_curIndent += lastGroupIndent;
std::auto_ptr<Group> pGroup(new Group(type)); std::auto_ptr<Group> pGroup(new Group(type));
...@@ -57,17 +144,23 @@ namespace YAML ...@@ -57,17 +144,23 @@ namespace YAML
pGroup->modifiedSettings = m_modifiedSettings; pGroup->modifiedSettings = m_modifiedSettings;
// set up group // set up group
pGroup->flow = GetFlowType(type); if(GetFlowType(type) == Block)
pGroup->flowType = FlowType::Block;
else
pGroup->flowType = FlowType::Flow;
pGroup->indent = GetIndent(); pGroup->indent = GetIndent();
pGroup->usingLongKey = (GetMapKeyFormat() == LongKey ? true : false);
m_groups.push(pGroup); m_groups.push(pGroup);
} }
void EmitterState::EndGroup(GROUP_TYPE type) void EmitterState::EndedGroup(GroupType::value type)
{ {
if(m_groups.empty()) if(m_groups.empty()) {
return SetError(ErrorMsg::UNMATCHED_GROUP_TAG); if(type == GroupType::Seq)
return SetError(ErrorMsg::UNEXPECTED_END_SEQ);
else
return SetError(ErrorMsg::UNEXPECTED_END_MAP);
}
// get rid of the current group // get rid of the current group
{ {
...@@ -84,49 +177,57 @@ namespace YAML ...@@ -84,49 +177,57 @@ namespace YAML
// some global settings that we changed may have been overridden // some global settings that we changed may have been overridden
// by a local setting we just popped, so we need to restore them // by a local setting we just popped, so we need to restore them
m_globalModifiedSettings.restore(); m_globalModifiedSettings.restore();
ClearModifiedSettings();
} }
GROUP_TYPE EmitterState::GetCurGroupType() const EmitterNodeType::value EmitterState::CurGroupNodeType() const
{ {
if(m_groups.empty()) if(m_groups.empty())
return GT_NONE; return EmitterNodeType::None;
return m_groups.top().type; return m_groups.top().NodeType();
} }
FLOW_TYPE EmitterState::GetCurGroupFlowType() const GroupType::value EmitterState::CurGroupType() 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()) return m_groups.empty() ? GroupType::None : m_groups.top().type;
m_groups.top().usingLongKey = true;
} }
void EmitterState::StartSimpleKey() FlowType::value EmitterState::CurGroupFlowType() const
{ {
if(!m_groups.empty()) return m_groups.empty() ? FlowType::None : m_groups.top().flowType;
m_groups.top().usingLongKey = false;
} }
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() void EmitterState::ClearModifiedSettings()
{ {
m_modifiedSettings.clear(); m_modifiedSettings.clear();
} }
bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope) bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FmtScope::value scope)
{ {
switch(value) { switch(value) {
case EmitNonAscii: case EmitNonAscii:
...@@ -138,7 +239,7 @@ namespace YAML ...@@ -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) { switch(value) {
case Auto: case Auto:
...@@ -152,7 +253,7 @@ namespace YAML ...@@ -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) { switch(value) {
case OnOffBool: case OnOffBool:
...@@ -165,7 +266,7 @@ namespace YAML ...@@ -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) { switch(value) {
case LongBool: case LongBool:
...@@ -177,7 +278,7 @@ namespace YAML ...@@ -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) { switch(value) {
case UpperCase: case UpperCase:
...@@ -190,7 +291,7 @@ namespace YAML ...@@ -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) { switch(value) {
case Dec: case Dec:
...@@ -203,16 +304,16 @@ namespace YAML ...@@ -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; return false;
_Set(m_indent, value, scope); _Set(m_indent, value, scope);
return true; return true;
} }
bool EmitterState::SetPreCommentIndent(unsigned value, FMT_SCOPE scope) bool EmitterState::SetPreCommentIndent(unsigned value, FmtScope::value scope)
{ {
if(value == 0) if(value == 0)
return false; return false;
...@@ -221,7 +322,7 @@ namespace YAML ...@@ -221,7 +322,7 @@ namespace YAML
return true; return true;
} }
bool EmitterState::SetPostCommentIndent(unsigned value, FMT_SCOPE scope) bool EmitterState::SetPostCommentIndent(unsigned value, FmtScope::value scope)
{ {
if(value == 0) if(value == 0)
return false; return false;
...@@ -230,30 +331,29 @@ namespace YAML ...@@ -230,30 +331,29 @@ namespace YAML
return true; 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) { switch(value) {
case Block: case Block:
case Flow: case Flow:
_Set(groupType == GT_SEQ ? m_seqFmt : m_mapFmt, value, scope); _Set(groupType == GroupType::Seq ? m_seqFmt : m_mapFmt, value, scope);
return true; return true;
default: default:
return false; 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 // force flow style if we're currently in a flow
FLOW_TYPE flowType = GetCurGroupFlowType(); if(CurGroupFlowType() == FlowType::Flow)
if(flowType == FT_FLOW)
return Flow; return Flow;
// otherwise, go with what's asked of use // otherwise, go with what's asked of us
return (groupType == GT_SEQ ? m_seqFmt.get() : m_mapFmt.get()); 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) { switch(value) {
case Auto: case Auto:
...@@ -265,7 +365,7 @@ namespace YAML ...@@ -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) if(value < 0 || value > std::numeric_limits<float>::digits10)
return false; return false;
...@@ -273,7 +373,7 @@ namespace YAML ...@@ -273,7 +373,7 @@ namespace YAML
return true; 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) if(value < 0 || value > std::numeric_limits<double>::digits10)
return false; return false;
......
...@@ -8,71 +8,20 @@ ...@@ -8,71 +8,20 @@
#include "ptr_stack.h" #include "ptr_stack.h"
#include "setting.h" #include "setting.h"
#include "yaml-cpp/emitterdef.h"
#include "yaml-cpp/emittermanip.h" #include "yaml-cpp/emittermanip.h"
#include <cassert> #include <cassert>
#include <vector> #include <vector>
#include <stack> #include <stack>
#include <memory> #include <memory>
#include <stdexcept>
namespace YAML namespace YAML
{ {
enum FMT_SCOPE { struct FmtScope { enum value { Local, Global }; };
LOCAL, struct GroupType { enum value { None, Seq, Map }; };
GLOBAL struct FlowType { enum value { None, Flow, Block }; };
};
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 class EmitterState
{ {
public: public:
...@@ -84,76 +33,81 @@ namespace YAML ...@@ -84,76 +33,81 @@ namespace YAML
const std::string GetLastError() const { return m_lastError; } const std::string GetLastError() const { return m_lastError; }
void SetError(const std::string& error) { m_isGood = false; m_lastError = error; } void SetError(const std::string& error) { m_isGood = false; m_lastError = error; }
// main state of the machine // node handling
EMITTER_STATE GetCurState() const { return m_stateStack.top(); } void SetAnchor();
void SwitchState(EMITTER_STATE state) { PopState(); PushState(state); } void SetTag();
void PushState(EMITTER_STATE state) { m_stateStack.push(state); } void SetNonContent();
void PopState() { m_stateStack.pop(); } void SetLongKey();
void ForceFlow();
void SetLocalValue(EMITTER_MANIP value); void StartedDoc();
void EndedDoc();
// group handling void StartedScalar();
void BeginGroup(GROUP_TYPE type); void StartedGroup(GroupType::value type);
void EndGroup(GROUP_TYPE type); void EndedGroup(GroupType::value type);
GROUP_TYPE GetCurGroupType() const; EmitterNodeType::value NextGroupType(GroupType::value type) const;
FLOW_TYPE GetCurGroupFlowType() const; EmitterNodeType::value CurGroupNodeType() const;
int GetCurIndent() const { return m_curIndent; }
GroupType::value CurGroupType() const;
bool CurrentlyInLongKey(); FlowType::value CurGroupFlowType() const;
void StartLongKey(); int CurGroupIndent() const;
void StartSimpleKey(); std::size_t CurGroupChildCount() const;
bool CurGroupLongKey() const;
bool RequiresSoftSeparation() const { return m_requiresSoftSeparation; } int LastIndent() const;
bool RequiresHardSeparation() const { return m_requiresHardSeparation; } int CurIndent() const { return m_curIndent; }
void RequireSoftSeparation() { m_requiresSoftSeparation = true; } bool HasAnchor() const { return m_hasAnchor; }
void RequireHardSeparation() { m_requiresSoftSeparation = true; m_requiresHardSeparation = true; } bool HasTag() const { return m_hasTag; }
void ForceHardSeparation() { m_requiresSoftSeparation = false; } bool HasBegunNode() const { return m_hasAnchor || m_hasTag || m_hasNonContent; }
void UnsetSeparation() { m_requiresSoftSeparation = false; m_requiresHardSeparation = false; } bool HasBegunContent() const { return m_hasAnchor || m_hasTag; }
void ClearModifiedSettings(); void ClearModifiedSettings();
// formatters // 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(); } 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(); } 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(); } 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(); } 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(); } 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(); } 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(); } 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(); } 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(); } int GetPostCommentIndent() const { return m_postCommentIndent.get(); }
bool SetFlowType(GROUP_TYPE groupType, EMITTER_MANIP value, FMT_SCOPE scope); bool SetFlowType(GroupType::value groupType, EMITTER_MANIP value, FmtScope::value scope);
EMITTER_MANIP GetFlowType(GROUP_TYPE groupType) const; 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(); } 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(); } 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(); } unsigned GetDoublePrecision() const { return m_doublePrecision.get(); }
private: private:
template <typename T> 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: private:
// basic state ok? // basic state ok?
...@@ -161,8 +115,6 @@ namespace YAML ...@@ -161,8 +115,6 @@ namespace YAML
std::string m_lastError; std::string m_lastError;
// other state // other state
std::stack<EMITTER_STATE> m_stateStack;
Setting<EMITTER_MANIP> m_charset; Setting<EMITTER_MANIP> m_charset;
Setting<EMITTER_MANIP> m_strFmt; Setting<EMITTER_MANIP> m_strFmt;
Setting<EMITTER_MANIP> m_boolFmt; Setting<EMITTER_MANIP> m_boolFmt;
...@@ -181,29 +133,50 @@ namespace YAML ...@@ -181,29 +133,50 @@ namespace YAML
SettingChanges m_globalModifiedSettings; SettingChanges m_globalModifiedSettings;
struct Group { 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; GroupType::value type;
EMITTER_MANIP flow; FlowType::value flowType;
bool usingLongKey;
int indent; int indent;
std::size_t childCount;
bool longKey;
SettingChanges modifiedSettings; 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; ptr_stack<Group> m_groups;
unsigned m_curIndent; unsigned m_curIndent;
bool m_requiresSoftSeparation; bool m_hasAnchor;
bool m_requiresHardSeparation; bool m_hasTag;
bool m_hasNonContent;
std::size_t m_docCount;
}; };
template <typename T> 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) { switch(scope) {
case LOCAL: case FmtScope::Local:
m_modifiedSettings.push(fmt.set(value)); m_modifiedSettings.push(fmt.set(value));
break; break;
case GLOBAL: case FmtScope::Global:
fmt.set(value); fmt.set(value);
m_globalModifiedSettings.push(fmt.set(value)); // this pushes an identity set, so when we restore, 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 // it restores to the value here, and not the previous one
......
...@@ -128,12 +128,12 @@ namespace YAML ...@@ -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()) if(str.empty())
return false; return false;
// first check the start // 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)) if(!start.Matches(str))
return false; return false;
...@@ -142,7 +142,7 @@ namespace YAML ...@@ -142,7 +142,7 @@ namespace YAML
return false; return false;
// then check until something is disallowed // 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::BlankOrBreak() + Exp::Comment())
|| Exp::NotPrintable() || Exp::NotPrintable()
|| Exp::Utf8_ByteOrderMark() || Exp::Utf8_ByteOrderMark()
...@@ -152,7 +152,7 @@ namespace YAML ...@@ -152,7 +152,7 @@ namespace YAML
while(buffer) { while(buffer) {
if(disallowed.Matches(buffer)) if(disallowed.Matches(buffer))
return false; return false;
if(allowOnlyAscii && (0x7F < static_cast<unsigned char>(buffer[0]))) if(allowOnlyAscii && (0x80 <= static_cast<unsigned char>(buffer[0])))
return false; return false;
++buffer; ++buffer;
} }
...@@ -160,7 +160,32 @@ namespace YAML ...@@ -160,7 +160,32 @@ namespace YAML
return true; 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"; static const char hexDigits[] = "0123456789abcdef";
char escSeq[] = "\\U00000000"; char escSeq[] = "\\U00000000";
...@@ -198,15 +223,30 @@ namespace YAML ...@@ -198,15 +223,30 @@ namespace YAML
} }
} }
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)
{ {
if(IsValidPlainScalar(str, inFlow, escapeNonAscii)) { switch(strFormat) {
out << str; case Auto:
return true; if(IsValidPlainScalar(str, flowType, escapeNonAscii))
} else return StringFormat::Plain;
return WriteDoubleQuotedString(out, str, escapeNonAscii); 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) bool WriteSingleQuotedString(ostream& out, const std::string& str)
{ {
out << "'"; out << "'";
...@@ -235,18 +275,23 @@ namespace YAML ...@@ -235,18 +275,23 @@ namespace YAML
GetNextCodePointAndAdvance(codePoint, i, str.end()); GetNextCodePointAndAdvance(codePoint, i, str.end());
) )
{ {
if (codePoint == '\"') switch(codePoint) {
out << "\\\""; case '\"': out << "\\\""; break;
else if (codePoint == '\\') case '\\': out << "\\\\"; break;
out << "\\\\"; case '\n': out << "\\n"; break;
else if (codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) // Control characters and non-breaking space case '\t': out << "\\t"; break;
WriteDoubleQuoteEscapeSequence(out, codePoint); case '\r': out << "\\r"; break;
else if (codePoint == 0xFEFF) // Byte order marks (ZWNS) should be escaped (YAML 1.2, sec. 5.2) case '\b': out << "\\b"; break;
WriteDoubleQuoteEscapeSequence(out, codePoint); default:
else if (escapeNonAscii && codePoint > 0x7E) if(codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) // Control characters and non-breaking space
WriteDoubleQuoteEscapeSequence(out, codePoint); WriteDoubleQuoteEscapeSequence(out, codePoint);
else else if (codePoint == 0xFEFF) // Byte order marks (ZWNS) should be escaped (YAML 1.2, sec. 5.2)
WriteCodePoint(out, codePoint); WriteDoubleQuoteEscapeSequence(out, codePoint);
else if (escapeNonAscii && codePoint > 0x7E)
WriteDoubleQuoteEscapeSequence(out, codePoint);
else
WriteCodePoint(out, codePoint);
}
} }
out << "\""; out << "\"";
return true; return true;
...@@ -293,15 +338,18 @@ namespace YAML ...@@ -293,15 +338,18 @@ namespace YAML
{ {
const unsigned curIndent = out.col(); const unsigned curIndent = out.col();
out << "#" << Indentation(postCommentIndent); out << "#" << Indentation(postCommentIndent);
out.set_comment();
int codePoint; int codePoint;
for(std::string::const_iterator i = str.begin(); for(std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end()); GetNextCodePointAndAdvance(codePoint, i, str.end());
) )
{ {
if(codePoint == '\n') if(codePoint == '\n') {
out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent); out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
else out.set_comment();
} else {
WriteCodePoint(out, codePoint); WriteCodePoint(out, codePoint);
}
} }
return true; return true;
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#endif #endif
#include "emitterstate.h"
#include "yaml-cpp/ostream.h" #include "yaml-cpp/ostream.h"
#include <string> #include <string>
...@@ -13,9 +14,12 @@ namespace YAML ...@@ -13,9 +14,12 @@ namespace YAML
{ {
class Binary; class Binary;
struct StringFormat { enum value { Plain, SingleQuoted, DoubleQuoted, Literal }; };
namespace Utils 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 WriteSingleQuotedString(ostream& out, const std::string& str);
bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii); bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii);
bool WriteLiteralString(ostream& out, const std::string& str, int indent); bool WriteLiteralString(ostream& out, const std::string& str, int indent);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
namespace YAML 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); reserve(1024);
} }
...@@ -37,6 +37,7 @@ namespace YAML ...@@ -37,6 +37,7 @@ namespace YAML
if(ch == '\n') { if(ch == '\n') {
m_row++; m_row++;
m_col = 0; m_col = 0;
m_comment = false;
} else } else
m_col++; m_col++;
} }
......
...@@ -38,7 +38,10 @@ public: ...@@ -38,7 +38,10 @@ public:
} }
T& top() { return *m_data.back(); } T& top() { return *m_data.back(); }
const T& top() const { 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: private:
std::vector<T*> m_data; std::vector<T*> m_data;
}; };
......
#include "spectests.h" #include "spectests.h"
#include "handlermacros.h"
#include "specexamples.h" #include "specexamples.h"
#include "yaml-cpp/yaml.h" #include "yaml-cpp/yaml.h"
#include "yaml-cpp/eventhandler.h"
#include <cassert> #include <cassert>
#define YAML_ASSERT(cond) do { if(!(cond)) return " Assert failed: " #cond; } while(false)
namespace Test { 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 { namespace Spec {
// 2.1 // 2.1
TEST SeqScalars() 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 "tests.h"
#include "handlermacros.h"
#include "yaml-cpp/yaml.h" #include "yaml-cpp/yaml.h"
#include <iostream> #include <iostream>
...@@ -190,7 +191,7 @@ namespace Test ...@@ -190,7 +191,7 @@ namespace Test
out << YAML::Value << "demon"; out << YAML::Value << "demon";
out << YAML::EndMap; 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) void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput)
...@@ -204,7 +205,7 @@ namespace Test ...@@ -204,7 +205,7 @@ namespace Test
out << YAML::Value << "angel"; out << YAML::Value << "angel";
out << YAML::EndMap; 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) void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput)
...@@ -218,7 +219,7 @@ namespace Test ...@@ -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::Literal << "literal scalar\nthat may span\nmany, many\nlines and have \"whatever\" crazy\tsymbols that we like";
out << YAML::EndSeq; 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) void AutoLongKeyScalar(YAML::Emitter& out, std::string& desiredOutput)
...@@ -256,7 +257,7 @@ namespace Test ...@@ -256,7 +257,7 @@ namespace Test
out << "total value"; out << "total value";
out << YAML::EndMap; 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) void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput)
...@@ -539,7 +540,7 @@ namespace Test ...@@ -539,7 +540,7 @@ namespace Test
{ {
out << YAML::Flow << YAML::BeginSeq << "foo" << YAML::Comment("foo!") << "bar" << YAML::EndSeq; 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) void CommentInFlowMap(YAML::Emitter& out, std::string& desiredOutput)
...@@ -550,7 +551,7 @@ namespace Test ...@@ -550,7 +551,7 @@ namespace Test
out << YAML::Key << "baz" << YAML::Value << "baz value" << YAML::Comment("baz!"); out << YAML::Key << "baz" << YAML::Value << "baz value" << YAML::Comment("baz!");
out << YAML::EndMap; 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) void Indentation(YAML::Emitter& out, std::string& desiredOutput)
...@@ -563,7 +564,7 @@ namespace Test ...@@ -563,7 +564,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; 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) void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput)
...@@ -597,7 +598,7 @@ namespace Test ...@@ -597,7 +598,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; 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) void Null(YAML::Emitter& out, std::string& desiredOutput)
...@@ -720,7 +721,7 @@ namespace Test ...@@ -720,7 +721,7 @@ namespace Test
out << YAML::Flow << YAML::BeginSeq; out << YAML::Flow << YAML::BeginSeq;
out << "a" << YAML::Newline << "b" << "c" << YAML::Newline << "d"; out << "a" << YAML::Newline << "b" << "c" << YAML::Newline << "d";
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "[a\n, b, c\n, d]"; desiredOutput = "[a,\nb, c,\nd]";
} }
void NewlineInBlockMap(YAML::Emitter& out, std::string& desiredOutput) void NewlineInBlockMap(YAML::Emitter& out, std::string& desiredOutput)
...@@ -730,7 +731,7 @@ namespace Test ...@@ -730,7 +731,7 @@ namespace Test
out << YAML::Key << "b" << YAML::Newline << YAML::Value << "bar"; out << YAML::Key << "b" << YAML::Newline << YAML::Value << "bar";
out << YAML::LongKey << YAML::Key << "c" << YAML::Newline << YAML::Value << "car"; out << YAML::LongKey << YAML::Key << "c" << YAML::Newline << YAML::Value << "car";
out << YAML::EndMap; 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) void NewlineInFlowMap(YAML::Emitter& out, std::string& desiredOutput)
...@@ -739,7 +740,7 @@ namespace Test ...@@ -739,7 +740,7 @@ namespace Test
out << YAML::Key << "a" << YAML::Value << "foo" << YAML::Newline; out << YAML::Key << "a" << YAML::Value << "foo" << YAML::Newline;
out << YAML::Key << "b" << YAML::Value << "bar"; out << YAML::Key << "b" << YAML::Value << "bar";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "{a: foo\n, b: bar}"; desiredOutput = "{a: foo,\nb: bar}";
} }
void LotsOfNewlines(YAML::Emitter& out, std::string& desiredOutput) void LotsOfNewlines(YAML::Emitter& out, std::string& desiredOutput)
...@@ -755,7 +756,7 @@ namespace Test ...@@ -755,7 +756,7 @@ namespace Test
out << YAML::LongKey << YAML::Key << "f" << YAML::Newline << YAML::Value << "foo"; out << YAML::LongKey << YAML::Key << "f" << YAML::Newline << YAML::Value << "foo";
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; 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) void Binary(YAML::Emitter& out, std::string& desiredOutput)
...@@ -911,7 +912,40 @@ namespace Test ...@@ -911,7 +912,40 @@ namespace Test
desiredOutput = "[31, 0x1f, 037]"; 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 // incorrect emitting
void ExtraEndSeq(YAML::Emitter& out, std::string& desiredError) void ExtraEndSeq(YAML::Emitter& out, std::string& desiredError)
...@@ -935,13 +969,6 @@ namespace Test ...@@ -935,13 +969,6 @@ namespace Test
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) void InvalidAnchor(YAML::Emitter& out, std::string& desiredError)
{ {
desiredError = YAML::ErrorMsg::INVALID_ANCHOR; desiredError = YAML::ErrorMsg::INVALID_ANCHOR;
...@@ -959,43 +986,6 @@ namespace Test ...@@ -959,43 +986,6 @@ namespace Test
out << YAML::Alias("new\nline"); out << YAML::Alias("new\nline");
out << YAML::EndSeq; 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 { namespace {
...@@ -1043,7 +1033,37 @@ namespace Test ...@@ -1043,7 +1033,37 @@ namespace Test
} }
total++; 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() bool RunEmitterTests()
{ {
...@@ -1126,17 +1146,16 @@ namespace Test ...@@ -1126,17 +1146,16 @@ namespace Test
RunEmitterTest(&Emitter::SetPrecision, "set precision", passed, total); RunEmitterTest(&Emitter::SetPrecision, "set precision", passed, total);
RunEmitterTest(&Emitter::DashInBlockContext, "dash in block context", passed, total); RunEmitterTest(&Emitter::DashInBlockContext, "dash in block context", passed, total);
RunEmitterTest(&Emitter::HexAndOct, "hex and oct", 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::ExtraEndSeq, "extra EndSeq", passed, total);
RunEmitterErrorTest(&Emitter::ExtraEndMap, "extra EndMap", 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::InvalidAnchor, "invalid anchor", passed, total);
RunEmitterErrorTest(&Emitter::InvalidAlias, "invalid alias", 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); RunEmitterErrorTest(&Emitter::BadLocalTag, "bad local tag", passed, total);
RunGenEmitterTests(passed, total);
std::cout << "Emitter tests: " << passed << "/" << total << " passed\n"; std::cout << "Emitter tests: " << passed << "/" << total << " passed\n";
return passed == total; 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 @@ ...@@ -5,18 +5,9 @@
#pragma once #pragma once
#endif #endif
#include <string> #include "teststruct.h"
namespace Test { 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 { namespace Spec {
// 2.1 // 2.1
TEST SeqScalars(); 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) add_executable(parse parse.cpp)
target_link_libraries(parse yaml-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