src/yaml-cpp/emitter.cpp
23b1bf8f
 #include "yaml-cpp/emitter.h"
d958d350
 #include "emitterstate.h"
 #include "emitterutils.h"
 #include "indentation.h"
23b1bf8f
 #include "yaml-cpp/exceptions.h"
d958d350
 #include <sstream>
 
 namespace YAML
 {	
 	Emitter::Emitter(): m_pState(new EmitterState)
 	{
 	}
 	
 	Emitter::~Emitter()
 	{
 	}
 	
 	const char *Emitter::c_str() const
 	{
 		return m_stream.str();
 	}
 	
 	unsigned Emitter::size() const
 	{
 		return m_stream.pos();
 	}
 	
 	// state checking
 	bool Emitter::good() const
 	{
 		return m_pState->good();
 	}
 	
 	const std::string Emitter::GetLastError() const
 	{
 		return m_pState->GetLastError();
 	}
 
 	// global setters
 	bool Emitter::SetOutputCharset(EMITTER_MANIP value)
 	{
 		return m_pState->SetOutputCharset(value, GLOBAL);
 	}
 
 	bool Emitter::SetStringFormat(EMITTER_MANIP value)
 	{
 		return m_pState->SetStringFormat(value, GLOBAL);
 	}
 	
 	bool Emitter::SetBoolFormat(EMITTER_MANIP value)
 	{
 		bool ok = false;
 		if(m_pState->SetBoolFormat(value, GLOBAL))
 			ok = true;
 		if(m_pState->SetBoolCaseFormat(value, GLOBAL))
 			ok = true;
 		if(m_pState->SetBoolLengthFormat(value, GLOBAL))
 			ok = true;
 		return ok;
 	}
 	
 	bool Emitter::SetIntBase(EMITTER_MANIP value)
 	{
 		return m_pState->SetIntFormat(value, GLOBAL);
 	}
 	
 	bool Emitter::SetSeqFormat(EMITTER_MANIP value)
 	{
 		return m_pState->SetFlowType(GT_SEQ, value, GLOBAL);
 	}
 	
 	bool Emitter::SetMapFormat(EMITTER_MANIP value)
 	{
 		bool ok = false;
 		if(m_pState->SetFlowType(GT_MAP, value, GLOBAL))
 			ok = true;
 		if(m_pState->SetMapKeyFormat(value, GLOBAL))
 			ok = true;
 		return ok;
 	}
 	
 	bool Emitter::SetIndent(unsigned n)
 	{
 		return m_pState->SetIndent(n, GLOBAL);
 	}
 	
 	bool Emitter::SetPreCommentIndent(unsigned n)
 	{
 		return m_pState->SetPreCommentIndent(n, GLOBAL);
 	}
 	
 	bool Emitter::SetPostCommentIndent(unsigned n)
 	{
 		return m_pState->SetPostCommentIndent(n, GLOBAL);
 	}
 
 	// SetLocalValue
 	// . Either start/end a group, or set a modifier locally
 	Emitter& Emitter::SetLocalValue(EMITTER_MANIP value)
 	{
 		if(!good())
 			return *this;
 		
 		switch(value) {
23b1bf8f
 			case BeginDoc:
 				EmitBeginDoc();
 				break;
 			case EndDoc:
 				EmitEndDoc();
 				break;
d958d350
 			case BeginSeq:
 				EmitBeginSeq();
 				break;
 			case EndSeq:
 				EmitEndSeq();
 				break;
 			case BeginMap:
 				EmitBeginMap();
 				break;
 			case EndMap:
 				EmitEndMap();
 				break;
 			case Key:
 				EmitKey();
 				break;
 			case Value:
 				EmitValue();
 				break;
23b1bf8f
 			case TagByKind:
 				EmitKindTag();
 				break;
 			case Newline:
 				EmitNewline();
 				break;
d958d350
 			default:
 				m_pState->SetLocalValue(value);
 				break;
 		}
 		return *this;
 	}
 	
 	Emitter& Emitter::SetLocalIndent(const _Indent& indent)
 	{
 		m_pState->SetIndent(indent.value, LOCAL);
 		return *this;
 	}
 
 	// GotoNextPreAtomicState
 	// . Runs the state machine, emitting if necessary, and returns 'true' if done (i.e., ready to emit an atom)
 	bool Emitter::GotoNextPreAtomicState()
 	{
 		if(!good())
 			return true;
 		
 		unsigned curIndent = m_pState->GetCurIndent();
 		
 		EMITTER_STATE curState = m_pState->GetCurState();
 		switch(curState) {
 				// document-level
 			case ES_WAITING_FOR_DOC:
 				m_pState->SwitchState(ES_WRITING_DOC);
 				return true;
 			case ES_WRITING_DOC:
 				return true;
23b1bf8f
 			case ES_DONE_WITH_DOC:
 				EmitBeginDoc();
 				return false;
d958d350
 				
 				// block sequence
 			case ES_WAITING_FOR_BLOCK_SEQ_ENTRY:
 				m_stream << IndentTo(curIndent) << "-";
23b1bf8f
 				m_pState->RequireSoftSeparation();
d958d350
 				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:
 				m_stream << ',';
23b1bf8f
 				m_pState->RequireSoftSeparation();
d958d350
 				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) << '?';
23b1bf8f
 					m_pState->RequireSoftSeparation();
d958d350
 				}
 				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:
 				m_pState->SwitchState(ES_WRITING_FLOW_MAP_KEY);
 				if(m_pState->CurrentlyInLongKey()) {
 					EmitSeparationIfNecessary();
 					m_stream << '?';
23b1bf8f
 					m_pState->RequireSoftSeparation();
d958d350
 				}
 				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:
 				m_stream << ':';
23b1bf8f
 				m_pState->RequireSoftSeparation();
d958d350
 				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:
23b1bf8f
 				if(!m_pState->CurrentlyInLongKey()) {
 					m_stream << ':';
 					m_pState->RequireSoftSeparation();
 				}
d958d350
 				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;
 		
23b1bf8f
 		if(m_pState->RequiresSoftSeparation())
d958d350
 			m_stream << ' ';
23b1bf8f
 		else if(m_pState->RequiresHardSeparation())
 			m_stream << '\n';
d958d350
 		m_pState->UnsetSeparation();
 	}
 	
23b1bf8f
 	// EmitBeginDoc
 	void Emitter::EmitBeginDoc()
 	{
 		if(!good())
 			return;
 		
 		EMITTER_STATE curState = m_pState->GetCurState();
 		if(curState != ES_WAITING_FOR_DOC && curState != ES_WRITING_DOC && curState != ES_DONE_WITH_DOC) {
 			m_pState->SetError("Unexpected begin document");
 			return;
 		}
 		
 		if(curState == ES_WRITING_DOC || curState == ES_DONE_WITH_DOC)
 			m_stream << '\n';		
 		m_stream << "---\n";
 
 		m_pState->UnsetSeparation();
 		m_pState->SwitchState(ES_WAITING_FOR_DOC);
 	}
 	
 	// EmitEndDoc
 	void Emitter::EmitEndDoc()
 	{
 		if(!good())
 			return;
 
 		
 		EMITTER_STATE curState = m_pState->GetCurState();
 		if(curState != ES_WAITING_FOR_DOC && curState != ES_WRITING_DOC && curState != ES_DONE_WITH_DOC) {
 			m_pState->SetError("Unexpected end document");
 			return;
 		}
 		
 		if(curState == ES_WRITING_DOC || curState == ES_DONE_WITH_DOC)
 			m_stream << '\n';		
 		m_stream << "...\n";
 		
 		m_pState->UnsetSeparation();
 		m_pState->SwitchState(ES_WAITING_FOR_DOC);
 	}
 
d958d350
 	// EmitBeginSeq
 	void Emitter::EmitBeginSeq()
 	{
 		if(!good())
 			return;
 		
 		// must have a long key if we're emitting a sequence
 		m_pState->StartLongKey();
 		
 		PreAtomicWrite();
 		
 		EMITTER_STATE curState = m_pState->GetCurState();
 		EMITTER_MANIP flowType = m_pState->GetFlowType(GT_SEQ);
 		if(flowType == Block) {
 			if(curState == ES_WRITING_BLOCK_SEQ_ENTRY ||
 			   curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE ||
 			   curState == ES_WRITING_DOC
 			) {
23b1bf8f
 				if(m_pState->RequiresHardSeparation() || curState != ES_WRITING_DOC) {
 					m_stream << "\n";
 					m_pState->UnsetSeparation();
 				}
d958d350
 			}
 			m_pState->PushState(ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
 		} else if(flowType == Flow) {
 			EmitSeparationIfNecessary();
 			m_stream << "[";
 			m_pState->PushState(ES_WAITING_FOR_FLOW_SEQ_ENTRY);
 		} else
 			assert(false);
 
 		m_pState->BeginGroup(GT_SEQ);
 	}
 	
 	// EmitEndSeq
 	void Emitter::EmitEndSeq()
 	{
 		if(!good())
 			return;
 		
 		if(m_pState->GetCurGroupType() != GT_SEQ)
 			return m_pState->SetError(ErrorMsg::UNEXPECTED_END_SEQ);
 		
 		EMITTER_STATE curState = m_pState->GetCurState();
 		FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
 		if(flowType == FT_BLOCK) {
 			// Note: block sequences are *not* allowed to be empty, but we convert it
 			//       to a flow sequence if it is
 			assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY || curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
 			if(curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY) {
 				// Note: only one of these will actually output anything for a given situation
 				EmitSeparationIfNecessary();
 				unsigned curIndent = m_pState->GetCurIndent();
 				m_stream << IndentTo(curIndent);
 
 				m_stream << "[]";
 			}
 		} else if(flowType == FT_FLOW) {
 			// Note: flow sequences are allowed to be empty
 			assert(curState == ES_DONE_WITH_FLOW_SEQ_ENTRY || curState == ES_WAITING_FOR_FLOW_SEQ_ENTRY);
 			m_stream << "]";
 		} else
 			assert(false);
 		
 		m_pState->PopState();
 		m_pState->EndGroup(GT_SEQ);
 
 		PostAtomicWrite();
 	}
 	
 	// EmitBeginMap
 	void Emitter::EmitBeginMap()
 	{
 		if(!good())
 			return;
 		
 		// must have a long key if we're emitting a map
 		m_pState->StartLongKey();
 
 		PreAtomicWrite();
 
 		EMITTER_STATE curState = m_pState->GetCurState();
 		EMITTER_MANIP flowType = m_pState->GetFlowType(GT_MAP);
 		if(flowType == Block) {
 			if(curState == ES_WRITING_BLOCK_SEQ_ENTRY ||
 			   curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE ||
 			   curState == ES_WRITING_DOC
 			) {
23b1bf8f
 				if(m_pState->RequiresHardSeparation() || (curState != ES_WRITING_DOC && curState != ES_WRITING_BLOCK_SEQ_ENTRY)) {
 					m_stream << "\n";
 					m_pState->UnsetSeparation();
 				}
d958d350
 			}
 			m_pState->PushState(ES_WAITING_FOR_BLOCK_MAP_ENTRY);
 		} else if(flowType == Flow) {
 			EmitSeparationIfNecessary();
 			m_stream << "{";
 			m_pState->PushState(ES_WAITING_FOR_FLOW_MAP_ENTRY);
 		} else
 			assert(false);
 		
 		m_pState->BeginGroup(GT_MAP);
 	}
 	
 	// EmitEndMap
 	void Emitter::EmitEndMap()
 	{
 		if(!good())
 			return;
 		
 		if(m_pState->GetCurGroupType() != GT_MAP)
 			return m_pState->SetError(ErrorMsg::UNEXPECTED_END_MAP);
 
 		EMITTER_STATE curState = m_pState->GetCurState();
 		FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
 		if(flowType == FT_BLOCK) {
 			// 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);
 			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);
23b1bf8f
 			m_pState->UnsetSeparation();
d958d350
 			m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_KEY);
 		} else if(flowType == FT_FLOW) {
 			if(curState == ES_DONE_WITH_FLOW_MAP_VALUE) {
 				m_stream << ',';
23b1bf8f
 				m_pState->RequireSoftSeparation();
d958d350
 			}
 			m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_KEY);
 		} else
 			assert(false);
 		
 		if(m_pState->GetMapKeyFormat() == LongKey)
 			m_pState->StartLongKey();
 		else if(m_pState->GetMapKeyFormat() == Auto)
 			m_pState->StartSimpleKey();
 		else
 			assert(false);
 	}
 	
 	// EmitValue
 	void Emitter::EmitValue()
 	{
 		if(!good())
 			return;
 
 		EMITTER_STATE curState = m_pState->GetCurState();
 		FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
 		if(curState != ES_DONE_WITH_BLOCK_MAP_KEY && curState != ES_DONE_WITH_FLOW_MAP_KEY)
 			return m_pState->SetError(ErrorMsg::UNEXPECTED_VALUE_TOKEN);
 
 		if(flowType == FT_BLOCK) {
23b1bf8f
 			if(m_pState->CurrentlyInLongKey()) {
d958d350
 				m_stream << '\n';
23b1bf8f
 				m_stream << IndentTo(m_pState->GetCurIndent());
 				m_stream << ':';
 				m_pState->RequireSoftSeparation();
 			}
d958d350
 			m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_VALUE);
 		} else if(flowType == FT_FLOW) {
 			m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_VALUE);
 		} else
 			assert(false);
 	}
 
23b1bf8f
 	// EmitNewline
 	void Emitter::EmitNewline()
 	{
 		if(!good())
 			return;
 
 		if(CanEmitNewline()) {
 			m_stream << '\n';
 			m_pState->UnsetSeparation();
 		}
 	}
 
 	bool Emitter::CanEmitNewline() const
 	{
 		FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
 		if(flowType == FT_BLOCK && m_pState->CurrentlyInLongKey())
 			return true;
 
 		EMITTER_STATE curState = m_pState->GetCurState();
 		return curState != ES_DONE_WITH_BLOCK_MAP_KEY && curState != ES_WAITING_FOR_BLOCK_MAP_VALUE && curState != ES_WRITING_BLOCK_MAP_VALUE;
 	}
 
d958d350
 	// *******************************************************************************************
 	// overloads of Write
 	
 	Emitter& Emitter::Write(const std::string& str)
 	{
 		if(!good())
 			return *this;
 		
 		// literal scalars must use long keys
 		if(m_pState->GetStringFormat() == Literal && m_pState->GetCurGroupFlowType() != FT_FLOW)
 			m_pState->StartLongKey();
 		
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		
 		bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii;
 		EMITTER_MANIP strFmt = m_pState->GetStringFormat();
 		FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
 		unsigned curIndent = m_pState->GetCurIndent();
 
 		switch(strFmt) {
 			case Auto:
 				Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii);
 				break;
 			case SingleQuoted:
 				if(!Utils::WriteSingleQuotedString(m_stream, str)) {
 					m_pState->SetError(ErrorMsg::SINGLE_QUOTED_CHAR);
 					return *this;
 				}
 				break;
 			case DoubleQuoted:
 				Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii);
 				break;
 			case Literal:
 				if(flowType == FT_FLOW)
 					Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii);
 				else
 					Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent());
 				break;
 			default:
 				assert(false);
 		}
 		
 		PostAtomicWrite();
 		return *this;
 	}
23b1bf8f
 
d958d350
 	void Emitter::PreWriteIntegralType(std::stringstream& str)
 	{
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		
 		EMITTER_MANIP intFmt = m_pState->GetIntFormat();
 		switch(intFmt) {
 			case Dec:
 				str << std::dec;
 				break;
 			case Hex:
 				str << std::hex;
 				break;
 				case Oct:
 				str << std::oct;
 				break;
 			default:
 				assert(false);
 		}
 	}
23b1bf8f
 
 	void Emitter::PreWriteStreamable(std::stringstream& str)
 	{
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		str.precision(15);
 	}
 
d958d350
 	void Emitter::PostWriteIntegralType(const std::stringstream& str)
 	{
 		m_stream << str.str();
 		PostAtomicWrite();
 	}
23b1bf8f
 
 	void Emitter::PostWriteStreamable(const std::stringstream& str)
 	{
 		m_stream << str.str();
 		PostAtomicWrite();
 	}
 
 	const char *Emitter::ComputeFullBoolName(bool b) const
 	{
 		const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool ? YesNoBool : m_pState->GetBoolFormat());
 		const EMITTER_MANIP caseFmt = m_pState->GetBoolCaseFormat();
 		switch(mainFmt) {
 			case YesNoBool:
 				switch(caseFmt) {
 					case UpperCase: return b ? "YES" : "NO";
 					case CamelCase: return b ? "Yes" : "No";
 					case LowerCase: return b ? "yes" : "no";
 					default: break;
 				}
 				break;
 			case OnOffBool:
 				switch(caseFmt) {
 					case UpperCase: return b ? "ON" : "OFF";
 					case CamelCase: return b ? "On" : "Off";
 					case LowerCase: return b ? "on" : "off";
 					default: break;
 				}
 				break;
 			case TrueFalseBool:
 				switch(caseFmt) {
 					case UpperCase: return b ? "TRUE" : "FALSE";
 					case CamelCase: return b ? "True" : "False";
 					case LowerCase: return b ? "true" : "false";
 					default: break;
 				}
 				break;
 			default:
 				break;
 		}
 		return b ? "y" : "n"; // should never get here, but it can't hurt to give these answers
 	}
 
d958d350
 	Emitter& Emitter::Write(bool b)
 	{
 		if(!good())
 			return *this;
 		
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
23b1bf8f
 	
 		const char *name = ComputeFullBoolName(b);
 		if(m_pState->GetBoolLengthFormat() == ShortBool)
d958d350
 			m_stream << name[0];
 		else
 			m_stream << name;
23b1bf8f
 
d958d350
 		PostAtomicWrite();
 		return *this;
 	}
 
 	Emitter& Emitter::Write(const _Alias& alias)
 	{
 		if(!good())
 			return *this;
 		
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		if(!Utils::WriteAlias(m_stream, alias.content)) {
 			m_pState->SetError(ErrorMsg::INVALID_ALIAS);
 			return *this;
 		}
 		PostAtomicWrite();
 		return *this;
 	}
 	
 	Emitter& Emitter::Write(const _Anchor& anchor)
 	{
 		if(!good())
 			return *this;
 		
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		if(!Utils::WriteAnchor(m_stream, anchor.content)) {
 			m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
 			return *this;
 		}
23b1bf8f
 		m_pState->RequireHardSeparation();
d958d350
 		// Note: no PostAtomicWrite() because we need another value for this node
 		return *this;
 	}
 	
 	Emitter& Emitter::Write(const _Tag& tag)
 	{
 		if(!good())
 			return *this;
23b1bf8f
 
d958d350
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
23b1bf8f
 		
 		bool success = false;
 		if(tag.type == _Tag::Type::Verbatim)
 			success = Utils::WriteTag(m_stream, tag.content, true);
 		else if(tag.type == _Tag::Type::PrimaryHandle)
 			success = Utils::WriteTag(m_stream, tag.content, false);
 		else
 			success = Utils::WriteTagWithPrefix(m_stream, tag.prefix, tag.content);
 		
 		if(!success) {
d958d350
 			m_pState->SetError(ErrorMsg::INVALID_TAG);
 			return *this;
 		}
23b1bf8f
 		
 		m_pState->RequireHardSeparation();
d958d350
 		// Note: no PostAtomicWrite() because we need another value for this node
 		return *this;
 	}
 
23b1bf8f
 	void Emitter::EmitKindTag()
 	{
 		Write(LocalTag(""));
 	}
 
d958d350
 	Emitter& Emitter::Write(const _Comment& comment)
 	{
 		if(!good())
 			return *this;
 		
 		m_stream << Indentation(m_pState->GetPreCommentIndent());
 		Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent());
 		return *this;
 	}
 
 	Emitter& Emitter::Write(const _Null& /*null*/)
 	{
 		if(!good())
 			return *this;
 		
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		m_stream << "~";
 		PostAtomicWrite();
 		return *this;
 	}
23b1bf8f
 
 	Emitter& Emitter::Write(const _Binary& binary)
 	{
 		Write(SecondaryTag("binary"));
 
 		if(!good())
 			return *this;
 		
 		PreAtomicWrite();
 		EmitSeparationIfNecessary();
 		Utils::WriteBinary(m_stream, binary.data, binary.size);
 		PostAtomicWrite();
 		return *this;
 	}
d958d350
 }