diff --git a/include/yaml-cpp/tagresolver.h b/include/yaml-cpp/tagresolver.h new file mode 100644 index 0000000..d474efb --- /dev/null +++ b/include/yaml-cpp/tagresolver.h @@ -0,0 +1,151 @@ + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/eventhandler.h" + +#include + +namespace YAML { +struct Mark; + +/** + * TagResolver is a base class for tag resolution. + * It acts as an event filter: it is an EventHandler itself, + * and it passes events on to a client EventHandler + * while resolving tags in the process. + + * This base class passes events unmodified. + * A subclass is supposed to implement tag resolution + * according to a particular schema, + * as recommended by the YAML 1.2 spec: + * http://yaml.org/spec/1.2/spec.html#Schema + + * Note that each tag in a schema also needs to provide + * a canonical representation of its values. + * Mapping values into canonical representations + * is not the responsibility of TagResolver though. + */ + +class YAML_CPP_API TagResolver : public EventHandler +{ +public: + using string = std::string; + + TagResolver(EventHandler & client): m_client(client) {} + + virtual void OnDocumentStart(const Mark& mark) + { + m_client.OnDocumentStart(mark); + } + + virtual void OnDocumentEnd() + { + m_client.OnDocumentEnd(); + } + + virtual void OnNull(const Mark& mark, anchor_t anchor) + { + m_client.OnNull(mark, anchor); + } + + virtual void OnAlias(const Mark& mark, anchor_t anchor) + { + m_client.OnAlias(mark, anchor); + } + + virtual void OnScalar(const Mark& mark, const std::string& tag, + anchor_t anchor, const std::string& value) + { + m_client.OnScalar(mark, tag, anchor, value); + } + + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) + { + m_client.OnSequenceStart(mark, tag, anchor, style); + } + + virtual void OnSequenceEnd() + { + m_client.OnSequenceEnd(); + } + + virtual void OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) + { + m_client.OnMapStart(mark, tag, anchor, style); + } + + virtual void OnMapEnd() + { + m_client.OnMapEnd(); + } + + EventHandler & m_client; +}; + +/** + * Implements tag resolution according to Core Schema, + * as defined by YAML 1.2 spec: + * http://yaml.org/spec/1.2/spec.html#id2804923 + */ + +class YAML_CPP_API CoreTagResolver : public TagResolver +{ +private: + + static bool IsBase10Digit(char c) + { + return c >= '0' and c <= '9'; + } + + static bool IsBase8Digit(char c) + { + return c >= '0' and c <= '7'; + } + + static bool IsBase16Digit(char c) + { + return IsBase10Digit(c) || (c >= 'a' and c <= 'f') || (c >= 'A' and c <= 'F'); + } + + static bool IsBase8Int(const string & text); + static bool IsBase16Int(const string & text); + static bool isBase10Int(const string & text); + +public: + CoreTagResolver(EventHandler & client): TagResolver(client) {} + + static bool TagIsNonSpecific(const string & tag) + { + return (tag == "?" || tag == "!"); + } + + + static bool ScalarIsNull(const string & text) + { + return (text == "null" || text == "Null" || text == "NULL" || text == "~"); + } + + static bool ScalarIsBool(const string & text) + { + return text == "true" || + text == "True" || + text == "TRUE" || + text == "false" || + text == "False" || + text == "FALSE"; + } + + static bool ScalarIsInt(const string & text); + + static bool ScalarIsFloat(const string & text); + + virtual void OnScalar(const Mark& mark, const std::string& tag, + anchor_t anchor, const std::string& value); + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style); + virtual void OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style); +}; + +} diff --git a/src/tagresolver.cpp b/src/tagresolver.cpp new file mode 100644 index 0000000..e1224c4 --- /dev/null +++ b/src/tagresolver.cpp @@ -0,0 +1,187 @@ +#include "../include/yaml-cpp/tagresolver.h" + +#include +#include + +namespace YAML { + +bool CoreTagResolver::IsBase8Int(const string & text) +{ + if (text.empty()) + return false; + for (string::size_type i = 0; i < text.size(); ++i) + { + if (!IsBase8Digit(text[i])) + return false; + } + return true; +} + +bool CoreTagResolver::IsBase16Int(const string & text) +{ + if (text.empty()) + return false; + for (string::size_type i = 0; i < text.size(); ++i) + { + if (!IsBase16Digit(text[i])) + return false; + } + return true; +} + +bool CoreTagResolver::isBase10Int(const string & text) +{ + using namespace std; + + if (text.empty()) + return false; + string::size_type pos = 0; + if ( text[pos] == '-' || + text[pos] == '+' ) + pos += 1; + if (pos == text.size()) + return false; + for (; pos < text.size(); ++pos) + { + if (!IsBase10Digit(text[pos])) + return false; + } + return true; +} + +bool CoreTagResolver::ScalarIsInt(const string & text) +{ + if (text.substr(0,2) == "0o") + { + return IsBase8Int(text.substr(2)); + } + else if (text.substr(0,2) == "0x") + { + return IsBase16Int(text.substr(2)); + } + else + { + return isBase10Int(text); + } +} + +bool CoreTagResolver::ScalarIsFloat(const string & text) +{ + static std::vector inf = { ".inf", ".Inf", ".INF" }; + static std::vector nan = { ".nan", ".NaN", ".NAN" }; + + if (text.empty()) + return false; + + if (std::find(nan.begin(), nan.end(), text) != nan.end()) + return true; + + string::size_type pos = 0; + + if ( text[0] == '-' || + text[0] == '+' ) + { + pos += 1; + } + + if (pos == text.size()) + return false; + + if (std::find(inf.begin(), inf.end(), text.substr(pos)) != inf.end()) + return true; + + string::size_type intStart = pos; + while(pos < text.size() && IsBase10Digit(text[pos])) + { + ++pos; + } + bool hasInt = pos > intStart; + + if (pos == text.size()) + return false; + + if (text[pos] != '.') + return false; + ++pos; + + string::size_type fracStart = pos; + while(pos < text.size() && IsBase10Digit(text[pos])) + { + ++pos; + } + bool hasFrac = pos > fracStart; + + if (!hasInt && !hasFrac) + return false; + + if (pos == text.size()) + return true; + + if (text[pos] != 'e' && text[pos] != 'E') + return false; + ++pos; + + if ( text[pos] == '-' || text[pos] == '+' ) + ++pos; + + string::size_type expStart = pos; + while(pos < text.size() && IsBase10Digit(text[pos])) + { + ++pos; + } + if (pos == expStart) + return false; + if (pos != text.size()) + return false; + return true; +} + +void CoreTagResolver::OnScalar(const Mark& mark, const std::string& tag, + anchor_t anchor, const std::string& value) +{ + if (tag == "!") + { + TagResolver::OnScalar(mark, "tag:yaml.org,2002:str", anchor, value); + } + else if (tag == "?") + { + string resolved_tag; + + if (ScalarIsNull(value)) + resolved_tag = "tag:yaml.org,2002:null"; + else if (ScalarIsBool(value)) + resolved_tag = "tag:yaml.org,2002:bool"; + else if (ScalarIsInt(value)) + resolved_tag = "tag:yaml.org,2002:int"; + else if (ScalarIsFloat(value)) + resolved_tag = "tag:yaml.org,2002:float"; + else + resolved_tag = "tag:yaml.org,2002:str"; + + TagResolver::OnScalar(mark, resolved_tag, anchor, value); + } + else + { + TagResolver::OnScalar(mark, tag, anchor, value); + } +} + +void CoreTagResolver::OnSequenceStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) +{ + if (TagIsNonSpecific(tag)) + TagResolver::OnSequenceStart(mark, "tag:yaml.org,2002:seq", anchor, style); + else + TagResolver::OnSequenceStart(mark, tag, anchor, style); +} + +void CoreTagResolver::OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) +{ + if (TagIsNonSpecific(tag)) + TagResolver::OnMapStart(mark, "tag:yaml.org,2002:map", anchor, style); + else + TagResolver::OnMapStart(mark, tag, anchor, style); +} + +}