update parser (#5)

This commit is contained in:
tompzf
2026-01-16 11:40:02 +01:00
committed by GitHub
parent 5039a37131
commit 234be8917f
115 changed files with 14038 additions and 5380 deletions

View File

@@ -42,7 +42,7 @@ add_library(core_services SHARED
"installation_composer.h"
"installation_composer.cpp"
)
"toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp")
# Link target
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

View File

@@ -61,10 +61,10 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
// Reset the current baseline
ResetConfigBaseline();
CParserTOML parser(ssContent);
toml_parser::CParser parser(ssContent);
// Check for config file compatibility
auto ptrConfigVersion = parser.GetRoot().GetDirect("Configuration.Version");
auto ptrConfigVersion = parser.Root().Direct("Configuration.Version");
if (!ptrConfigVersion)
{
SDV_LOG_ERROR("Configuration version must be present in the configuration file.");
@@ -84,17 +84,17 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
!GetAppControl().IsMaintenanceApplication())
{
// Load all modules in the component section
auto ptrComponents = parser.GetRoot().GetDirect("Component");
if (ptrComponents && ptrComponents->GetArray())
auto ptrComponents = parser.Root().Direct("Component");
if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
{
for (uint32_t uiIndex = 0; uiIndex < ptrComponents->GetArray()->GetCount(); uiIndex++)
for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex);
auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
if (ptrComponent)
{
// Get the information from the component.
std::filesystem::path pathModule;
auto ptrModule = ptrComponent->GetDirect("Path");
auto ptrModule = ptrComponent->Direct("Path");
if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
// If there is no path, this is allowed... skip this module
@@ -118,17 +118,17 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
}
// Load all modules from the module section.
auto ptrModules = parser.GetRoot().GetDirect("Module");
if (ptrModules && ptrModules->GetArray())
auto ptrModules = parser.Root().Direct("Module");
if (ptrModules && ptrModules->Cast<toml_parser::CArray>())
{
for (uint32_t uiIndex = 0; uiIndex < ptrModules->GetArray()->GetCount(); uiIndex++)
for (uint32_t uiIndex = 0; uiIndex < ptrModules->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{
auto ptrModule = ptrModules->GetArray()->Get(uiIndex);
auto ptrModule = ptrModules->Cast<toml_parser::CArray>()->Get(uiIndex);
if (ptrModule)
{
// Get the information from the component.
std::filesystem::path pathModule;
auto ptrModulePath = ptrModule->GetDirect("Path");
auto ptrModulePath = ptrModule->Direct("Path");
if (ptrModulePath) pathModule = static_cast<std::string>(ptrModulePath->GetValue());
if (pathModule.empty())
@@ -158,23 +158,23 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
// Start all components from the component section
std::filesystem::path pathLastModule;
auto ptrComponents = parser.GetRoot().GetDirect("Component");
if (ptrComponents && ptrComponents->GetArray())
auto ptrComponents = parser.Root().Direct("Component");
if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
{
for (uint32_t uiIndex = 0; uiIndex < ptrComponents->GetArray()->GetCount(); uiIndex++)
for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex);
auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
if (ptrComponent)
{
// Get the information from the component.
std::filesystem::path pathModule;
auto ptrModule = ptrComponent->GetDirect("Path");
auto ptrModule = ptrComponent->Direct("Path");
if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
std::string ssClass;
auto ptrClass = ptrComponent->GetDirect("Class");
auto ptrClass = ptrComponent->Direct("Class");
if (ptrClass) ssClass = static_cast<std::string>(ptrClass->GetValue());
std::string ssName;
auto ptrName = ptrComponent->GetDirect("Name");
auto ptrName = ptrComponent->Direct("Name");
if (ptrName) ssName = static_cast<std::string>(ptrName->GetValue());
// If there is a path, store it. If there is none, take the last stored
@@ -204,10 +204,10 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
{
auto itModule = mapModules.find(pathModule);
if (itModule == mapModules.end()) continue; // Module was not loaded before...
tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->CreateTOMLText());
tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->GenerateTOML());
}
else
tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->CreateTOMLText());
tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML());
if (!tObjectID)
{

View File

@@ -723,7 +723,7 @@ void CAppControl::BroadcastOperationState(sdv::app::EAppOperationState eState)
bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
{
CParserTOML parserConfig;
toml_parser::CParser parserConfig;
std::string ssError;
try
{
@@ -736,7 +736,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
}
// Get the reporting settings (if this succeeded at all...)
auto ptrReport = parserConfig.GetRoot().GetDirect("Console.Report");
auto ptrReport = parserConfig.Root().Direct("Console.Report");
if (ptrReport && ptrReport->GetValue() == "Silent") m_bSilent = true;
if (ptrReport && ptrReport->GetValue() == "Verbose") m_bVerbose = true;
@@ -751,8 +751,8 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
// Allow a custom logger to be defined
std::filesystem::path pathLoggerModule;
std::string ssLoggerClass;
std::shared_ptr<CNode> ptrLogHandlerPath = parserConfig.GetRoot().GetDirect("LogHandler.Path");
std::shared_ptr<CNode> ptrLogHandlerClass = parserConfig.GetRoot().GetDirect("LogHandler.Class");
std::shared_ptr<toml_parser::CNode> ptrLogHandlerPath = parserConfig.Root().Direct("LogHandler.Path");
std::shared_ptr<toml_parser::CNode> ptrLogHandlerClass = parserConfig.Root().Direct("LogHandler.Class");
if (ptrLogHandlerPath && !ptrLogHandlerClass)
{
if (!m_bSilent)
@@ -778,12 +778,12 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
}
// Get an optional program tag for the logger
std::shared_ptr<CNode> ptrLogPogramTag = parserConfig.GetRoot().GetDirect("LogHandler.Tag");
std::shared_ptr<toml_parser::CNode> ptrLogPogramTag = parserConfig.Root().Direct("LogHandler.Tag");
if (ptrLogPogramTag) m_ssProgramTag = static_cast<std::string>(ptrLogPogramTag->GetValue());
// Get the application-mode
std::string ssApplication = "Standalone";
std::shared_ptr<CNode> ptrApplication = parserConfig.GetRoot().GetDirect("Application.Mode");
std::shared_ptr<toml_parser::CNode> ptrApplication = parserConfig.Root().Direct("Application.Mode");
if (ptrApplication) ssApplication = static_cast<std::string>(ptrApplication->GetValue());
if (ssApplication == "Standalone") m_eContextMode = sdv::app::EAppContext::standalone;
else if (ssApplication == "External") m_eContextMode = sdv::app::EAppContext::external;
@@ -820,21 +820,21 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error;
if (IsMainApplication() || IsIsolatedApplication())
eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info;
std::shared_ptr<CNode> ptrLogSeverityFilter = parserConfig.GetRoot().GetDirect("LogHandler.Filter");
std::shared_ptr<toml_parser::CNode> ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.Filter");
m_eSeverityFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
sdv::core::ELogSeverity::info);
ptrLogSeverityFilter = parserConfig.GetRoot().GetDirect("LogHandler.ViewFilter");
ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.ViewFilter");
m_eSeverityViewFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
eLogDefaultViewSeverityFilter);
// Get the optional instance ID.
std::shared_ptr<CNode> ptrInstance = parserConfig.GetRoot().GetDirect("Application.Instance");
std::shared_ptr<toml_parser::CNode> ptrInstance = parserConfig.Root().Direct("Application.Instance");
if (ptrInstance)
m_uiInstanceID = ptrInstance->GetValue();
else
m_uiInstanceID = 1000u;
// Number of attempts to establish a connection to a running instance.
std::shared_ptr<CNode> ptrRetries = parserConfig.GetRoot().GetDirect("Application.Retries");
std::shared_ptr<toml_parser::CNode> ptrRetries = parserConfig.Root().Direct("Application.Retries");
if (ptrRetries)
{
m_uiRetries = ptrRetries->GetValue();
@@ -848,7 +848,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
if (IsMainApplication() || IsIsolatedApplication())
{
// Get the optional installation directory.
std::shared_ptr<CNode> ptrInstallDir = parserConfig.GetRoot().GetDirect("Application.InstallDir");
std::shared_ptr<toml_parser::CNode> ptrInstallDir = parserConfig.Root().Direct("Application.InstallDir");
if (ptrInstallDir)
{
m_pathRootDir = ptrInstallDir->GetValue();
@@ -877,7 +877,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
// not auto-updateable.
if (!IsMaintenanceApplication() && !IsIsolatedApplication())
{
auto ptrConfigFile = parserConfig.GetRoot().GetDirect("Application.Config");
auto ptrConfigFile = parserConfig.Root().Direct("Application.Config");
if (ptrConfigFile)
m_pathAppConfig = ptrConfigFile->GetValue();
}
@@ -905,10 +905,10 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
try
{
// Read the configuration
CParserTOML parserSettings(ssContent);
toml_parser::CParser parserSettings(ssContent);
// Check for the version
auto ptrVersion = parserSettings.GetRoot().GetDirect("Settings.Version");
auto ptrVersion = parserSettings.Root().Direct("Settings.Version");
if (!ptrVersion)
{
if (!m_bSilent)
@@ -925,12 +925,12 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
}
// Read the system configurations
auto ptrSystemConfigs = parserSettings.GetRoot().GetDirect("Settings.SystemConfig");
if (ptrSystemConfigs && ptrSystemConfigs->GetArray())
auto ptrSystemConfigs = parserSettings.Root().Direct("Settings.SystemConfig");
if (ptrSystemConfigs && ptrSystemConfigs->Cast<toml_parser::CArray>())
{
for (uint32_t uiIndex = 0; uiIndex < ptrSystemConfigs->GetArray()->GetCount(); uiIndex++)
for (uint32_t uiIndex = 0; uiIndex < ptrSystemConfigs->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{
auto ptrSystemConfig = ptrSystemConfigs->GetArray()->Get(uiIndex);
auto ptrSystemConfig = ptrSystemConfigs->Cast<toml_parser::CArray>()->Get(uiIndex);
if (!ptrSystemConfig) continue;
m_vecSysConfigs.push_back(static_cast<std::string>(ptrSystemConfig->GetValue()));
}
@@ -939,7 +939,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
// Get the application config - but only when not specified over the app-control-config.
if (m_pathAppConfig.empty())
{
auto ptrAppConfig = parserSettings.GetRoot().GetDirect("Settings.AppConfig");
auto ptrAppConfig = parserSettings.Root().Direct("Settings.AppConfig");
if (ptrAppConfig)
{
// Path available. Enable auto-update.

View File

@@ -164,22 +164,22 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
m_bBlockSystemObjects = bBlockSystemObjects;
// Parse the manifest
CParserTOML parser(rssManifest);
toml_parser::CParser parser(rssManifest);
// Get the installation version - must be identical to the interface version
auto ptrInstallVersionNode = parser.GetRoot().GetDirect("Installation.Version");
auto ptrInstallVersionNode = parser.Root().Direct("Installation.Version");
if (!ptrInstallVersionNode || ptrInstallVersionNode->GetValue() != SDVFrameworkInterfaceVersion) return false;
// Get the installation name
auto ptrInstallNameNode = parser.GetRoot().GetDirect("Installation.Name");
auto ptrInstallNameNode = parser.Root().Direct("Installation.Name");
if (!ptrInstallNameNode) return false;
m_ssInstallName = static_cast<std::string>(ptrInstallNameNode->GetValue());
if (m_ssInstallName.empty()) return false;
// Get installation properties. The properties are optional
auto ptrProperties = parser.GetRoot().GetDirect("Properties");
std::shared_ptr<CTable> ptrPropertyTable;
if (ptrProperties) ptrPropertyTable = ptrProperties->GetTable();
auto ptrProperties = parser.Root().Direct("Properties");
std::shared_ptr<toml_parser::CTable> ptrPropertyTable;
if (ptrProperties) ptrPropertyTable = ptrProperties->Cast<toml_parser::CTable>();
if (ptrPropertyTable)
{
for (uint32_t uiIndex = 0; uiIndex < ptrPropertyTable->GetCount(); uiIndex++)
@@ -191,9 +191,9 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
}
// Build the module list
auto ptrModulesNode = parser.GetRoot().GetDirect("Module");
auto ptrModulesNode = parser.Root().Direct("Module");
if (!ptrModulesNode) return true; // No modules in the manifest
auto ptrModuleArrayNode = ptrModulesNode->GetArray();
auto ptrModuleArrayNode = ptrModulesNode->Cast<toml_parser::CArray>();
if (!ptrModuleArrayNode) return false; // Must be array
for (uint32_t uiModuleIndex = 0; uiModuleIndex < ptrModuleArrayNode->GetCount(); uiModuleIndex++)
{
@@ -202,19 +202,19 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
if (!ptrModule) continue;
// Get the module path
auto ptrModulePath = ptrModule->GetDirect("Path");
auto ptrModulePath = ptrModule->Direct("Path");
if (!ptrModulePath) continue;
std::filesystem::path pathModule = static_cast<std::string>(ptrModulePath->GetValue());
std::string ssModuleManifest;
// Get the component list (if available)
auto ptrModuleComponents = ptrModule->GetDirect("Component");
auto ptrModuleComponents = ptrModule->Direct("Component");
if (ptrModuleComponents)
{
// The module manifest contains the TOML text of the component array
auto ptrModuleComponentArray = ptrModuleComponents->GetArray();
auto ptrModuleComponentArray = ptrModuleComponents->Cast<toml_parser::CArray>();
if (ptrModuleComponentArray)
ssModuleManifest = ptrModuleComponents->CreateTOMLText();
ssModuleManifest = ptrModuleComponents->GenerateTOML();
}
// Add the module
@@ -256,10 +256,10 @@ std::string CInstallManifest::Write() const
sstream << "[[Module]]" << std::endl << "Path=\"" << rsEntry.pathRelModule.generic_u8string() << "\"" << std::endl;
// Read the module manifest
CParserTOML parser(rsEntry.ssManifest);
toml_parser::CParser parser(rsEntry.ssManifest);
// Add the module manifest as part of the installation manifest.
sstream << parser.CreateTOMLText("Module") << std::endl;
sstream << parser.GenerateTOML("Module") << std::endl;
}
return sstream.str();
@@ -286,14 +286,14 @@ bool CInstallManifest::AddModule(const std::filesystem::path& rpathModulePath,
if (!ssManifest.empty())
{
// Check the interface version for compatibility
CParserTOML parser(ssManifest);
auto ptrInterfaceNode = parser.GetRoot().GetDirect("Interface.Version");
toml_parser::CParser parser(ssManifest);
auto ptrInterfaceNode = parser.Root().Direct("Interface.Version");
if (!ptrInterfaceNode) return false;
if (ptrInterfaceNode->GetValue() != SDVFrameworkInterfaceVersion) return false;
auto ptrComponentsNode = parser.GetRoot().GetDirect("Component");
auto ptrComponentsNode = parser.Root().Direct("Component");
if (!ptrComponentsNode) return true; // No component available in the manifest
if (!ptrComponentsNode->GetArray()) return false;
ssComponentsManifest = ptrComponentsNode->CreateTOMLText();
if (!ptrComponentsNode->Cast<toml_parser::CArray>()) return false;
ssComponentsManifest = ptrComponentsNode->GenerateTOML();
}
// Store path and component
@@ -398,7 +398,7 @@ bool CInstallManifest::NeedQuotedName(const std::string& rssName)
{
for (char c : rssName)
{
if (!std::isalnum(c) && c != '_' && c != '-')
if (static_cast<uint8_t>(c) > 127u || (!std::isalnum(c) && c != '_' && c != '-'))
return true;
}
return false;
@@ -408,10 +408,10 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
bool bBlockSystemObjects) : pathRelModule(rpathRelModule), ssManifest(rssManifest)
{
// Parse the manifest and extract information from them...
CParserTOML parser(rssManifest);
auto ptrComponents = parser.GetRoot().GetDirect("Component");
toml_parser::CParser parser(rssManifest);
auto ptrComponents = parser.Root().Direct("Component");
if (!ptrComponents) return; // No objects...
auto ptrComponentArray = ptrComponents->GetArray();
auto ptrComponentArray = ptrComponents->Cast<toml_parser::CArray>();
if (!ptrComponentArray) return; // No objects...
for (uint32_t uiIndex = 0; uiIndex < ptrComponentArray->GetCount(); uiIndex++)
{
@@ -422,14 +422,14 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
SComponent sComponent{};
//sComponent.pathModule = rpathModule;
sComponent.pathRelModule = rpathRelModule;
sComponent.ssManifest = ptrComponent->CreateTOMLText("Component");
auto ptrClassName = ptrComponent->GetDirect("Class");
sComponent.ssManifest = ptrComponent->GenerateTOML(toml_parser::CGenContext("Component"));
auto ptrClassName = ptrComponent->Direct("Class");
if (!ptrClassName) continue;
sComponent.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
auto ptrAliases = ptrComponent->GetDirect("Aliases");
auto ptrAliases = ptrComponent->Direct("Aliases");
if (ptrAliases)
{
auto ptrAliasesArray = ptrAliases->GetArray();
auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++)
{
auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
@@ -437,10 +437,10 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
sComponent.seqAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
}
}
auto ptrDefaultName = ptrComponent->GetDirect("DefaultName");
auto ptrDefaultName = ptrComponent->Direct("DefaultName");
if (ptrDefaultName) sComponent.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
else sComponent.ssDefaultObjectName = sComponent.ssClassName;
auto ptrType = ptrComponent->GetDirect("Type");
auto ptrType = ptrComponent->Direct("Type");
if (!ptrType) continue;
std::string ssType = static_cast<std::string>(ptrType->GetValue());
if (ssType == "System") sComponent.eType = sdv::EObjectType::SystemObject;
@@ -453,13 +453,13 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
else if (ssType == "Utility") sComponent.eType = sdv::EObjectType::Utility;
else continue;
if (bBlockSystemObjects && sComponent.eType == sdv::EObjectType::SystemObject) continue;
auto ptrSingleton = ptrComponent->GetDirect("Singleton");
auto ptrSingleton = ptrComponent->Direct("Singleton");
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
sComponent.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
auto ptrDependencies = ptrComponent->GetDirect("Dependencies");
auto ptrDependencies = ptrComponent->Direct("Dependencies");
if (ptrDependencies)
{
auto ptrDependencyArray = ptrDependencies->GetArray();
auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
uiDependencyIndex++)
{

View File

@@ -299,7 +299,7 @@ private:
/**
* @brief Interpret a version number as string.
* @details A version string is composed of: major.minor.patch (numbers only; characters and whitespace are ignored).
* @param rssVersion Reference to the version string.
* @param[in] rssVersion Reference to the version string.
* @return The interpreted version.
*/
inline sdv::installation::SPackageVersion InterpretVersionString(const std::string& rssVersion)

View File

@@ -206,10 +206,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
try
{
CParserTOML parser(ssManifest);
toml_parser::CParser parser(ssManifest);
// Check for the interface version - currently must be equal.
auto ptrVersion = parser.GetRoot().GetDirect("Interface.Version");
auto ptrVersion = parser.Root().Direct("Interface.Version");
if (ptrVersion) m_uiIfcVersion = ptrVersion->GetValue();
if (!ptrVersion || m_uiIfcVersion != SDVFrameworkInterfaceVersion)
{
@@ -230,26 +230,26 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
}
// Get available classes
auto ptrComponents = parser.GetRoot().GetDirect("Component");
if (!ptrComponents || !ptrComponents->GetArray())
auto ptrComponents = parser.Root().Direct("Component");
if (!ptrComponents || !ptrComponents->Cast<toml_parser::CArray>())
{
SDV_LOG_ERROR("Error opening SDV module: ", rpathModule.generic_u8string(), " error: no components available");
Unload(true);
return false;
}
for (std::uint32_t uiIndex = 0; uiIndex < ptrComponents->GetArray()->GetCount(); uiIndex++)
for (std::uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{
// Fill in the class info.
sdv::SClassInfo sInfo{};
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex);
auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
if (!ptrComponent) continue;
auto ptrClassName = ptrComponent->GetDirect("Class");
auto ptrClassName = ptrComponent->Direct("Class");
if (!ptrClassName) continue;
sInfo.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
auto ptrAliases = ptrComponent->GetDirect("Aliases");
auto ptrAliases = ptrComponent->Direct("Aliases");
if (ptrAliases)
{
auto ptrAliasesArray = ptrAliases->GetArray();
auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++)
{
auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
@@ -257,10 +257,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
sInfo.seqClassAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
}
}
auto ptrDefaultName = ptrComponent->GetDirect("DefaultName");
auto ptrDefaultName = ptrComponent->Direct("DefaultName");
if (ptrDefaultName) sInfo.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
else sInfo.ssDefaultObjectName = sInfo.ssClassName;
auto ptrType = ptrComponent->GetDirect("Type");
auto ptrType = ptrComponent->Direct("Type");
if (!ptrType) continue;
std::string ssType = static_cast<std::string>(ptrType->GetValue());
if (ssType == "System") sInfo.eType = sdv::EObjectType::SystemObject;
@@ -272,13 +272,13 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
else if (ssType == "Stub") sInfo.eType = sdv::EObjectType::Stub;
else if (ssType == "Utility") sInfo.eType = sdv::EObjectType::Utility;
else continue;
auto ptrSingleton = ptrComponent->GetDirect("Singleton");
auto ptrSingleton = ptrComponent->Direct("Singleton");
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
sInfo.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
auto ptrDependencies = ptrComponent->GetDirect("Dependencies");
auto ptrDependencies = ptrComponent->Direct("Dependencies");
if (ptrDependencies)
{
auto ptrDependencyArray = ptrDependencies->GetArray();
auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
uiDependencyIndex++)
{

View File

@@ -312,11 +312,11 @@ sdv::core::TModuleID CModuleControl::ContextLoad(const std::filesystem::path& rp
{
size_t nIndex = 0;
size_t nUnsupportedObjectCount = 0;
CParserTOML parser(rssManifest);
toml_parser::CParser parser(rssManifest);
do
{
std::shared_ptr<CNode> ptrComponentType =
parser.GetRoot().GetDirect(std::string("Component[") + std::to_string(nIndex++) + "].Type");
std::shared_ptr<toml_parser::CNode> ptrComponentType =
parser.Root().Direct(std::string("Component[") + std::to_string(nIndex++) + "].Type");
if (!ptrComponentType) break;
std::string ssType = static_cast<std::string>(ptrComponentType->GetValue());
if (ssType == "Device") continue; // Okay

View File

@@ -983,11 +983,11 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
sstreamConfig << "Object = \"" << rssObjectName << "\"" << std::endl << std::endl;
if (!rssObjectConfig.empty())
{
CParserTOML parserConfig(rssObjectConfig);
sstreamConfig << parserConfig.CreateTOMLText("Isolation.Config");
toml_parser::CParser parserConfig(rssObjectConfig);
sstreamConfig << parserConfig.GenerateTOML("Isolation.Config");
}
CParserTOML parserConnection(ssConnectionString);
sstreamConfig << parserConnection.CreateTOMLText("Isolation.Connection");
toml_parser::CParser parserConnection(ssConnectionString);
sstreamConfig << parserConnection.GenerateTOML("Isolation.Connection");
// Create command line arguments
sdv::sequence<sdv::u8string> seqArguments;
@@ -1177,8 +1177,9 @@ sdv::core::TObjectID CRepository::CreateObjectID()
static sdv::core::TObjectID tCurrent = 0;
if (!tCurrent)
{
srand(static_cast<unsigned int>(time(0)));
tCurrent = rand();
std::srand(static_cast<unsigned int>(time(0)));
tCurrent = 0;
while (!tCurrent) tCurrent = std::rand();
}
return ++tCurrent;
}

View File

@@ -22,7 +22,7 @@ public:
CSDVCore();
/**
* \brief Destructor
* @brief Destructor
*/
~CSDVCore();

View File

@@ -1,323 +1,302 @@
#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}
/// The TOML parser namespace
namespace toml_parser
{
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)
CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) : m_ssString{rssString}
{
return "";
CheckForInvalidUTF8Bytes();
CheckForInvalidUTF8Sequences();
}
size_t offset{0};
for (std::size_t i = 0; i < n - 1; ++i)
void CCharacterReaderUTF8::Feed(const std::string& rssString)
{
offset += GetLengthOfNextCharacter(offset);
if (m_nDataLength <= m_nCursor + offset)
m_ssString = rssString;
m_nCursor = 0;
CheckForInvalidUTF8Bytes();
CheckForInvalidUTF8Sequences();
}
void CCharacterReaderUTF8::Reset()
{
m_ssString.clear();
m_nCursor = 0;
}
std::string CCharacterReaderUTF8::Peek(size_t nSkip /*= 0*/, size_t nAmount /*= 1*/) const
{
if (IsEOF())
return {};
size_t nOffset = 0;
for (size_t n = 0; n < nSkip; ++n)
{
return "";
nOffset += GetLengthOfNextCharacter(nOffset);
if (m_ssString.size() <= m_nCursor + nOffset)
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)
std::string ssCharacters;
for (size_t n = 0; n < nAmount; n++)
{
if (delimiter == character)
ssCharacters += GetNextCharacter(nOffset);
nOffset += GetLengthOfNextCharacter(nOffset);
}
return ssCharacters;
}
std::string CCharacterReaderUTF8::PeekUntil(const std::vector<std::string>& lstCollection) const
{
size_t nOffset = 0;
bool bFound = false;
std::string accumulation;
while (!bFound && m_ssString.size() > m_nCursor + nOffset)
{
std::string ssCharacter = GetNextCharacter(nOffset);
nOffset += GetLengthOfNextCharacter(nOffset);
for (const auto& delimiter : lstCollection)
{
found = true;
if (delimiter == ssCharacter)
bFound = true;
}
if (!bFound)
accumulation += ssCharacter;
}
return accumulation;
}
std::string CCharacterReaderUTF8::Consume(size_t nSkip /*= 0*/, size_t nAmount /*= 1*/)
{
if (IsEOF())
return {};
for (size_t n = 0; n < nSkip; ++n)
{
size_t nLength = GetLengthOfNextCharacter();
m_nCursor += nLength;
if (!nLength || m_ssString.size() < m_nCursor)
return {};
}
std::string ssCharacters;
for (size_t n = 0; n < nAmount; n++)
{
ssCharacters += GetNextCharacter();
m_nCursor += GetLengthOfNextCharacter();
}
return ssCharacters;
}
std::string CCharacterReaderUTF8::ConsumeUntil(const std::vector<std::string>& lstCollection)
{
size_t nOffset = 0;
bool bFound = false;
std::string accumulation;
while (!bFound && m_ssString.size() > m_nCursor + nOffset)
{
std::string ssCharacter = GetNextCharacter(nOffset);
nOffset += GetLengthOfNextCharacter(nOffset);
for (const auto& delimiter : lstCollection)
{
if (delimiter == ssCharacter)
{
bFound = true;
}
}
if (!bFound)
{
accumulation += ssCharacter;
}
else
{
nOffset -= ssCharacter.size();
}
}
if (!found)
{
accumulation += character;
}
m_nCursor += nOffset;
return accumulation;
}
return accumulation;
}
std::string CCharacterReaderUTF8::Consume()
{
if (IsEOF())
bool CCharacterReaderUTF8::IsEOF() const
{
return "";
return m_ssString.size() < m_nCursor + 1;
}
std::string character{GetNextCharacter()};
m_nCursor += GetLengthOfNextCharacter();
return character;
}
std::string CCharacterReaderUTF8::Consume(std::size_t n)
{
if (n < 1)
void CCharacterReaderUTF8::SetBookmark()
{
return "";
m_nBookmark = m_nCursor;
}
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 CCharacterReaderUTF8::StringFromBookmark() const
{
std::string character = GetNextCharacter(offset);
offset += GetLengthOfNextCharacter(offset);
for (const auto& delimiter : lstCollection)
return m_ssString.substr(m_nBookmark, (m_nCursor > m_nBookmark) ? m_nCursor - m_nBookmark : 0);
}
void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const
{
const unsigned char invalidByteC0{0xC0};
const unsigned char invalidByteC1{0xC1};
const unsigned char lowerBoundInvalidRegion{0xF5};
for (size_t i = 0; i < m_ssString.size(); ++i)
{
if (delimiter == character)
unsigned char uc = m_ssString[i];
if (uc == invalidByteC0 || uc == invalidByteC1 || uc >= lowerBoundInvalidRegion)
{
found = true;
std::stringstream message;
message << "Invalid byte " << std::hex << uc << std::dec << " at position " << i << "\n";
throw XTOMLParseException(message.str());
}
}
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::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;
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)
auto fnCheckByteInNeutralState = [&uiIndex, &eCurrentState](unsigned char uc)
{
std::stringstream message;
message << "Invalid byte " << std::hex << uc << std::dec << " at position " << i << "\n";
throw XTOMLParseException(message.str());
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 (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());
}
}
}
}
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)
if (eCurrentState != EState::state_neutral)
{
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";
sstreamMessage << "Unfinished character at the end of file\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())
std::string CCharacterReaderUTF8::GetNextCharacter(size_t nOffset /*= 0*/) const
{
return "";
if (IsEOF())
return {};
return {m_ssString, m_nCursor + nOffset, GetLengthOfNextCharacter(nOffset)};
}
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)
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter(size_t nOffset) const
{
ret = 1;
if (IsEOF())
return 0;
uint8_t ui = static_cast<uint8_t>(m_ssString[m_nCursor + nOffset]);
int32_t uiRet = 0;
if ((ui & m_uiOneByteCheckMask) == m_OneByteCheckValue)
{
uiRet = 1;
}
else if ((ui & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
{
uiRet = 4;
}
else if ((ui & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
{
uiRet = 3;
}
else if ((ui & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
{
uiRet = 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 uiRet;
}
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;
}
} // namespace toml_parser

View File

@@ -6,122 +6,141 @@
#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
/// The TOML parser namespace
namespace toml_parser
{
public:
/**
* @brief Standard constructor for empty character reader
* @brief Reads a given input string, interprets bytes as UTF-8 and returns UTF-8 characters or strings in order of demand.
*/
CCharacterReaderUTF8();
class CCharacterReaderUTF8
{
public:
/**
* @brief Standard constructor for empty character reader.
*/
CCharacterReaderUTF8() = default;
/**
* @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 Constructs a character reader from a given string.
* @param[in] rssString UTF-8 input string.
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
* @throw XTOMLParseException 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 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 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 Get the next n-th UTF-8 character without advancing the cursor.
* @param[in] nSkip Amount of characters to skip.
* @param[in] nAmount The amount of characters to read.
* @return Returns one or more characters starting at the requested position after the current cursor or an empty string
* if the requested position is behind the last character.
*/
std::string Peek(size_t nSkip = 0, size_t nAmount = 1) const;
/**
* @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 Get 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) const;
/**
* @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 Get the next n-th UTF-8 character and advancing the cursor by n.
* @param[in] nSkip Amount of characters to skip.
* @param[in] nAmount The amount of characters to read.
* @return Returns one or more characters starting at the requested position after the current cursor or an empty string
* if the requested position is behind the last character.
*/
std::string Consume(size_t nSkip = 0, size_t nAmount = 1);
/**
* @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 Get 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 (excluding) the first occurrence of one of the given characters.
*/
std::string ConsumeUntil(const std::vector<std::string>& lstCollection);
/**
* @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 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;
/**
* @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 Set the bookmark at the current sursor position. This can be used to read the raw string.
*/
void SetBookmark();
/**
* @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;
/**
* @brief Get string from bookmark.
* @return Returns the raw string from the last bookmark position (if set), or from the beginning of the string (if not
* set).
*/
std::string StringFromBookmark() const;
private:
void CheckForInvalidUTF8Bytes() const;
private:
/**
* @brief Check for an out-of-bound UTF8 byte.
* @throw XTOMLParseException Throws an InvalidByteException if the input contains for UTF-8 invalid bytes.
*/
void CheckForInvalidUTF8Bytes() const;
void CheckForInvalidUTF8Sequences() const;
/**
* @brief Check for an invalid UTF8 sequence.
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
*/
void CheckForInvalidUTF8Sequences() const;
std::string GetNextCharacter();
/**
* @brief Get the next character using an nOffset of nOffset bytes.
* @attention This function doesn't protect for an invalid nOffset when reading multi-byte characters.
* @remarks In UTF8 one character could contain up to 4 bytes.
* @param[in] nOffset The nOffset in bytes to the next character to read.
* @return The character from the string or an empty string if there are no more characters in the string.
*/
std::string GetNextCharacter(size_t nOffset = 0) const;
std::string GetNextCharacter(std::size_t offset);
/**
* @brief Get the length of the next character in bytes.
* @remarks In UTF8 one character could contain up to 4 bytes.
* @param[in] nOffset The nOffset in bytes to the next character to read.
* @return the length of the character in bytes or zero when there are no more characters in the string.
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
*/
size_t GetLengthOfNextCharacter(size_t nOffset = 0) const;
size_t GetLengthOfNextCharacter() 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
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;
};
std::string m_ssString; ///< String containing the characters to acquire.
size_t m_nCursor = 0; ///< Current position pointing to the next character.
size_t m_nBookmark = 0; ///< Bookmark cursor position to use to get raw string chunks.
};
} // namespace toml_parser
#endif // CHARACTER_READER_UTF_8_H

View File

@@ -3,18 +3,31 @@
#include <interfaces/toml.h>
except XTOMLParseException : public sdv::toml::XTOMLParseException
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Constructor
* @brief Extended exception for the TOML parser.
*/
XTOMLParseException(const std::string& rss) { ssMessage = rss; };
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(); }
};
/**
* @brief Return the explanatory string.
* @return The descriptive string.
*/
virtual const char* what() const noexcept override
{
return ssMessage.c_str();
}
};
} // namespace toml_parser
#endif // !defined CONFIG_EXCEPTION_H

File diff suppressed because it is too large Load Diff

View File

@@ -3,224 +3,375 @@
#include <algorithm>
#include <stack>
#include <list>
#include <functional>
#include "character_reader_utf_8.h"
#include "lexer_toml_token.h"
/**
* @brief Tokenizes the output of a character reader in regard of the TOML format and returns tokens in order on demand
*/
class CLexerTOML
/// The TOML parser namespace
namespace toml_parser
{
public:
/**
* @brief Enum for all possible token categories
* @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
* following ranges can be identified:
* - code before the node, not belonging to another node
* - code before the node with comments belonging to the node
* - the first part of the node
* - the in between part, which might contain other nodes (in case of an inline node collection)
* - the second part of the node finishing the node (in case of an inline node collection)
* - code behind the node with comments belonging to the node
* - code behind the node, not belonging to another node (this is the code until the end of the node list)
*/
enum class ETokenCategory : uint8_t
class CNodeTokenRange
{
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
public:
/**
* @brief Initialize the range with the first initial token. Any token before that belong to the token range of the previous
* node.
* @param[in] rInitialToken The initial token that defines/starts the range.
*/
CNodeTokenRange(const CToken& rInitialToken);
/**
* @brief Construct a node range with the main node token range.
* @param[in] rrangeNodeMain Reference to the token range holding the main node tokens.
*/
CNodeTokenRange(const CTokenRange& rrangeNodeMain);
/**
* @brief Set the token identifying the area before the extended node. Will start before or at and end at the extended node
* range.
* @param[in] rTokenBegin Reference to the token identifying the begin of the lines before the extended node range.
* @remarks Setting the begin will not change the extended node range. If starting within the extended node range, the begin
* of the extended node range determines the begin of the lines before.
*/
void LinesBeforeNode(const CToken& rTokenBegin);
/**
* @brief Get the lines before range, being located before the extended node range.
* @return The token range with begin and end token of the lines before. If identical, there are no lines before.
*/
CTokenRange LinesBeforeNode() const;
/**
* @brief Set the token range identifying the extended node range. Will start before or at and end behind or at the main
* node ranges (incl. the finish range).
* @param[in] rRange Token range holding the begin and end of the extended node range.
* @remarks If the range starts or ends within the main node range, the extended node range is adapted to the main node
* range. If the lines before or behind the node fall within the extended node range, the lines will be adapted to the
* extended node range.
*/
void ExtendedNode(const CTokenRange& rRange);
/**
* @brief Get the extended node range. The range includes the main node ranges (including the node finish range).
* @return The token range with begin and end token of the extended node range.
*/
CTokenRange ExtendedNode() const;
/**
* @brief Get the node comments part before the node definition, which is the difference between the beginning of the
* extended node and the main node.
* @return The token range with begin and end token of the node comments range.
*/
CTokenRange NodeCommentsBefore() const;
/**
* @brief Set the main node range.
* @param[in] rRange Token range holding the begin and end of the main node range.
* @remarks If the main node finish range starts within or before the main node range, the main node finish range will be
* updated. If the extended node range does not include the main range completely (incl. finish node range), the extended
* node range will be updated.
*/
void NodeMain(const CTokenRange& rRange);
/**
* @brief Get the main node range.
* @return The token range with begin and end token of the main node range.
*/
CTokenRange NodeMain() const;
/**
* @brief Set the main node finish range (for inline tables and arrays).
* @param[in] rRange Token range holding the begin and end of the main node finish range.
* @remarks If the main node finish range starts within or before the main node range, the main node finish range will be
* updated. If the extended node range does not include the main range completely (incl. finish node range), the extended
* node range will be updated.
*/
void NodeMainFinish(const CTokenRange& rRange);
/**
* @brief Get the main node finish range.
* @return The token range with begin and end token of the main node finish range.
*/
CTokenRange NodeMainFinish() const;
/**
* @brief Get the node comments part behind the node definition, which is the difference between the end of the
* main node and the end of the extended node.
* @return The token range with begin and end token of the node comments range.
*/
CTokenRange NodeCommentsBehind() const;
/**
* @brief Set the token identifying the area behind the extended node. Will start at and end behind or at the extended node
* range.
* @param[in] rTokenEnd Reference to the token identifying the end of the lines behind the extended node range.
* @remarks Setting the end will not change the extended node range. If ending within the extended node range, the end
* of the extended node range determines the end of the lines before.
*/
void LinesBehindNode(const CToken& rTokenEnd);
/**
* @brief Get the lines behind range, being located behind the extended node range.
* @return The token range with begin and end token of the lines behind. If identical, there are no lines behind.
*/
CTokenRange LinesBehindNode() const;
private:
std::reference_wrapper<const CToken> m_refBeforeNodeBegin; ///< The start of the lines before the extended node (not
///< belonging to any node).
CTokenRange m_rangeExtendedNode; ///< The extended node including comments before and behind
///< the node.
CTokenRange m_rangeNodeMain; ///< The node tokens or for inline tables and arrays the
///< opening part of the node.
CTokenRange m_rangeNodeFinish; ///< For inline tables and arrays, the node tokens for closing the node.
std::reference_wrapper<const CToken> m_refBehindNodeEnd; ///< The end of the lines behind the extended node (not
///< belonging to any node).
};
/**
* @brief Contains lexed information for the parser
* @brief Tokenizes the output of a character reader using TOML v1.0: https://toml.io/en/v1.0.0
*/
struct SToken
class CLexer
{
public:
/**
* @brief Default constructor
*/
SToken() = default;
CLexer() = 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
* @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
* @param[in] bValueOnly When set, the lexer should treat the string as a value assignment.
*/
explicit SToken(ETokenCategory category) : eCategory(category)
{}
CLexer(const std::string& rssString, bool bValueOnly = false);
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 Feed the lexer with the given string. This will replace a previous lexing result.
* @param[in] rssString UTF-8 input string.
* @param[in] bValueOnly When set, the lexer should treat the string as a value assignment.
*/
void Feed(const std::string& rssString, bool bValueOnly = false);
/**
* @brief Reset the lexer cursor position.
*/
void Reset();
/**
* @brief Navigation modes supported by the lexer.
*/
enum class ENavigationMode
{
skip_comments_and_whitespace = 1, ///< Skip comments and whitespace during navigation (default)
do_not_skip_anything = 2, ///< Do not skip anything during navigation.
};
/**
* @brief Get the current navigation mode.
* @return The current naviation mode.
*/
ENavigationMode NavigationMode() const;
/**
* @brief Set the navigation mode.
* @param[in] eMode The mode to be used for navigation.
*/
void NavigationMode(ENavigationMode eMode);
/**
* @brief Gets the n-th token after the current cursor without advancing the cursor
* @remarks Whitespace and comments are skipped.
* @param[in] nSkip Skip the amount of tokens.
* @return Returns smart pointer to the token in the token list or an empty pointer.
*/
const CToken& Peek(size_t nSkip = 0) const;
/**
* @brief Gets the n-th token after the current cursor and advancing the cursor by n
* @remarks Whitespace and comments are skipped.
* @param[in] nSkip Skip the amount of tokens.
* @return Returns smart pointer to the token in the token list or an empty pointer.
*/
const CToken& Consume(size_t nSkip = 0);
/**
* @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;
/**
* @brief Check and extend the provided boundaries of a given token range to include white space and comments which
* obviously belong to the token range and check for additional tokens when reaching the end of the token list.
* @param[in, out] rTokenRange Reference to the node token range being updated with extended boundaries.
*/
void SmartExtendNodeRange(CNodeTokenRange& rTokenRange) const;
private:
/**
* @brief Run through the string and generate tokens.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
*/
void GenerateTokens(CCharacterReaderUTF8& rReader);
/**
* @brief Read a quoted key. A quoted key accepts basic strings as keys surrounded by double quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a basic quoted key or an empty token if the token hasn't been found.
*/
CToken ReadBasicQuotedKey(CCharacterReaderUTF8& rReader) const;
/**
* @brief Read a literal quoted key. A literal quoted key accepts basic strings as keys surrounded by single quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a literal quoted key or an empty token if the token hasn't been found.
*/
CToken ReadLiteralQuotedKey(CCharacterReaderUTF8& rReader) const;
/**
* @brief Read a bare key. A bare key may only contain ASCII letters, ASCII digits, underscores and dashes (A-Za-z0-9_-).
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a bare key or an empty token if the token hasn't been found.
*/
CToken ReadBareKey(CCharacterReaderUTF8& rReader) const;
/**
* @brief Read a basic string. A basic string may contain any unicode character. Some characters need to be escaped. The
* basis string is surrounded by double quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a basic string or an empty token if the token hasn't been found.
*/
CToken ReadBasicString(CCharacterReaderUTF8& rReader);
/**
* @brief Read a multi-line basic string. A basic string may contain any unicode character. Some characters need to be
* escaped. The multi-line basis string is surrounded by three double quotes before and behind the string.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a multi-line basic string or an empty token if the token hasn't been found.
*/
CToken ReadBasicMultilineString(CCharacterReaderUTF8& rReader);
/**
* @brief Read a literal string. A literal string may contain any unicode character, but does not support escaped
* characters. The literal string is surrounded by single quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a literal string or an empty token if the token hasn't been found.
*/
CToken ReadLiteralString(CCharacterReaderUTF8& rReader);
/**
* @brief Read a multi-line literal string. A literal string may contain any unicode character, but does not support escaped
* characters. The multi-line literal string is surrounded by three single quotes before and behind the string.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a multi-line literal string or an empty token if the token hasn't been found.
*/
CToken ReadLiteralMultilineString(CCharacterReaderUTF8& rReader);
/**
* @brief Read the integer. An integer is number optionally preceded by a sign and possible defined as hexadecimal
* number (preceded by 0x), octal number (preceded by 0o) or binary number (preceeeded by 0b). Numbers can be split with
* underscores.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for an integer number or an empty token if the token hasn't been found.
*/
CToken ReadInteger(CCharacterReaderUTF8& rReader);
/**
* @brief Read the floating point number. A floating point number follows the rules defined in IEEE 754 binary64 and consist
* of a integer part followed by a fractional part (separated by a dot). An sign can be preceded and an exponential part
* can be succeeded (separated from the fractional part with the 'e'). The number can be split with underscores. Infinite
* and not-a-number are also supported (as defined in the IEEE 754).
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a floating point number or an empty token if the token hasn't been found.
*/
CToken ReadFloat(CCharacterReaderUTF8& rReader);
/**
* @brief Read the boolean. A boolean has the value 'true' or 'false'.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a boolean or an empty token if the token hasn't been found.
*/
CToken ReadBool(CCharacterReaderUTF8& rReader);
/**
* @brief Get the whitespace. Whitespace is a tab or space.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for whitespace or an empty token if the token hasn't been found.
*/
CToken ReadWhitespace(CCharacterReaderUTF8& rReader) const;
/**
* @brief Get the syntax element. A syntax element identifies tables, arrays, new-lines, separators and
* assignments: '[]{},.=\\r\\n'.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a syntax element or an empty token if the token hasn't been found.
*/
CToken ReadSyntaxElement(CCharacterReaderUTF8& rReader);
/**
* @brief Read the comment. A comment is identified by a hash '#' and includes the rest of the line.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for comment or an empty token if the token hasn't been found.
*/
static CToken ReadComment(CCharacterReaderUTF8& rReader);
/**
* @brief Read the unknown sequence (not represented by defined tokens).
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for an unknown sequence or an empty token if the reader has reached EOF.
*/
CToken ReadUnknownSequence(CCharacterReaderUTF8& rReader);
/**
* @brief In case of an escape, return the interpretation of the character following.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return The unescaped character.
*/
static std::string Unescape(CCharacterReaderUTF8& rReader);
/**
* @brief Generic implementation of escaped unicode character interpretation.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @param[in] nDigits The amount of digits the unicode character consist of.
* @return The interpreted unicode character.
*/
static std::string EscapedUnicodeCharacterToUTF8(CCharacterReaderUTF8& rReader, size_t nDigits);
ENavigationMode m_eNavMode = ENavigationMode::skip_comments_and_whitespace; ///< The current navigation mode.
TTokenList m_lstTokens; ///< List of tokens.
TTokenListIterator m_itCursor{m_lstTokens.end()}; ///< Current position within token list.
/**
* @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
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
};
/**
* @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
};
} // namespace toml_parser
#endif // LEXER_TOML_H

View File

@@ -0,0 +1,552 @@
#include "lexer_toml_token.h"
#include <sstream>
/// The TOML parser namespace
namespace toml_parser
{
CToken::CToken()
{}
CToken::CToken(const TTokenList& rTokenList, EBoundary eBoundary)
{
switch (eBoundary)
{
case EBoundary::lower_boundary:
m_optTokenList = rTokenList;
// NOTE: No iterator assignment.
break;
case EBoundary::upper_boundary:
m_optTokenList = rTokenList;
m_optLocation = rTokenList.end();
break;
case EBoundary::no_boundary:
default:
// NOTE: No tokenlist assignment.
break;
}
}
CToken::CToken(ETokenCategory eCategory) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
case ETokenCategory::token_integer:
case ETokenCategory::token_float:
case ETokenCategory::token_boolean:
case ETokenCategory::token_time_local:
case ETokenCategory::token_date_time_offset:
case ETokenCategory::token_date_time_local:
case ETokenCategory::token_date_local:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for token without ssContent: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
default:
break;
}
}
CToken::CToken(ETokenCategory eCategory, const std::string& rssContent,
ETokenStringType eStringType /*= ETokenStringType::not_specified*/) :
m_eCategory(eCategory), m_eStringType(eStringType)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(rssContent);
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for string token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, int64_t iContent) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_integer:
m_iContentInteger = iContent;
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for integer token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, double dContent) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_float:
m_dContentFloatingpoint = dContent;
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token categoryfor floating point token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, bool bContent) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_boolean:
m_bContentBoolean = bContent;
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for boolean token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, const sdv::toml::XTOMLParseException& rexcept) :
CToken(eCategory, std::string(rexcept.what()))
{}
CToken::CToken(const CToken& rToken) :
m_ssRawString(rToken.m_ssRawString), m_optTokenList(rToken.m_optTokenList), m_optLocation(rToken.m_optLocation),
m_eCategory(rToken.m_eCategory), m_eStringType(rToken.m_eStringType)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(rToken.m_ssContentString);
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
}
CToken::CToken(CToken&& rToken) :
m_ssRawString(std::move(rToken.m_ssRawString)), m_optTokenList(std::move(rToken.m_optTokenList)),
m_optLocation(rToken.m_optLocation), m_eCategory(rToken.m_eCategory), m_eStringType(rToken.m_eStringType)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(std::move(rToken.m_ssContentString));
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
// Clear the ssContent
rToken.~CToken();
rToken.m_eCategory = ETokenCategory::token_none;
rToken.m_eStringType = ETokenStringType::not_specified;
}
CToken::~CToken()
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
m_ssContentString.~basic_string<char>();
break;
default:
break;
}
}
CToken& CToken::operator=(const CToken& rToken)
{
// Clear current ssContent.
this->~CToken();
// Copy new ssContent
m_ssRawString = rToken.m_ssRawString;
m_optTokenList = rToken.m_optTokenList;
m_optLocation = rToken.m_optLocation;
m_eCategory = rToken.m_eCategory;
m_eStringType = rToken.m_eStringType;
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(rToken.m_ssContentString);
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
return *this;
}
CToken& CToken::operator=(CToken&& rToken)
{
// Clear current ssContent.
this->~CToken();
// Copy new ssContent
switch (rToken.m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(std::move(rToken.m_ssContentString));
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
m_ssRawString = std::move(rToken.m_ssRawString);
m_optTokenList = std::move(rToken.m_optTokenList);
m_optLocation = std::move(rToken.m_optLocation);
m_eCategory = rToken.m_eCategory;
m_eStringType = rToken.m_eStringType;
// Clear origional ssContent
rToken.~CToken();
rToken.m_eCategory = ETokenCategory::token_none;
rToken.m_eStringType = ETokenStringType::not_specified;
return *this;
}
bool CToken::operator==(const CToken& rToken) const
{
if (m_optLocation && rToken.m_optLocation)
return *m_optLocation == *rToken.m_optLocation;
if (m_eCategory != rToken.m_eCategory)
return false;
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
return m_ssContentString == rToken.m_ssContentString;
case ETokenCategory::token_integer:
return m_iContentInteger == rToken.m_iContentInteger;
case ETokenCategory::token_float:
return m_dContentFloatingpoint == rToken.m_dContentFloatingpoint;
case ETokenCategory::token_boolean:
return m_bContentBoolean == rToken.m_bContentBoolean;
default:
return true; // No value to compare
}
}
bool CToken::operator!=(const CToken& rToken) const
{
if (m_optLocation && rToken.m_optLocation)
return *m_optLocation != *rToken.m_optLocation;
if (m_eCategory != rToken.m_eCategory)
return true;
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
return m_ssContentString != rToken.m_ssContentString;
case ETokenCategory::token_integer:
return m_iContentInteger != rToken.m_iContentInteger;
case ETokenCategory::token_float:
return m_dContentFloatingpoint != rToken.m_dContentFloatingpoint;
case ETokenCategory::token_boolean:
return m_bContentBoolean != rToken.m_bContentBoolean;
default:
return false; // No value to compare
}
}
CToken::operator bool() const
{
return m_eCategory != ETokenCategory::token_none;
}
const CToken& CToken::Next(size_t nSkip /*= 0*/) const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
const TTokenListIterator& ritToken = *m_optLocation;
TTokenList::const_iterator ritToken2 = ritToken;
const TTokenList& rTokenList = *m_optTokenList;
if (ritToken2 == rTokenList.end()) return m_optTokenList->get().tokenEnd;
size_t nLocalSkip = nSkip + 1;
do
{
++ritToken2;
if (ritToken2 == rTokenList.end()) return m_optTokenList->get().tokenEnd;
} while (--nLocalSkip);
return *ritToken2;
}
const CToken& CToken::Prev(size_t nSkip /*= 0*/) const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
const TTokenListIterator& ritToken = *m_optLocation;
TTokenList::const_iterator ritToken2 = ritToken;
const TTokenList& rTokenList = *m_optTokenList;
size_t nLocalSkip = nSkip + 1;
do
{
if (ritToken2 == rTokenList.begin()) return m_optTokenList->get().tokenReverseEnd;
--ritToken2;
} while (--nLocalSkip);
return *ritToken2;
}
const CToken& CToken::JumpToBegin() const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
return *(m_optTokenList->get().begin());
}
const CToken& CToken::JumpToEnd() const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
return m_optTokenList->get().tokenEnd;
}
ETokenCategory CToken::Category() const
{
return m_eCategory;
}
ETokenStringType CToken::StringType() const
{
return m_eStringType;
}
std::string CToken::StringValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
return m_ssContentString;
default:
return {};
}
}
int64_t CToken::IntegerValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_integer:
return m_iContentInteger;
default:
return 0;
}
}
double CToken::FloatValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_float:
return m_dContentFloatingpoint;
default:
return 0.0;
}
}
bool CToken::BooleanValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_boolean:
return m_bContentBoolean;
default:
return false;
}
}
const std::string& CToken::RawString() const
{
return m_ssRawString;
}
uint32_t CToken::TokenIndex() const
{
if (m_eCategory == ETokenCategory::token_none)
return 0xffffffff;
return m_uiIndex;
}
void CToken::RawDataInfo(const std::string& rssString, const TTokenList& rTokenList, const TTokenListIterator& ritLocation)
{
m_ssRawString = rssString;
m_optTokenList = rTokenList;
m_optLocation = std::cref(ritLocation);
}
const std::optional<TTokenListIterator>& CToken::Location() const
{
return m_optLocation;
}
const std::optional<std::reference_wrapper<const TTokenList>>& CToken::TokenList() const
{
return m_optTokenList;
}
uint32_t CToken::CreateIndex()
{
static uint32_t uiIndexCounter = 0;
return uiIndexCounter++;
}
CTokenList::CTokenList() :
tokenReverseEnd(*this, CToken::EBoundary::lower_boundary), tokenEnd(*this, CToken::EBoundary::upper_boundary)
{}
CTokenRange::CTokenRange(const CToken& rTokenBegin) : m_rTokenBegin(rTokenBegin), m_rTokenEnd(rTokenBegin.JumpToEnd())
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
}
CTokenRange::CTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd) :
m_rTokenBegin(rTokenBegin), m_rTokenEnd(rTokenEnd)
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
if (!rTokenEnd.TokenList())
throw XTOMLParseException("The end token provided to the token range doesn't have a token list assigned.");
if (&(*rTokenBegin.TokenList()).get() != &(*rTokenEnd.TokenList()).get())
throw XTOMLParseException("The end token of the range is not from the same token list as the begin token.");
}
void CTokenRange::ReassignBeginToken(const CToken& rTokenBegin)
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
m_rTokenBegin = rTokenBegin;
m_rTokenEnd = rTokenBegin.JumpToEnd();
}
void CTokenRange::ReassignTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd)
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
if (!rTokenEnd.TokenList())
throw XTOMLParseException("The end token provided to the token range doesn't have a token list assigned.");
if (&(*rTokenBegin.TokenList()).get() != &(*rTokenEnd.TokenList()).get())
throw XTOMLParseException("The end token of the range is not from the same token list as the begin token.");
m_rTokenBegin = rTokenBegin;
m_rTokenEnd = rTokenEnd;
}
void CTokenRange::AssignEndToken(const CToken& rTokenEnd, bool bIncludeEnd /*= true*/)
{
// Check whether the end token corresponds to the same list of the begin token.
if (!rTokenEnd.TokenList())
throw XTOMLParseException("The end token provided to the token range doesn't have a token list assigned.");
if (&(*m_rTokenBegin.get().TokenList()).get() != &(*rTokenEnd.TokenList()).get())
throw XTOMLParseException("The end token of the range is not from the same token list as the begin token.");
const CToken& rTokenEndCorrected = bIncludeEnd ? rTokenEnd.Next() : rTokenEnd;
m_rTokenEnd = rTokenEndCorrected;
}
const CToken& CTokenRange::Begin() const
{
return m_rTokenBegin.get();
}
const CToken& CTokenRange::End() const
{
return m_rTokenEnd.get();
}
std::list<CToken> CTokenRange::TokenListSLice() const
{
if (!m_rTokenBegin.get().Location() || !m_rTokenEnd.get().Location())
return {};
return std::list<CToken>(*m_rTokenBegin.get().Location(), *m_rTokenEnd.get().Location());
}
CTokenRange::operator bool() const
{
return m_rTokenEnd.get() != m_rTokenBegin.get() && m_rTokenBegin.get().TokenIndex() < m_rTokenEnd.get().TokenIndex();
}
} // namespace toml_parser

View File

@@ -0,0 +1,421 @@
#ifndef LEXER_TOML_TOKEN_H
#define LEXER_TOML_TOKEN_H
#include <string>
#include <cstdint>
#include <list>
#include <optional>
#include <memory>
#include "exception.h"
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Enum for all possible token categories
*/
enum class ETokenCategory : uint32_t
{
token_none, ///< Default - not having read anything
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 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_whitespace, ///< Whitespace token
token_comment, ///< Comment token
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 The string type for key and string tokens.
*/
enum ETokenStringType
{
not_specified, ///< Not specified or not applicable
quoted_string, ///< Quoted string
literal_string, ///< Literal string
multi_line_quoted, ///< Multiple lines as a quoted string (only for string tokens)
multi_line_literal, ///< Multiple lines as a literal string (only for string tokens)
};
// Forward declaration
class CLexer;
class CToken;
class CTokenList;
class CTokenRange;
/// Token list type definition
using TTokenList = CTokenList;
/// Token list iterator definition
using TTokenListIterator = std::list<CToken>::const_iterator;
/**
* @brief Contains lexed information for the parser
*/
class CToken
{
// Access to private functions is allowed for the following classes:
friend class CLexer;
friend class CTokenRange;
public:
/**
* @brief Default constructor
*/
CToken();
/**
* @brief Boundary identification.
*/
enum EBoundary
{
no_boundary, ///< The token is not defined a boundary token.
lower_boundary, ///< One token before the first token in the token list.
upper_boundary, ///< One token behind the last token in the token list.
};
/**
* @brief Constructor to construct a boundary token (beginning or end of the token list).
* @param[in] rTokenList Reference to the token list this token resides in.
* @param[in] eBoundary The boundary type to define.
*/
explicit CToken(const TTokenList& rTokenList, EBoundary eBoundary);
/**
* @brief Constructs a new Token object with a given category (can be anything, except key, string, integer, boolean, time
* and error).
* @param[in] eCategory The initial token category value for the token to be constructed.
*/
explicit CToken(ETokenCategory eCategory);
/**
* @brief Constructs a new Token object with a given category (can only be a key, a string or an error).
* @param[in] eCategory The token category value for the token to be constructed.
* @param[in] rssContent Reference to the string value.
* @param[in] eStringType The type of string that should be used.
*/
CToken(ETokenCategory eCategory, const std::string& rssContent,
ETokenStringType eStringType = ETokenStringType::not_specified);
/**
* @brief Constructs a new Token object with a given category (can only be an integer value).
* @param[in] eCategory The token category value for the token to be constructed.
* @param[in] iContent The integer value.
*/
explicit CToken(ETokenCategory eCategory, int64_t iContent);
/**
* @brief Constructs a new Token object with a given category (can only be a floating point value).
* @param[in] eCategory The token category value for the token to be constructed.
* @param[in] dContent The double precision floating point value.
*/
explicit CToken(ETokenCategory eCategory, double dContent);
/**
* @brief Constructs a new Token object with a given category (can only be a boolean value).
* @param[in] eCategory The token category value for the token to be constructed
* @param[in] bContent The boolean value.
*/
explicit CToken(ETokenCategory eCategory, bool bContent);
/**
* @brief Error token.
* @param[in] eCategory The token category value for the token to be constructed (category error or terminated).
* @param[in] rexcept Reference to the exception that was triggered.
*/
CToken(ETokenCategory eCategory, const sdv::toml::XTOMLParseException& rexcept);
/**
* @brief Copy constructor.
* @param[in] rToken Reference to the token to copy from.
*/
CToken(const CToken& rToken);
/**
* @brief Move constructor.
* @param[in] rToken Reference to the token to move from.
*/
CToken(CToken&& rToken);
/**
* @brief Destructor
*/
~CToken();
/**
* @brief Copy assignment operator.
* @param[in] rToken Reference to the token to copy from.
* @return Reference to this token.
*/
CToken& operator=(const CToken& rToken);
/**
* @brief Move constructor.
* @param[in] rToken Reference to the token to move from.
* @return Reference to this token.
*/
CToken& operator=(CToken&& rToken);
/**
* @brief Check whether the supplied token is identical to this token. The tokens are identical if the token iterators of
* the token list are identical, or if they don't have a token iterator, have identical category and value.
* @param[in] rToken Reference to the token to use for the comparison.
* @return Returns whether the supplied token is identical.
*/
bool operator==(const CToken& rToken) const;
/**
* @brief Check whether the supplied token is not identical to this token. The tokens are identical if the token iterators
* of the token list are identical, or if they don't have a token iterator, have identical category and value.
* @param[in] rToken Reference to the token to use for the comparison.
* @return Returns whether the supplied token is not identical.
*/
bool operator!=(const CToken& rToken) const;
/**
* @brief Checks whether the token is initialized with any token other then ETokenCategory::token_none.
* @return Returns whether the token is initialized.
*/
operator bool() const;
/**
* @brief Quick navigation through the token list. Get the next token in the token list.
* @param[in] nSkip The amount of tokens to skip while getting the next token.
* @return Returns the next token or an none-token when the end of the token list has been reached or no tokenlist
* assignment has been made for this token.
*/
const CToken& Next(size_t nSkip = 0) const;
/**
* @brief Quick navigation through the token list. Get the previous token in the token list.
* @param[in] nSkip The amount of tokens to skip while getting the previous token.
* @return Returns the previous token or an none-token when the begin of the token list has been reached or no tokenlist
* assignment has been made for this token.
*/
const CToken& Prev(size_t nSkip = 0) const;
/**
* @brief Quick navigation through the token list to the begin of the token list.
* @return Returns the first token of the token list.
*/
const CToken& JumpToBegin() const;
/**
* @brief Quick navigation through the token list to the end of the token list.
* @remarks The last token of the token list will return "false" when tested. It can be used to iterate backwards through
* the token list.
* @return Returns the one beyond the last token of the token list.
*/
const CToken& JumpToEnd() const;
/**
* @brief Return the current category.
* @return The token category.
*/
ETokenCategory Category() const;
/**
* @brief Get the token string type.
* @return Returns the string type for the key or string token. Returns ETokenStringType::not_specified when there is no
* string type defined or the token isn't string based.
*/
ETokenStringType StringType() const;
/**
* @brief Get the string value. Will be filled for keys, strings and errors.
* @return The string value or empty string when the category type is invalid.
*/
std::string StringValue() const;
/**
* @brief Get the integer value. Will be filled for a integer number.
* @return The integer value or zero when the category type is invalid.
*/
int64_t IntegerValue() const;
/**
* @brief Get the floating point number value. Will be filled for a floating point number.
* @return The floating point number value value or 0.0 when the category type is invalid.
*/
double FloatValue() const;
/**
* @brief Get the boolean value. Will be filled for a boolean.
* @return The boolean value or false when the category type is invalid.
*/
bool BooleanValue() const;
/**
* @brief Get the associated raw string.
* @return Reference to the raw string.
*/
const std::string& RawString() const;
/**
* @brief The token index identifies the token order.
* @return The unique index of this token.
*/
uint32_t TokenIndex() const;
private:
/**
* @brief Set the string chunk from the original TOML belonging to this token and the location within the lexer token list.
* @param[in] rssString Reference to the string containing the "raw" value of the token.
* @param[in] rTokenList Reference to the token list the iterator is referring to.
* @param[in] ritLocation The location in the lexer list.
*/
void RawDataInfo(const std::string& rssString, const TTokenList& rTokenList, const TTokenListIterator& ritLocation);
/**
* @brief Return the location in the lexer token list.
* @return Reference to the iterator location option.
*/
const std::optional<TTokenListIterator>& Location() const;
/**
* @brief Return a reference to the token list this token is located in.
* @return Reference to the token list reference option.
*/
const std::optional<std::reference_wrapper<const TTokenList>>& TokenList() const;
/**
* @brief Create a new index. Indices are used to guarantee correct token order.
* @return The next index number.
*/
static uint32_t CreateIndex();
std::string m_ssRawString; ///< The raw string without interpretation.
std::optional<std::reference_wrapper<const TTokenList>> m_optTokenList; ///< The token list this token is referring to.
std::optional<TTokenListIterator> m_optLocation; ///< Location in the lexer token list.
union
{
std::string m_ssContentString; ///< Token string content (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
};
ETokenCategory m_eCategory = ETokenCategory::token_none; ///< Token category
ETokenStringType m_eStringType = ETokenStringType::not_specified; ///< Token string type for key and string tokens.
uint32_t m_uiIndex = CreateIndex(); ///< Current token index.
};
/**
* @brief Token list with boundary tokens used in navigation.
*/
class CTokenList : public std::list<CToken>
{
public:
/**
* @brief Default constructor.
*/
CTokenList();
CToken tokenReverseEnd; ///< One token before first token in token list. Used to detect backwards iteration boundary.
CToken tokenEnd; ///< One token behind last token in token list. Used to detect forwards iteration boundary.
};
/**
* @brief The token range is used to identify the initial and the final token belonging to a collection. All intermediate
* tokens are then part of the collection.
*/
class CTokenRange
{
public:
/**
* @brief The default constructor is deleted.
*/
CTokenRange() = delete;
/**
* @brief Start a token range using the begin token.
* @remarks The end token is automatically set to one token beyond the last token in the list. This can be changed by
* assigning the end token.
* @param[in] rTokenBegin Reference to the first token in this range.
*/
CTokenRange(const CToken& rTokenBegin);
/**
* @brief Directly assign a token range.
* @remarks If the begin token and the end token are identical, the range doesn't contain any tokens.
* @param[in] rTokenBegin Reference to the first token in this range.
* @param[in] rTokenEnd Reference to one token beyond the last token in this range.
*/
CTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd);
/**
* @brief Reassign the first token in the range. This will set the end token to the one beyond the last token in the list.
* @param[in] rTokenBegin Reference to the first token in this range.
*/
void ReassignBeginToken(const CToken& rTokenBegin);
/**
* @brief Reassign the token range.
* @remarks If the begin token and the end token are identical, the range doesn't contain any tokens.
* @param[in] rTokenBegin Reference to the first token in the range.
* @param[in] rTokenEnd Reference to one token beyond the last token in the range.
*/
void ReassignTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd);
/**
* @brief Assign or overwrite an end token.
* @param[in] rTokenEnd Reference to the last token in this range.
* @param[in] bIncludeEnd When set, the token belongs to the range; if not, the token points past the last range token.
*/
void AssignEndToken(const CToken& rTokenEnd, bool bIncludeEnd = true);
/**
* @brief Get the initial token in the range.
* @return Reference to the smart pointer containing the first token in this range.
*/
const CToken& Begin() const;
/**
* @brief Get one token beyond the final token in the range.
* @remarks The end token can be one token beyond the last token in the list. This token will return 'false' when tested and
* can be used for navigation.
* @return Reference to the smart pointer containing one token beyond the final token in this range.
*/
const CToken& End() const;
/**
* @brief Create a copy of the token list part identified by the start and end token (not including the end token).
* @return The token list containing the slice.
*/
std::list<CToken> TokenListSLice() const;
/**
* @brief Does the token range contain a valid range of at least one token.
* @return Returns whether the token range is valid.
*/
operator bool() const;
private:
std::reference_wrapper<const CToken> m_rTokenBegin; ///< Smart pointer to the first token in the range.
std::reference_wrapper<const CToken> m_rTokenEnd; ///< Smart pointer to one token beyond the last token in the range.
};
} // namespace toml_parser
#endif // !defined LEXER_TOML_TOKEN_H

View File

@@ -0,0 +1,549 @@
#include "miscellaneous.h"
#include "exception.h"
#include <sstream>
#include <limits>
namespace toml_parser
{
std::string EscapedUnicodeCharacterToUTF8(const std::string& rss)
{
// Read the characters
uint32_t uiUTFVal = HexadecimalToDecimal(rss);
// One byte UTF-8 character
if (uiUTFVal < 0x80)
return std::string() + static_cast<char>(uiUTFVal);
// Two byte UTF-8 character
if (uiUTFVal < 0x800)
return std::string() + static_cast<char>(uiUTFVal >> 6 | 0xc0) + static_cast<char>((uiUTFVal & 0b111111) | 0x80);
// Three byte UTF-8 character
if (uiUTFVal < 0x10000)
return std::string() + static_cast<char>(uiUTFVal >> 12 | 0xe0) + static_cast<char>(((uiUTFVal >> 6) & 0b111111) | 0x80)
+ static_cast<char>((uiUTFVal & 0b111111) | 0x80);
// Four byte UTF-8 character
if (uiUTFVal < 0x110000)
{
return std::string() + static_cast<char>(uiUTFVal >> 18 | 0xf0)
+ static_cast<char>(((uiUTFVal >> 12) & 0b111111) | 0x80)
+ static_cast<char>(((uiUTFVal >> 6) & 0b111111) | 0x80) + static_cast<char>((uiUTFVal & 0b111111) | 0x80);
}
std::stringstream sstream;
sstream << "Invalid unicode conversion for hexadecimal number: " << rss;
throw XTOMLParseException(sstream.str());
}
uint32_t HexadecimalToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid hexadecimal number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c >= '0' && c <= '9')
uiVal = uiVal * 16u + static_cast<uint32_t>(c - '0');
else if (c >= 'A' && c <= 'F')
uiVal = uiVal * 16u + static_cast<uint32_t>(c - 'A' + 10u);
else if (c >= 'a' && c <= 'f')
uiVal = uiVal * 16u + static_cast<uint32_t>(c - 'a' + 10u);
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid hexadecimal number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 16u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
uint32_t DecimalToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid decimal number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c >= '0' && c <= '9')
uiVal = uiVal * 10u + static_cast<uint32_t>(c - '0');
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid decimal number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 10u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
uint32_t OctalToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid octal number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c >= '0' && c <= '7')
uiVal = uiVal * 8u + static_cast<uint32_t>(c - '0');
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid octal number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 8u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
uint32_t BinaryToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid binary number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c == '0')
uiVal = uiVal * 2u;
else if (c == '1')
uiVal = uiVal * 2u + static_cast<uint32_t>(c - '0');
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid binary number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 2u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
std::pair<std::string, std::string> SplitNodeKey(const std::string& rssKeyPath)
{
if (rssKeyPath.empty())
return {};
enum class EType
{
normal,
expect_separator,
single_quoted_string,
double_quoted_string,
expect_index,
expect_index_or_end_bracket
} eType = EType::normal;
size_t nPos = 0;
std::string ssFirst;
while (nPos < rssKeyPath.size())
{
char c = rssKeyPath[nPos];
switch (c)
{
case ' ':
case '\t':
// When inside a string, add the whitespace to the key.
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (eType == EType::normal && !ssFirst.empty()) // This is only allowed at the end of a key
eType = EType::expect_separator;
break;
case '\r':
case '\n':
if (eType == EType::normal && !ssFirst.empty()) // This is only allowed at the end of a key
eType = EType::expect_separator;
break;
case '\'':
if (eType == EType::normal)
{
if (!ssFirst.empty())
return {}; // Cannot be placed in the middle of the key
eType = EType::single_quoted_string;
}
else if (eType == EType::single_quoted_string)
eType = EType::expect_separator;
else if (eType == EType::double_quoted_string)
ssFirst += c;
else
return {}; // Invalid usage
break;
case '\"':
if (eType == EType::normal)
{
if (!ssFirst.empty())
return {}; // Cannot be placed in the middle of the key
eType = EType::double_quoted_string;
}
else if (eType == EType::double_quoted_string)
eType = EType::expect_separator;
else if (eType == EType::single_quoted_string)
ssFirst += c;
else
return {}; // Invalid usage
break;
case '\\':
if (eType == EType::single_quoted_string)
{
ssFirst += c;
break;
}
if (eType != EType::double_quoted_string) return {}; // Only allowed for double quoted strings.
nPos++;
if (nPos >= rssKeyPath.size()) return {}; // Ended with escape...
try
{
switch (rssKeyPath[nPos])
{
case 'b': ssFirst += '\b'; break;
case 't': ssFirst += '\t'; break;
case 'n': ssFirst += '\n'; break;
case 'f': ssFirst += '\f'; break;
case 'r': ssFirst += '\r'; break;
case '"': ssFirst += '\"'; break;
case '\\': ssFirst += '\\'; break;
case 'u': ssFirst += EscapedUnicodeCharacterToUTF8(rssKeyPath.substr(nPos + 1, 4)); nPos += 4; break;
case 'U': ssFirst += EscapedUnicodeCharacterToUTF8(rssKeyPath.substr(nPos + 1, 8)); nPos += 8; break;
default:
return {}; // Invalid escape sequence
}
}
catch (const sdv::toml::XTOMLParseException&)
{
return {};
}
break;
case '.':
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (ssFirst.empty()) break; // Ignore an initial dot.
if (eType != EType::normal && eType != EType::expect_separator)
return {}; // Unexpected separator
return std::make_pair(ssFirst, rssKeyPath.substr(nPos + 1));
case '[':
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (eType != EType::normal && eType != EType::expect_separator)
return {}; // Unexpected separator
if (!ssFirst.empty()) // Belongs to second part?
return std::make_pair(ssFirst, rssKeyPath.substr(nPos));
eType = EType::expect_index;
break;
case ']':
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (eType != EType::expect_index_or_end_bracket) // Allowed?
return {};
eType = EType::expect_separator; // Expected a separator to follow
break;
default:
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
ssFirst += c;
else if ((eType == EType::expect_index || eType == EType::expect_index_or_end_bracket) && std::isdigit(c))
{
ssFirst += c;
eType = EType::expect_index_or_end_bracket;
}
// Protect against multi-byte characters (UTF-8)
else if (eType == EType::normal && static_cast<uint8_t>(c) < 127u && (std::isalnum(c) || c == '-' || c == '_'))
ssFirst += c;
else
return {}; // Invalid key character
break;
}
nPos++;
}
// When within quotes, this is an error
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
return {};
// When expecting index and/or end bracket, this is an error
if (eType == EType::expect_index || eType == EType::expect_index_or_end_bracket)
return {};
// When coming here, there is no more character in the string.
return std::make_pair(ssFirst, std::string());
}
std::pair<std::reference_wrapper<const CToken>, CTokenRange> SplitNodeKey(const CTokenRange& rrangeKeyPath)
{
std::reference_wrapper<const CToken> refToken = rrangeKeyPath.Begin();
std::reference_wrapper<const CToken> refTokenFirst = refToken;
enum class EState
{
initial_key_or_index_marker,
dot_or_index_marker_or_end,
key,
index,
index_marker_close,
} eState = EState::initial_key_or_index_marker;
while (refToken.get() != rrangeKeyPath.End())
{
switch (refToken.get().Category())
{
case ETokenCategory::token_whitespace:
case ETokenCategory::token_comment:
case ETokenCategory::token_syntax_new_line:
break;
case ETokenCategory::token_key:
if (eState == EState::initial_key_or_index_marker)
refTokenFirst = refToken;
else if (eState == EState::key)
return std::make_pair(refTokenFirst, CTokenRange(refToken.get(), rrangeKeyPath.End()));
else
throw XTOMLParseException("Internal error: invalid token range, expecting key.");
eState = EState::dot_or_index_marker_or_end;
break;
case ETokenCategory::token_syntax_dot:
if (eState != EState::dot_or_index_marker_or_end)
throw XTOMLParseException("Internal error: invalid token range, expecting dot.");
eState = EState::key;
break;
case ETokenCategory::token_syntax_array_open:
if (eState == EState::initial_key_or_index_marker)
eState = EState::index;
else if (eState == EState::dot_or_index_marker_or_end)
return std::make_pair(refTokenFirst, CTokenRange(refToken, rrangeKeyPath.End()));
else
throw XTOMLParseException("Internal error: invalid token range, unexpected array open.");
break;
case ETokenCategory::token_integer:
if (eState != EState::index)
throw XTOMLParseException("Internal error: invalid token range, unexpected index.");
refTokenFirst = refToken;
eState = EState::index_marker_close;
break;
case ETokenCategory::token_syntax_array_close:
if (eState != EState::index_marker_close)
throw XTOMLParseException("Internal error: invalid token range, unexpected array close.");
eState = EState::dot_or_index_marker_or_end;
break;
default:
throw XTOMLParseException("Internal error: invalid token range, unexpected token.");
}
refToken = refToken.get().Next();
}
// Coming here would mean that there was one key, but nothing more
if (eState != EState::dot_or_index_marker_or_end)
throw XTOMLParseException("Internal error: invalid token range, unexpected end.");
// Return a pair with the first token and an empty token range.
return std::make_pair(refTokenFirst, CTokenRange(refToken.get(), refToken.get()));
}
std::string ExtractKeyName(const std::string& rssKeyPath)
{
// Split the key parh until there is no second part any more.
auto prSplittedKey = SplitNodeKey(rssKeyPath);
while (!prSplittedKey.second.empty())
prSplittedKey = SplitNodeKey(prSplittedKey.second);
return prSplittedKey.first;
}
std::string QuoteText(const std::string& rssText, EQuoteRequest eQuoteRequest /*= EQuoteRequest::smart_key*/)
{
EQuoteRequest eQuoteNeeded = eQuoteRequest;
if (rssText.empty() && eQuoteNeeded == EQuoteRequest::smart_key)
eQuoteNeeded = EQuoteRequest::quoted_text;
std::stringstream sstreamQuotedText;
sstreamQuotedText << "\"";
for (size_t nPos = 0; nPos < rssText.size(); nPos++)
{
uint8_t uiChar = static_cast<uint8_t>(rssText[nPos]);
uint32_t uiUTFChar = 0;
switch (uiChar)
{
case '\'':
if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text &&
eQuoteRequest != EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << '\'';
break; // Single quote character
case '\b':
if (eQuoteRequest == EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
else if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\b";
break; // Escape backspace
case '\t':
if (eQuoteRequest == EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
else if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\t";
break; // Escape tab
case '\n':
if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text &&
eQuoteRequest != EQuoteRequest::multi_line_literal_text)
{
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\n";
}
else
sstreamQuotedText << "\n";
break; // Escape linefeed
case '\f':
if (eQuoteRequest == EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
else if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\f";
break; // Escape form feed
case '\r':
if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text &&
eQuoteRequest != EQuoteRequest::multi_line_literal_text)
{
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\r";
} else
sstreamQuotedText << "\r";
break; // Escape carriage return
case '\"':
if (eQuoteNeeded == EQuoteRequest::smart_key)
eQuoteNeeded = EQuoteRequest::quoted_text;
else if (eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::literal_text;
sstreamQuotedText << "\\\"";
break; // Escape quote
case '\\':
if (eQuoteNeeded == EQuoteRequest::smart_key)
eQuoteNeeded = EQuoteRequest::quoted_text;
else if (eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::literal_text;
sstreamQuotedText << "\\\\";
break; // Escape backslash
default:
// Check for ASCII character
if (uiChar >= 0x20 && uiChar < 0x7f)
{
// Standard ASCII
sstreamQuotedText << static_cast<char>(uiChar);
if (!std::isalnum(static_cast<char>(uiChar)) && uiChar != '-' && uiChar != '_')
{
if (eQuoteNeeded == EQuoteRequest::smart_key || eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
}
break;
}
// Not a standard ASCII character
if (eQuoteNeeded == EQuoteRequest::smart_key || eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
else if (eQuoteNeeded == EQuoteRequest::literal_text && (uiChar < 0x20 || uiChar == 0x7f))
{
// If the character is one of the control characters (< 0x20 || == 0x7f) then must be quoted
eQuoteNeeded = EQuoteRequest::quoted_text;
}
else if (eQuoteNeeded == EQuoteRequest::multi_line_literal_text && (uiChar < 0x20 || uiChar == 0x7f))
{
// If the character is one of the control characters (< 0x20 || == 0x7f) then must be quoted
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
}
// Use UNICODE escape character for the quoted text
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 >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[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 >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[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 >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
}
// Stream the UTF character
if (uiUTFChar <= 0xFFFF)
sstreamQuotedText << "\\u" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << uiUTFChar;
else
sstreamQuotedText << "\\U" << std::uppercase << std::hex << std::setfill('0') << std::setw(8) << uiUTFChar;
break;
}
}
sstreamQuotedText << "\"";
// Return depedent of the needed quotation.
switch (eQuoteNeeded)
{
case EQuoteRequest::smart_key:
return rssText; // No need to change
case EQuoteRequest::literal_text:
return "'" + rssText + "'";
case EQuoteRequest::multi_line_literal_text:
return "'''" + rssText + "'''";
case EQuoteRequest::multi_line_quoted_text:
return "\"\"" + sstreamQuotedText.str() + "\"\"";
case EQuoteRequest::smart_text:
case EQuoteRequest::quoted_text:
default:
return sstreamQuotedText.str();
}
}
} // namespace toml_parser

View File

@@ -0,0 +1,99 @@
#ifndef MISCELLANEOUS_H
#define MISCELLANEOUS_H
#include <string>
#include <cstdint>
#include <cctype>
#include <utility>
#include "lexer_toml_token.h"
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Implementation of hexadecimal number to UTF8 unicode character.
* @param[in] rss Reference to the string providing the hexadecimal unicode number.
* @return The interpreted unicode character.
*/
std::string EscapedUnicodeCharacterToUTF8(const std::string& rss);
/**
* @brief Calculate the interpretation value of a hexadecimal number in a string (characters 0-9a-fA-F).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t HexadecimalToDecimal(const std::string& rss);
/**
* @brief Calculate the interpretation value of a decimal number in a string (characters 0-9).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t DecimalToDecimal(const std::string& rss);
/**
* @brief Calculate the interpretation value of an octal number in a string (characters 0-7).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t OctalToDecimal(const std::string& rss);
/**
* @brief Calculate the interpretation value of a binary number in a string (characters 0-1).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t BinaryToDecimal(const std::string& rss);
/**
* @brief Split the node key in a first node key and the rest of the node key (if available). Separators are the dot, for table
* separation and [] index for array separation.
* @param[in] rssKeyPath Reference to the string containing the key path.
* @return Pair of string containing the first key (unquoted) and the rest of the key string (still quoted) if applicable. If
* no key is available, will be a reference to two empty strings. The dot and the indexing markers will not be part of the
* returned first string.
*/
std::pair<std::string, std::string> SplitNodeKey(const std::string& rssKeyPath);
/**
* @brief Split the node key contained in the token range in a first node key and the rest of the node key (if available).
* Separators are the dot, for table separation and [] index for array separation.
* @param[in] rrangeKeyPath Reference to the token range ccontaining the tokens representing the key path.
* @return Pair of string containing the first key (unquoted) and the rest of the key string (still quoted) if applicable. If
* no key is available, will be a reference to two empty strings. The dot and the indexing markers will not be part of the
* returned token.
*/
std::pair<std::reference_wrapper<const CToken>, CTokenRange> SplitNodeKey(const CTokenRange& rrangeKeyPath);
/**
* @brief Extract the unquoted key name from a key path. The key name is the last node on the key path.
* @remarks Removes quotes, escape characters and index markers from the key name before returning.
* @param[in] rssKeyPath Reference to the string containing the key path.
* @return The unquoted key name.
*/
std::string ExtractKeyName(const std::string& rssKeyPath);
/**
* @brief Quotation request enum for quoting text (or keys).
*/
enum class EQuoteRequest
{
smart_key, ///< Uses quotation markers and escape characters if needed.
smart_text, ///< Use literal markers if possible, use quotation markers if needed.
quoted_text, ///< Will always use quotation markers.
literal_text, ///< Will use literal markers if possible; otherwise uses quotation markers.
multi_line_quoted_text, ///< Multiple lines quoted text (carriage return and newline are not escaped).
multi_line_literal_text, ///< Multiple lines literal text (carriage return and newline are not escaped).
};
/**
* @brief If the provided key contains characters which are not allowed to be used in a bare-key, add quotes to the key and
* use escape characters if applicable.
* @param[in] rssText The text to (optionally) quote.
* @param[in] eQuoteRequest The quotation method requested.
* @return The quoted key (if applicable); otherwise the key without quotes.
*/
std::string QuoteText(const std::string& rssText, EQuoteRequest eQuoteRequest = EQuoteRequest::smart_text);
} // namespace toml_parser
#endif // !defined MISCELLANEOUS_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,380 +1,507 @@
#include "parser_toml.h"
#include <iostream>
#include "miscellaneous.h"
#include "exception.h"
CParserTOML::CParserTOML(const std::string& rssString) : m_lexer(rssString)
/// The TOML parser namespace
namespace toml_parser
{
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
CParser::CParser(const std::string& rssString)
{
// Run through all tokens of the lexer and process the tokens.
while (!m_lexer.IsEnd())
Process(rssString);
}
void CParser::Clear()
{
m_ptrRoot.reset();
m_ptrCurrentCollection.reset();
m_lexer.Reset();
while (!m_stackEnvironment.empty())
m_stackEnvironment.pop();
}
bool CParser::Process(/*in*/ const sdv::u8string& ssContent)
{
// Process the TOML string
Clear();
m_lexer.Feed(ssContent);
// Create the root node.
m_ptrRoot = std::make_shared<CRootTable>(*this);
m_ptrCurrentCollection = m_ptrRoot;
// Nodes available at all?
if (m_lexer.IsEnd()) return true;
// The initial start position for the node token is the begin of the token list.
std::reference_wrapper<const CToken> refStartNodeToken = m_lexer.Peek().JumpToBegin();
CNodeTokenRange rangeRootTokens(refStartNodeToken);
try
{
CLexerTOML::SToken current = m_lexer.Peek();
switch (current.eCategory)
// Run through all tokens of the lexer and process the tokens.
bool bEOF = false; // Explicit test, since the peek could return EOF, but the cursor might not be at the end yet.
while (!bEOF && !m_lexer.IsEnd())
{
case CLexerTOML::ETokenCategory::token_syntax_table_open:
ProcessTable();
// The node has its own token range
CNodeTokenRange rangeNode(refStartNodeToken.get());
const CToken& rCurrent = m_lexer.Peek();
if (!rCurrent)
{
bEOF = true;
break;
}
switch (rCurrent.Category())
{
case ETokenCategory::token_syntax_table_open:
ProcessTable(rangeNode);
break;
case ETokenCategory::token_syntax_table_array_open:
ProcessTableArray(rangeNode);
break;
case ETokenCategory::token_key:
ProcessValueKey(rangeNode);
break;
case ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
break;
case ETokenCategory::token_terminated:
case ETokenCategory::token_error:
throw XTOMLParseException(rCurrent.StringValue());
break;
default:
throw XTOMLParseException("Invalid Syntax; not a Key, Table or Tablearray");
}
// Update the start of the next value range
refStartNodeToken = rangeNode.LinesBehindNode().End();
}
}
catch (const sdv::toml::XTOMLParseException& e)
{
std::cout << e.what() << '\n';
throw;
}
// In case there are no nodes any more, but still comments and whitespace, attach this to the root node.
if (refStartNodeToken.get() != m_lexer.Peek())
{
rangeRootTokens.NodeMain(CTokenRange(refStartNodeToken, refStartNodeToken));
rangeRootTokens.LinesBehindNode(m_lexer.Peek());
m_ptrRoot->UpdateNodeCode(rangeRootTokens);
}
return true;
}
CLexer& CParser::Lexer()
{
return m_lexer;
}
const CNodeCollection& CParser::Root() const
{
auto ptrCollection = m_ptrRoot->Cast<CTable>();
return *ptrCollection.get();
}
CNodeCollection& CParser::Root()
{
auto ptrCollection = m_ptrRoot->Cast<CTable>();
return *ptrCollection.get();
}
std::string CParser::GenerateTOML(const std::string& rssPrefixKey) const
{
return m_ptrRoot->GenerateTOML(rssPrefixKey);
}
void CParser::ProcessTable(CNodeTokenRange& rNodeRange)
{
// Get the table path (table name preceded by parent tables separated with dots).
CTokenRange rangeMain(m_lexer.Consume());
CTokenRange rangeKeyPath = ProcessKeyPath();
const CToken& rToken = m_lexer.Consume();
if (!rToken || rToken.Category() != ETokenCategory::token_syntax_table_close)
throw XTOMLParseException("Invalid Table construct");
rangeMain.AssignEndToken(rToken); // NOTE: This includes only the name and the brackets, not the values.
// Assign the main token range to the node token range and let the lexer determine the extended token range
rNodeRange.NodeMain(rangeMain);
m_lexer.SmartExtendNodeRange(rNodeRange);
// Add the table to the root
auto ptrTable = m_ptrRoot->Insert<CTable>(sdv::toml::npos, rangeKeyPath, false);
if (ptrTable)
{
m_ptrCurrentCollection = ptrTable->Cast<CTable>();
ptrTable->UpdateNodeCode(rNodeRange);
}
}
void CParser::ProcessTableArray(CNodeTokenRange& rNodeRange)
{
CTokenRange rangeMain(m_lexer.Consume());
CTokenRange rangeKeyPath = ProcessKeyPath();
const CToken& rToken = m_lexer.Consume();
if (!rToken || rToken.Category() != ETokenCategory::token_syntax_table_array_close)
throw XTOMLParseException("Invalid Table Array construct");
rangeMain.AssignEndToken(rToken); // NOTE: This includes only the name and the brackets, not the values.
// Assign the main token range to the node token range and let the lexer determine the extended token range
rNodeRange.NodeMain(rangeMain);
m_lexer.SmartExtendNodeRange(rNodeRange);
// Add the table array to the root
auto ptrTableArray = m_ptrRoot->Insert<CTableArray>(sdv::toml::npos, rangeKeyPath);
if (ptrTableArray)
{
m_ptrCurrentCollection = ptrTableArray->Cast<CNodeCollection>();
ptrTableArray->UpdateNodeCode(rNodeRange);
}
}
void CParser::ProcessValueKey(CNodeTokenRange& rNodeRange)
{
// Initial part of the main token range containing the key and the assignment
CTokenRange rangeMain(m_lexer.Peek());
CTokenRange rangeKeyPath = ProcessKeyPath();
const CToken& rToken = m_lexer.Consume();
if (rToken.Category() != ETokenCategory::token_syntax_assignment)
throw XTOMLParseException("Assignment expected");
rangeMain.AssignEndToken(rToken);
// Store this initial range
rNodeRange.NodeMain(rangeMain);
// Process the value assignment
ProcessValue(rangeKeyPath, rNodeRange);
}
void CParser::ProcessValue(const CTokenRange& rrangeKeyPath, CNodeTokenRange& rNodeRange)
{
// Extend the main range
CTokenRange rangeMain = rNodeRange.NodeMain();
// Skip newlines (other whitespace and comments are already skipped)
while (m_lexer.Peek().Category() == ETokenCategory::token_syntax_new_line)
m_lexer.Consume();
// Get the value
const CToken& rAssignmentValue = m_lexer.Consume();
if (!rAssignmentValue)
throw XTOMLParseException("Missing value");
// Assign the end token for the main part
rangeMain.AssignEndToken(rAssignmentValue);
rNodeRange.NodeMain(rangeMain);
// Process the value
std::shared_ptr<CNode> ptrNode;
switch (rAssignmentValue.Category())
{
case ETokenCategory::token_boolean:
ptrNode = m_ptrCurrentCollection->Insert<CBooleanNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.BooleanValue(),
rAssignmentValue.RawString());
break;
case ETokenCategory::token_integer:
ptrNode = m_ptrCurrentCollection->Insert<CIntegerNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.IntegerValue(),
rAssignmentValue.RawString());
break;
case ETokenCategory::token_float:
ptrNode = m_ptrCurrentCollection->Insert<CFloatingPointNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.FloatValue(),
rAssignmentValue.RawString());
break;
case ETokenCategory::token_string:
switch (rAssignmentValue.StringType())
{
case ETokenStringType::literal_string:
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(),
CStringNode::EQuotationType::literal_string, rAssignmentValue.RawString());
break;
case CLexerTOML::ETokenCategory::token_syntax_table_array_open:
ProcessTableArray();
case ETokenStringType::multi_line_literal:
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_literal,
rAssignmentValue.RawString());
break;
case CLexerTOML::ETokenCategory::token_key:
ProcessValueKey();
case ETokenStringType::multi_line_quoted:
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_quoted,
rAssignmentValue.RawString());
break;
case CLexerTOML::ETokenCategory::token_syntax_new_line:
case ETokenStringType::quoted_string:
default:
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::quoted_string,
rAssignmentValue.RawString());
break;
}
break;
case ETokenCategory::token_syntax_array_open:
{
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
ptrNode = m_ptrCurrentCollection->Insert<CArray>(sdv::toml::npos, rrangeKeyPath);
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
m_stackEnvironment.push(EEnvironment::env_array);
ProcessArray(rNodeRange);
m_stackEnvironment.pop();
m_ptrCurrentCollection = ptrCurrentCollectionStored;
}
break;
case ETokenCategory::token_syntax_inline_table_open:
{
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);
ProcessInlineTable(rNodeRange);
m_stackEnvironment.pop();
m_ptrCurrentCollection = ptrCurrentCollectionStored;
}
break;
default:
throw XTOMLParseException("Missing value");
break;
}
// let the lexer determine the extended token range and update the node
m_lexer.SmartExtendNodeRange(rNodeRange);
// Deal with the next value if expecting
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
if (!m_stackEnvironment.empty())
{
// Skip newlines
while (refToken.get().Category() == ETokenCategory::token_syntax_new_line)
{
m_lexer.Consume();
refToken = m_lexer.Peek();
}
switch (m_stackEnvironment.top())
{
case EEnvironment::env_array:
{
int32_t index = 1;
while (refToken.get() && refToken.get().Category() == ETokenCategory::token_syntax_new_line)
{
refToken = m_lexer.Peek(index++);
}
if (!refToken.get()
|| (refToken.get().Category() != ETokenCategory::token_syntax_comma
&& refToken.get().Category() != ETokenCategory::token_syntax_array_close))
{
throw XTOMLParseException("Invalid Token after value assignment in array; ',' or ']' needed");
}
}
break;
case CLexerTOML::ETokenCategory::token_terminated:
case CLexerTOML::ETokenCategory::token_error:
throw XTOMLParseException(current.ssContentString);
case EEnvironment::env_inline_table:
if (!refToken.get()
|| (refToken.get().Category() != ETokenCategory::token_syntax_comma
&& refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close))
{
throw XTOMLParseException("Invalid Token after value assignment in inline table; ',' or '}' needed ");
}
break;
default:
throw XTOMLParseException("Invalid Syntax; not a Key, Table or Tablearray");
break;
}
rNodeRange.LinesBehindNode(refToken);
}
}
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();
{
if (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_new_line)
{
throw XTOMLParseException("Invalid Token after value assignment; newline needed");
}
}
if (ptrNode) ptrNode->UpdateNodeCode(rNodeRange);
}
return ssPath;
}
void CParser::ProcessArray(CNodeTokenRange& rNodeRange)
{
/*
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''' ]
*/
// The initial start position for the value token is the end of the first main part.
std::reference_wrapper<const CToken> refStartValueToken = rNodeRange.LinesBehindNode().End();
// Iterator token
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
// State machine
enum class EExpect
{
value_or_end,
comma_or_end // Trailing comma is allowed
} eExpect = EExpect::value_or_end;
// Iterate through the values
size_t nIndex = 0;
bool bAdditionalCommaBeforeEnd = false;
std::reference_wrapper<const CToken> refCommaToken = m_lexer.Peek();
while (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_array_close)
{
// The value has its own token range
CNodeTokenRange rangeNode(refStartValueToken.get());
switch (refToken.get().Category())
{
case ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
break;
case ETokenCategory::token_syntax_comma:
if (eExpect == EExpect::value_or_end) throw XTOMLParseException("Expecting value or array end.");
m_lexer.Consume();
eExpect = EExpect::value_or_end;
refCommaToken = refToken;
bAdditionalCommaBeforeEnd = true;
break;
default:
{
bAdditionalCommaBeforeEnd = false;
if (eExpect == EExpect::comma_or_end) throw XTOMLParseException("Expecting comma or array end.");
CLexer lexerLocal("[" + std::to_string(nIndex++) + "]", true);
CTokenRange rangeIndexKey(lexerLocal.Peek(), lexerLocal.Peek().JumpToEnd());
rangeNode.NodeMain(CTokenRange(refToken, refToken.get().Next()));
ProcessValue(rangeIndexKey, rangeNode);
eExpect = EExpect::comma_or_end;
// Update the start of the next value range
refStartValueToken = rangeNode.LinesBehindNode().End();
break;
}
}
// Get the next value
refToken = m_lexer.Peek();
}
// This is the node token range finishing main part.
rNodeRange.NodeMainFinish(
CTokenRange(bAdditionalCommaBeforeEnd ? refStartValueToken.get() : refToken.get(), refToken.get().Next()));
// Consume the token
m_lexer.Consume();
}
void CParser::ProcessInlineTable(CNodeTokenRange& rNodeRange)
{
/*
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" }
*/
// The initial start position for the value token is the end of the first main part.
std::reference_wrapper<const CToken> refStartValueToken = rNodeRange.LinesBehindNode().End();
// Iterator token
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
// State machine
enum class EExpect
{
value_or_end,
value,
comma_or_end
} eExpect = EExpect::value_or_end;
// Iterate through the value
while (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close)
{
// The value has its own token range
CNodeTokenRange rangeNode(refStartValueToken.get());
switch (refToken.get().Category())
{
case ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
break;
case ETokenCategory::token_syntax_comma:
if (eExpect != EExpect::comma_or_end) throw XTOMLParseException("Expecting value or table end.");
m_lexer.Consume();
eExpect = EExpect::value;
break;
default:
if (eExpect == EExpect::comma_or_end) throw XTOMLParseException("Expecting comma or table end.");
ProcessValueKey(rangeNode);
eExpect = EExpect::comma_or_end;
break;
}
// Update the start of the next value range
refStartValueToken = rangeNode.LinesBehindNode().End();
// Get the next value
refToken = m_lexer.Peek();
}
if (eExpect == EExpect::value)
throw XTOMLParseException("Expecting a value before inline table end.");
// This is the node token range finishing main part.
rNodeRange.NodeMainFinish(CTokenRange(refToken.get(), refToken.get().Next()));
// Consume the token
m_lexer.Consume();
}
CTokenRange CParser::ProcessKeyPath()
{
//std::string ssPath;
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
std::reference_wrapper<const CToken> refKeyStart = refToken;
if (!refToken.get()
|| (refToken.get().Category() != ETokenCategory::token_syntax_dot
&& refToken.get().Category() != ETokenCategory::token_key))
throw XTOMLParseException("Invalid Token to assemble path from keys");
while (refToken.get()
&& (refToken.get().Category() == ETokenCategory::token_syntax_dot
|| refToken.get().Category() == ETokenCategory::token_key))
{
m_lexer.Consume();
//if (refToken.get().Category() == ETokenCategory::token_key)
//{
// EQuoteRequest eQuoteRequest = EQuoteRequest::smart_key;
// switch (refToken.get().StringType())
// {
// case ETokenStringType::literal_string:
// eQuoteRequest = EQuoteRequest::literal_text;
// break;
// case ETokenStringType::quoted_string:
// eQuoteRequest = EQuoteRequest::quoted_text;
// break;
// case ETokenStringType::multi_line_literal:
// eQuoteRequest = EQuoteRequest::multi_line_literal_text;
// break;
// case ETokenStringType::multi_line_quoted:
// eQuoteRequest = EQuoteRequest::multi_line_quoted_text;
// break;
// default:
// break;
// }
//}
refToken = m_lexer.Peek();
}
return CTokenRange(refKeyStart.get(), refToken);
}
} // namespace toml_parser

View File

@@ -3,171 +3,137 @@
#include "lexer_toml.h"
#include "parser_node_toml.h"
#include "miscellaneous.h"
#include <stack>
#include <memory>
#include <string>
/**
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data
*/
class CParserTOML : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
/// The TOML parser namespace
namespace toml_parser
{
public:
class CNode;
/**
* @brief Default constructor
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data
*/
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
class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
{
env_array, //!< Environment for an array
env_inline_table //!< Environment for a table
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);
// 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 Get the lexer containing the token list.
* @return A reference to the lexer containing the token list.
*/
CLexer& Lexer();
/**
* @{
* @brief Return the root node.
* @return Reference to the root node collection.
*/
const CNodeCollection& Root() const;
CNodeCollection& Root();
/**
* @}
*/
/**
* @brief Get the TOML text based on the content.
* @param[in] rssPrefixKey When present, uses the prefix node into the TOML text generation. The string must follow the key
* rules for separation with bare, literal and quoted keys.
* @return The string containing the TOML text.
*/
std::string GenerateTOML(const std::string& rssPrefixKey = std::string()) const;
private:
/**
* @brief Process a table declaration.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/
void ProcessTable(CNodeTokenRange& rNodeRange);
/**
* @brief Process a table array declaration.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/
void ProcessTableArray(CNodeTokenRange& rNodeRange);
/**
* @brief Process the value key.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/
void ProcessValueKey(CNodeTokenRange& rNodeRange);
/**
* @brief Process the value with the supplied key.
* @param[in] rrangeKeyPath Reference to the key path token range.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/
void ProcessValue(const CTokenRange& rrangeKeyPath, CNodeTokenRange& rNodeRange);
/**
* @brief Process the array value with the supplied key.
* @param[in, out] rNodeRange Reference to the extended token range of the node. The second main range will be added.
*/
void ProcessArray(CNodeTokenRange& rNodeRange);
/**
* @brief Process the inline table value with the supplied key.
* @param[in, out] rNodeRange Reference to the extended token range of the node. The second main range will be added.
*/
void ProcessInlineTable(CNodeTokenRange& rNodeRange);
/**
* @brief Compose a path from lexer tokens. A path is composed of table and array elements separated with a dot.
* @return The token range of the key path.
*/
CTokenRange ProcessKeyPath();
/**
* @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<CRootTable> m_ptrRoot; ///< The one root node.
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
CLexer m_lexer; ///< Lexer.
};
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;
}
} // namespace toml_parser
#endif // PARSER_TOML_H

View File

@@ -25,7 +25,7 @@ public:
DECLARE_OBJECT_CLASS_NAME("TOMLParserUtility")
private:
CParserTOML m_parser; ///< Configuration parser
toml_parser::CParser m_parser; ///< Configuration parser
};
DEFINE_SDV_OBJECT_NO_EXPORT(CTOMLParserUtility)

View File

@@ -12,7 +12,8 @@ CChannelConnector::CChannelConnector(CCommunicationControl& rcontrol, uint32_t u
m_pDataSend(m_ptrChannelEndpoint.GetInterface<sdv::ipc::IDataSend>())
{
m_tConnectionID.uiIdent = uiIndex;
m_tConnectionID.uiControl = static_cast<uint32_t>(rand());
while (!m_tConnectionID.uiControl)
m_tConnectionID.uiControl = static_cast<uint32_t>(rand());
}
CChannelConnector::~CChannelConnector()
@@ -29,7 +30,7 @@ CChannelConnector::~CChannelConnector()
lock.unlock();
// Cancel the processing
rsEntry.bCancel = true;
rsEntry.eState = SCallEntry::EState::canceled;
rsEntry.cvWaitForResult.notify_all();
// Handle next call.
@@ -44,6 +45,10 @@ CChannelConnector::~CChannelConnector()
if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie);
pConnection->Disconnect();
}
// There are several dependencies on this channel connector, which should be availble during the processing of the asynchronous
// disconnect function. Wait for a quarter second to allow the processing to complete.
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
bool CChannelConnector::ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllowReconnect)
@@ -230,20 +235,25 @@ void CChannelConnector::DecoupledReceiveData(/*inout*/ sdv::sequence<sdv::pointe
// Send the result back
m_pDataSend->SendData(seqResult);
} else
}
else
{
// Look for the call entry
std::unique_lock<std::mutex> lockCallMap(m_mtxCalls);
auto itCall = m_mapCalls.find(sAddress.uiCallIndex);
if (itCall == m_mapCalls.end()) return;
if (itCall == m_mapCalls.end())
return;
SCallEntry& rsCallEntry = itCall->second;
m_mapCalls.erase(itCall);
lockCallMap.unlock();
if (rsCallEntry.eState != SCallEntry::EState::processing)
return;
// Update the result
std::unique_lock<std::mutex> lockCall(rsCallEntry.mtxWaitForResult);
rsCallEntry.seqResult = seqData;
lockCall.unlock();
rsCallEntry.eState = SCallEntry::EState::processed;
rsCallEntry.cvWaitForResult.notify_all();
}
}
@@ -271,12 +281,14 @@ sdv::sequence<sdv::pointer<uint8_t>> CChannelConnector::MakeCall(sdv::ps::TMarsh
std::unique_lock<std::mutex> lock(m_mtxCalls);
SCallEntry sResult;
m_mapCalls.try_emplace(sAddress.uiCallIndex, sResult);
sResult.eState = SCallEntry::EState::processing;
lock.unlock();
// Store the channel context (used to marshall interfaces over the same connector)
m_rcontrol.SetConnectorContext(this);
// Send the data
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult);
try
{
if (!m_pDataSend->SendData(rseqInputData)) throw sdv::ps::XMarshallExcept();
@@ -291,10 +303,13 @@ sdv::sequence<sdv::pointer<uint8_t>> CChannelConnector::MakeCall(sdv::ps::TMarsh
}
// Wait for the result
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout();
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult);
sResult.cvWaitForResult.wait(lockResult);
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout();
// NOTE: Sinde the conditional variable doesn't keep its state, it might happen, that the variable is set before the wait
// function has been entered (race condition). This would cause the function to wait forever.
while (sResult.eState == SCallEntry::EState::processing)
sResult.cvWaitForResult.wait_for(lockResult, std::chrono::milliseconds(1));
if (sResult.eState == SCallEntry::EState::canceled)
throw sdv::ps::XMarshallTimeout();
return sResult.seqResult;
}

View File

@@ -114,10 +114,19 @@ private:
*/
struct SCallEntry
{
sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data.
std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing.
bool bCancel = false; ///< Cancel processing when set.
/**
* @brief Call entry state.
*/
enum EState
{
initialized,
processing,
processed,
canceled,
} eState = EState::initialized; ///< Data processing state.
sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data.
std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing.
};
CCommunicationControl& m_rcontrol; ///< Reference to the communication control class.

View File

@@ -32,7 +32,9 @@ sdv::interface_t CMarshallObject::InitializeAsProxy(uint32_t uiProxyIndex, sdv::
m_eType = EType::proxy;
// Create marshall ID from index and a random number.
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiProxyIndex, static_cast<uint32_t>(rand()) };
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiProxyIndex, 0 };
while (!tMarshallID.uiControl)
tMarshallID.uiControl = static_cast<uint32_t>(rand());
// Get the stub creation interface from the repository
sdv::core::IRepositoryMarshallCreate* pMarshallCreate =
@@ -99,6 +101,8 @@ bool CMarshallObject::InitializeAsStub(uint32_t uiStubIndex, sdv::interface_t if
// Create marshall ID from index and a random number.
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiStubIndex, static_cast<uint32_t>(rand()) };
while (!tMarshallID.uiControl)
tMarshallID.uiControl = static_cast<uint32_t>(rand());
// Get the stub creation interface from the repository
sdv::core::IRepositoryMarshallCreate* pMarshallCreate =

View File

@@ -342,7 +342,8 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
if (m_eStatus != sdv::ipc::EConnectStatus::uninitialized)
{
for (auto& rprEventCallback : m_lstEventCallbacks)
if (rprEventCallback.pCallback) rprEventCallback.pCallback->SetStatus(sdv::ipc::EConnectStatus::connection_error);
if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
rprEventCallback.pCallback->SetStatus(sdv::ipc::EConnectStatus::connection_error);
return false;
}
@@ -456,6 +457,8 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess*
sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface<sdv::ipc::IConnectEventCallback>();
if (!pCallback) return 0;
uint64_t uiCookie = rand();
while (!uiCookie)
uiCookie = rand();
std::unique_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
m_lstEventCallbacks.emplace(m_lstEventCallbacks.begin(), std::move(SEventCallback{ uiCookie, pCallback }));
return uiCookie;
@@ -490,7 +493,10 @@ void CConnection::DestroyObject()
// Clear all events callbacks (if not done so already)
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
for (auto& rprEventCallback : m_lstEventCallbacks)
{
rprEventCallback.uiCookie = 0;
rprEventCallback.pCallback = nullptr;
}
lock.unlock();
// Just in case... so no calls are made into the destructed class any more.
@@ -519,7 +525,10 @@ void CConnection::SetStatus(sdv::ipc::EConnectStatus eStatus)
m_eStatus = eStatus;
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
for (auto& rprEventCallback : m_lstEventCallbacks)
if (rprEventCallback.pCallback) rprEventCallback.pCallback->SetStatus(eStatus);
{
if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
rprEventCallback.pCallback->SetStatus(eStatus);
}
// If disconnected by force update the disconnect status.
if (m_eStatus == sdv::ipc::EConnectStatus::disconnected_forced)
@@ -554,12 +563,13 @@ void CConnection::ReceiveMessages()
{
SetStatus(sdv::ipc::EConnectStatus::communication_error);
SDV_LOG_ERROR("No valid shared memory for receiving.");
lock.unlock();
m_cvStartConnect.notify_all();
return;
}
m_cvStartConnect.notify_all();
lock.unlock();
m_cvStartConnect.notify_all();
// Read processing
auto tpStart = std::chrono::high_resolution_clock::time_point();
@@ -623,8 +633,24 @@ void CConnection::ReceiveMessages()
case EMsgType::data_fragment:
case EMsgType::data:
break;
case EMsgType::sync_request:
TRACE("Receive sync-request message of of ", message.GetSize(), " bytes");
break;
case EMsgType::sync_answer:
TRACE("Receive sync-answer message of of ", message.GetSize(), " bytes");
break;
case EMsgType::connect_request:
TRACE("Receive connect-request message of of ", message.GetSize(), " bytes");
break;
case EMsgType::connect_answer:
TRACE("Receive connect-answer message of of ", message.GetSize(), " bytes");
break;
case EMsgType::connect_term:
TRACE("Receive connect-termination message of of ", message.GetSize(), " bytes");
break;
default:
TRACE("Receive raw data 0x", (void*) &message.GetMsgHdr(), " of ", message.GetSize(), " bytes");
TRACE("Received unknown message of type ", (uint32_t) message.GetMsgHdr().eType, " of ", message.GetSize(), " bytes");
break;
}
#endif
@@ -1011,7 +1037,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
if (!rMessage.GetFragmentedHdr().uiOffset)
{
#if ENABLE_REPORTING >= 1
TRACE("Start receive fragmented data message of ", rsDataCtxt.uiTotalSize, " bytes");
TRACE("Start receive fragmented data message of ", rsDataCtxt.uiTotalSize, " bytes (following the header)");
#endif
// Read the data directory table...
// Note: it is assumed that the table fits in the first message completely.
@@ -1029,7 +1055,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
if (!sstream.str().empty()) sstream << ", ";
sstream << rptrData.size();
}
TRACE("Fragmented message has ", rsDataCtxt.seqDataChunks.size(), " of {", sstream.str(), "} bytes");
TRACE("Fragmented message has ", rsDataCtxt.seqDataChunks.size(), " chunk of ", sstream.str(), " bytes");
#endif
}

View File

@@ -22,7 +22,7 @@
/// When put to 1, decoupling of receive data is activated (default is not activated).
#define ENABLE_DECOUPLING 0
#if ENABLE_REPORTING > 0
#if ENABLE_REPORTING > 0 && !defined(ENABLE_TRACE)
/// Enable tracing
#define ENABLE_TRACE 1
#endif

View File

@@ -29,7 +29,7 @@ public:
CInProcMemBuffer(const std::string& rssConnectionString);
/**
* \brief Default destructor
* @brief Default destructor
*/
~CInProcMemBuffer() = default;

View File

@@ -43,7 +43,7 @@ public:
CSharedMemBuffer(CSharedMemBuffer&&) = delete;
/**
* \brief Default destructor
* @brief Default destructor
*/
~CSharedMemBuffer();
@@ -364,10 +364,15 @@ CSharedMemBuffer<TAccessor>::~CSharedMemBuffer()
munmap(m_pBuffer, m_uiSize);
if (m_bServer && !m_ssName.empty())
shm_unlink((std::string("/") + m_ssName).c_str());
if (m_iFileDescr >= 0) close(m_iFileDescr);
if (m_bServer && m_pSemaphoreTx)
sem_unlink(m_ssSyncTx.c_str());
if (m_pSemaphoreTx)
sem_close(m_pSemaphoreTx);
if (m_bServer && m_pSemaphoreRx)
sem_unlink(m_ssSyncRx.c_str());
if (m_pSemaphoreRx)
sem_close(m_pSemaphoreRx);
}
template <class TAccessor>

View File

@@ -53,7 +53,7 @@ public:
CSharedMemBuffer(CSharedMemBuffer&&) = delete;
/**
* \brief Default destructor
* @brief Default destructor
*/
~CSharedMemBuffer();

View File

@@ -28,6 +28,7 @@
#include <thread>
#include <queue>
#include <condition_variable>
#include <atomic>
// Forward declaration
class CConnection;
@@ -114,7 +115,7 @@ private:
///< connection is scheduled for destruction.
std::queue<std::shared_ptr<CConnection>> m_queueScheduledConnectionDestructions; ///< Scheduled connection for destruction.
std::thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions.
bool m_bShutdown = false; ///< Set when shutting down the watchdog
std::atomic_bool m_bShutdown = false; ///< Set when shutting down the watchdog
};
#endif // !defined WATCH_DOG_H

View File

@@ -17,6 +17,7 @@
#include <map>
#include <set>
#include <condition_variable>
#include <atomic>
/**
* @brief Process control service class
@@ -135,41 +136,40 @@ public:
void MonitorThread();
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access
std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access
#ifdef _WIN32
std::map<sdv::process::TProcessID, std::pair<HANDLE,HANDLE>> m_mapProcessThreadShutdown; ///< Map with process IDs and event handles
#elif __unix__
std::set<sdv::process::TProcessID> m_setProcessThreadShutdown; ///< Set with process IDs
std::set<sdv::process::TProcessID> m_setProcessThreadShutdown; ///< Set with process IDs
#else
#error OS is not supported!
#endif
/**
* @brief Process helper structure
*/
struct SProcessHelper
{
sdv::process::TProcessID tProcessID = 0; ///< Process ID
/**
* @brief Process helper structure
*/
struct SProcessHelper
{
sdv::process::TProcessID tProcessID = 0; ///< Process ID
#ifdef _WIN32
HANDLE hProcess = 0; ///< process handle
HANDLE hProcess = 0; ///< process handle
#elif defined __unix__
bool bNotAChild = false; ///< When set, the process is not a child of the monitor process.
bool bNotAChild = false; ///< When set, the process is not a child of the monitor process.
#else
#error OS is not supported!
#endif
bool bRunning = true; ///< Set when the process is running and not terminated yet.
int64_t iRetVal = 0; ///< Process return value.
std::map<uint32_t, sdv::process::IProcessLifetimeCallback*> mapAssociatedMonitors; ///< Map with associated monitors.
std::mutex mtxProcess; ///< Mutex for process access.
std::condition_variable cvWaitForProcess; ///< Condition variable to wait for process termination.
};
mutable std::mutex m_mtxProcesses; ///< Access control for monitor map.
std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors.
bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
std::thread m_threadMonitor; ///< Monitor thread.
std::atomic_bool bRunning = true; ///< Set when the process is running and not terminated yet.
int64_t iRetVal = 0; ///< Process return value.
std::map<uint32_t, sdv::process::IProcessLifetimeCallback*> mapAssociatedMonitors; ///< Map with associated monitors.
std::mutex mtxProcess; ///< Mutex for process access.
std::condition_variable cvWaitForProcess; ///< Condition variable to wait for process termination.
};
mutable std::mutex m_mtxProcesses; ///< Access control for monitor map.
std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors.
std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
std::thread m_threadMonitor; ///< Monitor thread.
};
DEFINE_SDV_OBJECT(CProcessControl)