Update sdv_packager (#6)

This commit is contained in:
tompzf
2026-03-27 14:12:49 +01:00
committed by GitHub
parent 234be8917f
commit aefefd52f7
717 changed files with 42252 additions and 11334 deletions

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#include "character_reader_utf_8.h"
#include "exception.h"

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef CHARACTER_READER_UTF_8_H
#define CHARACTER_READER_UTF_8_H

View File

@@ -0,0 +1,425 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#include "code_snippet.h"
#include "parser_node_toml.h"
/// The TOML parser namespace
namespace toml_parser
{
void CCodeSnippet::SetTokenList(const std::list<CToken>& rlstTokens)
{
m_lstTokens = rlstTokens;
m_bUseDefault = false;
}
void CCodeSnippet::SetTokenList(std::list<CToken>&& rlstTokens)
{
m_lstTokens = std::move(rlstTokens);
m_bUseDefault = false;
}
std::string CCodeSnippet::Compose(EComposeMode eMode, const CGenContext& rContext, size_t nAssignmentOffset /*= 0*/,
size_t nCommentOffset /*= 0*/) const
{
// Build the stream until the first comment.
std::stringstream sstream;
TTokenListIterator it = m_lstTokens.begin();
bool bCommaAvailable = false;
bool bPrintableCharsAvailable = false;
bool bLastTokenIsComment = false;
while (it != m_lstTokens.end() && it->Category() != ETokenCategory::token_comment)
{
// Add comma only when needed.
bool bSkip = false;
switch (it->Category())
{
case ETokenCategory::token_syntax_comma:
if (eMode != EComposeMode::compose_behind || !rContext.CommaNeeded() || bCommaAvailable)
{
bSkip = true;
break; // Only when placed behind a node
}
bCommaAvailable = true;
break;
case ETokenCategory::token_comment:
bSkip = !rContext.CommentAndNewlineAllowed();
if (!bSkip) bLastTokenIsComment = true;
break;
case ETokenCategory::token_syntax_new_line:
bSkip = !rContext.NewlineAllowed();
if (!bSkip) bLastTokenIsComment = false;
break;
default:
break;
}
if (!bSkip)
{
sstream << it->RawString();
bPrintableCharsAvailable = true;
}
++it;
}
// Determine the last comment.
TTokenListIterator itFirstComment = it;
TTokenListIterator itLastComment = it;
while (it != m_lstTokens.end())
{
if (it->Category() == ETokenCategory::token_comment)
itLastComment = it;
++it;
}
// Determine the code behind the last comment (and the obligatory newline).
TTokenListIterator itPostComment = itLastComment;
if (itPostComment != m_lstTokens.end())
++itPostComment;
if (itPostComment != m_lstTokens.end() && itPostComment->Category() == ETokenCategory::token_syntax_new_line)
++itPostComment;
// Stream the comment
if (!m_bCommentReplaced)
{
for (it = itFirstComment; it != itPostComment; ++it)
{
// Add comma only when needed.
bool bSkip = false;
switch (it->Category())
{
case ETokenCategory::token_syntax_comma:
if (eMode != EComposeMode::compose_behind || !rContext.CommaNeeded() || bCommaAvailable)
{
bSkip = true;
break; // Only when placed behind a node
}
bCommaAvailable = true;
break;
case ETokenCategory::token_comment:
bSkip = !rContext.CommentAndNewlineAllowed();
if (!bSkip) bLastTokenIsComment = true;
break;
case ETokenCategory::token_syntax_new_line:
bSkip = !rContext.NewlineAllowed();
if (!bSkip) bLastTokenIsComment = false;
break;
default:
break;
}
if (!bSkip)
{
sstream << it->RawString();
bPrintableCharsAvailable = true;
}
}
}
else
{
switch (eMode)
{
case EComposeMode::compose_inline:
case EComposeMode::compose_behind:
if (itFirstComment == m_lstTokens.end() && !m_ssComment.empty())
sstream << " ";
break;
case EComposeMode::compose_standalone_behind:
if (itFirstComment == m_lstTokens.end() && !m_ssComment.empty())
sstream << std::endl;
break;
default:
break;
}
// TODO: Align the comment....
size_t nPos = 0;
bool bEmptyLine = false;
while (nPos != std::string::npos)
{
// Find the chunk until the next newline or the end of the string
size_t nNextPos = m_ssComment.find('\n', nPos);
if (nNextPos != std::string::npos) nNextPos++;
if (nNextPos >= m_ssComment.size()) nNextPos = std::string::npos;
std::string ssChunk = m_ssComment.substr(nPos, nNextPos);
nPos = nNextPos;
// Insert an extra line break, but only when there isn't a line-break already...
if (!bEmptyLine && !sstream.str().empty())
{
switch (eMode)
{
case EComposeMode::compose_before:
case EComposeMode::compose_behind:
case EComposeMode::compose_inline:
sstream << std::string(nCommentOffset, ' ') << "#";
break;
default:
break;
}
sstream << std::endl;
}
if (ssChunk.find_first_not_of("\r\n") == std::string::npos)
bEmptyLine = true;
else
{
sstream << std::string(nCommentOffset, ' ') << "# " << ssChunk;
if (ssChunk.find('\n') == std::string::npos)
sstream << std::endl; // Comment is always followed by a newline.
bEmptyLine = false;
}
}
// TODO: Add spaces for next assignment
switch (eMode)
{
case EComposeMode::compose_inline:
if (itPostComment == m_lstTokens.end() && nAssignmentOffset)
sstream << std::string(nAssignmentOffset, ' ');
break;
case EComposeMode::compose_standalone_before:
if (itFirstComment == m_lstTokens.end() && !m_ssComment.empty())
sstream << std::endl;
break;
default:
break;
}
}
// Stream the rest of the tokens
for (it = itPostComment; it != m_lstTokens.end(); ++it)
{
bool bSkip = false;
switch (it->Category())
{
case ETokenCategory::token_syntax_comma:
if (eMode != EComposeMode::compose_behind || !rContext.CommaNeeded() || bCommaAvailable)
{
bSkip = true;
break; // Only when placed behind a node
}
bCommaAvailable = true;
break;
case ETokenCategory::token_comment:
bSkip = !rContext.CommentAndNewlineAllowed();
if (!bSkip) bLastTokenIsComment = true;
break;
case ETokenCategory::token_syntax_new_line:
bSkip = !rContext.NewlineAllowed();
if (!bSkip) bLastTokenIsComment = false;
break;
default:
break;
}
if (!bSkip)
{
sstream << it->RawString();
bPrintableCharsAvailable = true;
}
}
// Post processing
switch (eMode)
{
case EComposeMode::compose_behind:
// Was a comma provided?
if (!bCommaAvailable && rContext.CommaNeeded())
{
std::string ssTemp = sstream.str();
ssTemp.insert(0, bPrintableCharsAvailable ? "," : ", ");
sstream.str(ssTemp);
}
// Default newline needed
if (rContext.FinalNewline() && ((m_lstTokens.empty() && m_ssComment.empty()) || bLastTokenIsComment))
sstream << std::endl;
break;
case EComposeMode::compose_before:
//if (!bPrintableCharsAvailable && rContext.Embedded())
// sstream << " ";
break;
default:
break;
}
return sstream.str();
}
std::string CCodeSnippet::GetComment() const
{
if (m_bCommentReplaced)
return m_ssComment;
std::stringstream sstream;
size_t nNewLineCount = 0;
for (const CToken& rToken : m_lstTokens)
{
// Deal with new line.
if (rToken.Category() == ETokenCategory::token_syntax_new_line)
{
if (sstream.str().empty())
continue;
nNewLineCount++;
if (nNewLineCount < 2)
continue;
sstream << std::endl;
continue;
}
if (rToken.Category() == ETokenCategory::token_whitespace)
continue;
nNewLineCount = 0;
if (rToken.Category() == toml_parser::ETokenCategory::token_comment && !rToken.RawString().empty()
&& rToken.RawString()[0] == '#')
{
// The comment doesn't have a newline inside the comment text. This allows us to glue multiple comments to one
// large string.
// A new line will be inserted on the following conditions:
// - the line starts as comment, but only has whitespace.
// - there are at least some characters in the stream already.
// - the comment starts with a tab or multiple spaces
if (rToken.RawString().size() <= 1 || rToken.RawString().find_first_not_of(" \t", 1) == std::string::npos)
{
// Line has only whitespace
sstream << std::endl;
continue;
}
if (rToken.RawString()[1] == '\t' || rToken.RawString().substr(1, 2) == " "
|| rToken.RawString().substr(1, 2) == " \t")
{
// Line starts with a tab, are there any characters in the stream already.
if (!sstream.str().empty())
sstream << std::endl;
sstream << rToken.RawString().substr(1);
continue;
}
size_t nStartComment = rToken.RawString()[1] == ' ' ? 2 : 1;
if (!sstream.str().empty())
{
// The stream is not empty. Are there extra spaces (indicating a list or so)
if (rToken.RawString().substr(1, 2) == " ")
sstream << std::endl;
else // No newline, but space?
if (!std::isspace(sstream.str().back()))
sstream << " ";
}
// Add the content to the stream
sstream << rToken.RawString().substr(nStartComment);
}
}
// Trim space at the end of the string
std::string ssResult = sstream.str();
while (!ssResult.empty() && std::isspace(ssResult.back()))
ssResult.pop_back();
return ssResult;
}
void CCodeSnippet::SetComment(const std::string& rssComment)
{
// Just store the string. Further processing is done in the compose function.
m_ssComment = rssComment;
m_bCommentReplaced = true;
m_bUseDefault = false;
}
void CCodeSnippet::Clear()
{
m_ssComment.clear();
m_lstTokens.clear();
m_bCommentReplaced = false;
m_bUseDefault = true;
}
bool CCodeSnippet::HasCode() const
{
return !m_bUseDefault || (m_bCommentReplaced || !m_lstTokens.empty());
}
void CCodeSnippet::RemoveFormat()
{
// Read the comment from the token list.
if (!m_bCommentReplaced && !m_lstTokens.empty())
{
m_ssComment = GetComment();
m_bCommentReplaced = !m_ssComment.empty();
}
// Clear the token list
m_lstTokens.clear();
m_bUseDefault = !m_bCommentReplaced;
}
bool CCodeSnippet::HasComma() const
{
return !m_bCommentReplaced && (std::find_if(m_lstTokens.begin(), m_lstTokens.end(),
[](const CToken& rToken) { return rToken.Category() == ETokenCategory::token_syntax_comma; }) != m_lstTokens.end());
}
void CCodeSnippet::Insert(const CCodeSnippet& rCode)
{
// Something to do?
if (!rCode.HasCode()) return;
// Simple assignment?
if (!HasCode())
{
m_bCommentReplaced = rCode.m_bCommentReplaced;
m_ssComment = rCode.m_ssComment;
m_lstTokens = rCode.m_lstTokens;
return;
}
// Differentiate between comment text or token lists.
if (m_bCommentReplaced)
m_ssComment.insert(0, rCode.GetComment());
else
{
if (rCode.m_bCommentReplaced)
{
m_ssComment = rCode.GetComment() + GetComment();
m_bCommentReplaced = true;
}
else
m_lstTokens.insert(m_lstTokens.begin(), rCode.m_lstTokens.begin(), rCode.m_lstTokens.end());
}
}
void CCodeSnippet::Append(const CCodeSnippet& rCode)
{
// Something to do?
if (!rCode.HasCode()) return;
// Simple assignment?
if (!HasCode())
{
m_bCommentReplaced = rCode.m_bCommentReplaced;
m_ssComment = rCode.m_ssComment;
m_lstTokens = rCode.m_lstTokens;
return;
}
// Differentiate between comment text or token lists.
if (m_bCommentReplaced)
m_ssComment.append(rCode.GetComment());
else
{
if (rCode.m_bCommentReplaced)
{
m_ssComment = rCode.GetComment() + GetComment();
m_bCommentReplaced = true;
}
else
m_lstTokens.insert(m_lstTokens.end(), rCode.m_lstTokens.begin(), rCode.m_lstTokens.end());
}
}
} // namespace toml_parser

View File

@@ -0,0 +1,220 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Erik Verhoeven - initial API and implementation
********************************************************************************/
#ifndef CODE_SNIPPET_H
#define CODE_SNIPPET_H
#include <list>
#include <string>
#include <memory>
#include <interfaces/toml.h>
#include "lexer_toml.h"
/// The TOML parser namespace
namespace toml_parser
{
// Forward declaration
class CGenContext;
/**
* @brief Comment or code snippet structure.
* @details Each node has multiple code snippets used to reproduce the exact code. For example with an assignment:
* @code
*
* # This is out of scope comment before
*
* # This is comment before
* var_a = "abc" # comment behind
* # more comment behind
*
* # This is out of scope comment behind
*
* @endcode
*
* The code snippets are identified as follows:
* @code
* <out of scope comment before>
* <comment before>
* <space before><key><space>=<space><value><comment behind>
* <out of scope comment behind>
* @endcode
*
* Each code snippet has token a list and comment string.
* - If the tokenlist is not empty and comment string is empty, the tokenlist determines the comment.
* - If the tokenlist is empty, the comment string determines the comment. The following steps are taken during the
* GetComment with the raw flag enabled:
* - For the comment behind, the current position for the comment is determined by examining the length of the sibling
* nodes. If the position is larger than 80, the start position is either 80 or one space character more than the
* node length.
* - For all other comment types, the start position is zero.
* - Stream spaces until the start position is reached; then stream the comment character '#' followed by a space.
* - The comment text is streamed word by word. If between the words a space exists, this can be replaced by a
* newline when the current position with the new word would exceed column 132. After the new line spaces fill up
* until the start position. Then the comment character and a space is streamed followed by more words from the
* comment.
* - If within the comment a newline is available, this is followed by an additional newline within the raw comment
* stream. Also if multiple empty lines occur (lines with no character sor only whitespace), this is also
* streamed with one additional newline at the beginning.
* - The last word is followed by a newline.
* - If tokenlist is not empty and comment string is not empty, the comments plus the newline following within the
* tokenlist are replaced by the new comment text like described above, with the exception that the start position is
* determined by the current position within the tokenlist.
*/
class CCodeSnippet
{
public:
/**
* @brief Set the token list containing the code for this code snippet.
* @param[in] rlstTokens Reference to the token list to be set.
*/
void SetTokenList(const std::list<CToken>& rlstTokens);
/**
* @brief Set the token list containing the code for this code snippet.
* @param[in] rlstTokens Reference to the token list to be set.
*/
void SetTokenList(std::list<CToken>&& rlstTokens);
/**
* @brief Mode the code snippet composer should run in.
*/
enum class EComposeMode
{
compose_inline, ///< Compose as inline whitespace and comment. If there is no token list and no comment
///< string, compose as one space. If there is only a comment string, insert a space, add
///< the comment followed by an obligatory newline, and insert spaces until the next
///< provided position. If there are tokens with a comment token, replace the comment. If
///< there are tokens without comment, add the comment, newline and spaces.
compose_before, ///< Compose as comment assigned to and located before the node. If there is no token list
///< and no comment string, doesn't add anything. If there is only a comment string, adds
///< the comment followed by the obligatory newline. If there are tokens with a comment
///< token, replace the comment. If there are tokens without the comment, place the comment
///< before the last newline or when not available, at the end of the tokens followed by a
///< new newline.
compose_behind, ///< Compose as comment assigned to and located behind the node. If there is no token list
///< and no comment string, add a newline. If there is a comment string and no tokens,
///< add a space, the comment string followed by the obligatory newline. If there is a token
///< list without comment, add a comment before the newline or at the end with an additional
///< newline.
compose_standalone_before, ///< Compose as stand-alone comment before the node. Replace any token list if a comment
///< string is available.
compose_standalone_behind, ///< Compose as stand-alone comment behind the node. Replace any token list if a comment
///< string is available.
};
/**
* @brief Compose a conde string from the stored tokens and/or string.
* @details The comment text to code translation is as follows:
* @code
* The following text might be split into multiple TOML comment lines.
* @endcode
* @code
* # The following text might be split into
* # multiple TOML comment lines.
* @endcode
* and
* @code
* This is part 1 of a text which will be as two separated blocks.
* This is part 2 of a text which will be as two separated blocks.
* @endcode
* @code
* # This is part 1 of a text which will be as
* # two separated blocks.
* #
* # This is part 2 of a text which will be as
* # two separated blocks.
* @endcode
* and
* @code
* This is a list or an indented text, which is streated as a list.
* First indented text which belongs as a separated line.
* Second indented text which belongs as a separate line.
* @endcode
* @code
* # This is a list or an indented text, which is
* # streated as a list.
* # First indented text which belongs as a
* # separated line.
* # Second indented text which belongs as a
* # separate line.
* @endcode
* @param[in] eMode The mode the composer should run in.
* @param[in] rContext Reference to the node context used for the code generation.
* @param[in] nAssignmentOffset The offset for a next assignent; only used for inline composition.
* @param[in] nCommentOffset The offset to insert a multi-line comment; only used for inline and behind composition.
* @return The composed code string.
*/
std::string Compose(EComposeMode eMode, const CGenContext& rContext, size_t nAssignmentOffset = 0, size_t nCommentOffset = 0) const;
/**
* @brief Get the stored comment from the code snippet. A stored comment string supersedes a comment string from the
* token list. Comment lines that belong together are combined together as a text without line-breaks.
* @return Returns the stored comment string.
*/
std::string GetComment() const;
/**
* @brief Set a comment string. The comment will be formatted in the compose function. Lines that should stay together
* should not be separated by a line-break. Comments should be provided without the TOML comment character '#'.
* @param[in] rssComment Reference to the string containing the comment text.
*/
void SetComment(const std::string& rssComment);
/**
* @brief Remove the content of the code snippet.
*/
void Clear();
/**
* @brief Does the snippet contain code?
* @return Returns whether the snippet has code or not.
*/
bool HasCode() const;
/**
* @brief This function will remove the formatting from the code.
*/
void RemoveFormat();
/**
* @brief Does the code snippet have a comma in the token list?
* @return Returns whether the token list is active and contains a comma token.
*/
bool HasComma() const;
/**
* @brief Insert a code snippet before the existing comment.
* @remarks In case the code has been replaced by a comment text of the current or the to be inserted code snippet, the
* result will be a combined comment text, disregarding the format.
* @param[in] rCode Reference to the code snippet to insert before the current code.
*/
void Insert(const CCodeSnippet& rCode);
/**
* @brief Append a code snippet behind the existing comment.
* @remarks In case the code has been replaced by a comment text of the current or the to be inserted code snippet, the
* result will be a combined comment text, disregarding the format.
* @param[in] rCode Reference to the code snippet to appended behind the current code.
*/
void Append(const CCodeSnippet& rCode);
private:
std::list<CToken> m_lstTokens; ///< Token list for the code snippet in raw format.
std::string m_ssComment; ///< The comment text for the code snippet in text format.
bool m_bCommentReplaced = false; ///< Comment replaced with SetComment function.
bool m_bUseDefault = false; ///< When set, the format was removed from the code.
};
} // namespace toml_parser
#endif // !defined CODE_SNIPPET_H

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef CONFIG_EXCEPTION_H
#define CONFIG_EXCEPTION_H

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#include "lexer_toml.h"
#include <interfaces/toml.h>
#include "exception.h"
@@ -176,7 +190,15 @@ namespace toml_parser
}
}
void CLexer::Reset()
void CLexer::Clear()
{
m_lstTokens.clear();
m_itCursor = m_lstTokens.end();
while (!m_stackExpectations.empty())
m_stackExpectations.pop();
}
void CLexer::ResetCursor()
{
m_itCursor = m_lstTokens.begin();
}
@@ -275,6 +297,54 @@ namespace toml_parser
void CLexer::SmartExtendNodeRange(CNodeTokenRange& rTokenRange) const
{
/*
Consider the following TOML code:
# Out of scope comment
# Pre comment with spaces before
# Pre comment
[table] # Post comment
# Post comment without indentation
# Out of scope comment
# Pre comment
var = "1234" # Post comment
# More post comment with indentation
# Pre comment of var2 due to indentation identical or smaller then var2 def indentation
var2 = "5678"
# Pre comment of var3
var3 = 9012 # Post comment of var3
# More post comment of var3 (due ot space at the beginning)
# Pre comment of var4 due to lesser indentation
# Pre comment of var4 due to previous pre comment
# Pre comment of var4
var4 = 3456
# Out of scope comment due to missing post comment at same line as defintion
var5 = [ 8765, # Post comment for var5[0] (event behind the comma)
# Pre comment for var[1]
4321 ] # Post comment for var5
var6 = 7890 # Post comment of var6
# Out of scope comment due to missing indentation
--------------------------------------------
The function uses as input the token range of the definition and extends the definition with comments and whitespace that
belong to the definition. Therefore the following rules count:
- Pre comments are defined on the lines before if they are covering the complete line and do not have an indentation larger
than the indentation of the definition.
- Post comments are defined at the same line following the definition.
- If a comment with indentation is supplied before the definition, it is a pre-comment if there is no post-comment defined
that connects to this comment. Otherwise the comment belongs to the definition before.
- If comments follow on the lines directly behind the definition, they are post-comments when the comment has started at
the same line as the definition and each comment has an indentation larger than the indentation of the next definition.
- The extended range always includes the complete line including the indentation before and the line-break behind.
*/
// Define an iterator range type.
using TRange = std::pair<TTokenListIterator, TTokenListIterator>;
@@ -285,7 +355,7 @@ namespace toml_parser
// return A pair containing the begin and one past the end of the line.
auto fnGetLine = [this](const TTokenListIterator& rit) -> TRange
{
// When the end of the list is reached, there is no more line.
// When the end of the list is reached, there are no more tokens in the line.
if (rit == m_lstTokens.end())
return std::make_pair(m_lstTokens.end(), m_lstTokens.end());
@@ -392,6 +462,26 @@ namespace toml_parser
return bComments;
};
// Check a range for a comment at the end (as last token or just before the line break).
// remarks No validity check is done for the supplied token iterator.
// param[in] rprRange Pair with the start position and one past the end position of a range.
// return Returns whether the line contains a post comment.
auto fnPostComment = [this](const TRange& rprRange) -> bool
{
auto itToken = rprRange.second;
if (itToken == rprRange.first)
return false;
--itToken;
if (itToken->Category() == ETokenCategory::token_comment)
return true;
if (itToken->Category() != ETokenCategory::token_syntax_new_line)
return false;
if (itToken == rprRange.first)
return false;
--itToken;
return itToken->Category() == ETokenCategory::token_comment;
};
// Check a range for empty line(s) only (ignore whitespace and newlines).
// remarks No validity check is done for the supplied token iterator.
// param[in] rprRange Pair with the start position and one past the end position of a range.
@@ -530,75 +620,127 @@ namespace toml_parser
// The beginning and the end cannot be the same, except for an empty range.
if (itTokenBegin == itTokenEnd) return;
// Determine line boundaries of the current range
// Determine whether there is a post comment at all
auto itTokenBeginLine = itTokenBegin == m_lstTokens.begin() ? itTokenBegin : fnGetLine(itTokenBegin).first;
auto itTokenEndLine = itTokenEnd;
--itTokenEndLine;
itTokenEndLine = fnGetLine(itTokenEndLine).second;
// Are the comments following the statement?
bool bCommentsSameLine = fnCommentsOnly(std::make_pair(itTokenEnd, itTokenEndLine));
bool bPostCommentPresent = fnCommentsOnly(std::make_pair(itTokenEnd, itTokenEndLine));
// Is there only whitespace before or after the statement
bool bWhitespaceBefore = fnEmptyLine(std::make_pair(itTokenBeginLine, itTokenBegin)) && !bIsParent;
bool bWhitespaceAfter = fnEmptyLine(std::make_pair(itTokenEnd, itTokenEndLine));
bool bWhitespaceBefore = fnEmptyLine(TRange{itTokenBeginLine, itTokenBegin}) && !bIsParent;
bool bWhitespaceAfter = fnEmptyLine(TRange{itTokenEnd, itTokenEndLine});
// Get the indentation length of the line (whether there is other comments before or not).
size_t nIndentLen = fnGetIndentation(std::make_pair(itTokenBeginLine, itTokenEnd));
// Post comments or whitespace until the end of the line definitely belongs to the extended range.
if (bPostCommentPresent || bWhitespaceAfter) itTokenEnd = itTokenEndLine;
// Extend the range to include the beginning of the line and the end of the line.
TRange prExtended = std::make_pair(bWhitespaceBefore ? itTokenBeginLine : itTokenBegin,
(bCommentsSameLine || bWhitespaceAfter) ? itTokenEndLine : itTokenEnd);
// Get the indentation length of the definition.
size_t nDefIndent = fnGetIndentation(std::make_pair(itTokenBeginLine, itTokenEnd));
// Deal with whitespace and optionally comments before
TRange prLine = prExtended;
bool bEmptyLineBefore = false;
if (bWhitespaceBefore)
// Classify the lines
struct SCommentLine
{
// Check for comments preceeding the range. If previous lines are having comments only (and optionally whitespace), and
// the indentation is identical or less than the indentation of the range, the comment belongs to the range.
while (fnGetPrevLine(prLine) && fnCommentsOnly(prLine) && fnGetIndentation(prLine) <= nIndentLen)
prExtended.first = prLine.first;
TRange tLine;
size_t nIndent;
};
std::list<SCommentLine> lstLinesBefore, lstLinesBehind;
// Check whether there is an empty line before the range or the range starts at the first token in the list.
bEmptyLineBefore = prLine.first != prExtended.first ? fnEmptyLine(prLine) : prLine.first == m_lstTokens.begin();
}
// Deal with whitespace and optionally comments following
if (bWhitespaceAfter)
// Classify the lines before if the definition doesn't have other definition parts before at the same line.
TRange tLine = {itTokenBeginLine, itTokenEndLine};
bool bIncludeAllBefore = false;
while (bWhitespaceBefore && tLine.first != m_lstTokens.begin())
{
// Check for comments following the range. But only when there are comments at the same line and the indentation of the
// next line is larger than the range indentation or there is an empty line following.
TRange prPotential = prExtended;
bool bUsePotential = false;
prLine = prExtended;
while (bCommentsSameLine && fnGetNextLine(prLine) && fnCommentsOnly(prLine))
if (!fnGetPrevLine(tLine))
{
if (bUsePotential || fnGetIndentation(prLine) > nIndentLen)
{
bUsePotential = true;
prPotential.second = prLine.second;
}
else
prExtended.second = prLine.second;
bIncludeAllBefore = true;
break;
}
// Check whether there is an empty line following the range or the range ends at the end of the list.
bool bEmptyLineBeyond = prLine.second == m_lstTokens.end() ? true : fnEmptyLine(prLine);
// If an empty line is following and a potential extension was detected, extend the range
if (bEmptyLineBeyond && bUsePotential)
prExtended.second = prPotential.second;
// If there is an empty line before, include any empty lines until the next token or the end of the list.
while (bEmptyLineBefore && bEmptyLineBeyond && fnGetNextLine(prLine) && fnEmptyLine(prLine))
prExtended.second = prLine.second;
if (fnEmptyLine(tLine))
{
bIncludeAllBefore = true;
break;
}
if (!fnCommentsOnly(tLine))
{
// Check for a post comment
bIncludeAllBefore = !fnPostComment(tLine);
break;
}
lstLinesBefore.emplace_front(SCommentLine{tLine, fnGetIndentation(tLine)});
}
rTokenRange.ExtendedNode(CTokenRange(fnToken(prExtended.first), fnToken(prExtended.second)));
// Classify the lines behind (when there is a post comment or only whitespace following).
size_t nNextStatementIndent = 0;
tLine = {itTokenBeginLine, itTokenEndLine};
bool bIncludeAllBehind = false;
while ((bPostCommentPresent || bWhitespaceAfter) && tLine.second != m_lstTokens.end())
{
if (!fnGetNextLine(tLine))
{
bIncludeAllBehind = true;
break;
}
if (fnEmptyLine(tLine))
{
// Note: one empty line still belongs to the extended range.
lstLinesBehind.emplace_back(SCommentLine{tLine, 0});
bIncludeAllBehind = true;
break;
}
size_t nIndent = fnGetIndentation(tLine);
if (!fnCommentsOnly(tLine))
{
nNextStatementIndent = nIndent;
break;
}
lstLinesBehind.emplace_back(SCommentLine{tLine, fnGetIndentation(tLine)});
}
TRange tExtended{itTokenBegin, itTokenEnd};
if (!lstLinesBefore.empty())
{
// Include all comments before, or only the comments with an indentation identical or smaller than the def indentation.
if (bIncludeAllBefore)
tExtended.first = lstLinesBefore.front().tLine.first;
else
{
auto it = lstLinesBefore.crbegin();
while (it != lstLinesBefore.crend())
{
// NOTE: Use of SCommentLine structure here, prevents unusedStruct warning of cppcheck
const SCommentLine& rsLine = *it;
if (rsLine.nIndent > nDefIndent)
break;
tExtended.first = rsLine.tLine.first;
++it;
}
}
}
if (!lstLinesBehind.empty())
{
// Include all comments behind, or only the comments with an indentation larger than the indentation of the next def.
// The latter only when there is a post-comment.
if (bIncludeAllBehind)
tExtended.second = lstLinesBehind.back().tLine.second;
else if (bPostCommentPresent)
{
auto it = lstLinesBehind.cbegin();
while (it != lstLinesBehind.cend())
{
// NOTE: Use of SCommentLine structure here, prevents unusedStruct warning of cppcheck
const SCommentLine& rsLine = *it;
if (rsLine.nIndent <= nNextStatementIndent)
break;
tExtended.second = rsLine.tLine.second;
++it;
}
}
}
rTokenRange.ExtendedNode(CTokenRange(fnToken(tExtended.first), fnToken(tExtended.second)));
// Determine if there are any more nodes before the end of the list.
auto itFinal = prExtended.second;
auto itFinal = tExtended.second;
bool bNextNodeFound = false;
while (itFinal != m_lstTokens.end() && !bNextNodeFound)
{
@@ -1161,7 +1303,7 @@ namespace toml_parser
{
token = CToken(ETokenCategory::token_syntax_array_close);
m_stackExpectations.pop();
if (m_stackExpectations.top() == EExpectation::expect_value_once)
if (!m_stackExpectations.empty() && m_stackExpectations.top() == EExpectation::expect_value_once)
m_stackExpectations.pop();
}
else
@@ -1184,7 +1326,7 @@ namespace toml_parser
rReader.Consume();
token = CToken(ETokenCategory::token_syntax_inline_table_close);
m_stackExpectations.pop();
if (m_stackExpectations.top() == EExpectation::expect_value_once)
if (!m_stackExpectations.empty() && m_stackExpectations.top() == EExpectation::expect_value_once)
m_stackExpectations.pop();
break;
case ',':

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef LEXER_TOML_H
#define LEXER_TOML_H
@@ -11,6 +25,32 @@
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Safe stack implementation for an enum returning a default value when the stack is empty.
*/
template <typename TEnum, TEnum TDefault>
class enum_stack : public std::stack<TEnum>
{
public:
/**
* @brief Pop the top-most entry from the stack if the stack is not empty.
*/
void pop()
{
if (!std::stack<TEnum>::empty())
std::stack<TEnum>::pop();
}
/**
* @brief Return the value of the top-most entry of the stack or if empty a default value.
* @return The top-most or default value.
*/
TEnum top() const
{
return std::stack<TEnum>::empty() ? TDefault : std::stack<TEnum>::top();
}
};
/**
* @brief Node token range used to regenerate the source from the node entries.
* @details A node can have several token ranges identifying code that belongs to the node or precedes or succeeds the node. The
@@ -166,10 +206,15 @@ namespace toml_parser
*/
void Feed(const std::string& rssString, bool bValueOnly = false);
/**
* @brief Clear the lexer content.
*/
void Clear();
/**
* @brief Reset the lexer cursor position.
*/
void Reset();
void ResetCursor();
/**
* @brief Navigation modes supported by the lexer.
@@ -362,11 +407,13 @@ namespace toml_parser
*/
enum class EExpectation
{
undefined, ///< Error case when code is supplied that was not expected.
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
enum_stack<EExpectation, EExpectation::undefined>
m_stackExpectations; ///< Tracking of key or value expectations in nested structures
const std::vector<std::string>
m_vecKeyDelimiters{"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#include "lexer_toml_token.h"
#include <sstream>

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef LEXER_TOML_TOKEN_H
#define LEXER_TOML_TOKEN_H
@@ -310,7 +324,7 @@ namespace toml_parser
union
{
std::string m_ssContentString; ///< Token string content (used with keys, strings and errors).
std::string m_ssContentString; ///< Token string contentv (used with keys, strings and errors).
int64_t m_iContentInteger; ///< Token integer content
double m_dContentFloatingpoint; ///< Token floatingpoint content
bool m_bContentBoolean; ///< Token boolean content

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#include "miscellaneous.h"
#include "exception.h"
#include <sstream>

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef MISCELLANEOUS_H
#define MISCELLANEOUS_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef PARSER_NODE_TOML_H
#define PARSER_NODE_TOML_H
@@ -9,10 +23,9 @@
#include <list>
#include <map>
#include <interfaces/toml.h>
#include <support/interface_ptr.h>
#include "lexer_toml.h"
#include "miscellaneous.h"
#include "code_snippet.h"
/// The TOML parser namespace
namespace toml_parser
@@ -30,9 +43,11 @@ namespace toml_parser
*/
enum class EGenerateOptions : uint32_t
{
inline_when_possible = 0x01, ///< Try to generate as much as possible as inline nodes.
explicit_when_possible = 0x02, ///< Try to generate as much as possible as explicit nodes.
no_comments = 0x10, ///< Do not include comments
inline_when_possible = 0x01, ///< Try to generate as much as possible as inline nodes.
explicit_when_possible = 0x02, ///< Try to generate as much as possible as explicit nodes.
no_comments = 0x10, ///< Do not include comments.
reduce_whitespace = 0x20, ///< Add comments, but reduce extra newlines before and after the node.
full_header = 0x40, ///< When generating tables or table arrays, include the header in generated code.
};
/**
@@ -50,7 +65,8 @@ namespace toml_parser
CGenContext(const std::string& rssPrefixKey = std::string(), uint32_t uiOptions = 0);
/**
* @brief Called by the node that is generating the TOML. If not initialized before, initializes with the provided node.
* @brief Called by the node that is generating the TOML. If not initialized before, extract the context from the node and
* assign this node as top node for the code generation.
* @param[in] rptrNode Reference to the node that could be used for initialization as top most node.
*/
void InitTopMostNode(const std::shared_ptr<const CNode>& rptrNode);
@@ -65,9 +81,12 @@ namespace toml_parser
/**
* @brief Create a copy of the context class with a new key context.
* @param[in] rssNewKeyContext Reference to the string containing the new key context.
* @param[in] rptrNode Reference to the node pointer to extract the context from.
* @param[in] bLastNode When set, this is the last node in the current view.
* @return The copy of the contetx class.
*/
CGenContext CopyWithContext(const std::string& rssNewKeyContext) const;
CGenContext CopyWithContext(const std::string& rssNewKeyContext, const std::shared_ptr<CNode>& rptrNode,
bool bLastNode) const;
/**
* @brief Get the stored prefix key that should be used for the TOML code generation.
@@ -81,6 +100,24 @@ namespace toml_parser
*/
const std::string& KeyContext() const;
/**
* @brief The key path composed of the prefix and the relative key path.
* @return Reference to the key path string.
*/
const std::string& KeyPath() const;
/**
* @brief The key path composed of the key kontext and the relative key path.
* @return Reference to the key path string.
*/
const std::string& FullKeyPath() const;
/**
* @brief The relative key path, relative to the current context.
* @return Reference to the key path string.
*/
const std::string& RelKeyPath() const;
/**
* @brief Is this the top most node?
* @return Returns when the node is the top most node.
@@ -100,17 +137,115 @@ namespace toml_parser
*/
bool CheckOption(EGenerateOptions eOption) const;
/**
* @brief Is the last-node-flag set?
* @return Returns whether the last-node-flag has been set indicating the node using this context to be the last node within
* the current view.
*/
bool LastNode() const;
/**
* @brief Node presentation form.
*/
enum class EPresentation
{
standard, ///< Standard presentation (root or within table/table-array)
standard_inline, ///< Inline presentation (root or within table/table-array)
embedded, ///< Embedded presentation (within array or inline-table)
};
/**
* @brief Get the presentation form of the node.
* @return The node presentation extracted from the node and the generation context.
*/
EPresentation Presentation() const;
/**
* @brief Is the node a standard node?
* @return Returns 'true' when the node is a standard node.
*/
bool Standard() const;
/**
* @brief Is the node an inline node?
* @remarks Embedded nodes are also inline.
* @return Returns 'true' when the node is an inline node.
*/
bool Inline() const;
/**
* @brief Is the node an embedded node (within an inline table or array)?
* @return Returns 'true' when the node is an embedded node.
*/
bool Embedded() const;
/**
* @brief Does the node need an assignment (key and when inline, equal sign)?
* @remarks Embedded nodes within an array do not need an assignment.
* @return Returns 'true' when the node needs an assignment.
*/
bool Assignment() const;
/**
* @brief For an embedded node, is a comma indicating the next node needed?
* @remarks Some inline arrays can have a final comma behind the last embedded node.
* @return Returns 'true' when a comma is needed.
*/
bool CommaNeeded() const;
/**
* @brief Are comments and newlines allowed? For an embedded node, this might be prohibited. But can also explicitly be
* defined in the context.
* @remarks Inline tables require a one line definition for the embedded nodes.
* @return Returns 'true' if comments and newlines are allowed.
*/
bool CommentAndNewlineAllowed() const;
/**
* @brief Are newlines allowed? For an embedded node, this might be prohibited. But can also explicitly be defined in the
* context.
* @remarks Inline tables require a one line definition for the embedded nodes.
* @return Returns 'true' if comments and newlines are allowed.
*/
bool NewlineAllowed() const;
/**
* @brief For a standard (inline) node, is a newline required at the end of the node definition?
* @returns Returns'true' if a newline is required behind the node definition.
*/
bool FinalNewline() const;
private:
std::shared_ptr<const CNode> m_ptrTopMostNode; ///< Top most node that is used for the generation. The parent nodes of
///< the top most node will not be part of the node generation and if
///< they contain child nodes in their view, the nodes are printed by
///< their parent and not by their view.
std::string m_ssPrefixKey; ///< Prefix key to be used during the generation of the TOML code.
std::string m_ssKeyContext; ///< string containing the current context. The string must follow the
///< key rules for separation with bare, literal and quoted keys.
uint32_t m_uiOptions = 0; ///< Zero or more options to take into account when creating the text to
///< the TOML nodes.
bool m_bTopMost = true; ///< Set when this context is the top most context.
void ExtractContext(const std::shared_ptr<const CNode>& rptrNode);
std::shared_ptr<const CNode> m_ptrTopMostNode; ///< Top most node that is used for the generation. The
///< parent nodes of the top most node will not be part of
///< the node generation and if they contain child nodes in
///< their view, the nodes are printed by their parent and
///< not by their view.
std::string m_ssPrefixKey; ///< Prefix key to be used during the generation of the TOML
///< not by their view.
std::string m_ssKeyContext2;
std::string m_ssKeyContext; ///< String containing the current context. The string must
///< follow the key rules for separation with bare, literal
///< and quoted keys.
std::string m_ssKeyPath; ///< The key path composed of the prefix and the relative
///< key path.
std::string m_ssFullKeyPath; ///< The full key path composed of the key context and the
///< relative key path.
std::string m_ssRelKeyPath; ///< The relative key path, relative to the current context.
uint32_t m_uiOptions = 0; ///< Zero or more options to take into account when creating
///< the text to the TOML nodes.
bool m_bTopMost = true; ///< Set when this context is the top most context.
bool m_bLastNode = false; ///< Is this the last node in the current view?
bool m_bFinalLastNode = false; ///< When set, this is the last (top level) node of the node
///< hierarchy. Only child nodes can still follow.
EPresentation m_ePresentation = EPresentation::standard; ///< Presentation of the node.
bool m_bOneLine = false; ///< Set when the node is not allowed to cover more than
///< one line (except when using multi-line strings).
bool m_bAssignment = false; ///< Does the node need an assignment.
bool m_bCommaNeeded = false; ///< Is a comma needed following the node definition?
bool m_bFinalNewline = false; ///< Is a final newline behind the definition required?
};
/**
@@ -118,7 +253,7 @@ namespace toml_parser
*/
class CNode :
public std::enable_shared_from_this<CNode>, public sdv::IInterfaceAccess, public sdv::toml::INodeInfo,
public sdv::toml::INodeDelete, public sdv::toml::INodeUpdate
public sdv::toml::INodeUpdate
{
protected:
/**
@@ -150,7 +285,6 @@ namespace toml_parser
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::toml::INodeInfo)
SDV_INTERFACE_ENTRY(sdv::toml::INodeDelete)
SDV_INTERFACE_ENTRY(sdv::toml::INodeUpdate)
END_SDV_INTERFACE_MAP()
@@ -194,8 +328,9 @@ namespace toml_parser
virtual sdv::any_t GetValue() const override;
/**
* @brief Get the index of this node within the parent collection. Overload of sdv::toml::INodeInfo::GetIndex.
* @return The index of the node within the parent collection node or npos when no parent is available.
* @brief Get the index of this node within the view collection (either the assigned view or the parent). Overload of
* sdv::toml::INodeInfo::GetIndex.
* @return The index of the node within the view collection node or npos when no parent is available.
*/
virtual uint32_t GetIndex() const override;
@@ -214,27 +349,25 @@ namespace toml_parser
/**
* @brief Set or replace a comment for the node. Overload of sdv::toml::INodeInfo::SetComment.
* @remarks This function can also be used to insert whitespace (with or without comments) when used in raw mode.
* Set the comment text for the node. If a comment is proided as text (normal behavior), the comment text will be
* formatted automatically when generating the TOML text. If the comment is provided as raw comment, the text should
* contain all whitespace and the comment '#' character before the comment text.
* Comments inserted before the enode will be inserted on the line before the node uness the comment is provided in raw
* format and is ended with a newline and optionally whitespace. Comment inserted behind the node will be inserted on
* @details Set the comment text for the node. If a comment is provided as text (normal behavior), the comment text will
* be formatted automatically when generating the TOML text. If the comment text should not contain the comment
* character '#' before the comment text.
* Comments inserted before the node will be inserted on the line before the node unless the comment is provided in raw
* format and is ended with a line-break and optionally whitespace. Comment inserted behind the node will be inserted on
* the same line as the node.
* Comments provided as text is automatically wrapped to 80 characters if possible. Newlines in the text will cause a
* new comment line to start.
* @param[in] ssComment String containing the comment text or the raw comment string to set.
* @param[in] uiFlags One or more ECommentFlags flags influencing the behavior of the comment.
* Comments provided as text is automatically wrapped to 132 characters if possible. Line-breaks in the text will cause
* a new comment line to start.
* @param[in] eType The comment type to set the comment text for.
* @param[in] ssComment String containing the comment text to set.
*/
virtual void SetComment(const sdv::u8string& ssComment, uint32_t uiFlags) override;
virtual void SetComment(sdv::toml::INodeInfo::ECommentType eType, const sdv::u8string& ssComment) override;
/**
* Get the current comment for the node. Overload of sdv::toml::INodeInfo::GetComment.
* @remarks To receive the whitespace formatting the node, use this function in raw mode.
* @param[in] uiFlags One or more ECommentFlags flags identifying the string format of the comment to return.
* @param[in] eType The comment type to get the comment text of.
* @return String with the comment text or an empty string if no comment is available.
*/
virtual sdv::u8string GetComment(uint32_t uiFlags) override;
virtual sdv::u8string GetComment(sdv::toml::INodeInfo::ECommentType eType) override;
/**
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
@@ -242,6 +375,18 @@ namespace toml_parser
*/
virtual void AutomaticFormat() override;
/**
* @brief Is the node inline? Overload of sdv::toml::INodeInfo::IsInline.
* @return Returns whether the node is defined as inline node.
*/
virtual bool IsInline() const override;
/**
* @brief Is the node defined as standard node? Overload of sdv::toml::INodeInfo::IsStandard.
* @return Returns whether the node is defined as standard node.
*/
virtual bool IsStandard() const override;
/**
* @brief Update the node with TOML code information. The default implementation takes the comment and whitespace around the
* node and stores this for node reconstruction.
@@ -249,19 +394,6 @@ namespace toml_parser
*/
virtual void UpdateNodeCode(const CNodeTokenRange& rNodeRange);
/**
* @brief Delete the current node. Overload of sdv::toml::INodeDelete::DeleteNode.
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
* @return Returns whether the deletion was successful.
*/
virtual bool DeleteNode() override;
/**
* @brief Is this node marked as deleted?
* @return Returns whether this node has been deleted.
*/
bool IsDeleted() const;
/**
* @brief Change the key name of the node (if the node is not a value node of an array). Overload of
* sdv::toml::INodeUpdate::ChangeName.
@@ -275,7 +407,7 @@ namespace toml_parser
/**
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
* @remarks Only valid for value nodes. Changing the value type is not supported.
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
* string. Conversion is automatically done to int64, double float, bool or u8string.
* @return Returns whether the value change was successful.
*/
@@ -297,6 +429,19 @@ namespace toml_parser
*/
virtual bool MoveDown() override;
/**
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
* @return Returns whether the deletion was successful.
*/
virtual bool DeleteNode() override;
/**
* @brief Is this node marked as deleted?
* @return Returns whether this node has been deleted.
*/
bool IsDeleted() const;
/**
* @brief Do a dynamic cast to one of the base types of the node.
* @return Casted shared pointer to the base type if the type is valid, or an empty pointer if not.
@@ -311,19 +456,19 @@ namespace toml_parser
template <typename TNodeType>
std::shared_ptr<const TNodeType> Cast() const;
/**
* @brief Gets the parent node pointer
* @return Returns the parent node pointer or an empty pointer when no parent was assigned or the stored weak pointer could
* not be locked.
*/
std::shared_ptr<CNodeCollection> GetParentPtr() const;
/**
* @brief Set the parent node.
* @param[in] rptrParent Reference to the node to assign to this node as a parent.
*/
void SetParentPtr(const std::shared_ptr<CNodeCollection>& rptrParent);
/**
* @brief Gets the parent node pointer.
* @return Returns the parent node pointer or an empty pointer when no parent was assigned or the stored weak pointer could
* not be locked.
*/
std::shared_ptr<CNodeCollection> GetParentPtr() const;
/**
* @brief Get the parent path of the node.
* @return Return the parent path if existining and not a root.
@@ -337,6 +482,12 @@ namespace toml_parser
*/
void SetViewPtr(const std::shared_ptr<CNodeCollection>& rptrView);
/**
* @brief Gets the view definition node pointer.
* @return Returns the store view definition node pointer or an empty pointer when no view was assigned.
*/
std::shared_ptr<CNodeCollection> GetViewPtr() const;
/**
* @brief Checks whether the node is part of the view.
* @details The node is part of the view if the supplied pointer is identical to the view definition pointer, when the view
@@ -367,95 +518,8 @@ namespace toml_parser
*/
virtual std::string GenerateTOML(const CGenContext& rContext = CGenContext()) const = 0;
protected:
/**
* @brief Compose a custom path from the node key path using a key prefix and a context.
* @param[in] rssPrefixKey The prefix to insert at as a base to the key tree.
* @param[in] rssContext The context that is used to define the relative portion of the key. To determine the relative
* portion, the context string contains the same prefix as is supplied in rssPrefixKey.
* @return Returns the custom path composed of the prefix and the relative portion of the original path.
*/
std::string GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const;
/**
* @brief Comment or code snippet structure.
* @details Each node has multiple code snippets used to reproduce the exact code. For example with an assignment:
* @code
*
* # This is out of scope comment before
*
* # This is comment before
* var_a = "abc" # comment behind
* # more comment behind
*
* # This is out of scope comment behind
*
* @endcode
*
* The code snippets are identified as follows:
* @code
* <out of scope comment before>
* <comment before>
* <space before><key><space>=<space><value><comment behind>
* <out of scope comment behind>
* @endcode
*/
class CCodeSnippet
{
public:
/**
* @brief Access the token list.
* @return Reference to the list with tokens.
*/
std::list<CToken>& List();
/**
* @brief Access the comment text string.
* @return Reference to the comment string.
*/
std::string& Str();
/**
* @brief Mode the code snippet composer should run in.
*/
enum class EComposeMode
{
compose_inline, ///< Compose as inline whitespace and comment. If there is no token list and no comment
///< string, compose as one space. If there is only a comment string, insert a space, add
///< the comment followed by an obligatory newline, and insert spaces until the next
///< provided position. If there are tokens with a comment token, replace the comment. If
///< there are tokens without comment, add the comment, newline and spaces.
compose_before, ///< Compose as comment assigned to and located before the node. If there is no token list
///< and no comment string, doesn't add anything. If there is only a comment string, adds
///< the comment followed by the obligatory newline. If there are tokens with a comment
///< token, replace the comment. If there are tokens without the comment, place the comment
///< before the last newline or when not available, at the end of the tokens followed by a
///< new newline.
compose_behind, ///< Compose as comment assigned to and located behind the node. If there is no token list
///< and no comment string, add a newline. If there is a comment string and no tokens,
///< add a space, the comment string followed by the obligatory newline. If there is a token
///< list without comment, add a comment before the newline or at the end with an additional
///< newline.
compose_standalone, ///< Compose as stand-alone comment. Replace any token list if a comment string is
///< available.
};
/**
* @brief Compose a conde string from the stored tokens and/or string.
* @param[in] eMode The mode the composer should run in.
* @param[in] nAssignmentOffset The offset for a next assignent; only used for inline composition.
* @param[in] nCommentOffset The offset to insert a multi-line comment; only used for inline and behind composition.
* @return The composed code string.
*/
std::string Compose(EComposeMode eMode, size_t nAssignmentOffset = 0, size_t nCommentOffset = 0) const;
private:
std::list<CToken> m_lstTokens; ///< Token list for the code snippet in raw format.
std::string m_ssComment; ///< The comment text for the code snippet in text format.
};
// White space and comment preservation indices for code generation.
const size_t m_nPreNodeCode = 0; ///< Code snippet before the node. Corresponds to
const size_t m_nPreNodeCode = 0; ///< Code snippet before the node. Corresponds to
///< sdv::toml::INodeInfo::ECommentFlags::comment_before.
const size_t m_nPostNodeCode = 1; ///< Comment behind the node. Corresponds to
///< sdv::toml::INodeInfo::ECommentFlags::comment_behind.
@@ -473,7 +537,7 @@ namespace toml_parser
/**
* @brief Get the code snippet.
* @param[in] nIndex The comment type index to get the comment for.
* @param[in] nIndex The comment type index to get the code for.
* @param[in] rssKey Reference to the key to be used for code snippet identification.
* @return Reference to the comment structure of the comment. If the provided index is not available in the vector,
* returns an empty code snippet.
@@ -481,15 +545,38 @@ namespace toml_parser
const CCodeSnippet& CodeSnippet(size_t nIndex, const std::string& rssKey = std::string()) const;
/**
* @brief Get the code snippet (write access). This allows moving the snippet from one node to the another node.
* @remarks Since the request to the code snippet could change the location of the vector allocation, access to the code
* snippet is valid until the next code snippet is requested.
* @param[in] nIndex The comment type index to get the comment for.
* @brief Get the code snippet (write access).
* @param[in] nIndex The comment type index to get the code for.
* @param[in] rssKey Reference to the key to be used for code snippet identification.
* @return Reference to the comment structure of the comment.
*/
CCodeSnippet& CodeSnippet(size_t nIndex, const std::string& rssKey = std::string());
/**
* @brief Get the code snippet using the comment type.
* @param[in] eType The comment type to get the code for.
* @return Reference to the comment structure of the comment. If the provided index is not available in the vector,
* returns an empty code snippet.
*/
const CCodeSnippet& CodeSnippet(sdv::toml::INodeInfo::ECommentType eType) const;
/**
* @brief Get the code snippet (write access) using the comment type.
* @param[in] eType The comment type to get the code for.
* @return Reference to the comment structure of the comment. If the provided index is not available in the vector,
* returns an empty code snippet.
*/
CCodeSnippet& CodeSnippet(sdv::toml::INodeInfo::ECommentType eType);
/**
* @brief Compose a custom path from the node key path using a key prefix and a context.
* @param[in] rssPrefixKey The prefix to insert at as a base to the key tree.
* @param[in] rssContext The context that is used to define the relative portion of the key. To determine the relative
* portion, the context string contains the same prefix as is supplied in rssPrefixKey.
* @return Returns the custom path composed of the prefix and the relative portion of the original path.
*/
std::string GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const;
private:
std::weak_ptr<CNodeCollection> m_ptrParent; ///< Weak pointer to the parent node (if existing).
std::weak_ptr<CNodeCollection> m_ptrView; ///< Weak pointer to the view node (if existing and explicitly set).
@@ -610,6 +697,12 @@ namespace toml_parser
*/
std::string RawValueText() const;
protected:
/**
* @brief When updating the node, reset the raw value text.
*/
void ResetRawValueText();
private:
std::string m_ssRawValue; ///< Raw value string.
};
@@ -647,7 +740,7 @@ namespace toml_parser
/**
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
* @remarks Only valid for value nodes. Changing the value type is not supported.
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
* string. Conversion is automatically done to int64, double float, bool or u8string.
* @return Returns whether the value change was successful.
*/
@@ -660,7 +753,7 @@ namespace toml_parser
virtual std::string ValueText() const override;
private:
bool m_bVal = false; ///< Value in case of boolean node.
bool m_bVal = false; ///< Value in case of virtual bool node.
};
/**
@@ -696,7 +789,7 @@ namespace toml_parser
/**
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
* @remarks Only valid for value nodes. Changing the value type is not supported.
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
* string. Conversion is automatically done to int64, double float, bool or u8string.
* @return Returns whether the value change was successful.
*/
@@ -745,7 +838,7 @@ namespace toml_parser
/**
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
* @remarks Only valid for value nodes. Changing the value type is not supported.
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
* string. Conversion is automatically done to int64, double float, bool or u8string.
* @return Returns whether the value change was successful.
*/
@@ -806,7 +899,7 @@ namespace toml_parser
/**
* @brief Change the value of the node. Overload of sdv::toml::INodeUpdate::ChangeValue.
* @remarks Only valid for value nodes. Changing the value type is not supported.
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, virtual bool value or a
* string. Conversion is automatically done to int64, double float, bool or u8string.
* @return Returns whether the value change was successful.
*/
@@ -826,8 +919,11 @@ namespace toml_parser
/**
* @brief Base structure for arrays and tables.
*/
class CNodeCollection : public CNode, public sdv::toml::INodeCollection, public sdv::toml::INodeCollectionInsert
class CNodeCollection : public CNode, public sdv::toml::INodeCollection, public sdv::toml::INodeCollectionInsert,
public sdv::toml::INodeCollectionConvert
{
// Friend class CNode.
friend CNode;
protected:
/**
* @brief Constructor
@@ -842,9 +938,16 @@ namespace toml_parser
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollection)
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollectionInsert)
SDV_INTERFACE_ENTRY(sdv::toml::INodeCollectionConvert)
SDV_INTERFACE_CHAIN_BASE(CNode)
END_SDV_INTERFACE_MAP()
/**
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
* will not be changed. Overload of sdv::toml::INodeInfo::AutomaticFormat.
*/
virtual void AutomaticFormat() override;
/**
* @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount.
* @return The amount of nodes.
@@ -900,7 +1003,7 @@ namespace toml_parser
* @param[in] ssName Name of the node to insert. Will be ignored for an array collection. The name must adhere to the
* key names defined by the TOML specification. Defining the key multiple times is not allowed. Quotation of key names
* is done automatically; the parser decides itself whether the key is bare-key, a literal key or a quoted key.
* @param[in] anyValue The value of the node, being either an integer, floating point number, boolean value or a string.
* @param[in] anyValue The value of the node, being either an integer, floating point number, virtual bool value or a string.
* Conversion is automatically done to int64, double float, bool or u8string.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
@@ -928,13 +1031,15 @@ namespace toml_parser
* collection count to insert the node at the end of the collection. Table nodes cannot be inserted before value nodes
* or arrays. If the index is referencing a position before a value node or an array, the index is automatically
* corrected.
* @param[in] ssKeyName Name of the table node to insert. Will be ignored if the current node is an array collection.
* @param[in] ssName Name of the table node to insert. Will be ignored if the current node is an array collection.
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
* literal key or a quoted key.
* @param[in] ePreference The preferred form of the node to be inserted.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
virtual sdv::IInterfaceAccess* InsertTable(uint32_t uiIndex, const sdv::u8string& ssKeyName) override;
virtual sdv::IInterfaceAccess* InsertTable(uint32_t uiIndex, const sdv::u8string& ssName,
sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override;
/**
* @brief Insert a table array into the collection at the location before the supplied index. Overload of
@@ -947,36 +1052,82 @@ namespace toml_parser
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
* literal key or a quoted key.
* @param[in] ePreference The preferred form of the node to be inserted.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
virtual sdv::IInterfaceAccess* InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName) override;
virtual sdv::IInterfaceAccess* InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName,
sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override;
/**
* @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string
* should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML
* string should contain and inline table nodes without names. Overload of sdv::toml::INodeCollectionInsert::InsertTOML.
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
* collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value
* nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically
* corrected.
* @param[in] ssTOML The TOML string to insert.
* @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted.
* @return The result of the insertion.
*/
virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(const sdv::u8string& ssTOML,
virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(uint32_t uiIndex, const sdv::u8string& ssTOML,
bool bRollbackOnPartly) override;
/**
* @brief Delete the current node. Overload of sdv::toml::INodeDelete::DeleteNode.
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
* @return Returns whether the deletion was successful.
*/
virtual bool DeleteNode() override;
//protected:
/**
* @brief Remove a node from the collection.
* @brief Can the node convert to an inline definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeInline.
* @return Returns whether the conversion to inline is possible. Returns 'true' when the node is already inline.
*/
virtual bool CanMakeInline() const override;
/**
* @brief Convert the node to an inline node. Overload of sdv::toml::INodeCollectionConvert::MakeInline.
* @return Returns whether the conversion was successful. Returns 'true' when the node was already inline.
*/
virtual bool MakeInline() override;
/**
* @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard.
* @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as
* standard node.
*/
virtual bool CanMakeStandard() const override;
/**
* @brief Convert the node to a standard node. Overload of sdv::toml::INodeCollectionConvert::MakeStandard.
* @return Returns whether the conversion was successful. Returns 'true' when the node was already defined as standard
* node.
*/
virtual bool MakeStandard() override;
/**
* @brief Delete a node from the collection.
* @remarks The node will not be deleted, but placed in the recycle bin. Deletion will take place at collection destruction.
* @param[in] rptrNode Reference to the smart pointer pointing to the node to remove.
* @return Returns whether the removal was successful.
*/
bool RemoveNode(const std::shared_ptr<CNode>& rptrNode);
bool DeleteNode(const std::shared_ptr<CNode>& rptrNode);
/**
* @brief Insert the node into the view.
* @remarks The node must be a descendant (direct child or an indirect child) of the node.
* @details Insert the node into the vector (and remove it from any previous vector if still assigned). Inline nodes can be
* assigned to stadard and inline nodes. Standard nodes can only be assigned to standard nodes. In the vector, first the
* inline nodes and then the standard nodes are located. This means that an inline node will be placed before standard nodes
* and standard nodes behind inline nodes, regardless of the index provided.
* @param[in] uiIndex Location within the vector to insert the node. Could be sdv::toml::npos to insert as last node.
* @param[in] rptrNode Reference to the smart pointer to the node to set the view for. If the node is not a direct child
* node, the view pointer of the node will be set.
* @return Returns true when the insertion was successful, false when node (likely because the node to insert is a standard
* node, whereas this node is an inline node.
*/
bool InsertIntoView(uint32_t uiIndex, const std::shared_ptr<CNode>& rptrNode);
/**
* @brief Remove a node from a view.
@@ -985,20 +1136,19 @@ namespace toml_parser
*/
bool RemoveFromView(const std::shared_ptr<CNode>& rptrNode);
/**
* @brief Check whether this node is the last node in the collection.
* @param[in] rptrNode Reference to the smart pointer pointing to the node to check for.
* @return Returns whether the provided node is the last node in the collection.
*/
bool CheckLast(const std::shared_ptr<CNode>& rptrNode);
public:
/**
* @brief Find the index belonging to the provided node.
* @param[in] rptrNode Reference to the smart pointer holding the node to return the index for.
* @return Return the node index. Returns npos if the node could not be found.
*/
uint32_t FindIndex(const std::shared_ptr<CNode>& rptrNode);
uint32_t FindIndex(const std::shared_ptr<CNode>& rptrNode) const;
/**
* @brief Is the provided child node a direct or indirect child node?
* @param[in] rptrNode Reference to the smart pointer of the potential descendant node.
* @return Returns whether the provided node is a descendant of the this node.
*/
bool IsDescendant(const std::shared_ptr<CNode>& rptrNode) const;
/**
* @brief Generic inserting function for nodes.
@@ -1023,6 +1173,24 @@ namespace toml_parser
template <typename TNodeType, typename... TArgs>
std::shared_ptr<CNode> Insert(uint32_t uiIndex, const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs);
/**
* @brief Combine the collection with the provided content (mathematical union).
* @details Update the child nodes with the child nodes of the provided collection. Extend the collection with nodes that do
* not exist and update the existing nodes with new values.
* @param[in] rptrCollection Reference to the collection being used for this operation.
* @return Returns whether the combination was successful.
*/
virtual bool Combine(const std::shared_ptr<CNodeCollection>& rptrCollection) = 0;
/**
* @brief Reduce the collection with by the provided content (mathematical difference).
* @details Reduce the child node with the child nodes already defined and identical in the provided collection. Different
* nodes or nodes that are not present in the collection remain.
* @param[in] rptrCollection Reference to the collection being used for this operation.
* @return Returns whether the reduction was successful.
*/
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) = 0;
private:
/**
* @brief When set, the child nodes need grouping (values following each other, tables and table arrays at the end).
@@ -1156,8 +1324,23 @@ namespace toml_parser
*/
virtual void MakeExplicit() override;
/**
* @brief Combine the collection with the provided content (mathematical union). Overload of CNodeCollection::Combine.
* @details Update the child nodes with the child nodes of the provided collection. Extend the collection with nodes that do
* not exist and update the existing nodes with new values.
* @param[in] rptrCollection Reference to the collection being used for this operation.
* @return Returns whether the combination was successful.
*/
virtual bool Combine(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
//bool m_bOpenToAddChildren = true; ///< If internal table, the table can be extended until the table is closed.
/**
* @brief Reduce the collection with by the provided content (mathematical difference). Overload of CNodeCollection::Reduce.
* @details Reduce the child node with the child nodes already defined and identical in the provided collection. Different
* nodes or nodes that are not present in the collection remain.
* @param[in] rptrCollection Reference to the collection being used for this operation.
* @return Returns whether the reduction was successful.
*/
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
private:
bool m_bDefinedExplicitly = true; ///< When set, the table is defined explicitly.
@@ -1267,6 +1450,13 @@ namespace toml_parser
*/
bool TableArray() const;
/**
* @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard.
* @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as
* standard node.
*/
virtual bool CanMakeStandard() const override;
/**
* @brief The derived class from the node collection can be inline or not. Overload of CNode::Inline.
* @return Returns whether the node is an inline node.
@@ -1287,9 +1477,34 @@ namespace toml_parser
*/
virtual bool Inline(bool bInline) override;
/**
* @brief Does the last child node need a comma following the node?
* @return Returns whether the array was defined with the last node requiring a comma following the node.
*/
bool LastNodeWithSucceedingComma() const;
/**
* @brief Combine the collection with the provided content (mathematical union). Overload of CNodeCollection::Combine.
* @details Update the child nodes with the child nodes of the provided collection. Extend the collection with nodes that do
* not exist and update the existing nodes with new values.
* @param[in] rptrCollection Reference to the collection being used for this operation.
* @return Returns whether the combination was successful.
*/
virtual bool Combine(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
/**
* @brief Reduce the collection with by the provided content (mathematical difference). Overload of CNodeCollection::Reduce.
* @details Reduce the child node with the child nodes already defined and identical in the provided collection. Different
* nodes or nodes that are not present in the collection remain.
* @param[in] rptrCollection Reference to the collection being used for this operation.
* @return Returns whether the reduction was successful.
*/
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
private:
bool m_bDefinedExplicitly = true; ///< When set, the array is defined explicitly.
bool m_bInline = false; ///< Flag determining whether the table is inline or not.
bool m_bDefinedExplicitly = true; ///< When set, the array is defined explicitly.
bool m_bInline = false; ///< Flag determining whether the table is inline or not.
bool m_bLastChildNodeWithComma = false; ///< Set when the last child node of the array initially has a comma behind the node.
};
/**
@@ -1323,7 +1538,7 @@ namespace toml_parser
{}
/**
* @brief Delete the current node. Overload of sdv::toml::INodeDelete::DeleteNode.
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
* @return Returns whether the deletion was successful.
*/
@@ -1387,10 +1602,9 @@ namespace toml_parser
{
ptrTableArray = std::make_shared<CTableArray>(Parser(), prKey.first.get().StringValue(),
prKey.first.get().RawString());
ptrTableArray->SetParentPtr(Cast<CNodeCollection>());
// Add to the list
m_lstNodes.push_back(ptrTableArray);
// Set the parent pointer; this will add the node to the list.
ptrTableArray->SetParentPtr(Cast<CNodeCollection>());
}
else
ptrTableArray = (*itNode)->template Cast<CTableArray>();
@@ -1401,6 +1615,7 @@ namespace toml_parser
// Create the table.
ptrNode = ptrTableArray->Insert<CTable>(sdv::toml::npos, CTokenRange(prKey.first, prKey.first.get().Next()),
false, true);
ptrNode->SetViewPtr(Cast<CNodeCollection>());
} else if (!Cast<CArray>() && itNode != m_lstNodes.end())
{
// If existing... this might be a duplicate if not explicitly defined before.
@@ -1422,10 +1637,9 @@ namespace toml_parser
// Create the target node.
ptrNode = std::make_shared<TNodeType>(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString(),
rtArgs...);
ptrNode->SetParentPtr(Cast<CNodeCollection>());
// Add to the list
m_lstNodes.push_back(ptrNode);
// Set the parent pointer; this will add the node to the list.
ptrNode->SetParentPtr(Cast<CNodeCollection>());
// If the current node is implicit, take over the inline flag (this determines whether a sub-table definition is
// allowed or not).
@@ -1480,7 +1694,6 @@ namespace toml_parser
// Create an implicit table node.
ptrNode = std::make_shared<CTable>(Parser(), prKey.first.get().StringValue(), prKey.first.get().RawString(), false, false);
ptrNode->SetParentPtr(Cast<CNodeCollection>());
m_lstNodes.push_back(ptrNode);
}
// Insert the node in the child node
@@ -1490,15 +1703,14 @@ namespace toml_parser
ptrNode = ptrNodeCollection->Insert<TNodeType>(sdv::toml::npos, prKey.second, rtArgs...);
if (!ptrNode)
throw XTOMLParseException("Could not create the node '" + prKey.first.get().StringValue() + "'.");
// This node is not the parent, but still presents of the created node. Add the view pointer that they are linked.
ptrNode->SetViewPtr(Cast<CNodeCollection>());
}
// Insert the node at the requested location if this node is inline or this is the view of the node.
auto itPos = (static_cast<size_t>(uiIndex) >= m_vecNodeOrder.size()) ? m_vecNodeOrder.end() :
m_vecNodeOrder.begin() + static_cast<size_t>(uiIndex);
m_vecNodeOrder.insert(itPos, ptrNode);
if (ptrNode->GetParentPtr() != Cast<CNodeCollection>())
ptrNode->SetViewPtr(Cast<CNodeCollection>());
// Return the result
return ptrNode;

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#include "parser_toml.h"
#include <iostream>
#include "miscellaneous.h"
@@ -15,7 +29,7 @@ namespace toml_parser
{
m_ptrRoot.reset();
m_ptrCurrentCollection.reset();
m_lexer.Reset();
m_lexer.Clear();
while (!m_stackEnvironment.empty())
m_stackEnvironment.pop();
}
@@ -109,6 +123,13 @@ namespace toml_parser
CNodeCollection& CParser::Root()
{
// Create the root node if not existing.
if (!m_ptrRoot)
{
m_ptrRoot = std::make_shared<CRootTable>(*this);
m_ptrCurrentCollection = m_ptrRoot;
}
auto ptrCollection = m_ptrRoot->Cast<CTable>();
return *ptrCollection.get();
}
@@ -246,7 +267,7 @@ namespace toml_parser
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
ptrNode = m_ptrCurrentCollection->Insert<CArray>(sdv::toml::npos, rrangeKeyPath);
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
m_stackEnvironment.push(EEnvironment::env_array);
m_stackEnvironment.push(EEnvironment::array);
ProcessArray(rNodeRange);
m_stackEnvironment.pop();
m_ptrCurrentCollection = ptrCurrentCollectionStored;
@@ -257,7 +278,7 @@ namespace toml_parser
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
ptrNode = m_ptrCurrentCollection->Insert<CTable>(sdv::toml::npos, rrangeKeyPath, true);
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
m_stackEnvironment.push(EEnvironment::env_inline_table);
m_stackEnvironment.push(EEnvironment::inline_table);
ProcessInlineTable(rNodeRange);
m_stackEnvironment.pop();
m_ptrCurrentCollection = ptrCurrentCollectionStored;
@@ -275,8 +296,9 @@ namespace toml_parser
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
if (!m_stackEnvironment.empty())
{
// Skip newlines
while (refToken.get().Category() == ETokenCategory::token_syntax_new_line)
// Skip newlines if not an inline table
while (m_stackEnvironment.top() != EEnvironment::inline_table &&
refToken.get().Category() == ETokenCategory::token_syntax_new_line)
{
m_lexer.Consume();
refToken = m_lexer.Peek();
@@ -284,7 +306,7 @@ namespace toml_parser
switch (m_stackEnvironment.top())
{
case EEnvironment::env_array:
case EEnvironment::array:
{
int32_t index = 1;
while (refToken.get() && refToken.get().Category() == ETokenCategory::token_syntax_new_line)
@@ -299,7 +321,7 @@ namespace toml_parser
}
}
break;
case EEnvironment::env_inline_table:
case EEnvironment::inline_table:
if (!refToken.get()
|| (refToken.get().Category() != ETokenCategory::token_syntax_comma
&& refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close))
@@ -433,7 +455,7 @@ namespace toml_parser
switch (refToken.get().Category())
{
case ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
throw XTOMLParseException("Line break within an inline table is not allowed.");
break;
case ETokenCategory::token_syntax_comma:
if (eExpect != EExpect::comma_or_end) throw XTOMLParseException("Expecting value or table end.");

View File

@@ -1,3 +1,17 @@
/********************************************************************************
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Martin Stimpfl - initial API and implementation
* Erik Verhoeven - writing TOML and whitespace preservation
********************************************************************************/
#ifndef PARSER_TOML_H
#define PARSER_TOML_H
@@ -19,16 +33,11 @@ namespace toml_parser
class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
{
public:
/**
* @brief Default constructor
*/
CParser() = default;
/**
* @brief Construct a new Parser object
* @param[in] rssString UTF-8 encoded data of a TOML source
*/
CParser(const std::string& rssString);
CParser(const std::string& rssString = std::string());
// Interface map
BEGIN_SDV_INTERFACE_MAP()
@@ -125,14 +134,15 @@ namespace toml_parser
*/
enum class EEnvironment
{
env_array, ///< Environment for an array
env_inline_table ///< Environment for a table
none, ///< No nested environment (used as default environment).
array, ///< Environment for an array
inline_table ///< Environment for a table
};
std::stack<EEnvironment> m_stackEnvironment; ///< Tracking of environments in nested structures.
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
CLexer m_lexer; ///< Lexer.
enum_stack<EEnvironment, EEnvironment::none> m_stackEnvironment; ///< Tracking of environments in nested structures.
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
CLexer m_lexer; ///< Lexer.
};
} // namespace toml_parser