Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions include/yaml-cpp/tagresolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@

#include "yaml-cpp/dll.h"
#include "yaml-cpp/eventhandler.h"

#include <string>

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);
};

}
187 changes: 187 additions & 0 deletions src/tagresolver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#include "../include/yaml-cpp/tagresolver.h"

#include <vector>
#include <algorithm>

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<string> inf = { ".inf", ".Inf", ".INF" };
static std::vector<string> 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);
}

}