From 7d1508e090636534a08971690df0384f752fddf1 Mon Sep 17 00:00:00 2001 From: rtweeks21 Date: Sat, 16 Oct 2010 09:08:03 +0000 Subject: [PATCH] - Reintegrated the other-tags branch into my staging branch. Local edits were required to integrate with the changes from the event-api branch. --- include/emitter.h | 2 + include/emittermanip.h | 19 +++++-- src/emitter.cpp | 20 +++++-- src/emitterutils.cpp | 10 ++-- src/emitterutils.h | 2 +- src/scantoken.cpp | 8 ++- src/singledocparser.cpp | 12 +++++ src/token.cpp | 6 +++ src/token.h | 2 + test/emittertests.cpp | 28 ++++++++++ test/parsertests.cpp | 115 +++++++++++++++++++++++++++++++++++++++- 11 files changed, 210 insertions(+), 14 deletions(-) create mode 100644 src/token.cpp diff --git a/include/emitter.h b/include/emitter.h index a353e71..8c2b917 100644 --- a/include/emitter.h +++ b/include/emitter.h @@ -77,6 +77,8 @@ namespace YAML void EmitEndMap(); void EmitKey(); void EmitValue(); + void EmitKindTag(); + void EmitTag(bool verbatim, const _Tag& tag); private: ostream m_stream; diff --git a/include/emittermanip.h b/include/emittermanip.h index c88054f..bbf48c4 100644 --- a/include/emittermanip.h +++ b/include/emittermanip.h @@ -11,6 +11,7 @@ namespace YAML enum EMITTER_MANIP { // general manipulators Auto, + TagByKind, // output character set EmitNonAscii, @@ -82,14 +83,24 @@ namespace YAML } struct _Tag { - _Tag(const std::string& content_): content(content_), verbatim(true) {} + explicit _Tag(const std::string& content_) + : content(content_), verbatim(true) + { + } std::string content; bool verbatim; }; - inline _Tag VerbatimTag(const std::string& content) { - return _Tag(content); - } + typedef _Tag VerbatimTag; + + struct LocalTag : public _Tag + { + explicit LocalTag(const std::string& content_) + : _Tag(content_) + { + verbatim = false; + } + }; struct _Comment { _Comment(const std::string& content_): content(content_) {} diff --git a/src/emitter.cpp b/src/emitter.cpp index 675fcf8..8c3da9a 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -120,6 +120,9 @@ namespace YAML case Value: EmitValue(); break; + case TagByKind: + EmitKindTag(); + break; default: m_pState->SetLocalValue(value); break; @@ -651,15 +654,26 @@ namespace YAML if(!good()) return *this; + EmitTag(tag.verbatim, tag); + return *this; + } + + void Emitter::EmitTag(bool verbatim, const _Tag& tag) + { PreAtomicWrite(); EmitSeparationIfNecessary(); - if(!Utils::WriteTag(m_stream, tag.content)) { + if(!Utils::WriteTag(m_stream, tag.content, verbatim)) { m_pState->SetError(ErrorMsg::INVALID_TAG); - return *this; + return; } m_pState->RequireSeparation(); // Note: no PostAtomicWrite() because we need another value for this node - return *this; + } + + void Emitter::EmitKindTag() + { + _Tag tag(""); + EmitTag(false, tag); } Emitter& Emitter::Write(const _Comment& comment) diff --git a/src/emitterutils.cpp b/src/emitterutils.cpp index 62a7ef3..d0a5ea0 100644 --- a/src/emitterutils.cpp +++ b/src/emitterutils.cpp @@ -294,12 +294,13 @@ namespace YAML return WriteAliasName(out, str); } - bool WriteTag(ostream& out, const std::string& str) + bool WriteTag(ostream& out, const std::string& str, bool verbatim) { - out << "!<"; + out << (verbatim ? "!<" : "!"); StringCharSource buffer(str.c_str(), str.size()); + const RegEx& reValid = verbatim ? Exp::URI() : Exp::Tag(); while(buffer) { - int n = Exp::URI().Match(buffer); + int n = reValid.Match(buffer); if(n <= 0) return false; @@ -308,7 +309,8 @@ namespace YAML ++buffer; } } - out << ">"; + if (verbatim) + out << ">"; return true; } } diff --git a/src/emitterutils.h b/src/emitterutils.h index 7ceb6ff..df1c9ae 100644 --- a/src/emitterutils.h +++ b/src/emitterutils.h @@ -18,7 +18,7 @@ namespace YAML bool WriteComment(ostream& out, const std::string& str, int postCommentIndent); bool WriteAlias(ostream& out, const std::string& str); bool WriteAnchor(ostream& out, const std::string& str); - bool WriteTag(ostream& out, const std::string& str); + bool WriteTag(ostream& out, const std::string& str, bool verbatim); } } diff --git a/src/scantoken.cpp b/src/scantoken.cpp index 3e7e1cc..3d02130 100644 --- a/src/scantoken.cpp +++ b/src/scantoken.cpp @@ -276,7 +276,12 @@ namespace YAML } else { bool canBeHandle; token.value = ScanTagHandle(INPUT, canBeHandle); - token.data = (token.value.empty() ? Tag::SECONDARY_HANDLE : Tag::PRIMARY_HANDLE); + if(!canBeHandle && token.value.empty()) + token.data = Tag::NON_SPECIFIC; + else if(token.value.empty()) + token.data = Tag::SECONDARY_HANDLE; + else + token.data = Tag::PRIMARY_HANDLE; // is there a suffix? if(canBeHandle && INPUT.peek() == Keys::Tag) { @@ -323,6 +328,7 @@ namespace YAML Token token(Token::SCALAR, mark); token.value = scalar; + token.params.push_back(Token::PLAIN_SCALAR); m_tokens.push(token); } diff --git a/src/singledocparser.cpp b/src/singledocparser.cpp index 964212e..4519f2d 100644 --- a/src/singledocparser.cpp +++ b/src/singledocparser.cpp @@ -8,6 +8,7 @@ #include "token.h" #include #include +#include namespace YAML { @@ -73,6 +74,17 @@ namespace YAML anchor_t anchor; ParseProperties(tag, anchor); + if (tag.empty()) { + const Token& token = m_scanner.peek(); + const std::vector& tparams = token.params; + if (token.type != Token::SCALAR || + std::find(tparams.begin(), tparams.end(), Token::PLAIN_SCALAR) != tparams.end()) { + tag = "?"; + } else { + tag = "!"; + } + } + // now split based on what kind of node we should be switch(m_scanner.peek().type) { case Token::SCALAR: diff --git a/src/token.cpp b/src/token.cpp new file mode 100644 index 0000000..4a77c5a --- /dev/null +++ b/src/token.cpp @@ -0,0 +1,6 @@ +#include "token.h" + +namespace YAML +{ + const std::string Token::PLAIN_SCALAR("pln"); +} \ No newline at end of file diff --git a/src/token.h b/src/token.h index bf4f922..7cf4921 100644 --- a/src/token.h +++ b/src/token.h @@ -59,6 +59,8 @@ namespace YAML TAG, SCALAR }; + + static const std::string PLAIN_SCALAR; // data Token(TYPE type_, const Mark& mark_): status(VALID), type(type_), mark(mark_), data(0) {} diff --git a/test/emittertests.cpp b/test/emittertests.cpp index 58c3d92..bb4cb91 100644 --- a/test/emittertests.cpp +++ b/test/emittertests.cpp @@ -373,6 +373,31 @@ namespace Test desiredOutput = "---\n- !\n []\n- !\n {}"; } + void ByKindTagWithScalar(YAML::Emitter& out, std::string& desiredOutput) + { + out << YAML::BeginSeq; + out << YAML::DoubleQuoted << "12"; + out << "12"; + out << YAML::TagByKind << "12"; + out << YAML::EndSeq; + + desiredOutput = "---\n- \"12\"\n- 12\n- ! 12"; + } + + void LocalTagWithScalar(YAML::Emitter& out, std::string& desiredOutput) + { + out << YAML::LocalTag("foo") << "bar"; + + desiredOutput = "--- !foo bar"; + } + + void BadLocalTag(YAML::Emitter& out, std::string& desiredError) + { + out << YAML::LocalTag("e!far") << "bar"; + + desiredError = "invalid tag"; + } + void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput) { out << YAML::BeginMap; @@ -789,6 +814,8 @@ namespace Test RunEmitterTest(&Emitter::VerbatimTagWithEmptySeq, "verbatim tag with empty seq", passed, total); RunEmitterTest(&Emitter::VerbatimTagWithEmptyMap, "verbatim tag with empty map", passed, total); RunEmitterTest(&Emitter::VerbatimTagWithEmptySeqAndMap, "verbatim tag with empty seq and map", passed, total); + RunEmitterTest(&Emitter::ByKindTagWithScalar, "by-kind tag with scalar", passed, total); + RunEmitterTest(&Emitter::LocalTagWithScalar, "local tag with scalar", passed, total); RunEmitterTest(&Emitter::ComplexDoc, "complex doc", passed, total); RunEmitterTest(&Emitter::STLContainers, "STL containers", passed, total); RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed, total); @@ -815,6 +842,7 @@ namespace Test 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); std::cout << "Emitter tests: " << passed << "/" << total << " passed\n"; return passed == total; diff --git a/test/parsertests.cpp b/test/parsertests.cpp index 47b1d8b..4fa5c2e 100644 --- a/test/parsertests.cpp +++ b/test/parsertests.cpp @@ -706,6 +706,106 @@ namespace Test return false; return true; } + + void PrepareNodeForTagExam(YAML::Node& doc, const std::string& input) + { + std::stringstream stream(input); + YAML::Parser parser(stream); + parser.GetNextDocument(doc); + } + + struct TagMismatch: public std::exception { + TagMismatch(const std::string& actualTag, const std::string& expectedTag) { + std::stringstream output; + output << "Tag has value \"" << actualTag << "\" but \"" << expectedTag << "\" was expected"; + what_ = output.str(); + } + virtual ~TagMismatch() throw() {} + virtual const char *what() const throw() { return what_.c_str(); } + + private: + std::string what_; + }; + + bool ExpectedTagValue(YAML::Node& node, const char* tag) + { + if(node.GetTag() == tag) + return true; + + throw TagMismatch(node.GetTag(), tag); + } + + bool DefaultPlainScalarTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- 12"); + + return ExpectedTagValue(node, "?"); + } + + bool DefaultSingleQuotedScalarTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- '12'"); + + return ExpectedTagValue(node, "!"); + } + + bool ExplicitNonSpecificPlainScalarTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- ! 12"); + + return ExpectedTagValue(node, "!"); + } + + bool BasicLocalTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- !foo 12"); + + return ExpectedTagValue(node, "!foo"); + } + + bool VerbatimLocalTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- ! 12"); + + return ExpectedTagValue(node, "!foo"); + } + + bool StandardShortcutTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- !!int 12"); + + return ExpectedTagValue(node, "tag:yaml.org,2002:int"); + } + + bool VerbatimURITag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- ! 12"); + + return ExpectedTagValue(node, "tag:yaml.org,2002:int"); + } + + bool DefaultSequenceTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- [12]"); + + return ExpectedTagValue(node, "?"); + } + + bool ExplicitNonSpecificSequenceTag() + { + YAML::Node node; + PrepareNodeForTagExam(node, "--- ! [12]"); + + return ExpectedTagValue(node, "!"); + } } namespace { @@ -746,7 +846,10 @@ namespace Test ok = test(); } catch(const YAML::Exception& e) { ok = false; - error = e.msg; + error = e.what(); + } catch(const Parser::TagMismatch& e) { + ok = false; + error = e.what(); } if(ok) { passed++; @@ -969,6 +1072,16 @@ namespace Test RunParserTest(&Parser::Bases, "bases", passed, total); RunParserTest(&Parser::KeyNotFound, "key not found", passed, total); RunParserTest(&Parser::DuplicateKey, "duplicate key", passed, total); + RunParserTest(&Parser::DefaultPlainScalarTag, "default plain scalar tag", passed, total); + RunParserTest(&Parser::DefaultSingleQuotedScalarTag, "default single-quoted scalar tag", passed, total); + RunParserTest(&Parser::ExplicitNonSpecificPlainScalarTag, "explicit, non-specific plain scalar tag", passed, total); + RunParserTest(&Parser::BasicLocalTag, "basic local tag", passed, total); + RunParserTest(&Parser::VerbatimLocalTag, "verbatim local tag", passed, total); + RunParserTest(&Parser::StandardShortcutTag, "standard shortcut tag", passed, total); + RunParserTest(&Parser::VerbatimURITag, "verbatim URI tag", passed, total); + RunParserTest(&Parser::DefaultPlainScalarTag, "default plain scalar tag", passed, total); + RunParserTest(&Parser::DefaultSequenceTag, "default sequence tag", passed, total); + RunParserTest(&Parser::ExplicitNonSpecificSequenceTag, "explicit, non-specific sequence tag", passed, total); RunEncodingTest(&EncodeToUtf8, false, "UTF-8, no BOM", passed, total); RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed, total);