Precommit (#1)

* first commit

* cleanup
This commit is contained in:
tompzf
2025-11-04 13:28:06 +01:00
committed by GitHub
parent dba45dc636
commit 6ed4b1534e
898 changed files with 256340 additions and 0 deletions

View File

@@ -0,0 +1,323 @@
#include "character_reader_utf_8.h"
#include "exception.h"
CCharacterReaderUTF8::CCharacterReaderUTF8() : m_nDataLength{0}, m_nCursor{0}
{}
CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) :
m_ssString{rssString}, m_nDataLength{rssString.size()}, m_nCursor{0}
{
CheckForInvalidUTF8Bytes();
CheckForInvalidUTF8Sequences();
}
void CCharacterReaderUTF8::Feed(const std::string& rssString)
{
m_ssString = rssString;
m_nDataLength = rssString.size();
m_nCursor = 0;
CheckForInvalidUTF8Bytes();
CheckForInvalidUTF8Sequences();
}
void CCharacterReaderUTF8::Reset()
{
m_ssString.clear();
m_nDataLength = 0;
m_nCursor = 0;
}
std::string CCharacterReaderUTF8::Peek()
{
return GetNextCharacter();
}
std::string CCharacterReaderUTF8::Peek(std::size_t n)
{
if (n < 1)
{
return "";
}
size_t offset{0};
for (std::size_t i = 0; i < n - 1; ++i)
{
offset += GetLengthOfNextCharacter(offset);
if (m_nDataLength <= m_nCursor + offset)
{
return "";
}
}
return GetNextCharacter(offset);
}
std::string CCharacterReaderUTF8::PeekUntil(const std::vector<std::string>& lstCollection)
{
size_t offset{0};
bool found{false};
std::string accumulation;
while (!found && m_nDataLength > m_nCursor + offset)
{
std::string character = GetNextCharacter(offset);
offset += GetLengthOfNextCharacter(offset);
for (const auto& delimiter : lstCollection)
{
if (delimiter == character)
{
found = true;
}
}
if (!found)
{
accumulation += character;
}
}
return accumulation;
}
std::string CCharacterReaderUTF8::Consume()
{
if (IsEOF())
{
return "";
}
std::string character{GetNextCharacter()};
m_nCursor += GetLengthOfNextCharacter();
return character;
}
std::string CCharacterReaderUTF8::Consume(std::size_t n)
{
if (n < 1)
{
return "";
}
size_t offset{0};
for (uint32_t i = 0; i < n - 1; ++i)
{
offset += GetLengthOfNextCharacter(offset);
if (m_nDataLength < m_nCursor + offset)
{
return "";
}
}
std::string character{GetNextCharacter(offset)};
m_nCursor += offset + GetLengthOfNextCharacter(offset);
return character;
}
std::string CCharacterReaderUTF8::ConsumeUntil(const std::vector<std::string>& lstCollection)
{
std::size_t offset{0};
bool found{false};
std::string accumulation;
while (!found && m_nDataLength > m_nCursor + offset)
{
std::string character = GetNextCharacter(offset);
offset += GetLengthOfNextCharacter(offset);
for (const auto& delimiter : lstCollection)
{
if (delimiter == character)
{
found = true;
}
}
if (!found)
{
accumulation += character;
}
else
{
offset -= character.size();
}
}
m_nCursor += offset;
return accumulation;
}
bool CCharacterReaderUTF8::IsEOF() const
{
return m_nDataLength < m_nCursor + 1;
}
void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const
{
const unsigned char invalidByteC0{0xC0};
const unsigned char invalidByteC1{0xC1};
const unsigned char lowerBoundInvalidRegion{0xF5};
for (std::size_t i = 0; i < m_ssString.size(); ++i)
{
unsigned char uc = m_ssString[i];
if (uc == invalidByteC0 || uc == invalidByteC1 || uc >= lowerBoundInvalidRegion)
{
std::stringstream message;
message << "Invalid byte " << std::hex << uc << std::dec << " at position " << i << "\n";
throw XTOMLParseException(message.str());
}
}
}
void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
{
enum class EState
{
state_neutral,
state_two_byte,
state_three_byte,
state_four_byte,
state_error
};
EState eCurrentState{EState::state_neutral};
uint8_t uiIndex{0};
auto fnCheckByteInNeutralState = [&uiIndex, &eCurrentState](unsigned char uc)
{
if ((uc & m_uiOneByteCheckMask) == m_OneByteCheckValue)
{
eCurrentState = EState::state_neutral;
uiIndex = 0;
}
else if ((uc & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
{
eCurrentState = EState::state_four_byte;
uiIndex = 1;
}
else if ((uc & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
{
eCurrentState = EState::state_three_byte;
uiIndex = 1;
}
else if ((uc & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
{
eCurrentState = EState::state_two_byte;
uiIndex = 1;
}
else
{
eCurrentState = EState::state_error;
}
};
auto fnCheckByteInTwoByteState = [&uiIndex, &eCurrentState](unsigned char uc)
{
if ((uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
{
uiIndex = 0;
eCurrentState = EState::state_neutral;
}
else
{
eCurrentState = EState::state_error;
}
};
auto fnCheckByteInThreeByteState = [&uiIndex, &eCurrentState](unsigned char uc)
{
if (uiIndex == 1 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
{
uiIndex = 2;
}
else if (uiIndex == 2 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
{
uiIndex = 0;
eCurrentState = EState::state_neutral;
}
else
{
eCurrentState = EState::state_error;
}
};
auto fnCheckByteInFourByteState = [&uiIndex, &eCurrentState](unsigned char uc)
{
if (uiIndex <= 2 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
{
++uiIndex;
}
else if (uiIndex == 3 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
{
uiIndex = 0;
eCurrentState = EState::state_neutral;
}
else
{
eCurrentState = EState::state_error;
}
};
for (std::size_t i = 0; i < m_ssString.size(); ++i)
{
uint8_t uiCurrentByte = m_ssString[i];
switch (eCurrentState)
{
case EState::state_neutral:
fnCheckByteInNeutralState(uiCurrentByte);
break;
case EState::state_two_byte:
fnCheckByteInTwoByteState(uiCurrentByte);
break;
case EState::state_three_byte:
fnCheckByteInThreeByteState(uiCurrentByte);
break;
case EState::state_four_byte:
fnCheckByteInFourByteState(uiCurrentByte);
break;
default:
std::stringstream sstreamMessage;
sstreamMessage << "Invalid character with byte " << std::hex << m_ssString[i - 1] << std::dec << "("
<< static_cast<int32_t>(m_ssString[i - 1]) << ") at index " << i - 1 << "\n";
throw XTOMLParseException(sstreamMessage.str());
break;
}
}
if (eCurrentState != EState::state_neutral)
{
std::stringstream sstreamMessage;
sstreamMessage << "Unfinished character at the end of file\n";
throw XTOMLParseException(sstreamMessage.str());
}
}
std::string CCharacterReaderUTF8::GetNextCharacter()
{
if (IsEOF())
{
return "";
}
return {m_ssString, m_nCursor, GetLengthOfNextCharacter()};
}
std::string CCharacterReaderUTF8::GetNextCharacter(std::size_t offset)
{
return {m_ssString, m_nCursor + offset, GetLengthOfNextCharacter(offset)};
}
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter() const
{
return GetLengthOfNextCharacter(0);
}
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter(std::size_t offset) const
{
uint8_t ui = m_ssString[m_nCursor + offset];
int32_t ret;
if ((ui & m_uiOneByteCheckMask) == m_OneByteCheckValue)
{
ret = 1;
}
else if ((ui & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
{
ret = 4;
}
else if ((ui & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
{
ret = 3;
}
else if ((ui & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
{
ret = 2;
}
else
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid character sequence with byte " << std::hex << ui << std::dec
<< " as start byte\n";
throw XTOMLParseException(sstreamMessage.str());
}
return ret;
}

View File

@@ -0,0 +1,127 @@
#ifndef CHARACTER_READER_UTF_8_H
#define CHARACTER_READER_UTF_8_H
#include <sstream>
#include <stdexcept>
#include <vector>
#include <cstdint>
/**
* @brief Reads a given input string, interprets bytes as UTF-8 and returns UTF-8 characters or strings in order on
* demand
*/
class CCharacterReaderUTF8
{
public:
/**
* @brief Standard constructor for empty character reader
*/
CCharacterReaderUTF8();
/**
* @brief Constructs a character reader from a given string
* @param[in] rssString UTF-8 input string.
* @throw InvalidCharacterException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters
* @throw InvalidByteException Throws an InvalidByteException if the input contains for UTF-8 invalid bytes
*/
CCharacterReaderUTF8(const std::string& rssString);
/**
* @brief Feed the character reader from the given string.
* @param[in] rssString UTF-8 input string.
*/
void Feed(const std::string& rssString);
/**
* @brief Reset the character reader content.
*/
void Reset();
/**
* @brief Gets the next UTF-8 character without advancing the cursor
* @return Returns the next UTF-8 character after the current cursor or an empty string if the cursor is at the
* end
*/
std::string Peek();
/**
* @brief Gets the next n-th UTF-8 character without advancing the cursor
* @param[in] n Step size
* @return Returns the n-th UTF-8 character after the current cursor or an empty string if n<1 or it would read
* after the last character
*/
std::string Peek(std::size_t n);
/**
* @brief Gets all upcoming UTF-8 characters until a terminating character without advancing the cursor
* @param[in] lstCollection A collection of terminating characters
* @return Returns a string of UTF-8 characters until (excluding) the first occurrence of one of the given
* characters
*/
std::string PeekUntil(const std::vector<std::string>& lstCollection);
/**
* @brief Gets the next UTF-8 character and advancing the cursor by one
* @return Returns the next UTF-8 character after the current cursor or an empty string if the cursor is at the
* end
*/
std::string Consume();
/**
* @brief Gets the next n-th UTF-8 character and advancing the cursor by n
* @param[in] n Step size
* @return Returns the n-th UTF-8 character after the current cursor or an empty string if n<1 or it would read
* after the last character
*/
std::string Consume(std::size_t n);
/**
* @brief Gets all upcoming UTF-8 characters until a terminating character and advancing the cursor by the number
* of characters in the returned string
* @param[in] lstCollection A collection of terminating characters
* @return Returns a string of UTF-8 characters until excludingg) the first occurrence of one of the given
* characters
*/
std::string ConsumeUntil(const std::vector<std::string>& lstCollection);
/**
* @brief Checks if the cursor is at the end of the data to read
* @return Returns true if the cursor is at the end of the readable data, false otherwise
*/
bool IsEOF() const;
private:
void CheckForInvalidUTF8Bytes() const;
void CheckForInvalidUTF8Sequences() const;
std::string GetNextCharacter();
std::string GetNextCharacter(std::size_t offset);
size_t GetLengthOfNextCharacter() const;
size_t GetLengthOfNextCharacter(std::size_t offset) const;
static const uint8_t m_uiOneByteCheckMask{0b10000000}; //!< Checkmask for 1-Byte UTF-8 characters
static const uint8_t m_OneByteCheckValue{0b00000000}; //!< Value of a 1-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiFollowByteCheckMask{0b11000000}; //!< Checkmask for followbyte of a multi-Byte UTF-8 character
static const uint8_t m_uiFollowByteValue{0b10000000}; //!< Value of a followbyte of a multi-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiTwoByteCheckMask{0b11100000}; //!< Checkmask for startbyte of 2-Byte UTF-8 characters
static const uint8_t m_uiTwoByteCheckValue{0b11000000}; //!< Value of a startbyte of a 2-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiThreeByteCheckMask{0b11110000}; //!< Checkmask for startbyte of 3-Byte UTF-8 characters
static const uint8_t m_uiThreeByteCheckValue{0b11100000}; //!< Value of a startbyte of a 3-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiFourByteCheckMask{0b11111000}; //!< Checkmask for startbyte of 4-Byte UTF-8 characters
static const uint8_t m_uiFourByteCheckValue{0b11110000}; //!< Value of a startbyte of a 4-Byte UTF-8 character
//!< after being or-ed with the checkmask
std::string m_ssString;
std::size_t m_nDataLength;
std::size_t m_nCursor;
};
#endif // CHARACTER_READER_UTF_8_H

View File

@@ -0,0 +1,20 @@
#ifndef CONFIG_EXCEPTION_H
#define CONFIG_EXCEPTION_H
#include <interfaces/toml.h>
except XTOMLParseException : public sdv::toml::XTOMLParseException
{
/**
* @brief Constructor
*/
XTOMLParseException(const std::string& rss) { ssMessage = rss; };
/**
* @brief Return the explanatory string.
* @return The descriptive string.
*/
virtual const char* what() const noexcept override { return ssMessage.c_str(); }
};
#endif // !defined CONFIG_EXCEPTION_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
#ifndef LEXER_TOML_H
#define LEXER_TOML_H
#include <algorithm>
#include <stack>
#include "character_reader_utf_8.h"
/**
* @brief Tokenizes the output of a character reader in regard of the TOML format and returns tokens in order on demand
*/
class CLexerTOML
{
public:
/**
* @brief Enum for all possible token categories
*/
enum class ETokenCategory : uint8_t
{
token_none, ///< Default
token_syntax_assignment, ///< '='
token_syntax_array_open, ///< '[' after '='
token_syntax_array_close, ///< ']' after an array open
token_syntax_table_open, ///< '['
token_syntax_table_close, ///< ']'
token_syntax_table_array_open, ///< '[['
token_syntax_table_array_close, ///< ']]'
token_syntax_inline_table_open, ///< '{'
token_syntax_inline_table_close, ///< '}'
token_syntax_comma, ///< ','
token_syntax_dot, ///< '.'
token_syntax_new_line, ///< Line break
token_key, ///< Key of a Key-Value-Pair
token_string, ///< A string for a Value of a Key-Value-Pair or Array
token_integer, ///< An integer for a Value of a Key-Value-Pair or Array
token_float, ///< A floating point number for a Value of a Key-Value-Pair or Array
token_boolean, ///< A bool for a Value of a Key-Value-Pair or Array
token_time_local, ///< Unused for now
token_date_time_offset, ///< Unused for now
token_date_time_local, ///< Unused for now
token_date_local, ///< Unused for now
token_eof, ///< End of File Token; may only be at the end of the token array
token_error, ///< Error token containing an error message; further lexing is not affected
token_empty, ///< Empty token for trying to read out of bounds
token_terminated, ///< Terminated token containing an error message; further lexing is terminated
};
/**
* @brief Contains lexed information for the parser
*/
struct SToken
{
/**
* @brief Default constructor
*/
SToken() = default;
/**
* @brief Constructs a new Token object with a given category
* @param[in] category The initial token category value for the token to be constructed
*/
explicit SToken(ETokenCategory category) : eCategory(category)
{}
std::string ssContentString; ///< Token string content
int64_t iContentInteger{}; ///< Token integer content
double dContentFloatingpoint{}; ///< Token floatingpoint content
bool bContentBoolean{}; ///< Token boolean content
ETokenCategory eCategory{ETokenCategory::token_none}; ///< Token category
};
/**
* @brief Default constructor
*/
CLexerTOML() = default;
/**
* @brief Constructs a new LexerTOML object with given input data that will be lexed
* @param[in] rssString The UTF-8 encoded content of a TOML source
*/
CLexerTOML(const std::string& rssString);
/**
* @brief Feed the lexer with the given string.
* @param[in] rssString UTF-8 input string.
*/
void Feed(const std::string& rssString);
/**
* @brief Reset the lexer content.
*/
void Reset();
/**
* @brief Gets the next token after the current cursor position without advancing the cursor
* @return Returns the next token after the current cursor position or a End-of-File-Token if there are no
* tokens
*/
SToken Peek() const;
/**
* @brief Gets the next token after the current cursor position and advancing the cursor by one
* @return Returns the next token after the current cursor position or a End-of-File-Token if there are no
* tokens
*/
SToken Consume();
/**
* @brief Gets the n-th token after the current cursor without advancing the cursor
* @param[in] n Step size
* @return Returns the n-th token after the current cursor position or and empty token if n<1 or the end-token
* if a step of n would read a position after the end-token
*/
SToken Peek(int32_t n);
/**
* @brief Gets the n-th token after the current cursor and advancing the cursor by n
* @param[in] n Step size
* @return Returns the n-th token after the current cursor position or and empty token if n<1 or the end-token
* if a step of n would read a position after the end-token
*/
SToken Consume(int32_t n);
/**
* @brief Checks if the end-token was consumed
* @return Returns true if the end-token was consumed by Consume() or Consume(n) or if there are no tokens;
* false otherwise
*/
bool IsEnd() const;
private:
void GenerateTokens();
bool IsBasicQuotedKey();
void ReadBasicQuotedKey();
bool IsLiteralQuotedKey();
void ReadLiteralQuotedKey();
bool IsBareKey();
void ReadBareKey();
bool IsBasicString();
void ReadBasicString();
bool IsBasicMultilineString();
void ReadBasicMultilineString();
bool IsLiteralString();
void ReadLiteralString();
bool IsLiteralMultilineString();
void ReadLiteralMultilineString();
bool IsInteger();
void ReadInteger();
bool IsFloat();
void ReadFloat();
bool IsBool();
void ReadBool();
bool IsWhitespace();
void ReadWhitespace();
bool IsSyntaxElement();
void ReadSyntaxElement();
bool IsComment();
void ReadComment();
void ReadUnknownSequence();
std::string Unescape();
std::string Unicode4DigitToUTF8();
std::string Unicode8DigitToUTF8();
std::string UnicodeToUTF8(uint8_t numCharacters);
static uint32_t HexToDecimal(const char character);
static uint32_t DecimalToDecimal(const char character);
static uint32_t OctalToDecimal(const char character);
static uint32_t BinaryToDecimal(const char character);
CCharacterReaderUTF8 m_reader;
std::vector<SToken> m_vecTokens;
std::size_t m_nCursor{0};
/**
* @brief Enum for differentiating between keys and values that are potentially indifferent like '"value" =
* "value"'
*/
enum class EExpectation
{
expect_key, ///< A key is expected over a value
expect_value, ///< A value is expected over a key
expect_value_once, ///< A value is expected over a key once
};
std::stack<EExpectation> m_stackExpectations; ///< Tracking of key or value expectations in nested structures
// int32_t m_LineCount{0};
const std::vector<std::string> m_vecKeyDelimiters{
"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key
const std::vector<std::string> m_vecValueDelimiters{
"\n", "\t", "\r", " ", ",", "", "]", "}"}; ///< Characters that delimit a value
};
#endif // LEXER_TOML_H

View File

@@ -0,0 +1,760 @@
#include <algorithm>
#include "parser_node_toml.h"
#include "exception.h"
#include <sstream>
size_t FindFirst(const std::string& rss, const std::string& rssSeparator /*= "."*/)
{
enum class EType {normal, single_quoted_string, double_quoted_string} eType = EType::normal;
size_t nPos = 0;
while (nPos < rss.size())
{
switch (rss[nPos])
{
case '\'':
if (eType == EType::normal)
eType = EType::single_quoted_string;
else if (eType == EType::single_quoted_string)
eType = EType::normal;
break;
case '\"':
if (eType == EType::normal)
eType = EType::double_quoted_string;
else if (eType == EType::double_quoted_string)
eType = EType::normal;
break;
case '\\':
nPos++;
break;
default:
if (eType == EType::normal && rssSeparator.find(rss[nPos]) != std::string::npos)
return nPos;
break;
}
nPos++;
}
return nPos >= rss.size() ? std::string::npos : nPos;
}
size_t FindLast(const std::string& rss, const std::string& rssSeparator /*= "."*/)
{
enum class EType {normal, single_quoted_string, double_quoted_string} eType = EType::normal;
size_t nPos = rss.size();
while (nPos)
{
nPos--;
bool bEscaped = nPos && rss[nPos - 1] == '\\';
switch (rss[nPos])
{
case '\'':
if (bEscaped)
nPos--;
else if (eType == EType::normal)
eType = EType::single_quoted_string;
else if (eType == EType::single_quoted_string)
eType = EType::normal;
break;
case '\"':
if (bEscaped)
nPos--;
else if (eType == EType::normal)
eType = EType::double_quoted_string;
else if (eType == EType::double_quoted_string)
eType = EType::normal;
break;
default:
if (eType == EType::normal && rssSeparator.find(rss[nPos]) != std::string::npos)
return nPos;
break;
}
}
return std::string::npos;
}
bool CompareEqual(const std::string& rss1, const std::string& rss2)
{
size_t nStart1 = 0, nStop1 = rss1.size();
if (rss1.size() && rss1.find_first_of("\"\'") == 0 && rss1.find_last_of("\"\'") == (rss1.size() - 1))
{
nStart1++;
nStop1--;
}
size_t nStart2 = 0, nStop2 = rss2.size();
if (rss2.size() && rss2.find_first_of("\"\'") == 0 && rss2.find_last_of("\"\'") == (rss2.size() - 1))
{
nStart2++;
nStop2--;
}
if (nStop1 - nStart1 != nStop2 - nStart2) return false;
for (size_t n = 0; n < (nStop1 - nStart1); n++)
{
if (rss1[nStart1 + n] != rss2[nStart2 + n])
return false;
}
return true;
}
std::string EscapeString(const std::string& rssString, const char cQuoteType /*= '\"'*/)
{
// Iterate through the string
std::stringstream sstream;
size_t nPos = 0;
uint32_t uiUTFChar = 0;
while (nPos < rssString.size())
{
uint8_t uiChar = static_cast<uint8_t>(rssString[nPos]);
switch (uiChar)
{
case '\a': sstream << "\\a"; break;
case '\b': sstream << "\\b"; break;
case '\f': sstream << "\\f"; break;
case '\n': sstream << "\\n"; break;
case '\r': sstream << "\\r"; break;
case '\t': sstream << "\\t"; break;
case '\v': sstream << "\\v"; break;
case '\\': sstream << "\\\\"; break;
case '\'': if (static_cast<uint8_t>(cQuoteType) == uiChar) sstream << "\\"; sstream << "\'"; break;
case '\"': if (static_cast<uint8_t>(cQuoteType) == uiChar) sstream << "\\"; sstream << "\""; break;
default:
if (uiChar >= 0x20 && uiChar < 0x7f)
{
// Standard ASCII
sstream << static_cast<char>(uiChar);
break;
}
else if (uiChar <= 0x80) // One byte UTF-8
uiUTFChar = static_cast<uint32_t>(uiChar);
else if (uiChar <= 0xDF) // Two bytes UTF-8
{
uiUTFChar = static_cast<size_t>(uiChar & 0b00011111) << 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssString.size()) break;
uiUTFChar |= static_cast<size_t>(rssString[nPos] & 0b00111111);
}
else if (uiChar <= 0xEF) // Three bytes UTF-8
{
uiUTFChar = static_cast<size_t>(uiChar & 0b00001111) << 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssString.size()) break;
uiUTFChar |= static_cast<size_t>(rssString[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssString.size()) break;
uiUTFChar |= static_cast<size_t>(rssString[nPos] & 0b00111111);
}
else if (uiChar <= 0xF7) // Four bytes UTF-8
{
uiUTFChar = static_cast<size_t>(uiChar & 0b00000111) << 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssString.size()) break;
uiUTFChar |= static_cast<size_t>(rssString[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssString.size()) break;
uiUTFChar |= static_cast<size_t>(rssString[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssString.size()) break;
uiUTFChar |= static_cast<size_t>(rssString[nPos] & 0b00111111);
}
// Stream the UTF character
if (uiUTFChar <= 0xFFFF)
sstream << "\\u" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << uiUTFChar;
else
sstream << "\\U" << std::uppercase << std::hex << std::setfill('0') << std::setw(8) << uiUTFChar;
break;
}
nPos++;
}
return sstream.str();
}
CNode::CNode(const std::string& rssName) : m_ssName(rssName)
{}
CNode::~CNode()
{}
sdv::u8string CNode::GetName() const
{
return m_ssName;
}
sdv::any_t CNode::GetValue() const
{
return sdv::any_t();
}
sdv::u8string CNode::GetTOML() const
{
std::string ssLastTable;
std::string ssParent;
return CreateTOMLText(ssParent, ssLastTable, true, false, true, true);
}
std::shared_ptr<const CArray> CNode::GetArray() const
{
if (!dynamic_cast<const CArray*>(this)) return {};
return std::static_pointer_cast<const CArray>(shared_from_this());
}
std::shared_ptr<CArray> CNode::GetArray()
{
if (!dynamic_cast<CArray*>(this)) return {};
return std::static_pointer_cast<CArray>(shared_from_this());
}
std::shared_ptr<const CTable> CNode::GetTable() const
{
if (!dynamic_cast<const CTable*>(this)) return {};
return std::static_pointer_cast<const CTable>(shared_from_this());
}
std::shared_ptr<CTable> CNode::GetTable()
{
if (!dynamic_cast<const CTable*>(this)) return {};
return std::static_pointer_cast<CTable>(shared_from_this());
}
std::weak_ptr<const CNode> CNode::GetParent() const
{
return m_ptrParent;
}
void CNode::SetParent(const std::shared_ptr<CNode>& rptrParent)
{
m_ptrParent = rptrParent;
}
std::shared_ptr<CNode> CNode::Find(const std::string& /*rssPath*/) const
{
return std::shared_ptr<CNode>();
}
std::shared_ptr<CNode> CNode::GetDirect(const std::string& /*rssPath*/) const
{
// The CNode implementation doesn't have any children. Therefore there is nothing to get.
return std::shared_ptr<CNode>();
}
std::string CNode::CreateTOMLText(const std::string& rssParent /*= std::string()*/) const
{
std::string ssLastTable;
return CreateTOMLText(rssParent, ssLastTable);
}
void CNode::Add(const std::string& rssPath, const std::shared_ptr<CNode>& /*rptrNode*/, bool /*bDefinedExplicitly = true*/)
{
throw XTOMLParseException(("Not allowed to add '" + rssPath + "'; parent node is final").c_str());
}
CBooleanNode::CBooleanNode(const std::string& rssName, bool bVal) : CNode(rssName), m_bVal(bVal)
{}
sdv::toml::ENodeType CBooleanNode::GetType() const
{
return sdv::toml::ENodeType::node_boolean;
}
sdv::any_t CBooleanNode::GetValue() const
{
return sdv::any_t(m_bVal);
}
std::string CBooleanNode::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool /*bRoot*/) const
{
std::stringstream sstreamEntry;
// Do we need to start a table?
if (!bEmbedded && bFirst && bAssignment && rssParent != rssLastPrintedTable)
{
sstreamEntry << std::endl << "[" << rssParent << "]" << std::endl;
rssLastPrintedTable = rssParent;
}
if (bEmbedded && !bFirst) // 2nd or higher array entry
sstreamEntry << ", ";
if (!bEmbedded || bAssignment) // Not an array entry
sstreamEntry << GetName() << " = ";
sstreamEntry << (m_bVal ? "true" : "false");
if (!bEmbedded) // Not an array entry
sstreamEntry << std::endl;
return sstreamEntry.str();
}
CIntegerNode::CIntegerNode(const std::string& rssName, int64_t iVal) : CNode(rssName), m_iVal(iVal)
{}
sdv::toml::ENodeType CIntegerNode::GetType() const
{
return sdv::toml::ENodeType::node_integer;
}
sdv::any_t CIntegerNode::GetValue() const
{
return sdv::any_t(m_iVal);
}
std::string CIntegerNode::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool /*bRoot*/) const
{
std::stringstream sstreamEntry;
// Do we need to start a table?
if (!bEmbedded && bFirst && bAssignment && rssParent != rssLastPrintedTable)
{
sstreamEntry << std::endl << "[" << rssParent << "]" << std::endl;
rssLastPrintedTable = rssParent;
}
if (bEmbedded && !bFirst) // 2nd or higher array entry
sstreamEntry << ", ";
if (!bEmbedded || bAssignment) // Not an array entry
sstreamEntry << GetName() << " = ";
sstreamEntry << m_iVal;
if (!bEmbedded) // Not an array entry
sstreamEntry << std::endl;
return sstreamEntry.str();
}
CFloatingPointNode::CFloatingPointNode(const std::string& rssName, double dVal) : CNode(rssName), m_dVal(dVal)
{}
sdv::toml::ENodeType CFloatingPointNode::GetType() const
{
return sdv::toml::ENodeType::node_floating_point;
}
sdv::any_t CFloatingPointNode::GetValue() const
{
return sdv::any_t(m_dVal);
}
std::string CFloatingPointNode::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst,
bool bEmbedded, bool bAssignment, bool /*bRoot*/) const
{
std::stringstream sstreamEntry;
// Do we need to start a table?
if (!bEmbedded && bFirst && bAssignment && rssParent != rssLastPrintedTable)
{
sstreamEntry << std::endl << "[" << rssParent << "]" << std::endl;
rssLastPrintedTable = rssParent;
}
if (bEmbedded && !bFirst) // 2nd or higher array entry
sstreamEntry << ", ";
if (!bEmbedded || bAssignment) // Not an array entry
sstreamEntry << GetName() << " = ";
sstreamEntry << std::setprecision(15) << std::defaultfloat << m_dVal;
if (!bEmbedded) // Not an array entry
sstreamEntry << std::endl;
return sstreamEntry.str();
}
CStringNode::CStringNode(const std::string& rssName, const std::string& rssVal) : CNode(rssName), m_ssVal(rssVal)
{}
sdv::toml::ENodeType CStringNode::GetType() const
{
return sdv::toml::ENodeType::node_string;
}
sdv::any_t CStringNode::GetValue() const
{
return sdv::any_t(m_ssVal);
}
std::string CStringNode::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool /*bRoot*/) const
{
std::stringstream sstreamEntry;
// Do we need to start a table?
if (!bEmbedded && bFirst && bAssignment && rssParent != rssLastPrintedTable)
{
sstreamEntry << std::endl << "[" << rssParent << "]" << std::endl;
rssLastPrintedTable = rssParent;
}
if (bEmbedded && !bFirst) // 2nd or higher array entry
sstreamEntry << ", ";
if (!bEmbedded || bAssignment) // Not an array entry
sstreamEntry << GetName() << " = ";
sstreamEntry << "\"" << EscapeString(m_ssVal) << "\"";
if (!bEmbedded) // Not an array entry
sstreamEntry << std::endl;
return sstreamEntry.str();
}
CNodeCollection::CNodeCollection(const std::string& rssName) : CNode(rssName)
{}
uint32_t CNodeCollection::GetCount() const
{
return static_cast<uint32_t>(m_vecContent.size());
}
sdv::IInterfaceAccess* CNodeCollection::GetNode(/*in*/ uint32_t uiIndex) const
{
auto ptrNode = Get(uiIndex);
return static_cast<sdv::IInterfaceAccess*>(ptrNode.get());
}
std::shared_ptr<CNode> CNodeCollection::Get(uint32_t uiIndex) const
{
if (static_cast<size_t>(uiIndex) >= m_vecContent.size()) return nullptr;
return m_vecContent[uiIndex];
}
sdv::IInterfaceAccess* CNodeCollection::GetNodeDirect(/*in*/ const sdv::u8string& ssPath) const
{
auto ptrNode = GetDirect(ssPath);
return static_cast<sdv::IInterfaceAccess*>(ptrNode.get());
}
bool CNodeCollection::AddElement(const std::shared_ptr<CNode>& rptrNode, bool bUnique /*= false*/)
{
if (!rptrNode) return false;
if (bUnique && std::find_if(m_vecContent.begin(), m_vecContent.end(), [&](const std::shared_ptr<CNode>& rptrNodeEntry)
{
return CompareEqual(rptrNodeEntry->GetName(), rptrNode->GetName());
}) != m_vecContent.end()) return false;
m_vecContent.push_back(rptrNode);
return true;
}
CTable::CTable(const std::string& rssName) : CNodeCollection(rssName)
{}
sdv::toml::ENodeType CTable::GetType() const
{
return sdv::toml::ENodeType::node_table;
}
std::shared_ptr<CNode> CTable::GetDirect(const std::string& rssPath) const
{
size_t nSeparator = FindFirst(rssPath, ".[");
std::string ssKey = rssPath.substr(0, nSeparator);
std::shared_ptr<CNode> ptrNode;
for (uint32_t uiIndex = 0; !ptrNode && uiIndex < GetCount(); uiIndex++)
{
std::shared_ptr<CNode> ptrNodeEntry = Get(uiIndex);
if (!ptrNodeEntry) continue;
if (CompareEqual(ptrNodeEntry->GetName(), ssKey)) ptrNode = ptrNodeEntry;
}
if (!ptrNode) return ptrNode; // Not found
// Done?
if (nSeparator == std::string::npos) return ptrNode;
// There is more...
if (rssPath[nSeparator] == '.') nSeparator++; // Skip dot
return ptrNode->GetDirect(rssPath.substr(nSeparator));
}
std::string CTable::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const
{
// Create the full name
std::string ssFullName = rssParent;
if (!GetName().empty() && !bRoot)
{
if (!ssFullName.empty()) ssFullName += ".";
ssFullName += GetName();
}
// Stream the table
std::stringstream sstreamEntry;
if (bEmbedded && !bFirst) // 2nd or higher array entry
sstreamEntry << ", ";
if (bEmbedded) // Embedded table in an array
sstreamEntry << "{";
for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++)
{
std::shared_ptr<CNode> ptrNode = Get(uiIndex);
if (!ptrNode) continue;
sstreamEntry << ptrNode->CreateTOMLText(ssFullName, rssLastPrintedTable, uiIndex == 0, bEmbedded);
}
if (bEmbedded) // Embedded table in an array
sstreamEntry << "}";
if (!bEmbedded && !bAssignment && !GetName().empty())
sstreamEntry << std::endl;
return sstreamEntry.str();
}
void CTable::Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly)
{
if (!m_bOpenToAddChildren)
throw XTOMLParseException(("Not allowed to add '" + rssPath + "'; parent node is final").c_str());
size_t nDotPos = FindFirst(rssPath);
std::string ssFirst = rssPath.substr(0, nDotPos);
std::string ssSecond = nDotPos == std::string::npos ? "" : rssPath.substr(nDotPos + 1);
auto ptrParent = Find(ssFirst);
// Element does not already exist at given path
if (!ptrParent)
{
// Add the new element as a direct child
if (nDotPos == std::string::npos)
{
rptrNode->SetParent(shared_from_this());
GetTable()->AddElement(rptrNode);
return;
}
// Add the new element as a descendant further down
auto ptrIntermediateTable = std::make_shared<CNormalTable>(ssFirst);
ptrIntermediateTable->SetParent(shared_from_this());
ptrIntermediateTable->m_bDefinedExplicitly = bDefinedExplicitly;
GetTable()->AddElement(ptrIntermediateTable);
static_cast<CNode*>(ptrIntermediateTable.get())->Add(ssSecond, rptrNode, bDefinedExplicitly);
return;
}
if (dynamic_cast<CTableArray*>(ptrParent.get()) && nDotPos == std::string::npos)
{
ptrParent->Add(ssFirst, rptrNode, bDefinedExplicitly);
return;
}
// Element already exists but would be inserted as a direct child
if (nDotPos == std::string::npos)
{
// Make an already implicitly defined table explicitly defined
if (ptrParent->GetType() == rptrNode->GetType() && dynamic_cast<CNodeCollection*>(ptrParent.get()) &&
!static_cast<CNodeCollection*>(ptrParent.get())->m_bDefinedExplicitly)
{
static_cast<CNodeCollection*>(ptrParent.get())->m_bDefinedExplicitly = true;
return;
}
throw XTOMLParseException(("Name '" + ssFirst + "' already exists").c_str());
}
// Element already exists and new element would be added as descendant
ptrParent->Add(ssSecond, rptrNode, bDefinedExplicitly);
}
std::shared_ptr<CNode> CTable::Find(const std::string& rssPath) const
{
size_t nDotPos = FindFirst(rssPath);
std::string ssFirst = rssPath.substr(0, nDotPos);
std::string ssSecond = nDotPos == std::string::npos ? "" : rssPath.substr(nDotPos + 1);
for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++)
{
std::shared_ptr<CNode> ptrNode;
ptrNode = Get(uiIndex);
if (!ptrNode) continue;
if (CompareEqual(ptrNode->GetName(), ssFirst))
{
if (nDotPos == std::string::npos)
return ptrNode;
return ptrNode->Find(ssSecond);
}
}
// No node found...
return std::shared_ptr<CNode>();
}
CArray::CArray(const std::string& rssName) : CNodeCollection(rssName)
{}
sdv::toml::ENodeType CArray::GetType() const
{
return sdv::toml::ENodeType::node_array;
}
std::shared_ptr<CNode> CArray::GetDirect(const std::string& rssPath) const
{
size_t nIndexBegin = FindFirst(rssPath, "[");
if (nIndexBegin == std::string::npos) return std::shared_ptr<CNode>(); // Unexpected
size_t nIndexEnd = rssPath.find_first_not_of("0123456789", nIndexBegin + 1);
if (nIndexEnd == std::string::npos) return std::shared_ptr<CNode>(); // Unexpected
if (rssPath[nIndexEnd] != ']') return std::shared_ptr<CNode>(); // Unexpected
std::string ssIndex = rssPath.substr(nIndexBegin + 1, nIndexEnd - nIndexBegin - 1);
if (ssIndex.empty()) return std::shared_ptr<CNode>(); // Unexpected
uint32_t uiIndex = std::atol(ssIndex.c_str());
nIndexEnd++;
// Get the node
if (uiIndex >= GetCount()) return std::shared_ptr<CNode>(); // Not found
std::shared_ptr<CNode> ptrNode = Get(uiIndex);
// Done?
if (nIndexEnd == rssPath.size()) return ptrNode;
// Expecting a dot?
size_t nSeparator = nIndexEnd;
if (rssPath[nSeparator] == '.') nSeparator++; // Skip dot
return ptrNode->GetDirect(rssPath.substr(nSeparator));
}
std::string CArray::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool /*bRoot*/) const
{
std::stringstream sstreamEntry;
// Do we need to start a table?
if (!bEmbedded && bFirst && bAssignment && rssParent != rssLastPrintedTable)
{
sstreamEntry << std::endl << "[" << rssParent << "]" << std::endl;
rssLastPrintedTable = rssParent;
}
// Stream the array
if (bEmbedded && !bFirst) // 2nd or higher array entry
sstreamEntry << ", ";
if (!bEmbedded || bAssignment) // Not an array entry
sstreamEntry << GetName() << " = ";
sstreamEntry << "[";
for (uint32_t ui = 0; ui < GetCount(); ui++)
sstreamEntry << Get(ui)->CreateTOMLText(rssParent, rssLastPrintedTable, ui == 0, true, false);
sstreamEntry << "]";
if (!bEmbedded) // Not an array entry
sstreamEntry << std::endl;
return sstreamEntry.str();
}
void CArray::Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly)
{
size_t nDotPos = FindFirst(rssPath);
std::string ssFirst = rssPath.substr(0, nDotPos);
std::string ssSecond = nDotPos == std::string::npos ? "" : rssPath.substr(nDotPos + 1);
// Add new element to array
if (nDotPos == std::string::npos)
{
GetArray()->AddElement(rptrNode);
return;
}
// Add new element to subelement of array
if (std::any_of(ssFirst.begin(), ssFirst.end(), [](char digit) { return (digit < '0') || (digit > '9'); }))
{
throw XTOMLParseException(("Invalid array access subscript '" + ssFirst + "'").c_str());
}
uint32_t uiIndex = std::stoi(ssFirst);
if (uiIndex >= GetArray()->GetCount())
{
// This indicates an array within an arrays. Add the intermediate array
auto ptrIntermediateArray = std::make_shared<CNormalArray>(ssFirst);
ptrIntermediateArray->SetParent(shared_from_this());
ptrIntermediateArray->m_bDefinedExplicitly = bDefinedExplicitly;
GetArray()->AddElement(ptrIntermediateArray);
static_cast<CNode*>(ptrIntermediateArray.get())->Add(ssSecond, rptrNode, bDefinedExplicitly);
//throw XTOMLParseException(("Invalid array access index '" + ssFirst + "'; out of bounds").c_str());
}
GetArray()->Get(uiIndex)->Add(ssSecond, rptrNode, bDefinedExplicitly);
}
std::shared_ptr<CNode> CArray::Find(const std::string& rssPath) const
{
size_t nDotPos = FindFirst(rssPath);
std::string ssFirst = rssPath.substr(0, nDotPos);
std::string ssSecond = nDotPos == std::string::npos ? "" : rssPath.substr(nDotPos + 1);
if (ssFirst.empty())
throw XTOMLParseException("Missing array subscript");
if (std::any_of(ssFirst.begin(), ssFirst.end(), [](char digit) { return (digit < '0') || (digit > '9'); }))
throw XTOMLParseException(("Invalid array access subscript '" + ssFirst + "'").c_str());
uint32_t uiIndex = std::stoi(ssFirst);
if (GetArray()->GetCount() <= uiIndex)
throw XTOMLParseException(
("Invalid array access index '" + ssFirst + "'; out of bounds").c_str());
if (nDotPos == std::string::npos)
return GetArray()->Get(uiIndex);
return GetArray()->Get(uiIndex)->Find(ssSecond);
}
std::string CTableArray::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool /*bFirst*/, bool /*bEmbedded*/,
bool /*bAssignment*/, bool bRoot) const
{
// Create the full name
std::string ssFullName = rssParent;
if (!ssFullName.empty() && !bRoot) ssFullName += ".";
ssFullName += GetName();
// Stream the array
std::stringstream sstreamEntry;
for (uint32_t ui = 0; ui < GetCount(); ui++)
{
sstreamEntry << std::endl << "[[" << ssFullName << "]]" << std::endl;
rssLastPrintedTable = ssFullName;
sstreamEntry << Get(ui)->CreateTOMLText(ssFullName, rssLastPrintedTable, ui == 0, false, false);
}
//sstreamEntry << std::endl;
return sstreamEntry.str();
}
void CTableArray::Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly)
{
//size_t nDotPos = FindFirst(rssPath);
//std::string ssFirst = rssPath.substr(0, nDotPos);
//std::string ssSecond = nDotPos == std::string::npos ? "" : rssPath.substr(nDotPos + 1);
uint32_t uiSize = GetArray()->GetCount();
if (uiSize == 0)
{
throw XTOMLParseException("Trying to access table in an empty array of tables");
}
GetArray()->Get(uiSize - 1)->Add(rssPath, rptrNode, bDefinedExplicitly);
}
std::shared_ptr<CNode> CTableArray::Find(const std::string& rssPath) const
{
//size_t nDotPos = FindFirst(rssPath);
//std::string ssFirst = rssPath.substr(0, nDotPos);
//std::string ssSecond = nDotPos == std::string::npos ? "" : rssPath.substr(nDotPos + 1);
if (!GetArray()->GetCount())
throw XTOMLParseException(("Trying to access empty table array; " + rssPath).c_str());
return GetArray()->Get(GetArray()->GetCount() - 1)->Find(rssPath);
}
std::string CRootTable::CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool /*bFirst*/,
bool /*bEmbedded*/, bool /*bAssignment*/, bool /*bRoot*/) const
{
// Create the full name
std::string ssFullName = rssParent;
// Stream the table
std::stringstream sstreamEntry;
for (uint32_t uiIndex = 0; uiIndex < GetCount(); uiIndex++)
{
std::shared_ptr<CNode> ptrNode = Get(uiIndex);
if (!ptrNode) continue;
sstreamEntry << ptrNode->CreateTOMLText(ssFullName, rssLastPrintedTable);
}
sstreamEntry << std::endl;
// Skip whitespace at the beginning
std::string ssRet = sstreamEntry.str();
size_t nStart = ssRet.find_first_not_of(" \t\f\r\n\v");
if (nStart == std::string::npos) return std::string();
return ssRet.substr(nStart);
}

View File

@@ -0,0 +1,718 @@
#ifndef PARSER_NODE_TOML_H
#define PARSER_NODE_TOML_H
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include <interfaces/toml.h>
#include <support/interface_ptr.h>
// Forward declaration
class CArray;
class CTable;
/**
* @brief Find the first separator character. Do not include string content (single/or double quoted) and escape characters.
* @param[in] rss Reference to the string.
* @param[in] rssSeparator One of the characters to find in the string. Must not be an empty string!
* @return The position of the first separator character or std::string::npos when none has found.
*/
size_t FindFirst(const std::string& rss, const std::string& rssSeparator = ".");
/**
* @brief Find the last separator character. Do not include string content (single/or double quoted) and escape characters.
* @param[in] rss Reference to the string.
* @param[in] rssSeparator One of the characters to find in the string. Must not be an empty string!
* @return The position of the last separator character or std::string::npos when none has found.
*/
size_t FindLast(const std::string& rss, const std::string& rssSeparator = ".");
/**
* @brief Compare both string ignoring the quotes at the first position and last position.
* @param[in] rss1 Reference to the first string.
* @param[in] rss2 Reference to the second string.
* @return The comparison result.
*/
bool CompareEqual(const std::string& rss1, const std::string& rss2);
/**
* @brief Escape a string using escape characters and UTF values.
*/
std::string EscapeString(const std::string& rssString, const char cQuoteType = '\"');
/**
* @brief Node to build up the parse tree
*/
class CNode : public std::enable_shared_from_this<CNode>, public sdv::IInterfaceAccess, public sdv::toml::INodeInfo
{
protected:
/**
* @brief Constructs a new node object representing a table or an array.
* @param[in] rssName Reference to the name of the node.
*/
CNode(const std::string& rssName);
public:
/**
* @brief Deleted since Nodes should only be handled via smart-pointer
* @{
*/
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
CNode(const CNode&&) = delete;
CNode& operator=(const CNode&&) = delete;
/**
* @}
*/
/**
* @brief Destroy the node object
*/
~CNode();
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::toml::INodeInfo)
END_SDV_INTERFACE_MAP()
/**
* @brief Get the node name. Overload of sdv::toml::INodeInfo::GetName.
* @return String containing the name of the node.
*/
virtual sdv::u8string GetName() const override;
/**
* @brief The node value. Overload of sdv::toml::INodeInfo::GetValue.
* @return For boolean, integer, floating point and strings, the function returns a value. Otherwise the function
* returns empty.
*/
virtual sdv::any_t GetValue() const override;
/**
* @brief The node value. Overload of sdv::toml::INodeInfo::GetTOML.
* @return For boolean, integer, floating point and strings, the function returns a value. Otherwise the function
* returns empty.
*/
virtual sdv::u8string GetTOML() const override;
/**
* @brief Gets the array value of a node
* @return Returns a shared pointer of the array value stored in the node if the stored type is array
*/
std::shared_ptr<const CArray> GetArray() const;
/**
* @brief Gets the array value of a node
* @return Returns a shared pointer of the array value stored in the node if the stored type is array
*/
std::shared_ptr<CArray> GetArray();
/**
* @brief Gets the table value of a node
* @return Returns a shared pointer of the table value stored in the node if the stored type is table
*/
std::shared_ptr<const CTable> GetTable() const;
/**
* @brief Gets the table value of a node
* @return Returns a shared pointer of the table value stored in the node if the stored type is table
*/
std::shared_ptr<CTable> GetTable();
protected:
/**
* @brief Gets the Parent Node
* @return Returns the parent Node
* @attention Beware of expiring pointers
*/
std::weak_ptr<const CNode> GetParent() const;
public:
/**
* @brief Set the parent node.
* @param[in] rptrParent Reference to the node to assign to this node as a parent.
*/
void SetParent(const std::shared_ptr<CNode>& rptrParent);
/**
* @brief Accesses a node by its key in the parse tree.
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
* E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and traversed
* by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'. These access
* conventions can also be chained like 'table.array[2][1].subtable.integerElement'.
* @attention Array indexing starts with 0!
* @param[in] rssPath The path of the node to searched for.
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
*/
virtual std::shared_ptr<CNode> GetDirect(const std::string& rssPath) const;
/**
* @brief Create the TOML text based on the content using an optional parent node.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @return The string containing the TOML text.
*/
std::string CreateTOMLText(const std::string& rssParent = std::string()) const;
/**
* @brief Get the TOML text based on the content.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst = true,
bool bEmbedded = false, bool bAssignment = true, bool bRoot = false) const = 0;
private:
std::weak_ptr<CNode> m_ptrParent; ///< Weak pointer to the parent node (if existing).
std::string m_ssName; ///< Name of the node.
public:
/**
* @brief Searches the subtree of the node for a node at the given location using the provided path.
* @remarks The path elements of arrays and tables are separated by a dot.
* @param[in] rssPath Reference to the string containing the path of the node to find.
* @return Returns a shared pointer to the wanted Node if it is found or an error-Node if it is not
*/
virtual std::shared_ptr<CNode> Find(const std::string& rssPath) const;
/**
* @brief Adds a given node to a given path in the tree
* @remarks The path elements of arrays and tables are separated by a dot.
* @param[in] rssPath Reference to the string containing the path in the tree of the location to the new node to be inserted.
* @param[in] rptrNode Reference to the smart pointer containing the new node to be added.
* @param[in] bDefinedExplicitly If a table that is created to create the path of the node to be added is defined explicitly.
* @throw XInvalidAccessException Throws an XInvalidAccessException if a ancestor node is not open to add children.
* @throw XDuplicateNameException Throws a XDuplicateNameException if a node with the same path as the node to
* be added is already defined explicitly
*/
virtual void Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly = true);
};
/**
* @brief Boolean value node.
*/
class CBooleanNode : public CNode
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the string containing the name of the node.
* @param[in] bVal The value to assign.
*/
CBooleanNode(const std::string& rssName, bool bVal);
/**
* @brief Get the node type. Overload of sdv::toml::INodeInfo::GetType.
* @return Type of the node.
*/
virtual sdv::toml::ENodeType GetType() const override;
/**
* @brief The node value. Overload of sdv::toml::INodeInfo::GetValue.
* @return For boolean, integer, floating point and strings, the function returns a value. Otherwise the function
* returns empty.
*/
virtual sdv::any_t GetValue() const override;
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
private:
bool m_bVal; ///< Value in case of boolean node.
};
/**
* @brief Integer value node.
*/
class CIntegerNode : public CNode
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the string containing the name of the node.
* @param[in] iVal The value to assign.
*/
CIntegerNode(const std::string& rssName, int64_t iVal);
/**
* @brief Get the node type. Overload of sdv::toml::INodeInfo::GetType.
* @return Type of the node.
*/
virtual sdv::toml::ENodeType GetType() const override;
/**
* @brief The node value. Overload of sdv::toml::INodeInfo::GetValue.
* @return For boolean, integer, floating point and strings, the function returns a value. Otherwise the function
* returns empty.
*/
virtual sdv::any_t GetValue() const override;
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
private:
int64_t m_iVal; ///< Value in case of integer node.
};
/**
* @brief Floating point value node.
*/
class CFloatingPointNode : public CNode
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the string containing the name of the node.
* @param[in] dVal The value to assign.
*/
CFloatingPointNode(const std::string& rssName, double dVal);
/**
* @brief Get the node type. Overload of sdv::toml::INodeInfo::GetType.
* @return Type of the node.
*/
virtual sdv::toml::ENodeType GetType() const override;
/**
* @brief The node value. Overload of sdv::toml::INodeInfo::GetValue.
* @return For boolean, integer, floating point and strings, the function returns a value. Otherwise the function
* returns empty.
*/
virtual sdv::any_t GetValue() const override;
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
private:
double m_dVal; ///< Value in case of floating point node.
};
/**
* @brief String value node.
*/
class CStringNode : public CNode
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the string containing the name of the node.
* @param[in] rssVal The value to assign.
*/
CStringNode(const std::string& rssName, const std::string& rssVal);
/**
* @brief Get the node type. Overload of sdv::toml::INodeInfo::GetType.
* @return Type of the node.
*/
virtual sdv::toml::ENodeType GetType() const override;
/**
* @brief The node value. Overload of sdv::toml::INodeInfo::GetValue.
* @return For boolean, integer, floating point and strings, the function returns a value. Otherwise the function
* returns empty.
*/
virtual sdv::any_t GetValue() const override;
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
private:
std::string m_ssVal; ///< Value in case of string or illegal (error) node.
};
/**
* @brief Base structure for arrays and tables.
*/
class CNodeCollection : public CNode, public sdv::toml::INodeCollection
{
protected:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CNodeCollection(const std::string& rssName);
public:
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollection)
SDV_INTERFACE_CHAIN_BASE(CNode)
END_SDV_INTERFACE_MAP()
/**
* @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount.
* @return The amount of nodes.
*/
virtual uint32_t GetCount() const override;
/**
* @brief Get the node. Overload of sdv::toml::INodeCollection::GetNode.
* @param[in] uiIndex Index of the node to get.
* @return Interface to the node object.
*/
virtual IInterfaceAccess* GetNode(/*in*/ uint32_t uiIndex) const override;
/**
* @brief Get the node.
* @param[in] uiIndex Index of the node to get.
* @return Smart pointer to the node object.
*/
std::shared_ptr<CNode> Get(uint32_t uiIndex) const;
/**
* @brief Searches a node by its key in the parse tree
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child
* name. E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be
* accessed and traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of
* the array 'array'. These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'.
* @attention Array indexing starts with 0!
* @param[in] ssPath The path of the node to searched for.
* @return Returns an interface the requested node if available.
*/
virtual sdv::IInterfaceAccess* GetNodeDirect(/*in*/ const sdv::u8string& ssPath) const override;
/**
* @brief Add an element to the collection.
* @param[in] rptrNode Reference to the node element smart pointer.
* @param[in] bUnique When set, check prevents adding an element with the same name.
* @return Returns whether the element addition was successful.
*/
bool AddElement(const std::shared_ptr<CNode>& rptrNode, bool bUnique = false);
private:
std::vector<std::shared_ptr<CNode>> m_vecContent; ///< Vector holding the child elements
public:
bool m_bDefinedExplicitly = true; ///< WHen set, the array/table is defined explicitly
///< (not internal).
};
/**
* @brief A dynamic table structure that allows mixed data in form of key value pairs
*/
class CTable : public CNodeCollection
{
protected:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CTable(const std::string& rssName);
public:
/**
* @brief Get the node type. Overload of sdv::toml::INodeInfo::GetType.
* @return Type of the node.
*/
virtual sdv::toml::ENodeType GetType() const override;
/**
* @brief Accesses a node by its key in the parse tree. Overload of CNode::GetDirect.
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
* E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and traversed
* by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'. These access
* conventions can also be chained like 'table.array[2][1].subtable.integerElement'.
* @attention Array indexing starts with 0!
* @param[in] rssPath The path of the node to searched for.
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
*/
virtual std::shared_ptr<CNode> GetDirect(const std::string& rssPath) const override;
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
/**
* @brief Adds a given node to a given path in the tree. Overload of CNode::Add.
* @param[in] rssPath Reference to the path in the tree where the new node is to be added
* @param[in] rptrNode Reference to the smart pointer holding the node.
* @param[in] bDefinedExplicitly If a table that is created to create the path of the node to be added is
* defined explicitly
* @throw XInvalidAccessException Throws an XInvalidAccessException if a ancestor node is not open to add
* children
* @throw XDuplicateNameException Throws a XDuplicateNameException if a node with the same path as the node to
* be added is already defined explicitly
*/
virtual void Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly) override;
/**
* @brief Searches the subtree of the node for a node at the given location using the provided path. Overload of CNode::Find.
* @remarks The path elements of arrays and tables are separated by a dot.
* @param[in] rssPath Reference to the path in the tree where the new node is to be added
* @return Returns a shared pointer to the wanted Node if it is found or an error-Node if it is not
*/
virtual std::shared_ptr<CNode> Find(const std::string& rssPath) const override;
bool m_bOpenToAddChildren = true; ///< If internal table, the table can be extended until the table
///< is closed.
};
/**
* @brief A dynamic array structure that allows mixed data
* @details The definition of an array in TOML differentiate massively from the syntax to access the elements. For example an array
* in TOML could be defined by:
* @code
* integers = [ 1, 2, 3 ]
* nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
* [[products]]
* name = "Hammer"
* sku = 738594937
* @endcode
* The first two examples define the complete array at once. The third example defines one element to be added to an array. Random
* access to previous definitions is not required.
* The access functions need random access to each element. The GetDirect function uses the syntax similar to C++:
* @code
* integers[1] --> gives: 2
* nested_mixed_array[1][2] --> gives: "c"
* products[0].sku --> gives: 738594937
* @endcode
* To find array elements, the path names are composed of elements separated by a dot. The Add and Find functions use the following
* syntax:
* @code
* integers.1 --> stores: 2
* nested_mixed_array.1.2 --> stores: "c"
* products.0.sku --> stores: 738594937
* @endcode
*/
class CArray : public CNodeCollection
{
protected:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CArray(const std::string& rssName);
public:
/**
* @brief Get the node type. Overload of sdv::toml::INodeInfo::GetType.
* @return Type of the node.
*/
virtual sdv::toml::ENodeType GetType() const override;
/**
* @brief Accesses a node by its key in the parse tree. Overload of CNode::GetDirect.
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
* E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and traversed
* by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'. These access
* conventions can also be chained like 'table.array[2][1].subtable.integerElement'.
* @attention Array indexing starts with 0!
* @param[in] rssPath Reference to the path of the node to searched for.
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
*/
virtual std::shared_ptr<CNode> GetDirect(const std::string& rssPath) const override;
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
/**
* @brief Adds a given node to a given path in the tree. Overload of CNode::Add.
* @param[in] rssPath Reference to the path in the tree where the new node is to be added.
* @param[in] rptrNode Reference top the smart pointer holding the node.
* @param[in] bDefinedExplicitly If a table that is created to create the path of the node to be added is
* defined explicitly
* @throw XInvalidAccessException Throws an XInvalidAccessException if a ancestor node is not open to add
* children
* @throw XDuplicateNameException Throws a XDuplicateNameException if a node with the same path as the node to
* be added is already defined explicitly
*/
virtual void Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly) override;
/**
* @brief Searches the subtree of the node for a node at the given location using the provided path. Overload of CNode::Find.
* @remarks The path elements of arrays and tables are separated by a dot.
* @param[in] rssPath Reference to the path of the node to find.
* @return Returns a shared pointer to the wanted Node if it is found or an error-Node if it is not
*/
virtual std::shared_ptr<CNode> Find(const std::string& rssPath) const override;
};
/**
* @brief Normal table
*/
class CNormalTable : public CTable
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CNormalTable(const std::string& rssName) : CTable(rssName) {}
};
/**
* @brief Inline table
*/
class CInlineTable : public CTable
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CInlineTable(const std::string& rssName) : CTable(rssName) {}
};
/**
* @brief Normal array
*/
class CNormalArray : public CArray
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CNormalArray(const std::string& rssName) : CArray(rssName) {}
};
/**
* @brief Array of tables
*/
class CTableArray : public CArray
{
public:
/**
* @brief Constructor
* @param[in] rssName Reference to the name of the node.
*/
CTableArray(const std::string& rssName) : CArray(rssName) {}
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
/**
* @brief Adds a given node to a given path in the tree. Overload of CNode::Add.
* @param[in] rssPath Reference to the path in the tree where the new node is to be added.
* @param[in] rptrNode Reference top the smart pointer holding the node.
* @param[in] bDefinedExplicitly If a table that is created to create the path of the node to be added is
* defined explicitly
* @throw XInvalidAccessException Throws an XInvalidAccessException if a ancestor node is not open to add
* children
* @throw XDuplicateNameException Throws a XDuplicateNameException if a node with the same path as the node to
* be added is already defined explicitly
*/
virtual void Add(const std::string& rssPath, const std::shared_ptr<CNode>& rptrNode, bool bDefinedExplicitly) override;
/**
* @brief Searches the subtree of the node for a node at the given location using the provided path. Overload of CNode::Find.
* @remarks The path elements of arrays and tables are separated by a dot.
* @param[in] rssPath Reference to the string containing the path of the node to find.
* @return Returns a shared pointer to the wanted Node if it is found or an error-Node if it is not
*/
virtual std::shared_ptr<CNode> Find(const std::string& rssPath) const override;
};
/**
* @brief Root table
*/
class CRootTable : public CNormalTable
{
public:
/**
* @brief Constructor
*/
CRootTable() : CNormalTable("root") {}
/**
* @brief Get the TOML text based on the content. Overload of CNode::CreateTOMLText.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @param[in] rssLastPrintedTable Reference to the string containing the last printed table. This might be necessary in case a
* different table was printed in between.
* @param[in] bFirst When set, this is the first entry in an array or table.
* @param[in] bEmbedded When set, this is an embedded definition in an array or table.
* @param[in] bAssignment When set, this is a table assignment.
* @param[in] bRoot Only for table entries, when set this is the root entry (suppress the table name).
* @return The string containing the TOML text.
*/
virtual std::string CreateTOMLText(const std::string& rssParent, std::string& rssLastPrintedTable, bool bFirst, bool bEmbedded,
bool bAssignment, bool bRoot) const override;
};
#endif // !defined PARSER_NODE_TOML_H

View File

@@ -0,0 +1,380 @@
#include "parser_toml.h"
#include <iostream>
#include "exception.h"
CParserTOML::CParserTOML(const std::string& rssString) : m_lexer(rssString)
{
Process(rssString);
}
void CParserTOML::Clear()
{
m_ptrRoot = std::make_shared<CRootTable>();
m_ssCurrentTable.clear();
m_lexer.Reset();
while (!m_stackEnvironment.empty()) m_stackEnvironment.pop();
}
bool CParserTOML::Process(/*in*/ const sdv::u8string& ssContent)
{
Clear();
m_lexer.Feed(ssContent);
try
{
// Run through all tokens of the lexer and process the tokens.
while (!m_lexer.IsEnd())
{
CLexerTOML::SToken current = m_lexer.Peek();
switch (current.eCategory)
{
case CLexerTOML::ETokenCategory::token_syntax_table_open:
ProcessTable();
break;
case CLexerTOML::ETokenCategory::token_syntax_table_array_open:
ProcessTableArray();
break;
case CLexerTOML::ETokenCategory::token_key:
ProcessValueKey();
break;
case CLexerTOML::ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
break;
case CLexerTOML::ETokenCategory::token_terminated:
case CLexerTOML::ETokenCategory::token_error:
throw XTOMLParseException(current.ssContentString);
break;
default:
throw XTOMLParseException("Invalid Syntax; not a Key, Table or Tablearray");
}
}
}
catch (const sdv::toml::XTOMLParseException& e)
{
std::cout << e.what() << '\n';
throw;
}
return true;
}
const CNodeCollection& CParserTOML::GetRoot() const
{
auto ptrCollection = m_ptrRoot->GetTable();
return *ptrCollection.get();
}
CNodeCollection& CParserTOML::GetRoot()
{
auto ptrCollection = m_ptrRoot->GetTable();
return *ptrCollection.get();
}
std::string CParserTOML::CreateTOMLText(const std::string& rssParent) const
{
std::string ssLastPrintedTable;
return m_ptrRoot->CreateTOMLText(rssParent, ssLastPrintedTable);
}
bool CParserTOML::Add(const std::string& rssPath, bool bVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CBooleanNode>(ssName, bVal));
return true;
}
bool CParserTOML::Add(const std::string& rssPath, int64_t iVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CIntegerNode>(ssName, iVal));
return true;
}
bool CParserTOML::Add(const std::string& rssPath, double dVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CFloatingPointNode>(ssName, dVal));
return true;
}
bool CParserTOML::Add(const std::string& rssPath, const std::string& rssVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CStringNode>(ssName, rssVal));
return true;
}
void CParserTOML::ProcessTable()
{
// Get the table path (table name preceded by parent tables separated with dots).
m_lexer.Consume();
std::string ssPath = ComposePath();
CLexerTOML::SToken sToken = m_lexer.Consume();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_table_close)
{
throw XTOMLParseException("invalid Table construct");
}
// Find the last dot - the name follows
size_t nOffset = FindLast(ssPath);
if (nOffset == std::string::npos)
nOffset = 0; // No dot found, the whole path is one table name
else
nOffset++; // Skip the dot
std::string ssName = ssPath.substr(nOffset);
// Add the table to the root
m_ptrRoot->Add(ssPath, std::make_shared<CNormalTable>(ssName), false);
m_ssCurrentTable = ssPath;
}
void CParserTOML::ProcessTableArray()
{
m_lexer.Consume();
std::string rssKeyPath = ComposePath();
auto ptrNode = m_ptrRoot->Find(rssKeyPath);
if (!ptrNode)
{
Add<CTableArray>(rssKeyPath);
ptrNode = m_ptrRoot->Find(rssKeyPath);
}
if (!ptrNode) return;
if (dynamic_cast<CTableArray*>(ptrNode.get()))
ptrNode->GetArray()->AddElement(std::make_shared<CNormalTable>(""));
else
throw XTOMLParseException(("'" + rssKeyPath + "' already defined as a non-table-array").c_str());
m_ssCurrentTable = rssKeyPath;
CLexerTOML::SToken sToken = m_lexer.Consume();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_table_array_close)
{
throw XTOMLParseException("invalid Table Array construct");
}
}
void CParserTOML::ProcessValueKey()
{
std::string rssKeyPath = (m_ssCurrentTable.empty() ? "" : (m_ssCurrentTable + ".")) + ComposePath();
CLexerTOML::SToken sToken = m_lexer.Consume();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_assignment)
{
throw XTOMLParseException("Assignment expected");
}
ProcessValue(rssKeyPath);
}
void CParserTOML::ProcessValue(const std::string& rssKeyPath)
{
CLexerTOML::SToken assignmentValue = m_lexer.Consume();
switch (assignmentValue.eCategory)
{
case CLexerTOML::ETokenCategory::token_boolean:
Add(rssKeyPath, assignmentValue.bContentBoolean);
break;
case CLexerTOML::ETokenCategory::token_integer:
Add(rssKeyPath, assignmentValue.iContentInteger);
break;
case CLexerTOML::ETokenCategory::token_float:
Add(rssKeyPath, assignmentValue.dContentFloatingpoint);
break;
case CLexerTOML::ETokenCategory::token_string:
Add(rssKeyPath, assignmentValue.ssContentString);
break;
case CLexerTOML::ETokenCategory::token_syntax_array_open:
Add<CNormalArray>(rssKeyPath);
m_stackEnvironment.push(EEnvironment::env_array);
ProcessArray(rssKeyPath);
m_stackEnvironment.pop();
break;
case CLexerTOML::ETokenCategory::token_syntax_inline_table_open:
Add<CInlineTable>(rssKeyPath);
m_stackEnvironment.push(EEnvironment::env_inline_table);
ProcessInlineTable(rssKeyPath);
m_stackEnvironment.pop();
break;
default:
throw XTOMLParseException("Missing value");
break;
}
CLexerTOML::SToken sToken = m_lexer.Peek();
if (!m_stackEnvironment.empty())
{
switch (m_stackEnvironment.top())
{
case EEnvironment::env_array:
{
int32_t index = 2;
while (sToken.eCategory == CLexerTOML::ETokenCategory::token_syntax_new_line)
{
sToken = m_lexer.Peek(index++);
}
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_comma
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_array_close)
{
throw XTOMLParseException(
"Invalid Token after value assignment in array; ',' or ']' needed");
}
}
break;
case EEnvironment::env_inline_table:
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_comma
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_inline_table_close)
{
throw XTOMLParseException(
"Invalid Token after value assignment in inline table; ',' or '}' needed ");
}
break;
default:
break;
}
}
else
{
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_new_line && sToken.eCategory != CLexerTOML::ETokenCategory::token_eof)
{
throw XTOMLParseException("Invalid Token after value assignment; newline needed");
}
}
}
void CParserTOML::ProcessArray(const std::string& rssKeyPath)
{
/*
Arrays are defined as follow: array_name = [value, value, ...]
Arrays can have new-lines between their values.
And can end with a comma.
For example:
integers = [ 1, 2, 3 ]
colors = [ "red", "yellow", "green", ]
nested_arrays_of_ints = [ [ 1, 2 ],
[3, 4, 5] ]
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
string_array = [ "all", 'strings', """are the same""", '''type''' ]
*/
CLexerTOML::SToken sToken = m_lexer.Peek();
size_t nIndex = 0;
enum class EExpect {value_comma_end, comma_end} eExpect = EExpect::value_comma_end;
while (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_array_close)
{
switch (sToken.eCategory)
{
//case CLexerTOML::ETokenCategory::token_syntax_array_open: // Embedded array
// if (eExpect == comma_end) throw XTOMLParseException("Expecting comma or table end.");
// m_lexer.Consume();
// ProcessArray(rssKeyPath + "." + std::to_string(nIndex++));
// eExpect = comma_end;
// break;
case CLexerTOML::ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
break;
case CLexerTOML::ETokenCategory::token_syntax_comma:
m_lexer.Consume();
eExpect = EExpect::value_comma_end;
break;
default:
if (eExpect == EExpect::comma_end)
throw XTOMLParseException("Expecting comma or table end.");
ProcessValue(rssKeyPath + "." + std::to_string(nIndex++));
eExpect = EExpect::comma_end;
break;
}
sToken = m_lexer.Peek();
}
m_lexer.Consume();
}
void CParserTOML::ProcessInlineTable(const std::string& rssKeyPath)
{
/*
Inline tables are defined as follow: table_name = {value, value, ...}
For example:
name = { first = "Tom", last = "Preston-Werner" }
point = { x = 1, y = 2 }
animal = { type.name = "pug" }
*/
CLexerTOML::SToken sToken = m_lexer.Peek();
std::string ssCurrentTableTemp = m_ssCurrentTable;
m_ssCurrentTable = rssKeyPath;
enum class EExpect { value_comma_end, value, comma_end } eExpect = EExpect::value_comma_end;
while (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_inline_table_close)
{
switch (sToken.eCategory)
{
case CLexerTOML::ETokenCategory::token_syntax_new_line:
throw XTOMLParseException("No newlines allowed in inline table");
break;
case CLexerTOML::ETokenCategory::token_syntax_comma:
if (eExpect == EExpect::value)
throw XTOMLParseException("Unexpected comma.");
m_lexer.Consume();
eExpect = EExpect::value;
break;
default:
if (eExpect == EExpect::comma_end)
throw XTOMLParseException("Expecting comma or table end.");
ProcessValueKey();
eExpect = EExpect::comma_end;
break;
}
sToken = m_lexer.Peek();
}
if (eExpect == EExpect::value)
throw XTOMLParseException("Expecting a value before inline table end.");
m_ptrRoot->Find(rssKeyPath)->GetTable()->m_bOpenToAddChildren = false;
m_lexer.Consume();
m_ssCurrentTable = ssCurrentTableTemp;
}
std::string CParserTOML::ComposePath()
{
std::string ssPath;
CLexerTOML::SToken sToken = m_lexer.Peek();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_dot
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_key)
throw XTOMLParseException("Invalid Token to assemble path from keys");
while (sToken.eCategory == CLexerTOML::ETokenCategory::token_syntax_dot
|| sToken.eCategory == CLexerTOML::ETokenCategory::token_key)
{
m_lexer.Consume();
if (sToken.eCategory == CLexerTOML::ETokenCategory::token_key)
ssPath += sToken.ssContentString;
else
ssPath += ".";
sToken = m_lexer.Peek();
}
return ssPath;
}

View File

@@ -0,0 +1,173 @@
#ifndef PARSER_TOML_H
#define PARSER_TOML_H
#include "lexer_toml.h"
#include "parser_node_toml.h"
/**
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data
*/
class CParserTOML : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
{
public:
/**
* @brief Default constructor
*/
CParserTOML() = default;
/**
* @brief Construct a new Parser object
* @param[in] rssString UTF-8 encoded data of a TOML source
*/
CParserTOML(const std::string& rssString);
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::toml::ITOMLParser)
SDV_INTERFACE_CHAIN_MEMBER(m_ptrRoot)
END_SDV_INTERFACE_MAP()
/**
* @brief Clears the current parse result.
* @attention This will render any pointer invalid!
*/
void Clear();
// Ignore cppcheck warning for not using dynamic binding when being called through the constructor.
// cppcheck-suppress virtualCallInConstructor
/**
* @brief Process the configuration from the supplied content string. Overload of sdv::toml::ITOMLParser.
* @param[in] ssContent Configuration string.
* @return Returns 'true' when the configuration could be read successfully, false when not.
*/
virtual bool Process(/*in*/ const sdv::u8string& ssContent) override;
/**
* @{
* @brief Return the root node.
* @return Reference to the root node collection.
*/
const CNodeCollection& GetRoot() const;
CNodeCollection& GetRoot();
/**
* @}
*/
/**
* @brief Get the TOML text based on the content.
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
* @return The string containing the TOML text.
*/
std::string CreateTOMLText(const std::string& rssParent = std::string()) const;
private:
/**
* @brief Add a collection node (table or array).
* @tparam TCollectionNode The collection node class to add (to create).
* @param[in] rssPath Reference to the node path.
* @return Returns whether the node could be added.
*/
template <class TCollectionNode>
bool Add(const std::string& rssPath);
/**
* @brief Add a boolean value node.
* @param[in] rssPath Reference to the node path.
* @param[in] bVal The boolean value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, bool bVal);
/**
* @brief Add a integer value node.
* @param[in] rssPath Reference to the node path.
* @param[in] iVal The integer value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, int64_t iVal);
/**
* @brief Add a floating point value node.
* @param[in] rssPath Reference to the node path.
* @param[in] dVal The floating point value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, double dVal);
/**
* @brief Add a string value node.
* @param[in] rssPath Reference to the node path.
* @param[in] rssVal Reference to the string value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, const std::string& rssVal);
/**
* @brief Process a table declaration.
*/
void ProcessTable();
/**
* @brief Process a table array declaration.
*/
void ProcessTableArray();
/**
* @brief Process the value key.
*/
void ProcessValueKey();
/**
* @brief Process the value with the supplied key.
* @param[in] rssKeyPath Reference to the key path string.
*/
void ProcessValue(const std::string& rssKeyPath);
/**
* @brief Process the array value with the supplied key.
* @param[in] rssKeyPath Reference to the key path string.
*/
void ProcessArray(const std::string& rssKeyPath);
/**
* @brief Process the inline table value with the supplied key.
* @param[in] rssKeyPath Reference to the key path string.
*/
void ProcessInlineTable(const std::string& rssKeyPath);
/**
* @brief Compose a path from lexer tokens. A path is composed of table and array elements separated with a dot.
* @return The composed path.
*/
std::string ComposePath();
/**
* @brief Enum for differentiating between an array environment and an inline table environment for syntax checks.
*/
enum class EEnvironment
{
env_array, //!< Environment for an array
env_inline_table //!< Environment for a table
};
std::stack<EEnvironment> m_stackEnvironment; ///< Tracking of environments in nested structures.
std::shared_ptr<CNode> m_ptrRoot = std::make_shared<CRootTable>(); ///< The one root node.
std::string m_ssCurrentTable; ///< Path to the current table.
CLexerTOML m_lexer; ///< Lexer.
};
template <class TCollectionNode>
inline bool CParserTOML::Add(const std::string& rssPath)
{
size_t nOffset = rssPath.rfind('.');
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<TCollectionNode>(ssName), true);
return true;
}
#endif // PARSER_TOML_H