mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
update parser (#5)
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
CSDVCore();
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CSDVCore();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
552
sdv_services/core/toml_parser/lexer_toml_token.cpp
Normal file
552
sdv_services/core/toml_parser/lexer_toml_token.cpp
Normal 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
|
||||
421
sdv_services/core/toml_parser/lexer_toml_token.h
Normal file
421
sdv_services/core/toml_parser/lexer_toml_token.h
Normal 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
|
||||
549
sdv_services/core/toml_parser/miscellaneous.cpp
Normal file
549
sdv_services/core/toml_parser/miscellaneous.cpp
Normal 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
|
||||
99
sdv_services/core/toml_parser/miscellaneous.h
Normal file
99
sdv_services/core/toml_parser/miscellaneous.h
Normal 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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
CInProcMemBuffer(const std::string& rssConnectionString);
|
||||
|
||||
/**
|
||||
* \brief Default destructor
|
||||
* @brief Default destructor
|
||||
*/
|
||||
~CInProcMemBuffer() = default;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
CSharedMemBuffer(CSharedMemBuffer&&) = delete;
|
||||
|
||||
/**
|
||||
* \brief Default destructor
|
||||
* @brief Default destructor
|
||||
*/
|
||||
~CSharedMemBuffer();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user