2774 lines
74 KiB
C++
2774 lines
74 KiB
C++
/*
|
|
* MIT License
|
|
*
|
|
* Copyright(c) 2018 Jimmie Bergmann
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files(the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions :
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "Yaml.hpp"
|
|
#include <memory>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <cstdio>
|
|
#include <stdarg.h>
|
|
|
|
|
|
// Implementation access definitions.
|
|
#define NODE_IMP static_cast<NodeImp*>(m_pImp)
|
|
#define NODE_IMP_EXT(node) static_cast<NodeImp*>(node.m_pImp)
|
|
#define TYPE_IMP static_cast<NodeImp*>(m_pImp)->m_pImp
|
|
|
|
|
|
#define IT_IMP static_cast<IteratorImp*>(m_pImp)
|
|
|
|
|
|
namespace Yaml
|
|
{
|
|
class ReaderLine;
|
|
|
|
// Exception message definitions.
|
|
static const std::string g_ErrorInvalidCharacter = "Invalid character found.";
|
|
static const std::string g_ErrorKeyMissing = "Missing key.";
|
|
static const std::string g_ErrorKeyIncorrect = "Incorrect key.";
|
|
static const std::string g_ErrorValueIncorrect = "Incorrect value.";
|
|
static const std::string g_ErrorTabInOffset = "Tab found in offset.";
|
|
static const std::string g_ErrorBlockSequenceNotAllowed = "Sequence entries are not allowed in this context.";
|
|
static const std::string g_ErrorUnexpectedDocumentEnd = "Unexpected document end.";
|
|
static const std::string g_ErrorDiffEntryNotAllowed = "Different entry is not allowed in this context.";
|
|
static const std::string g_ErrorIncorrectOffset = "Incorrect offset.";
|
|
static const std::string g_ErrorSequenceError = "Error in sequence node.";
|
|
static const std::string g_ErrorCannotOpenFile = "Cannot open file.";
|
|
static const std::string g_ErrorIndentation = "Space indentation is less than 2.";
|
|
static const std::string g_ErrorInvalidBlockScalar = "Invalid block scalar.";
|
|
static const std::string g_ErrorInvalidQuote = "Invalid quote.";
|
|
static const std::string g_EmptyString = "";
|
|
static Yaml::Node g_NoneNode;
|
|
|
|
// Global function definitions. Implemented at end of this source file.
|
|
static std::string ExceptionMessage(const std::string & message, ReaderLine & line);
|
|
static std::string ExceptionMessage(const std::string & message, ReaderLine & line, const size_t errorPos);
|
|
static std::string ExceptionMessage(const std::string & message, const size_t errorLine, const size_t errorPos);
|
|
static std::string ExceptionMessage(const std::string & message, const size_t errorLine, const std::string & data);
|
|
|
|
static bool FindQuote(const std::string & input, size_t & start, size_t & end, size_t searchPos = 0);
|
|
static size_t FindNotCited(const std::string & input, char token, size_t & preQuoteCount);
|
|
static size_t FindNotCited(const std::string & input, char token);
|
|
static bool ValidateQuote(const std::string & input);
|
|
static void CopyNode(const Node & from, Node & to);
|
|
static bool ShouldBeCited(const std::string & key);
|
|
static void AddEscapeTokens(std::string & input, const std::string & tokens);
|
|
static void RemoveAllEscapeTokens(std::string & input);
|
|
|
|
// Exception implementations
|
|
Exception::Exception(const std::string & message, const eType type) :
|
|
std::runtime_error(message),
|
|
m_Type(type)
|
|
{
|
|
}
|
|
|
|
Exception::eType Exception::Type() const
|
|
{
|
|
return m_Type;
|
|
}
|
|
|
|
const char * Exception::Message() const
|
|
{
|
|
return what();
|
|
}
|
|
|
|
InternalException::InternalException(const std::string & message) :
|
|
Exception(message, InternalError)
|
|
{
|
|
|
|
}
|
|
|
|
ParsingException::ParsingException(const std::string & message) :
|
|
Exception(message, ParsingError)
|
|
{
|
|
|
|
}
|
|
|
|
OperationException::OperationException(const std::string & message) :
|
|
Exception(message, OperationError)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
class TypeImp
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~TypeImp()
|
|
{
|
|
}
|
|
|
|
virtual const std::string & GetData() const = 0;
|
|
virtual bool SetData(const std::string & data) = 0;
|
|
virtual size_t GetSize() const = 0;
|
|
virtual Node * GetNode(const size_t index) = 0;
|
|
virtual Node * GetNode(const std::string & key) = 0;
|
|
virtual Node * Insert(const size_t index) = 0;
|
|
virtual Node * PushFront() = 0;
|
|
virtual Node * PushBack() = 0;
|
|
virtual void Erase(const size_t index) = 0;
|
|
virtual void Erase(const std::string & key) = 0;
|
|
|
|
};
|
|
|
|
class SequenceImp : public TypeImp
|
|
{
|
|
|
|
public:
|
|
|
|
~SequenceImp()
|
|
{
|
|
for(auto it = m_Sequence.begin(); it != m_Sequence.end(); it++)
|
|
{
|
|
delete it->second;
|
|
}
|
|
}
|
|
|
|
virtual const std::string & GetData() const
|
|
{
|
|
return g_EmptyString;
|
|
}
|
|
|
|
virtual bool SetData(const std::string & data)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual size_t GetSize() const
|
|
{
|
|
return m_Sequence.size();
|
|
}
|
|
|
|
virtual Node * GetNode(const size_t index)
|
|
{
|
|
auto it = m_Sequence.find(index);
|
|
if(it != m_Sequence.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * GetNode(const std::string & key)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * Insert(const size_t index)
|
|
{
|
|
if(m_Sequence.size() == 0)
|
|
{
|
|
Node * pNode = new Node;
|
|
m_Sequence.insert({0, pNode});
|
|
return pNode;
|
|
}
|
|
|
|
if(index >= m_Sequence.size())
|
|
{
|
|
auto it = m_Sequence.end();
|
|
--it;
|
|
Node * pNode = new Node;
|
|
m_Sequence.insert({it->first, pNode});
|
|
return pNode;
|
|
}
|
|
|
|
auto it = m_Sequence.cbegin();
|
|
while(it != m_Sequence.cend())
|
|
{
|
|
m_Sequence[it->first+1] = it->second;
|
|
|
|
if(it->first == index)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Node * pNode = new Node;
|
|
m_Sequence.insert({index, pNode});
|
|
return pNode;
|
|
}
|
|
|
|
virtual Node * PushFront()
|
|
{
|
|
for(auto it = m_Sequence.cbegin(); it != m_Sequence.cend(); it++)
|
|
{
|
|
m_Sequence[it->first+1] = it->second;
|
|
}
|
|
|
|
Node * pNode = new Node;
|
|
m_Sequence.insert({0, pNode});
|
|
return pNode;
|
|
}
|
|
|
|
virtual Node * PushBack()
|
|
{
|
|
size_t index = 0;
|
|
if(m_Sequence.size())
|
|
{
|
|
auto it = m_Sequence.end();
|
|
--it;
|
|
index = it->first + 1;
|
|
}
|
|
|
|
Node * pNode = new Node;
|
|
m_Sequence.insert({index, pNode});
|
|
return pNode;
|
|
}
|
|
|
|
virtual void Erase(const size_t index)
|
|
{
|
|
auto it = m_Sequence.find(index);
|
|
if(it == m_Sequence.end())
|
|
{
|
|
return;
|
|
}
|
|
delete it->second;
|
|
m_Sequence.erase(index);
|
|
}
|
|
|
|
virtual void Erase(const std::string & key)
|
|
{
|
|
}
|
|
|
|
std::map<size_t, Node*> m_Sequence;
|
|
|
|
};
|
|
|
|
class MapImp : public TypeImp
|
|
{
|
|
|
|
public:
|
|
|
|
~MapImp()
|
|
{
|
|
for(auto it = m_Map.begin(); it != m_Map.end(); it++)
|
|
{
|
|
delete it->second;
|
|
}
|
|
}
|
|
|
|
virtual const std::string & GetData() const
|
|
{
|
|
return g_EmptyString;
|
|
}
|
|
|
|
virtual bool SetData(const std::string & data)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual size_t GetSize() const
|
|
{
|
|
return m_Map.size();
|
|
}
|
|
|
|
virtual Node * GetNode(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * GetNode(const std::string & key)
|
|
{
|
|
auto it = m_Map.find(key);
|
|
if(it == m_Map.end())
|
|
{
|
|
Node * pNode = new Node;
|
|
m_Map.insert({key, pNode});
|
|
return pNode;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
virtual Node * Insert(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * PushFront()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * PushBack()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual void Erase(const size_t index)
|
|
{
|
|
}
|
|
|
|
virtual void Erase(const std::string & key)
|
|
{
|
|
auto it = m_Map.find(key);
|
|
if(it == m_Map.end())
|
|
{
|
|
return;
|
|
}
|
|
delete it->second;
|
|
m_Map.erase(key);
|
|
}
|
|
|
|
std::map<std::string, Node*> m_Map;
|
|
|
|
};
|
|
|
|
class ScalarImp : public TypeImp
|
|
{
|
|
|
|
public:
|
|
|
|
~ScalarImp()
|
|
{
|
|
}
|
|
|
|
virtual const std::string & GetData() const
|
|
{
|
|
return m_Value;
|
|
}
|
|
|
|
virtual bool SetData(const std::string & data)
|
|
{
|
|
m_Value = data;
|
|
return true;
|
|
}
|
|
|
|
virtual size_t GetSize() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual Node * GetNode(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * GetNode(const std::string & key)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * Insert(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * PushFront()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual Node * PushBack()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual void Erase(const size_t index)
|
|
{
|
|
}
|
|
|
|
virtual void Erase(const std::string & key)
|
|
{
|
|
}
|
|
|
|
std::string m_Value;
|
|
|
|
};
|
|
|
|
|
|
// Node implementations.
|
|
class NodeImp
|
|
{
|
|
|
|
public:
|
|
|
|
NodeImp() :
|
|
m_Type(Node::None),
|
|
m_pImp(nullptr)
|
|
{
|
|
}
|
|
|
|
~NodeImp()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
if(m_pImp != nullptr)
|
|
{
|
|
delete m_pImp;
|
|
m_pImp = nullptr;
|
|
}
|
|
m_Type = Node::None;
|
|
}
|
|
|
|
void InitSequence()
|
|
{
|
|
if(m_Type != Node::SequenceType || m_pImp == nullptr)
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
delete m_pImp;
|
|
}
|
|
m_pImp = new SequenceImp;
|
|
m_Type = Node::SequenceType;
|
|
}
|
|
}
|
|
|
|
void InitMap()
|
|
{
|
|
if(m_Type != Node::MapType || m_pImp == nullptr)
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
delete m_pImp;
|
|
}
|
|
m_pImp = new MapImp;
|
|
m_Type = Node::MapType;
|
|
}
|
|
}
|
|
|
|
void InitScalar()
|
|
{
|
|
if(m_Type != Node::ScalarType || m_pImp == nullptr)
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
delete m_pImp;
|
|
}
|
|
m_pImp = new ScalarImp;
|
|
m_Type = Node::ScalarType;
|
|
}
|
|
|
|
}
|
|
|
|
Node::eType m_Type; ///< Type of node.
|
|
TypeImp * m_pImp; ///< Imp of type.
|
|
|
|
};
|
|
|
|
// Iterator implementation class
|
|
class IteratorImp
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~IteratorImp()
|
|
{
|
|
}
|
|
|
|
virtual Node::eType GetType() const = 0;
|
|
virtual void InitBegin(SequenceImp * pSequenceImp) = 0;
|
|
virtual void InitEnd(SequenceImp * pSequenceImp) = 0;
|
|
virtual void InitBegin(MapImp * pMapImp) = 0;
|
|
virtual void InitEnd(MapImp * pMapImp) = 0;
|
|
|
|
};
|
|
|
|
class SequenceIteratorImp : public IteratorImp
|
|
{
|
|
|
|
public:
|
|
|
|
virtual Node::eType GetType() const
|
|
{
|
|
return Node::SequenceType;
|
|
}
|
|
|
|
virtual void InitBegin(SequenceImp * pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.begin();
|
|
}
|
|
|
|
virtual void InitEnd(SequenceImp * pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.end();
|
|
}
|
|
|
|
virtual void InitBegin(MapImp * pMapImp)
|
|
{
|
|
}
|
|
|
|
virtual void InitEnd(MapImp * pMapImp)
|
|
{
|
|
}
|
|
|
|
void Copy(const SequenceIteratorImp & it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<size_t, Node *>::iterator m_Iterator;
|
|
|
|
};
|
|
|
|
class MapIteratorImp : public IteratorImp
|
|
{
|
|
|
|
public:
|
|
|
|
virtual Node::eType GetType() const
|
|
{
|
|
return Node::MapType;
|
|
}
|
|
|
|
virtual void InitBegin(SequenceImp * pSequenceImp)
|
|
{
|
|
}
|
|
|
|
virtual void InitEnd(SequenceImp * pSequenceImp)
|
|
{
|
|
}
|
|
|
|
virtual void InitBegin(MapImp * pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.begin();
|
|
}
|
|
|
|
virtual void InitEnd(MapImp * pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.end();
|
|
}
|
|
|
|
void Copy(const MapIteratorImp & it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<std::string, Node *>::iterator m_Iterator;
|
|
|
|
};
|
|
|
|
class SequenceConstIteratorImp : public IteratorImp
|
|
{
|
|
|
|
public:
|
|
|
|
virtual Node::eType GetType() const
|
|
{
|
|
return Node::SequenceType;
|
|
}
|
|
|
|
virtual void InitBegin(SequenceImp * pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.begin();
|
|
}
|
|
|
|
virtual void InitEnd(SequenceImp * pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.end();
|
|
}
|
|
|
|
virtual void InitBegin(MapImp * pMapImp)
|
|
{
|
|
}
|
|
|
|
virtual void InitEnd(MapImp * pMapImp)
|
|
{
|
|
}
|
|
|
|
void Copy(const SequenceConstIteratorImp & it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<size_t, Node *>::const_iterator m_Iterator;
|
|
|
|
};
|
|
|
|
class MapConstIteratorImp : public IteratorImp
|
|
{
|
|
|
|
public:
|
|
|
|
virtual Node::eType GetType() const
|
|
{
|
|
return Node::MapType;
|
|
}
|
|
|
|
virtual void InitBegin(SequenceImp * pSequenceImp)
|
|
{
|
|
}
|
|
|
|
virtual void InitEnd(SequenceImp * pSequenceImp)
|
|
{
|
|
}
|
|
|
|
virtual void InitBegin(MapImp * pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.begin();
|
|
}
|
|
|
|
virtual void InitEnd(MapImp * pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.end();
|
|
}
|
|
|
|
void Copy(const MapConstIteratorImp & it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<std::string, Node *>::const_iterator m_Iterator;
|
|
|
|
};
|
|
|
|
|
|
// Iterator class
|
|
Iterator::Iterator() :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
}
|
|
|
|
Iterator::~Iterator()
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
delete static_cast<SequenceIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapIteratorImp*>(m_pImp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Iterator::Iterator(const Iterator & it) :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
*this = it;
|
|
}
|
|
|
|
Iterator & Iterator::operator = (const Iterator & it)
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
delete static_cast<SequenceIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapIteratorImp*>(m_pImp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
m_pImp = nullptr;
|
|
m_Type = None;
|
|
}
|
|
|
|
IteratorImp * pNewImp = nullptr;
|
|
|
|
switch(it.m_Type)
|
|
{
|
|
case SequenceType:
|
|
m_Type = SequenceType;
|
|
pNewImp = new SequenceIteratorImp;
|
|
static_cast<SequenceIteratorImp*>(pNewImp)->m_Iterator = static_cast<SequenceIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
m_Type = MapType;
|
|
pNewImp = new MapIteratorImp;
|
|
static_cast<MapIteratorImp*>(pNewImp)->m_Iterator = static_cast<MapIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_pImp = pNewImp;
|
|
return *this;
|
|
}
|
|
|
|
std::pair<const std::string &, Node &> Iterator::operator *()
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
return { g_EmptyString, *(static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator->second)};
|
|
break;
|
|
case MapType:
|
|
return {static_cast<MapIteratorImp*>(m_pImp)->m_Iterator->first,
|
|
*(static_cast<MapIteratorImp*>(m_pImp)->m_Iterator->second)};
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_NoneNode.Clear();
|
|
return { g_EmptyString, g_NoneNode};
|
|
}
|
|
|
|
Iterator & Iterator::operator ++ (int dummy)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Iterator & Iterator::operator -- (int dummy)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool Iterator::operator == (const Iterator & it)
|
|
{
|
|
if(m_Type != it.m_Type)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
return static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator == static_cast<SequenceIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
return static_cast<MapIteratorImp*>(m_pImp)->m_Iterator == static_cast<MapIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Iterator::operator != (const Iterator & it)
|
|
{
|
|
return !(*this == it);
|
|
}
|
|
|
|
|
|
// Const Iterator class
|
|
ConstIterator::ConstIterator() :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
}
|
|
|
|
ConstIterator::~ConstIterator()
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
delete static_cast<SequenceConstIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapConstIteratorImp*>(m_pImp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
ConstIterator::ConstIterator(const ConstIterator & it) :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
*this = it;
|
|
}
|
|
|
|
ConstIterator & ConstIterator::operator = (const ConstIterator & it)
|
|
{
|
|
if(m_pImp)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
delete static_cast<SequenceConstIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapConstIteratorImp*>(m_pImp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
m_pImp = nullptr;
|
|
m_Type = None;
|
|
}
|
|
|
|
IteratorImp * pNewImp = nullptr;
|
|
|
|
switch(it.m_Type)
|
|
{
|
|
case SequenceType:
|
|
m_Type = SequenceType;
|
|
pNewImp = new SequenceConstIteratorImp;
|
|
static_cast<SequenceConstIteratorImp*>(pNewImp)->m_Iterator = static_cast<SequenceConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
m_Type = MapType;
|
|
pNewImp = new MapConstIteratorImp;
|
|
static_cast<MapConstIteratorImp*>(pNewImp)->m_Iterator = static_cast<MapConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_pImp = pNewImp;
|
|
return *this;
|
|
}
|
|
|
|
std::pair<const std::string &, const Node &> ConstIterator::operator *()
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
return { g_EmptyString, *(static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator->second)};
|
|
break;
|
|
case MapType:
|
|
return {static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator->first,
|
|
*(static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator->second)};
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_NoneNode.Clear();
|
|
return { g_EmptyString, g_NoneNode};
|
|
}
|
|
|
|
ConstIterator & ConstIterator::operator ++ (int dummy)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ConstIterator & ConstIterator::operator -- (int dummy)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ConstIterator::operator == (const ConstIterator & it)
|
|
{
|
|
if(m_Type != it.m_Type)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch(m_Type)
|
|
{
|
|
case SequenceType:
|
|
return static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator == static_cast<SequenceConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
return static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator == static_cast<MapConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ConstIterator::operator != (const ConstIterator & it)
|
|
{
|
|
return !(*this == it);
|
|
}
|
|
|
|
|
|
// Node class
|
|
Node::Node() :
|
|
m_pImp(new NodeImp)
|
|
{
|
|
}
|
|
|
|
Node::Node(const Node & node) :
|
|
Node()
|
|
{
|
|
*this = node;
|
|
}
|
|
|
|
Node::Node(const std::string & value) :
|
|
Node()
|
|
{
|
|
*this = value;
|
|
}
|
|
|
|
Node::Node(const char * value) :
|
|
Node()
|
|
{
|
|
*this = value;
|
|
}
|
|
|
|
Node::~Node()
|
|
{
|
|
delete static_cast<NodeImp*>(m_pImp);
|
|
}
|
|
|
|
Node::eType Node::Type() const
|
|
{
|
|
return NODE_IMP->m_Type;
|
|
}
|
|
|
|
bool Node::IsNone() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::None;
|
|
}
|
|
|
|
bool Node::IsSequence() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::SequenceType;
|
|
}
|
|
|
|
bool Node::IsMap() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::MapType;
|
|
}
|
|
|
|
bool Node::IsScalar() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::ScalarType;
|
|
}
|
|
|
|
void Node::Clear()
|
|
{
|
|
NODE_IMP->Clear();
|
|
}
|
|
|
|
size_t Node::Size() const
|
|
{
|
|
if(TYPE_IMP == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return TYPE_IMP->GetSize();
|
|
}
|
|
|
|
Node & Node::Insert(const size_t index)
|
|
{
|
|
NODE_IMP->InitSequence();
|
|
return *TYPE_IMP->Insert(index);
|
|
}
|
|
|
|
Node & Node::PushFront()
|
|
{
|
|
NODE_IMP->InitSequence();
|
|
return *TYPE_IMP->PushFront();
|
|
}
|
|
Node & Node::PushBack()
|
|
{
|
|
NODE_IMP->InitSequence();
|
|
return *TYPE_IMP->PushBack();
|
|
}
|
|
|
|
Node & Node::operator[](const size_t index)
|
|
{
|
|
NODE_IMP->InitSequence();
|
|
Node * pNode = TYPE_IMP->GetNode(index);
|
|
if(pNode == nullptr)
|
|
{
|
|
g_NoneNode.Clear();
|
|
return g_NoneNode;
|
|
}
|
|
return *pNode;
|
|
}
|
|
|
|
Node & Node::operator[](const std::string & key)
|
|
{
|
|
NODE_IMP->InitMap();
|
|
return *TYPE_IMP->GetNode(key);
|
|
}
|
|
|
|
void Node::Erase(const size_t index)
|
|
{
|
|
if(TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::SequenceType)
|
|
{
|
|
return;
|
|
}
|
|
|
|
return TYPE_IMP->Erase(index);
|
|
}
|
|
|
|
void Node::Erase(const std::string & key)
|
|
{
|
|
if(TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::MapType)
|
|
{
|
|
return;
|
|
}
|
|
|
|
return TYPE_IMP->Erase(key);
|
|
}
|
|
|
|
Node & Node::operator = (const Node & node)
|
|
{
|
|
NODE_IMP->Clear();
|
|
CopyNode(node, *this);
|
|
return *this;
|
|
}
|
|
|
|
Node & Node::operator = (const std::string & value)
|
|
{
|
|
NODE_IMP->InitScalar();
|
|
TYPE_IMP->SetData(value);
|
|
return *this;
|
|
}
|
|
|
|
Node & Node::operator = (const char * value)
|
|
{
|
|
NODE_IMP->InitScalar();
|
|
TYPE_IMP->SetData(value ? std::string(value) : "");
|
|
return *this;
|
|
}
|
|
|
|
Iterator Node::Begin()
|
|
{
|
|
Iterator it;
|
|
|
|
if(TYPE_IMP != nullptr)
|
|
{
|
|
IteratorImp * pItImp = nullptr;
|
|
|
|
switch(NODE_IMP->m_Type)
|
|
{
|
|
case Node::SequenceType:
|
|
it.m_Type = Iterator::SequenceType;
|
|
pItImp = new SequenceIteratorImp;
|
|
pItImp->InitBegin(static_cast<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = Iterator::MapType;
|
|
pItImp = new MapIteratorImp;
|
|
pItImp->InitBegin(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
ConstIterator Node::Begin() const
|
|
{
|
|
ConstIterator it;
|
|
|
|
if(TYPE_IMP != nullptr)
|
|
{
|
|
IteratorImp * pItImp = nullptr;
|
|
|
|
switch(NODE_IMP->m_Type)
|
|
{
|
|
case Node::SequenceType:
|
|
it.m_Type = ConstIterator::SequenceType;
|
|
pItImp = new SequenceConstIteratorImp;
|
|
pItImp->InitBegin(static_cast<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = ConstIterator::MapType;
|
|
pItImp = new MapConstIteratorImp;
|
|
pItImp->InitBegin(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
Iterator Node::End()
|
|
{
|
|
Iterator it;
|
|
|
|
if(TYPE_IMP != nullptr)
|
|
{
|
|
IteratorImp * pItImp = nullptr;
|
|
|
|
switch(NODE_IMP->m_Type)
|
|
{
|
|
case Node::SequenceType:
|
|
it.m_Type = Iterator::SequenceType;
|
|
pItImp = new SequenceIteratorImp;
|
|
pItImp->InitEnd(static_cast<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = Iterator::MapType;
|
|
pItImp = new MapIteratorImp;
|
|
pItImp->InitEnd(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
ConstIterator Node::End() const
|
|
{
|
|
ConstIterator it;
|
|
|
|
if(TYPE_IMP != nullptr)
|
|
{
|
|
IteratorImp * pItImp = nullptr;
|
|
|
|
switch(NODE_IMP->m_Type)
|
|
{
|
|
case Node::SequenceType:
|
|
it.m_Type = ConstIterator::SequenceType;
|
|
pItImp = new SequenceConstIteratorImp;
|
|
pItImp->InitEnd(static_cast<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = ConstIterator::MapType;
|
|
pItImp = new MapConstIteratorImp;
|
|
pItImp->InitEnd(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
const std::string & Node::AsString() const
|
|
{
|
|
if(TYPE_IMP == nullptr)
|
|
{
|
|
return g_EmptyString;
|
|
}
|
|
|
|
return TYPE_IMP->GetData();
|
|
}
|
|
|
|
|
|
|
|
// Reader implementations
|
|
/**
|
|
* @breif Line information structure.
|
|
*
|
|
*/
|
|
class ReaderLine
|
|
{
|
|
|
|
public:
|
|
|
|
/**
|
|
* @breif Constructor.
|
|
*
|
|
*/
|
|
ReaderLine(const std::string & data = "",
|
|
const size_t no = 0,
|
|
const size_t offset = 0,
|
|
const Node::eType type = Node::None,
|
|
const unsigned char flags = 0) :
|
|
Data(data),
|
|
No(no),
|
|
Offset(offset),
|
|
Type(type),
|
|
Flags(flags),
|
|
NextLine(nullptr)
|
|
{
|
|
}
|
|
|
|
enum eFlag
|
|
{
|
|
LiteralScalarFlag, ///< Literal scalar type, defined as "|".
|
|
FoldedScalarFlag, ///< Folded scalar type, defined as "<".
|
|
ScalarNewlineFlag ///< Scalar ends with a newline.
|
|
};
|
|
|
|
/**
|
|
* @breif Set flag.
|
|
*
|
|
*/
|
|
void SetFlag(const eFlag flag)
|
|
{
|
|
Flags |= FlagMask[static_cast<size_t>(flag)];
|
|
}
|
|
|
|
/**
|
|
* @breif Set flags by mask value.
|
|
*
|
|
*/
|
|
void SetFlags(const unsigned char flags)
|
|
{
|
|
Flags |= flags;
|
|
}
|
|
|
|
/**
|
|
* @breif Unset flag.
|
|
*
|
|
*/
|
|
void UnsetFlag(const eFlag flag)
|
|
{
|
|
Flags &= ~FlagMask[static_cast<size_t>(flag)];
|
|
}
|
|
|
|
/**
|
|
* @breif Unset flags by mask value.
|
|
*
|
|
*/
|
|
void UnsetFlags(const unsigned char flags)
|
|
{
|
|
Flags &= ~flags;
|
|
}
|
|
|
|
/**
|
|
* @breif Get flag value.
|
|
*
|
|
*/
|
|
bool GetFlag(const eFlag flag) const
|
|
{
|
|
return Flags & FlagMask[static_cast<size_t>(flag)];
|
|
}
|
|
|
|
/**
|
|
* @breif Copy and replace scalar flags from another ReaderLine.
|
|
*
|
|
*/
|
|
void CopyScalarFlags(ReaderLine * from)
|
|
{
|
|
if (from == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned char newFlags = from->Flags & (FlagMask[0] | FlagMask[1] | FlagMask[2]);
|
|
Flags |= newFlags;
|
|
}
|
|
|
|
static const unsigned char FlagMask[3];
|
|
|
|
std::string Data; ///< Data of line.
|
|
size_t No; ///< Line number.
|
|
size_t Offset; ///< Offset to first character in data.
|
|
Node::eType Type; ///< Type of line.
|
|
unsigned char Flags; ///< Flags of line.
|
|
ReaderLine * NextLine; ///< Pointer to next line.
|
|
|
|
|
|
|
|
};
|
|
|
|
const unsigned char ReaderLine::FlagMask[3] = { 0x01, 0x02, 0x04 };
|
|
|
|
|
|
/**
|
|
* @breif Implementation class of Yaml parsing.
|
|
* Parsing incoming stream and outputs a root node.
|
|
*
|
|
*/
|
|
class ParseImp
|
|
{
|
|
|
|
public:
|
|
|
|
/**
|
|
* @breif Default constructor.
|
|
*
|
|
*/
|
|
ParseImp()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @breif Destructor.
|
|
*
|
|
*/
|
|
~ParseImp()
|
|
{
|
|
ClearLines();
|
|
}
|
|
|
|
/**
|
|
* @breif Run full parsing procedure.
|
|
*
|
|
*/
|
|
void Parse(Node & root, std::iostream & stream)
|
|
{
|
|
try
|
|
{
|
|
root.Clear();
|
|
ReadLines(stream);
|
|
PostProcessLines();
|
|
//Print();
|
|
ParseRoot(root);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
root.Clear();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
/**
|
|
* @breif Copy constructor.
|
|
*
|
|
*/
|
|
ParseImp(const ParseImp & copy)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* @breif Read all lines.
|
|
* Ignoring:
|
|
* - Empty lines.
|
|
* - Comments.
|
|
* - Document start/end.
|
|
*
|
|
*/
|
|
void ReadLines(std::iostream & stream)
|
|
{
|
|
std::string line = "";
|
|
size_t lineNo = 0;
|
|
bool documentStartFound = false;
|
|
bool foundFirstNotEmpty = false;
|
|
std::streampos streamPos = 0;
|
|
|
|
// Read all lines, as long as the stream is ok.
|
|
while (!stream.eof() && !stream.fail())
|
|
{
|
|
// Read line
|
|
streamPos = stream.tellg();
|
|
std::getline(stream, line);
|
|
lineNo++;
|
|
|
|
// Remove comment
|
|
const size_t commentPos = FindNotCited(line, '#');
|
|
if(commentPos != std::string::npos)
|
|
{
|
|
line.resize(commentPos);
|
|
}
|
|
|
|
// Start of document.
|
|
if (documentStartFound == false && line == "---")
|
|
{
|
|
// Erase all lines before this line.
|
|
ClearLines();
|
|
documentStartFound = true;
|
|
continue;
|
|
}
|
|
|
|
// End of document.
|
|
if (line == "...")
|
|
{
|
|
break;
|
|
}
|
|
else if(line == "---")
|
|
{
|
|
stream.seekg(streamPos);
|
|
break;
|
|
}
|
|
|
|
// Remove trailing return.
|
|
if (line.size())
|
|
{
|
|
if (line[line.size() - 1] == '\r')
|
|
{
|
|
line.resize(line.size() - 1);
|
|
}
|
|
}
|
|
|
|
// Validate characters.
|
|
for (size_t i = 0; i < line.size(); i++)
|
|
{
|
|
if (line[i] != '\t' && (line[i] < 32 || line[i] > 125))
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorInvalidCharacter, lineNo, i + 1));
|
|
}
|
|
}
|
|
|
|
// Validate tabs
|
|
const size_t firstTabPos = line.find_first_of('\t');
|
|
size_t startOffset = line.find_first_not_of(" \t");
|
|
|
|
// Make sure no tabs are in the very front.
|
|
if (startOffset != std::string::npos)
|
|
{
|
|
if(firstTabPos < startOffset)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorTabInOffset, lineNo, firstTabPos));
|
|
}
|
|
|
|
// Remove front spaces.
|
|
line = line.substr(startOffset);
|
|
}
|
|
else
|
|
{
|
|
startOffset = 0;
|
|
line = "";
|
|
}
|
|
|
|
// Add line.
|
|
if(foundFirstNotEmpty == false)
|
|
{
|
|
if(line.size())
|
|
{
|
|
foundFirstNotEmpty = true;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ReaderLine * pLine = new ReaderLine(line, lineNo, startOffset);
|
|
m_Lines.push_back(pLine);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @breif Run post-processing on all lines.
|
|
* Basically split lines into multiple lines if needed, to follow the parsing algorithm.
|
|
*
|
|
*/
|
|
void PostProcessLines()
|
|
{
|
|
for (auto it = m_Lines.begin(); it != m_Lines.end();)
|
|
{
|
|
// Sequence.
|
|
if (PostProcessSequenceLine(it) == true)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Mapping.
|
|
if (PostProcessMappingLine(it) == true)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Scalar.
|
|
PostProcessScalarLine(it);
|
|
}
|
|
|
|
// Set next line of all lines.
|
|
if (m_Lines.size())
|
|
{
|
|
if (m_Lines.back()->Type != Node::ScalarType)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *m_Lines.back()));
|
|
}
|
|
|
|
if (m_Lines.size() > 1)
|
|
{
|
|
auto prevEnd = m_Lines.end();
|
|
--prevEnd;
|
|
|
|
for (auto it = m_Lines.begin(); it != prevEnd; it++)
|
|
{
|
|
auto nextIt = it;
|
|
++nextIt;
|
|
|
|
(*it)->NextLine = *nextIt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @breif Run post-processing and check for sequence.
|
|
* Split line into two lines if sequence token is not on it's own line.
|
|
*
|
|
* @return true if line is sequence, else false.
|
|
*
|
|
*/
|
|
bool PostProcessSequenceLine(std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
ReaderLine * pLine = *it;
|
|
|
|
// Sequence split
|
|
if (IsSequenceStart(pLine->Data) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pLine->Type = Node::SequenceType;
|
|
|
|
ClearTrailingEmptyLines(++it);
|
|
|
|
const size_t valueStart = pLine->Data.find_first_not_of(" \t", 1);
|
|
if (valueStart == std::string::npos)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Create new line and insert
|
|
std::string newLine = pLine->Data.substr(valueStart);
|
|
it = m_Lines.insert(it, new ReaderLine(newLine, pLine->No, pLine->Offset + valueStart));
|
|
pLine->Data = "";
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @breif Run post-processing and check for mapping.
|
|
* Split line into two lines if mapping value is not on it's own line.
|
|
*
|
|
* @return true if line is mapping, else move on to scalar parsing.
|
|
*
|
|
*/
|
|
bool PostProcessMappingLine(std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
ReaderLine * pLine = *it;
|
|
|
|
// Find map key.
|
|
size_t preKeyQuotes = 0;
|
|
size_t tokenPos = FindNotCited(pLine->Data, ':', preKeyQuotes);
|
|
if (tokenPos == std::string::npos)
|
|
{
|
|
return false;
|
|
}
|
|
if(preKeyQuotes > 1)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine));
|
|
}
|
|
|
|
pLine->Type = Node::MapType;
|
|
|
|
// Get key
|
|
std::string key = pLine->Data.substr(0, tokenPos);
|
|
const size_t keyEnd = key.find_last_not_of(" \t");
|
|
if (keyEnd == std::string::npos)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorKeyMissing, *pLine));
|
|
}
|
|
key.resize(keyEnd + 1);
|
|
|
|
// Handle cited key.
|
|
if(preKeyQuotes == 1)
|
|
{
|
|
if(key.front() != '"' || key.back() != '"')
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine));
|
|
}
|
|
|
|
key = key.substr(1, key.size() - 2);
|
|
}
|
|
RemoveAllEscapeTokens(key);
|
|
|
|
// Get value
|
|
std::string value = "";
|
|
size_t valueStart = std::string::npos;
|
|
if (tokenPos + 1 != pLine->Data.size())
|
|
{
|
|
valueStart = pLine->Data.find_first_not_of(" \t", tokenPos + 1);
|
|
if (valueStart != std::string::npos)
|
|
{
|
|
value = pLine->Data.substr(valueStart);
|
|
}
|
|
}
|
|
|
|
// Make sure the value is not a sequence start.
|
|
if (IsSequenceStart(value) == true)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorBlockSequenceNotAllowed, *pLine, valueStart));
|
|
}
|
|
|
|
pLine->Data = key;
|
|
|
|
|
|
// Remove all empty lines after map key.
|
|
ClearTrailingEmptyLines(++it);
|
|
|
|
// Add new empty line?
|
|
size_t newLineOffset = valueStart;
|
|
if(newLineOffset == std::string::npos)
|
|
{
|
|
if(it != m_Lines.end() && (*it)->Offset > pLine->Offset)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
newLineOffset = tokenPos + 2;
|
|
}
|
|
else
|
|
{
|
|
newLineOffset += pLine->Offset;
|
|
}
|
|
|
|
// Add new line with value.
|
|
unsigned char dummyBlockFlags = 0;
|
|
if(IsBlockScalar(value, pLine->No, dummyBlockFlags) == true)
|
|
{
|
|
newLineOffset = pLine->Offset;
|
|
}
|
|
ReaderLine * pNewLine = new ReaderLine(value, pLine->No, newLineOffset, Node::ScalarType);
|
|
it = m_Lines.insert(it, pNewLine);
|
|
|
|
// Return false in order to handle next line(scalar value).
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @breif Run post-processing and check for scalar.
|
|
* Checking for multi-line scalars.
|
|
*
|
|
* @return true if scalar search should continue, else false.
|
|
*
|
|
*/
|
|
void PostProcessScalarLine(std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
ReaderLine * pLine = *it;
|
|
pLine->Type = Node::ScalarType;
|
|
|
|
size_t parentOffset = pLine->Offset;
|
|
if(pLine != m_Lines.front())
|
|
{
|
|
std::list<ReaderLine *>::iterator lastIt = it;
|
|
--lastIt;
|
|
parentOffset = (*lastIt)->Offset;
|
|
}
|
|
|
|
std::list<ReaderLine *>::iterator lastNotEmpty = it++;
|
|
|
|
// Find last empty lines
|
|
while(it != m_Lines.end())
|
|
{
|
|
pLine = *it;
|
|
pLine->Type = Node::ScalarType;
|
|
if(pLine->Data.size())
|
|
{
|
|
if(pLine->Offset <= parentOffset)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
lastNotEmpty = it;
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
|
|
ClearTrailingEmptyLines(++lastNotEmpty);
|
|
}
|
|
|
|
/**
|
|
* @breif Process root node and start of document.
|
|
*
|
|
*/
|
|
void ParseRoot(Node & root)
|
|
{
|
|
// Get first line and start type.
|
|
auto it = m_Lines.begin();
|
|
if(it == m_Lines.end())
|
|
{
|
|
return;
|
|
}
|
|
Node::eType type = (*it)->Type;
|
|
ReaderLine * pLine = *it;
|
|
|
|
// Handle next line.
|
|
switch(type)
|
|
{
|
|
case Node::SequenceType:
|
|
ParseSequence(root, it);
|
|
break;
|
|
case Node::MapType:
|
|
ParseMap(root, it);
|
|
break;
|
|
case Node::ScalarType:
|
|
ParseScalar(root, it);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(it != m_Lines.end())
|
|
{
|
|
throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine));
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @breif Process sequence node.
|
|
*
|
|
*/
|
|
void ParseSequence(Node & node, std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
ReaderLine * pNextLine = nullptr;
|
|
while(it != m_Lines.end())
|
|
{
|
|
ReaderLine * pLine = *it;
|
|
Node & childNode = node.PushBack();
|
|
|
|
// Move to next line, error check.
|
|
++it;
|
|
if(it == m_Lines.end())
|
|
{
|
|
throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine));
|
|
}
|
|
|
|
// Handle value of map
|
|
Node::eType valueType = (*it)->Type;
|
|
switch(valueType)
|
|
{
|
|
case Node::SequenceType:
|
|
ParseSequence(childNode, it);
|
|
break;
|
|
case Node::MapType:
|
|
ParseMap(childNode, it);
|
|
break;
|
|
case Node::ScalarType:
|
|
ParseScalar(childNode, it);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Check next line. if sequence and correct level, go on, else exit.
|
|
// If same level but but of type map = error.
|
|
if(it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset))
|
|
{
|
|
break;
|
|
}
|
|
if(pNextLine->Offset > pLine->Offset)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine));
|
|
}
|
|
if(pNextLine->Type != Node::SequenceType)
|
|
{
|
|
throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @breif Process map node.
|
|
*
|
|
*/
|
|
void ParseMap(Node & node, std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
ReaderLine * pNextLine = nullptr;
|
|
while(it != m_Lines.end())
|
|
{
|
|
ReaderLine * pLine = *it;
|
|
Node & childNode = node[pLine->Data];
|
|
|
|
// Move to next line, error check.
|
|
++it;
|
|
if(it == m_Lines.end())
|
|
{
|
|
throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine));
|
|
}
|
|
|
|
// Handle value of map
|
|
Node::eType valueType = (*it)->Type;
|
|
switch(valueType)
|
|
{
|
|
case Node::SequenceType:
|
|
ParseSequence(childNode, it);
|
|
break;
|
|
case Node::MapType:
|
|
ParseMap(childNode, it);
|
|
break;
|
|
case Node::ScalarType:
|
|
ParseScalar(childNode, it);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Check next line. if map and correct level, go on, else exit.
|
|
// if same level but but of type map = error.
|
|
if(it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset))
|
|
{
|
|
break;
|
|
}
|
|
if(pNextLine->Offset > pLine->Offset)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine));
|
|
}
|
|
if(pNextLine->Type != pLine->Type)
|
|
{
|
|
throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @breif Process scalar node.
|
|
*
|
|
*/
|
|
void ParseScalar(Node & node, std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
std::string data = "";
|
|
ReaderLine * pFirstLine = *it;
|
|
ReaderLine * pLine = *it;
|
|
|
|
// Check if current line is a block scalar.
|
|
unsigned char blockFlags = 0;
|
|
bool isBlockScalar = IsBlockScalar(pLine->Data, pLine->No, blockFlags);
|
|
const bool newLineFlag = static_cast<bool>(blockFlags & ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::ScalarNewlineFlag)]);
|
|
const bool foldedFlag = static_cast<bool>(blockFlags & ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::FoldedScalarFlag)]);
|
|
const bool literalFlag = static_cast<bool>(blockFlags & ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::LiteralScalarFlag)]);
|
|
size_t parentOffset = 0;
|
|
|
|
// Find parent offset
|
|
if(it != m_Lines.begin())
|
|
{
|
|
std::list<ReaderLine *>::iterator parentIt = it;
|
|
--parentIt;
|
|
parentOffset = (*parentIt)->Offset;
|
|
}
|
|
|
|
// Move to next iterator/line if current line is a block scalar.
|
|
if(isBlockScalar)
|
|
{
|
|
++it;
|
|
if(it == m_Lines.end() || (pLine = *it)->Type != Node::ScalarType)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Not a block scalar, cut end spaces/tabs
|
|
if(isBlockScalar == false)
|
|
{
|
|
while(1)
|
|
{
|
|
pLine = *it;
|
|
|
|
if(parentOffset != 0 && pLine->Offset <= parentOffset)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine));
|
|
}
|
|
|
|
const size_t endOffset = pLine->Data.find_last_not_of(" \t");
|
|
if(endOffset == std::string::npos)
|
|
{
|
|
data += "\n";
|
|
}
|
|
else
|
|
{
|
|
data += pLine->Data.substr(0, endOffset + 1);
|
|
}
|
|
|
|
// Move to next line
|
|
++it;
|
|
if(it == m_Lines.end() || (*it)->Type != Node::ScalarType)
|
|
{
|
|
break;
|
|
}
|
|
|
|
data += " ";
|
|
}
|
|
|
|
if(ValidateQuote(data) == false)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorInvalidQuote, *pFirstLine));
|
|
}
|
|
}
|
|
// Block scalar
|
|
else
|
|
{
|
|
pLine = *it;
|
|
size_t blockOffset = pLine->Offset;
|
|
if(blockOffset <= parentOffset)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine));
|
|
}
|
|
|
|
bool addedSpace = false;
|
|
while(it != m_Lines.end() && (*it)->Type == Node::ScalarType)
|
|
{
|
|
pLine = *it;
|
|
|
|
const size_t endOffset = pLine->Data.find_last_not_of(" \t");
|
|
if(endOffset != std::string::npos && pLine->Offset < blockOffset)
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine));
|
|
}
|
|
|
|
if(endOffset == std::string::npos)
|
|
{
|
|
if(addedSpace)
|
|
{
|
|
data[data.size() - 1] = '\n';
|
|
addedSpace = false;
|
|
}
|
|
else
|
|
{
|
|
data += "\n";
|
|
}
|
|
|
|
++it;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if(blockOffset != pLine->Offset && foldedFlag)
|
|
{
|
|
if(addedSpace)
|
|
{
|
|
data[data.size() - 1] = '\n';
|
|
addedSpace = false;
|
|
}
|
|
else
|
|
{
|
|
data += "\n";
|
|
}
|
|
}
|
|
data += std::string(pLine->Offset - blockOffset, ' ');
|
|
data += pLine->Data;
|
|
}
|
|
|
|
// Move to next line
|
|
++it;
|
|
if(it == m_Lines.end() || (*it)->Type != Node::ScalarType)
|
|
{
|
|
if(newLineFlag)
|
|
{
|
|
data += "\n";
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(foldedFlag)
|
|
{
|
|
data += " ";
|
|
addedSpace = true;
|
|
}
|
|
else if(literalFlag && endOffset != std::string::npos)
|
|
{
|
|
data += "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
if(data.size() && (data[0] == '"' || data[0] == '\''))
|
|
{
|
|
data = data.substr(1, data.size() - 2 );
|
|
}
|
|
|
|
node = data;
|
|
}
|
|
|
|
/**
|
|
* @breif Debug printing.
|
|
*
|
|
*/
|
|
void Print()
|
|
{
|
|
for (auto it = m_Lines.begin(); it != m_Lines.end(); it++)
|
|
{
|
|
|
|
ReaderLine * pLine = *it;
|
|
|
|
// Print type
|
|
if (pLine->Type == Node::SequenceType)
|
|
{
|
|
std::cout << "seq ";
|
|
}
|
|
else if (pLine->Type == Node::MapType)
|
|
{
|
|
std::cout << "map ";
|
|
}
|
|
else if (pLine->Type == Node::ScalarType)
|
|
{
|
|
std::cout << "sca ";
|
|
}
|
|
else
|
|
{
|
|
std::cout << " ";
|
|
}
|
|
|
|
// Print flags
|
|
if (pLine->GetFlag(ReaderLine::FoldedScalarFlag))
|
|
{
|
|
std::cout << "f";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "-";
|
|
}
|
|
if (pLine->GetFlag(ReaderLine::LiteralScalarFlag))
|
|
{
|
|
std::cout << "l";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "-";
|
|
}
|
|
if (pLine->GetFlag(ReaderLine::ScalarNewlineFlag))
|
|
{
|
|
std::cout << "n";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "-";
|
|
}
|
|
if (pLine->NextLine == nullptr)
|
|
{
|
|
std::cout << "e";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "-";
|
|
}
|
|
|
|
|
|
std::cout << "| ";
|
|
std::cout << pLine->No << " ";
|
|
std::cout << std::string(pLine->Offset, ' ');
|
|
|
|
if (pLine->Type == Node::ScalarType)
|
|
{
|
|
std::string scalarValue = pLine->Data;
|
|
for (size_t i = 0; (i = scalarValue.find("\n", i)) != std::string::npos;)
|
|
{
|
|
scalarValue.replace(i, 1, "\\n");
|
|
i += 2;
|
|
}
|
|
std::cout << scalarValue << std::endl;
|
|
}
|
|
else if (pLine->Type == Node::MapType)
|
|
{
|
|
std::cout << pLine->Data + ":" << std::endl;
|
|
}
|
|
else if (pLine->Type == Node::SequenceType)
|
|
{
|
|
std::cout << "-" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "> UNKOWN TYPE <" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @breif Clear all read lines.
|
|
*
|
|
*/
|
|
void ClearLines()
|
|
{
|
|
for (auto it = m_Lines.begin(); it != m_Lines.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
m_Lines.clear();
|
|
}
|
|
|
|
void ClearTrailingEmptyLines(std::list<ReaderLine *>::iterator & it)
|
|
{
|
|
while(it != m_Lines.end())
|
|
{
|
|
ReaderLine * pLine = *it;
|
|
if(pLine->Data.size() == 0)
|
|
{
|
|
delete *it;
|
|
it = m_Lines.erase(it);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static bool IsSequenceStart(const std::string & data)
|
|
{
|
|
if (data.size() == 0 || data[0] != '-')
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (data.size() >= 2 && data[1] != ' ')
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool IsBlockScalar(const std::string & data, const size_t line, unsigned char & flags)
|
|
{
|
|
flags = 0;
|
|
if(data.size() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(data[0] == '|')
|
|
{
|
|
if(data.size() >= 2)
|
|
{
|
|
if(data[1] != '-' && data[1] != ' ' && data[1] != '\t')
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flags |= ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::ScalarNewlineFlag)];
|
|
}
|
|
flags |= ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::LiteralScalarFlag)];
|
|
return true;
|
|
}
|
|
|
|
if(data[0] == '>')
|
|
{
|
|
if(data.size() >= 2)
|
|
{
|
|
if(data[1] != '-' && data[1] != ' ' && data[1] != '\t')
|
|
{
|
|
throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flags |= ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::ScalarNewlineFlag)];
|
|
}
|
|
flags |= ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::FoldedScalarFlag)];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::list<ReaderLine *> m_Lines; ///< List of lines.
|
|
|
|
};
|
|
|
|
// Parsing functions
|
|
void Parse(Node & root, const char * filename)
|
|
{
|
|
std::ifstream f(filename, std::ifstream::binary);
|
|
if (f.is_open() == false)
|
|
{
|
|
throw OperationException(g_ErrorCannotOpenFile);
|
|
}
|
|
|
|
f.seekg(0, f.end);
|
|
size_t fileSize = static_cast<size_t>(f.tellg());
|
|
f.seekg(0, f.beg);
|
|
|
|
std::unique_ptr<char[]> data(new char[fileSize]);
|
|
f.read(data.get(), fileSize);
|
|
f.close();
|
|
|
|
Parse(root, data.get(), fileSize);
|
|
}
|
|
|
|
void Parse(Node & root, std::iostream & stream)
|
|
{
|
|
ParseImp * pImp = nullptr;
|
|
|
|
try
|
|
{
|
|
pImp = new ParseImp;
|
|
pImp->Parse(root, stream);
|
|
delete pImp;
|
|
}
|
|
catch (const Exception e)
|
|
{
|
|
delete pImp;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void Parse(Node & root, const std::string & string)
|
|
{
|
|
std::stringstream ss(string);
|
|
Parse(root, ss);
|
|
}
|
|
|
|
void Parse(Node & root, const char * buffer, const size_t size)
|
|
{
|
|
std::stringstream ss(std::string(buffer, size));
|
|
Parse(root, ss);
|
|
}
|
|
|
|
|
|
// Serialize configuration structure.
|
|
SerializeConfig::SerializeConfig(const size_t spaceIndentation,
|
|
const size_t scalarMaxLength,
|
|
const bool sequenceMapNewline,
|
|
const bool mapScalarNewline) :
|
|
SpaceIndentation(spaceIndentation),
|
|
ScalarMaxLength(scalarMaxLength),
|
|
SequenceMapNewline(sequenceMapNewline),
|
|
MapScalarNewline(mapScalarNewline)
|
|
{
|
|
}
|
|
|
|
|
|
// Serialization functions
|
|
void Serialize(const Node & root, const char * filename, const SerializeConfig & config)
|
|
{
|
|
std::stringstream stream;
|
|
Serialize(root, stream, config);
|
|
|
|
std::ofstream f(filename);
|
|
if (f.is_open() == false)
|
|
{
|
|
throw OperationException(g_ErrorCannotOpenFile);
|
|
}
|
|
|
|
f.write(stream.str().c_str(), stream.str().size());
|
|
f.close();
|
|
}
|
|
|
|
size_t LineFolding(const std::string & input, std::vector<std::string> & folded, const size_t maxLength)
|
|
{
|
|
folded.clear();
|
|
if(input.size() == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
size_t currentPos = 0;
|
|
size_t lastPos = 0;
|
|
size_t spacePos = std::string::npos;
|
|
while(currentPos < input.size())
|
|
{
|
|
currentPos = lastPos + maxLength;
|
|
|
|
if(currentPos < input.size())
|
|
{
|
|
spacePos = input.find_first_of(' ', currentPos);
|
|
}
|
|
|
|
if(spacePos == std::string::npos || currentPos >= input.size())
|
|
{
|
|
const std::string endLine = input.substr(lastPos);
|
|
if(endLine.size())
|
|
{
|
|
folded.push_back(endLine);
|
|
}
|
|
|
|
return folded.size();
|
|
}
|
|
|
|
folded.push_back(input.substr(lastPos, spacePos - lastPos));
|
|
|
|
lastPos = spacePos + 1;
|
|
}
|
|
|
|
return folded.size();
|
|
}
|
|
|
|
static void SerializeLoop(const Node & node, std::iostream & stream, bool useLevel, const size_t level, const SerializeConfig & config)
|
|
{
|
|
const size_t indention = config.SpaceIndentation;
|
|
|
|
switch(node.Type())
|
|
{
|
|
case Node::SequenceType:
|
|
{
|
|
for(auto it = node.Begin(); it != node.End(); it++)
|
|
{
|
|
const Node & value = (*it).second;
|
|
if(value.IsNone())
|
|
{
|
|
continue;
|
|
}
|
|
stream << std::string(level, ' ') << "- ";
|
|
useLevel = false;
|
|
if(value.IsSequence() || (value.IsMap() && config.SequenceMapNewline == true))
|
|
{
|
|
useLevel = true;
|
|
stream << "\n";
|
|
}
|
|
|
|
SerializeLoop(value, stream, useLevel, level + 2, config);
|
|
}
|
|
|
|
}
|
|
break;
|
|
case Node::MapType:
|
|
{
|
|
size_t count = 0;
|
|
for(auto it = node.Begin(); it != node.End(); it++)
|
|
{
|
|
const Node & value = (*it).second;
|
|
if(value.IsNone())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(useLevel || count > 0)
|
|
{
|
|
stream << std::string(level, ' ');
|
|
}
|
|
|
|
std::string key = (*it).first;
|
|
AddEscapeTokens(key, "\\\"");
|
|
if(ShouldBeCited(key))
|
|
{
|
|
stream << "\"" << key << "\"" << ": ";
|
|
}
|
|
else
|
|
{
|
|
stream << key << ": ";
|
|
}
|
|
|
|
|
|
useLevel = false;
|
|
if(value.IsScalar() == false || (value.IsScalar() && config.MapScalarNewline))
|
|
{
|
|
useLevel = true;
|
|
stream << "\n";
|
|
}
|
|
|
|
SerializeLoop(value, stream, useLevel, level + indention, config);
|
|
|
|
useLevel = true;
|
|
count++;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case Node::ScalarType:
|
|
{
|
|
const std::string value = node.As<std::string>();
|
|
|
|
// Empty scalar
|
|
if(value.size() == 0)
|
|
{
|
|
stream << "\n";
|
|
break;
|
|
}
|
|
|
|
// Get lines of scalar.
|
|
std::string line = "";
|
|
std::vector<std::string> lines;
|
|
std::istringstream iss(value);
|
|
while (iss.eof() == false)
|
|
{
|
|
std::getline(iss, line);
|
|
lines.push_back(line);
|
|
}
|
|
|
|
// Block scalar
|
|
const std::string & lastLine = lines.back();
|
|
const bool endNewline = lastLine.size() == 0;
|
|
if(endNewline)
|
|
{
|
|
lines.pop_back();
|
|
}
|
|
|
|
// Literal
|
|
if(lines.size() > 1)
|
|
{
|
|
stream << "|";
|
|
}
|
|
// Folded/plain
|
|
else
|
|
{
|
|
const std::string frontLine = lines.front();
|
|
if(config.ScalarMaxLength == 0 || lines.front().size() <= config.ScalarMaxLength ||
|
|
LineFolding(frontLine, lines, config.ScalarMaxLength) == 1)
|
|
{
|
|
if(useLevel)
|
|
{
|
|
stream << std::string(level, ' ');
|
|
}
|
|
|
|
if(ShouldBeCited(value))
|
|
{
|
|
stream << "\"" << value << "\"\n";
|
|
break;
|
|
}
|
|
stream << value << "\n";
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
stream << ">";
|
|
}
|
|
}
|
|
|
|
if(endNewline == false)
|
|
{
|
|
stream << "-";
|
|
}
|
|
stream << "\n";
|
|
|
|
|
|
for(auto it = lines.begin(); it != lines.end(); it++)
|
|
{
|
|
stream << std::string(level, ' ') << (*it) << "\n";
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Serialize(const Node & root, std::iostream & stream, const SerializeConfig & config)
|
|
{
|
|
if(config.SpaceIndentation < 2)
|
|
{
|
|
throw OperationException(g_ErrorIndentation);
|
|
}
|
|
|
|
SerializeLoop(root, stream, false, 0, config);
|
|
}
|
|
|
|
void Serialize(const Node & root, std::string & string, const SerializeConfig & config)
|
|
{
|
|
std::stringstream stream;
|
|
Serialize(root, stream, config);
|
|
string = stream.str();
|
|
}
|
|
|
|
|
|
|
|
// Static function implementations
|
|
std::string ExceptionMessage(const std::string & message, ReaderLine & line)
|
|
{
|
|
return message + std::string(" Line ") + std::to_string(line.No) + std::string(": ") + line.Data;
|
|
}
|
|
|
|
std::string ExceptionMessage(const std::string & message, ReaderLine & line, const size_t errorPos)
|
|
{
|
|
return message + std::string(" Line ") + std::to_string(line.No) + std::string(" column ") + std::to_string(errorPos + 1) + std::string(": ") + line.Data;
|
|
}
|
|
|
|
std::string ExceptionMessage(const std::string & message, const size_t errorLine, const size_t errorPos)
|
|
{
|
|
return message + std::string(" Line ") + std::to_string(errorLine) + std::string(" column ") + std::to_string(errorPos);
|
|
}
|
|
|
|
std::string ExceptionMessage(const std::string & message, const size_t errorLine, const std::string & data)
|
|
{
|
|
return message + std::string(" Line ") + std::to_string(errorLine) + std::string(": ") + data;
|
|
}
|
|
|
|
bool FindQuote(const std::string & input, size_t & start, size_t & end, size_t searchPos)
|
|
{
|
|
start = end = std::string::npos;
|
|
size_t qPos = searchPos;
|
|
bool foundStart = false;
|
|
|
|
while(qPos != std::string::npos)
|
|
{
|
|
// Find first quote.
|
|
qPos = input.find_first_of("\"'", qPos);
|
|
if(qPos == std::string::npos)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const char token = input[qPos];
|
|
if(token == '"' && (qPos == 0 || input[qPos-1] != '\\'))
|
|
{
|
|
// Found start quote.
|
|
if(foundStart == false)
|
|
{
|
|
start = qPos;
|
|
foundStart = true;
|
|
}
|
|
// Found end quote
|
|
else
|
|
{
|
|
end = qPos;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check if it's possible for another loop.
|
|
if(qPos + 1 == input.size())
|
|
{
|
|
return false;
|
|
}
|
|
qPos++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
size_t FindNotCited(const std::string & input, char token, size_t & preQuoteCount)
|
|
{
|
|
preQuoteCount = 0;
|
|
size_t tokenPos = input.find_first_of(token);
|
|
if(tokenPos == std::string::npos)
|
|
{
|
|
return std::string::npos;
|
|
}
|
|
|
|
// Find all quotes
|
|
std::vector<std::pair<size_t, size_t>> quotes;
|
|
|
|
size_t quoteStart = 0;
|
|
size_t quoteEnd = 0;
|
|
while(FindQuote(input, quoteStart, quoteEnd, quoteEnd))
|
|
{
|
|
quotes.push_back({quoteStart, quoteEnd});
|
|
|
|
if(quoteEnd + 1 == input.size())
|
|
{
|
|
break;
|
|
}
|
|
quoteEnd++;
|
|
}
|
|
|
|
if(quotes.size() == 0)
|
|
{
|
|
return tokenPos;
|
|
}
|
|
|
|
size_t currentQuoteIndex = 0;
|
|
std::pair<size_t, size_t> currentQuote = {0, 0};
|
|
|
|
while(currentQuoteIndex < quotes.size())
|
|
{
|
|
currentQuote = quotes[currentQuoteIndex];
|
|
|
|
if(tokenPos < currentQuote.first)
|
|
{
|
|
return tokenPos;
|
|
}
|
|
preQuoteCount++;
|
|
if(tokenPos <= currentQuote.second)
|
|
{
|
|
// Find next token
|
|
if(tokenPos + 1 == input.size())
|
|
{
|
|
return std::string::npos;
|
|
}
|
|
tokenPos = input.find_first_of(token, tokenPos + 1);
|
|
if(tokenPos == std::string::npos)
|
|
{
|
|
return std::string::npos;
|
|
}
|
|
}
|
|
|
|
currentQuoteIndex++;
|
|
}
|
|
|
|
return tokenPos;
|
|
}
|
|
|
|
size_t FindNotCited(const std::string & input, char token)
|
|
{
|
|
size_t dummy = 0;
|
|
return FindNotCited(input, token, dummy);
|
|
}
|
|
|
|
bool ValidateQuote(const std::string & input)
|
|
{
|
|
if(input.size() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
char token = 0;
|
|
size_t searchPos = 0;
|
|
if(input[0] == '\"' || input[0] == '\'')
|
|
{
|
|
if(input.size() == 1)
|
|
{
|
|
return false;
|
|
}
|
|
token = input[0];
|
|
searchPos = 1;
|
|
}
|
|
|
|
while(searchPos != std::string::npos && searchPos < input.size() - 1)
|
|
{
|
|
searchPos = input.find_first_of("\"'", searchPos + 1);
|
|
if(searchPos == std::string::npos)
|
|
{
|
|
break;
|
|
}
|
|
|
|
const char foundToken = input[searchPos];
|
|
|
|
if(input[searchPos] == '\"' || input[searchPos] == '\'')
|
|
{
|
|
if(token == 0 && input[searchPos-1] != '\\')
|
|
{
|
|
return false;
|
|
}
|
|
//if(foundToken == token)
|
|
//{
|
|
|
|
/*if(foundToken == token && searchPos == input.size() - 1 && input[searchPos-1] != '\\')
|
|
{
|
|
return true;
|
|
if(searchPos == input.size() - 1)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else */
|
|
if(foundToken == token && input[searchPos-1] != '\\')
|
|
{
|
|
if(searchPos == input.size() - 1)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//}
|
|
}
|
|
}
|
|
|
|
return token == 0;
|
|
}
|
|
|
|
void CopyNode(const Node & from, Node & to)
|
|
{
|
|
const Node::eType type = from.Type();
|
|
|
|
switch(type)
|
|
{
|
|
case Node::SequenceType:
|
|
for(auto it = from.Begin(); it != from.End(); it++)
|
|
{
|
|
const Node & currentNode = (*it).second;
|
|
Node & newNode = to.PushBack();
|
|
CopyNode(currentNode, newNode);
|
|
}
|
|
break;
|
|
case Node::MapType:
|
|
for(auto it = from.Begin(); it != from.End(); it++)
|
|
{
|
|
const Node & currentNode = (*it).second;
|
|
Node & newNode = to[(*it).first];
|
|
CopyNode(currentNode, newNode);
|
|
}
|
|
break;
|
|
case Node::ScalarType:
|
|
to = from.As<std::string>();
|
|
break;
|
|
case Node::None:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool ShouldBeCited(const std::string & key)
|
|
{
|
|
return key.find_first_of("\":{}[],&*#?|-<>=!%@") != std::string::npos;
|
|
}
|
|
|
|
void AddEscapeTokens(std::string & input, const std::string & tokens)
|
|
{
|
|
for(auto it = tokens.begin(); it != tokens.end(); it++)
|
|
{
|
|
const char token = *it;
|
|
const std::string replace = std::string("\\") + std::string(1, token);
|
|
size_t found = input.find_first_of(token);
|
|
while(found != std::string::npos)
|
|
{
|
|
input.replace(found, 1, replace);
|
|
found = input.find_first_of(token, found + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemoveAllEscapeTokens(std::string & input)
|
|
{
|
|
size_t found = input.find_first_of("\\");
|
|
while(found != std::string::npos)
|
|
{
|
|
if(found + 1 == input.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string replace(1, input[found + 1]);
|
|
input.replace(found, 2, replace);
|
|
found = input.find_first_of("\\", found + 1);
|
|
}
|
|
}
|
|
|
|
|
|
}
|