Precommit (#1)

* first commit

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

View File

@@ -0,0 +1,26 @@
# Include cross-compilation toolchain file
include(../cross-compile-tools.cmake)
# Use new policy for project version settings and default warning level
cmake_policy(SET CMP0048 NEW) # requires CMake 3.14
cmake_policy(SET CMP0092 NEW) # requires CMake 3.15
# Define project
project(SDVServices VERSION 1.0 LANGUAGES CXX)
# Include export into the include directory path
include_directories(../export)
# Add executable projects
add_subdirectory(sdv_idl_compiler)
add_subdirectory(sdv_dbc_util)
add_subdirectory(sdv_vss_util)
add_subdirectory(sdv_control)
add_subdirectory(sdv_core)
add_subdirectory(sdv_iso)
add_subdirectory(sdv_packager)
add_subdirectory(sdv_local_shutdown)
add_subdirectory(sdv_trace_mon)
# Appending all executables to the service list
set(SDV_Executable_List ${SDV_Executable_List} PARENT_SCOPE)

119
sdv_executables/error_msg.h Normal file
View File

@@ -0,0 +1,119 @@
#ifndef EXECUTABLES_ERROR_MSG
#define EXECUTABLES_ERROR_MSG
#include <string>
#ifdef WIN32
// The define NO_ERROR could have been defined already by Windows. Suppress the definition during the error message creation.
// Then re-enable the define. This only works, because the newly assigned value and the value used by Windows is identical.
#ifdef NO_ERROR
#pragma push_macro("NO_ERROR")
#undef NO_ERROR
#define SUPPRESS_NO_ERROR
#endif
#endif
/**
* @brief Macro for making an error message definition.
* @details This macro creates three variables for the use of error reporting:<br>
* &lt;identifier&gt; The assigned error code (application return code).<br>
* &lt;identifier&gt;_MSG The assigned error message.<br>
* &lt;identifier&gt;_DESC The assigned error explanation.<br>
* @param id The ID of the message (integer)
* @param identifier The identifier used for the message assignment.
* @param topic The string to explain the error in one sentence.
* @param description The string to provide detailed information about the error.
*/
#define MAKE_ERROR_MSG(id, identifier, topic, description) \
[[maybe_unused]] static const int identifier = id; \
[[maybe_unused]] static const char* identifier##_MSG = topic; \
[[maybe_unused]] static const char* identifier##_DESC = description;
////////// GENERIC ERROR CODES ////////////
MAKE_ERROR_MSG(0, NO_ERROR, "No error.", "Execution succeeded expectinly.")
MAKE_ERROR_MSG(-1, NOT_IMPLEMENTED, "Not implemented.", "The function is not implemented.")
MAKE_ERROR_MSG(-2, CMDLN_ARG_ERR, "Command line argument error.", "An incorrect argument was supplied on the command line.")
MAKE_ERROR_MSG(-3, CMDLN_SILENT_VERBOSE, "Cannot supply both silent and verbose options.", "Both silent and verbose were supplied, which cannot be used at the same time.")
MAKE_ERROR_MSG(-100, PROCESS_TIMEOUT, "Potential deadlock detected; termination enforced.", "The process watchdog has detected a potential deadlock and initiated the process termination.")
////////// CONNECTION AND CORE SERVICE ACCESS ERROR CODES ////////////
MAKE_ERROR_MSG(-124, CONNECT_SDV_SERVER_ERROR, "Could not connect to SDV Server.", "Failed to connect to a running SDV service instance.")
MAKE_ERROR_MSG(-131, PROCESS_CONTROL_SERVICE_ACCESS_ERROR, "Process control service is not accessible.", "The process control service could not be accessed.")
MAKE_ERROR_MSG(-132, MODULE_CONTROL_SERVICE_ACCESS_ERROR, "Module control service is not accessible.", "The module control service could not be accessed.")
MAKE_ERROR_MSG(-133, REPOSITORY_SERVICE_ACCESS_ERROR, "Repository service is not accessible.", "The repository service could not be accessed.")
MAKE_ERROR_MSG(-134, CONFIG_SERVICE_ACCESS_ERROR, "Configuration service is not accessible.", "The configuration service could not be accessed.")
MAKE_ERROR_MSG(-135, COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR, "Communication control service is not accessible.", "The communication control service service could not be accessed.")
MAKE_ERROR_MSG(-136, APP_CONTROL_SERVICE_ACCESS_ERROR, "Application control service is not accessible.", "The application control service could not be accessed.")
MAKE_ERROR_MSG(-170, CANNOT_FIND_OBJECT, "The object cannot be found.", "A search for an object with supplied name was not successful.")
////////// LOCAL APP CONTROL ERROR CODES ////////////
MAKE_ERROR_MSG(-204, APP_CONTROL_STARTUP_ERROR, "Cannot start application control.", "Application control start was initiated, but returned an error.")
MAKE_ERROR_MSG(-205, APP_CONTROL_START_LOOP_FAIELD, "Starting the running loop failed.", "Failed to enter the processing loop for a server application.")
MAKE_ERROR_MSG(-206, APP_CONTROL_DUPLICATE_INSTANCE, "Another process with specified instance is already running.", "Another core process initiated the application control with the same instance. Only one core process per instance is allowed.")
MAKE_ERROR_MSG(-210, APP_CONTROL_INVALID_ISOLATION_CONFIG, "An invalid isolation configuration was supplied.", "The application control configuration section contains an invalid isolation configuration.")
MAKE_ERROR_MSG(-218, SETTING_FILE_VERSION_INVALID, "Settings file has an invalid version.", "Settings file contains an invalid/incompatible version number.")
MAKE_ERROR_MSG(-222, REGISTER_PROCESS_MONITOR_ERROR, "Failed to register the process monitor.", "During the registration of the process monitor, an error occurred.")
MAKE_ERROR_MSG(-214, LINK_REPO_SERVICE_ERROR, "Cannot link the core repository to the isolated repository.", "Failed to link the core repository to the repository of the isolated application.")
MAKE_ERROR_MSG(-230, ISOLATION_INVALID_OBJECT_TYPE, "An object of this type cannot be run isolated.", "The isolation of an objeczt is supported for complex services and utilities.")
MAKE_ERROR_MSG(-231, ISOLATION_FAILED, "The isolation of the object failed.", "The request to run an object in an isolated environment failed.")
MAKE_ERROR_MSG(-240, REGISTER_FOREIGN_OBJECT_FAILED, "The registration of a foreign object failed.", "An running object registration as a foreign object was not successful.")
////////// SDV CONTROL ERROR CODES ////////////
MAKE_ERROR_MSG(-821, STARTUP_CORE_ERROR, "Could not start the SDV core process.", "Failed to start the SDV core process.")
MAKE_ERROR_MSG(-822, CORE_NOT_STARTED, "The SDV core process was not running.", "A request for the status of a SDV core process returned that the process was not running.")
MAKE_ERROR_MSG(-823, SHUTDOWN_CORE_ERROR, "Could not start the SDV core process.", "Failed to start the SDV core process.")
MAKE_ERROR_MSG(-840, START_OBJECT_ERROR, "Could not start the object.", "Failed to start an object.")
MAKE_ERROR_MSG(-841, STOP_OBJECT_ERROR, "Could not stop the object.", "Failed to stop/destroy an object.")
////////// SDV PACKAGER ERROR CODES ////////////
MAKE_ERROR_MSG(-1005, CMDLN_INSTALL_NAME_MISSING, "No installation name provided.", "The installation name was not supplied at the command line. This is needed for packing, direct installation and uninstallation.")
MAKE_ERROR_MSG(-1006, CMDLN_SOURCE_LOCATION_ERROR, "Source location cannot be found or is not a directory.", "The supplied source location is not a valid directory.")
MAKE_ERROR_MSG(-1007, CMDLN_OUTPUT_LOCATION_ERROR, "Output location cannot be found or is not a directory.", "The supplied output location is not a valid directory.")
MAKE_ERROR_MSG(-1004, CMDLN_TARGET_LOCATION_ERROR, "Target location cannot be found or is not a directory.", "The supplied target location is not a valid directory.")
MAKE_ERROR_MSG(-1008, CMDLN_MULTIPLE_CFG_FILES, "Only one configuration file can be generated.", "Multiple configuration files were supplied at the command line. Only one configuration file can be generated at the time.")
MAKE_ERROR_MSG(-1009, CMDLN_CREATE_ONLY_MANIFEST_DIRECT_INSTALL_ERROR, "create_manifest_only-flag is not compatible with direct_install-flag.", "Command line flag for only creation of a manifest cannot be combined with the flag for direct installation.")
MAKE_ERROR_MSG(-1010, CMDLN_NOTHING_TO_DO, "No action supplied; nothing to do.", "No command was supplied on the command line. There is nothing to do.")
MAKE_ERROR_MSG(-1011, CMDLN_UPDATE_OVERWRITE_ERROR, "Cannot specify update and overwrite at the same time.", "Update and overwrite were specified at the same time. This is not valid.")
MAKE_ERROR_MSG(-1012, CMDLN_SOURCE_FILE_ERROR, "Source file cannot be found or is invalid.", "The supplied source file name is not a valid file.")
MAKE_ERROR_MSG(-1013, CMDLN_SOURCE_FILE_MISSING, "No source file specified.", "There was no source file specified at the command line.")
MAKE_ERROR_MSG(-1014, CMDLN_TOO_MANY_SOURCE_FILES, "Too many source files were specified.", "Too many source files were specified at the command line. Only one file is supported at the time.")
MAKE_ERROR_MSG(-1015, CMDLN_INCOMPATIBLE_ARGUMENTS, "Incompatible arguments were supplied, not fitting the command.", "Some arguments cannot be combined with the command on the command line.")
MAKE_ERROR_MSG(-1016, CMDLN_MISSING_SHOW_COMMAND, "Missing show command.", "The command SHOW was supplied on the command line, but not what to show.")
MAKE_ERROR_MSG(-1017, CMDLN_INVALID_CONFIG_STRING, "The configuration string is invalid.", "The configuration string should consist of path (only for local) followed by +component,component,....")
MAKE_ERROR_MSG(-1018, SAVE_INSTALL_MANIFEST_ERROR, "Failed to save the installation manifest.", "Saving the installation manifest returned with an error.")
MAKE_ERROR_MSG(-1019, SAVE_SETTINGS_FILE_ERROR, "Failed to save application settings file.", "Saving the application settings file returned with an error.")
MAKE_ERROR_MSG(-1020, SAVE_CONFIG_FILE_ERROR, "Failed to save the config file.", "Saving the configuration file returned with an error.")
MAKE_ERROR_MSG(-1021, CANNOT_REMOVE_INSTALL_DIR, "Cannot remove existing installation directory.", "Failed to remove an existing installation directory.")
MAKE_ERROR_MSG(-1022, CREATE_INSTALL_DIR_ERROR, "Cannot create installation directory.", "Failed to create the installation directory.")
MAKE_ERROR_MSG(-1023, CREATE_TARGET_DIR_ERROR, "Cannot create target root directory.", "Failed to create the target root directory.")
MAKE_ERROR_MSG(-1024, CREATE_CONFIG_DIR_ERROR, "Cannot create config target directory.", "Failed to create the config target directory.")
MAKE_ERROR_MSG(-1026, NO_SOURCE_FILES, "No source files were found.", "No source files were found to add to the package.")
MAKE_ERROR_MSG(-1027, PACKAGE_CREATION_ERROR, "A compose package error has occurred.", "An error has occurred during the package creation.")
MAKE_ERROR_MSG(-1028, PACKAGE_READ_ERROR, "A read package error has occurred.", "An error has occurred while reading the package.")
////////// SDV TRACE MONITOR ERROR CODES ////////////
MAKE_ERROR_MSG(-5500, TRACE_MON_REG_HNDLR_ERROR, "Failed to register application control handler.", "The OS returned an error during the registration of the application control handler.")
MAKE_ERROR_MSG(-5501, TRACE_MON_FIFO_OPEN_ERROR, "Failed to open the trace fifo.", "Failure trying to open a connection to the trace fifo.")
MAKE_ERROR_MSG(-2051, LOAD_DBC_FILE_ERROR, "Cannot load the DBC file.", "Trying to read the DBC file failed.")
MAKE_ERROR_MSG(-2052, COMPILE_ERROR, "Failed to compile.", "An compilation attempt failed.")
MAKE_ERROR_MSG(-2052, BASIC_SERVICE_DATA_ERROR, "Cannot find vehicle device.", "Creating basic service component failed.")
#ifdef WIN32
#ifdef SUPPRESS_NO_ERROR
#pragma pop_macro("NO_ERROR")
#endif
#endif
#endif // !defined EXECUTABLES_ERROR_MSG

View File

@@ -0,0 +1,23 @@
# Define project
project (sdv_control VERSION 1.0 LANGUAGES CXX)
# Add include directories
include_directories(../export)
# Define the executable
add_executable(sdv_control
main.cpp
"list_elements.cpp"
"list_elements.h"
"startup_shutdown.h"
"startup_shutdown.cpp"
"context.h"
"print_table.h" "start_stop_service.cpp" "start_stop_service.h" "installation.h" "installation.cpp")
target_link_libraries(sdv_control ${CMAKE_DL_LIBS})
# Build dependencies
add_dependencies(sdv_control core_services)
# Appending the executable to the service list
set(SDV_Executable_List ${SDV_Executable_List} sdv_control PARENT_SCOPE)

View File

@@ -0,0 +1,52 @@
#ifndef CONTEXT_H
#define CONTEXT_H
#include <cstdint>
#include <filesystem>
#include <support/string.h>
#include <support/sequence.h>
/**
* @brief SDV context
*/
struct SContext
{
bool bSilent = false; ///< Silence flag
bool bVerbose = false; ///< Verbose flag
bool bServerSilent = false; ///< Silence flag of server
bool bServerVerbose = false; ///< Verbose flag of server
uint32_t uiInstanceID = 1000; ///< Instance ID
bool bListNoHdr = false; ///< Do not print a header with the listing table.
bool bListShort = false; ///< Print only a shortened list with one column.
sdv::sequence<sdv::u8string> seqCmdLine; ///< The commands provided on the command line.
std::filesystem::path pathInstallDir; ///< Optional installation directory.
};
#include <cctype> // std::tolower
#include <algorithm> // std::equal
/**
* @brief Compare whether two characters are identical when both are converted to lower case.
* @remarks Cannot deal with Unicode letters comprising of more than one characters.
* @param[in] cLeft First character
* @param[in] cRight Second character
* @return The case independent equality of the characters.
*/
inline bool ichar_equals(char cLeft, char cRight)
{
return std::tolower(static_cast<unsigned char>(cLeft)) ==
std::tolower(static_cast<unsigned char>(cRight));
}
/**
* @brief Compare for case-independent equality.
* @param[in] rssLeft Reference to the left string.
* @param[in] rssRight Reference to the right string.
* @return The case-independent equality of the strings.
*/
inline bool iequals(const std::string& rssLeft, const std::string& rssRight)
{
return std::equal(rssLeft.begin(), rssLeft.end(), rssRight.begin(), rssRight.end(), ichar_equals);
}
#endif // !defined CONTEXT_H

View File

@@ -0,0 +1,124 @@
#include "installation.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <interfaces/process.h>
#include <support/interface_ptr.h>
#include <support/local_service_access.h>
#include <interfaces/com.h>
#include <interfaces/app.h>
#include "../error_msg.h"
void InstallationHelp(const SContext& rsContext)
{
// First argument should be "INSTALL", "UPDATE" or "UNINSTALL".
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing install/update/uninstall command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "INSTALL"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control INSTALL <name> [files...] [options...]\n\n"
"Make a new installation with a set of files. This command succeeds when the files contain at least one component "
"module and another installation with the same name is not existing. Complex services are automatically added to the "
"configuration.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "UPDATE"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control UPDATE <name> [files...] [options...]\n\n"
"Update an existing installation. This will not remove any configuration (unless a component is not available any "
"more after, or a new component was added by the update.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "UNINSTALL"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control UNINSTALL <name> [files...] [options...]\n\n"
"Remove an existing installation. This will remove the configuration for the components as well. Dependent components "
"become inactive and will be activated again when the component is installed again.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid install/update/uninstall command..." << std::endl;
}
int Install(const SContext& rsContext)
{
// First argument should be "INSTALL"
if (rsContext.seqCmdLine.size() < 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Not enough parameters for installation command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "INSTALL"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command : " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Followed by the installation name
//std::string ssName = rsContext.seqCmdLine[1];
#if 0
// Determine the service to start
sdv::u8string ssClass, ssName;
if (rsContext.seqCmdLine.size() == 3)
{
ssClass = rsContext.seqCmdLine[1];
ssName = rsContext.seqCmdLine[2];
} else
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::core::IRepositoryControl* pRepoControl = nullptr;
if (pObjectAccess)
pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryControl>();
if (!pRepoControl)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPO_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Create the object
sdv::core::TObjectID tObjectID = pRepoControl->CreateObject(ssClass, ssName, {});
if (!tObjectID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << START_OBJECT_ERROR_ERROR_MSG << " Object name: " << ssName << std::endl;
return START_OBJECT_ERROR;
}
#endif
// All good...
return NO_ERROR;
}
int Update(const SContext& /*rsContext*/)
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
}
int Uninstall(const SContext& /*rsContext*/)
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,39 @@
#ifndef INSTALLATION_H
#define INSTALLATION_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
#include <support/interface_ptr.h>
/**
* @brief Help for installing, updating and uninstalling components.
* @param[in] rsContext Reference to the context.
*/
void InstallationHelp(const SContext& rsContext);
/**
* @brief Install components.
* @details The command line context.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int Install(const SContext& rsContext);
/**
* @brief Update installation.
* @details The command line context.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int Update(const SContext& rsContext);
/**
* @brief Uninstall components.
* @details The command line context.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int Uninstall(const SContext& rsContext);
#endif // !defined INSTALLATION_H

View File

@@ -0,0 +1,416 @@
#include "list_elements.h"
#include "print_table.h"
#include <interfaces/config.h>
#include <support/sdv_core.h>
#include <support/local_service_access.h>
#include "../../global/cmdlnparser/cmdlnparser.h"
#include "../../global/exec_dir_helper.h"
#include "../../global/flags.h"
#include "../error_msg.h"
void ListHelp(const SContext& rsContext)
{
// First argument should be "LIST"
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing listing command..." << std::endl;
return;
}
if (!iequals(rsContext.seqCmdLine[0], "LIST"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return;
}
// Which list command help is requested
enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown;
if (rsContext.seqCmdLine.size() >= 2)
{
if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules;
else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes;
else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components;
else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations;
else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections;
}
// Print help if requested
if (!rsContext.bSilent)
{
switch (eListCommand)
{
case EListCommand::modules:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST MODULES [options...]\n\nShow a list of all loaded modules.\n");
break;
case EListCommand::classes:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CLASSES [options...]\n\nShow a list of available component classes.\n");
break;
case EListCommand::components:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST COMPONENTS [options...]\n\nShow a list of all loaded components.\n");
break;
case EListCommand::installations:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST INSTALLATIONS [options...]\n\nShow a list of all installations.\n");
break;
case EListCommand::connections:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CONNECTIONS [options...]\n\nShow a list of all connected applications.\n");
break;
default:
CCommandLine::PrintHelpText(std::cout, R"code(Usage: sdv_control LIST <list_command> [options...]
Supported listing commands:
LIST MODULES Show a list of loaded server modules.
LIST CLASSES Show a list of available component classes.
LIST COMPONENTS Show a list of instantiated server components
LIST INSTALLATIONS Show a list of installations.
LIST CONNECTIONS Show a list of current connections.
)code");
break;
}
std::cout << "Options:\n";
std::cout << " --no_header Do not print a header for the listing table.\n";
std::cout << " --short Print only the most essential information as one column.\n\n";
}
}
int ListElements(const SContext& rsContext, std::ostream& rstream /*= std::cout*/)
{
// First argument should be "LIST"
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "LIST"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Which list command help is requested
enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown;
if (rsContext.seqCmdLine.size() >= 2)
{
if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules;
else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes;
else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components;
else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations;
else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections;
}
else
{
if (!rsContext.bSilent)
{
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command.." << std::endl << std::endl;
ListHelp(rsContext);
}
return CMDLN_ARG_ERR;
}
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << "." << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
int iRet = -100;
switch (eListCommand)
{
case EListCommand::modules:
iRet = ListModules(rsContext, ptrRepository, rstream);
break;
case EListCommand::classes:
iRet = ListClasses(rsContext, ptrRepository, rstream);
break;
case EListCommand::components:
iRet = ListComponents(rsContext, ptrRepository, rstream);
break;
case EListCommand::installations:
iRet = ListInstallations(rsContext, ptrRepository, rstream);
break;
case EListCommand::connections:
iRet = ListConnections(rsContext, ptrRepository, rstream);
break;
default:
break;
}
return iRet;
}
int ListModules(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::core::IModuleInfo* pModuleInfo = nullptr;
if (pObjectAccess)
pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).
GetInterface<sdv::core::IModuleInfo>();
if (!pModuleInfo)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << MODULE_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return MODULE_CONTROL_SERVICE_ACCESS_ERROR;
}
// Relative path from exe
auto fnPrintRelPath = [](const sdv::u8string& rssPath)
{
std::filesystem::path path = rssPath.c_str();
if (path.is_relative()) return path.generic_u8string();
return path.lexically_relative(GetExecDirectory()).generic_u8string();
};
// Create the module list
sdv::sequence<sdv::core::SModuleInfo> seqModules = pModuleInfo->GetModuleList();
if (rsContext.bListShort)
{
std::vector<std::array<std::string, 1>> vecShortModuleList;
vecShortModuleList.push_back({ "Path" });
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
vecShortModuleList.push_back({ fnPrintRelPath(rsModuleInfo.ssPath) });
// Print the module list
PrintTable(vecShortModuleList, rstream, rsContext.bListNoHdr);
}
else
{
std::vector<std::array<std::string, 5>> vecModuleList;
vecModuleList.push_back({ "Module ID", "Filename", "Version", "Active", "Path" });
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
vecModuleList.push_back({
std::to_string(static_cast<int64_t>(rsModuleInfo.tModuleID)),
rsModuleInfo.ssFilename,
std::to_string(rsModuleInfo.uiVersion / 100) + "." + std::to_string(rsModuleInfo.uiVersion % 100),
(rsModuleInfo.bActive ? " +" : " -"),
fnPrintRelPath(rsModuleInfo.ssPath) });
// Print the module list
PrintTable(vecModuleList, rstream, rsContext.bListNoHdr);
}
return NO_ERROR;
}
int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::core::IModuleInfo* pModuleInfo = nullptr;
if (pObjectAccess)
pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).
GetInterface<sdv::core::IModuleInfo>();
if (!pModuleInfo)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << MODULE_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return MODULE_CONTROL_SERVICE_ACCESS_ERROR;
}
// Class type
auto fnPrintClassType = [](sdv::EObjectType eType)
{
switch (eType)
{
case sdv::EObjectType::SystemObject: return "SystemObject";
case sdv::EObjectType::Device: return "Device";
case sdv::EObjectType::BasicService: return "BasicService";
case sdv::EObjectType::ComplexService: return "ComplexService";
case sdv::EObjectType::Application: return "Application";
case sdv::EObjectType::Proxy: return "Proxy";
case sdv::EObjectType::Stub: return "Stub";
case sdv::EObjectType::Utility: return "Utility";
default: return "Unknown";
}
};
// Class flags
auto fnPrintClassFlags = [](uint32_t uiFlags)
{
hlpr::flags<sdv::EObjectFlags> flags(uiFlags);
std::stringstream sstream;
if (flags.check(sdv::EObjectFlags::singleton))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Singleton";
}
return std::string("{") + sstream.str() + std::string("}");
};
// Dependencies
auto fnPrintList = [](const sdv::sequence<sdv::u8string>& rseqList)
{
std::stringstream sstream;
for (const sdv::u8string& rss : rseqList)
{
if (!sstream.str().empty()) sstream << ", ";
sstream << rss;
}
return std::string("{") + sstream.str() + std::string("}");
};
// Create the module list
if (rsContext.bListShort)
{
std::vector<std::array<std::string, 2>> vecShortClassList;
vecShortClassList.push_back({ "Class name", "Aliases"});
sdv::sequence<sdv::core::SModuleInfo> seqModules = pModuleInfo->GetModuleList();
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
{
sdv::sequence<sdv::SClassInfo> seqClasses = pModuleInfo->GetClassList(rsModuleInfo.tModuleID);
for (const sdv::SClassInfo& rsClassInfo : seqClasses)
vecShortClassList.push_back({ rsClassInfo.ssClassName, fnPrintList(rsClassInfo.seqClassAliases) });
}
// Print the module list
PrintTable(vecShortClassList, rstream, rsContext.bListNoHdr);
}
else
{
std::vector<std::array<std::string, 7>> vecClassList;
vecClassList.push_back({ "Class name", "Aliases", "Default name", "Type", "Flags", "Module ID", "Dependencies" });
sdv::sequence<sdv::core::SModuleInfo> seqModules = pModuleInfo->GetModuleList();
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
{
sdv::sequence<sdv::SClassInfo> seqClasses = pModuleInfo->GetClassList(rsModuleInfo.tModuleID);
for (const sdv::SClassInfo& rsClassInfo : seqClasses)
vecClassList.push_back({
rsClassInfo.ssClassName,
fnPrintList(rsClassInfo.seqClassAliases),
rsClassInfo.ssDefaultObjectName,
fnPrintClassType(rsClassInfo.eType),
fnPrintClassFlags(rsClassInfo.uiFlags),
std::to_string(static_cast<int64_t>(rsModuleInfo.tModuleID)),
fnPrintList(rsClassInfo.seqDependencies) });
}
// Print the module list
PrintTable(vecClassList, rstream, rsContext.bListNoHdr);
}
return NO_ERROR;
}
int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::core::IRepositoryInfo* pRepoInfo = nullptr;
if (pObjectAccess)
pRepoInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryInfo>();
if (!pRepoInfo)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Class type
auto fnPrintClassType = [](sdv::EObjectType eType)
{
switch (eType)
{
case sdv::EObjectType::SystemObject: return "SystemObject";
case sdv::EObjectType::Device: return "Device";
case sdv::EObjectType::BasicService: return "BasicService";
case sdv::EObjectType::ComplexService: return "ComplexService";
case sdv::EObjectType::Application: return "Application";
case sdv::EObjectType::Proxy: return "Proxy";
case sdv::EObjectType::Stub: return "Stub";
case sdv::EObjectType::Utility: return "Utility";
default: return "Unknown";
}
};
// Object flags
auto fnPrintObjectFlags = [](uint32_t uiFlags)
{
hlpr::flags<sdv::core::EObjectInfoFlags> flags(uiFlags);
std::stringstream sstream;
if (flags.check(sdv::core::EObjectInfoFlags::object_controlled))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Controlled";
}
if (flags.check(sdv::core::EObjectInfoFlags::object_foreign))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Foreign";
}
if (flags.check(sdv::core::EObjectInfoFlags::object_isolated))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Isolated";
}
return std::string("{") + sstream.str() + std::string("}");
};
// Create the object list
if (rsContext.bListShort)
{
std::vector<std::array<std::string, 6>> vecShortObjectList;
vecShortObjectList.push_back({ "Object name" });
sdv::sequence<sdv::core::SObjectInfo> seqObjects = pRepoInfo->GetObjectList();
for (const sdv::core::SObjectInfo& rsObjectInfo : seqObjects)
vecShortObjectList.push_back({ rsObjectInfo.ssObjectName });
// Print the module list
PrintTable(vecShortObjectList, rstream, rsContext.bListNoHdr);
}
else
{
std::vector<std::array<std::string, 6>> vecObjectList;
vecObjectList.push_back({ "Object ID", "Class name", "Object name", "Type", "Flags", "Module ID" });
sdv::sequence<sdv::core::SObjectInfo> seqObjects = pRepoInfo->GetObjectList();
for (const sdv::core::SObjectInfo& rsObjectInfo : seqObjects)
vecObjectList.push_back({
std::to_string(static_cast<int64_t>(rsObjectInfo.tModuleID)),
rsObjectInfo.sClassInfo.ssClassName,
rsObjectInfo.ssObjectName,
fnPrintClassType(rsObjectInfo.sClassInfo.eType),
fnPrintObjectFlags(rsObjectInfo.uiFlags),
std::to_string(static_cast<int64_t>(rsObjectInfo.tModuleID)) });
// Print the module list
PrintTable(vecObjectList, rstream, rsContext.bListNoHdr);
}
return NO_ERROR;
}
int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::installation::IAppInstall* pAppInstall = nullptr;
if (pObjectAccess)
pAppInstall = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ConfigService")).
GetInterface<sdv::installation::IAppInstall>();
if (!pAppInstall)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONFIG_SERVICE_ACCESS_ERROR_MSG << std::endl;
return CONFIG_SERVICE_ACCESS_ERROR;
}
// Create the object list
std::vector<std::array<std::string, 1>> vecInstallList;
vecInstallList.push_back({"Installation"});
sdv::sequence<sdv::u8string> seqInstallations = pAppInstall->GetInstallations();
for (const sdv::u8string& rssInstallation : seqInstallations)
vecInstallList.push_back({
rssInstallation});
// Print the module list
PrintTable(vecInstallList, rstream, rsContext.bListNoHdr);
return NO_ERROR;
}
int ListConnections(const SContext& /*rsContext*/, const sdv::TObjectPtr& /*rptrRepository*/, std::ostream& /*rstream = std::cout */ )
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,69 @@
#ifndef LIST_ELEMENTS_H
#define LIST_ELEMENTS_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
#include <support/interface_ptr.h>
/**
* @brief Help for listing elements.
* @param[in] rsContext Reference to the context.
*/
void ListHelp(const SContext& rsContext);
/**
* @brief List elements in a table. Base function. The elements to list are to be parsed from the arguments. The context could
* indicate additional flags.
* @param[in] rsContext Reference to the context.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListElements(const SContext& rsContext, std::ostream& rstream = std::cout);
/**
* @brief List the loaded server modules.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListModules(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the available component classes.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the instantiated server components.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the installations.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the current connections.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListConnections(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
#endif // !defined LIST_ELEMENTS_H

View File

@@ -0,0 +1,223 @@
#include "../../global/process_watchdog.h"
#include <support/sdv_core.h>
#include "../../global/cmdlnparser/cmdlnparser.cpp"
#include "../../global/exec_dir_helper.h"
#include <support/mem_access.h>
#include <support/sdv_core.h>
#include <support/app_control.h>
#include <iostream>
#include <array>
#include <algorithm>
#include <thread>
#include "context.h"
#include "startup_shutdown.h"
#include "list_elements.h"
#include "start_stop_service.h"
#include "installation.h"
#include "../error_msg.h"
/**
* @brief Add a slag to the string.
* @param[in] rssString The string to add a flag to.
* @param[in] rssFlag The flag text to add.
*/
void AddFlagString(std::string& rssString, const std::string& rssFlag)
{
if (!rssString.empty()) rssString += " | ";
rssString += rssFlag;
}
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int iArgc, const wchar_t* rgszArgv[])
#else
extern "C" int main(int iArgc, const char* rgszArgv[])
#endif
{
// Workaround for GCC to make certain that POSIX thread library is loaded before the components are loaded.
// REASON: If the first call to a thread is done in a dynamic library, the application is already classified as single
// threaded and a termination is initiated.
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: This task has been taken over by the process watchdog.
CProcessWatchdog watchdog;
// If not set, set the runtime location to the EXE directory.
if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty())
sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory());
if (sdv::app::CAppControl::GetComponentInstallDirectory().empty())
sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory());
CCommandLine cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character));
SContext sContext;
bool bError = false;
bool bHelp = false;
bool bVersion = false;
std::string ssArgError;
try
{
auto& rArgHelpDef = cmdln.DefineOption("?", bHelp, "Show help. Use <COMMAND> --help for specific help on the command.");
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = cmdln.DefineOption("s", sContext.bSilent, "Do not show any information on std::cout. Not compatible with 'verbose'.");
rArgSilentDef.AddSubOptionName("silent");
auto& rArgVerboseDef = cmdln.DefineOption("v", sContext.bVerbose, "Provide verbose information. Not compatible with 'silent'.");
rArgVerboseDef.AddSubOptionName("verbose");
cmdln.DefineSubOption("version", bVersion, "Show version information.");
cmdln.DefineSubOption("instance", sContext.uiInstanceID, "The instance ID of the SDV instance (default ID is 1000).");
cmdln.DefineSubOption("server_silent", sContext.bServerSilent, "Only used with STARTUP command: Server is started using "
"silent option. Not compatible with 'server_verbose'.");
cmdln.DefineSubOption("server_verbose", sContext.bServerVerbose, "Only used with STARTUP command: Server is started using "
"verbose option. Not compatible with 'server_silent'.");
cmdln.DefineSubOption("install_dir", sContext.pathInstallDir, "Only used with STARTUP command: Installation directory "
"(absolute or relative to the sdv_core executable).");
cmdln.DefineSubOption("no_header", sContext.bListNoHdr, "Only used with LIST command: Do not print a header for the "
"listing table.");
cmdln.DefineSubOption("short", sContext.bListShort, "Only used with LIST command: Print only the most essential "
"information as one column.");
cmdln.DefineDefaultArgument(sContext.seqCmdLine, "COMMAND");
cmdln.Parse(static_cast<size_t>(iArgc), rgszArgv);
} catch (const SArgumentParseException& rsExcept)
{
ssArgError = rsExcept.what();
bHelp = true;
bError = true;
}
if (!sContext.bSilent)
{
std::cout << "SDV Server Control Utility" << std::endl;
std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl;
std::cout << "Author: Erik Verhoeven" << std::endl << std::endl;
}
if (!ssArgError.empty() && !sContext.bSilent)
std::cerr << "ERROR: " << ssArgError << std::endl;
if (!bHelp && !bVersion && sContext.seqCmdLine.empty())
{
if (!sContext.bSilent)
std::cerr << "ERROR: Missing command!" << std::endl;
bHelp = true;
bError = true;
}
enum class ECommand { unknown, startup, shutdown, list, install, update, uninstall, start, stop } eCommand = ECommand::unknown;
if (!sContext.seqCmdLine.empty())
{
if (iequals(sContext.seqCmdLine[0], "STARTUP")) eCommand = ECommand::startup;
else if (iequals(sContext.seqCmdLine[0], "SHUTDOWN")) eCommand = ECommand::shutdown;
else if (iequals(sContext.seqCmdLine[0], "LIST")) eCommand = ECommand::list;
else if (iequals(sContext.seqCmdLine[0], "INSTALL")) eCommand = ECommand::install;
else if (iequals(sContext.seqCmdLine[0], "UPDATE")) eCommand = ECommand::update;
else if (iequals(sContext.seqCmdLine[0], "UNINSTALL")) eCommand = ECommand::uninstall;
else if (iequals(sContext.seqCmdLine[0], "START")) eCommand = ECommand::start;
else if (iequals(sContext.seqCmdLine[0], "STOP")) eCommand = ECommand::stop;
else
{
if (!sContext.bSilent)
std::cerr << "ERROR: Invalid command: " << sContext.seqCmdLine[0] << std::endl;
bHelp = true;
bError = true;
}
}
if (bHelp)
{
if (!sContext.bSilent)
{
if (bError)
std::cout << std::endl;
switch (eCommand)
{
case ECommand::startup:
case ECommand::shutdown:
StartupShutdownHelp(sContext);
break;
case ECommand::start:
case ECommand::stop:
StartStopServiceHelp(sContext);
break;
case ECommand::list:
ListHelp(sContext);
break;
case ECommand::install:
case ECommand::update:
case ECommand::uninstall:
InstallationHelp(sContext);
break;
default:
cmdln.PrintHelp(std::cout, R"code(Supported commands:
STARTUP Start the core application server
SHUTDOWN Stop the core application server
LIST List module/classes/component/installation/connection information.
INSTALL Install a new application or service.
UPDATE Update an existing installation.
UNINSTALL Uninstall an installed application or service.
START Start a service (complex services only).
STOP Stop a service (complex services only).
)code");
break;
}
}
return bError ? CMDLN_ARG_ERR : NO_ERROR;
}
if (bError) return CMDLN_ARG_ERR;
if (bVersion)
std::cout << "Version: " << (SDVFrameworkBuildVersion / 100) << "." << (SDVFrameworkBuildVersion % 100) << " build " <<
SDVFrameworkSubbuildVersion << " interface " << SDVFrameworkInterfaceVersion << std::endl;
if (sContext.seqCmdLine.empty()) return NO_ERROR; // Done.
// Start the application control
if (sContext.bVerbose)
std::cout << "Starting local application control.." << std::endl;
sdv::app::CAppControl appcontrol;
std::string ssAppConfig = std::string(R"code(
[Application]
Mode = "Maintenance"
Instance = )code") + std::to_string(sContext.uiInstanceID) + R"code(
)code";
if (!appcontrol.Startup(ssAppConfig))
{
if (!sContext.bSilent)
std::cerr << "ERROR: " << APP_CONTROL_STARTUP_ERROR_MSG << std::endl;
return APP_CONTROL_STARTUP_ERROR;
}
int iRet = 0;
switch (eCommand)
{
case ECommand::startup:
iRet = StartupSDVServer(sContext);
break;
case ECommand::shutdown:
iRet = ShutdownSDVServer(sContext);
break;
case ECommand::list:
iRet = ListElements(sContext);
break;
case ECommand::start:
iRet = StartService(sContext);
break;
case ECommand::stop:
iRet = StopService(sContext);
break;
case ECommand::install:
iRet = Install(sContext);
break;
case ECommand::update:
iRet = Update(sContext);
break;
case ECommand::uninstall:
iRet = Uninstall(sContext);
break;
default:
std::cout << "Command missing :-(" << std::endl;
break;
}
if (sContext.bVerbose) std::cout << "Shutting down local application control.." << std::endl;
appcontrol.Shutdown();
return iRet;
}

View File

@@ -0,0 +1,83 @@
#ifndef TABLE_H
#define TABLE_H
#include <string>
#include <vector>
#include <array>
#include <iostream>
/**
* @brief Print a table of a vector of rows.
* @tparam nSize The array size.
* @details the first line is the header.
* @param[in] rvecRows Reference to the vector of rows.
* @param[in] rstream The output stream to use for printing.
* @param[in] bNoHeader When set, skip the first row (the header row).
*/
template <size_t nSize>
void PrintTable(const std::vector<std::array<std::string, nSize>>& rvecRows, std::ostream& rstream, bool bNoHeader = false)
{
// Calculate the maximum length of the column by going through each cell.
bool bSkipFirst = bNoHeader;
size_t rgnLength[nSize] = {};
for (const auto& rrgssRow : rvecRows)
{
if (bSkipFirst)
{
bSkipFirst = false;
continue;
}
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
{
if (rrgssRow[nIndex].size() > rgnLength[nIndex])
rgnLength[nIndex] = rrgssRow[nIndex].size();
}
}
// Print a cell
auto fnPrintCell = [&](size_t nIndex, const std::string& rssText)
{
// Print the text
rstream << rssText;
// Check index
if (nIndex == nSize - 1) // Last column; nl needed
rstream << std::endl;
else // Separator needed
rstream << std::string(rgnLength[nIndex] - rssText.size() + 2, ' ');
};
// Print each line
bSkipFirst = bNoHeader;
bool bInitialLine = !bNoHeader;
for (const auto& rrgssRow : rvecRows)
{
if (bSkipFirst)
{
bSkipFirst = false;
continue;
}
// Start with a space if a header is present
if (!bNoHeader)
rstream << " ";
// Print the text
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
fnPrintCell(nIndex, rrgssRow[nIndex]);
// Need a vertical separator?
if (bInitialLine)
{
size_t nSeparatorLength = 1;
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
nSeparatorLength += rgnLength[nIndex] + 2;
nSeparatorLength--;
rstream << std::string(nSeparatorLength, '-') << std::endl;
bInitialLine = false;
}
}
}
#endif // !defined TABLE_H

View File

@@ -0,0 +1,176 @@
#include "start_stop_service.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <interfaces/process.h>
#include <support/interface_ptr.h>
#include <support/local_service_access.h>
#include <interfaces/com.h>
#include <interfaces/app.h>
#include "../error_msg.h"
void StartStopServiceHelp(const SContext& rsContext)
{
// First argument should be "START" or "STOP".
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing start/stop command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "START"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control START <name> [options...]\n"
" sdv_control START <class> <name> [options...]\n\n"
"Start a complex service with the supplied object name as is defined in the configuration (first usage) or the "
"supplied class name and with object name assignment and add to the configuration (second usage).\n"
"Only complex services can be started. If the service depends on not running other services, these are started as "
"well.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "STOP"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control STOP <name> [options...]\n"
" sdv_control STOP <ID> [options...]\n"
" sdv_control STOP <class> [options...]\n"
" sdv_control STOP <module> [options...]\n\n"
"Stop the complex service(s) with the supplied object name (first usage), with supplied object ID (second usage), with "
"supplied class name (third usage) or contained in the module with the supplied module name (fourth usage).\n"
"Only complex services can be stopped. Dependent services are stopped as well. If one of object to be stopped is not a "
"complex service, the command fails.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid start/stop command..." << std::endl;
}
int StartService(const SContext& rsContext)
{
// First argument should be "START"
if (rsContext.seqCmdLine.size() < 2)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Not enough parameters for START command." << std::endl;
return CMDLN_ARG_ERR;
}
if (rsContext.seqCmdLine.size() > 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Too many parameters for START command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "START"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Determine the service to start
sdv::u8string ssClass, ssName;
if (rsContext.seqCmdLine.size() == 3)
{
ssClass = rsContext.seqCmdLine[1];
ssName = rsContext.seqCmdLine[2];
} else
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::core::IRepositoryControl* pRepoControl = nullptr;
if (pObjectAccess)
pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryControl>();
if (!pRepoControl)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Create the object
sdv::core::TObjectID tObjectID = pRepoControl->CreateObject(ssClass, ssName, {});
if (!tObjectID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << START_OBJECT_ERROR_MSG << " Object name: " << ssName << std::endl;
return START_OBJECT_ERROR;
}
// All good...
return NO_ERROR;
}
int StopService(const SContext& rsContext)
{
// First argument should be "STOP"
if (rsContext.seqCmdLine.size() < 2)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Not enough parameters for STOP command." << std::endl;
return CMDLN_ARG_ERR;
}
if (rsContext.seqCmdLine.size() > 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Too many parameters for STOP command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "STOP"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command : " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Determine the service to start
sdv::u8string ssClass, ssName;
if (rsContext.seqCmdLine.size() == 3)
{
ssClass = rsContext.seqCmdLine[1];
ssName = rsContext.seqCmdLine[2];
} else
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::core::IRepositoryControl* pRepoControl = nullptr;
if (pObjectAccess)
pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryControl>();
if (!pRepoControl)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Create the object
sdv::core::TObjectID tObjectID = pRepoControl->DestroyObject(ssName);
if (!tObjectID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << STOP_OBJECT_ERROR_MSG << " Object name: " << ssName << std::endl;
return STOP_OBJECT_ERROR;
}
// All good...
return NO_ERROR;
}

View File

@@ -0,0 +1,30 @@
#ifndef START_STOP_SERVICE_H
#define START_STOP_SERVICE_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
#include <support/interface_ptr.h>
/**
* @brief Help for start or stop a complex service.
* @param[in] rsContext Reference to the context.
*/
void StartStopServiceHelp(const SContext& rsContext);
/**
* @brief Start the service.
* @details The command line context includes the service to start or stop.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int StartService(const SContext& rsContext);
/**
* @brief Stop the complex service.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int StopService(const SContext& rsContext);
#endif // !defined START_STOP_SERVICE_H

View File

@@ -0,0 +1,206 @@
#include "startup_shutdown.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <interfaces/process.h>
#include <support/interface_ptr.h>
#include <support/local_service_access.h>
#include <interfaces/com.h>
#include <interfaces/app.h>
#include "../error_msg.h"
sdv::process::TProcessID g_tServerProcessID = 0;
/**
* @brief Process lifetime monitor struct
*/
struct SProcessLifetimeCallback : sdv::IInterfaceAccess, sdv::process::IProcessLifetimeCallback
{
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::process::IProcessLifetimeCallback)
END_SDV_INTERFACE_MAP()
/**
* @brief Called when the process was terminated.
* @remarks The process return value is not always valid. The validity depends on the support of the underlying system.
* @param[in] tProcessID The process ID of the process being terminated.
* @param[in] iRetValueParam Process return value or 0 when not supported.
*/
virtual void ProcessTerminated([[maybe_unused]] /*in*/ sdv::process::TProcessID tProcessID, /*in*/ int64_t iRetValueParam) override
{
iRetValue = iRetValueParam;
}
/**
* @brief Get the process return value or 0 when no value was set.
* @return The process return value.
*/
int64_t GetRetValue() const
{
return iRetValue;
}
int64_t iRetValue = 0; ///< Process return value to be updated by the process monitor.
};
void StartupShutdownHelp(const SContext& rsContext)
{
// First and only argument should be "STARTUP" or "SHUTDOWN".
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing startup/shutdown command..." << std::endl;
return;
}
if (rsContext.seqCmdLine.size() > 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: too many commands following startup/shutdown command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "STARTUP"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control STARTUP [options...]\n\n"
"Start the SDV server with the supplied instance ID. If no instance ID is supplied, use the default instance "
"ID #1000.\n\n"
"Options:\n"
" --server_silent Server is started using silent option. Not compatible with 'server_verbose'.\n"
" --server_verbose Server is started using verbose option. Not compatible with 'server_silent'.\n"
" --install_dir Installation directory (absolute or relative to the sdv_core executable).\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "SHUTDOWN"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control SHUTDOWN [options...]\n\nStop the SDV server with the supplied "
"instance ID. If no instance ID is supplied, use the default instance ID #1000.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid startup/shutdown command..." << std::endl;
}
int StartupSDVServer(const SContext& rsContext)
{
if (rsContext.seqCmdLine.size() != 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid arguments following STARTUP command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!rsContext.bSilent)
std::cout << "Starting the SDV Server..." << std::endl;
// Get the process control service
sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject<sdv::process::IProcessControl>("ProcessControlService");
sdv::process::IProcessLifetime* pProcessLifetime = sdv::core::GetObject<sdv::process::IProcessLifetime>("ProcessControlService");
if (!pProcessControl || !pProcessLifetime)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << PROCESS_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return PROCESS_CONTROL_SERVICE_ACCESS_ERROR;
}
sdv::sequence<sdv::u8string> seqArgTemp;
if (rsContext.bVerbose)
std::cout << "Instance ID: " << rsContext.uiInstanceID << std::endl;
seqArgTemp.push_back("-server");
seqArgTemp.push_back("--instance" + std::to_string(rsContext.uiInstanceID));
seqArgTemp.push_back("--no_banner");
if (rsContext.bServerSilent) seqArgTemp.push_back("--silent");
if (rsContext.bServerVerbose) seqArgTemp.push_back("--verbose");
if (!rsContext.pathInstallDir.empty())
seqArgTemp.push_back("--install_dir\"" + rsContext.pathInstallDir.generic_u8string() + "\"");
g_tServerProcessID = pProcessControl->Execute("sdv_core", seqArgTemp, sdv::process::EProcessRights::parent_rights);
if (!g_tServerProcessID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << STARTUP_CORE_ERROR_MSG << std::endl;
return STARTUP_CORE_ERROR;
}
// Register the process monitor...
SProcessLifetimeCallback sProcessLifetimeCallback;
uint32_t uiCookie = pProcessLifetime->RegisterMonitor(g_tServerProcessID, &sProcessLifetimeCallback);
if (!uiCookie)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REGISTER_PROCESS_MONITOR_ERROR_MSG << std::endl;
return REGISTER_PROCESS_MONITOR_ERROR;
}
if (rsContext.bVerbose)
std::cout << "Process ID: " << g_tServerProcessID << std::endl;
// Wait for 3 seconds to see if the process terminates... (which should not occur).
bool bTerminated = pProcessLifetime->WaitForTerminate(g_tServerProcessID, 3000);
pProcessLifetime->UnregisterMonitor(uiCookie);
if (bTerminated)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CORE_NOT_STARTED_MSG << " Instance: #" << rsContext.uiInstanceID << " Exit code: " <<
sProcessLifetimeCallback.iRetValue << std::endl;
return CORE_NOT_STARTED;
}
// Try to connect
auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " SDV Server instance #:" << rsContext.uiInstanceID <<
std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
if (!rsContext.bSilent)
std::cout << "SDV Server #" << rsContext.uiInstanceID << " has successfully started..." << std::endl;
return NO_ERROR;
}
int ShutdownSDVServer(const SContext& rsContext)
{
if (rsContext.seqCmdLine.size() != 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid arguments following SHUTDOWN command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!rsContext.bSilent)
std::cout << "Connecting to the SDV #" << rsContext.uiInstanceID << " server..." << std::endl;
// Try to connect
auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
if (!rsContext.bSilent)
std::cout << "Requesting shutdown..." << std::endl;
sdv::core::IObjectAccess* pObjAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::app::IAppShutdownRequest* pShutdownRequest = nullptr;
if (pObjAccess)
pShutdownRequest = sdv::TInterfaceAccessPtr(pObjAccess->GetObject("AppControlService")).
GetInterface<sdv::app::IAppShutdownRequest>();
if (pShutdownRequest)
pShutdownRequest->RequestShutdown();
ptrRepository.Clear();
if (!pShutdownRequest)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << APP_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return APP_CONTROL_SERVICE_ACCESS_ERROR;
}
if (!rsContext.bSilent)
std::cout << "Shutdown initiated..." << std::endl;
return NO_ERROR;
}
sdv::process::TProcessID GetServerProcessID()
{
return g_tServerProcessID;
}

View File

@@ -0,0 +1,39 @@
#ifndef STARTUP_SHUTDOWN_H
#define STARTUP_SHUTDOWN_H
#include "context.h"
#include <vector>
#include <string>
#include <support/sequence.h>
#include <support/string.h>
#include <interfaces/process.h>
/**
* @brief Help for startup or shutdown the server.
* @param[in] rsContext Reference to the context.
*/
void StartupShutdownHelp(const SContext& rsContext);
/**
* @brief Startup the SDV server.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int StartupSDVServer(const SContext& rsContext);
/**
* @brief Shutdown the SDV Server.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int ShutdownSDVServer(const SContext& rsContext);
/**
* @brief Get the server process ID after a startup of the server.
* @remarks The process ID is set when server execution is initiated. The presence of the process ID doesn't guarantee a successful
* running of the server.
* @return The process ID if the process execution was initiated. Or zero when the execution failed.
*/
sdv::process::TProcessID GetServerProcessID();
#endif // !defined STARTUP_SHUTDOWN_H

View File

@@ -0,0 +1,16 @@
# Define project
project(sdv_core VERSION 1.0 LANGUAGES CXX)
# build test application
add_executable(sdv_core main.cpp)
if (WIN32)
target_link_libraries(sdv_core ${CMAKE_THREAD_LIBS_INIT} Ws2_32 Winmm Rpcrt4.lib)
else()
target_link_libraries(sdv_core ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} rt)
endif()
# Build dependencies
add_dependencies(sdv_core core_services)
# Appending the executable to the service list
set(SDV_Executable_List ${SDV_Executable_List} sdv_core PARENT_SCOPE)

View File

@@ -0,0 +1,192 @@
#include <support/sdv_core.h>
#include "../../global/cmdlnparser/cmdlnparser.cpp"
#include "../../global/exec_dir_helper.h"
#include <support/app_control.h>
#include "../error_msg.h"
/**
* @brief Main function of the executable.
* @param[in] iArgc Amount of arguments provided.
* @param[in] rgszArgv Array of C-string pointers. The first entry is the name or path to this executable.
* @return Returns the executable's exit code.
*/
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int iArgc, const wchar_t* rgszArgv[])
#else
extern "C" int main(int iArgc, const char* rgszArgv[])
#endif
{
// Workaround for GCC to make certain that POSIX thread library is loaded before the components are loaded.
// REASON: If the first call to a thread is done in a dynamic library, the application is already classified as single
// threaded and a termination is initiated.
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and
// leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing.
bool bThreadStarted = false;
std::thread thread = std::thread([&]()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
bThreadStarted = true;
});
while (!bThreadStarted) {std::this_thread::sleep_for(std::chrono::milliseconds(100));}
if (thread.joinable())
thread.join();
// If not set, set the runtime location to the EXE directory.
if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty())
sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory());
if (sdv::app::CAppControl::GetComponentInstallDirectory().empty())
sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory());
CCommandLine cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character));
bool bHelp = false;
bool bError = false;
bool bSilent = false;
bool bNoBanner = false;
bool bVerbose = false;
bool bVersion = false;
bool bStandalone = false;
bool bServer = false;
std::filesystem::path pathConfig;
uint32_t uiInstanceID = 1000; // Default instance is 1000
std::filesystem::path pathInstallDir;
std::string ssArgError;
try
{
auto& rArgHelpDef = cmdln.DefineOption("?", bHelp, "Show help");
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = cmdln.DefineOption("s", bSilent, "Do not show any information on STDOUT. Not compatible with 'verbose'.");
rArgSilentDef.AddSubOptionName("silent");
auto& rArgVerboseDef = cmdln.DefineOption("v", bVerbose, "Provide verbose information. Not compatible with 'silent'.");
rArgVerboseDef.AddSubOptionName("verbose");
cmdln.DefineSubOption("no_banner", bNoBanner, "Do not show banner information. Ignored when silent is activated.");
cmdln.DefineSubOption("version", bVersion, "Show version information.");
cmdln.DefineSubOption("instance", uiInstanceID, "The instance ID of the SDV instance (default ID is 1000).");
cmdln.DefineOption("local", bStandalone, "Start the local version (default - no IPC available).");
cmdln.DefineOption("server", bServer, "Start the server version (not compatible with the local version).");
cmdln.DefineSubOption("install_dir", pathInstallDir, "Installation directory (absolute or relative to this executable).");
cmdln.DefineDefaultArgument(pathConfig, "Configuration file to start running (compulsory; applicable for local version only).");
cmdln.Parse(static_cast<size_t>(iArgc), rgszArgv);
} catch (const SArgumentParseException& rsExcept)
{
ssArgError = rsExcept.what();
bHelp = true;
bError = true;
}
if (!bSilent && !bNoBanner)
{
std::cout << "SDV core application" << std::endl;
std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl;
std::cout << "Author: Erik Verhoeven" << std::endl << std::endl;
}
if (!ssArgError.empty() && !bSilent)
std::cerr << "ERROR: " << ssArgError << std::endl;
if (bServer && bStandalone)
{
std::cerr << "ERROR: The commandline options -server and -local cannot be supplied at the same time." << std::endl;
bHelp = true;
bError = true;
}
bStandalone = !bServer;
if (!pathConfig.empty() && bServer)
{
std::cerr << "ERROR: Cannot supply a configuration when running as server." << std::endl;
bHelp = true;
bError = true;
}
else if (pathConfig.empty() && bStandalone)
{
std::cerr << "ERROR: Needing a configuration when running as local application." << std::endl;
bHelp = true;
bError = true;
}
if (bHelp)
{
if (!bSilent)
{
if (bError)
std::cout << std::endl;
cmdln.PrintHelp(std::cout, "The core application is used to start the service framework for use by applications. The "
"safe version using component isolation and installation manifests should be started with the -safe option and is "
"for use in the vehicle. For local simulation the stand-alone version can be used.\n");
}
return bError ? CMDLN_ARG_ERR : NO_ERROR;
}
if (bError) return CMDLN_ARG_ERR;
if (bVersion || bVerbose)
std::cout << "Version: " << (SDVFrameworkBuildVersion / 100) << "." << (SDVFrameworkBuildVersion % 100) << " build " <<
SDVFrameworkSubbuildVersion << " interface " << SDVFrameworkInterfaceVersion << std::endl;
if (!bSilent)
{
std::cout << "Running as: " << (bServer ? "server" : "local") << std::endl;
std::cout << "Instance ID: " << uiInstanceID << std::endl;
if (bStandalone)
std::cout << "Config: " << pathConfig.generic_u8string() << std::endl;
}
if (bVerbose)
std::cout << "Installation directory: " << pathInstallDir.generic_u8string() << std::endl;
// Create the startup config
std::stringstream sstreamConfig;
sstreamConfig << "[Application]" << std::endl;
sstreamConfig << "Mode = \"" << (bServer ? "Main" : "Standalone") << "\"" << std::endl;
sstreamConfig << "Instance = " << uiInstanceID << std::endl;
if (!pathConfig.empty())
sstreamConfig << "Config = \"" << pathConfig.generic_u8string() << "\"" << std::endl;
if (!pathInstallDir.empty())
{
if (pathInstallDir.is_relative())
sstreamConfig << "InstallDir = \"" << pathInstallDir.generic_u8string() << "\"" << std::endl;
else
sstreamConfig << "InstallDir = \"" << pathInstallDir.lexically_relative(GetExecDirectory()).generic_u8string() <<
"\"" << std::endl;
}
// Add console information
if (bVerbose || bSilent)
{
sstreamConfig << "[Console]" << std::endl;
sstreamConfig << "Report = ";
if (bSilent) sstreamConfig << "\"Silent\"";
else
sstreamConfig << "\"Verbose\"";
sstreamConfig << std::endl;
}
if (bVerbose)
std::cout << "Starting up..." << std::endl;
// Start the application control
sdv::app::CAppControl appcontrol;
if (!appcontrol.Startup(sstreamConfig.str()))
{
if (!bSilent)
std::cerr << "ERROR: " << APP_CONTROL_STARTUP_ERROR_MSG << std::endl;
return APP_CONTROL_STARTUP_ERROR;
}
// Start the running loop
if (!appcontrol.RunLoop())
{
if (!bSilent)
std::cerr << "ERROR: " << APP_CONTROL_DUPLICATE_INSTANCE_MSG << std::endl;
return APP_CONTROL_DUPLICATE_INSTANCE;
}
if (bVerbose)
{
std::cout << "Shutdown request received..." << std::endl;
std::cout << "Shutting down..." << std::endl;
}
// Shutdwown
appcontrol.Shutdown();
return NO_ERROR;
}

View File

@@ -0,0 +1,29 @@
# Define project
project (sdv_dbc_util VERSION 1.0 LANGUAGES CXX)
# Add include directories
include_directories(../export)
# Define the executable
add_executable(sdv_dbc_util
main.cpp
can_dl.h
can_dl.cpp
fmu.h
fmu.cpp
cmake_generator.cpp
cmake_generator.h
codegen_base.cpp
"fmu_templates.h")
if (WIN32)
target_link_libraries(sdv_dbc_util ${CMAKE_DL_LIBS} Rpcrt4.lib)
else()
target_link_libraries(sdv_dbc_util ${CMAKE_DL_LIBS})
endif()
# Build dependencies
add_dependencies(sdv_dbc_util CompileCoreIDL)
# Appending the executable to the service list
set(SDV_Executable_List ${SDV_Executable_List} sdv_dbc_util PARENT_SCOPE)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,198 @@
#ifndef CAN_DL_H
#define CAN_DL_H
#include "../global/dbcparser/dbcparser.h"
#include <fstream>
#include "codegen_base.h"
/**
* @brief CAN data link generator class.
*/
class CCanDataLinkGen : public CCodeGeneratorBase
{
public:
/**
* @brief Constructor
* @param[in] rpathOutputDir Reference to the outpur directory.
* @param[in] rparser Reference to the DBC parser containing the definitions.
* @param[in] rssVersion Reference to a string representing a version of the dbc file (could be empty).
* @param[in] rssIfcName Reference to the string holding the interface name (could be empty).
* @param[in] nIfcIdx The interface index (valid if no name is provided).
* @param[in] rvecNodes Reference to a list of nodes this data link object is representing. An empty list will implement
* sending and receiving functions for all nodes.
*/
CCanDataLinkGen(const std::filesystem::path& rpathOutputDir, const dbc::CDbcParser& rparser, const std::string& rssVersion,
const std::string& rssIfcName, size_t nIfcIdx, const std::vector<std::string>& rvecNodes);
private:
/**
* @brief Keyword map for keyword replacement in a string.
*/
typedef std::map<std::string, std::string> CKeywordMap;
/**
* @brief Replace keywords in a string.
* @param[in] rssStr Reference to the string containing the keywords.
* @param[in] rmapKeywords Map with keywords to replace.
* @param[in] cMarker Character to identify the keyword with (placed before and after the keyword; e.g. %keyword%).
* @return Returns the string with replacements.
*/
static std::string ReplaceKeywords(const std::string& rssStr, const CKeywordMap& rmapKeywords, char cMarker = '%');
/**
* @brief validates that the string represents a float
* @param[in] rssInput Reference to the string representing a float.
* @return Returns the input string. If '.' not found it adds ".0" to the string
*/
std::string StringMustBeFloatValue(const std::string& rssInput);
/**
* @brief Composes a dbc file version information string
* @param[in] rssVersion Reference to a string representing a version of the dbc file.
* @return The code to insert.
*/
std::string CodeDBCFileVersion(const std::string& rssVersion);
/**
* @brief Provide code to determine the interface index based on the interface name (if supplied).
* @param[in] rssIfcName Reference to the string holding the interface name (could be empty).
* @return The code to insert during initialization.
*/
std::string CodeInitInterfaceIndex(const std::string& rssIfcName);
/**
* @brief Provide code for the RX message definition.
* @param[in] rsMsg Reference to the DBC message to provide the message definition for.
* @return The code to insert.
*/
std::string CodeRxMessageDefinition(const dbc::SMessageDef& rsMsg);
/**
* @brief Provide code for the TX message definition.
* @param[in] rsMsg Reference to the DBC message to provide the message definition for.
* @return The code to insert.
*/
std::string CodeTxMessageDefinition(const dbc::SMessageDef& rsMsg);
/**
* @brief Provide code for the signal declaration.
* @param[in] rsSig Reference to the DBC signal to provide a declaration for.
* @return The code to insert.
*/
std::string CodeSignalDecl(const dbc::SSignalDef& rsSig);
/**
* @brief Initialize the RX message variable.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeInitVarRxMessage(const dbc::SMessageDef& rsMsg);
/**
* @brief Initialize the TX message variable.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeInitVarTxMessage(const dbc::SMessageDef& rsMsg);
/**
* @brief Initialize the RX message structure.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeInitRxMessage(const dbc::SMessageDef& rsMsg);
/**
* @brief Initialize the TX message structure.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeInitTxMessage(const dbc::SMessageDef& rsMsg);
/**
* @brief Terminate the RX message structure.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeTermRxMessage(const dbc::SMessageDef& rsMsg);
/**
* @brief Terminate the TX message structure.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeTermTxMessage(const dbc::SMessageDef& rsMsg);
/**
* @brief Provide the RX message structure functions.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeRxMessageFunctions(const dbc::SMessageDef& rsMsg);
/**
* @brief Provide the TX message structure functions.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @return The code to insert.
*/
std::string CodeTxMessageFunctions(const dbc::SMessageDef& rsMsg);
/**
* @brief Initialize the signal registration.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rsSig Reference to the DBC signal to provide the registration for.
* @return The code to insert.
*/
std::string CodeRegisterRxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig);
/**
* @brief Initialize the signal registration.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rsSig Reference to the DBC signal to provide the registration for.
* @return The code to insert.
*/
std::string CodeRegisterTxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig);
/**
* @brief Remove the signal registration.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rsSig Reference to the DBC signal to provide the unregistration for.
* @return The code to insert.
*/
std::string CodeUnregisterSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig);
/**
* @brief Process the signal data for the specific message.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rsSig Reference to the DBC signal to provide the processing for.
* @return The code to insert.
*/
std::string CodeProcessRxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig);
/**
* @brief Initialize the TX trigger with the signal..
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rsSig Reference to the DBC signal to provide the trigger initialization for.
* @return The code to insert.
*/
std::string InitTrigger(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig);
/**
* @brief Compose the message from the signal data.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rsSig Reference to the DBC signal to provide the composition for.
* @return The code to insert.
*/
std::string CodeComposeTxSignal(const dbc::SMessageDef& rsMsg, const dbc::SSignalDef& rsSig);
std::filesystem::path m_pathProject; ///< Project file path
std::filesystem::path m_pathHeader; ///< Header file path
std::filesystem::path m_pathCpp; ///< C++ file path
std::ofstream m_fstreamHeader; ///< The header file
std::ofstream m_fstreamCpp; ///< The C++ file
const dbc::CDbcParser& m_rparser; ///< Reference to the DBC parser.
};
#endif // !defined CAN_DL_H

View File

@@ -0,0 +1,141 @@
#include "cmake_generator.h"
#include <sstream>
#include <fstream>
#include <set>
#include <algorithm>
#include <thread>
#include <chrono>
CDbcUtilCMakeGenerator::CDbcUtilCMakeGenerator(const std::filesystem::path& rpathOutputDir, const std::string& rssComponent)
{
if (rssComponent.empty()) return; // TODO: Exception
// Create project directory
if (!rpathOutputDir.empty())
m_pathProject = rpathOutputDir;
if (!rssComponent.empty())
m_pathProject /= "can_dl";
std::filesystem::create_directories(m_pathProject);
// The source string
std::string ssSource;
// File with "CMakeLists.txt" function; read completely if existing
std::filesystem::path pathFile = m_pathProject / "CMakeLists.txt";
if (std::filesystem::exists(pathFile))
{
std::ifstream stream;
stream.open(pathFile);
if (!stream.is_open()) return; // TODO: Exception: throw CCompileException("Failed to open the CMakeLists.txt file for reading.");
// Read the complete source
std::stringstream sstream;
sstream << stream.rdbuf();
ssSource = std::move(sstream.str());
}
else // Create the file in memory
{
ssSource = R"code(# Enforce CMake version 3.20 or newer needed for path function
cmake_minimum_required (VERSION 3.20)
# Use new policy for project version settings and default warning level
cmake_policy(SET CMP0048 NEW) # requires CMake 3.14
cmake_policy(SET CMP0092 NEW) # requires CMake 3.15
# Define project
project(%component% VERSION 1.0 LANGUAGES CXX)
# Use C++17 support
set(CMAKE_CXX_STANDARD 17)
# Library symbols are hidden by default
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# Set the SDV_FRAMEWORK_DEV_INCLUDE if not defined yet
if (NOT DEFINED SDV_FRAMEWORK_DEV_INCLUDE)
if (NOT DEFINED ENV{SDV_FRAMEWORK_DEV_INCLUDE})
message( FATAL_ERROR "The environment variable SDV_FRAMEWORK_DEV_INCLUDE needs to be pointing to the SDV V-API development include files location!")
endif()
set (SDV_FRAMEWORK_DEV_INCLUDE "$ENV{SDV_FRAMEWORK_DEV_INCLUDE}")
endif()
# Include link to export directory of SDV V-API development include files location
include_directories(${SDV_FRAMEWORK_DEV_INCLUDE})
# Set platform specific compile flags
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/W4 /WX /wd4996 /wd4100 /permissive- /Zc:rvalueCast)
else()
add_compile_options(-Werror -Wall -Wextra -Wshadow -Wpedantic -Wunreachable-code -fno-common)
endif()
# Add the dynamic library
add_library(%component% SHARED)
# Set extension to .sdv
set_target_properties(%component% PROPERTIES PREFIX "")
set_target_properties(%component% PROPERTIES SUFFIX ".sdv")
)code";
CKeywordMap mapKeywords;
mapKeywords["component"] = rssComponent;
ssSource = ReplaceKeywords(ssSource, mapKeywords);
}
// Search function for caseless finding in the string.
auto fnNCFind = [&](const std::string& rssText, size_t nPos = 0) -> size_t
{
auto it = std::search(ssSource.begin() + nPos, ssSource.end(), rssText.begin(), rssText.end(),
[](unsigned char ch1, unsigned char ch2) { return std::tolower(ch1) == std::tolower(ch2); }
);
if (it == ssSource.end()) return std::string::npos;
return std::distance(ssSource.begin(), it);
};
// Find the add_library function
size_t nPos = fnNCFind("add_library");
if (nPos == std::string::npos) return; // TODO: Exception: throw CCompileException("Missing 'add_library' keyword.");
// Search for shared keyword
nPos = fnNCFind("shared", nPos);
if (nPos == std::string::npos) return; // TODO: Exception: throw CCompileException("Missing 'shared' keyword.");
nPos += 6;
// Build set with files
size_t nStop = fnNCFind(")", nPos);
if (nStop == std::string::npos) return; // TODO: Exception: throw CCompileException("Missing ')' closing the 'add_library' statement.");
std::set<std::string> setFiles;
while (nPos < nStop)
{
// Skip whitespace
while (std::isspace(ssSource[nPos])) nPos++;
// Read file name
size_t nFileBegin = nPos;
while (nPos < nStop && !std::isspace(ssSource[nPos])) nPos++;
// Store the file
setFiles.insert(ssSource.substr(nFileBegin, nPos - nFileBegin));
}
// Insert additional files if needed
size_t nSourceSize = ssSource.size();
if (setFiles.find("datalink.h") == setFiles.end())
ssSource.insert(nStop, std::string("\n datalink.h"));
if (setFiles.find("datalink.cpp") == setFiles.end())
ssSource.insert(nStop, std::string("\n datalink.cpp"));
// Write the file again if needed
if (nSourceSize != ssSource.size())
{
std::ofstream stream;
stream.open(pathFile, std::ofstream::trunc);
if (!stream.is_open()) return; // TODO: Exception: throw CCompileException("Failed to open the CMakeLists.txt file for writing.");
// Write the complete source
stream << ssSource;
}
}
CDbcUtilCMakeGenerator::~CDbcUtilCMakeGenerator()
{}

View File

@@ -0,0 +1,30 @@
#ifndef CMAKE_GENERATOR_H
#define CMAKE_GENERATOR_H
#include <filesystem>
#include <string>
#include "codegen_base.h"
/**
* @brief CMake generator class.
*/
class CDbcUtilCMakeGenerator : public CCodeGeneratorBase
{
public:
/**
* @brief Constructor
* @param[in] rpathOutputDir Reference to the outpur directory.
* @param[in] rssComponent Current component this CMake file is for.
*/
CDbcUtilCMakeGenerator(const std::filesystem::path& rpathOutputDir, const std::string& rssComponent);
/**
* @brief Destructor
*/
virtual ~CDbcUtilCMakeGenerator();
private:
std::filesystem::path m_pathProject; ///< Project file path
};
#endif // !defined CMAKE_GENERATOR_H

View File

@@ -0,0 +1,38 @@
#include "codegen_base.h"
#include <sstream>
std::string CCodeGeneratorBase::ReplaceKeywords(const std::string& rssStr, const CKeywordMap& rmapKeywords, char cMarker /*= '%'*/)
{
std::stringstream sstream;
size_t nPos = 0;
while (nPos < rssStr.size())
{
// Find the initial separator
size_t nSeparator = rssStr.find(cMarker, nPos);
sstream << rssStr.substr(nPos, nSeparator == std::string::npos ? nSeparator : nSeparator - nPos);
nPos = nSeparator;
if (nSeparator == std::string::npos) continue;
nPos++;
// Find the next separator.
nSeparator = rssStr.find(cMarker, nPos);
if (nSeparator == std::string::npos)
{
// Internal error: missing second separator during code building.
continue;
}
// Find the keyword in the keyword map (between the separator and the position).
CKeywordMap::const_iterator itKeyword = rmapKeywords.find(rssStr.substr(nPos, nSeparator - nPos));
if (itKeyword == rmapKeywords.end())
{
// Internal error: invalid keyword during building code.
nPos = nSeparator + 1;
continue;
}
sstream << itKeyword->second;
nPos = nSeparator + 1;
}
return sstream.str();
}

View File

@@ -0,0 +1,71 @@
#ifndef CODEGEN_BASE_H
#define CODEGEN_BASE_H
#include <map>
#include <string>
#include <sstream>
#include <cmath>
/**
* @brief Code generator base class.
*/
class CCodeGeneratorBase
{
protected:
/**
* @brief Keyword map for keyword replacement in a string.
*/
typedef std::map<std::string, std::string> CKeywordMap;
/**
* @brief Replace keywords in a string.
* @param[in] rssStr Reference to the string containing the keywords.
* @param[in] rmapKeywords Map with keywords to replace.
* @param[in] cMarker Character to identify the keyword with (placed before and after the keyword; e.g. %keyword%).
* @return Returns the string with replacements.
*/
static std::string ReplaceKeywords(const std::string& rssStr, const CKeywordMap& rmapKeywords, char cMarker = '%');
/**
* @brief Generator specific string generator. The std::to_string doesn't handle floating points very well.
* @tparam T Type of the variable to stringetize.
* @param[in] rtVar Reference to the variable.
* @return The generated string object.
*/
template <typename T>
static std::string to_string(const T& rtVar)
{
std::ostringstream sstream;
sstream << rtVar;
return sstream.str();
}
/**
* @brief Overloading for double.
* @param[in] rdVar Reference to the double variable.
* @return The generated string object.
*/
static std::string to_string(const double& rdVar)
{
std::ostringstream sstream;
sstream << rdVar;
if (std::round(rdVar) == rdVar) sstream << ".0";
return sstream.str();
}
/**
* @brief Overloading for float.
* @param[in] rfVar Reference to the float variable.
* @return The generated string object.
*/
static std::string to_string(const float& rfVar)
{
std::ostringstream sstream;
sstream << rfVar;
if (std::round(rfVar) == rfVar) sstream << ".0";
sstream << "f";
return sstream.str();
}
};
#endif // !defined CODEGEN_BASE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,398 @@
#ifndef SOFTCAR_FMU_H
#define SOFTCAR_FMU_H
#ifdef WIN32
#include <Rpc.h>
#endif
#include "../global/dbcparser/dbcparser.h"
#include <fstream>
#include "codegen_base.h"
/**
* @brief Signal definition structure
*/
struct signalDefinition
{
std::string message_name; ///< Message name
std::string name; ///< Signal name
std::string attributes; ///< Attributes
uint32_t index = 0; ///< Index
bool isInput = false; ///< Is input signal
bool isDouble = false; ///< for FMU we distinguish only between integer and double (float)
uint32_t uiSize = 0; ///< everything > 8 we declare as isDouble
dbc::SSignalDef::EValueType signalType = dbc::SSignalDef::EValueType::signed_integer; ///< Signal type
};
/**
* @brief CAN data link generator class.
*/
class CSoftcarFMUGen : public CCodeGeneratorBase
{
public:
/**
* @brief Constructor
* @param[in] rpathOutputDir Reference to the outpur directory.
* @param[in] rparser Reference to the DBC parser containing the definitions.
* @param[in] ssModelIdentifier Reference to the modelIdentifier of the FMU.
* @param[in] rsVersion Reference to a string representing a version of the dbc file (could be empty).
* @param[in] rvecNodes Reference to a list of nodes this data link object is representing. An empty list will implement
* sending and receiving functions for all nodes.
*/
CSoftcarFMUGen(const std::filesystem::path& rpathOutputDir, const dbc::CDbcParser& rparser, const std::string& ssModelIdentifier, const std::string& rsVersion, const std::vector<std::string>& rvecNodes);
private:
/**
* @brief Keyword map for keyword replacement in a string.
*/
typedef std::map<std::string, std::string> CKeywordMap;
/**
* @brief Add keywords to the map.
* @param[in] rsModelIdentifier Reference to the modelIdentifier of the FMU.
* @param[in] rsVersion Reference to a string representing a version of the dbc file (could be empty).
* @param[in] rvecNodes Reference to a list of nodes this data link object is representing. An empty list will implement
* @param[in] rmapKeywords Map with keywords to replace.
* @return Returns the string with replacements.
*/
void UpdateKeywordMap(const std::string& rsModelIdentifier, const std::string& rsVersion,
const std::vector<std::string>& rvecNodes, CKeywordMap& rmapKeywords) const;
/**
* @brief Create files in the resources folder.
* @param[in] rRootPath parent folder
* @return Returns the string with replacements.
*/
void CreateResourcesFiles(const std::filesystem::path& rRootPath) const;
/**
* @brief Create files in the source folder.
* @param[in] rRootPath parent folder
* @return Returns the string with replacements.
*/
void CreateSourceFiles(const std::filesystem::path& rRootPath) const;
/**
* @brief Create files in the include folder.
* @param[in] rRootPath parent folder
* @return Returns the string with replacements.
*/
void CreateIncludeFiles(const std::filesystem::path& rRootPath) const;
/**
* @brief Create files in the include folder.
* @param[in] rRootPath parent folder
* @param[in] rsModelIdentifier Reference to the modelIdentifier of the FMU.
* @param[in] rmapKeywords Map with keywords to replace.
* @return Returns the string with replacements.
*/
void CreateFMUFiles(const std::filesystem::path& rRootPath, const std::string& rsModelIdentifier, CKeywordMap& rmapKeywords) const;
/**
* @brief Cleanup the directory except of the root cmake file
* @param[in] rpathRootDirectory root folder.
* @return Returns true if folder exists or could be created, otherwise false.
*/
bool CleanUpDirectory(const std::filesystem::path& rpathRootDirectory) const;
/**
* @brief Create subfolder if not exists
* @param[in] rpathRootDirectory root folder.
* @param[in] rpathSubDirectory sub folder.
* @return Returns true if folder exists or could be created, otherwise false.
*/
bool CreateDirectories(const std::filesystem::path& rpathRootDirectory, const std::filesystem::path& rpathSubDirectory) const;
/**
* @brief Create content of the signal_identifier file
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string Code_AllSignalsIdentifierList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Create single signal line of the signal_identifier file
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string Code_SignalIdentifierList(const signalDefinition& rSignal) const;
/**
* @brief Replace keywords in a string.
* @param[in] rsStr Reference to the string containing the keywords.
* @param[in] rmapKeywords Map with keywords to replace.
* @param[in] cMarker Character to identify the keyword with (placed before and after the keyword; e.g. %keyword%).
* @return Returns the string with replacements.
*/
static std::string ReplaceKeywords(const std::string& rsStr, const CKeywordMap& rmapKeywords, char cMarker = '%');
/**
* @brief Composes the fmi build description xml
* @param[in] rsModelIdentifier Reference to model identifier.*
* @return The code to insert.
*/
std::string CodeBuildDescriptionXML(const std::string& rsModelIdentifier) const;
/**
* @brief Composes a dbc file version information string
* @param[in] rsVersion Reference to a string representing a version of the dbc file.
* @return The code to insert.
*/
std::string CodeDBCFileVersion(const std::string& rsVersion) const;
/**
* @brief returns a basic model identifier if not available
* @param[in] rsModelIdentifier Reference to a string representing a version of the dbc file.
* @return The code to insert.
*/
std::string CodeModelIdentifier(const std::string& rsModelIdentifier) const;
/**
* @brief Get List of variables for the fmi xml file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeFMIFile_VariableList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the fmi xml file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeFMIFile_SignalVariableList(const signalDefinition& rSignal) const;
/**
* @brief Get List of variables for the value reference in the config.h file.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rIndex Reference to the index of the variable starting with 0.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeConfigH_Rx_ValueReference(const dbc::SMessageDef& rsMsg, uint32_t& rIndex,
std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the value reference in the config.h file.
* @param[in] rsMsg Reference to the DBC message to provide the code for.
* @param[in] rIndex Reference to the index of the variable starting with 0.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeConfigH_Tx_ValueReference(const dbc::SMessageDef& rsMsg, uint32_t& rIndex,
std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the value reference in the config.h file.
* @param[in] rsSig Reference to the DBC signal to provide the information for.
* @param[in] index Reference to the index of the variable starting with 0.
* @param[in] rsMessageName Reference to the message name.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeConfigH_Rx_SignalForValueReference(const dbc::SSignalDef& rsSig, const uint32_t index,
const std::string& rsMessageName, std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the value reference in the config.h file.
* @param[in] rsSig Reference to the DBC signal to provide the information for.
* @param[in] index Reference to the index of the variable starting with 0.
* @param[in] rsMessageName Reference to the message name.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeConfigH_Tx_SignalForValueReference(const dbc::SSignalDef& rsSig, const uint32_t index,
const std::string& rsMessageName, std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the model data in the config.h file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeConfigH_ModelDataList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the model data in the config.h file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeConfigH_SignalForModelData(const signalDefinition& rSignal) const;
/**
* @brief Get List of variables for the mapping trigger file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeVariableList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of variables for the mapping trigger file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeVariableName(const signalDefinition& rSignal) const;
/**
* @brief Get List of global signals definitions for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalDefinitionList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get global signal definitions for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalSignalDefinition(const signalDefinition& rSignal) const;
/**
* @brief Get List of registering the global signals for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalRegisterList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Check list of registered global signals for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalRegisterCheckList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get registering the a global signal for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalRegisterSignal(const signalDefinition& rSignal) const;
/**
* @brief Get check of a global signal for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @param[in] first should be true for the first call, otherwise false.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalRegisterSignalCheck(const signalDefinition& rSignal, bool first) const;
/**
* @brief Get List of resetting the global signals for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalResetList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get resetting the a global signal for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_GlobalResetSignal(const signalDefinition& rSignal) const;
/**
* @brief Get List of signals for the update event for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_EventUpdateList(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get Signal for writing int the update event method for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_EventUpdateSignalDefinitionWrite(const signalDefinition& rSignal) const;
/**
* @brief Get Signal for reading the update event method for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_EventUpdateSignalDefinitionRead(const signalDefinition& rSignal) const;
/**
* @brief Get List of signals for the GetFloat64 method for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_GetFloat64(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of signals for the GetInt32 method for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_GetInt32(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of signals for the SetFloat64 method for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_SetFloat64(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get List of signals for the SetInt32 method for the model.cpp file.
* @param[in] rvecSignalDefinitions Reference to the container for all signal definitions.
* @return The code to insert.
*/
std::string CodeModelCPP_SetInt32(const std::vector<signalDefinition>& rvecSignalDefinitions) const;
/**
* @brief Get Signal for GetFloat64 method for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_GetFloat64SignalDefinition(const signalDefinition& rSignal) const;
/**
* @brief Get Signal for GetInt32 method for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_GetInt32SignalDefinition(const signalDefinition& rSignal) const;
/**
* @brief Get Signal for SetFloat64 method for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_SetFloat64SignalDefinition(const signalDefinition& rSignal) const;
/**
* @brief Get Signal for SetInt32 method for the model.cpp file.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeModelCPP_SetInt32SignalDefinition(const signalDefinition& rSignal) const;
/**
* @brief Get comment for the OpenAPILoad function for the model.cpp file.
* @return The code to insert.
*/
std::string CodeModelCPP_OpenAPILoadFunction() const;
/**
* @brief Get default value of a tx signal. Used as start value un the build description.
* @param[in] rSignal Reference to the signal definition.
* @return The code to insert.
*/
std::string CodeGetDefaultValueOfTxSignal(const dbc::SSignalDef& rSignal) const;
/**
* @brief Get minimum and maximum of a signal
* @param[in] rSignal Reference to the signal definition.
* @param[in] isDouble Reference to bool variable. Signal is either integer or double.
* @return The code to insert.
*/
std::string GetAttributes(const dbc::SSignalDef& rSignal, const bool& isDouble) const;
/**
* @brief returns guid string
* @return The guid.
*/
std::string newUUID() const;
const dbc::CDbcParser& m_rparser; ///< Reference to the DBC parser.
};
#endif // !defined SOFTCAR_FMU_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,550 @@
/**
* @brief Build description template. File 'buildDescription.xml'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szBuildDescriptionTemplate[] = R"code(<!--
@file %buiddescription_path%
@date %date%
This file is the fmi Build Description xml.
This file was generated by the DBC utility from:
%dbc_sources%
%dbc_version%
-->
%buildDescription_xml%
)code";
/**
* @brief CMake build project template. File 'CMakeLists.txt'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szMappingCMakeFileListsTemplate[] = R"code(
# @file %cmakefilelists_path%
# This file is cmake project file.
# This file was generated by the DBC utility from:
# %dbc_sources%
# %dbc_version%
# Based on CMakeLists.txt from https://github.com/modelica/Reference-FMUs
# only FMI 2.0, only CoSimulation
# without fumsim
# Only valid for Windows
if ( WIN32 )
# Enforce CMake version 3.20 or newer needed for path function
cmake_minimum_required (VERSION 3.20)
# Use new policy for project version settings and default warning level
cmake_policy(SET CMP0048 NEW) # requires CMake 3.14
cmake_policy(SET CMP0092 NEW) # requires CMake 3.15
project (%model_Identifier%Project)
# Use C++17 support
set(CMAKE_CXX_STANDARD 17)
# Library symbols are hidden by default
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# Set the SDV_FRAMEWORK_DEV_INCLUDE if not defined yet
if (NOT DEFINED SDV_FRAMEWORK_DEV_INCLUDE)
if (NOT DEFINED ENV{SDV_FRAMEWORK_DEV_INCLUDE})
message( FATAL_ERROR "The environment variable SDV_FRAMEWORK_DEV_INCLUDE needs to be pointing to the SDV V-API development include files location!")
endif()
set (SDV_FRAMEWORK_DEV_INCLUDE "$ENV{SDV_FRAMEWORK_DEV_INCLUDE}")
endif()
# Include link to export directory of SDV V-API development include files location
include_directories(${SDV_FRAMEWORK_DEV_INCLUDE})
set(VAPI_CORE_SDV_BINARY_DIR ${CMAKE_BINARY_DIR}/bin)
set(MODEL_NAME %model_Identifier%)
set(TARGET_NAME ${MODEL_NAME})
set(FMU_FULL_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/fmus/${MODEL_NAME}.fmu")
FUNCTION(cat IN_FILE OUT_FILE)
file(READ ${IN_FILE} CONTENTS)
file(APPEND ${OUT_FILE} "${CONTENTS}")
ENDFUNCTION()
set(FMI_VERSION 2 CACHE STRING "FMI Version")
set_property(CACHE FMI_VERSION PROPERTY STRINGS 2)
set(FMI_TYPE CS CACHE STRING "FMI Version")
set_property(CACHE FMI_TYPE PROPERTY STRINGS CS)
set(FMI_TYPE "")
set (FMI_PLATFORM win32)
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set (FMI_PLATFORM win64)
endif ()
SET(HEADERS
${MODEL_NAME}/config.h
include/cosimulation.h
include/model.h
)
SET(HEADERS
${HEADERS}
include/fmi2Functions.h
include/fmi2FunctionTypes.h
include/fmi2TypesPlatform.h
)
SET(SOURCES
${MODEL_NAME}/model.cpp
src/fmi${FMI_VERSION}Functions.c
src/cosimulation.c
)
add_library(${TARGET_NAME} SHARED
${HEADERS}
${SOURCES}
${MODEL_NAME}/FMI${FMI_VERSION}${FMI_TYPE}.xml
${MODEL_NAME}/buildDescription.xml
)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/fmus)
set(FMU_BUILD_DIR temp/${MODEL_NAME})
target_compile_definitions(${TARGET_NAME} PRIVATE
FMI_VERSION=${FMI_VERSION}
DISABLE_PREFIX
)
#[[
if (MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc")
endif()
]]
target_compile_definitions(${TARGET_NAME} PRIVATE FMI_COSIMULATION)
target_include_directories(${TARGET_NAME} PRIVATE include ${MODEL_NAME})
target_link_libraries(${TARGET_NAME} Winmm Ws2_32 Rpcrt4.lib)
set_target_properties(${TARGET_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
LIBRARY_OUTPUT_DIRECTORY "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
LIBRARY_OUTPUT_DIRECTORY_DEBUG "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
ARCHIVE_OUTPUT_DIRECTORY "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${FMU_BUILD_DIR}/binaries/${FMI_PLATFORM}"
)
set_target_properties(${TARGET_NAME} PROPERTIES PREFIX "")
set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${MODEL_NAME})
# modelDescription.xml
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_NAME}/FMI${FMI_VERSION}${FMI_TYPE}.xml
"${FMU_BUILD_DIR}/modelDescription.xml"
)
set(ARCHIVE_FILES "modelDescription.xml" "binaries")
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_NAME}/resources")
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_NAME}/resources"
"${FMU_BUILD_DIR}/resources/"
)
set(ARCHIVE_FILES ${ARCHIVE_FILES} "resources")
endif()
# When windows robocopy command (using cmd.exe) is used to copy files its important to set the dependencies
# to assure that the copy command is finished before the next custom action to avoid copy/file access failures
# Copy sdv binaries of this FMU
set(DEST_DIR "${FMU_BUILD_DIR}/resources")
set(SOURCE_DIR_EXAMPLES_BIN "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
add_custom_target(copy_function_sdv_files_${TARGET_NAME} DEPENDS ${TARGET_NAME})
add_custom_command(TARGET copy_function_sdv_files_${TARGET_NAME}
COMMAND cmd /C "robocopy \"${SOURCE_DIR_EXAMPLES_BIN}\" \"${DEST_DIR}\" *.pdb *.sdv /NP /R:3 /W:5 || exit /b 0"
COMMENT "Copying contents from ${SOURCE_DIR_EXAMPLES_BIN} to ${DEST_DIR}, include only *.pdb *.sdv files"
)
add_dependencies(copy_function_sdv_files_${TARGET_NAME} ${TARGET_NAME})
# Copy framework sdv binaries
set(SOURCE_DIR_CORE_BIN "${SDV_FRAMEWORK_RUNTIME}")
add_custom_target(copy_framework_sdv_files_${TARGET_NAME} DEPENDS copy_function_sdv_files_${TARGET_NAME})
add_custom_command(TARGET copy_framework_sdv_files_${TARGET_NAME}
COMMAND cmd /C "robocopy \"${SOURCE_DIR_CORE_BIN}\" \"${DEST_DIR}\" *.pdb *.sdv /NP /R:3 /W:5 || exit /b 0"
COMMENT "Copying contents from ${SOURCE_DIR_CORE_BIN} to ${DEST_DIR}, include only *.pdb *.sdv files"
)
add_dependencies(copy_framework_sdv_files_${TARGET_NAME} copy_function_sdv_files_${TARGET_NAME})
# FMU content created, all files copied
# to zip the files create a new target 'create_zip' which is build after all files have been copied
add_custom_target(create_zip_${TARGET_NAME} ALL DEPENDS copy_framework_sdv_files_${TARGET_NAME} )
add_custom_command(TARGET create_zip_${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E tar "cfv" ${FMU_FULL_FILE_NAME} --format=zip ${ARCHIVE_FILES}
WORKING_DIRECTORY ${FMU_BUILD_DIR}
COMMENT "Creating ZIP ${FMU_FULL_FILE_NAME}"
)
add_dependencies(create_zip_${TARGET_NAME} copy_framework_sdv_files_${TARGET_NAME})
#TODO
#add_dependencies(${TARGET_NAME} <add_cmake_target_this_depends_on>)
endif ()
)code";
/**
*@brief Config Header template. File 'config.h'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szConfigHTemplate[] = R"code(/**
* @file %configH_path%
* @date %date%
* This file defines the data link object between CAN and the V-API devices.
* This file was generated by the DBC utility from:
* %dbc_sources%
* %dbc_version%
*/
#ifndef %safeguardconfig%
#define %safeguardconfig%
#include <stdint.h>
// define class name and unique id
#define MODEL_IDENTIFIER %model_Identifier%
#define INSTANTIATION_TOKEN "%model_guid%"
#define FMI_VERSION 2
#define CO_SIMULATION
// define model size
#define NX 0
#define NZ 0
#define GET_INT32
#define SET_INT32
#define GET_FLAOT64
#define SET_FLOAT64
#define EVENT_UPDATE
#define CLEAN_UP
#define FIXED_SOLVER_STEP 0.04
#define DEFAULT_STOP_TIME 10
typedef enum {
%value_reference%
} ValueReference;
typedef struct {
%model_data%
} ModelData;
#endif // !defined %safeguardconfig%
)code";
/**
*@brief FMI2 xml template. File 'FMI2.XML'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szFMI2XMLTemplate[] = R"code(<?xml version="1.0" encoding="UTF-8"?>
<fmiModelDescription
fmiVersion = "2.0"
modelName = "%model_Identifier%"
guid = "%model_guid%"
description = "TargetLink FMU for %model_Identifier%"
generationTool = "TargetLink was generated by the DBC utility: %dbc_sources%"
generationDateAndTime = "%date%"
variableNamingConvention = "structured"
numberOfEventIndicators = "0">
<CoSimulation
modelIdentifier = "%model_Identifier%"
canHandleVariableCommunicationStepSize = "false"
canGetAndSetFMUstate = "false"
canSerializeFMUstate = "false"
providesDirectionalDerivative = "false"
canBeInstantiatedOnlyOncePerProcess = "true"
canInterpolateInputs = "false"
canRunAsynchronuously = "false">
<SourceFiles>
<File name="all.c"/>
</SourceFiles>
</CoSimulation>
<DefaultExperiment startTime="0" stepSize="0.01"/>
<ModelVariables>
%model_variables%
</ModelVariables>
<ModelStructure>
<Outputs>
<Unknown index="%unknown_index%"/>
</Outputs>
</ModelStructure>
</fmiModelDescription>
)code";
/**
*@brief Model cpp template. File 'model.cpp'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szModelCPPTemplate[] = R"code(/**
* @file %modelcpp_path%
* @date %date%
* This file defines the data link object between CAN and the V-API devices.
* This file was generated by the DBC utility from:
* %dbc_sources%
* %dbc_version%
*/
#include <memory>
#include <vector>
#include <iostream>
#include <fstream>
#include <support/timer.h>
#include "signal_identifier.h"
#include <support/signal_support.h>
#include <support/app_control.h>
%global_signals%
// in case the simulation timer should be used
sdv::core::ITimerSimulationStep* g_pTimerSimulationStep;
std::unique_ptr<sdv::app::CAppControl> g_appcontrol;
bool InitializeAppControl(const std::string& resource, const std::string& configFileName)
{
auto bResult = g_appcontrol->AddModuleSearchDir( resource );
bResult &= g_appcontrol->Startup("");
g_appcontrol->SetConfigMode();
bResult &= g_appcontrol->AddConfigSearchDir( resource );
if (!configFileName.empty())
{
bResult &= g_appcontrol->LoadConfig(configFileName.c_str()) == sdv::core::EConfigProcessResult::successful;
}
return bResult;
}
sdv::core::EConfigProcessResult RegisterAllSignals()
{
std::string msg = "register all signals: ";
sdv::core::CDispatchService dispatch;
%global_signals_register%
%global_signals_register_check%
return sdv::core::EConfigProcessResult::failed;
}
bool ResetAllSignals()
{
sdv::core::CDispatchService dispatch;
%global_signals_reset%
SDV_LOG_INFO("Reset signals");
return true;
}
bool CreateCoreServiceTomlFile(const std::string& resources)
{
std::ofstream tomlFile("sdv_core_reloc.toml");
if (tomlFile.is_open())
{
tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \"";
tomlFile << resources;
tomlFile << "\"\n";
tomlFile.close();
return true;
}
return false;
}
bool OpenAPILoad(const std::string& resources)
{
bool success = CreateCoreServiceTomlFile(resources);
g_appcontrol = std::make_unique<sdv::app::CAppControl> ();
//
// TODO: Dispatch service must be loaded first, adjust the correct toml file
//
success &= InitializeAppControl(resources, "data_dispatch_config_file.toml");
if (!success)
{
std::cout << "Error: InitializeAppControl() failed" << std::endl;
SDV_LOG_ERROR("Failed InitializeAppControl");
return false;
}
success &= RegisterAllSignals() == sdv::core::EConfigProcessResult::successful;
if (!success)
{
SDV_LOG_ERROR("Signals could not be registered");
}
%vapi_load_config_files%
g_appcontrol->SetRunningMode();
return success;
}
void OpenAPIShutdown()
{
ResetAllSignals();
}
#ifdef __cplusplus
extern "C" {
#endif
#include <float.h> // for DBL_EPSILON
#include <math.h> // for fabs()
#include "config.h"
#include "model.h"
Status cleanup(ModelInstance*)
{
SDV_LOG_INFO("Shutting down...");
OpenAPIShutdown();
return OK;
}
bool setStartValues(ModelInstance* comp)
{
std::string path(comp->resourceLocation);
std::string resourcePath = path.substr(8);
std::replace(resourcePath.begin(), resourcePath.end(), '\\', '/');
if (!OpenAPILoad(resourcePath))
{
std::cout << "Error: OpenAPILoad() failed." << std::endl;
comp->terminateSimulation = true;
return false;
}
// TODO: move this to initialize()?
comp->nextEventTime = 0;
comp->nextEventTimeDefined = true;
return true;
}
Status calculateValues(ModelInstance* comp)
{
UNUSED(comp);
// nothing to do
return OK;
}
Status getFloat64(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] double values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index)
{
ASSERT_NVALUES(1);
%getFloat64%
return OK;
}
Status getInt32(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] int32_t values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index)
{
ASSERT_NVALUES(1);
%getInt32%
return OK;
}
Status setFloat64(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] const double values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index)
{
ASSERT_NVALUES(1);
%setFloat64%
return OK;
}
Status setInt32(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] const int32_t values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index)
{
ASSERT_NVALUES(1);
%setInt32%
return OK;
}
void eventUpdate(ModelInstance* comp)
{
%event_update%
double epsilon = (1.0 + fabs(comp->time)) * DBL_EPSILON;
if (comp->nextEventTimeDefined && comp->time + epsilon >= comp->nextEventTime) {
comp->nextEventTime += FIXED_SOLVER_STEP;
}
comp->valuesOfContinuousStatesChanged = false;
comp->nominalsOfContinuousStatesChanged = false;
comp->terminateSimulation = false;
comp->nextEventTimeDefined = true;
}
#ifdef __cplusplus
} // end of extern "C"
#endif
)code";
/**
*@brief Signal name mapping template. Signal names contain the can names of the signal which must match the names of the vehicle devices.
* File 'signal_identifier.h'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szMappingSignalIdentifierTemplate[] = R"code(/**
@file %signalidentifierfile_path%
@date %date%
This file is the signal identifier header.
This file was generated by the DBC utility from:
%dbc_sources%
%dbc_version%
*/
#ifndef %safeguardsignalidentifier%
#define %safeguardsignalidentifier%
namespace %object_prefix%
{
// Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space
%signals%
} // %object_prefix%
#endif // %safeguardsignalidentifier%
)code";
/**
*@brief Data dispatch service config template. File 'data_dispatch_config_file.toml'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szDataDispatchServiceTomlFile[] = R"code([Configuration]
Version = 100
[[Component]]
Path = "data_dispatch_service.sdv"
Class = "DataDispatchService"
)code";
/**
*@brief Simulation task timer service config template. File 'simulation_task_timer_config_file.toml'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szSimulationTaskTimerServiceTomlFile[] = R"code([Configuration]
Version = 100
[[Component]]
Path = "simulation_task_timer.sdv"
Class = "SimulationTaskTimerService"
)code";
/**
*@brief Task timer service config template. File 'task_timer_config_file.toml'. Code chunks are inserted at the keywords surrounded by %%.
*/
const char szTaskTimerhServiceTomlFile[] = R"code([Configuration]
Version = 100
[[Component]]
Path = "task_timer.sdv"
Class = "TaskTimerService"
)code";

View File

@@ -0,0 +1,103 @@
#include "../../global/process_watchdog.h"
#include <interfaces/core.h>
#include <interfaces/mem.h>
#include "../../global/cmdlnparser/cmdlnparser.cpp"
#include "../../global/dbcparser/dbcparser.cpp"
#include "../../global/localmemmgr.h"
#include <iostream>
#include "can_dl.h"
#include "fmu.h"
#include "cmake_generator.h"
#include "../error_msg.h"
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int iArgc, const wchar_t* rgszArgv[])
#else
extern "C" int main(int iArgc, const char* rgszArgv[])
#endif
{
// Workaround for GCC to make certain that POSIX thread library is loaded before the components are loaded.
// REASON: If the first call to a thread is done in a dynamic library, the application is already classified as single
// threaded and a termination is initiated.
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: This task has been taken over by the process watchdog.
CProcessWatchdog watchdog;
// Suppress the warning supplied by ccpcheck about an unsused variable. The memory manager registers itself into the system and
// needs to stay in scope.
// cppcheck-suppress unusedVariable
CLocalMemMgr memmgr;
CCommandLine cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character));
bool bHelp = false;
bool bError = false;
bool bSilent = false;
bool bVerbose = false;
size_t nIfcIndex = 0;
std::string ssIfcName;
std::string ssVersion;
std::string ssModelIdentifier;
std::string ssDataLinkLibName = "can_dl";
std::vector<std::filesystem::path> vecFileNames;
std::vector<std::string> vecNodes;
std::filesystem::path pathOutputDir;
try
{
auto& rArgHelpDef = cmdln.DefineOption("?", bHelp, "Show help");
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = cmdln.DefineOption("s", bSilent, "Do not show any information on STDOUT. Not compatible with 'verbose'.");
rArgSilentDef.AddSubOptionName("silent");
auto& rArgVerboseDef = cmdln.DefineOption("v", bVerbose, "Provide verbose information. Not compatible with 'silent'.");
rArgVerboseDef.AddSubOptionName("verbose");
cmdln.DefineSubOption("version", ssVersion, "Optional: version information of the dbc file.");
cmdln.DefineSubOption("module", ssModelIdentifier, "Optional: module identifier for the fmu. Default 'BasicModelIdentifier'");
cmdln.DefineSubOption("dl_lib_name", ssDataLinkLibName, "Data link library target name in the generated cmake file (default=\"can_dl\")..");
cmdln.DefineOption("O", pathOutputDir, "Set output directory");
cmdln.DefineSubOption("ifc_idx", nIfcIndex, "Interface index this DBC file is aiming for.");
cmdln.DefineSubOption("ifc_name", ssIfcName, "Name of the interface this DBC file is aiming for.");
cmdln.DefineSubOption("nodes", vecNodes, "List of nodes that will be implemented.");
cmdln.DefineDefaultArgument(vecFileNames, "One or more DBC files");
cmdln.Parse(static_cast<size_t>(iArgc), rgszArgv);
} catch (const SArgumentParseException& rsExcept)
{
std::cout << "ERROR: " << rsExcept.what() << std::endl;
bHelp = true;
bError = true;
}
if (!bSilent)
{
std::cout << "DBC utility" << std::endl;
std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl;
std::cout << "Author: Erik Verhoeven" << std::endl;
}
if (bHelp)
{
if (!bSilent)
cmdln.PrintHelp(std::cout);
return bError ? CMDLN_ARG_ERR : NO_ERROR;
}
dbc::CDbcParser parser;
for (const std::filesystem::path& rpath : vecFileNames)
{
try
{
if (bVerbose)
std::cout << "Processing: " << rpath.generic_u8string() << std::endl;
dbc::CDbcSource src(rpath);
parser.Parse(src);
CSoftcarFMUGen fmu(pathOutputDir, parser, ssModelIdentifier, ssVersion, vecNodes);
CCanDataLinkGen dl(pathOutputDir, parser, ssVersion, ssIfcName, nIfcIndex, vecNodes);
CDbcUtilCMakeGenerator cmakegen(pathOutputDir, ssDataLinkLibName);
}
catch (dbc::SDbcParserException& rsException)
{
if (!bSilent)
std::cout << "ERROR " << LOAD_DBC_FILE_ERROR_MSG << " Reason: " << rsException.what() << std::endl;
return LOAD_DBC_FILE_ERROR;
}
}
return NO_ERROR;
}

View File

@@ -0,0 +1,69 @@
# Define project
project (sdv_idl_compiler VERSION 1.0 LANGUAGES CXX)
# Get xxHash from github
include(FetchContent)
FetchContent_Declare(
xxhash
GIT_REPOSITORY https://github.com/Cyan4973/xxHash.git
GIT_TAG v0.8.3
)
FetchContent_MakeAvailable(xxhash)
# Add include directories
include_directories(../export ${xxhash_SOURCE_DIR})
# Define the executable
add_executable(sdv_idl_compiler
main.cpp
exception.cpp
environment.cpp
logger.cpp
token.cpp
tokenlist.cpp
codepos.cpp
lexer.cpp
parser.cpp
parsecontext.cpp
preproc.cpp
environment.cpp
source.cpp
macro.cpp
constvariant.cpp
entities/entity_base.cpp
entities/root_entity.cpp
entities/module_entity.cpp
entities/declaration_entity.cpp
entities/typedef_entity.cpp
entities/variable_entity.cpp
entities/entity_value.cpp
entities/definition_entity.cpp
entities/struct_entity.cpp
entities/interface_entity.cpp
entities/attribute_entity.cpp
entities/operation_entity.cpp
entities/parameter_entity.cpp
entities/exception_entity.cpp
entities/union_entity.cpp
entities/enum_entity.cpp
entities/hash_calc.cpp
entities/meta_entity.cpp
generator/definition_generator.cpp
generator/proxy_generator.cpp
generator/stub_generator.cpp
generator/context.cpp
"generator/ps_class_generator_base.cpp"
"generator/serdes_generator.h"
"generator/serdes_generator.cpp"
"generator/cmake_generator.cpp"
"generator/ps_cpp_generator.h"
"generator/ps_cpp_generator.cpp" "generator/definition_generator_base.h")
if (UNIX)
target_link_libraries(sdv_idl_compiler ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} stdc++fs)
else()
target_link_libraries(sdv_idl_compiler ${CMAKE_DL_LIBS})
endif (UNIX)
# Appending the executable to the service list
set(SDV_Executable_List ${SDV_Executable_List} sdv_idl_compiler PARENT_SCOPE)

View File

@@ -0,0 +1,314 @@
#include "codepos.h"
#include "lexer.h"
#include "exception.h"
#include "token.h"
CCodePos::CCodePos()
{}
CCodePos::CCodePos(const char* szCode) : m_szCode(szCode)
{
if (!szCode)
throw CCompileException("No code supplied.");
m_szCurrent = szCode;
m_uiLine = 1;
m_uiCol = 1;
}
CCodePos::CCodePos(const CCodePos& rCode) :
m_szCode(rCode.m_szCode), m_szCurrent(rCode.m_szCurrent), m_szPrependedPos(rCode.m_szPrependedPos),
m_szPrependedCode(nullptr), m_szPrependedCurrent(nullptr), m_ssPrependedCode(rCode.m_ssPrependedCode),
m_uiLine(rCode.m_uiLine), m_uiCol(rCode.m_uiCol)
{
if (rCode.m_szPrependedCurrent && rCode.m_szPrependedCode)
{
m_szPrependedCode = m_ssPrependedCode.c_str();
m_szPrependedCurrent = m_szPrependedCode + (rCode.m_szPrependedCurrent - rCode.m_szPrependedCode);
}
}
CCodePos::CCodePos(CCodePos&& rCode) noexcept :
m_szCode(rCode.m_szCode), m_szCurrent(rCode.m_szCurrent), m_szPrependedPos(rCode.m_szPrependedPos),
m_szPrependedCode(rCode.m_szPrependedCode), m_szPrependedCurrent(rCode.m_szPrependedCurrent),
m_ssPrependedCode(std::move(rCode.m_ssPrependedCode)), m_lstCodeChunks(std::move(rCode.m_lstCodeChunks)),
m_uiLine(rCode.m_uiLine), m_uiCol(rCode.m_uiCol)
{
rCode.m_szCode = nullptr;
rCode.m_szCurrent = nullptr;
rCode.m_szPrependedPos = nullptr;
rCode.m_szPrependedCode = nullptr;
rCode.m_szPrependedCurrent = nullptr;
rCode.m_uiLine = 0;
rCode.m_uiCol = 0;
}
bool CCodePos::IsValid() const
{
return m_szCode != nullptr &&
m_szCurrent != nullptr &&
m_uiLine > 0 &&
m_uiCol > 0;
}
CCodePos::operator bool() const
{
return IsValid();
}
void CCodePos::Reset()
{
m_szCurrent = m_szCode;
m_szPrependedPos = nullptr;
m_szPrependedCode = nullptr;
m_szPrependedCurrent = nullptr;
m_uiLine = 1;
m_uiCol = 1;
m_ssPrependedCode.clear();
}
uint32_t CCodePos::GetLine() const
{
return m_uiLine;
}
uint32_t CCodePos::GetCol() const
{
return m_uiCol;
}
bool CCodePos::HasEOF() const
{
return !m_szCurrent || ((!m_szPrependedCurrent || *m_szPrependedCurrent == '\0') && *m_szCurrent == '\0');
}
bool CCodePos::HasEOL() const
{
if (HasEOF()) return true;
if (m_szPrependedCurrent && *m_szPrependedCurrent != '\0')
return *m_szPrependedCurrent == '\n' || (m_szPrependedCurrent[0] == '\r' && m_szPrependedCurrent[1] == '\n');
return *m_szCurrent == '\n' || (m_szCurrent[0] == '\r' && m_szCurrent[1] == '\n');
}
CToken CCodePos::GetLocation(ETokenType eTokenType /*= ETokenType::token_none*/) const
{
CToken token;
token.m_szCode = (m_szPrependedCurrent && *m_szPrependedCurrent) ? m_szPrependedCurrent : m_szCurrent;
token.m_uiLine = m_uiLine;
token.m_uiCol = m_uiCol;
token.m_eType = eTokenType;
return token;
}
void CCodePos::UpdateLocation(CToken& rtoken) const
{
// Check for a valid token.
if (!rtoken) return;
// If the token start position is within the prepended start code and the prepended current code, update the location by
// creating a code chunk.
if (m_szPrependedCode && rtoken.m_szCode >= m_szPrependedCode && rtoken.m_szCode <= m_szPrependedCurrent)
{
// Create a chunk based on the prepended code
std::string ssChunk = std::string(rtoken.m_szCode, m_szPrependedCurrent - rtoken.m_szCode);
// If the current position of the prepended code is at the end, add a part of the code.
if (!*m_szPrependedCurrent)
ssChunk += std::string(m_szPrependedPos, m_szCurrent - m_szPrependedPos);
// Add the chunk to the list
m_lstCodeChunks.emplace_back(std::move(ssChunk));
// Assign the new start and length infos
rtoken.m_szCode = m_lstCodeChunks.back().c_str();
rtoken.m_uiLen = static_cast<uint32_t>(m_lstCodeChunks.back().size());
return;
}
// If the token start position is within the code, update the length
if (rtoken.m_szCode >= m_szCode && rtoken.m_szCode <= m_szCurrent)
{
rtoken.m_uiLen = static_cast<uint32_t>(m_szCurrent - rtoken.m_szCode);
return;
}
// The token start position is invalid. Set the code to nullptr and the length to 0.
rtoken.m_szCode = nullptr;
rtoken.m_uiLen = 0;
}
void CCodePos::UpdateLocation(CToken& rtoken, ETokenLiteralType eLiteralType) const
{
UpdateLocation(rtoken);
switch (rtoken.GetType())
{
case ETokenType::token_literal:
case ETokenType::token_none:
rtoken.m_eType = ETokenType::token_literal;
rtoken.m_eLiteralType = eLiteralType;
break;
default:
throw CCompileException("Internal error: invalid token type during literal type assignment.");
break;
}
}
void CCodePos::UpdateLocation(CToken& rtoken, ETokenMetaType eMetaType) const
{
UpdateLocation(rtoken);
switch (rtoken.GetType())
{
case ETokenType::token_meta:
case ETokenType::token_none:
rtoken.m_eType = ETokenType::token_meta;
rtoken.m_eMetaType = eMetaType;
break;
default:
throw CCompileException("Internal error: invalid token type during meta type assignment.");
break;
}
}
void CCodePos::PrependCode(const std::string& rssCode)
{
if (rssCode.empty()) return;
std::string ssPrependedCode = rssCode;
if (m_szPrependedCurrent)
ssPrependedCode += m_szPrependedCurrent;
m_ssPrependedCode = std::move(ssPrependedCode);
m_szPrependedCode = m_ssPrependedCode.c_str();
m_szPrependedCurrent = m_ssPrependedCode.c_str();
m_szPrependedPos = m_szCurrent;
}
bool CCodePos::CurrentPositionInMacroExpansion() const
{
return m_szPrependedCurrent && *m_szPrependedCurrent;
}
CCodePos& CCodePos::operator=(const CCodePos& rCode)
{
m_szCode = rCode.m_szCode;
m_szCurrent = rCode.m_szCurrent;
m_szPrependedPos = rCode.m_szPrependedPos;
m_ssPrependedCode = rCode.m_ssPrependedCode;
if (rCode.m_szPrependedCurrent && rCode.m_szPrependedCode)
{
m_szPrependedCode = m_ssPrependedCode.c_str();
m_szPrependedCurrent = m_szPrependedCode + (rCode.m_szPrependedCurrent - rCode.m_szPrependedCode);
} else
{
m_szPrependedCode = nullptr;
m_szPrependedCurrent = nullptr;
}
m_uiLine = rCode.m_uiLine;
m_uiCol = rCode.m_uiCol;
return *this;
}
CCodePos& CCodePos::operator=(CCodePos&& rCode) noexcept
{
m_szCode = rCode.m_szCode;
m_szCurrent = rCode.m_szCurrent;
m_szPrependedPos = rCode.m_szPrependedPos;
m_szPrependedCode = rCode.m_szPrependedCode;
m_szPrependedCurrent = rCode.m_szPrependedCurrent;
m_ssPrependedCode = std::move(rCode.m_ssPrependedCode);
m_lstCodeChunks = std::move(rCode.m_lstCodeChunks);
m_uiLine = rCode.m_uiLine;
m_uiCol = rCode.m_uiCol;
rCode.m_szCode = nullptr;
rCode.m_szCurrent = nullptr;
rCode.m_szPrependedPos = nullptr;
rCode.m_szPrependedCode = nullptr;
rCode.m_szPrependedCurrent = nullptr;
rCode.m_uiLine = 0;
rCode.m_uiCol = 0;
return *this;
}
char CCodePos::operator[](uint32_t uiOffset /*= 0*/) const
{
// Handle an offset past the prepended string.
for (uint32_t uiIndex = 0; uiIndex <= uiOffset; uiIndex++)
{
if (!m_szPrependedCurrent || m_szPrependedCurrent[uiIndex] == '\0')
return m_szCurrent ? m_szCurrent[uiOffset - uiIndex] : '\0';
}
// Return a character from the prepended string.
return m_szPrependedCurrent[uiOffset];
}
char CCodePos::operator*() const
{
if (m_szPrependedCurrent && m_szPrependedCurrent[0]) return m_szPrependedCurrent[0];
return m_szCurrent ? *m_szCurrent : '\0';
}
CCodePos::operator const char*() const
{
if (m_szPrependedCurrent) return m_szPrependedCurrent;
return m_szCurrent;
}
CCodePos CCodePos::operator++(int)
{
CCodePos posCopy(*this);
operator++();
return posCopy;
}
CCodePos& CCodePos::operator++()
{
// Check for a valid prepended character.
if (m_szPrependedCurrent && *m_szPrependedCurrent)
{
// Increase current position
m_szPrependedCurrent++;
// End of prepended code?
if (!m_szPrependedCurrent)
{
m_szPrependedCurrent = nullptr;
m_ssPrependedCode.clear();
}
return *this;
}
// Check for a valid character
if (m_szCurrent && *m_szCurrent != '\0')
{
// Currently a newline?
if (*m_szCurrent == '\n')
{
// Set new position at beginning of ext line
m_uiCol = 1;
m_uiLine++;
}
else if (*m_szCurrent == '\t')
{
// Increase the column
m_uiCol++;
// Align with a tab of 4 characters
while ((m_uiCol - 1) % 4) m_uiCol++;
}
else
m_uiCol++; // Increase the column
// Next character
m_szCurrent++;
}
return *this;
}
CCodePos& CCodePos::operator+=(uint32_t uiOffset)
{
for (uint32_t uiIndex = 0; uiIndex != uiOffset; uiIndex++)
operator++(0);
return *this;
}

View File

@@ -0,0 +1,200 @@
#ifndef CODEPOS_H
#define CODEPOS_H
#include "lexerbase.h"
#include <cstdint>
#include <list>
#include <string>
// Forward declaration
class CToken;
/**
* @brief Code string with position information.
*/
class CCodePos
{
public:
/**
* @brief Default constructor
* @remarks Use the assignment operator to set the code to be covered by the code position class.
*/
CCodePos();
/**
* @brief Constructor
* @param[in] szCode Pointer to zero terminated string containing the IDL code. Must not be NULL.
*/
CCodePos(const char* szCode);
/**
* @brief Copy constructor
* @param[in] rCode Reference to the code to copy.
*/
CCodePos(const CCodePos& rCode);
/**
* @brief Move constructor
* @param[in] rCode Reference to the code to move.
*/
CCodePos(CCodePos&& rCode) noexcept;
/**
* @{
* @brief Is the code position class initialized correctly?
* @return Returns whether the class is initialized.
*/
bool IsValid() const;
operator bool() const;
/**
* @}
*/
/**
* @brief Reset the current navigation.
* @attention Any prepended code will be removed.
*/
void Reset();
/**
* @brief Return the current line.
* @return The current line.
*/
uint32_t GetLine() const;
/**
* @brief Return the current column.
* @return The current column.
*/
uint32_t GetCol() const;
/**
* @brief Check for EOF.
* @return Returns 'true' when EOF has been reached. Otherwise returns 'false'.
*/
bool HasEOF() const;
/**
* @brief Check for EOL.
* @return Returns 'true' when EOL has been reached. Otherwise returns 'false'.
*/
bool HasEOL() const;
/**
* @brief Get the token at the current location.
* @attention The returned position is volatile and might be invalid as soon as code is prepended. Once the UpdateLocation
* has been called, the code is persistent.
* @param[in] eTokenType The type of token this token is referring to.
* @return The token at the current location.
*/
CToken GetLocation(ETokenType eTokenType = ETokenType::token_none) const;
/**
* @{
* @brief Update the length of the token with current position. Persists the code if composed from (part of) prepended code.
* @param[in, out] rtoken Reference to the token to be updated.
*/
void UpdateLocation(CToken& rtoken) const;
/**
* @brief Update the length of the token with current position. Persists the code if composed from (part of) prepended code.
* @param[in, out] rtoken Reference to the token to be updated.
* @param[in] eLiteralType The literal type to be assigned to the token (if any).
*/
void UpdateLocation(CToken& rtoken, ETokenLiteralType eLiteralType) const;
/**
* @brief Update the length of the token with current position. Persists the code if composed from (part of) prepended code.
* @param[in, out] rtoken Reference to the token to be updated.
* @param[in] eMetaType The meta type to be assigned to the token (if any).
*/
void UpdateLocation(CToken& rtoken, ETokenMetaType eMetaType) const;
/**
* @brief Insert code at the current position allowing to parse the prepended code.
* @details This function prepends code at the current position. The code could be a macro expansion or precedence parsing.
* @attention This function might invalidate tokens that have a start location, but no fixed location.
* @param[in] rssCode Reference to a string object representing the code to prepend
*/
void PrependCode(const std::string& rssCode);
/**
* @brief Return whether the current position is part of the prepended code.
* @return Returns 'true' when the current position is within the prepended code; otherwise returns 'false'.
*/
bool CurrentPositionInMacroExpansion() const;
/**
* @brief Assignment operator.
* @param[in] rCode Reference to the code to copy.
* @return Returns reference to this class.
*/
CCodePos& operator=(const CCodePos& rCode);
/**
* @brief Move operator.
* @param[in] rCode Reference to the code to move.
* @return Returns reference to this class.
*/
CCodePos& operator=(CCodePos&& rCode) noexcept;
/**
* @brief Get current character (with offset)
* @param[in] uiOffset The offset of the character to read relative to the current character.
* @attention The caller must make certain, that a valid character is available at the provided offset by interpreting the
* characters before.
* @return The current character or '\0' when there is no current character.
*/
char operator[](uint32_t uiOffset /*= 0*/) const;
/**
* @brief Get current character
* @attention The caller must make certain, that a valid character is available at the provided offset by interpreting the
* characters before.
* @return The current character or '\0' when there is no current character.
*/
char operator*() const;
/**
* @brief Get the code at the current position.
* @attention The caller must make certain, that a valid character is available at the provided offset by interpreting the
* characters before.
* @return Pointer to the code at the current location or nullptr when there is no code.
*/
operator const char*() const;
/**
* @brief Increment the current position
* @return Copy of the code position before incrementation.
*/
CCodePos operator++(int);
/**
* @brief Increment the current position
* @return Reference to this class.
*/
CCodePos& operator++();
/**
* @brief Increment the current position
* @param[in] uiOffset The offset of the character to increment to.
* @return Reference to this class.
*/
CCodePos& operator+=(uint32_t uiOffset);
private:
const char* m_szCode = nullptr; ///< The code
const char* m_szCurrent = nullptr; ///< The current position in the code
const char* m_szPrependedPos = nullptr; ///< The position in the code where the prepended code is
///< inserted into.
const char* m_szPrependedCode = nullptr; ///< The start position of the prepended code.
const char* m_szPrependedCurrent = nullptr; ///< The current position in the prepended code;
std::string m_ssPrependedCode; ///< The prepended code being returned before parsing of the
///< original code continues.
mutable std::list<std::string> m_lstCodeChunks; ///< List of code chunks existing of part or complete
///< prepended code.
uint32_t m_uiLine = 0; ///< The current line
uint32_t m_uiCol = 0; ///< The current column
};
#endif // !defined CODEPOS_H

View File

@@ -0,0 +1,439 @@
#include "constvariant.h"
#include "constvariant.inl"
CConstVariant::CConstVariant(const CConstVariant& rvar) : m_varValue(rvar.m_varValue)
{}
CConstVariant::CConstVariant(CConstVariant&& rvar) noexcept : m_varValue(std::move(rvar.m_varValue))
{}
CConstVariant::CConstVariant(bool bValue) : m_varValue(bValue) {}
CConstVariant::CConstVariant(int8_t iValue) : m_varValue(iValue) {}
CConstVariant::CConstVariant(uint8_t uiValue) : m_varValue(uiValue) {}
CConstVariant::CConstVariant(int16_t iValue) : m_varValue(iValue) {}
CConstVariant::CConstVariant(uint16_t uiValue) : m_varValue(uiValue) {}
#ifdef _WIN32
CConstVariant::CConstVariant(long int iValue) : m_varValue(static_cast<int32_t>(iValue)) {}
CConstVariant::CConstVariant(unsigned long int uiValue) : m_varValue(static_cast<uint32_t>(uiValue)) {}
#endif
CConstVariant::CConstVariant(int32_t iValue) : m_varValue(iValue) {}
CConstVariant::CConstVariant(uint32_t uiValue) : m_varValue(uiValue) {}
CConstVariant::CConstVariant(int64_t iValue) : m_varValue(iValue) {}
CConstVariant::CConstVariant(uint64_t uiValue) : m_varValue(uiValue) {}
#if defined(__GNUC__) && !defined(_WIN32)
CConstVariant::CConstVariant(long long int iValue) : CConstVariant(static_cast<int64_t>(iValue)) {}
CConstVariant::CConstVariant(unsigned long long int uiValue) : CConstVariant(static_cast<uint64_t>(uiValue)) {}
#endif
CConstVariant::CConstVariant(fixed fixValue) : m_varValue(fixValue) {}
CConstVariant::CConstVariant(float fValue) : m_varValue(fValue) {}
CConstVariant::CConstVariant(double dValue) : m_varValue(dValue) {}
CConstVariant::CConstVariant(long double ldValue) : m_varValue(ldValue) {}
CConstVariant::CConstVariant(const std::string& rssValue) : m_varValue(rssValue) {}
CConstVariant::CConstVariant(const std::u16string& rssValue) : m_varValue(rssValue) {}
CConstVariant::CConstVariant(const std::u32string& rssValue) : m_varValue(rssValue) {}
CConstVariant::CConstVariant(const std::wstring& rssValue) : m_varValue(rssValue) {}
CConstVariant& CConstVariant::operator=(const CConstVariant& rvar)
{
m_varValue = rvar.m_varValue;
return *this;
}
CConstVariant& CConstVariant::operator=(CConstVariant&& rvar) noexcept
{
m_varValue = std::move(rvar.m_varValue);
return *this;
}
bool CConstVariant::IsArithmetic() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return std::is_arithmetic_v<bool>;
case ETypeMapping::type_uint8_t: return std::is_arithmetic_v<uint8_t>;
case ETypeMapping::type_uint16_t: return std::is_arithmetic_v<uint16_t>;
case ETypeMapping::type_uint32_t: return std::is_arithmetic_v<uint32_t>;
case ETypeMapping::type_uint64_t: return std::is_arithmetic_v<uint64_t>;
case ETypeMapping::type_int8_t: return std::is_arithmetic_v<int8_t>;
case ETypeMapping::type_int16_t: return std::is_arithmetic_v<int16_t>;
case ETypeMapping::type_int32_t: return std::is_arithmetic_v<int32_t>;
case ETypeMapping::type_int64_t: return std::is_arithmetic_v<int64_t>;
case ETypeMapping::type_fixed: return std::is_arithmetic_v<fixed>;
case ETypeMapping::type_float: return std::is_arithmetic_v<float>;
case ETypeMapping::type_double: return std::is_arithmetic_v<double>;
case ETypeMapping::type_long_double: return std::is_arithmetic_v<long double>;
case ETypeMapping::type_string: return std::is_arithmetic_v<std::string>;
case ETypeMapping::type_u16string: return std::is_arithmetic_v<std::u16string>;
case ETypeMapping::type_u32string: return std::is_arithmetic_v<std::u32string>;
case ETypeMapping::type_wstring: return std::is_arithmetic_v<std::wstring>;
default:
throw CCompileException("Internal error: the variant doesn't contain any valid data type.");
}
}
bool CConstVariant::IsIntegral() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return std::is_integral_v<bool>;
case ETypeMapping::type_uint8_t: return std::is_integral_v<uint8_t>;
case ETypeMapping::type_uint16_t: return std::is_integral_v<uint16_t>;
case ETypeMapping::type_uint32_t: return std::is_integral_v<uint32_t>;
case ETypeMapping::type_uint64_t: return std::is_integral_v<uint64_t>;
case ETypeMapping::type_int8_t: return std::is_integral_v<int8_t>;
case ETypeMapping::type_int16_t: return std::is_integral_v<int16_t>;
case ETypeMapping::type_int32_t: return std::is_integral_v<int32_t>;
case ETypeMapping::type_int64_t: return std::is_integral_v<int64_t>;
case ETypeMapping::type_fixed: return std::is_integral_v<fixed>;
case ETypeMapping::type_float: return std::is_integral_v<float>;
case ETypeMapping::type_double: return std::is_integral_v<double>;
case ETypeMapping::type_long_double: return std::is_integral_v<long double>;
case ETypeMapping::type_string: return std::is_integral_v<std::string>;
case ETypeMapping::type_u16string: return std::is_integral_v<std::u16string>;
case ETypeMapping::type_u32string: return std::is_integral_v<std::u32string>;
case ETypeMapping::type_wstring: return std::is_integral_v<std::wstring>;
default:
throw CCompileException("Internal error: the variant doesn't contain any valid data type.");
}
}
bool CConstVariant::IsFloatingPoint() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return std::is_floating_point_v<bool>;
case ETypeMapping::type_uint8_t: return std::is_floating_point_v<uint8_t>;
case ETypeMapping::type_uint16_t: return std::is_floating_point_v<uint16_t>;
case ETypeMapping::type_uint32_t: return std::is_floating_point_v<uint32_t>;
case ETypeMapping::type_uint64_t: return std::is_floating_point_v<uint64_t>;
case ETypeMapping::type_int8_t: return std::is_floating_point_v<int8_t>;
case ETypeMapping::type_int16_t: return std::is_floating_point_v<int16_t>;
case ETypeMapping::type_int32_t: return std::is_floating_point_v<int32_t>;
case ETypeMapping::type_int64_t: return std::is_floating_point_v<int64_t>;
case ETypeMapping::type_fixed: return std::is_floating_point_v<fixed>;
case ETypeMapping::type_float: return std::is_floating_point_v<float>;
case ETypeMapping::type_double: return std::is_floating_point_v<double>;
case ETypeMapping::type_long_double: return std::is_floating_point_v<long double>;
case ETypeMapping::type_string: return std::is_floating_point_v<std::string>;
case ETypeMapping::type_u16string: return std::is_floating_point_v<std::u16string>;
case ETypeMapping::type_u32string: return std::is_floating_point_v<std::u32string>;
case ETypeMapping::type_wstring: return std::is_floating_point_v<std::wstring>;
default:
throw CCompileException("Internal error: the variant doesn't contain any valid data type.");
}
}
bool CConstVariant::IsBoolean() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return true;
case ETypeMapping::type_uint8_t: return false;
case ETypeMapping::type_uint16_t: return false;
case ETypeMapping::type_uint32_t: return false;
case ETypeMapping::type_uint64_t: return false;
case ETypeMapping::type_int8_t: return false;
case ETypeMapping::type_int16_t: return false;
case ETypeMapping::type_int32_t: return false;
case ETypeMapping::type_int64_t: return false;
case ETypeMapping::type_fixed: return false;
case ETypeMapping::type_float: return false;
case ETypeMapping::type_double: return false;
case ETypeMapping::type_long_double: return false;
case ETypeMapping::type_string: return false;
case ETypeMapping::type_u16string: return false;
case ETypeMapping::type_u32string: return false;
case ETypeMapping::type_wstring: return false;
default:
throw CCompileException("Internal error: the variant doesn't contain any valid data type.");
}
}
bool CConstVariant::IsSigned() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return std::is_signed_v<bool>;
case ETypeMapping::type_uint8_t: return std::is_signed_v<uint8_t>;
case ETypeMapping::type_uint16_t: return std::is_signed_v<uint16_t>;
case ETypeMapping::type_uint32_t: return std::is_signed_v<uint32_t>;
case ETypeMapping::type_uint64_t: return std::is_signed_v<uint64_t>;
case ETypeMapping::type_int8_t: return std::is_signed_v<int8_t>;
case ETypeMapping::type_int16_t: return std::is_signed_v<int16_t>;
case ETypeMapping::type_int32_t: return std::is_signed_v<int32_t>;
case ETypeMapping::type_int64_t: return std::is_signed_v<int64_t>;
case ETypeMapping::type_fixed: return std::is_signed_v<fixed>;
case ETypeMapping::type_float: return std::is_signed_v<float>;
case ETypeMapping::type_double: return std::is_signed_v<double>;
case ETypeMapping::type_long_double: return std::is_signed_v<long double>;
case ETypeMapping::type_string: return std::is_signed_v<std::string>;
case ETypeMapping::type_u16string: return std::is_signed_v<std::u16string>;
case ETypeMapping::type_u32string: return std::is_signed_v<std::u32string>;
case ETypeMapping::type_wstring: return std::is_signed_v<std::wstring>;
default:
throw CCompileException("Internal error: the variant doesn't contain any valid data type.");
}
}
bool CConstVariant::IsUnsigned() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return std::is_unsigned_v<bool>;
case ETypeMapping::type_uint8_t: return std::is_unsigned_v<uint8_t>;
case ETypeMapping::type_uint16_t: return std::is_unsigned_v<uint16_t>;
case ETypeMapping::type_uint32_t: return std::is_unsigned_v<uint32_t>;
case ETypeMapping::type_uint64_t: return std::is_unsigned_v<uint64_t>;
case ETypeMapping::type_int8_t: return std::is_unsigned_v<int8_t>;
case ETypeMapping::type_int16_t: return std::is_unsigned_v<int16_t>;
case ETypeMapping::type_int32_t: return std::is_unsigned_v<int32_t>;
case ETypeMapping::type_int64_t: return std::is_unsigned_v<int64_t>;
case ETypeMapping::type_fixed: return std::is_unsigned_v<fixed>;
case ETypeMapping::type_float: return std::is_unsigned_v<float>;
case ETypeMapping::type_double: return std::is_unsigned_v<double>;
case ETypeMapping::type_long_double: return std::is_unsigned_v<long double>;
case ETypeMapping::type_string: return std::is_unsigned_v<std::string>;
case ETypeMapping::type_u16string: return std::is_unsigned_v<std::u16string>;
case ETypeMapping::type_u32string: return std::is_unsigned_v<std::u32string>;
case ETypeMapping::type_wstring: return std::is_unsigned_v<std::wstring>;
default:
throw CCompileException("Internal error: the variant doesn't contain any valid data type.");
}
}
size_t CConstVariant::Ranking() const
{
// The ranking is provided through the index.
return m_varValue.index();
}
void CConstVariant::Convert(size_t nRank)
{
if (nRank == Ranking()) return;
switch (static_cast<ETypeMapping>(nRank))
{
case ETypeMapping::type_bool: m_varValue = Get<bool>(); break;
case ETypeMapping::type_uint8_t: m_varValue = Get<uint8_t>(); break;
case ETypeMapping::type_uint16_t: m_varValue = Get<uint16_t>(); break;
case ETypeMapping::type_uint32_t: m_varValue = Get<uint32_t>(); break;
case ETypeMapping::type_uint64_t: m_varValue = Get<uint64_t>(); break;
case ETypeMapping::type_int8_t: m_varValue = Get<int8_t>(); break;
case ETypeMapping::type_int16_t: m_varValue = Get<int16_t>(); break;
case ETypeMapping::type_int32_t: m_varValue = Get<int32_t>(); break;
case ETypeMapping::type_int64_t: m_varValue = Get<int64_t>(); break;
case ETypeMapping::type_fixed: m_varValue = Get<fixed>(); break;
case ETypeMapping::type_float: m_varValue = Get<float>(); break;
case ETypeMapping::type_double: m_varValue = Get<double>(); break;
case ETypeMapping::type_long_double: m_varValue = Get<long double>(); break;
default:
throw CCompileException("Internal error: incompatible data type conversion.");
}
}
CConstVariant CConstVariant::operator!() const
{
return UnaryOperationIntegral(*this, [](auto tOperand) {return !tOperand;});
}
CConstVariant CConstVariant::operator~() const
{
return UnaryOperationIntegral(*this, [](auto tOperand) -> CConstVariant {
if constexpr (!std::is_same_v<decltype(tOperand), bool>)
return ~tOperand;
else
throw CCompileException("Cannot execute bitwise operations on a boolean."); });
}
CConstVariant CConstVariant::operator+() const
{
return UnaryOperation(*this, [](auto tOperand) {return tOperand;});
}
CConstVariant CConstVariant::operator-() const
{
return CConstVariant::UnaryOperation(*this, [](auto tOperand) -> CConstVariant {
if constexpr (!std::is_same_v<decltype(tOperand), bool>)
{
if constexpr (std::is_signed_v<decltype(tOperand)>)
return -tOperand;
else if constexpr (std::is_integral_v<decltype(tOperand)>)
{
// Two's complement plus 1.
return ~tOperand + 1;
} else
throw CCompileException("Internal error: cannot execute unary arithmic operation on the type.");
}
else
throw CCompileException("Cannot execute unary arithmic operations on a boolean."); });
}
void Equalize(CConstVariant& rvar1, CConstVariant& rvar2)
{
// Are the types of both variant the same?
if (rvar1.Ranking() == rvar2.Ranking()) return;
// Equalization only works if both types are arithmetic.
if (!rvar1.IsArithmetic() || !rvar2.IsArithmetic())
throw CCompileException("The types of both operands are not compatible.");
// Check for highest ranking and adapt the lowest.
if (rvar1.Ranking() > rvar2.Ranking())
rvar2.Convert(rvar1.Ranking());
else
rvar1.Convert(rvar2.Ranking());
}
CConstVariant operator*(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 * tOperand2;});
}
CConstVariant operator/(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#endif
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) -> CConstVariant
{
if constexpr (!std::is_same_v<decltype(tOperand1), bool>)
{
if (!tOperand2) throw CCompileException("Division by zero.");
return tOperand1 / tOperand2;
}
else
throw CCompileException("Cannot divide a boolean.");
});
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
CConstVariant operator+(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 + tOperand2;});
}
CConstVariant operator-(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 - tOperand2;});
}
CConstVariant operator<(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 < tOperand2;});
}
CConstVariant operator<=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 <= tOperand2;});
}
CConstVariant operator>(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 > tOperand2;});
}
CConstVariant operator>=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 >= tOperand2;});
}
CConstVariant operator==(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 == tOperand2;});
}
CConstVariant operator!=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 != tOperand2;});
}
CConstVariant operator%(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#endif
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) -> CConstVariant
{
if constexpr (std::is_integral_v<decltype(tOperand1)> && !std::is_same_v<decltype(tOperand1), bool>)
{
if (!tOperand2) throw CCompileException("Division by zero.");
return tOperand1 % tOperand2;
}
else
throw CCompileException("Cannot divide a boolean.");
});
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
CConstVariant operator<<(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperationIntegral(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 << tOperand2;});
}
CConstVariant operator>>(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperationIntegral(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 >> tOperand2;});
}
CConstVariant operator&(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperationIntegral(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 & tOperand2;});
}
CConstVariant operator^(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperationIntegral(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 ^ tOperand2;});
}
CConstVariant operator|(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperationIntegral(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2) {return tOperand1 | tOperand2;});
}
CConstVariant operator&&(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2)
{
if constexpr (std::is_integral_v<decltype(tOperand1)> &&
std::is_integral_v<decltype(tOperand2)>)
return tOperand1 && tOperand2;
else
return false;
});
}
CConstVariant operator||(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2)
{
return CConstVariant::BinaryOperation(rvarOperand1, rvarOperand2,
[](auto tOperand1, auto tOperand2)
{
if constexpr (std::is_integral_v<decltype(tOperand1)> &&
std::is_integral_v<decltype(tOperand2)>)
return tOperand1 || tOperand2;
else
return false;
});
}

View File

@@ -0,0 +1,550 @@
#ifndef VARIANT_H
#define VARIANT_H
#include <variant>
#include <string>
#include <type_traits>
#include <limits>
#include <cstdint>
/**
* @brief Fixed data type is based on the double data type. Highest value is 31 digits positive and lowest 31 digits negative. The
* best precision is 1/31.
*/
struct fixed
{
double dValue; ///< The value
/**
* @brief Constructor
* @tparam TType Type of the value to create a fixed type of
* @param[in] tValue The value to assign
*/
template <typename TType>
constexpr fixed(TType tValue) : dValue(static_cast<double>(tValue)) {}
/**
* @brief Assignment operator
* @tparam TType Type of the value to assign
* @param[in] tValue The value to assign
* @return Reference to this class
*/
template <typename TType>
constexpr fixed& operator=(TType tValue) { dValue = tValue; return *this; }
/**
* @{
* @brief Contained value as double
* @return The value
*/
constexpr operator double() const {return dValue;}
constexpr operator const double&() const {return dValue;}
constexpr operator double&() {return dValue;}
/**
* @}
*/
/**
* @brief Unary operator to return a fixed type.
* @return the fixed type as a copy from this class.
*/
fixed constexpr operator+() const {return *this;}
/**
* @brief Unary operator to return a negative fixed type.
* @return the fixed type as a copy from this class.
*/
fixed constexpr operator-() const {return fixed(-dValue);}
};
/**
* @brief Add two fixed types
* @param[in] val1 Value 1
* @param[in] val2 Value 2
* @return The result
*/
inline fixed operator+(fixed val1, fixed val2) { return val1.dValue + val2.dValue; }
/**
* @brief Subtract two fixed types
* @param[in] val1 Value 1
* @param[in] val2 Value 2
* @return The result
*/
inline fixed operator-(fixed val1, fixed val2) { return val1.dValue - val2.dValue; }
/**
* @brief Multiply two fixed types
* @param[in] val1 Value 1
* @param[in] val2 Value 2
* @return The result
*/
inline fixed operator*(fixed val1, fixed val2) { return val1.dValue * val2.dValue; }
/**
* @brief Divide two fixed types
* @param[in] val1 Value 1
* @param[in] val2 Value 2
* @return The result
*/
inline fixed operator/(fixed val1, fixed val2) { return val1.dValue / val2.dValue; }
/**
* @{
* @brief Compare two types
* @param[in] val1 Value 1
* @param[in] val2 Value 2
* @return The result
*/
inline bool operator<(fixed val1, fixed val2) { return val1.dValue < val2.dValue; }
inline bool operator<(fixed val1, double val2) { return val1.dValue < val2; }
inline bool operator<(double val1, fixed val2) { return val1 < val2.dValue; }
inline bool operator<=(fixed val1, fixed val2) { return val1.dValue <= val2.dValue; }
inline bool operator<=(fixed val1, double val2) { return val1.dValue <= val2; }
inline bool operator<=(double val1, fixed val2) { return val1 <= val2.dValue; }
inline bool operator>(fixed val1, fixed val2) { return val1.dValue > val2.dValue; }
inline bool operator>(fixed val1, double val2) { return val1.dValue > val2; }
inline bool operator>(double val1, fixed val2) { return val1 > val2.dValue; }
inline bool operator>=(fixed val1, fixed val2) { return val1.dValue >= val2.dValue; }
inline bool operator>=(fixed val1, double val2) { return val1.dValue >= val2; }
inline bool operator>=(double val1, fixed val2) { return val1 >= val2.dValue; }
inline bool operator==(fixed val1, fixed val2) { return val1.dValue == val2.dValue; }
inline bool operator==(fixed val1, double val2) { return val1.dValue == val2; }
inline bool operator==(double val1, fixed val2) { return val1 == val2.dValue; }
inline bool operator!=(fixed val1, fixed val2) { return val1.dValue != val2.dValue; }
inline bool operator!=(fixed val1, double val2) { return val1.dValue != val2; }
inline bool operator!=(double val1, fixed val2) { return val1 != val2.dValue; }
/**
* @}
*/
namespace std
{
#ifndef DOXYGEN_IGNORE
/**
* @brief Specialization of floating point variable for fixed data type.
*/
template <>
inline constexpr bool is_floating_point_v<::fixed> = true;
/**
* @brief Specialization of signed number variable for fixed data type.
*/
template <>
inline constexpr bool is_signed_v<::fixed> = true;
/**
* @brief Specialization of arithmic data type variable for fixed data type.
*/
template <>
inline constexpr bool is_arithmetic_v<::fixed> = true;
#endif
/**
* @brief Specialization of numeric limits class for fixed data type.
*/
template <>
struct numeric_limits<::fixed> : numeric_limits<double>
{
/**
* @brief Returns the smallest finite value representable by fixed.
* @return The smallest finite type.
*/
[[nodiscard]] static constexpr ::fixed(min)() noexcept
{
return 1.0 / static_cast<double>(max());
}
/**
* @brief Returns the minimum largest value representable by fixed.
* @return The largest finite type.
*/
[[nodiscard]] static constexpr ::fixed(max)() noexcept
{
// 31 bits available
return 1ll << 31ll;
}
/**
* @brief Returns the lowest finite value representable by fixed.
* @return The lowest finite type.
*/
[[nodiscard]] static constexpr ::fixed lowest() noexcept
{
return -(max)();
}
};
}
/**
* @brief Variant class to store const values.
* @details This class is used for the storage of all possible data types as well as solving the expressions in const assignments.
* Only 32-bit, 64-bit integers, largest floating point and several string data type variations are used. The conversion rules for
* data types follow the C++11 conversion rules:
* - Assignment of data type:
* - Signed integers are assigned to int32_t when smaller than 64-bit; otherwise are assigned to int64_t.
* - Unsigned integers are assigned to uint32_t when smaller than 64-bit; otherwise are assigned to uint64_t.
* - Integral signed data types are: char, short, long, long long, int8_t, int16_t, int32_t int64_t.
* - Integral unsigned data types are: bool, unsigned short, unsigned long, unsigned long long, uint8_t, uint16_t, uint32_t
* uint64_t, wchar_t, char16_t, char32_t.
* - String objects and string pointers are assigned to their corresponding counterparts.
* - Arithmic operations with two operands:
* - If either operand is long double, the other operand is converted to long double.
* - If both integral operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the
* - operand with the greater conversion rank.
* - Otherwise, if the unsigned integral operand's conversion rank is greater or equal to the conversion rank of the signed
* operand, the signed operand is converted to the unsigned operand's type.
* - Otherwise, if the signed integral operand's type can represent all values of the unsigned operand, the unsigned operand
* is converted to the signed operand's type.
* - Otherwise, both integral operands are converted to the unsigned counterpart of the signed operand's type.
* - Conversions with and between string based operands are not supported.
* - Cast to data type:
* - If the stored data type and target data type are both signed or both unsigned, the target data type boundaries are
* checked with the value before assignment takes place.
* - If the destination type is unsigned, the resulting value is the smallest unsigned value equal to the source value modulo
* 2^n where n is the number of bits used to represent the destination type.
* - If the destination type is signed, the value does not change if the source integer can be represented in the destination
* type.
*/
class CConstVariant
{
/**
* The variant type containing the value in one of the data types supplied. The data type order provides the ranking of the
* types.
*/
using TVariant = std::variant<bool, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, fixed, float,
double, long double, std::string, std::u16string, std::u32string, std::wstring>;
public:
/**
* @brief Type to index mapping.
* @remarks 'char' is represented by 'int8_t'. 'char16_t' is represented by 'uint16_t'. 'char32_t' is represented by
* 'int32_t'. 'wchar_t' is either represented by 'uint16_t' or by 'uint32_t'. ASCII and UTF-8 strings are represented by
* 'std::string'.
*/
enum ETypeMapping
{
type_bool = 0, type_int8_t, type_uint8_t, type_int16_t, type_uint16_t, type_int32_t, type_uint32_t, type_int64_t,
type_uint64_t, type_fixed, type_float, type_double, type_long_double, type_string, type_u16string, type_u32string,
type_wstring,
};
/**
* @brief Default constructor.
*/
CConstVariant() = default;
/**
* @brief Copy constructor.
* @param[in] rvar Reference to the const variant to copy from.
*/
CConstVariant(const CConstVariant& rvar);
/**
* @brief Move constructor is not available.
* @param[in] rvar Reference to the const variant to move from.
*/
CConstVariant(CConstVariant&& rvar) noexcept;
/**
* @brief Assignment constructor.
* @param[in] bValue Value to assign.
*/
CConstVariant(bool bValue);
/**
* @brief Assignment constructor.
* @param[in] iValue Value to assign.
*/
CConstVariant(int8_t iValue);
/**
* @brief Assignment constructor.
* @param[in] uiValue Value to assign.
*/
CConstVariant(uint8_t uiValue);
/**
* @brief Assignment constructor.
* @param[in] iValue Value to assign.
*/
CConstVariant(int16_t iValue);
/**
* @brief Assignment constructor.
* @param[in] uiValue Value to assign.
*/
CConstVariant(uint16_t uiValue);
#ifdef _WIN32
/**
* @brief Assignment constructor.
* @param[in] iValue Value to assign.
*/
CConstVariant(long int iValue);
/**
* @brief Assignment constructor.
* @param[in] uiValue Value to assign.
*/
CConstVariant(unsigned long int uiValue);
#endif
/**
* @brief Assignment constructor.
* @param[in] iValue Value to assign.
*/
CConstVariant(int32_t iValue);
/**
* @brief Assignment constructor.
* @param[in] uiValue Value to assign.
*/
CConstVariant(uint32_t uiValue);
/**
* @brief Assignment constructor.
* @param[in] iValue Value to assign.
*/
CConstVariant(int64_t iValue);
#if defined(__GNUC__) && !defined(_WIN32)
/**
* @brief Assignment constructor.
* @param[in] iValue Value to assign.
*/
CConstVariant(long long int iValue);
/**
* @brief Assignment constructor.
* @param[in] uiValue Value to assign.
*/
CConstVariant(unsigned long long int uiValue);
#endif
/**
* @brief Assignment constructor.
* @param[in] uiValue Value to assign.
*/
CConstVariant(uint64_t uiValue);
/**
* @brief Assignment constructor.
* @param[in] fixValue Value to assign.
*/
CConstVariant(fixed fixValue);
/**
* @brief Assignment constructor.
* @param[in] fValue Value to assign.
*/
CConstVariant(float fValue);
/**
* @brief Assignment constructor.
* @param[in] dValue Value to assign.
*/
CConstVariant(double dValue);
/**
* @brief Assignment constructor.
* @param[in] ldValue Value to assign.
*/
CConstVariant(long double ldValue);
/**
* @brief Assignment constructor.
* @param[in] rssValue Reference to the string value to assign.
*/
CConstVariant(const std::string& rssValue);
/**
* @brief Assignment constructor.
* @param[in] rssValue Reference to the string value to assign.
*/
CConstVariant(const std::u16string& rssValue);
/**
* @brief Assignment constructor.
* @param[in] rssValue Reference to the string value to assign.
*/
CConstVariant(const std::u32string& rssValue);
/**
* @brief Assignment constructor.
* @param[in] rssValue Reference to the string value to assign.
*/
CConstVariant(const std::wstring& rssValue);
/**
* @brief Copy assignment operator
* @param[in] rvar Reference to the const variant to copy from.
* @return Reference to the const variant instance.
*/
CConstVariant& operator=(const CConstVariant& rvar);
/**
* @brief Move operator is not available.
* @param[in] rvar Reference to the const variant to move from.
* @return Reference to the const variant instance.
*/
CConstVariant& operator=(CConstVariant&& rvar) noexcept;
/**
* @brief Assignment operator.
* @tparam TType Type of the value to assign.
* @param[in] rtValue Reference to the value to assign.
* @return Reference to the const variant instance.
*/
template <typename TType>
CConstVariant& operator=(const TType& rtValue);
/**
* @brief Get the value of the variant casted to the provided target type.
* @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible.
* @tparam TTargetType The target type.
* @return The value casted to the target type.
*/
template <typename TTargetType>
TTargetType Get() const;
/**
* @brief Get the value as string.
* @return Get the value as a string.
*/
std::string GetAsString() const;
/**
* @{
* @brief Meta information
* @remarks The ranking function provides a number indicating the ranking of the type.
* @return Returns 'true' when the type stored in the variant has the specific attribute; 'false' otherwise.
*/
bool IsArithmetic() const;
bool IsIntegral() const;
bool IsFloatingPoint() const;
bool IsBoolean() const;
bool IsSigned() const;
bool IsUnsigned() const;
size_t Ranking() const;
template <typename TType> bool IsSame() const;
/**
* @}
*/
/**
* @brief Convert the variant to the type supplied.
* @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible.
* @param[in] nRank The rank of the type to convert to.
*/
void Convert(size_t nRank);
/**
* @brief Convert the variant to the type supplied.
* @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible.
* @tparam TType The type to convert to.
*/
template <typename TType>
void Convert();
/**
* @{
* @brief Unary arithmetic operators.
* @remarks The bit operator '~' and the logical operator '!' work with integral operands only.
* @result The result of the operation.
*/
CConstVariant operator!() const;
CConstVariant operator~() const;
CConstVariant operator+() const;
CConstVariant operator-() const;
/**
* @}
*/
/**
* @{
* @brief Do unary operation. The first version allows the operation on any arithmetic type. The second version works for
* integral operation only.
* @tparam TFunction The function type that should do the operation. The function uses the prototype:
* @code CConstVariant(auto tOperand); @endcode
* @param[in] rvarOperand The operand.
* @param[in] tOperation The function that is used for the operation.
* @return The const variant with the result.
*/
template <typename TFunction>
static CConstVariant UnaryOperation(const CConstVariant& rvarOperand, TFunction tOperation);
template <typename TFunction>
static CConstVariant UnaryOperationIntegral(const CConstVariant& rvarOperand, TFunction tOperation);
/**
* @}
*/
/**
* @{
* @brief Do binary operation. The first version allows the operation on any arithmetic type. The second version works for
* integral operation only.
* @tparam TFunction The function type that should do the operation. The function uses the prototype:
* @code CConstVariant(auto tOperand1, auto tOperand2); @endcode
* @param[in] rvarOperand1 The first operand.
* @param[in] rvarOperand2 The second operand.
* @param[in] tOperation The function that is used for the operation.
* @return The const variant with the result.
*/
template <typename TFunction>
static CConstVariant BinaryOperation(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2,
TFunction tOperation);
template <typename TFunction>
static CConstVariant BinaryOperationIntegral(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2,
TFunction tOperation);
/**
* @}
*/
private:
/**
* @brief Get the value of the variant.
* @details Get the value of the variant by providing the variant type as well as the target type. Do conversion if necessary.
* Follow the casting rules as defined at the class description.
* @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible.
* @tparam TTargetType The target type.
* @tparam TVariantType The variant type
* @return The value casted to the target type.
*/
template <typename TTargetType, typename TVariantType>
TTargetType InternalGet() const;
TVariant m_varValue; ///< The variant that stores the value.
};
/**
* @brief Equalize variants.
* @details Convert the variant of lesser ranking to a higher ranking. This function is used for binary arithmetic operations. The
* function implements the rules as described at the CConstVariant class description.
* @param[in] rvar1 The first variant.
* @param[in] rvar2 The second variant.
*/
void Equalize(CConstVariant& rvar1, CConstVariant& rvar2);
/**
* @{
* @brief Arithmetic operators.
* @remarks The bit operators (~ & | ^ << >>) as well as the remainder (%) operator work with integral operands only.
* @param[in] rvarOperand1 The first operand of a binary operation.
* @param[in] rvarOperand2 The second operand of a binary operation.
* @result The result of the operation.
*/
CConstVariant operator*(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator/(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator+(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator-(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator<(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator<=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator>(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator>=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator+(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator==(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator!=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator%(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator<<(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator>>(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator&(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator^(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator|(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator&&(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
CConstVariant operator||(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2);
/**
* @}
*/
#endif // !defined(VARIANT_H)

View File

@@ -0,0 +1,343 @@
#ifndef CONSTVARIANT_INL
#define CONSTVARIANT_INL
#include "exception.h"
#include "constvariant.h"
#include <cmath>
#include <cstdlib>
template <typename TType>
inline CConstVariant& CConstVariant::operator=(const TType& rtValue)
{
m_varValue = rtValue;
return *this;
}
#ifdef _WIN32
template <>
inline CConstVariant& CConstVariant::operator=(const long int& rtValue)
{
m_varValue = static_cast<int32_t>(rtValue);
return *this;
}
template <>
inline CConstVariant& CConstVariant::operator=(const unsigned long int& rtValue)
{
m_varValue = static_cast<uint32_t>(rtValue);
return *this;
}
#endif
#if defined( __GNUC__) && !defined(_WIN32)
template <>
inline CConstVariant& CConstVariant::operator=(const long long int& rtValue)
{
m_varValue = static_cast<int64_t>(rtValue);
return *this;
}
template <>
inline CConstVariant& CConstVariant::operator=(const unsigned long long int& rtValue)
{
m_varValue = static_cast<uint64_t>(rtValue);
return *this;
}
#endif
template <typename TTargetType>
inline TTargetType CConstVariant::Get() const
{
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return InternalGet<TTargetType, bool>();
case ETypeMapping::type_uint8_t: return InternalGet<TTargetType, uint8_t>();
case ETypeMapping::type_uint16_t: return InternalGet<TTargetType, uint16_t>();
case ETypeMapping::type_uint32_t: return InternalGet<TTargetType, uint32_t>();
case ETypeMapping::type_uint64_t: return InternalGet<TTargetType, uint64_t>();
case ETypeMapping::type_int8_t: return InternalGet<TTargetType, int8_t>();
case ETypeMapping::type_int16_t: return InternalGet<TTargetType, int16_t>();
case ETypeMapping::type_int32_t: return InternalGet<TTargetType, int32_t>();
case ETypeMapping::type_int64_t: return InternalGet<TTargetType, int64_t>();
case ETypeMapping::type_fixed: return InternalGet<TTargetType, fixed>();
case ETypeMapping::type_float: return InternalGet<TTargetType, float>();
case ETypeMapping::type_double: return InternalGet<TTargetType, double>();
case ETypeMapping::type_long_double: return InternalGet<TTargetType, long double>();
case ETypeMapping::type_string: return InternalGet<TTargetType, std::string>();
case ETypeMapping::type_u16string: return InternalGet<TTargetType, std::u16string>();
case ETypeMapping::type_u32string: return InternalGet<TTargetType, std::u32string>();
case ETypeMapping::type_wstring: return InternalGet<TTargetType, std::wstring>();
default:
throw CCompileException("Conversion to target data type is not supported.");
}
}
inline std::string CConstVariant::GetAsString() const
{
// TODO: Add conversion for the missing types
switch (static_cast<ETypeMapping>(m_varValue.index()))
{
case ETypeMapping::type_bool: return std::to_string(std::get<bool>(m_varValue));
case ETypeMapping::type_uint8_t: return std::to_string(std::get<uint8_t>(m_varValue));
case ETypeMapping::type_uint16_t: return std::to_string(std::get<uint16_t>(m_varValue));
case ETypeMapping::type_uint32_t: return std::to_string(std::get<uint32_t>(m_varValue));
case ETypeMapping::type_uint64_t: return std::to_string(std::get<uint64_t>(m_varValue));
case ETypeMapping::type_int8_t: return std::to_string(std::get<int8_t>(m_varValue));
case ETypeMapping::type_int16_t: return std::to_string(std::get<int16_t>(m_varValue));
case ETypeMapping::type_int32_t: return std::to_string(std::get<int32_t>(m_varValue));
case ETypeMapping::type_int64_t: return std::to_string(std::get<int64_t>(m_varValue));
case ETypeMapping::type_fixed: return "fixed";
case ETypeMapping::type_float: return std::to_string(std::get<float>(m_varValue));
case ETypeMapping::type_double: return std::to_string(std::get<double>(m_varValue));
case ETypeMapping::type_long_double: return std::to_string(std::get<long double>(m_varValue));
case ETypeMapping::type_string: return std::get<std::string>(m_varValue);
case ETypeMapping::type_u16string: return "UTF-16 string";
case ETypeMapping::type_u32string: return "UTF-32string";
case ETypeMapping::type_wstring: return "wstring";
default:
throw CCompileException("Creating a string from value is not possible.");
}
}
template <typename TType>
inline bool CConstVariant::IsSame() const
{
return std::holds_alternative<TType>(m_varValue);
}
template <typename TType>
inline void CConstVariant::Convert()
{
// Conversion needed?
if (IsSame<TType>()) return;
if constexpr (std::is_integral_v<TType>)
{
if constexpr (std::is_signed_v<TType>)
{
// Singed integer
// NOTE: 'if' is used instead of switch to allow the use of constexpr.
if constexpr (sizeof(TType) == sizeof(int8_t))
Convert(ETypeMapping::type_int8_t);
else if constexpr (sizeof(TType) == sizeof(int16_t))
Convert(ETypeMapping::type_int16_t);
else if constexpr (sizeof(TType) == sizeof(int32_t))
Convert(ETypeMapping::type_int32_t);
else
Convert(ETypeMapping::type_int64_t);
} else
{
// Unsigned integer
// NOTE: 'if' is used instead of switch to allow the use of constexpr.
if constexpr (sizeof(TType) == sizeof(uint8_t))
{
if constexpr (std::is_same_v<bool, TType>)
Convert(ETypeMapping::type_bool);
else
Convert(ETypeMapping::type_uint8_t);
} else if constexpr (sizeof(TType) == sizeof(uint16_t))
Convert(ETypeMapping::type_uint16_t);
else if constexpr (sizeof(TType) == sizeof(uint32_t))
Convert(ETypeMapping::type_uint32_t);
else
Convert(ETypeMapping::type_uint64_t);
}
// Done!
return;
}
else if constexpr (std::is_floating_point_v<TType>)
{
// NOTE: 'if' is used instead of switch to allow the use of constexpr.
if constexpr (std::is_same_v<TType, fixed>)
Convert(ETypeMapping::type_fixed);
else if constexpr (sizeof(TType) == sizeof(float))
Convert(ETypeMapping::type_float);
else if constexpr (sizeof(TType) == sizeof(double))
Convert(ETypeMapping::type_double);
else
Convert(ETypeMapping::type_long_double);
// Done!
return;
} else
{
// Conversion is only possible between arithmetic types.
throw CCompileException("Internal error: incompatible data type conversion.");
}
}
template <typename TFunction>
inline CConstVariant CConstVariant::UnaryOperation(const CConstVariant& rvarOperand, TFunction tOperation)
{
// Based on the operand type, execute the provided function
switch (static_cast<ETypeMapping>(rvarOperand.Ranking()))
{
case ETypeMapping::type_bool: return tOperation(rvarOperand.Get<bool>()); break;
case ETypeMapping::type_uint8_t: return tOperation(rvarOperand.Get<uint8_t>()); break;
case ETypeMapping::type_uint16_t: return tOperation(rvarOperand.Get<uint16_t>()); break;
case ETypeMapping::type_uint32_t: return tOperation(rvarOperand.Get<uint32_t>()); break;
case ETypeMapping::type_uint64_t: return tOperation(rvarOperand.Get<uint64_t>()); break;
case ETypeMapping::type_int8_t: return tOperation(rvarOperand.Get<int8_t>()); break;
case ETypeMapping::type_int16_t: return tOperation(rvarOperand.Get<int16_t>()); break;
case ETypeMapping::type_int32_t: return tOperation(rvarOperand.Get<int32_t>()); break;
case ETypeMapping::type_int64_t: return tOperation(rvarOperand.Get<int64_t>()); break;
case ETypeMapping::type_fixed: return tOperation(rvarOperand.Get<fixed>()); break;
case ETypeMapping::type_float: return tOperation(rvarOperand.Get<float>()); break;
case ETypeMapping::type_double: return tOperation(rvarOperand.Get<double>()); break;
case ETypeMapping::type_long_double: return tOperation(rvarOperand.Get<long double>()); break;
default:
throw CCompileException("Internal error: incompatible data type conversion.");
}
}
template <typename TFunction>
inline CConstVariant CConstVariant::UnaryOperationIntegral(const CConstVariant& rvarOperand, TFunction tOperation)
{
// Based on the operand type, execute the provided function
switch (static_cast<ETypeMapping>(rvarOperand.Ranking()))
{
case ETypeMapping::type_bool: return tOperation(rvarOperand.Get<bool>()); break;
case ETypeMapping::type_uint8_t: return tOperation(rvarOperand.Get<uint8_t>()); break;
case ETypeMapping::type_uint16_t: return tOperation(rvarOperand.Get<uint16_t>()); break;
case ETypeMapping::type_uint32_t: return tOperation(rvarOperand.Get<uint32_t>()); break;
case ETypeMapping::type_uint64_t: return tOperation(rvarOperand.Get<uint64_t>()); break;
case ETypeMapping::type_int8_t: return tOperation(rvarOperand.Get<int8_t>()); break;
case ETypeMapping::type_int16_t: return tOperation(rvarOperand.Get<int16_t>()); break;
case ETypeMapping::type_int32_t: return tOperation(rvarOperand.Get<int32_t>()); break;
case ETypeMapping::type_int64_t: return tOperation(rvarOperand.Get<int64_t>()); break;
default:
throw CCompileException("Internal error: incompatible data type conversion.");
}
}
template <typename TFunction>
inline CConstVariant CConstVariant::BinaryOperation(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2,
TFunction tOperation)
{
// Equalize the operands
CConstVariant varOperand1 = rvarOperand1;
CConstVariant varOperand2 = rvarOperand2;
Equalize(varOperand1, varOperand2);
// Based on the operand type, execute the provided function
switch (static_cast<ETypeMapping>(varOperand1.Ranking()))
{
// NOTE: Arithmetic operations on "bool" can cause warnings. Use uint8_t instead.
case ETypeMapping::type_bool: return tOperation(varOperand1.Get<uint8_t>(), varOperand2.Get<uint8_t>()); break;
case ETypeMapping::type_uint8_t: return tOperation(varOperand1.Get<uint8_t>(), varOperand2.Get<uint8_t>()); break;
case ETypeMapping::type_uint16_t: return tOperation(varOperand1.Get<uint16_t>(), varOperand2.Get<uint16_t>()); break;
case ETypeMapping::type_uint32_t: return tOperation(varOperand1.Get<uint32_t>(), varOperand2.Get<uint32_t>()); break;
case ETypeMapping::type_uint64_t: return tOperation(varOperand1.Get<uint64_t>(), varOperand2.Get<uint64_t>()); break;
case ETypeMapping::type_int8_t: return tOperation(varOperand1.Get<int8_t>(), varOperand2.Get<int8_t>()); break;
case ETypeMapping::type_int16_t: return tOperation(varOperand1.Get<int16_t>(), varOperand2.Get<int16_t>()); break;
case ETypeMapping::type_int32_t: return tOperation(varOperand1.Get<int32_t>(), varOperand2.Get<int32_t>()); break;
case ETypeMapping::type_int64_t: return tOperation(varOperand1.Get<int64_t>(), varOperand2.Get<int64_t>()); break;
case ETypeMapping::type_fixed: return tOperation(varOperand1.Get<fixed>(), varOperand2.Get<fixed>()); break;
case ETypeMapping::type_float: return tOperation(varOperand1.Get<float>(), varOperand2.Get<float>()); break;
case ETypeMapping::type_double: return tOperation(varOperand1.Get<double>(), varOperand2.Get<double>()); break;
case ETypeMapping::type_long_double: return tOperation(varOperand1.Get<long double>(), varOperand2.Get<long double>()); break;
default:
throw CCompileException("Internal error: incompatible data type conversion.");
}
}
template <typename TFunction>
inline CConstVariant CConstVariant::BinaryOperationIntegral(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2,
TFunction tOperation)
{
// Equalize the operands
CConstVariant varOperand1 = rvarOperand1;
CConstVariant varOperand2 = rvarOperand2;
Equalize(varOperand1, varOperand2);
// Based on the operand type, execute the provided function
switch (static_cast<ETypeMapping>(varOperand1.Ranking()))
{
case ETypeMapping::type_uint8_t: return tOperation(varOperand1.Get<uint8_t>(), varOperand2.Get<uint8_t>()); break;
case ETypeMapping::type_uint16_t: return tOperation(varOperand1.Get<uint16_t>(), varOperand2.Get<uint16_t>()); break;
case ETypeMapping::type_uint32_t: return tOperation(varOperand1.Get<uint32_t>(), varOperand2.Get<uint32_t>()); break;
case ETypeMapping::type_uint64_t: return tOperation(varOperand1.Get<uint64_t>(), varOperand2.Get<uint64_t>()); break;
case ETypeMapping::type_int8_t: return tOperation(varOperand1.Get<int8_t>(), varOperand2.Get<int8_t>()); break;
case ETypeMapping::type_int16_t: return tOperation(varOperand1.Get<int16_t>(), varOperand2.Get<int16_t>()); break;
case ETypeMapping::type_int32_t: return tOperation(varOperand1.Get<int32_t>(), varOperand2.Get<int32_t>()); break;
case ETypeMapping::type_int64_t: return tOperation(varOperand1.Get<int64_t>(), varOperand2.Get<int64_t>()); break;
default:
throw CCompileException("Internal error: incompatible data type conversion.");
}
}
template <typename TTargetType, typename TVariantType>
inline TTargetType CConstVariant::InternalGet() const
{
if constexpr (std::is_same_v<TTargetType, TVariantType>)
return std::get<TVariantType>(m_varValue);
else if constexpr (std::is_floating_point_v<TTargetType> && std::is_arithmetic_v<TVariantType>)
{
TVariantType tValue = std::get<TVariantType>(m_varValue);
if (static_cast<long double>(tValue) > static_cast<long double>(std::numeric_limits<TTargetType>::max()))
throw CCompileException("Cannot cast to type, the value exceeds the maximum possible value of the target type.");
if (static_cast<long double>(tValue) < static_cast<long double>(std::numeric_limits<TTargetType>::lowest()))
throw CCompileException("Cannot cast to type, the value is below the minumum possible value of the target type.");
if constexpr (std::is_floating_point_v<TVariantType>)
{
int iExpValue = 0, iExpMin = 0;
long double ldDigitsValue = std::fabs(std::frexp(static_cast<long double>(tValue), &iExpValue));
long double ldDigitsMin = std::frexp(static_cast<long double>(std::numeric_limits<TTargetType>::min()), &iExpMin);
if ((iExpValue < iExpMin) || ((iExpValue == iExpMin) && ldDigitsValue < ldDigitsMin))
throw CCompileException("Cannot cast to type, the value precision is below the smallest possible"
" precision of the target type.");
}
return static_cast<TTargetType>(tValue);
}
else if constexpr (std::is_integral_v<TTargetType> && std::is_integral_v<TVariantType> && !std::is_same_v<TTargetType, bool>)
{
if constexpr (std::is_signed_v<TTargetType> && std::is_signed_v<TVariantType>)
{
TVariantType tValue = std::get<TVariantType>(m_varValue);
if (static_cast<int64_t>(tValue) > static_cast<int64_t>(std::numeric_limits<TTargetType>::max()))
throw CCompileException("Cannot cast to type, the value exceeds the maximum possible value of the target type.");
if (static_cast<int64_t>(tValue) < static_cast<int64_t>(std::numeric_limits<TTargetType>::min()))
throw CCompileException("Cannot cast to type, the value is below the minumum possible value of the target type.");
return static_cast<TTargetType>(tValue);
}
if constexpr (std::is_unsigned_v<TTargetType> && std::is_unsigned_v<TVariantType>)
{
TVariantType tValue = std::get<TVariantType>(m_varValue);
if (static_cast<uint64_t>(tValue) > static_cast<uint64_t>(std::numeric_limits<TTargetType>::max()))
throw CCompileException("Cannot cast to type, the value exceeds the maximum possible value of the target type.");
if (static_cast<uint64_t>(tValue) < static_cast<uint64_t>(std::numeric_limits<TTargetType>::min()))
throw CCompileException("Cannot cast to type, the value is below the minumum possible value of the target type.");
return static_cast<TTargetType>(tValue);
}
if constexpr (std::is_signed_v<TTargetType> && std::is_unsigned_v<TVariantType>)
{
TVariantType tValue = std::get<TVariantType>(m_varValue);
if (static_cast<uint64_t>(tValue) > static_cast<uint64_t>(std::numeric_limits<TTargetType>::max()))
throw CCompileException("Cannot cast to type, the value exceeds the maximum possible value of the target type.");
return static_cast<TTargetType>(tValue);
}
if constexpr (std::is_unsigned_v<TTargetType> && std::is_signed_v<TVariantType>)
{
TVariantType tValue = std::get<TVariantType>(m_varValue);
if (tValue > 0 && static_cast<uint64_t>(tValue) > static_cast<uint64_t>(std::numeric_limits<TTargetType>::max()))
throw CCompileException("Cannot cast to type, the value exceeds the maximum possible value of the target type.");
else if (tValue < 0 && static_cast<int64_t>(tValue) <
static_cast<int64_t>(std::numeric_limits<std::make_signed_t<TTargetType>>::min()))
throw CCompileException("Cannot cast to type, the value is below the minumum possible value of the signed version"
" of the target type.");
return static_cast<TTargetType>(tValue);
}
}
else if constexpr (std::is_same_v<TTargetType, bool> && std::is_arithmetic_v<TVariantType>)
return std::get<TVariantType>(m_varValue) != static_cast<TVariantType>(0) ? true : false;
else
{
// Conversion not possible
throw CCompileException("Cannot cast to target type, the types are incompatible.");
}
}
#endif // !defined(CONSTVARIANT_INL)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
#include "attribute_entity.h"
CAttributeEntity::CAttributeEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bReadOnly) :
CDeclarationEntity(rptrContext, ptrParent), m_bReadOnly(bReadOnly),
m_iteratorReadExceptions(GetReadExceptionVector()), m_iteratorWriteExceptions(GetWriteExceptionVector())
{}
sdv::interface_t CAttributeEntity::GetInterface(sdv::interface_id idInterface)
{
// Expose interfaces
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IAttributeEntity>())
return static_cast<sdv::idl::IAttributeEntity*>(this);
return CDeclarationEntity::GetInterface(idInterface);
}
sdv::idl::IEntityIterator* CAttributeEntity::GetReadExceptions()
{
if (!GetReadExceptionVector().empty()) return &m_iteratorReadExceptions;
return nullptr;
}
sdv::idl::IEntityIterator* CAttributeEntity::GetWriteExceptions()
{
if (!GetWriteExceptionVector().empty()) return &m_iteratorWriteExceptions;
return nullptr;
}
void CAttributeEntity::Process()
{
CDeclarationEntity::Process();
}
bool CAttributeEntity::SupportArrays() const
{
return false;
}
bool CAttributeEntity::IsReadOnly() const
{
return m_bReadOnly;
}
bool CAttributeEntity::SupportMultipleDeclarations() const
{
return true;
}
bool CAttributeEntity::SupportRaiseExceptions() const
{
return true;
}
bool CAttributeEntity::SupportSeparateSetGetRaiseExceptions() const
{
return true;
}

View File

@@ -0,0 +1,99 @@
#ifndef ATTRIBUTE_ENTITY_H
#define ATTRIBUTE_ENTITY_H
#include "declaration_entity.h"
/**
* @brief The attribute definition of an IDL file.
* @details The attribute section of the IDL file defines attribute values.
*/
class CAttributeEntity : public CDeclarationEntity, public sdv::idl::IAttributeEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] bReadOnly When set, the attribute is defined as readonly.
*/
CAttributeEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bReadOnly);
/**
* @brief Destructor
*/
virtual ~CAttributeEntity() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get the list of possible exceptions that might be fired during a read operation. Overload of
* sdv::idl::IAttributeEntity::GetReadExceptions.
* @return Returns a pointer to the exceptions iterator or NULL when no exceptions were defined.
*/
virtual sdv::idl::IEntityIterator* GetReadExceptions() override;
/**
* @brief Get the list of possible exceptions that might be fired during a write operation. Overload of
* sdv::idl::IAttributeEntity::GetWriteExceptions.
* @return Returns a pointer to the exceptions iterator or NULL when no exceptions were defined.
*/
virtual sdv::idl::IEntityIterator* GetWriteExceptions() override;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the attribute entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_attribute; }
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
protected:
/**
* @brief Does the entity support arrays? Overload of CDeclarationEntity::SupportArrays.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportArrays() const override;
/**
* @brief Is the entity readonly? Overload of IEntityInfo::IsReadOnly.
* @return Returns 'true' when the entity defined as readonly; 'false' otherwise.
*/
virtual bool IsReadOnly() const override;
/**
* @brief Does the entity support multiple declarations on one line of code? Overload of
* CDeclarationEntity::SupportMultipleDeclarations.
* @return Returns 'true' when the entity supports multiple declarations; 'false' otherwise.
*/
virtual bool SupportMultipleDeclarations() const override;
/**
* @brief Does the entity support raising exceptions? Overload of CDeclarationEntity::SupportRaiseExceptions.
* @return Returns 'true' when the entity supports exceptions; 'false' otherwise.
*/
virtual bool SupportRaiseExceptions() const override;
/**
* @brief Does the entity support separate set/get raising exceptions? Overload of
* CDeclarationEntity::SupportSeparateSetGetRaiseExceptions.
* @return Returns 'true' when the entity supports separate set/get raise exceptions; 'false' otherwise.
*/
virtual bool SupportSeparateSetGetRaiseExceptions() const override;
private:
bool m_bReadOnly = false; ///< When set, the attribute is readonly.
CEntityIterator m_iteratorReadExceptions; ///< Exceptions iterator for read exceptions
CEntityIterator m_iteratorWriteExceptions; ///< Exceptions iterator for write exceptions
};
#endif // !defined(ATTRIBUTE_ENTITY_H)

View File

@@ -0,0 +1,749 @@
#include "declaration_entity.h"
#include "../exception.h"
#include "../logger.h"
#include "../token.h"
#include "../tokenlist.h"
#include "../support.h"
#include "../lexer.h"
#include "../constvariant.inl"
#include "../parser.h"
#include "struct_entity.h"
#include "interface_entity.h"
#include "typedef_entity.h"
#include "variable_entity.h"
#include "attribute_entity.h"
#include "operation_entity.h"
#include "exception_entity.h"
#include "parameter_entity.h"
#include "enum_entity.h"
#include "union_entity.h"
#include <functional>
#include <type_traits>
#include <limits>
CDeclarationEntity::CDeclarationEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CEntity(rptrContext, ptrParent)
{}
CDeclarationEntity::CDeclarationEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent,
const CTokenList& rlstTokenList) :
CEntity(rptrContext, ptrParent, rlstTokenList)
{}
sdv::interface_t CDeclarationEntity::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IDeclarationEntity>())
return static_cast<sdv::idl::IDeclarationEntity*>(this);
return CEntity::GetInterface(idInterface);
}
sdv::IInterfaceAccess* CDeclarationEntity::GetDeclarationType() const
{
return const_cast<CTypeDeclaration*>(&m_typedecl);
}
CEntityPtr CDeclarationEntity::GetTypeEntity() const
{
return m_typedecl.GetTypeDefinitionEntityPtr();
}
bool CDeclarationEntity::HasArray() const
{
return m_vecMultiArraySizeTokenList.size() ? true : false;
}
sdv::sequence<sdv::idl::SArrayDimension> CDeclarationEntity::GetArrayDimensions() const
{
sdv::sequence<sdv::idl::SArrayDimension> seqArrayDimensions;
if (!HasArray()) return seqArrayDimensions;
// Traverse through each array entry
std::function<void(const CValueNodePtr& ptrValue)> fnCollectArrayDimensions =
[&](const CValueNodePtr& ptrValue)
{
// Is the value node an array at all?
CArrayValueNode* pArrayValue = ptrValue->Get<CArrayValueNode>();
if (!pArrayValue) return;
// Fill the array dimension struct
sdv::idl::SArrayDimension sArrayDimension{};
sArrayDimension.eType = sdv::idl::SArrayDimension::EDimensionType::bound;
if (pArrayValue->IsUnbound())
sArrayDimension.eType = sdv::idl::SArrayDimension::EDimensionType::unbound;
sArrayDimension.ssExpression = MakeFullScoped(pArrayValue->GetSizeExpression());
// Store in sequence
seqArrayDimensions.push_back(sArrayDimension);
// Process the next array dimension
if (pArrayValue->GetSize() != 0)
fnCollectArrayDimensions((*pArrayValue)[0]);
};
// Collect the array dimensions.
fnCollectArrayDimensions(ValueRef());
return seqArrayDimensions;
}
bool CDeclarationEntity::HasAssignment() const
{
return !m_lstAssignmentTokenList.empty();
}
sdv::u8string CDeclarationEntity::GetAssignment() const
{
std::stringstream sstreamAssignment;
bool bInitial = true;
for (const CToken& rToken : m_lstAssignmentTokenList)
{
if (!bInitial) sstreamAssignment << " ";
bInitial = false;
sstreamAssignment << static_cast<std::string>(rToken);
}
return sstreamAssignment.str();
}
void CDeclarationEntity::Process()
{
CLog log("Processing declaration (preparation)...");
// Determine whether the comments are preceding the token (either on the same line or the line before).
CTokenList lstPreComments = GetPreCommentTokenList();
if (!lstPreComments.empty()) SetCommentTokens(lstPreComments);
// Process the type
CTypeDeclaration sTypeDecl = ProcessType();
// Check for the support of interface and void types
if (sTypeDecl.GetBaseType() == sdv::idl::EDeclType::decltype_interface && !SupportInterface())
throw CCompileException("The declaration of interfaces is not supported.");
if (sTypeDecl.GetBaseType() == sdv::idl::EDeclType::decltype_void && !SupportVoid())
throw CCompileException("The use of 'void' as type is not supported.");
// Preprocess potential array declaration (only for operations).
if (GetType() == sdv::idl::EEntityType::type_operation)
PreprocessArrayDeclaration();
// Process the declaration
ProcessDeclaration(sTypeDecl);
}
void CDeclarationEntity::ProcessDeclaration(const CTypeDeclaration& rTypeDecl)
{
// Store the type
m_typedecl = rTypeDecl;
CLog log("Processing declaration...");
// Expecting an identifier.
CToken token = GetToken();
if (token.GetType() == ETokenType::token_keyword)
{
// Keywords as names are allowed if the extension is enabled.
if (!GetParserRef().GetEnvironment().ContextDependentNamesExtension())
throw CCompileException(token, "The identifier cannot be a reserved keyword.");
}
else if (token.GetType() != ETokenType::token_identifier)
throw CCompileException(token, "Expecting an identifier.");
SetName(token);
log << "Declaration name '" << GetName() << "'..." << std::endl;
// Preprocess potential array declaration (not for operations).
if (GetType() != sdv::idl::EEntityType::type_operation)
PreprocessArrayDeclaration();
// Further processing...
token = GetToken();
// Requires parameters?
if (RequiresParameters())
{
// Expect a left bracket
if (token != "(") throw CCompileException(token, "Expected left bracket '('.");
log << "Reading parameter list..." << std::endl;
PreprocessTokenListVector(m_vecParametersTokenList);
// Expect a right bracket
token = GetToken();
if (token != ")")
throw CCompileException(token, "Expected right bracket ')'.");
// Get the next token...
token = GetToken();
// Check for the 'const' keyword. If set, the operation is defined as const-operation.
if (token == "const")
{
SetOperationAsConst();
token = GetToken();
}
}
// Supports exceptions
if (SupportRaiseExceptions())
{
while (true)
{
// Check for the 'raises' keyword.
enum class EExceptType {raises, getraises, setraises, none} eExceptType = EExceptType::none;
if (token == "raises")
eExceptType = EExceptType::raises;
else if (token == "getraises")
eExceptType = EExceptType::getraises;
else if (token == "setraises")
eExceptType = EExceptType::setraises;
if (eExceptType == EExceptType::none)
break;
// Check for validity
if (!SupportSeparateSetGetRaiseExceptions() && eExceptType == EExceptType::getraises)
throw CCompileException(token,
"Cannot set a separate 'get-raises' exception list; use the 'raises' keyword instead.");
if (!SupportSeparateSetGetRaiseExceptions() && eExceptType == EExceptType::setraises)
throw CCompileException(token,
"Cannot set a separate 'set-raises' exception list; use the 'raises' keyword instead.");
if (SupportSeparateSetGetRaiseExceptions() && eExceptType == EExceptType::raises && IsReadOnly())
eExceptType = EExceptType::getraises;
if (eExceptType == EExceptType::setraises && IsReadOnly())
throw CCompileException(token, "Cannot set a set-raises exception list for a readonly type.");
if ((eExceptType == EExceptType::raises) && !m_vecRaisesExceptionsTokenList.empty())
throw CCompileException(token, "Multiple definitions of 'raises' exceptions are not allowed.");
if ((eExceptType == EExceptType::setraises) && !m_vecSetRaisesExceptionsTokenList.empty())
throw CCompileException(token, "Multiple definitions of 'set-raises' exceptions are not allowed.");
if ((eExceptType == EExceptType::getraises) && !m_vecGetRaisesExceptionsTokenList.empty())
throw CCompileException(token, "Multiple definitions of 'get-raises' exceptions are not allowed.");
// Expect a left bracket
token = GetToken();
if (token != "(") throw CCompileException(token, "Expected left bracket '('.");
// Processes raises exception list.
switch (eExceptType)
{
case EExceptType::raises:
log << "Reading 'raises' exception list..." << std::endl;
PreprocessTokenListVector(m_vecRaisesExceptionsTokenList);
if (m_vecRaisesExceptionsTokenList.empty()) throw CCompileException(token, "Missing exception types.");
break;
case EExceptType::setraises:
log << "Reading 'setraises' exception list..." << std::endl;
PreprocessTokenListVector(m_vecSetRaisesExceptionsTokenList);
if (m_vecSetRaisesExceptionsTokenList.empty()) throw CCompileException(token, "Missing exception types.");
break;
case EExceptType::getraises:
log << "Reading 'getraises' exception list..." << std::endl;
PreprocessTokenListVector(m_vecGetRaisesExceptionsTokenList);
if (m_vecGetRaisesExceptionsTokenList.empty()) throw CCompileException(token, "Missing exception types.");
break;
default:
break;
}
// Expect a right bracket
token = GetToken();
if (token != ")")
throw CCompileException(token, "Expected right bracket ')'.");
// Get the next token...
token = GetToken();
}
}
// Is there an assignment?
if (token == "=")
{
if (!SupportAssignments())
throw CCompileException(token, "Assignment operator detected, but type doesn't support assignments.");
log << "Declaration assignment detected; storing expression for later processing..." << std::endl;
// Read the tokens for the assignment expression. Read until ';' or ','; the latter not within an expression
// sub-statement.
size_t nDepth = 0;
log << "Assignment expression:" << std::endl;
while(true)
{
token = GetToken();
if (!token)
throw CCompileException("Unexpected end of file found; missing ';'.");
if (token == ";" || ((token == "," || token == "}") && !nDepth))
break;
log << " " << static_cast<std::string>(token);
if (token == "{") nDepth++;
if (token == "}") nDepth--;
m_lstAssignmentTokenList.push_back(token);
}
log << std::endl;
} else
{
// Does the entity need an assignment?
if (RequiresAssignment())
throw CCompileException(token, "Expecting an assignment operator.");
}
// Assign any succeeding comments
ProcessPostCommentTokenList(token.GetLine());
// Another declaration?
if (token == ",")
{
if (!SupportMultipleDeclarations())
throw CCompileException(token, "Multiple declarations on a single line of code is not supported for this type.");
log << "Another declaration of the same type..." << std::endl;
// Peek for ending the definition
if (DoNotEnfoceNextDeclarationAfterComma() && PeekToken() == "}") return;
// Create another declaration
CDeclarationEntity* pTypeEntity = nullptr;
if (Get<CVariableEntity>()) pTypeEntity = CreateChild<CVariableEntity>(token.GetContext(), GetParentEntity().get(), IsReadOnly(), false)->Get<CDeclarationEntity>();
else if (Get<CEnumEntry>()) pTypeEntity = CreateChild<CEnumEntry>(token.GetContext(), GetParentEntity().get())->Get<CDeclarationEntity>();
else if (Get<CAttributeEntity>()) pTypeEntity = CreateChild<CAttributeEntity>(token.GetContext(), GetParentEntity().get(), IsReadOnly())->Get<CDeclarationEntity>();
else
throw CCompileException(token, "Unexpected token ','.");
if (!pTypeEntity) throw CCompileException(token, "Internal error: failed to create another declaration entity.");
// Set the new begin position of the declaration
CToken tokenTemp = PeekToken();
pTypeEntity->SetBeginPosition(tokenTemp.GetLine(), tokenTemp.GetCol());
// Use the same type for the processing.
pTypeEntity->ProcessDeclaration(m_typedecl);
// Done.
return;
}
// Set the end position of the declaration
SetEndPosition(token.GetLine(), token.GetCol());
// Reinsert the token
PrependToken(token);
}
void CDeclarationEntity::PreprocessArrayDeclaration()
{
CLog log("Checking for array...");
// For each array dimension, add a tokenlist to the m_vecMultiArraySizeTokenList variable.
bool bIsArray = false;
while (true)
{
// Check for an array
CToken token = GetToken();
if (token != "[")
{
PrependToken(token);
break;
}
if (!SupportArrays())
throw CCompileException(token, "Unexpected token '['.");
log << "Array detected; storing expression for later processing..." << std::endl;
log << "Array expression: [";
// Check for multidimensional arrays
if (bIsArray && !GetParserRef().GetEnvironment().MultiDimArrayExtension())
throw CCompileException(token, "Multi-dimentsional arrays are not allowed. Unexpected token '['.");
bIsArray = true;
// Read the tokens for the array size. Read until ']'.
CTokenList lstArraySize;
size_t nDepth = 1;
while (true)
{
token = GetToken();
log << " " << static_cast<std::string>(token);
if (!token)
throw CCompileException("Unexpected end of file found; missing ']'.");
if (token == ";")
throw CCompileException("Unexpected end of declaration; missing ']'.");
if (token == "[")
nDepth++;
if (token == "]")
{
nDepth--;
if (!nDepth)
{
m_vecMultiArraySizeTokenList.push_back(std::move(lstArraySize));
break;
}
}
lstArraySize.push_back(token);
}
log << std::endl;
}
}
void CDeclarationEntity::PreprocessTokenListVector(std::vector<CTokenList>& rvecTokenList)
{
CLog log("Checking for comma separated token lists...");
CTokenList lstTokens;
bool bInitial = true;
while (true)
{
// Check for array index (allowed in certain situations)
CToken token = GetToken();
if (token == "[")
{
do
{
lstTokens.push_back(token);
token = GetToken();
} while (token && token != "]");
lstTokens.push_back(token);
token = GetToken();
}
// Check for template parameters
if (token == "<")
{
size_t nDepth = 0;
do
{
if (token == "<") nDepth++;
if (token == ">>") // Special case when closing nested templates.
{
token = CToken(">", ETokenType::token_operator);
PrependToken(token);
}
if (token == ">") nDepth--;
lstTokens.push_back(token);
token = GetToken();
} while (token && static_cast<bool>(nDepth));
lstTokens.push_back(token);
token = GetToken();
}
// Check for end of processing
if (!token || token == "]" || token == ")" || token == ";")
{
if (!bInitial)
rvecTokenList.push_back(std::move(lstTokens));
PrependToken(token);
break;
}
if (bInitial)
log << "Comma separated list detected: ";
bInitial = false;
log << " " << static_cast<std::string>(token);
// Check for comma separator
if (token == ",")
{
rvecTokenList.push_back(std::move(lstTokens));
continue;
}
// Add the token to the token list
lstTokens.push_back(token);
}
if (rvecTokenList.empty())
log << "No comma separated list detected..." << std::endl;
else
log << std::endl;
}
void CDeclarationEntity::PostProcess()
{
CLog log("Post process declaration...");
// Check the assignment processing progression state...
switch (m_eProcAssState)
{
case EProcessAssignmentProgression::unprocessed:
// Not processed yet, start processing...
m_eProcAssState = EProcessAssignmentProgression::currently_processing;
break;
case EProcessAssignmentProgression::currently_processing:
log << "Post processing declaration assignment takes place already; cannot do this more than once at the same time..."
<< std::endl;
// Alarm, circular references... cannot continue.
throw CCompileException("Circular referencing entity.");
case EProcessAssignmentProgression::processed:
default:
// Already done...
log << "Post processing declaration was done before; no need to repeat..." << std::endl;
return;
}
// The parent value is the value of the parent entity, if there is any value.
CValueNodePtr ptrValueParent = GetParentEntity() ? GetParentEntity()->ValueRef() : nullptr;
if (ptrValueParent)
log << "The parent entity '" << GetParentEntity()->GetName() << "' has a value node..." << std::endl;
else
log << "No parent entity or no value node assigned to the parent entity..." << std::endl;
// Create a value at each bottom leaf of a multi-dimensional array.
std::function<void(CValueNodePtr&, const CValueNodePtr, std::function<CValueNodePtr(const CValueNodePtr)>)> fnCreateAtBottomLeaf =
[&](CValueNodePtr& rptrValue, const CValueNodePtr& rptrValueParent, std::function<CValueNodePtr(const CValueNodePtr)> fnCreate)
{
// Check for an array value
CArrayValueNode* psArrayValue = dynamic_cast<CArrayValueNode*>(rptrValue.get());
// If there is an array value, call the function once more for each leaf.
if (psArrayValue)
{
for (size_t nIndex = 0; nIndex < psArrayValue->GetSize(); nIndex++)
fnCreateAtBottomLeaf((*psArrayValue)[nIndex], rptrValue, fnCreate);
return;
}
// Create a new element...
rptrValue = fnCreate(rptrValueParent);
};
// Process parameters - since parameters might depend on other parameters, do the processing in two steps.
size_t nIndex = 1;
for (const CTokenList& rlstTokenList : m_vecParametersTokenList)
{
log << "Processing parameter #" << nIndex++ << std::endl;
CEntityPtr ptrEntity = std::make_shared<CParameterEntity>(GetContext(), shared_from_this(), rlstTokenList);
ptrEntity->Process();
m_vecParameters.push_back(ptrEntity);
}
for (CEntityPtr& rptrParameter : m_vecParameters)
rptrParameter->Get<CParameterEntity>()->PostProcess();
// Build array tree
if (m_vecMultiArraySizeTokenList.empty()) log << "No array processing needed..." << std::endl;
else if (m_vecMultiArraySizeTokenList.size() == 1) log << "Single-dimensional array processing needed..." << std::endl;
else log << "Multi-dimensional array processing needed..." << std::endl;
for (const CTokenList& rlstArrayExpression : m_vecMultiArraySizeTokenList)
{
std::pair<CConstVariant, bool> prArraySize = {0, false};
log << "Start processing array dimension..." << std::endl;
// Empty expression indicates retrieving the size from the assignment.
if (!rlstArrayExpression.empty())
{
log << "Calculate the array size..." << std::endl;
prArraySize = ProcessNumericExpression(rlstArrayExpression);
// Is the array size dynamic?
if (prArraySize.second)
throw CCompileException(*rlstArrayExpression.begin(), "Cannot use non-const variable for the array size.");
log << "The array has " << prArraySize.first.Get<uint32_t>() << " elements..." << std::endl;
// Check whether the size is integral
if (!prArraySize.first.IsIntegral())
throw CCompileException(*rlstArrayExpression.begin(), "Only integral data types are supported for the array size.");
if ((prArraySize.first < CConstVariant(0)).Get<bool>())
throw CCompileException(*rlstArrayExpression.begin(), "Invalid array size.");
} else
{
log << "Array is defined as unbound array retrieving the size from the variable assignment..." << std::endl;
// Unbound arrays are not possible for writable variables. Exception are operations, attributes and parameters of local
// interfaces.
bool bError = true;
switch (GetType())
{
case sdv::idl::EEntityType::type_typedef:
bError = false;
break;
case sdv::idl::EEntityType::type_variable:
// When not read-only, this is an error.
if (IsReadOnly()) bError = false;
break;
default:
break;
}
if (bError)
throw CCompileException("Retrieving the size of the array through its assignment is"
" only possible with const declarations and typedefs.");
}
// The array creation function
auto fnCreateArray = [&, this](const CValueNodePtr& rptrValueParent) -> CValueNodePtr
{
std::shared_ptr<CArrayValueNode> ptrArrayValue = std::make_shared<CArrayValueNode>(shared_from_this(), rptrValueParent);
if (rlstArrayExpression.empty())
ptrArrayValue->SetFixedSizeUnbound();
else if (prArraySize.second)
ptrArrayValue->SetDynamicSize(prArraySize.first.Get<size_t>(), rlstArrayExpression);
else
ptrArrayValue->SetFixedSize(prArraySize.first.Get<size_t>(), rlstArrayExpression);
return ptrArrayValue;
};
// Create the array value at the bottom leaf.
fnCreateAtBottomLeaf(ValueRef(), ptrValueParent, fnCreateArray);
log << "Finalized processing array dimension..." << std::endl;
}
// Add the values of the type.
if (m_vecMultiArraySizeTokenList.size() == 0)
log << "Copy the existing type value tree or create a declaration value node for this assignment..." << std::endl;
else
log << "For each array element, copy the existing type value tree or create a"
" declaration value node for this assignment..." << std::endl;
auto fnCreateTypeValues = [&, this](const CValueNodePtr& rptrValueParent) -> CValueNodePtr
{
if (m_typedecl.GetTypeDefinitionEntityPtr())
{
// In case the original type was not processed yet, do so now.
CDeclarationEntity* pOriginalType = m_typedecl.GetTypeDefinitionEntityPtr()->Get<CDeclarationEntity>();
if (pOriginalType) pOriginalType->PostProcess();
// Copy the existing entity of the type... this contains all the default assignments already...
log << "Copy type value tree..." << std::endl;
CValueNodePtr ptrValue = m_typedecl.GetTypeDefinitionEntityPtr()->ValueRef();
if (ptrValue)
return ptrValue->CreateCopy(shared_from_this(), rptrValueParent);
else
{
if (pOriginalType)
throw CCompileException("Internal error: value tree must be available for '", GetName(), "'.");
log << "No value tree present; nothing to copy..." << std::endl;
return nullptr;
}
}
else
{
log << "Create declaration value node..." << std::endl;
return std::make_shared<CSimpleTypeValueNode>(shared_from_this(), ptrValueParent);
}
};
fnCreateAtBottomLeaf(ValueRef(), ptrValueParent, fnCreateTypeValues);
// If this is a variable declaration, add the value as part of the parent tree
if (CanSupportComplexTypeAssignments() && ptrValueParent)
{
log << "The entity value tree is part of the value tree of the parent node..." << std::endl;
ptrValueParent->AddChild(ValueRef());
}
// Add the assignment.
if (!m_lstAssignmentTokenList.empty())
{
log << "Assignment was available, process the assignment..." << std::endl;
if (!SupportAssignments())
throw CCompileException(*m_lstAssignmentTokenList.begin(), "Type definitions cannot be assigned any values.");
ValueRef()->ProcessValueAssignment(m_lstAssignmentTokenList);
} else
{
// Does the entity need an assignment?
if (RequiresAssignment())
throw CCompileException("Expecting an assignment operator for '", GetName(), "'.");
}
// Build raises exception lists
for (const CTokenList& rlstTokenList : m_vecRaisesExceptionsTokenList)
{
log << "Processing raising exception..." << std::endl;
std::pair<std::string, CEntityPtr> prBase = ProcessScopedName(rlstTokenList);
if (prBase.first.empty() || !prBase.second || !prBase.second->Get<CExceptionEntity>())
throw CCompileException("Exception definition not found.");
if (SupportSeparateSetGetRaiseExceptions())
{
m_vecGetRaisesExceptions.push_back(prBase.second);
m_vecSetRaisesExceptions.push_back(prBase.second);
} else
m_vecRaisesExceptions.push_back(prBase.second);
log << "Entity could raise exception on operation/attribute: " << prBase.first << std::endl;
}
for (const CTokenList& rlstTokenList : m_vecSetRaisesExceptionsTokenList)
{
log << "Processing set-raising exception..." << std::endl;
std::pair<std::string, CEntityPtr> prBase = ProcessScopedName(rlstTokenList);
if (prBase.first.empty() || !prBase.second || !prBase.second->Get<CExceptionEntity>())
throw CCompileException("Exception definition not found.");
m_vecSetRaisesExceptions.push_back(prBase.second);
log << "Entity could raise exception on set attribute: " << prBase.first << std::endl;
}
for (const CTokenList& rlstTokenList : m_vecGetRaisesExceptionsTokenList)
{
log << "Processing get-raising exception..." << std::endl;
std::pair<std::string, CEntityPtr> prBase = ProcessScopedName(rlstTokenList);
if (prBase.first.empty() || !prBase.second || !prBase.second->Get<CExceptionEntity>())
throw CCompileException("Exception definition not found.");
m_vecGetRaisesExceptions.push_back(prBase.second);
log << "Entity could raise exception on get attribute: " << prBase.first << std::endl;
}
// Processing is finalized...
m_eProcAssState = EProcessAssignmentProgression::processed;
}
bool CDeclarationEntity::RequiresAssignment() const
{
if (!ValueRef()) return false; // No value assigned yet...
// If the type has an unbound array in its value, it requires assignment to determine the size of the type.
CValueNodePtr ptrValue = ValueRef();
while (ptrValue)
{
if (ptrValue->IsArray() && ptrValue->IsUnbound())
return true;
ptrValue = ptrValue->GetParentNode();
}
return false;
}
void CDeclarationEntity::CalcHash(CHashObject& rHash) const
{
// Add the type
if (m_typedecl.GetTypeDefinitionEntityPtr())
m_typedecl.GetTypeDefinitionEntityPtr()->CalcHash(rHash);
else
rHash << m_typedecl.GetTypeString();
// Add base entity
CEntity::CalcHash(rHash);
// Add array dimensions
sdv::sequence<sdv::idl::SArrayDimension> seqArray = GetArrayDimensions();
for (const sdv::idl::SArrayDimension& rsDimentation : seqArray)
{
rHash << "[";
if (!rsDimentation.ssExpression.empty())
rHash << rsDimentation.ssExpression;
rHash << "]";
}
// Get the assignment
std::string ssAssignment = GetAssignment();
if (!ssAssignment.empty()) rHash << ssAssignment;
// Add parameters
for (const CEntityPtr& rptrEntity : m_vecParameters)
rptrEntity->CalcHash(rHash);
// Add exceptions
for (const CEntityPtr& rptrEntity : m_vecRaisesExceptions)
{
rHash << "raises";
rptrEntity->CalcHash(rHash);
}
// Add get-exceptions
for (const CEntityPtr& rptrEntity : m_vecGetRaisesExceptions)
{
rHash << "get_raises";
rptrEntity->CalcHash(rHash);
}
// Add set-exceptions
for (const CEntityPtr& rptrEntity : m_vecSetRaisesExceptions)
{
rHash << "set_raises";
rptrEntity->CalcHash(rHash);
}
// Add whether it is readonly
if (IsReadOnly())
rHash << "const";
}

View File

@@ -0,0 +1,372 @@
#ifndef BASIC_TYPE_ENTITY_H
#define BASIC_TYPE_ENTITY_H
#include "entity_base.h"
#include "entity_value.h"
#include "../constvariant.h"
#include <vector>
/**
* @brief The base for declaration entity definitions within an IDL file (declarations, typedefs, attributes, operations,
* parameters, case declarations, enum entries).
* @details The declaration entity definitions all have a similar setup with small differences in detail. Consider the following
* structures:
* @code
* <type> <name> = <value>
* const <type> <name> = <value>
* readonly attribute <name>
* <operation_type> <operation_name>(<parameter_type> <parameter_name>) const
* @endcode
* The following generalized structure applies to all declaration structures:
* @code
* prefix type name
* prefix type name = value
* prefix type name(parameters) postfix
* @endcode
* The first statement is a declaration. The second statement is a declaration with an assignment. The last statement represents
* an operation.
* The prefix is used to provide a specific interpretation to the declaration (in, out, inout, const, attribute, readonly,
* typedef, struct, union, enum).
* The type defines the type the declaration represents (either a system type or a typedefed type - scoped name).
* The name is the defined name o the declaration.
* The value is the expression used for the assignment.
* The parameters are a list of zero or more declaration statements.
* The postfix is used to provide a specific interpretation to the declaration (const).
* Some declarations might start as a complex type (e.g. struct, union, enum). The might contain the type definition as well as
* the declaration. For example:
* @code
* struct <def_name> { <definition> } <decl_name>
* struct { <definition> } <decl_name>
* @endcode
* In the first statement, the struct is defined and declared in one statement. In the second statement, an anonymous struct is
* defined and declared in one statement.
* Multiple declarations are possible for many types. The declarations are separated by a comma and follow the same rules as a
* single declaration starting at the &lt;name&gt;.
* @code
* <type> <name_1> = <value_1>, <name_2> = <value_2>, <name_3>, <name_4> = <value_4>
* @endcode
*/
class CDeclarationEntity : public CEntity, public sdv::idl::IDeclarationEntity
{
friend CSimpleTypeValueNode;
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CDeclarationEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Constructor using the provided token-list to process the code.
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] rlstTokenList Reference to the token list holding the tokens to process.
*/
CDeclarationEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CTokenList& rlstTokenList);
/**
* @brief Destructor
*/
virtual ~CDeclarationEntity() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get declaration type. Overload of sdv::idl::IDeclarationEntity::GetDeclarationType.
* @return Interface to the declaration type object.
*/
virtual sdv::IInterfaceAccess* GetDeclarationType() const override;
/**
* @brief Is array? Overload of sdv::idl::IDeclarationEntity::HasArray/HasDynamicArray.
* @return Retrurns whether the declaration reqpresents an array.
*/
virtual bool HasArray() const override;
/**
* @brief Get the array dimensions (if there are any). Overload of IDeclarationEntity::GetDimensionCount.
* @return Smart pointer to the sequence of array dimensions.
*/
virtual sdv::sequence<sdv::idl::SArrayDimension> GetArrayDimensions() const override;
/**
* @brief Has assignment? Overload of sdv::idl::IDeclarationEntity::HasAssignment.
* @return Returns whether the declaration has an assignment.
*/
virtual bool HasAssignment() const override;
/**
* @brief Get assignment string. Overload of sdv::idl::IDeclarationEntity::GetAssignment.
* @details The assignment can be an algebraic expression composed from constants and variables. If the assignment is an array,
* the expression is composed like this: {{expr1},{expr2},{expr...}}
* @return On success, returns the assignment string object or an empty string when no assignment is available.
*/
virtual sdv::u8string GetAssignment() const override;
/**
* @brief Get the base type of the entity.
* @return Returns the base type.
*/
virtual sdv::idl::EDeclType GetBaseType() const { return m_typedecl.GetBaseType(); }
/**
* @brief Get the type entity if the type is not a system type.
* @details Complex types (struct, enum, union) and type definitions are based on a type entity.
* @return Returns a pointer to the type entity if available.
*/
CEntityPtr GetTypeEntity() const;
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Process a declaration.
* @details The processing of a declaration is done through several steps: processing the declaration type, processing the
* declaration identifier, processing (multi-dimensional) arrays, processing assignments, processing the next declaration
* identifier and so on.
* The following statement are examples:
* @code
* <type> <identifier>; // No assignment, one declaration
* <type> <identifier>, <identifier>; // No assignment, multiple declarations
* <type> <identifier>=<expression>; // One definition (declaration with assignment)
* <type> <identifier>=<expressoin>, <identifier>=<expression>; // Multiple definitions
* <type> <identifier>=<expression>, <identifier>; // Mixed expression, definition
* <type> <identifier>[]={<expression>, <expression>}; // Array definition
* <type> <identifier>[<expression>]; // Array declaration
* <type> <identifier>[<expression>]={<expression>, <expression>}; // Array definition
* struct <type> <identifier>; // Struct declaration with explicit struct keyword
* struct <type> <identifier>={<expression>, <expression>}; // Struct declaration with assignment
* union <type> <identifier>; // Union declaration with explicit union keyword
* enum <type> <identifier>; // Enum declaration with explicit enum keyword
* enum <type> <identifier>=<exppression>; // Enum definition with explicit enum keyword
* @endcode
* The 'type' can be a system type (short, int64, float) or a scoped type definition (MyType, \::MyModule\::MyType). Without
* assignment the type can be 'struct' or 'union', with or without the 'struct' and 'union' type identification (not allowed
* for const entities, which need an assignment).
* The 'identifier' is the unique name for the declaration. This name must be case-insensitive unique within the current
* scope. The identifier can be followed by square-brackets indicating an array. The size of the array can either be retrieved
* through the assignment or through the expression. If both an assignment and an expression are available, they must match.
* The array expression must be of an integral type and is not allowed to become negative. Furthermore, it can be created
* through a mathematical expression existing of constants and/or when not defined to be a constant entity, through
* declarations made before (this deviates to C/C++, where dynamic arrays are not allowed). In case of a dynamic array, an
* assignment is not supported.
* The assignment 'expression' defines a mathematical expression existing of constants and/or when not defined to be a
* constant entity, through declarations made before.
* @remarks Array size expressions and assignment expressions are stored as tokenlists to be processed by the
* ProcessValueAssignment function.
* @param[in] rTypeDecl Reference to the type identifier (can be a system type as well as a scoped name to a previously
* defined type).
*/
void ProcessDeclaration(const CTypeDeclaration& rTypeDecl);
/**
* @brief Preprocess the multi-dimensional array declaration.
* @details Preprocess the potential multi-dimensional array declaration by detecting square brackets and storing the tokens
* between the brackets. The tokens for each dimension are placed in the m_vecMultiArraySizeTokenList vector.
*/
void PreprocessArrayDeclaration();
/**
* @brief Preprocess a list of comma separated declarations.
* @details Preprocess a list of tokens separated by the comma ',' separator and place the tokens in the provided vector. The
* processing is continued until a square bracket ']' or normal bracket ')' or no token exists any more.
* @param[in] rvecTokenList Reference to the vector containing the token lists to be filled.
*/
void PreprocessTokenListVector(std::vector<CTokenList>& rvecTokenList);
/**
* @brief Postprocess the token lists that where read in the preprocess functions.
* @pre SupportAssignments returns true and at least m_vecMultiArraySizeTokenList or m_lstAssignmentTokenList is filled.
* @details For typedef, const and declaration entities, create the value structure containing the arrays and the values of
* the type entity. For const and declaration entities, fill the value structure using the assignment stored in the assignment
* expression token list. For attributes and operations build the raising exception lists. For the operations process the
* parameter list.
*/
void PostProcess();
/**
* @brief Does the entity support assignments (const and variable declarations do, others don't)?
* @details Determines whether the entity supports assignments. Default value is 'false'.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportAssignments() const { return false; }
/**
* @brief Does the entity require an assignment (const declarations do)?
* @details Determines whether the entity requires an assignment. Default value is is based on the presence of an unbound
* value in the type.
* @return Returns 'true' when the entity requires an assignment; 'false' otherwise.
*/
virtual bool RequiresAssignment() const;
/**
* @brief Can the entity be used for assignments of complex types (variable declarations do)?
* @details Returns whether the entity is defined to be usable for complex type assignments. Default value is 'false'.
* @return Returns 'true' when the entity defined as declaration; 'false' otherwise.
*/
virtual bool CanSupportComplexTypeAssignments() const { return false; }
/**
* @brief Does the entity support arrays (const and variable declarations, as well as typedefs and attributes do)?
* @details Determines whether the entity supports arrays. Default value is 'false'.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportArrays() const { return false; }
/**
* @brief Is the entity readonly (variable declarations and writable attributes aren't)? Overload of
* IDeclarationEntity::IsReadOnly.
* @details Returns whether the entity is readonly by design or whether it is defined readonly by the code. Default value is
* 'true'.
* @return Returns 'true' when the entity defined as readonly; 'false' otherwise.
*/
virtual bool IsReadOnly() const override { return true; }
/**
* @brief Is the entity transparent when used in a struct? Overload of IDeclarationEntity::IsAnonymous.
* @details Returns whether the entity is anonymous when used in a struct/union (unnamed and not declared). This allows its
* members to appear directly as members within the struct. Default value is 'false'.
* @return Returns 'true' when the entity defined as anonymous; 'false' otherwise.
*/
virtual bool IsAnonymous() const override { return false; }
/**
* @brief Does the entity support multiple declarations on one line of code (const and var declarations and attributes do)?
* @details Returns whether the entity supports multiple declarations separated by a comma ','. Default value is 'false'.
* @return Returns 'true' when the entity supports multiple declarations; 'false' otherwise.
*/
virtual bool SupportMultipleDeclarations() const { return false; }
/**
* @brief Do not enforce next declaration after comma (enums do)?
* @pre SupportMultipleDeclarations needs to be supported.
* @details Returns whether the entity supports ending the definition after a comma ','. Default value is 'false'.
* @return Returns 'true' when not enforcing the next declaration; 'false' otherwise.
*/
virtual bool DoNotEnfoceNextDeclarationAfterComma() const { return false; }
/**
* @brief Does the entity support raising exceptions (attributes and operations do)?
* @details Returns whether the entity supports exceptions (defined through the keywords: raises, getraises and setraises).
* Default value is 'false'.
* @return Returns 'true' when the entity supports exceptions; 'false' otherwise.
*/
virtual bool SupportRaiseExceptions() const { return false; }
/**
* @brief Does the entity support separate set/get raising exceptions (only attributes do)?
* @details Returns whether the entity supports exceptions (defined through the keywords: getraises and setraises).
* Default value is 'false'.
* @return Returns 'true' when the entity supports separate set/get raise exceptions; 'false' otherwise.
*/
virtual bool SupportSeparateSetGetRaiseExceptions() const { return false; }
/**
* @brief Does the entity support an interface as base type (non-const variables, operations and parameters do)?
* @details Returns whether the entity supports the an interface as base type base type. Default value is 'false'.
* @return Returns 'true' when the entity supports interfaces as base type; 'false' otherwise.
*/
virtual bool SupportInterface() const { return false; }
/**
* @brief Does the entity support 'void' as base type (operations do)?
* @details Returns whether the entity supports the 'void' base type. Default value is 'false'.
* @return Returns 'true' when the entity supports void as base type; 'false' otherwise.
*/
virtual bool SupportVoid() const { return false; }
/**
* @brief Does the entity require parameters (operations do)?
* @details Returns whether the entity requires parameters. Default value is 'false'.
* @return Returns 'true' when the entity requires parameters; 'false' otherwise.
*/
virtual bool RequiresParameters() const { return false; }
/**
* @brief Set operation as const (operations only).
* @details If the declaration requires parameters, the declaration is checked for being defined as const operation. If so,
* this function is called. Default implementation doesn't do anything.
* @pre Only called when RequiresParameters is true and the declaration is defined as const.
*/
virtual void SetOperationAsConst() {}
/**
* @brief Calculate the hash of this entity and all encapsulated entities. Overload of CBaseEntity::CalcHash.
* @param[in, out] rHash Hash object to be filled with data.
*/
virtual void CalcHash(CHashObject& rHash) const override;
protected:
/**
* @brief Get parameter vector.
* @return Returns the vector with the parameter entities.
*/
CEntityVector& GetParamVector() { return m_vecParameters; }
/**
* @brief Get parameter vector.
* @return Returns the vector with the parameter entities.
*/
const CEntityVector& GetParamVector() const { return m_vecParameters; }
/**
* @brief Get "raises" exceptions vector.
* @return Returns the vector with the exception entities.
*/
CEntityVector& GetExceptionVector() { return m_vecRaisesExceptions; }
/**
* @brief Get "get_raises" exceptions vector.
* @return Returns the vector with the exception entities.
*/
CEntityVector& GetReadExceptionVector() { return m_vecGetRaisesExceptions; }
/**
* @brief Get "set_raises" exceptions vector.
* @return Returns the vector with the exception entities.
*/
CEntityVector& GetWriteExceptionVector() { return m_vecSetRaisesExceptions; }
private:
/**
* @brief Process assignment state.
* @details The assignment processing progression state which is used to control the processing of assignments as well as to
* prevent circular use of assignments.
*/
enum class EProcessAssignmentProgression
{
unprocessed, ///< Assignment hasn't been processed yet
currently_processing, ///< Processing currently takes place
processed, ///< Processing has been done
};
CTypeDeclaration m_typedecl; ///< The type definition of this declaration.
std::vector<CTokenList> m_vecMultiArraySizeTokenList; ///< The list of tokens for each array dimension to be
///< calculated during the post processing phase.
CTokenList m_lstAssignmentTokenList; ///< The list of tokens forming the assignment.
std::vector<CTokenList> m_vecRaisesExceptionsTokenList; ///< The list of tokens for each exception to be parsed
///< during the post processing phase.
std::vector<CTokenList> m_vecSetRaisesExceptionsTokenList; ///< The list of tokens for each exception to be parsed
///< during the post processing phase.
std::vector<CTokenList> m_vecGetRaisesExceptionsTokenList; ///< The list of tokens for each exception to be parsed
///< during the post processing phase.
std::vector<CTokenList> m_vecParametersTokenList; ///< The list of tokens for each parameter to be parsed
///< during the post processing phase.
EProcessAssignmentProgression m_eProcAssState = EProcessAssignmentProgression::unprocessed; ///< Processing assignment
///< progression state.
CEntityVector m_vecRaisesExceptions; ///< Can raise the exceptions while reading/writing.
CEntityVector m_vecGetRaisesExceptions; ///< Can raise the exceptions while reading.
CEntityVector m_vecSetRaisesExceptions; ///< Can raise the exceptions while writing.
CEntityVector m_vecParameters; ///< Vector of parameters.
};
#endif // !defined(BASIC_TYPE_ENTITY_H)

View File

@@ -0,0 +1,765 @@
#include "definition_entity.h"
#include "../exception.h"
#include "../logger.h"
#include "../parser.h"
#include "struct_entity.h"
#include "union_entity.h"
#include "typedef_entity.h"
#include "variable_entity.h"
#include "attribute_entity.h"
#include "operation_entity.h"
#include "enum_entity.h"
#include "exception_entity.h"
#include "interface_entity.h"
#include "meta_entity.h"
#include <iostream>
CDefinitionEntity::CDefinitionEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CEntity(rptrContext, ptrParent), m_iteratorChildren(GetChildrenVector()), m_iteratorInheritance(m_vecInheritance)
{}
CDefinitionEntity::CDefinitionEntity(CParser& rParser, const CContextPtr& rptrContext) :
CEntity(rParser, rptrContext), m_iteratorChildren(GetChildrenVector()), m_iteratorInheritance(m_vecInheritance)
{}
sdv::interface_t CDefinitionEntity::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::idl::IDefinitionEntity>())
return static_cast<sdv::idl::IDefinitionEntity*>(this);
return CEntity::GetInterface(idInterface);
}
void CDefinitionEntity::ProcessContent()
{
CLog log("Processing definition content...");
// Get tokens until no token is returned any more.
while (true)
{
// Prepare proocessing the next token
CToken token = PeekToken();
// Add any meta entity
std::list<CParser::SMetaToken> lstMeta = GetParserRef().GetAndRemoveMeta();
for (const CParser::SMetaToken& rsMeta : lstMeta)
AddChild(std::make_shared<CMetaEntity>(rsMeta.tokenMeta.GetContext(), shared_from_this(), rsMeta.tokenMeta,
rsMeta.lstComments));
// Process the acquired token
if (!token) break;
if (!IsRootEntity() && token == "}")
break;
// Determine whether the comments are preceding the token (either on the same line or the line before).
CTokenList lstPreComments = GetPreCommentTokenList();
// Check for prefixes
bool bPrefixConst = false;
bool bPrefixReadOnly = false;
bool bPrefixTypedef = false;
bool bPrefixLocal = false;
token = GetToken();
uint32_t uiLineBegin = token.GetLine();
uint32_t uiColBegin = token.GetCol();
if (token == "const") // This must be a const declaration
{
if (!Supports(EDefinitionSupport::support_const_variable))
throw CCompileException(token, "Unexpected keyword 'const'.");
bPrefixConst = true;
token = GetToken();
}
else if (token == "readonly") // This must be a readonly attribute
{
if (!Supports(EDefinitionSupport::support_attribute))
throw CCompileException(token, "Unexpected keyword 'readonly'.");
bPrefixReadOnly = true;
token = GetToken();
} else if (token == "typedef") // This must be a typedef declaration
{
if (!Supports(EDefinitionSupport::support_typedef))
throw CCompileException(token, "Unexpected keyword 'typedef'.");
bPrefixTypedef = true;
token = GetToken();
} else if (token == "local") // This must be a const declaration
{
if (!Supports(EDefinitionSupport::support_interface))
throw CCompileException(token, "Unexpected keyword 'local'.");
bPrefixLocal = true;
token = GetToken();
// The next token must be interface
if (token != "interface")
throw CCompileException(token, "Unexpected token. Only interfaces can be local.");
}
// For keywords that could be a definition as well as a declaration, find out whether the statement here is a
// - forward declaration
// - a definition
// - a definition as well as a declaration
// - a declaration
// Check for a definition (first three points).
auto fnIsDefinition = [&]() -> bool
{
// A declaration without definition has the construction
// <def type> <def identifier> <decl identifier>;
// e.g.
// struct STest sTest;
// The <def type> was retrieved already. The next token would be the identifier.
if (PeekToken(0).GetType() == ETokenType::token_identifier &&
PeekToken(1).GetType() == ETokenType::token_identifier) return false;
// All other situations indicate a definition (possibly followed by a declaration).
return true;
};
CEntityPtr ptrDefinitionEntity;
CToken tokenDefinitionType = token;
if (token == "module" && fnIsDefinition())
{
if (!Supports(EDefinitionSupport::support_module))
throw CCompileException(token, "Unexpected keyword 'module'.");
ptrDefinitionEntity = CreateChild<CModuleEntity>(token.GetContext(), this);
}
else if (token == "enum" && fnIsDefinition())
{
if (fnIsDefinition() && !Supports(EDefinitionSupport::support_enum))
throw CCompileException(token, "Unexpected keyword 'enum'.");
ptrDefinitionEntity = CreateChild<CEnumEntity>(token.GetContext(), this);
}
else if (token == "exception" && fnIsDefinition())
{
if (!Supports(EDefinitionSupport::support_exception))
throw CCompileException(token, "Unexpected keyword 'exception'.");
ptrDefinitionEntity = CreateChild<CExceptionEntity>(token.GetContext(), this);
}
else if (token == "struct" && fnIsDefinition())
{
if (!Supports(EDefinitionSupport::support_struct))
throw CCompileException(token, "Unexpected keyword 'struct'.");
ptrDefinitionEntity = CreateChild<CStructEntity>(token.GetContext(), this);
}
else if (token == "union" && fnIsDefinition())
{
if (!Supports(EDefinitionSupport::support_union))
throw CCompileException(token, "Unexpected keyword 'union'.");
ptrDefinitionEntity = CreateChild<CUnionEntity>(token.GetContext(), this);
}
else if (token == "interface" && fnIsDefinition())
{
if (!Supports(EDefinitionSupport::support_interface))
throw CCompileException(token, "Unexpected keyword 'interface'.");
ptrDefinitionEntity = CreateChild<CInterfaceEntity>(token.GetContext(), this, bPrefixLocal);
}
// Definition available?
if (ptrDefinitionEntity)
{
// Assign the preceding comments (only if not prefixed)
if (!bPrefixConst && !bPrefixReadOnly && !bPrefixTypedef && !lstPreComments.empty())
{
ptrDefinitionEntity->SetCommentTokens(lstPreComments);
lstPreComments.clear();
}
// Set the location in the source file
ptrDefinitionEntity->SetBeginPosition(uiLineBegin, uiColBegin);
// Process the definition
log << "Detected " << static_cast<std::string>(token) << " definition..." << std::endl;
ptrDefinitionEntity->Process();
token = GetToken();
// Assign any succeeding comments
ptrDefinitionEntity->ProcessPostCommentTokenList(token.GetLine());
}
// A struct allows the definition of types that require a declaration within the struct, but the declaration is anonymous
// (unnamed and not declared) so the members of the definition appear as if they are part of the struct.
bool bAnonymousDecl = false;
if (token == ";" && GetType() == sdv::idl::EEntityType::type_struct && ptrDefinitionEntity &&
ptrDefinitionEntity->Get<CDefinitionEntity>()->RequireDeclaration())
{
if (ptrDefinitionEntity->Get<CDefinitionEntity>()->AllowAutoTransparentDeclaration())
{
// Allow processing still
PrependToken(token);
// Create a dummy name
token = CToken(GetParserRef().GenerateAnonymousEntityName("var"));
bAnonymousDecl = true;
} else
throw CCompileException(token,
"The definition requires a declaration following the definition content or cannot find a variable within "
"current scope - unexpected token ';'.");
}
// Statement finished?
if (token == ";")
{
if (bPrefixConst || bPrefixReadOnly || bPrefixTypedef)
throw CCompileException(token, "Unexpected token ';'.");
if (ptrDefinitionEntity)
{
// Assign any succeeding comments
ptrDefinitionEntity->ProcessPostCommentTokenList(token.GetLine());
// Set the end position in the source file.
ptrDefinitionEntity->SetEndPosition(token.GetLine(), token.GetCol());
// Declaration needed?
if (ptrDefinitionEntity->Get<CDefinitionEntity>()->RequireDeclaration())
throw CCompileException(token,
"The definition requires a declaration following the definition content - unexpected token ';'.");
}
continue;
};
if (!token && !bPrefixConst && !bPrefixReadOnly && !bPrefixTypedef)
throw CCompileException(GetLastValidToken(), "Missing ';' following the token.");
// The declarative part can consist of the following types of declaration
// - an attribute - read-only prefix possible
// - a typedef
// - an operation
// - a variable - const prefix possible
CEntityPtr ptrDeclarationEntity;
if (bPrefixReadOnly && token != "attribute")
throw CCompileException(token, "Expecting 'attribute' keyword following 'readonly'.");
if (token == "attribute")
{
if (bPrefixConst || bPrefixTypedef || ptrDefinitionEntity || !Supports(EDefinitionSupport::support_attribute))
throw CCompileException(token, "Unexpected keyword 'attribute'.");
ptrDeclarationEntity = CreateChild<CAttributeEntity>(token.GetContext(), this, bPrefixReadOnly);
if (bPrefixReadOnly)
log << "Detected read-only attribute declaration..." << std::endl;
else
log << "Detected attribute declaration..." << std::endl;
}
else if (token == "case" || token == "default")
{
if (bPrefixReadOnly || bPrefixConst || bPrefixTypedef || ptrDefinitionEntity || !Supports(EDefinitionSupport::support_case_declaration))
throw CCompileException(token, "Unexpected keyword 'case'.");
ptrDeclarationEntity = CreateChild<CCaseEntry>(token.GetContext(), this, token == "default");
log << "Detected case entry declaration..." << std::endl;
} else
{
// Reinsert the token into the tokenlist
PrependToken(token);
// Check whether the declaration is a variable declaration or an operation.
// An operation can have the following structure:
// <keyword> <identifier>(); e.g. int32 Func()
// <keyword> <keyword> <identifier>(); e.g. long long Func()
// <keyword> <keyword> <keyword> <identifier>(); e.g. unsigned long long Func()
// <identifier> <identifier>() e.g. mytype Func()
// A variable declaration can have the following structure:
// <keyword> <identifier>; e.g. int32 iVar;
// <keyword> <keyword> <identifier>; e.g. long long llVar;
// <keyword> <keyword> <keyword> <identifier>; e.g. unsigned long long ullVar;
// <keyword> <identifier> = <expression>; e.g. int32 iVar = 10;
// <keyword> <keyword> <identifier> = <expression>; e.g. long long llVar = 10;
// <keyword> <keyword> <keyword> <identifier> = <expression>; e.g. unsigned long long ullVar = 10;
// Only variables can be declared with const prefix.
// A variable declaration without assignment can also be a typedef when declared with typedef prefix.
// Operations cannot be declared follwoing a definition.
// As type expecting up to three keywords or one scoped identifier (composition of identifier and scope operator).
// If there is a definition, then the declaration and definition are combined. The type is there then already.
size_t nIndex = 0;
bool bTypeFound = ptrDefinitionEntity ? true : false;
CToken tokenLocal;
bool bLongType = false;
bool bUnsignedType = false;
while ((tokenLocal = PeekToken(nIndex)).GetType() == ETokenType::token_keyword)
{
// Unsigned is allowed only as first keyword
if (tokenLocal == "unsigned")
{
if (nIndex) break;
bUnsignedType = true;
}
// Long should be followed by long or double.
if (bLongType && tokenLocal != "long" && tokenLocal != "double") break;
// Increase the token index...
nIndex++;
// Type found...
bTypeFound = true;
// After unsigned, at least one more keyword needs to follow
if (bUnsignedType && nIndex == 1) continue;
// After long, another type can follow, but only when this long didn't follow a previous long already.
if (tokenLocal == "long")
{
if (bLongType) break; // type was long long.
bLongType = true;
continue;
}
// The type can be a templated type (or even nested templated types are possible).
tokenLocal = PeekToken(nIndex);
CToken tokenLocalTemplate;
if (tokenLocal == "<")
{
size_t nDepth = 0;
tokenLocalTemplate = tokenLocal;
do
{
if (tokenLocal == "<") nDepth++;
if (tokenLocal == ">") nDepth--;
if (tokenLocal == ">>") nDepth-=2; // This actually is an operator
nIndex++;
tokenLocal = PeekToken(nIndex);
} while (tokenLocal && static_cast<bool>(nDepth));
}
// No more types are expected to follow
break;
}
while (!bTypeFound)
{
// Check for the scope parameter
tokenLocal = PeekToken(nIndex);
if (tokenLocal == "::")
{
nIndex++;
tokenLocal = PeekToken(nIndex);
}
// Check for the identifier
if (tokenLocal.GetType() == ETokenType::token_identifier)
{
nIndex++;
if (PeekToken(nIndex) != "::")
bTypeFound = true;
continue;
}
// Coming here means, there was no type
throw CCompileException(tokenLocal, "Invalid token '", static_cast<std::string>(tokenLocal),
"' found for type name.");
}
// The return value can be an array - but only in case of operations.
tokenLocal = PeekToken(nIndex);
CToken tokenLocalArray;
while (tokenLocal == "[")
{
if (!tokenLocalArray) tokenLocalArray = tokenLocal;
do
{
nIndex++;
tokenLocal = PeekToken(nIndex);
} while (tokenLocal && tokenLocal != "]");
nIndex++;
tokenLocal = PeekToken(nIndex);
}
// Expecting identfier representing the name
// If the extension is enabled, this could also be a keyword.
if (tokenLocal.GetType() != ETokenType::token_identifier &&
(!GetParserRef().GetEnvironment().ContextDependentNamesExtension() ||
tokenLocal.GetType() != ETokenType::token_keyword))
throw CCompileException(tokenLocal, "Invalid token '", static_cast<std::string>(tokenLocal),
"' found for type name (or missing semi-colon ';' ?).");
nIndex++;
// An operation has a left bracket
tokenLocal = PeekToken(nIndex);
if (tokenLocal == "(") // This indicates a operation
{
if (ptrDefinitionEntity || bPrefixConst || bPrefixTypedef || !Supports(EDefinitionSupport::support_operation))
throw CCompileException(tokenLocal, "Unexpected left bracket '('.");
ptrDeclarationEntity = CreateChild<COperationEntity>(token.GetContext(), this);
log << "Detected operation declaration..." << std::endl;
}
else // This could be a variable declaration or a typedef declaration
{
// An array is not allowed when not using an operation
if (tokenLocalArray)
throw CCompileException(tokenLocalArray, "Invalid token '[' found for type name.");
// If there is a definition, add the name of the definition
if (ptrDefinitionEntity)
{
CToken tokenName(ptrDefinitionEntity->GetName());
PrependToken(tokenName);
}
// Check for typedef declaration
if (bPrefixTypedef)
{
ptrDeclarationEntity = CreateChild<CTypedefEntity>(token.GetContext(), this);
log << "Detected typedef declaration..." << std::endl;
}
else // Variable declaration
{
if (!bPrefixConst && !Supports(EDefinitionSupport::support_variable))
throw CCompileException(token, "Variable declaration is not supported.");
ptrDeclarationEntity = CreateChild<CVariableEntity>(token.GetContext(), this, bPrefixConst, bAnonymousDecl);
if (bPrefixConst)
log << "Detected const variable declaration..." << std::endl;
else
log << "Detected variable declaration..." << std::endl;
}
}
}
// Process the entity declaration.
if (!ptrDeclarationEntity) throw CCompileException("Declaration expected.");
ptrDeclarationEntity->SetBeginPosition(uiLineBegin, uiColBegin);
ptrDeclarationEntity->Process();
if (!lstPreComments.empty())
ptrDeclarationEntity->SetCommentTokens(lstPreComments);
// Expect ';'
token = GetToken();
if (token != ";")
throw CCompileException(token, "Missing semicolon ';' following the declaration.");
}
log << "For definition '" << GetName() << "', processing value assignments within content..." << std::endl;
// If supported create the value node for the definition (this allows assignments of values to this entity).
CreateValueNode();
// If there is a value node (assignment is supported), add the inherited value sub-trees to the type as children. If there are
// inherited entities with value nodes of their own, of course.
if (ValueRef())
CreateInheritanceValueChildNodes();
// Go through all the type members and do post processing (building value chains, resolving arrays, etc.).
for (CEntityPtr ptrTypeEntity : m_lstTypeMembers)
{
CTypedefEntity* pTypedefEntity = ptrTypeEntity->Get<CTypedefEntity>();
if (pTypedefEntity)
{
log << "Post-processing typedef entity '" << pTypedefEntity->GetName() << "'..." << std::endl;
pTypedefEntity->PostProcess();
}
CUnionEntity* pUnionEntity = ptrTypeEntity->Get<CUnionEntity>();
if (pUnionEntity)
{
log << "Post-processing union entity '" << pUnionEntity->GetName() << "'..." << std::endl;
pUnionEntity->PostProcess();
}
}
// Go through all the typedef members and do post processing (building value chains, resolving arrays, variable assignment,
// etc.).
for (CEntityPtr ptrConstEntity : m_lstConstMembers)
{
CVariableEntity* pConstEntity = ptrConstEntity->Get<CVariableEntity>();
if (!pConstEntity)
throw CCompileException("Internal error: non-const entities in const entity list.");
else
{
log << "Post-processing const variable entity '" << pConstEntity->GetName() << "'..." << std::endl;
pConstEntity->PostProcess();
}
}
// Go through all the definition members and do post processing (resolving arrays, creating exception and parameter lists,
// etc.).
for (CEntityPtr ptrDefinitionEntity : m_lstAttributesOperation)
{
CAttributeEntity* pAttribute = ptrDefinitionEntity->Get<CAttributeEntity>();
COperationEntity* pOperation = ptrDefinitionEntity->Get<COperationEntity>();
if (!pAttribute && !pOperation)
throw CCompileException("Internal error: wrong entities in definition list.");
if (pAttribute)
{
log << "Postprocessing attribute entity '" << pAttribute->GetName() << "'..." << std::endl;
pAttribute->PostProcess();
}
if (pOperation)
{
log << "Postprocessing operation entity '" << pOperation->GetName() << "'..." << std::endl;
pOperation->PostProcess();
}
}
// Go through all the variable members and create the default values for all var members if the value node is a compound type
// value node.
for (CEntityPtr ptrDeclEntity : m_lstDeclMembers)
{
CVariableEntity *pDeclEntity = ptrDeclEntity->Get<CVariableEntity>();
if (!pDeclEntity)
throw CCompileException("Internal error: non-declaration entity in declaration entity list.");
else
{
log << "Postprocess variable entity '" << pDeclEntity->GetName() << "'..." << std::endl;
pDeclEntity->PostProcess();
}
}
}
void CDefinitionEntity::Process()
{
CLog log("Processing definition of entity...");
// Check for an identifier.
// If present, this is the name of the definition.
CToken token = GetToken();
std::string ssName;
m_bAnonymousDefinition = false;
if (token.GetType() == ETokenType::token_identifier)
{
ssName = static_cast<std::string>(token);
log << "Definition type name '" << ssName << "'..." << std::endl;
} else
if (SupportsAnonymous())
{
if (!GetParentEntity() || GetParentEntity()->IsRootEntity())
throw CCompileException(token, "Unnamed definitions are not supported at root level.");
std::string ssPrefix = "anonymous";
switch (GetType())
{
case sdv::idl::EEntityType::type_enum: ssPrefix = "enum"; break;
case sdv::idl::EEntityType::type_struct: ssPrefix = "struct"; break;
case sdv::idl::EEntityType::type_union: ssPrefix = "union"; break;
case sdv::idl::EEntityType::type_module: ssPrefix = "namespace"; break;
case sdv::idl::EEntityType::type_interface: ssPrefix = "interface"; break;
case sdv::idl::EEntityType::type_exception: ssPrefix = "except"; break;
case sdv::idl::EEntityType::type_typedef: ssPrefix = "typedef"; break;
default: break;
}
ssName = GetParserRef().GenerateAnonymousEntityName(ssPrefix);
log << "Unnamed definition was automatically assigned name '" << ssName << "'..." << std::endl;
m_bRequiresContent = true;
m_bAnonymousDefinition = true;
PrependToken(token);
} else
throw CCompileException(token, "Unnamed definition is not supported.");
// Process the definition addendum
ProcessDefinitionAddendum();
// Expecting curly bracket when for a
token = GetToken();
if (RequireContentDefinition() && token != "{")
throw CCompileException(token, "Expecting curly bracket '{'.");
// If there is no content, consider the statement as a (forward) declaration.
SetName(ssName, token != "{");
// If there is no content, the processing is done.
if (token != "{")
{
log << "Definition type was defined as forward declaration." << std::endl;
log << "Finished processing definition entity..." << std::endl;
PrependToken(token);
return;
}
// Definition content...
ProcessContent();
// Close the definition
token = GetToken();
if (token != "}")
throw CCompileException(token, "Expecting curly bracket '}'.");
log << "Finished processing definition entity..." << std::endl;
}
void CDefinitionEntity::ProcessDefinitionAddendum()
{
// Check for inheritance
CToken token = GetToken();
if (token != ":")
{
PrependToken(token);
return;
}
if (!SupportsInheritance()) throw CCompileException(token, "Inheritance is not supported.");
// When an inheritance list is provided, the content should also be provided.
m_bRequiresContent = true;
CLog log("Processing inheritance list...");
// Get the list of base entities
while (true)
{
// Find the base entity.
std::pair<std::string, CEntityPtr> prBase = ProcessScopedName();
if (prBase.first.empty() || !prBase.second)
throw CCompileException("Base not found.");
log << "Inherited from base entity: " << prBase.first << std::endl;
if (prBase.second->GetResolvedEntity()->GetType() != GetType())
throw CCompileException("Cannot inherit from different types.");
// Check whether the base entity is more than only a declaration.
if (prBase.second->ForwardDeclaration())
throw CCompileException("Base type found, but only declared; definition is missing.");
// TODO: Check whether the entity was already previously inherited directly or indirectly. Inheriting through a base
// prevents inheriting again.
// Add the base entity to the base entity list.
m_vecInheritance.push_back(prBase.second);
// Get the next base entity or the beginning of the definition.
token = GetToken();
if (token != ",")
{
PrependToken(token);
break;
}
}
}
void CDefinitionEntity::CreateInheritanceValueChildNodes()
{
CLog log("Creating values for inherited child nodes...");
if (!ValueRef()) throw CCompileException("Internal error: cannot create inheritance value child nodes.");
// For each base entity, copy the value tree
for (CEntityPtr ptrInheritedEntity : m_vecInheritance)
{
// Check for a valid value of the base entity.
if (!ptrInheritedEntity->GetResolvedEntity()->ValueRef())
throw CCompileException("Internal error: base entity wasn't processed for assignment yet.");
log << "Assigning values from base entity '" << ptrInheritedEntity->GetName() << "'." << std::endl;
// Create a copy of the tree and add as child to own tree.
ValueRef()->AddChild(ptrInheritedEntity->GetResolvedEntity()->ValueRef()->CreateCopy(shared_from_this(), ValueRef()));
}
}
sdv::idl::IEntityIterator* CDefinitionEntity::GetChildren()
{
if (SupportsChildren()) return &m_iteratorChildren;
return nullptr;
}
sdv::idl::IEntityIterator* CDefinitionEntity::GetInheritance()
{
if (SupportsInheritance() && !m_vecInheritance.empty()) return &m_iteratorInheritance;
return nullptr;
}
void CDefinitionEntity::CalcHash(CHashObject& rHash) const
{
// First calc the hash of the base entity.
CEntity::CalcHash(rHash);
// Add the hashes of all inherited entities
for (const CEntityPtr& ptrEntity : m_vecInheritance)
ptrEntity->CalcHash(rHash);
// Add the declarations
for (const CEntityPtr& ptrEntity : m_lstDeclMembers)
ptrEntity->CalcHash(rHash);
// Add the attributes and operations
for (const CEntityPtr& ptrEntity : m_lstAttributesOperation)
ptrEntity->CalcHash(rHash);
}
const CEntityList CDefinitionEntity::GetDeclMembers() const
{
CEntityList lstMembers;
// Add all declarations from the inherited members
for (const CEntityPtr& rptrInheritedEntity : m_vecInheritance)
{
const CDefinitionEntity* pDefinition = rptrInheritedEntity->GetResolvedEntity()->Get<CDefinitionEntity>();
if (pDefinition)
{
CEntityList lstInheritedEntities = pDefinition->GetDeclMembers();
for (const CEntityPtr& ptrInheritedMember : lstInheritedEntities)
lstMembers.push_back(ptrInheritedMember);
}
}
// Add own declarations
for (const CEntityPtr& rptrMember : m_lstDeclMembers)
lstMembers.push_back(rptrMember);
return lstMembers;
}
void CDefinitionEntity::AddChild(CEntityPtr ptrChild)
{
CLog log;
// Call base class first
CEntity::AddChild(ptrChild);
// Dependable on the type of entity, add the entity to dedicated lists as well.
if (ptrChild->Get<CTypedefEntity>())
{
m_lstTypeMembers.push_back(ptrChild);
log << "Added typedef declaration to type member list..." << std::endl;
}
else if (ptrChild->Get<CAttributeEntity>())
{
m_lstAttributesOperation.push_back(ptrChild);
log << "Added attribute to definition list..." << std::endl;
} else if (ptrChild->Get<COperationEntity>())
{
m_lstAttributesOperation.push_back(ptrChild);
log << "Added operation to definition list..." << std::endl;
}
else if (ptrChild->Get<CDeclarationEntity>())
{
if (ptrChild->Get<CDeclarationEntity>()->IsReadOnly())
{
m_lstConstMembers.push_back(ptrChild);
log << "Added const declaration to const member list..." << std::endl;
}
else
{
m_lstDeclMembers.push_back(ptrChild);
log << "Added declaration to variable member list..." << std::endl;
}
}
else if (ptrChild->Get<CDefinitionEntity>())
{
m_lstTypeMembers.push_back(ptrChild);
log << "Added definition to type member list..." << std::endl;
}
else if (ptrChild->Get<CMetaEntity>())
{
GetRootEntity()->Get<CRootEntity>()->AddMeta(ptrChild);
log << "Added meta data to root based meta list..." << std::endl;
}
}
std::pair<CEntityPtr, bool> CDefinitionEntity::FindLocal(const std::string& rssName, bool bDeclaration) const
{
// Call base class implementation first.
std::pair<CEntityPtr, bool> prEntity = CEntity::FindLocal(rssName, bDeclaration);
if (prEntity.first) return prEntity;
// Check through all inherited entities
for (const CEntityPtr& rptrBaseEntity : m_vecInheritance)
{
if (!rptrBaseEntity) continue;
CEntityPtr ptrResolvedBaseEntity = rptrBaseEntity->GetResolvedEntity();
if (!ptrResolvedBaseEntity->Get<CDefinitionEntity>()) continue;
prEntity = ptrResolvedBaseEntity->Get<CDefinitionEntity>()->FindLocal(rssName, bDeclaration);
if (prEntity.first)
{
// Set the inherited flag
prEntity.second = true;
return prEntity;
}
}
// No entity found.
return std::make_pair(CEntityPtr(), false);
}

View File

@@ -0,0 +1,249 @@
#ifndef DEFINITION_ENTITY_H
#define DEFINITION_ENTITY_H
#include "entity_base.h"
/**
* @brief Support flags for the content of a definition (the part between the curly brackets '{...}').
*/
enum class EDefinitionSupport : uint32_t
{
support_variable, ///< Support variable declarations.
support_const_variable, ///< Support const variable declarations.
support_case_declaration, ///< Support case declarations.
support_enum_entry, ///< Support enumerator entry.
support_module, ///< Support module definitions.
support_typedef, ///< Support typedef declarations.
support_interface, ///< Support interface definitions.
support_struct, ///< Support struct definitions.
support_union, ///< Support union definitions.
support_enum, ///< Support enum definitions.
support_exception, ///< Support exception definitions.
support_attribute, ///< Support attribute declarations.
support_operation, ///< Support operation declarations.
};
/**
* @brief The base for definition entity definitions within an IDL file (struct, union, exception, interface, enum).
* @details The definition entity definitions all have a similar setup with small differences in detail. Consider the following
* structures:
* @code
* struct <name | anonymous> : <inheritance list> { <member list> }
* union <name | anonymous> switch(<type>) { <case member list> }
* interface <name> : <inheritance list> { <member list> }
* exception <name> { <member list> }
* enum <name> : <type> { <item list> }
* @endcode
* The following generalized structure applies to all complex structures:
* @code
* keyword name
* prefix keyword name postfix { definition }
* @endcode
* The first statement is a forward declaration. The second represents the type definition.
* The prefix is used to provide a specific interpretation to the definition.
* The keyword defines the type of definition (can be struct, union, interface, exception, enum).
* The name is the defined name of the definition. For types that allow anonymous names, this part is optionally.
* The postfix allows the specification of additional information needed for the definition (inheritynce list, switch list).
* The definition defines the content of the type.
* Some declarations might start as a type definition (e.g. struct, union, enum) followed with a declaration. For example:
* @code
* struct <def_name> { <definition> } <decl_name>
* struct { <definition> } <decl_name>
* @endcode
* In the first statement, the struct is defined and declared in one statement. In the second statement, an anonymous struct is
* defined and declared in one statement.
*/
class CDefinitionEntity : public CEntity, public sdv::idl::IDefinitionEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CDefinitionEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
protected:
/**
* @brief Root entity constructor (name is 'root' and no parent).
* @param[in] rParser Reference to the parser.
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
*/
CDefinitionEntity(CParser& rParser, const CContextPtr& rptrContext);
public:
/**
* @brief Destructor
*/
virtual ~CDefinitionEntity() override = default;
/**
* @brief Get access to another interface. Overload of IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Process the content of the definition.
* @details Process the content of the definition. This function parses through the content until a closing curly bracket
* has been detected. First the function checks for prefixes. Then the function determines whether the statement is a
* declaration or a definition. It then creates the corresponding entity and let the entity process itself.
*/
virtual void ProcessContent();
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Process the definition addendum.
* @details Process the definition addendum following the definition statement before the content definition. The default
* implementation checks for an inheritance list.
*/
virtual void ProcessDefinitionAddendum();
/**
* @brief Create a values for inherited child nodes.
*/
void CreateInheritanceValueChildNodes();
/**
* \brief Does the entity have an Unnamed definition. Overload of IDefinitionEntity::Unnamed.
* @return Returns 'true' when the definition supports unnamed definition; 'false' otherwise.
*/
virtual bool IsUnnamed() const override { return m_bAnonymousDefinition; };
/**
* @brief Request whether the definition supports the content. This function must be implemented by the derived entity.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const = 0;
/**
* @brief Does the entity support children? Overload of CEntity::SupportsChildren.
* @details Complex types support children per default.
* @return Returns whether the entity supports children (which is the case).
*/
virtual bool SupportsChildren() const override { return true; }
/**
* @brief Does the entity support inheritance?
* @details The default implementation is that inheritance is not supported.
* @return Returns whether the entity supports inheritance (which is not the case).
*/
virtual bool SupportsInheritance() const { return false; }
/**
* @brief Does the entity support anonymous naming?
* @details The default implementation is that anonymous naming is not supported.
* @return Returns whether the entity supports inheritance (which is not the case).
*/
virtual bool SupportsAnonymous() const { return false; }
/**
* @brief Does the complex entity support attributes in its content?
* @details The default implementation doesn't support attributes (they are specific to interfaces).
* @return Returns whether the entity supports attributes (which is not the case).
*/
virtual bool SupportContentAttributes() const { return false; }
/**
* @brief Does the complex entity support operations in its content?
* @details The default implementation doesn't support operations (they are specific to interfaces).
* @return Returns whether the entity supports operations (which is not the case).
*/
virtual bool SupportContentOperations() const { return false; }
/**
* @brief Does the definition entity require a content definition?
* @details In certain cases, it is required that the content definition is following the definition statement. For example,
* when an inheritance list is provided. The default implementation checks the m_bRequiresContent variable.
* @return Returns whether the content definition should be defined following the definition statement.
*/
virtual bool RequireContentDefinition() const { return m_bRequiresContent; }
/**
* @brief Does the definition require a declaration?
* @details In certain cases, it is required that the definition is followed by a declaration. For example,
* when the definition was made anonymously or when the definition is dependent on a variable within the same struct (e.g. with
* unions).
* @return Returns whether the definition requires a declaration.
*/
virtual bool RequireDeclaration() const { return IsUnnamed(); }
/**
* @brief Does the definition allow automatic transparent declaration if not present?
* @details When set an automatic transparent declaration is allowed without an explicit variable declaration. Currently this
* is only the case with unions with a variable based switch type.
* @return Returns whether the definition allows an automatic transparent declaration.
*/
virtual bool AllowAutoTransparentDeclaration() const { return false; }
/**
* @brief Get child entity iterator if children are available and supported by the definition. Overload of
* sdv::idl::IDefinitionEntity::GetChildren.
* @return Returns a pointer to the child entity iterator or NULL when not available.
*/
virtual sdv::idl::IEntityIterator* GetChildren() override;
/**
* @brief Get inheritance entity iterator if the definition was inheriting from other entities. Overload of
* sdv::idl::IDefinitionEntity::GetInheritance.
* @return Returns a pointer to the inheritance entity iterator or NULL when not available.
*/
virtual sdv::idl::IEntityIterator* GetInheritance() override;
/**
* @brief Calculate the hash of this entity and all encapsulated entities. Overload of CBaseEntity::CalcHash.
* @param[in, out] rHash Hash object to be filled with data.
*/
virtual void CalcHash(CHashObject& rHash) const override;
/**
* @brief Get the declaration members.
* @return List of declaration members.
*/
const CEntityList GetDeclMembers() const;
protected:
/**
* @brief Add the child to the children list. Called by CreateChild function. Overload of CEntity::AddChild.
* @param[in] ptrChild Pointer to the child entity to add.
*/
virtual void AddChild(CEntityPtr ptrChild) override;
/**
* @brief Find the entity locally by looking in the entity children map and the entity children maps of all the chained
* entities. The search is done case-insensitive. Overload of CEntity::FindLocal.
* @param[in] rssName Reference to the string object containing the name of the entity to search for.
* @param[in] bDeclaration When set, the name belongs to a declaration; otherwise it belongs to a definition. Needed to allow
* the reuse of names between declarations and definitions.
* @return Returns a pair object containing an entity pointer if the entity exists or a NULL pointer if not as well as a
* boolean that indicates that the entity was from an inherited entity.
*/
virtual std::pair<CEntityPtr, bool> FindLocal(const std::string& rssName, bool bDeclaration) const override;
/**
* @brief Create the content value node. Overridable when supporting content values.
* @details When supporting value assignments, create the value node and assign the value node to the ValueRef() reference. For
* definitions that do not support value assignments, do nothing (default).
*/
virtual void CreateValueNode() {};
protected:
CEntityVector m_vecInheritance; ///< List of base entities in the order of appearance.
CEntityList m_lstTypeMembers; ///< List of typedef member declarations and type definitions.
CEntityList m_lstConstMembers; ///< List of const member declarations.
CEntityList m_lstDeclMembers; ///< List of variable member declarations.
CEntityList m_lstAttributesOperation; ///< List with attributes and operations.
bool m_bRequiresContent = false; ///< When set, the definition statement requires content to follow.
bool m_bAnonymousDefinition = false; ///< When set, the entity has an anonymous generated name.
CEntityIterator m_iteratorChildren; ///< Children iterator
CEntityIterator m_iteratorInheritance; ///< Inheritance iterator
};
#endif // !defined(DEFINITION_ENTITY_H)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,803 @@
#include "entity_value.h"
#include "../exception.h"
#include "variable_entity.h"
#include "definition_entity.h"
#include "../constvariant.inl"
#include "../support.h"
#include "../parser.h"
CEntityValueNode::CEntityValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
m_ptrEntity(ptrEntity), m_ptrParent(ptrParent)
{
if (!ptrEntity) throw CCompileException("Internal error: expected a valid entity.");
CLog log;
if (!ptrParent)
log << "The value node of '" << ptrEntity->GetName() << "' has no parent node..." << std::endl;
else
log << "The value node of '" << ptrEntity->GetName() << "' has a parent node..." << std::endl;
}
CEntityValueNode::CEntityValueNode(const CEntityValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
m_ptrEntity(ptrEntity), m_ptrParent(ptrParent), m_vecChildren(rValueNode.m_vecChildren)
{
if (!ptrEntity) throw CCompileException("Internal error: expected a valid entity.");
CLog log;
if (!ptrParent)
log << "The value node of '" << ptrEntity->GetName() << "' has no parent node..." << std::endl;
else
log << "The value node of '" << ptrEntity->GetName() << "' has a parent node..." << std::endl;
}
void CEntityValueNode::ProcessChildNodes(const CTokenList& rlstExpression)
{
CLog log;
log << "Processing " << m_vecChildren.size() << " child nodes..." << std::endl;
bool bInitial = true;
for (CValueNodePtr& rptrValue : m_vecChildren)
{
// Check for a comma separator.
if (!bInitial)
{
if (rlstExpression.Current() != ",")
throw CCompileException(rlstExpression.LastValid(), "Missing comma ',' for value separation.");
++rlstExpression;
}
bInitial = false;
log << "Processing child node..." << std::endl;
rptrValue->ProcessValueAssignment(rlstExpression);
}
}
void CEntityValueNode::AddChild(CValueNodePtr ptrChild)
{
CLog log;
log << "Adding child node..." << std::endl;
if (!ptrChild)
throw CCompileException("Internal error: cannot add an empty child node.");
m_vecChildren.push_back(ptrChild);
}
std::string CEntityValueNode::GetDeclTypeStr(bool bResolveTypedef) const
{
return GetDeclEntity()->GetDeclTypeStr(bResolveTypedef);
}
bool CEntityValueNode::IsConst() const
{
// Default implementation
// - In case the entity is a const entity, return 'true'.
// - Otherwise check whether the parent value is available and ask the parent if the declaration entity is a const entity.
// - If no parent is available, this entity must be a declaration entity and is therefore not a const entity.
if (m_ptrEntity->Get<CVariableEntity>() && m_ptrEntity->Get<CVariableEntity>()->IsReadOnly()) return true;
if (m_ptrParent) return m_ptrParent->IsConst();
return false;
}
bool CEntityValueNode::IsDeclaration() const
{
// Default implementation
return m_ptrEntity->Get<CVariableEntity>();
}
CSimpleTypeValueNode::CSimpleTypeValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
CEntityValueNode(ptrEntity, ptrParent)
{}
CSimpleTypeValueNode::CSimpleTypeValueNode(const CSimpleTypeValueNode& rValueNode, CEntityPtr ptrEntity,
const CValueNodePtr ptrParent) :
CEntityValueNode(rValueNode, ptrEntity, ptrParent), m_varValue(rValueNode.m_varValue),
m_lstExpression(rValueNode.m_lstExpression), m_eValueDef(rValueNode.m_eValueDef)
{}
CValueNodePtr CSimpleTypeValueNode::CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const
{
return std::make_shared<std::decay_t<std::remove_pointer_t<decltype(this)>>>(*this, ptrEntity, ptrParent);
}
void CSimpleTypeValueNode::ProcessValueAssignment(const CTokenList& rlstExpression)
{
CLog log("Processing declaration value assignment...");
if (!m_ptrEntity) throw CCompileException("Internal error: no entity assigned to the value node.");
const CDeclarationEntity* pSystemTypeEntity = m_ptrEntity->Get<CDeclarationEntity>();
if (!pSystemTypeEntity)
throw CCompileException("Internal error: type mismatch between value node and entity type.");
// Process the assignment expression and convert the result in the target type.
std::pair<CConstVariant, bool> prValue{0, false};
switch (pSystemTypeEntity->GetBaseType())
{
case sdv::idl::EDeclType::decltype_short:
log << "Processing system type value node of type 'int16/short'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<int16_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<int64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_long:
log << "Processing system type value node of type 'int32/long'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<int32_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<int64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_long_long:
log << "Processing system type value node of type 'int64/long long'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<int64_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<int64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_unsigned_short:
log << "Processing system type value node of type 'uint16/unsigned short'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<uint16_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_unsigned_long:
log << "Processing system type value node of type 'uint32/unsigned long'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<uint32_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_unsigned_long_long:
log << "Processing system type value node of type 'uint64/unsigned long long'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<uint64_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_char:
log << "Processing system type value node of type 'int8/UTF-8 or ASCII char'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<int8_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<int64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_char16:
log << "Processing system type value node of type 'UTF-16 char'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<uint16_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_char32:
log << "Processing system type value node of type 'UTF-32 char'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<uint32_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_wchar:
log << "Processing system type value node of type 'wchar'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
if (sizeof(wchar_t) == 2)
prValue.first.Convert<uint16_t>();
else
prValue.first.Convert<uint32_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_float:
log << "Processing system type value node of type 'float'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<float>();
log << "Simple type value node has calculated value " << prValue.first.Get<long double>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_double:
log << "Processing system type value node of type 'double'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<double>();
log << "Simple type value node has calculated value " << prValue.first.Get<long double>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_long_double:
log << "Processing system type value node of type 'long double'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<long double>();
log << "Simple type value node has calculated value " << prValue.first.Get<long double>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_fixed:
log << "Processing system type value node of type 'fixed'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<fixed>();
log << "Simple type value node has calculated value " << prValue.first.Get<long double>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_boolean:
log << "Processing system type value node of type 'boolean'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<bool>();
log << "Simple type value node has calculated value " << (prValue.first.Get<bool>() ? "'true'" : "'false'") << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_native:
log << "Processing system type value node of type 'native'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<size_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<size_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_octet:
log << "Processing system type value node of type 'uint8/octet'..." << std::endl;
prValue = pSystemTypeEntity->ProcessNumericExpression(rlstExpression);
prValue.first.Convert<uint8_t>();
log << "Simple type value node has calculated value " << prValue.first.Get<uint64_t>() << "..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_string:
log << "Processing system type value node of type 'ASCII string'..." << std::endl;
prValue = pSystemTypeEntity->ProcessStringExpression(rlstExpression);
prValue.first.Convert<std::string>();
log << "Simple type value node has constructed string \"" << prValue.first.Get<std::string>() << "\"..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_u8string:
log << "Processing system type value node of type 'UTF-8 string'..." << std::endl;
prValue = pSystemTypeEntity->ProcessStringExpression(rlstExpression);
prValue.first.Convert<std::string>();
log << "Simple type value node has constructed string \"" << prValue.first.Get<std::string>() << "\"..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_u16string:
log << "Processing system type value node of type 'UTF-16 string'..." << std::endl;
prValue = pSystemTypeEntity->ProcessStringExpression(rlstExpression);
prValue.first.Convert<std::u16string>();
log << "Simple type value node has constructed string..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_u32string:
log << "Processing system type value node of type 'UTF-32 string'..." << std::endl;
prValue = pSystemTypeEntity->ProcessStringExpression(rlstExpression);
prValue.first.Convert<std::u32string>();
log << "Simple type value node has constructed string..." << std::endl;
break;
case sdv::idl::EDeclType::decltype_wstring:
log << "Processing system type value node of type 'wstring'..." << std::endl;
prValue = pSystemTypeEntity->ProcessStringExpression(rlstExpression);
prValue.first.Convert<std::wstring>();
log << "Simple type value node has constructed string..." << std::endl;
break;
default:
log << "Processing system type value node of unknown type..." << std::endl;
throw CCompileException("Internal error: expression build error.");
}
// Check for a dynamic result
if (prValue.second)
SetDynamicValue(rlstExpression);
else
SetFixedValue(prValue.first, rlstExpression);
}
void CSimpleTypeValueNode::SetFixedValue(const CConstVariant& rvarValue, const CTokenList& rlstExpression)
{
CLog log;
switch (m_eValueDef)
{
case EValueDef::dynamic:
log << "The value was re-assigned; the previous value was a dynamic value..." << std::endl;
break;
case EValueDef::fixed:
log << "The value was re-assigned; the previous value was a fixed value of " <<
m_varValue.GetAsString() << "..." << std::endl;
break;
default:
break;
}
m_eValueDef = EValueDef::fixed;
log << "The system type value node was set to a fixed value of " << rvarValue.GetAsString() << "..." << std::endl;
m_varValue = rvarValue;
m_lstExpression = rlstExpression;
}
void CSimpleTypeValueNode::SetDynamicValue(const CTokenList& rlstExpression)
{
CLog log;
switch (m_eValueDef)
{
case EValueDef::dynamic:
log << "The value was re-assigned; the previous value was a dynamic value..." << std::endl;
break;
case EValueDef::fixed:
log << "The value was re-assigned; the previous value was a fixed value of " <<
m_varValue.GetAsString() << "..." << std::endl;
break;
default:
break;
}
m_eValueDef = EValueDef::dynamic;
log << "The system type value node was set to a dynamic value..." << std::endl;
m_lstExpression = rlstExpression;
}
CArrayValueNode::CArrayValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
CEntityValueNode(ptrEntity, ptrParent)
{}
CArrayValueNode::CArrayValueNode(const CArrayValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
CEntityValueNode(rValueNode, ptrEntity, ptrParent), m_lstArraySizeExpression(rValueNode.m_lstArraySizeExpression),
m_eSizeDef(rValueNode.m_eSizeDef)
{
m_vecChildren = rValueNode.m_vecChildren;
}
CValueNodePtr CArrayValueNode::CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const
{
return std::make_shared<std::decay_t<std::remove_pointer_t<decltype(this)>>>(*this, ptrEntity, ptrParent);
}
void CArrayValueNode::ProcessValueAssignment(const CTokenList& rlstExpression)
{
CLog log("Process array value node...");
if (rlstExpression.End())
throw CCompileException(rlstExpression.LastValid(), "Expecting left curly bracket '{'");
// The array assignment is placed between brackets.
// In case the type is a character, the array might also be defined as a string literal
// Or identified as a value sequence.
if (rlstExpression.Current() == "{")
{
// Skip the bracket
++rlstExpression;
// Process the child nodes
ProcessChildNodes(rlstExpression);
// Expecting '}'
if (rlstExpression.End() || rlstExpression.Current() != "}")
throw CCompileException(rlstExpression.LastValid(), "Expecting right curly bracket '}'");
++rlstExpression;
}
else if (rlstExpression.Current().GetType() == ETokenType::token_literal && rlstExpression.Current().IsString())
{
// Process the string node
ProcessStringNode(rlstExpression.Current());
++rlstExpression;
}
else
throw CCompileException(rlstExpression.LastValid(), "Expecting left curly bracket '{'");
}
void CArrayValueNode::ProcessChildNodes(const CTokenList& rlstExpression)
{
CLog log;
CValueNodePtr ptrValueTemplateUnbound;
if (IsUnbound())
{
// The array should have one node. This is a template for all the nodes.
if (m_vecChildren.size() != 1)
throw CCompileException(rlstExpression.LastValid(), "Internal error: the size of the array value nodes is not as expected for the"
" unbound array.");
ptrValueTemplateUnbound = std::move(m_vecChildren[0]);
log << "Processing ubound element nodes..." << std::endl;
}
else
log << "Processing " << m_vecChildren.size() << " element nodes..." << std::endl;
size_t nIndex = 0;
while (!rlstExpression.End() && rlstExpression.Current() != "}")
{
// Check for a comma separator.
if (nIndex)
{
if (rlstExpression.Current() != ",")
throw CCompileException(rlstExpression.LastValid(), "Missing comma ',' for value separation.");
++rlstExpression;
}
// Extend the array for unbound
if (IsUnbound())
{
m_vecChildren.resize(nIndex + 1);
m_vecChildren[nIndex] = ptrValueTemplateUnbound->CreateCopy(m_ptrEntity, shared_from_this());
}
// Does the size fit?
if (nIndex >= m_vecChildren.size())
throw CCompileException(rlstExpression.LastValid(), "The assignment doesn't fit the amount of elements.");
log << "Processing element node #" << nIndex << std::endl;
m_vecChildren[nIndex]->ProcessValueAssignment(rlstExpression);
// Next node
nIndex++;
}
// Check whether the index corresponds to the size of the aray.
if (nIndex != m_vecChildren.size())
throw CCompileException(rlstExpression.LastValid(), "Missing elements for array assignment.");
}
void CArrayValueNode::ProcessStringNode(const CToken& rToken)
{
CLog log;
if (!rToken.IsString())
throw CCompileException(rToken, "Internal error: token is expected to be a string literal.");
// Check for unbound arrays.
CValueNodePtr ptrValueTemplateUnbound;
if (IsUnbound())
{
// The array should have one node. This is a template for all the nodes.
if (m_vecChildren.size() != 1)
throw CCompileException(rToken, "Internal error: the size of the array value nodes is not as expected for the"
" unbound array.");
ptrValueTemplateUnbound = std::move(m_vecChildren[0]);
log << "Processing ubound element nodes..." << std::endl;
}
else
log << "Processing " << m_vecChildren.size() << " element nodes..." << std::endl;
// Determine the start position of the string
const std::string ssValue = static_cast<std::string>(rToken);
size_t nStart = ssValue.find('"');
if (nStart == std::string::npos)
throw CCompileException(rToken, "Internal error: invalid string token.");
nStart++;
// For raw string, determine the delimiter
std::string ssDelimiter = "\"";
bool bRaw = false;
if (rToken.IsRawString())
{
bRaw = true;
size_t nStart2 = ssValue.find('(', nStart);
if (nStart2 == std::string::npos)
throw CCompileException(rToken, "Internal error: invalid raw string token.");
ssDelimiter = ")" + ssValue.substr(nStart, nStart2 - nStart) + "\"";
nStart = nStart2;
}
// Check whether the base type corresponds to the string and count the characters.
bool bError = true;
uint32_t uiByteCnt = 0;
std::vector<CConstVariant> lstValues;
switch (GetDeclEntity()->GetBaseType())
{
case sdv::idl::EDeclType::decltype_char:
{
if (!rToken.IsAscii() && !rToken.IsUtf8()) break;
bError = false;
std::string ssText;
InterpretCText(ssValue.c_str() + nStart, ssDelimiter.c_str(), ssText, uiByteCnt, bRaw, rToken.IsAscii());
for (char c : ssText)
lstValues.push_back(c);
break;
}
case sdv::idl::EDeclType::decltype_wchar:
{
if (!rToken.IsWide()) break;
bError = false;
std::wstring ssText;
InterpretCText(ssValue.c_str() + nStart, ssDelimiter.c_str(), ssText, uiByteCnt, bRaw);
for (wchar_t c : ssText)
lstValues.push_back(c);
break;
}
case sdv::idl::EDeclType::decltype_char16:
{
if (!rToken.IsUtf16()) break;
bError = false;
std::u16string ssText;
InterpretCText(ssValue.c_str() + nStart, ssDelimiter.c_str(), ssText, uiByteCnt, bRaw);
for (char16_t c : ssText)
lstValues.push_back(c);
break;
}
case sdv::idl::EDeclType::decltype_char32:
{
if (!rToken.IsUtf32()) break;
bError = false;
std::u32string ssText;
InterpretCText(ssValue.c_str() + nStart, ssDelimiter.c_str(), ssText, uiByteCnt, bRaw);
for (char32_t c : ssText)
lstValues.push_back(c);
break;
}
default:
throw CCompileException(rToken, "Expecting left curly bracket '{'");
break;
}
if (bError)
throw CCompileException(rToken, "Invalid string type for the assignment");
// Add the terminateing zero
lstValues.push_back(0);
// Extend the array for unbound
if (IsUnbound())
{
m_vecChildren.resize(lstValues.size());
for (size_t nIndex = 0; nIndex < lstValues.size(); nIndex++)
m_vecChildren[nIndex] = ptrValueTemplateUnbound->CreateCopy(m_ptrEntity, shared_from_this());
}
// Does the size fit?
if (lstValues.size() != m_vecChildren.size())
throw CCompileException(rToken, "The assignment doesn't fit the amount of elements.");
// Assign the values
for (size_t nIndex = 0; nIndex < m_vecChildren.size(); nIndex++)
{
log << "Processing element node #" << nIndex << std::endl;
CSimpleTypeValueNode* pValueNode = m_vecChildren[nIndex]->Get<CSimpleTypeValueNode>();
if (!pValueNode)
throw CCompileException(rToken, "Internal error: the array element is not of type simple value node.");
CTokenList lstExpression;
lstExpression.push_back(rToken);
pValueNode->SetFixedValue(lstValues[0], lstExpression);
}
}
void CArrayValueNode::SetFixedSize(size_t nSize, const CTokenList& rlstExpression)
{
CLog log;
if constexpr (std::is_signed_v<size_t>)
{
if (static_cast<int64_t>(nSize) < 0)
throw CCompileException("Internal error: array index is negative.");
}
if (m_eSizeDef != ESizeDef::not_defined)
throw CCompileException("Internal error: size of the value array was defined more than once.");
m_eSizeDef = ESizeDef::fixed;
// Allocate the array with empty value pointers.
m_vecChildren.resize(nSize);
log << "Array set to a fixed size of " << nSize << "..." << std::endl;
// Copy the token list resulting in the size.
m_lstArraySizeExpression = rlstExpression;
}
void CArrayValueNode::SetDynamicSize(size_t nSize, const CTokenList& rlstExpression)
{
if (IsConst()) throw CCompileException("Cannot use a value of a variable declaration as size within a const declaration.");
CLog log;
if (m_eSizeDef != ESizeDef::not_defined)
throw CCompileException("Internal error: size of the value array was defined more than once.");
m_eSizeDef = ESizeDef::dynamic;
// Allocate the array with one element.
m_vecChildren.resize(nSize);
log << "Array set to a dynamic size based on a variable of " << nSize << "..." << std::endl;
// Copy the token list resulting in the size.
m_lstArraySizeExpression = rlstExpression;
}
void CArrayValueNode::SetFixedSizeUnbound()
{
CLog log;
if (m_eSizeDef != ESizeDef::not_defined)
throw CCompileException("Internal error: size of the value array was defined more than once.");
m_eSizeDef = ESizeDef::fixed_unbound;
// Allocate the array with one element.
m_vecChildren.resize(1);
log << "Array set to be unbound..." << std::endl;
}
const CValueNodePtr& CArrayValueNode::operator[](size_t nIndex) const
{
if (m_eSizeDef == ESizeDef::not_defined)
throw CCompileException("Internal error: accessing the array can only take place after the size has been defined.");
if constexpr (std::is_signed_v<size_t>)
{
if (static_cast<int64_t>(nIndex) < 0)
throw CCompileException("Internal error: array index is negative.");
}
return m_vecChildren[nIndex];
}
CValueNodePtr& CArrayValueNode::operator[](size_t nIndex)
{
if (m_eSizeDef == ESizeDef::not_defined)
throw CCompileException("Internal error: accessing the array can only take place after the size has been defined.");
if constexpr (std::is_signed_v<size_t>)
{
if (static_cast<int64_t>(nIndex) < 0)
throw CCompileException("Internal error: array index is negative.");
}
return m_vecChildren[nIndex];
}
size_t CArrayValueNode::GetSize() const
{
return m_vecChildren.size();
}
bool CArrayValueNode::IsArray() const
{
return true;
}
bool CArrayValueNode::IsUnbound() const
{
return m_eSizeDef == ESizeDef::fixed_unbound;
}
std::string CArrayValueNode::GetSizeExpression() const
{
if (IsUnbound()) return std::string();
std::string ss;
for (const CToken& rtoken : m_lstArraySizeExpression)
ss += static_cast<std::string>(rtoken);
return ss;
}
std::string CArrayValueNode::GetDeclTypeStr(bool bResolveTypedef) const
{
if (!GetSize()) return "[]"; // Not resolved yet...
CValueNodePtr ptrValue = (*this)[0];
if (!ptrValue) return std::string("[" + std::to_string(GetSize()) + "]"); // Not resolved yet...
return ptrValue->GetDeclTypeStr(bResolveTypedef) + "[" + std::to_string(GetSize()) + "]";
}
CCompoundTypeValueNode::CCompoundTypeValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
CEntityValueNode(ptrEntity, ptrParent)
{}
CCompoundTypeValueNode::CCompoundTypeValueNode(const CCompoundTypeValueNode& rValueNode, CEntityPtr ptrEntity,
const CValueNodePtr ptrParent) : CEntityValueNode(rValueNode, ptrEntity, ptrParent)
{}
CValueNodePtr CCompoundTypeValueNode::CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const
{
return std::make_shared<std::decay_t<std::remove_pointer_t<decltype(this)>>>(*this, ptrEntity, ptrParent);
}
void CCompoundTypeValueNode::ProcessValueAssignment(const CTokenList& rlstExpression)
{
CLog log("Processing struct value node...");
if (rlstExpression.End())
throw CCompileException(rlstExpression.LastValid(), "Missing assignment");
if (!m_ptrEntity)
throw CCompileException(rlstExpression.LastValid(), "Internal error: a value node should be assigned to an entity.");
// There are two options: either a scoped name representing a type (or typedef) being of the same type (identical name), or a
// compound value (between curly brackets '{...}').
if (rlstExpression.Current() != "{") // Type value assignment
{
CValueNodePtr ptrValue = m_ptrEntity->FindValue(rlstExpression);
// Check if the original type of the assginee and the assigned are identical. For this both entities must be declarations.
if (!GetDeclEntity()->Get<CDeclarationEntity>())
throw CCompileException(rlstExpression.LastValid(), "Internal error: the assignee entity must be a declaration entity.");
// Assignment can only work if the types are identical. This is regardless of the typedefs that have been made from the
// type.
if (GetDeclTypeStr(true) != ptrValue->GetDeclTypeStr(true))
throw CCompileException(rlstExpression.LastValid(), "For '", GetDeclEntity()->GetName(), "', cannot assign '",
ptrValue->GetDeclTypeStr(true), "' to '", ptrValue->GetDeclTypeStr(true), "'; base types are different.");
// Replace the value by the value chain of the assigned to the assignee.
m_ptrEntity->ValueRef() = ptrValue->CreateCopy(m_ptrEntity, m_ptrParent);
} else // Compound value assignment
{
++rlstExpression; // Skip left bracket
// Process the child nodes
ProcessChildNodes(rlstExpression);
// Expecting '}'
if (rlstExpression.End() || rlstExpression.Current() != "}")
throw CCompileException(rlstExpression.LastValid(), "Expecting right curly bracket '}'");
++rlstExpression; // Skip right bracket
}
}
CInterfaceValueNode::CInterfaceValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
CEntityValueNode(ptrEntity, ptrParent)
{}
CInterfaceValueNode::CInterfaceValueNode(const CInterfaceValueNode& rValueNode, CEntityPtr ptrEntity,
const CValueNodePtr ptrParent) :
CEntityValueNode(rValueNode, ptrEntity, ptrParent)
{}
CValueNodePtr CInterfaceValueNode::CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const
{
return std::make_shared<std::decay_t<std::remove_pointer_t<decltype(this)>>>(*this, ptrEntity, ptrParent);
}
void CInterfaceValueNode::ProcessValueAssignment(const CTokenList& rlstExpression)
{
CLog log("Processing declaration value assignment...");
if (!m_ptrEntity) throw CCompileException("Internal error: no entity assigned to the value node.");
const CDeclarationEntity* pSystemTypeEntity = m_ptrEntity->Get<CDeclarationEntity>();
if (!pSystemTypeEntity)
throw CCompileException("Internal error: type mismatch between value node and entity type.");
// Only "null" and "0" are allowed.
if ((!m_ptrEntity->GetParserRef().GetEnvironment().InterfaceTypeExtension() || rlstExpression.Current() != "null") &&
rlstExpression.Current() != "0")
throw CCompileException(rlstExpression.Current(),
"Error: assignment of interface values is only possible with the value \"null\".");
// Skip the value.
++rlstExpression;
}
CValueNodePtr CCompoundTypeValueNode::Member(const std::string& rssName) const
{
// Check the entity names of the child value nodes.
// ATTENTION: The child value nodes are copies of the original definition. They still contain the link to the declarations
// that were part of the definition. The entity belonging to a declaration of a compound type does not have any children (the
// entity itself is just the declaration and not the definition):
for (const CValueNodePtr& rptrValue : m_vecChildren)
if (rptrValue->GetDeclEntity()->GetName() == rssName) return rptrValue;
return nullptr; // Value not found.
}
CEnumValueNode::CEnumValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) :
CEntityValueNode(ptrEntity, ptrParent)
{}
CEnumValueNode::CEnumValueNode(const CEnumValueNode& rValueNode, CEntityPtr ptrEntity,
const CValueNodePtr ptrParent) :
CEntityValueNode(rValueNode, ptrEntity, ptrParent), m_ptrEntryVal(rValueNode.m_ptrEntryVal),
m_lstExpression(rValueNode.m_lstExpression)
{}
CValueNodePtr CEnumValueNode::CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const
{
return std::make_shared<std::decay_t<std::remove_pointer_t<decltype(this)>>>(*this, ptrEntity, ptrParent);
}
void CEnumValueNode::ProcessValueAssignment(const CTokenList& rlstExpression)
{
CLog log("Processing enum value assignment...");
// The assignment is done using the enum entry type. Find the scoped name. This could be the enum value. Check the type first.
CTokenList rlstExpressionTemp = rlstExpression;
std::pair<std::string, CEntityPtr> prEntity = GetDeclEntity()->GetTypeEntity()->ProcessScopedName(rlstExpression, true);
// If not an enum value from the type, use as scope the parent of the current enum to search for other declarations.
if (!prEntity.second) prEntity = GetDeclEntity()->GetParentEntity()->ProcessScopedName(rlstExpressionTemp, true);
if (!prEntity.second) throw CCompileException(rlstExpression.LastValid(), "Expecting an enum value.");
// This could either be an assignment of an enum to another enum or an enum entry to an enum.
log << "The to be assigned type is '" << prEntity.second->GetDeclTypeStr(true) << "'..." << std::endl;
log << "The assignee type is '" << GetDeclEntity()->GetDeclTypeStr(true) << "'..." << std::endl;
if (prEntity.second->GetDeclTypeStr(true) == GetDeclEntity()->GetDeclTypeStr(true))
{
if (!prEntity.second->ValueRef())
throw CCompileException(rlstExpression.LastValid(), "Internal error: the enum doesn't have an assigned value.");
if (!prEntity.second->ValueRef()->Get<CEnumValueNode>())
throw CCompileException(rlstExpression.LastValid(), "Internal error: the enum doesn't have an assigned enum value.");
m_ptrEntryVal = prEntity.second->ValueRef()->Get<CEnumValueNode>()->m_ptrEntryVal;
} else if (prEntity.second->GetParentEntity()->GetDeclTypeStr(true) == GetDeclEntity()->GetDeclTypeStr(true))
{
log << "Assigning enum entry..." << std::endl;
m_ptrEntryVal = prEntity.second;
} else
throw CCompileException(rlstExpression.LastValid(), "Invalid enum value; enum types are not identical.");
log << "Enum entity was assigned value '" << m_ptrEntryVal->GetParentEntity()->GetName() << "::" <<
m_ptrEntryVal->GetName() << "'..." << std::endl;
}
const CConstVariant& CEnumValueNode::Variant() const
{
static CConstVariant temp;
if (!m_ptrEntryVal) return temp;
if (!m_ptrEntryVal->ValueRef()) return temp;
const CSimpleTypeValueNode* pValueNode = m_ptrEntryVal->ValueRef()->Get<CSimpleTypeValueNode>();
if (!pValueNode) return temp;
return pValueNode->Variant();
}
std::string CEnumValueNode::String() const
{
if (!m_ptrEntryVal) return std::string();
return m_ptrEntryVal->GetParentEntity()->GetScopedName() + "::" + m_ptrEntryVal->GetName();
}

View File

@@ -0,0 +1,584 @@
#ifndef ENTITIY_VALUE_H
#define ENTITIY_VALUE_H
#include "entity_base.h"
#include "../constvariant.h"
#include "../token.h"
#include "../tokenlist.h"
#include <vector>
#include <memory>
// Forward declarations
class CDeclarationEntity;
/**
* @brief The entity value node base class.
* @details Entity values form a value tree made of all the values that are part of one assignment. Each value has its special
* value grammar when used in a assignment. All entities have their own personal linked list, which can be formed from other
* entities when they are defined as type of a declaration. Variable declarations can also be part of a larger linked list. Arrays
* form their own links within the linked list
* Examples:
* @code
* // Value tree for i: CSimpleTypeValueNode assigning the value 10.
* int32 i = 10;
*
* // Value tree for S1: CCompoundTypeValueNode with child CSimpleTypeValueNode assigning the value 20.
* struct S1
* {
* const int32 m_i1 = 10; // Value node for m_i1: CSimpleTypeValueNode assigning the value 10.
* int32 m_i2 = 20; // Part of the value tree of S1.
* };
*
* // Value tree for s1: sub-tree of S1 assigning the value 200 to m_i2.
* S1 s1 = {200};
*
* // Value tree for S2: CCompoundTypeValueNode with children sub-tree of S1 and CSimpleTypeValueNode assigning the value 5.
* struct S2 : S1
* {
* int32 m_i3 = 5; // Part of value tree of S2.
* };
*
* // Value tree for s2: sub-tree of S2 assigning the value 150 to S1 and 100 to m_i3.
* s2 = {{150}, 100};
*
* // Value tree for rgi: CArrayValueNode having two children assigning the value 10 and 20.
* int32 rgi[2] = {10, 20};
*
* // Value tree for rgs3: CarrayValueNode having two children S1 assigning the values 1000 and 2000.
* S1 rgs3[2] = {{1000}, {2000}};
* @endcode
*/
class CEntityValueNode : public std::enable_shared_from_this<CEntityValueNode>
{
public:
/**
* @brief Constructor
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Can only be nullptr for const and declaration entities or when
* creating a value node for a definition which will be copied in a larger declaration at a later stage.
*/
CEntityValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Copy constructor with new parent and entity.
* @param[in] rValueNode Reference to the value node to copy from.
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CEntityValueNode(const CEntityValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Default destructor
* @remarks This constructor needs to be virtual to allow derived classes to be destroyed correctly.
*
*/
virtual ~CEntityValueNode() = default;
/**
* @brief Create a copy of the value. Prototype to be implemented from derived class.
* @param[in] ptrEntity Smart pointer to the entity receiving the copy of the value node.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
* @return Returns a smart pointer of the copy of the value.
*/
virtual CValueNodePtr CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const = 0;
/**
* @brief Process the assignment. Prototype, to be implemented by derived value class.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessValueAssignment(const CTokenList& rlstExpression) = 0;
/**
* @brief Process the child nodes.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessChildNodes(const CTokenList& rlstExpression);
/**
* @{
* @brief Get the derived value class.
* @tparam TValue The value class to request a pointer for.
* @return Returns a pointer to the derived class or NULL when the requested class didn't derive from this value.
*/
template <class TValue>
TValue* Get();
template <class TValue>
const TValue* Get() const;
/**
* @}
*/
/**
* @brief Add a child node
* @param[in] ptrChild Pointer to the child value node.
*/
void AddChild(CValueNodePtr ptrChild);
/**
* @brief Get the parent node.
* @return Returns a reference to the smart pointer of the parent node (if assigned).
*/
const CValueNodePtr& GetParentNode() const { return m_ptrParent; }
/**
* @{
* @brief Get the attached declaration entity.
* @return Returns the smart pointer to the entity.
*/
const CDeclarationEntity* GetDeclEntity() const { return m_ptrEntity->Get<CDeclarationEntity>(); }
CDeclarationEntity* GetDeclEntity() { return m_ptrEntity->Get<CDeclarationEntity>(); }
/**
* @}
*/
/**
* @brief Get the declaration type build from the value chain.
* @details The declaration type consists of "<base type> <type identifier> <arrays>".
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with entity type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const;
/**
* @{
* @brief Information functions.
* @details Each function is implemented by an entity value class. The IsConst function traverses through the parents to find
* out whether or not a value is a const value (this is the case when the entity holding the top most value is a const
* entity.)
* @return Returns the information.
*/
virtual bool IsArray() const { return false; }
virtual bool IsUnbound() const { return false; }
virtual bool IsConst() const;
virtual bool IsDeclaration() const;
virtual bool IsDynamic() const { return false; }
virtual bool IsLiteral() const { return false; }
virtual bool IsComplex() const { return false; }
virtual bool HasParent() const{ return m_ptrParent ? true : false; }
virtual bool HasChildren() const{ return !m_vecChildren.empty(); }
/**
* @}
*/
protected:
CEntityPtr m_ptrEntity; ///< The entity implementing the value type.
const CValueNodePtr m_ptrParent; ///< Parent value node - can be nullptr for root node.
std::vector<CValueNodePtr> m_vecChildren; ///< Child nodes.
};
/**
* @brief Entity declaration value (used as const variable assignment as well as default declaration assignment).
*/
class CSimpleTypeValueNode : public CEntityValueNode
{
public:
/**
* @brief Constructor
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Can only be nullptr for upper most value level.
*/
CSimpleTypeValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Copy constructor with new parent and entity.
* @param[in] rValueNode Reference to the value node to copy from.
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CSimpleTypeValueNode(const CSimpleTypeValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Default destructor
* @remarks This constructor needs to be virtual to allow derived classes to be destroyed correctly.
*
*/
virtual ~CSimpleTypeValueNode() override = default;
/**
* @brief Create a copy of the value. Overload of CEntityValueNode::CreateCopy.
* @param[in] ptrEntity Smart pointer to the entity receiving the copy of the value node.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
* @return Returns a smart pointer of the copy of the value.
*/
virtual CValueNodePtr CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const override;
/**
* @brief Process the assignment. Overload of CEntityValueNode::ProcessValueAssignment.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessValueAssignment(const CTokenList& rlstExpression) override;
/**
* @brief Set to a fixed value.
* @remarks In case the entity is not a const entity, the value is considered to be a default value.
* @param[in] rvarValue Reference to the fixed value.
* @param[in] rlstExpression Reference to the expression list used to calculate the value.
*/
void SetFixedValue(const CConstVariant& rvarValue, const CTokenList& rlstExpression);
/**
* @brief Set to a dynamic value
* @remarks This function can only be called when the entity of any of the parent entities is not a const entity.
* @param[in] rlstExpression Reference to the expression list used to calculate the value.
*/
void SetDynamicValue(const CTokenList& rlstExpression);
/**
* @brief Is the value defined?
* @return Returns 'true' when the value is defined; 'false' otherwise.
*/
virtual bool IsDefined() const { return m_eValueDef != EValueDef::not_defined; }
/**
* @brief Is this value dynamic (will it be defined through a non-const variable definition)?
* @return Returns 'true' when the value is dynamic; 'false' otherwise.
*/
virtual bool IsDynamic() const override { return m_eValueDef != EValueDef::dynamic; }
/**
* @brief Get access to the underlying const variant.
* @return The const variant storing the calculated value.
*/
const CConstVariant &Variant() const { return m_varValue; }
private:
/**
* @brief The definition of the size is either fixed or not fixed. When it is fixed, it could be derived from assigning its
* values.
*/
enum class EValueDef
{
not_defined, ///< Currently not defined yet.
fixed, ///< The value is fixed; could be used for const and declaration entities.
dynamic, ///< The value is dependable on a variable declaration. Only to be used with a declaration entity.
};
CConstVariant m_varValue; ///< The calculated value.
CTokenList m_lstExpression; ///< List of tokens holding the value expression.
EValueDef m_eValueDef = EValueDef::not_defined; ///< Dynamic value based on variables.
};
/**
* @brief Entity array value (containing an array of value pointers).
* @remarks When the array contains non-allocated value pointers, the array has been declared, but the values haven't been
* assigned.
*/
class CArrayValueNode : public CEntityValueNode
{
public:
/**
* @brief Constructor
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CArrayValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Copy constructor with new parent and entity.
* @param[in] rValueNode Reference to the value node to copy from.
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CArrayValueNode(const CArrayValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Default destructor
* @remarks This constructor needs to be virtual to allow derived classes to be destroyed correctly.
*
*/
virtual ~CArrayValueNode() override = default;
/**
* @brief Create a copy of the value. Overload of CEntityValueNode::CreateCopy.
* @param[in] ptrEntity Smart pointer to the entity receiving the copy of the value node.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
* @return Returns a smart pointer of the copy of the value.
*/
virtual CValueNodePtr CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const override;
/**
* @brief Process the assignment. Overload of CEntityValueNode::ProcessValueAssignment.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessValueAssignment(const CTokenList& rlstExpression) override;
/**
* @brief Process the child nodes. Overload of CEntityValueNode::ProcessChildNodes.
* @param[in] rlstExpression Reference to the expression token list.
*/
void ProcessChildNodes(const CTokenList& rlstExpression) override;
/**
* @brief Process the string nodes. This is an array processing function based on a string as character array.
* @param[in] rToken Reference to the string literal token.
*/
void ProcessStringNode(const CToken& rToken);
/**
* @brief Set the fixed size of the array when available during declaration.
* @attention Must not be called after the value has been set to dynamic or set to a delayed fixed size.
* @param[in] nSize The calculated size of the array.
* @param[in] rlstExpression Reference to the expression list used to calculate the value.
*/
void SetFixedSize(size_t nSize, const CTokenList& rlstExpression);
/**
* @brief Set the array to a dynamic size based on a variable declaration. This is only allowed for entities that are not
* declared as const entity (where all the parent values are not defined as const).
* @attention Must not be called after the value has been set to a fixed size (delayed or not).
* @param[in] nSize The calculated size of the array.
* @param[in] rlstExpression Reference to the expression list used to calculate the size.
*/
void SetDynamicSize(size_t nSize, const CTokenList& rlstExpression);
/**
* @brief Set the array to a fixed size through its value assignment. This is only allowed for entities that are declared as
* const entity (where one of the parents was defined as const).
* @attention Must not be called after the value has been set to a fixed size (not delayed) or a dynamic size.
*/
void SetFixedSizeUnbound();
/**
* @{
* @brief Element access of the array.
* @param[in] nIndex The index of the element. For dynamic arrays, only an index of 0 is allowed.
* @return Returns a reference to the element.
*/
const CValueNodePtr& operator[](size_t nIndex) const;
CValueNodePtr& operator[](size_t nIndex);
/**
* @}
*/
/**
* @brief Get the size of the array.
* @return The size of the array.
*/
size_t GetSize() const;
/**
* @brief This is an array entity.
* @return Returns 'true'.
*/
virtual bool IsArray() const override;
/**
* @brief Is the size of the array defined through the assignment?
* @return Returns 'true' when the size of the array has to be defined through the assignment.
*/
virtual bool IsUnbound() const override;
/**
* @brief Get the size expression.
* @return The size expression string.
*/
std::string GetSizeExpression() const;
/**
* @brief Get the declaration type build from the value chain. Overload of CEntityValueNode::GetDeclTypeStr.
* @details The declaration type consists of "<base type> <type identifier> <arrays>".
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with entity type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
private:
/**
* @brief The definition of the size is either fixed or not fixed. When it is fixed, it could be derived from assigning its
* values.
*/
enum class ESizeDef
{
not_defined, ///< Currently not defined yet.
fixed, ///< The size is fixed; could be used for const and declaration entities.
dynamic, ///< The size is dependable on a variable declaration. Only to be used with a declaration entity.
fixed_unbound, ///< The size is fixed, but gets defined during value assignment. Only to be used with a const
///< entity.
};
CTokenList m_lstArraySizeExpression; ///< List of tokens holding the array size expression.
ESizeDef m_eSizeDef = ESizeDef::not_defined; ///< The size definition.
};
/**
* @brief Entity struct value containing two arrays of value pointers, one array for the member const declarations and one array
* for the member variable declarations.
*/
class CCompoundTypeValueNode : public CEntityValueNode
{
public:
/**
* @brief Constructor
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Can only be nullptr when creating a value node for a definition
* which will be copied in a larger declaration at a later stage.
*/
CCompoundTypeValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Copy constructor with new parent and entity.
* @param[in] rValueNode Reference to the value node to copy from.
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CCompoundTypeValueNode(const CCompoundTypeValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Default destructor
* @remarks This constructor needs to be virtual to allow derived classes to be destroyed correctly.
*
*/
virtual ~CCompoundTypeValueNode() override = default;
/**
* @brief Create a copy of the value. Overload of CEntityValueNode::CreateCopy.
* @param[in] ptrEntity Smart pointer to the entity receiving the copy of the value node.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
* @return Returns a smart pointer of the copy of the value.
*/
virtual CValueNodePtr CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const override;
/**
* @brief Process the assignment. Overload of CEntityValueNode::ProcessValueAssignment.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessValueAssignment(const CTokenList& rlstExpression) override;
/**
* @brief Return the value for the supplied member.
* @param[in] rssName Name Reference to the string object containing the name of the member.
* @return Returns the value node smart pointer of the requested member, or nullptr when member doesn't have a value or
* doesn't exist.
*/
CValueNodePtr Member(const std::string& rssName) const;
};
/**
* @brief Interface value node.
* @note The only assignable values of an interface are "null" and "0". No variable is allowed to be assigned and an interface
* cannot be const.
*/
class CInterfaceValueNode : public CEntityValueNode
{
public:
/**
* @brief Constructor
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Can only be nullptr for upper most value level.
*/
CInterfaceValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Copy constructor with new parent and entity.
* @param[in] rValueNode Reference to the value node to copy from.
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CInterfaceValueNode(const CInterfaceValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Default destructor
* @remarks This constructor needs to be virtual to allow derived classes to be destroyed correctly.
*
*/
virtual ~CInterfaceValueNode() override = default;
/**
* @brief Create a copy of the value. Overload of CEntityValueNode::CreateCopy.
* @param[in] ptrEntity Smart pointer to the entity receiving the copy of the value node.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
* @return Returns a smart pointer of the copy of the value.
*/
virtual CValueNodePtr CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const override;
/**
* @brief Process the assignment. Overload of CEntityValueNode::ProcessValueAssignment.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessValueAssignment(const CTokenList& rlstExpression) override;
/**
* @brief Is this value dynamic (will it be defined through a non-const variable definition)? This is always the case.
* @return Returns 'true' when the value is dynamic; 'false' otherwise.
*/
virtual bool IsDynamic() const override { return true; }
};
/**
* @brief Enum entity declaration value.
*/
class CEnumValueNode : public CEntityValueNode
{
public:
/**
* @brief Constructor
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Can only be nullptr for upper most value level.
*/
CEnumValueNode(CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Copy constructor with new parent and entity.
* @param[in] rValueNode Reference to the value node to copy from.
* @param[in] ptrEntity Smart pointer to the entity implementing the type. Cannot be nullptr.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
*/
CEnumValueNode(const CEnumValueNode& rValueNode, CEntityPtr ptrEntity, const CValueNodePtr ptrParent);
/**
* @brief Default destructor
* @remarks This constructor needs to be virtual to allow derived classes to be destroyed correctly.
*
*/
virtual ~CEnumValueNode() override = default;
/**
* @brief Create a copy of the value. Overload of CEntityValueNode::CreateCopy.
* @param[in] ptrEntity Smart pointer to the entity receiving the copy of the value node.
* @param[in] ptrParent Smart pointer to the parent value. Cannot be nullptr.
* @return Returns a smart pointer of the copy of the value.
*/
virtual CValueNodePtr CreateCopy(CEntityPtr ptrEntity, const CValueNodePtr ptrParent) const override;
/**
* @brief Process the assignment. Overload of CEntityValueNode::ProcessValueAssignment.
* @param[in] rlstExpression Reference to the expression token list.
*/
virtual void ProcessValueAssignment(const CTokenList& rlstExpression) override;
/**
* @brief Is the value defined?
* @return Returns 'true' when the value is defined; 'false' otherwise.
*/
virtual bool IsDefined() const { return m_ptrEntryVal ? true : false; }
/**
* @brief Get access to the assigned const variant of the enum entry.
* @return The const variant storing the value.
*/
const CConstVariant& Variant() const;
/**
* @brief Get access to the assigned name string of the enum entry.
* @return The name string of the enum entry.
*/
std::string String() const;
private:
CEntityPtr m_ptrEntryVal; ///< The enum entry value when assigned.
CTokenList m_lstExpression; ///< List of tokens holding the value expression.
};
template <class TValue>
TValue* CEntityValueNode::Get()
{
return dynamic_cast<TValue*>(this);
}
template <class TValue>
const TValue* CEntityValueNode::Get() const
{
return dynamic_cast<const TValue*>(this);
}
#endif // !define(ENTITY_VALUE_H)

View File

@@ -0,0 +1,213 @@
#include "enum_entity.h"
#include "entity_value.h"
#include "typedef_entity.h"
#include "../exception.h"
#include <set>
CEnumEntry::CEnumEntry(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CDeclarationEntity(rptrContext, ptrParent)
{}
std::string CEnumEntry::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return GetScopedName();
}
void CEnumEntry::Process()
{
// Get the base type of the enum entity and insert it in front of the declaration.
const CEnumEntity* pEnumEntity = GetParentEntity()->Get<CEnumEntity>();
if (!pEnumEntity) throw CCompileException("Internal error: expected an enum entity as parent.");
CToken token(DeclTypeToString(pEnumEntity->GetEnumType()));
PrependToken(token);
// Process as if normal declaration
CDeclarationEntity::Process();
}
CEnumEntity::CEnumEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CDefinitionEntity(rptrContext, ptrParent)
{
m_typedecl.SetBaseType(sdv::idl::EDeclType::decltype_long);
}
sdv::interface_t CEnumEntity::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IEnumEntity>())
return static_cast<sdv::idl::IEnumEntity*>(this);
return CDefinitionEntity::GetInterface(idInterface);
}
std::string CEnumEntity::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return std::string("enum ") + GetScopedName();
}
void CEnumEntity::GetBaseType(sdv::idl::EDeclType& reType, sdv::IInterfaceAccess*& rpType) const
{
reType = m_typedecl.GetBaseType();
rpType = m_typedecl.GetTypeDefinition();
}
void CEnumEntity::Process()
{
// The definition and declaration can be defined:
// enum <enum_identifier>; --> forward declaration
// enum <enum_identifier> {...}; --> enum definition
// <enum_identifier> <decl_identifier>; --> enum variable declaration
// <enum_identifier> <decl_identifier> = <enum_value>; --> enum variable declaration and assignment
// enum <enum_identifier> <decl_identifier>; --> enum variable declaration
// enum <enum_identifier> <decl_identifier> = <enum_value>; --> enum variable declaration and assignment
// enum <enum_identifier> {...} <decl_identifier>; --> enum definition and variable declaration
// enum <enum_identifier> {...} <decl_identifier> = <enum_value>; --> enum definition, variable declaration and assignment
// enum {...} <decl_identifier>; --> anonymous enum definition and variable declaration
// enum {...} <decl_identifier> = <enum_value>; --> anonymous enum definition, variable declaration and assignment
// const enum <enum_identifier> <decl_identifier> = <enum_value>;
// const <enum_identifier> <decl_identifier> = <enum_value>;
// const enum {...} <decl_identifier> = <enum_value>;
// typedef <enum_identifier> <type_identifier>;
// typedef enum <enum_identifier> {...} <type_identifier>;
// typedef <enum_identifier> <type_identifier>;
// typedef enum <enum_identifier> {...} <type_identifier>;
// typedef enum {...} <type_identifier>;
CDefinitionEntity::Process();
// If supported create the value node for the definition (this allows assignments of values to this entity).
CreateValueNode();
}
void CEnumEntity::ProcessDefinitionAddendum()
{
// Check for inheritance
CToken token = GetToken();
if (token != ":")
{
PrependToken(token);
return;
}
CLog log("Processing inheritance type...");
// Process the type
m_typedecl = ProcessType();
// The base type must be an integral type and not an enum.
if (!IsIntegralDeclType(m_typedecl.GetBaseType()) && m_typedecl.GetBaseType() != sdv::idl::EDeclType::decltype_enum)
throw CCompileException("Expecting an integral type to inherit from.");
}
void CEnumEntity::ProcessContent()
{
CLog log("Processing definition content...");
// An enum definition consists of one declaration statement with entries separated by a comma.
CToken token = PeekToken();
if (!token) return;
if (token == "}") return;
// Expecting an enum entry
CEntityPtr ptrEntry = CreateChild<CEnumEntry>(token.GetContext(), this);
if (!ptrEntry) throw CCompileException("Internal error: could not create enum entry.");
ptrEntry->Process();
// The entries should reside in the const members list
// Check the values and create values where none is declared.
// Separate between signed and unsigned.
int64_t iSignedNext = 0;
std::set<int64_t> setSigned;
uint64_t uiUnsignedNext = 0;
std::set<uint64_t> setUnsigned;
for (CEntityPtr ptrMember : m_lstConstMembers)
{
CEnumEntry* pEntry = ptrMember->Get<CEnumEntry>();
if (!pEntry) throw CCompileException("Internal error: expected only enum entries in an enumeration.");
// Do post processing
pEntry->PostProcess();
// Get the value from the entity; if one was assigned.
CSimpleTypeValueNode* pValue = pEntry->ValueRef() ? pEntry->ValueRef()->Get<CSimpleTypeValueNode>() : nullptr;
if (!pValue)
throw CCompileException("The value for '", ptrEntry->GetName(), "' must be a system type value.");
// Deal with signed and unsigned values
if (IsSignedDeclType(m_typedecl.GetBaseType()))
{
// Is a value assigned?
if (pValue->IsDefined())
{
// Check whether the value is already in use.
int64_t iValue = pValue->Variant().Get<int64_t>();
if (setSigned.find(iValue) != setSigned.end())
throw CCompileException("The value for '", ptrEntry->GetName(), "' is already defined for another entry.");
// Store the value
setSigned.insert(iValue);
iSignedNext = iValue + 1;
}
else
{
// Get the next available value
while (setSigned.find(iSignedNext) != setSigned.end())
iSignedNext++;
// Create a value assignment
CTokenList lstValueTokens;
lstValueTokens.push_back(CToken(std::to_string(iSignedNext), ETokenLiteralType::token_literal_dec_integer));
pValue->SetFixedValue(iSignedNext, lstValueTokens);
// Store the value
setSigned.insert(iSignedNext);
iSignedNext++;
}
}
else
{
// Is a value assigned?
if (pValue->IsDefined())
{
// Check whether the value is already in use.
uint64_t uiValue = pValue->Variant().Get<uint64_t>();
if (setUnsigned.find(uiValue) != setUnsigned.end())
throw CCompileException("The value for '", ptrEntry->GetName(), "' is already defined for another entry.");
// Store the value
setUnsigned.insert(uiValue);
uiUnsignedNext = uiValue + 1;
}
else
{
// Get the next available value
while (setUnsigned.find(uiUnsignedNext) != setUnsigned.end())
uiUnsignedNext++;
// Create a value assignment
CTokenList lstValueTokens;
lstValueTokens.push_back(CToken(std::to_string(uiUnsignedNext), ETokenLiteralType::token_literal_dec_integer));
pValue->SetFixedValue(uiUnsignedNext, lstValueTokens);
// Store the value
setUnsigned.insert(uiUnsignedNext);
iSignedNext++;
}
}
}
}
bool CEnumEntity::Supports(EDefinitionSupport eSupport) const
{
switch (eSupport)
{
case EDefinitionSupport::support_enum_entry: return true;
default: return false;
}
}
void CEnumEntity::CreateValueNode()
{
// Create a simple type value node for this definition.
ValueRef() = std::make_shared<CEnumValueNode>(shared_from_this(), nullptr);
}

View File

@@ -0,0 +1,178 @@
#ifndef ENUM_ENTITY_H
#define ENUM_ENTITY_H
#include "definition_entity.h"
#include "declaration_entity.h"
#include "variable_entity.h"
/**
* @brief The enum entry declaration.
*/
class CEnumEntry : public CDeclarationEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CEnumEntry(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Destructor
*/
virtual ~CEnumEntry() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns enum entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_enum_entry; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with enum type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Does the entity support assignments? Overload of CDeclarationEntity::SupportAssignments.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportAssignments() const override { return true; }
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Is the entity readonly? Overload of IEntityInfo::IsReadOnly.
* @return Returns 'true' when the entity defined as readonly; 'false' otherwise.
*/
virtual bool IsReadOnly() const override { return true; }
/**
* @brief Does the entity support multiple declarations on one line of code? Overload of
* CDeclarationEntity::SupportMultipleDeclarations.
* @return Returns 'true' when the entity supports multiple declarations; 'false' otherwise.
*/
virtual bool SupportMultipleDeclarations() const override { return true; }
/**
* @brief Do not enforce next declaration after comma (enums do)? Overload of
* CDeclarationEntity::DoNotEnfoceNextDeclarationAfterComma.
* @return Returns 'true' when not enforcing the next declaration; 'false' otherwise.
*/
virtual bool DoNotEnfoceNextDeclarationAfterComma() const override { return true; }
};
/**
* @brief The enum definition of an IDL file.
* @details The enum section of the IDL file contains a list of enum value entries
*/
class CEnumEntity : public CDefinitionEntity, public sdv::idl::IEnumEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CEnumEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Destructor
*/
virtual ~CEnumEntity() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns enum entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_enum; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with enum type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
/**
* @brief Get the enumerator base type. Overload of sdv::idl::IEnumEntity.
* @param[out] reType Reference to the declaration type. The type if EEntityType::type_unknown if not available.
* @param[out] rpType Reference to the interface pointer if the type is a complex type. Otherwise is NULL.
*/
virtual void GetBaseType(sdv::idl::EDeclType& reType, sdv::IInterfaceAccess*& rpType) const override;
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Process the content of the definition.
*/
virtual void ProcessContent() override;
/**
* @brief Process the definition addendum (following the definition statement before the content definition).
*/
virtual void ProcessDefinitionAddendum() override;
/**
* @brief Get the enum type.
* @return Returns the underlying enum type.
*/
sdv::idl::EDeclType GetEnumType() const { return m_typedecl.GetBaseType(); }
/**
* @brief Request whether the definition supports the content. Overload of CDefintionEntity::Supports.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const override;
/**
* @brief Does the entity support children? Overload of CEntity::SupportsChildren.
* @details The struct supports children.
* @return Returns whether the entity supports children (which is supported).
*/
virtual bool SupportsChildren() const override { return true; }
/**
* @brief Does the entity support anonymous naming?
* @details The default implementation is that anonymous naming is not supported.
* @return Returns whether the entity supports inheritance (which is supported).
*/
virtual bool SupportsAnonymous() const override { return true; }
/**
* @brief Does the entity support inheritance?
* @details The default implementation is that inheritance is not supported.
* @return Returns whether the entity supports inheritance (which is supported).
*/
virtual bool SupportsInheritance() const override { return true; }
/**
* @brief Create the content value node. Overload of CDefinitionEntity::CreateValueNode.
* @details Create the value node and assign the value node to the ValueRef() reference..
*/
virtual void CreateValueNode() override;
private:
CTypeDeclaration m_typedecl; ///< The base type of the enum.
};
#endif // !defined(ENUM_ENTITY_H)

View File

@@ -0,0 +1,40 @@
#include "exception_entity.h"
#include "../exception.h"
#include "../logger.h"
#include "typedef_entity.h"
#include "variable_entity.h"
#include <iostream>
CExceptionEntity::CExceptionEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CStructEntity(rptrContext, ptrParent)
{}
std::string CExceptionEntity::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return std::string("exception ") + GetScopedName();
}
void CExceptionEntity::Process()
{
CStructEntity::Process();
}
bool CExceptionEntity::Supports(EDefinitionSupport eSupport) const
{
switch (eSupport)
{
case EDefinitionSupport::support_variable: return true;
case EDefinitionSupport::support_const_variable: return true;
case EDefinitionSupport::support_typedef: return true;
case EDefinitionSupport::support_struct: return true;
case EDefinitionSupport::support_union: return true;
case EDefinitionSupport::support_enum: return true;
default: return false;
}
}
void CExceptionEntity::CreateValueNode()
{
// Create a compound type value node for this definition.
ValueRef() = std::make_shared<CCompoundTypeValueNode>(shared_from_this(), nullptr);
}

View File

@@ -0,0 +1,62 @@
#ifndef EXCEPTION_ENTITY_H
#define EXCEPTION_ENTITY_H
#include "definition_entity.h"
#include "struct_entity.h"
/**
* @brief The struct definition of an IDL file.
* @details The struct section of the IDL file contains multiple declarations of members, as well as the definitions of structs
* and unions.
*/
class CExceptionEntity : public CStructEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CExceptionEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Destructor
*/
virtual ~CExceptionEntity() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return the exception entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_exception; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with exception type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Request whether the definition supports the content. Overload of CDefintionEntity::Supports.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const override;
/**
* @brief Create the content value node. Overload of CDefinitionEntity::CreateValueNode.
* @details Create the value node and assign the value node to the ValueRef() reference..
*/
virtual void CreateValueNode() override;
};
#endif // !defined(EXCEPTION_ENTITY_H)

View File

@@ -0,0 +1,39 @@
#include "hash_calc.h"
CHashObject::CHashObject()
{
// Create a xxHash state
m_state = XXH64_createState();
/* Initialize state with selected seed */
XXH64_hash_t seed = 0; /* or any other value */
if (XXH64_reset(m_state, seed) == XXH_ERROR)
{
XXH64_freeState(m_state);
m_state = nullptr;
}
}
CHashObject::~CHashObject()
{
if (m_state) XXH64_freeState(m_state);
}
CHashObject& CHashObject::operator<<(const std::string& rssString)
{
if (!m_state) return *this;
if (rssString.empty()) return *this;
if (XXH64_update(m_state, rssString.c_str(), rssString.size()) == XXH_ERROR)
{
XXH64_freeState(m_state);
m_state = nullptr;
}
return *this;
}
uint64_t CHashObject::GetHash() const
{
if (!m_state) return 0;
XXH64_hash_t hash = XXH64_digest(m_state);
return hash;
}

View File

@@ -0,0 +1,52 @@
#ifndef HASH_CALC_H
#define HASH_CALC_H
#ifdef _MSC_VER
// Prevent warnings for XXHash during static code analysis.
#pragma warning(push)
#pragma warning(disable : 26812 26451)
#endif
#define XXH_INLINE_ALL
#include <xxhash.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <string>
/**
* @brief Calculate a hash value
*/
class CHashObject
{
public:
/**
* @brief Constructor
*/
CHashObject();
/**
* @brief Destructor
*/
~CHashObject();
/**
* @brief Shift operator adding a string to the hash.
* @param[in] rssString Reference to the string.
* @return
*/
CHashObject& operator<<(const std::string& rssString);
/**
* @brief Get the current hash value.
* @return The hash value.
*/
uint64_t GetHash() const;
private:
XXH64_state_t* m_state = nullptr; ///< Hash state
};
#endif // !defined(HASH_CALC_H)

View File

@@ -0,0 +1,85 @@
#include "interface_entity.h"
#include "../exception.h"
#include "../logger.h"
#include "typedef_entity.h"
#include "variable_entity.h"
#include <iostream>
CInterfaceEntity::CInterfaceEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bIsLocal) :
CDefinitionEntity(rptrContext, ptrParent), m_bIsLocal(bIsLocal)
{}
sdv::interface_t CInterfaceEntity::GetInterface(sdv::interface_id idInterface)
{
// Expose interfaces
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::idl::IInterfaceEntity*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IInterfaceEntity>())
return static_cast<sdv::idl::IInterfaceEntity*>(this);
return CDefinitionEntity::GetInterface(idInterface);
}
std::string CInterfaceEntity::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return std::string("interface ") + GetScopedName();
}
void CInterfaceEntity::Process()
{
// The definition and declaration can be defined:
// struct <struct_identifier>; --> forward declaration
// struct <struct_identifier> {...}; --> struct definition
// struct <struct_identifier> : <base_struct,...> {...}; --> struct definition with inheritance
// <struct_identifier> <decl_identifier>; --> struct variable declaration
// <struct_identifier> <decl_identifier> = {...}; --> struct variable declaration and assignment
// struct <struct_identifier> <decl_identifier>; --> struct variable declaration
// struct <struct_identifier> <decl_identifier> = {...}; --> struct variable declaration and assignment
// struct <struct_identifier> {...} <decl_identifier>; --> struct definition and variable declaration
// struct <struct_identifier> : <base_struct,...> {...} <decl_identifier>; --> struct definition with inheritance and variable declaration
// struct <struct_identifier> {...} <decl_identifier> = {...}; --> struct definition, variable declaration and assignment
// struct <struct_identifier> : <base_struct,...> {...} <decl_identifier> = {...}; --> struct definition with inheritance, variable declaration and assignment
// struct {...} <decl_identifier>; --> anonymous struct definition and variable declaration
// struct : <base_struct,...> {...} <decl_identifier>; --> anonymous struct definition with inheritance and variable declaration
// struct {...} <decl_identifier> = {...}; --> anonymous struct definition, variable declaration and assignment
// struct : <base_struct,...> {...} <decl_identifier> = {...}; --> anonymous struct definition with inheritance, variable declaration and assignment
// typedef <struct_identifier> <type_identifier>;
// typedef struct <struct_identifier> { ... } <type_identifier>;
// typedef <struct_identifier> <type_identifier>;
// typedef struct <struct_identifier> { ... } <type_identifier>;
// typedef struct { ... } <type_identifier>;
// const struct <struct_identifier> <decl_identifier> = {...};
// const <struct_identifier> <decl_identifier> = {...};
// const struct {...} <decl_identifier> = {...};
// The declaration is as follows:
// <struct_identifier> <decl_identifier>;
// struct <struct_identifier> <decl_identifier>;
// struct <struct_identifier> { ... } <decl_identifier>;
// TODO Enforce inheritance from sdv::IInterfaceAccess unless the interface is sdv::IInterfaceAccess.
CDefinitionEntity::Process();
}
bool CInterfaceEntity::Supports(EDefinitionSupport eSupport) const
{
switch (eSupport)
{
case EDefinitionSupport::support_const_variable: return true;
case EDefinitionSupport::support_typedef: return true;
case EDefinitionSupport::support_struct: return true;
case EDefinitionSupport::support_union: return true;
case EDefinitionSupport::support_enum: return true;
case EDefinitionSupport::support_attribute: return true;
case EDefinitionSupport::support_operation: return true;
default: return false;
}
}
void CInterfaceEntity::CreateValueNode()
{
// Create a compound type value node for this definition.
ValueRef() = std::make_shared<CInterfaceValueNode>(shared_from_this(), nullptr);
}

View File

@@ -0,0 +1,96 @@
#ifndef INTERFACE_ENTITY_H
#define INTERFACE_ENTITY_H
#include "definition_entity.h"
/**
* @brief The interface definition of an IDL file.
* @details The interface section of the IDL file contains multiple declarations of attributes and operations, as well as the
* definitions of enums, structs and unions.
*/
class CInterfaceEntity : public CDefinitionEntity, public sdv::idl::IInterfaceEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] bIsLocal When set, the interface is defined as a local interface not intended to be marshalled.
*/
CInterfaceEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bIsLocal);
/**
* @brief Destructor
*/
virtual ~CInterfaceEntity() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Is this interface local? Overload of sdv::idl::IInterfaceEntity::IsLocal.
* @return Returns whether the interface is defined as local.
*/
virtual bool IsLocal() const override { return m_bIsLocal; }
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the interface entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_interface; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with interface type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Create the content value node. Overload of CDefinitionEntity::CreateValueNode.
* @details Create the value node and assign the value node to the ValueRef() reference..
*/
virtual void CreateValueNode() override;
/**
* @brief Request whether the definition supports the content. Overload of CDefintionEntity::Supports.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const override;
/**
* @brief Does the entity support inheritance? Overload of CDefinitionEntity::SupportsInheritance.
* @details Returns whether the entity supports inheritance.
* @return Returns whether inheritance is supported (which is the case).
*/
virtual bool SupportsInheritance() const override { return true; }
/**
* @brief Does the complex entity support attributes in its content? Overload of CDefinitionEntity::SupportContentAttributes.
* @details The default implementation doesn't support attributes (they are specific to interfaces).
* @return Returns whether the entity supports attributes (which is the case).
*/
virtual bool SupportContentAttributes() const override { return true; }
/**
* @brief Does the complex entity support operations in its content? Overload of CDefinitionEntity::SupportContentOperations.
* @details The default implementation doesn't support operations (they are specific to interfaces).
* @return Returns whether the entity supports operations (which is the case).
*/
virtual bool SupportContentOperations() const override { return true; }
private:
bool m_bIsLocal = false; ///< Flag indicating that the interface is local and not intended to be marshalled.
};
#endif // !defined(INTERFACE_ENTITY_H)

View File

@@ -0,0 +1,65 @@
#include "meta_entity.h"
CMetaEntity::CMetaEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CToken& rtokenMeta,
const CTokenList lstComments) :
CEntity(rptrContext, ptrParent)
{
std::string ssContent = rtokenMeta;
// Skip initial whitespace and store the content if not verbatim text.
size_t nSkip = 0;
if (rtokenMeta.GetMetaType() != ETokenMetaType::token_meta_verbatim)
{
while (nSkip < ssContent.size() && std::isspace(ssContent[nSkip]))
++nSkip;
}
m_ssContent = ssContent.substr(nSkip);
// Store the type
switch (rtokenMeta.GetMetaType())
{
case ETokenMetaType::token_meta_include_local:
m_eType = sdv::idl::IMetaEntity::EType::include_local;
break;
case ETokenMetaType::token_meta_include_global:
m_eType = sdv::idl::IMetaEntity::EType::include_global;
break;
case ETokenMetaType::token_meta_define:
m_eType = sdv::idl::IMetaEntity::EType::define;
break;
case ETokenMetaType::token_meta_undef:
m_eType = sdv::idl::IMetaEntity::EType::undef;
break;
case ETokenMetaType::token_meta_verbatim:
m_eType = sdv::idl::IMetaEntity::EType::verbatim;
break;
default:
throw CCompileException("Internal error: incomplete meta token received.");
break;
}
// Store comments
SetCommentTokens(lstComments, true);
// Set the position in the source file
SetBeginPosition(rtokenMeta.GetLine(), rtokenMeta.GetCol());
}
sdv::interface_t CMetaEntity::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IMetaEntity>())
return static_cast<sdv::idl::IMetaEntity*>(this);
return CEntity::GetInterface(idInterface);
}
sdv::idl::IMetaEntity::EType CMetaEntity::GetMetaType() const
{
return m_eType;
}
sdv::u8string CMetaEntity::GetContent() const
{
return m_ssContent;
}

View File

@@ -0,0 +1,68 @@
#ifndef META_ENTITY_H
#define META_ENTITY_H
#include "entity_base.h"
/**
* @brief The meta data inserted into the code.
*/
class CMetaEntity : public CEntity, public sdv::idl::IMetaEntity
{
public:
/**
* @brief Constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] rtokenMeta The meta data content
* @param[in] lstComments Any preceding comments
*/
CMetaEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CToken& rtokenMeta,
const CTokenList lstComments);
/**
* @brief Destructor
*/
virtual ~CMetaEntity() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the meta entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_meta; }
/**
* @brief Get the name of the entity. Overload of CEntity::GetName.
* @return The entity name.
*/
virtual sdv::u8string GetName() const override { return "meta"; }
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get the meta data type.
* @return Returns meta entity type.
*/
virtual sdv::idl::IMetaEntity::EType GetMetaType() const override;
/**
* @brief Get the meta data content.
* @return Returns a string object.
*/
virtual sdv::u8string GetContent() const override;
/**
* @brief Process the code. Since there is none... nothing to do. Overload of CEntity::Process.
*/
virtual void Process() override {};
private:
sdv::idl::IMetaEntity::EType m_eType; ///< Type of meta data
std::string m_ssContent; ///< The meta data string
};
#endif ///defined(META_ENTITY_H)

View File

@@ -0,0 +1,49 @@
#include "module_entity.h"
#include "typedef_entity.h"
#include "struct_entity.h"
#include "union_entity.h"
#include "interface_entity.h"
#include "exception_entity.h"
#include "../exception.h"
#include "../token.h"
CModuleEntity::CModuleEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CDefinitionEntity(rptrContext, ptrParent)
{}
CModuleEntity::CModuleEntity(CParser& rParser, const CContextPtr& rptrContext) :
CDefinitionEntity(rParser, rptrContext)
{}
void CModuleEntity::Process()
{
CDefinitionEntity::Process();
}
bool CModuleEntity::Supports(EDefinitionSupport eSupport) const
{
switch (eSupport)
{
case EDefinitionSupport::support_module: return true;
case EDefinitionSupport::support_const_variable: return true;
case EDefinitionSupport::support_typedef: return true;
case EDefinitionSupport::support_struct: return true;
case EDefinitionSupport::support_union: return true;
case EDefinitionSupport::support_enum: return true;
case EDefinitionSupport::support_interface: return true;
case EDefinitionSupport::support_exception: return true;
default: return false;
}
}
bool CModuleEntity::IsExtendable() const
{
// Allow extending the module.
return true;
}
bool CModuleEntity::SupportsChildren() const
{
// Allow the support of children.
return true;
}

View File

@@ -0,0 +1,86 @@
#ifndef MODULE_ENTITY_H
#define MODULE_ENTITY_H
#include "definition_entity.h"
#include <map>
#include "../constvariant.h"
/**
* @brief The module definition of an IDL file.
* @details The module section of the IDL file contains multiple definitions of nested modules, const definitions and type
* definitions.
*/
class CModuleEntity : public CDefinitionEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CModuleEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
protected:
/**
* @brief Root entity constructor (name is 'root' and no parent).
* @param[in] rParser Reference to the parser.
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
*/
CModuleEntity(CParser& rParser, const CContextPtr& rptrContext);
public:
/**
* @brief Destructor
*/
virtual ~CModuleEntity() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the module entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_module; }
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Request whether the definition supports the content. Overload of CDefintionEntity::Supports.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const override;
// Suppress warning of cppcheck of a useless override. The function implementation improves readability.
// cppcheck-suppress uselessOverride
/**
* @brief Is this definition a root entity? Overload of CDefinitionEntity::IsRootEntity.
* @details The root entity is not expecting curly brackets '{...}'.
* @return Returns whether this is a root entity (which is not the case).
*/
virtual bool IsRootEntity() const override { return false; }
/**
* @brief Is the entity extendable? Overload of CEntity::IsExtendable.
* @details Allow extendability of the module entity.
* @return Returns whether the entity is extendable.
*/
virtual bool IsExtendable() const override;
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Does the entity support children? Overload of CEntity::SupportsChildren.
* @details The module supports children.
* @return Return whether the entity support children.
*/
virtual bool SupportsChildren() const override;
private:
};
#endif // !defined(MODULE_ENTITY_H)

View File

@@ -0,0 +1,47 @@
#include "operation_entity.h"
#include "interface_entity.h"
COperationEntity::COperationEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CDeclarationEntity(rptrContext, ptrParent), m_iteratorParameters(GetParamVector()),
m_iteratorExceptions(GetExceptionVector())
{}
sdv::interface_t COperationEntity::GetInterface(sdv::interface_id idInterface)
{
// Expose interfaces
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IOperationEntity>())
return static_cast<sdv::idl::IOperationEntity*>(this);
return CDeclarationEntity::GetInterface(idInterface);
}
sdv::idl::IEntityIterator* COperationEntity::GetParameters()
{
if (!GetParamVector().empty()) return &m_iteratorParameters;
return nullptr;
}
sdv::idl::IEntityIterator* COperationEntity::GetExceptions()
{
if (!GetExceptionVector().empty()) return &m_iteratorExceptions;
return nullptr;
}
std::pair<CEntityPtr, bool> COperationEntity::FindLocal(const std::string& rssName, bool /*bDeclaration*/) const
{
const CEntityVector& rvecParams = GetParamVector();
for (const CEntityPtr& rptrEntity : rvecParams)
{
if (rptrEntity->GetName() == rssName)
return std::make_pair(rptrEntity, false);
}
return std::make_pair(nullptr, false);
}
bool COperationEntity::RequiresAssignment() const
{
const CInterfaceEntity* pInterface = GetParentEntity() ? GetParentEntity()->Get<CInterfaceEntity>() : nullptr;
if (pInterface && pInterface->IsLocal()) return false;
return CDeclarationEntity::RequiresAssignment();
}

View File

@@ -0,0 +1,128 @@
#ifndef OPERATION_ENTITY_H
#define OPERATION_ENTITY_H
#include "declaration_entity.h"
/**
* @brief The operation definition of an IDL file.
* @details The operation section of the IDL file defines operations.
*/
class COperationEntity : public CDeclarationEntity, public sdv::idl::IOperationEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
COperationEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Destructor
*/
virtual ~COperationEntity() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get parameter entity iterator if the definition has any parameters. Overload of
* sdv::idl::IOperationEntity::GetParameters.
* @return Returns a pointer to the parameter entity iterator or NULL when not available.
*/
virtual sdv::idl::IEntityIterator* GetParameters() override;
/**
* @brief Get the list of possible exceptions that might be fired for this operation. Overload of
* sdv::idl::IOperationEntity::GetExceptions.
* @return Interface pointer to the exception iterator.
*/
virtual sdv::idl::IEntityIterator* GetExceptions() override;
/**
* @brief Is the entity readonly (variable declarations and writable attributes aren't)? Overload of IDeclarationEntity::IsReadOnly.
* @details Returns whether the entity is readonly by design or whether it is defined readonly by the code. Default value is
* 'true'.
* @return Returns 'true' when the entity defined as readonly; 'false' otherwise.
*/
virtual bool IsReadOnly() const override { return m_bOperationIsConst; }
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the operation type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_operation; }
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override { CDeclarationEntity::Process(); }
protected:
/**
* @brief Does the entity support raising exceptions? Overload of CDeclarationEntity::SupportRaiseExceptions.
* @return Returns 'true' when the entity defined as attribute; 'false' otherwise.
*/
virtual bool SupportRaiseExceptions() const override { return true; }
/**
* @brief Does the entity support arrays? Overload of CDeclarationEntity::SupportArrays.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportArrays() const override { return true; }
/**
* @brief Does the entity support an interface as base type? Overload of CDeclarationEntity::SupportVoid.
* @return Returns 'true' when the entity supports interfaces as base type; 'false' otherwise.
*/
virtual bool SupportInterface() const override { return true; }
/**
* @brief Does the entity support 'void' as base type? Overload of CDeclarationEntity::SupportVoid.
* @details Returns whether the entity supports the 'void' base type. Default value is 'false'.
* @return Returns 'true' when the entity supports void as base type; 'false' otherwise.
*/
virtual bool SupportVoid() const override { return true; }
/**
* @brief Does the entity support parameters? Overload of CDeclarationEntity::RequiresParameters.
* @details Returns whether the entity supports parameters. Default value is 'false'.
* @return Returns 'true' when the entity requires parameters; 'false' otherwise.
*/
virtual bool RequiresParameters() const override { return true; }
/**
* @brief Set operation as const. Overload of CDeclarationEntity::SetOperationAsConst.
*/
virtual void SetOperationAsConst() override { m_bOperationIsConst = true; }
/**
* @brief Find the entity locally by looking in the parameter list. Overload of CEntity::FindLocal.
* @param[in] rssName Reference to the string object containing the name of the entity to search for.
* @param[in] bDeclaration When set, the name belongs to a declaration; otherwise it belongs to a definition. Needed to allow
* the reuse of names between declarations and definitions.
* @return Returns a pair object containing an entity pointer if the entity exists or a NULL pointer if not as well as a
* boolean that indicates that the entity was from an inherited entity.
*/
virtual std::pair<CEntityPtr, bool> FindLocal(const std::string& rssName, bool bDeclaration) const override;
/**
* @brief Does the entity require an assignment (const declarations do)? Overload of CDeclarationEntity::RequiresAssignment.
* @details Default processing is done by the declaration function (checking for unbound arrays). Exception: when the
* parent interface is defined as local, assignment is not required.
* @return Returns 'true' when the entity requires an assignment; 'false' otherwise.
*/
virtual bool RequiresAssignment() const override;
private:
bool m_bOperationIsConst = false; ///< When set, the operation is defined as 'const' operation.
CEntityIterator m_iteratorParameters; ///< Parameters iterator
CEntityIterator m_iteratorExceptions; ///< Exceptions iterator
};
#endif // !defined(OPERATION_ENTITY_H)

View File

@@ -0,0 +1,66 @@
#include "parameter_entity.h"
#include "interface_entity.h"
#include "operation_entity.h"
#include "../exception.h"
CParameterEntity::CParameterEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent,
const CTokenList& rlstTokenList, bool bEnforceDirection /*= true*/) :
CDeclarationEntity(rptrContext, ptrParent, rlstTokenList), m_bEnforceDirection(bEnforceDirection)
{}
sdv::interface_t CParameterEntity::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::idl::IParameterEntity>())
return static_cast<sdv::idl::IParameterEntity*>(this);
return CDeclarationEntity::GetInterface(idInterface);
}
void CParameterEntity::Process()
{
// Check for direction indicators.
CToken token = GetToken();
if (token == "in") m_eDirection = sdv::idl::IParameterEntity::EParameterDirection::input;
else if (token == "out") m_eDirection = sdv::idl::IParameterEntity::EParameterDirection::output;
else if (token == "inout") m_eDirection = sdv::idl::IParameterEntity::EParameterDirection::in_out;
else
{
PrependToken(token);
// Enforce the direction?
if (m_bEnforceDirection) throw CCompileException(token, "Missing direction indicator.");
}
// Let the basic type entity process further
CDeclarationEntity::Process();
}
bool CParameterEntity::RequiresAssignment() const
{
COperationEntity* pOperation = GetParentEntity() ? GetParentEntity()->Get<COperationEntity>() : nullptr;
const CInterfaceEntity* pInterface = pOperation && pOperation->GetParentEntity() ? pOperation->GetParentEntity()->Get<CInterfaceEntity>() : nullptr;
if (pInterface && pInterface->IsLocal()) return false;
return CDeclarationEntity::RequiresAssignment();
}
void CParameterEntity::CalcHash(CHashObject& rHash) const
{
// Call base class
CDeclarationEntity::CalcHash(rHash);
// Add the direction
switch (m_eDirection)
{
case sdv::idl::IParameterEntity::EParameterDirection::input:
rHash << "in";
break;
case sdv::idl::IParameterEntity::EParameterDirection::output:
rHash << "out";
break;
case sdv::idl::IParameterEntity::EParameterDirection::in_out:
rHash << "inout";
break;
default:
break;
}
}

View File

@@ -0,0 +1,92 @@
#ifndef PARAMETER_ENTITY_H
#define PARAMETER_ENTITY_H
#include "declaration_entity.h"
/**
* @brief The parameter definition of an operation and value type.
* @details The parameter section contains the definition of the parameter for operations and value types.
*/
class CParameterEntity : public CDeclarationEntity, public sdv::idl::IParameterEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] rlstTokenList Reference to the token list holding the tokens to process.
* @param[in] bEnforceDirection Enforce parameter direction.
*/
CParameterEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CTokenList& rlstTokenList,
bool bEnforceDirection = true);
/**
* @brief Destructor
*/
virtual ~CParameterEntity() override = default;
/**
* @brief Get access to another interface. Overload of IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the parameter type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_parameter; }
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Does the entity support arrays? Overload of CDeclarationEntity::SupportArrays.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportArrays() const override { return true; }
/**
* @brief Does the entity support an interface as base type? Overload of CDeclarationEntity::SupportVoid.
* @return Returns 'true' when the entity supports interfaces as base type; 'false' otherwise.
*/
virtual bool SupportInterface() const override { return true; }
/**
* @brief Get the parameter direction. Overload of sdv::idl::IParameterEntity::GetDirection.
* @return Parameter direction.
*/
virtual sdv::idl::IParameterEntity::EParameterDirection GetDirection() const override { return m_eDirection; }
/**
* @brief Does the entity require an assignment (const declarations do)? Overload of CDeclarationEntity::RequiresAssignment.
* @details Default processing is done by the declaration function (checking for unbound arrays). Exception: when the
* parent interface is defined as local, assignment is not required.
* @return Returns 'true' when the entity requires an assignment; 'false' otherwise.
*/
virtual bool RequiresAssignment() const override;
/**
* @brief Is the entity readonly (variable declarations and writable attributes aren't)? Overload of IDeclarationEntity::IsReadOnly.
* @details Returns whether the entity is readonly by design or whether it is defined readonly by the code. Default value is
* 'true'.
* @return Returns 'true' when the entity defined as readonly; 'false' otherwise.
*/
virtual bool IsReadOnly() const override { return false; }
/**
* @brief Calculate the hash of this entity and all encapsulated entities. Overload of CBaseEntity::CalcHash.
* @param[in, out] rHash Hash object to be filled with data.
*/
virtual void CalcHash(CHashObject& rHash) const override;
private:
bool m_bEnforceDirection; ///< When set, the parameter enforces the direction indicator when processing.
sdv::idl::IParameterEntity::EParameterDirection m_eDirection =
sdv::idl::IParameterEntity::EParameterDirection::unknown; ///< The direction type of the parameter.
};
#endif // !defined(PARAMETER_ENTITY_H)

View File

@@ -0,0 +1,30 @@
#include "root_entity.h"
#include "module_entity.h"
#include "../exception.h"
#include "../token.h"
CRootEntity::CRootEntity(CParser& rParser, const CContextPtr& rptrContext) :
CModuleEntity(rParser, rptrContext)
{}
void CRootEntity::Process()
{
// Skip the definition and process the content directly.
ProcessContent();
}
bool CRootEntity::IsExtendable() const
{
return false;
}
void CRootEntity::AddMeta(const CEntityPtr& ptrMeta)
{
m_lstMetaEntities.push_back(ptrMeta);
}
const CEntityList& CRootEntity::GetMeta() const
{
return m_lstMetaEntities;
}

View File

@@ -0,0 +1,57 @@
#ifndef ROOT_ENTITY_H
#define ROOT_ENTITY_H
#include "module_entity.h"
#include "meta_entity.h"
/**
* @brief The root definition of an IDL file.
* @details The root section of the IDL file contains multiple definitions of modules, constants and types.
*/
class CRootEntity : public CModuleEntity
{
public:
/**
* @brief Default constructor
* @param[in] rParser Reference to the parser used to parse the code.
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
*/
CRootEntity(CParser& rParser, const CContextPtr& rptrContext);
/**
* @brief Process the code. Overload of CModuleEntity::Process.
*/
virtual void Process() override;
/**
* @brief Is this definition a root entity? Overload of CDefinitionEntity::IsRootEntity.
* @details The root entity is not expecting curly brackets '{...}'.
* @return Returns whether this entity is the root entity.
*/
virtual bool IsRootEntity() const override { return true; }
/**
* @brief Is the entity extendable? Overload of CEntity::IsExtendable.
* @details Prevents extending the root entity.
* @return Returns whether the entity is extendable.
*/
virtual bool IsExtendable() const override;
/**
* @brief Add meta data entity.
* @param[in] ptrMeta Shared pointer to the meta data entity.
*/
void AddMeta(const CEntityPtr& ptrMeta);
/**
* @brief Get the meta data entity list.
* @return Reference to the meta data entity list.
*/
const CEntityList& GetMeta() const;
private:
CEntityList m_lstMetaEntities; ///< List of meta entities.
};
#endif // !defined(ROOT_ENTITY_H)

View File

@@ -0,0 +1,71 @@
#include "struct_entity.h"
#include "../exception.h"
#include "../logger.h"
#include "typedef_entity.h"
#include "variable_entity.h"
#include <iostream>
CStructEntity::CStructEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CDefinitionEntity(rptrContext, ptrParent)
{}
std::string CStructEntity::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return std::string("struct ") + GetScopedName();
}
void CStructEntity::Process()
{
// The definition and declaration can be defined:
// struct <struct_identifier>; --> forward declaration
// struct <struct_identifier> {...}; --> struct definition
// struct <struct_identifier> : <base_struct,...> {...}; --> struct definition with inheritance
// <struct_identifier> <decl_identifier>; --> struct variable declaration
// <struct_identifier> <decl_identifier> = {...}; --> struct variable declaration and assignment
// struct <struct_identifier> <decl_identifier>; --> struct variable declaration
// struct <struct_identifier> <decl_identifier> = {...}; --> struct variable declaration and assignment
// struct <struct_identifier> {...} <decl_identifier>; --> struct definition and variable declaration
// struct <struct_identifier> : <base_struct,...> {...} <decl_identifier>; --> struct definition with inheritance and variable declaration
// struct <struct_identifier> {...} <decl_identifier> = {...}; --> struct definition, variable declaration and assignment
// struct <struct_identifier> : <base_struct,...> {...} <decl_identifier> = {...}; --> struct definition with inheritance, variable declaration and assignment
// struct {...} <decl_identifier>; --> anonymous struct definition and variable declaration
// struct : <base_struct,...> {...} <decl_identifier>; --> anonymous struct definition with inheritance and variable declaration
// struct {...} <decl_identifier> = {...}; --> anonymous struct definition, variable declaration and assignment
// struct : <base_struct,...> {...} <decl_identifier> = {...}; --> anonymous struct definition with inheritance, variable declaration and assignment
// typedef <struct_identifier> <type_identifier>;
// typedef struct <struct_identifier> { ... } <type_identifier>;
// typedef <struct_identifier> <type_identifier>;
// typedef struct <struct_identifier> { ... } <type_identifier>;
// typedef struct { ... } <type_identifier>;
// const struct <struct_identifier> <decl_identifier> = {...};
// const <struct_identifier> <decl_identifier> = {...};
// const struct {...} <decl_identifier> = {...};
// The declaration is as follows:
// <struct_identifier> <decl_identifier>;
// struct <struct_identifier> <decl_identifier>;
// struct <struct_identifier> { ... } <decl_identifier>;
CDefinitionEntity::Process();
}
bool CStructEntity::Supports(EDefinitionSupport eSupport) const
{
switch (eSupport)
{
case EDefinitionSupport::support_variable: return true;
case EDefinitionSupport::support_const_variable: return true;
case EDefinitionSupport::support_typedef: return true;
case EDefinitionSupport::support_struct: return true;
case EDefinitionSupport::support_union: return true;
case EDefinitionSupport::support_enum: return true;
default: return false;
}
}
void CStructEntity::CreateValueNode()
{
// Create a compound type value node for this definition.
ValueRef() = std::make_shared<CCompoundTypeValueNode>(shared_from_this(), nullptr);
}

View File

@@ -0,0 +1,75 @@
#ifndef STRUCT_ENTITY_H
#define STRUCT_ENTITY_H
#include "definition_entity.h"
/**
* @brief The struct definition of an IDL file.
* @details The struct section of the IDL file contains multiple declarations of members, as well as the definitions of structs
* and unions.
*/
class CStructEntity : public CDefinitionEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CStructEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Destructor
*/
virtual ~CStructEntity() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the struct type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_struct; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with struct type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Request whether the definition supports the content. Overload of CDefintionEntity::Supports.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const override;
/**
* @brief Does the entity support inheritance? Overload of CDefinitionEntity::SupportsInheritance.
* @return Returns whether the entity supports inheritance.
*/
virtual bool SupportsInheritance() const override { return true; }
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Does the entity declaration support anonymous naming? Overload of CDefinitionEntity::SupportsAnonymous.
* @remarks C11 supports anonymous structs. C++ not! Therefore, IDL does not support anonymous structs.
* @return Returns whether the entity supports inheritance.
*/
virtual bool SupportsAnonymous() const override { return false; }
/**
* @brief Create the content value node. Overload of CDefinitionEntity::CreateValueNode.
* @details Create the value node and assign the value node to the ValueRef() reference..
*/
virtual void CreateValueNode() override;
};
#endif // !defined(STRUCT_ENTITY_H)

View File

@@ -0,0 +1,44 @@
#include "typedef_entity.h"
#include "../exception.h"
CTypedefEntity::CTypedefEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CDeclarationEntity(rptrContext, ptrParent)
{
}
CTypedefEntity::CTypedefEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent,
const std::string& rssName) :
CDeclarationEntity(rptrContext, ptrParent)
{
if (rssName.empty()) throw CCompileException("Internal error: trying to create a named type entity without valid name.");
SetName(rssName, true, true);
}
std::string CTypedefEntity::GetDeclTypeStr(bool bResolveTypedef) const
{
// When not resolving the typedef, return the default implementation ("typedef <name>").
if (!bResolveTypedef) return std::string("typedef ") + GetScopedName();
// Otherwise resolve the typedef by taking the base type.
if (GetTypeEntity())
return GetTypeEntity()->GetDeclTypeStr(bResolveTypedef);
else
return DeclTypeToString(GetBaseType());
}
void CTypedefEntity::Process()
{
CDeclarationEntity::Process();
}
std::pair<CEntityPtr, bool> CTypedefEntity::FindLocal(const std::string& rssName, bool bDeclaration) const
{
// Call the find local function on the original type.
if (!GetTypeEntity()) return std::pair<CEntityPtr, bool>();
return GetTypeEntity()->FindLocal(rssName, bDeclaration);
}

View File

@@ -0,0 +1,79 @@
#ifndef TYPEDEF_ENTITY_H
#define TYPEDEF_ENTITY_H
#include "declaration_entity.h"
/**
* @brief The const definition of an IDL file.
* @details The const section of the IDL file defines const values.
*/
class CTypedefEntity : public CDeclarationEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CTypedefEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Constructor for a local named type entity without parsing.
* @attention This type entity is not attached as a child.
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] rssName Reference to the name.
*/
CTypedefEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const std::string& rssName);
/**
* @brief Destructor
*/
virtual ~CTypedefEntity() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return The typedef entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_typedef; }
/**
* @brief Get the qualified type of the entity. Overload of CEntity::GetDeclTypeStr.
* @attention To get the qualified type including array sizes, use the GetDeclTypeStr of the CEntityValueNode class.
* @details The qualified type consists of "<base type> <type identifier>".
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return String with the typedef entity type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Does the entity support arrays? Overload of CDeclarationEntity::SupportArrays.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportArrays() const override { return true; }
/**
* @brief Does the entity require an assignment? Overload of CDeclarationEntity::RequiresAssignment.
* @return Returns 'true' when the entity requires an assignment; 'false' otherwise.
*/
virtual bool RequiresAssignment() const override { return false; }
/**
* @brief Find the entity locally. Overload of CEntity::FindLocal.
* @param[in] rssName Reference to the string object containing the name of the entity to search for.
* @param[in] bDeclaration When set, the name belongs to a declaration; otherwise it belongs to a definition. Needed to allow
* the reuse of names between declarations and definitions.
* @return Returns a pair object containing an entity pointer if the entity exists or a NULL pointer if not as well as a
* boolean that indicates that the entity was from an inherited entity.
*/
virtual std::pair<CEntityPtr, bool> FindLocal(const std::string& rssName, bool bDeclaration) const override;
};
#endif // !defined(TYPEDEF_ENTITY_H)

View File

@@ -0,0 +1,434 @@
#include "union_entity.h"
#include "../exception.h"
#include "../logger.h"
#include "../parser.h"
#include "typedef_entity.h"
#include "variable_entity.h"
#include <iostream>
#include <set>
CCaseEntry::CCaseEntry(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bDefault) :
CVariableEntity(rptrContext, ptrParent, false, false), m_bDefault(bDefault)
{}
std::string CCaseEntry::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return GetScopedName();
}
sdv::interface_t CCaseEntry::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::ICaseEntity>())
return static_cast<sdv::idl::ICaseEntity*>(this);
return CVariableEntity::GetInterface(idInterface);
}
sdv::u8string CCaseEntry::GetLabel() const
{
// The default case entry doesn#t have a label.
if (m_bDefault) return sdv::u8string();
// Get the label string from the value of the case label entity.
CVariableEntity* pLabelVariableEntity = m_ptrLabel->Get<CVariableEntity>();
if (!pLabelVariableEntity)
throw CCompileException("Internal error: expected a variable entity as case label.");
if (!pLabelVariableEntity->ValueRef())
throw CCompileException("Internal error: expected a value for the variable entity case label.");
// Enum value node?
const CEnumValueNode* pEnumValueNode = pLabelVariableEntity->ValueRef()->Get<CEnumValueNode>();
if (pEnumValueNode) return pEnumValueNode->String();
// Simple value node?
const CSimpleTypeValueNode* pSimpleTypeValueNode = pLabelVariableEntity->ValueRef()->Get<CSimpleTypeValueNode>();
if (pSimpleTypeValueNode) return pSimpleTypeValueNode->Variant().GetAsString();
// Otherwise error...
throw CCompileException("Internal error: expected a value for the variable entity case label.");
}
const CToken& CCaseEntry::GetLabelToken() const
{
return m_tokenLabel;
}
void CCaseEntry::Process()
{
CLog log("Processing case label...");
// Peek for the label token (used for reporting parsing errors).
m_tokenLabel = PeekToken();
// Processing of the case label value is done in post processing since the switch type might not be known yet.
CToken token;
std::string ssCaseLabel;
while ((token = GetToken()) != ":")
{
if (!ssCaseLabel.empty())
ssCaseLabel += ' ';
ssCaseLabel += static_cast<std::string>(token);
m_lstCaseValue.push_back(token);
}
log << "Case label name '" << ssCaseLabel << "'" << std::endl;
// Only unnamed nested struct and union definitions are not allowed. Furthermore, anonymous structs and unions (without
// explicit declaration cannot be supported (also not part of the C++ standard). Varable based switch type unions are
// also not supported since the variable for the switch needs to be part of the parent switch case and thus needs a struct to
// hold it.
token = PeekToken();
if ((token == "struct" && (PeekToken(1) == "{" || PeekToken(2) == "{")) ||
(token == "union" && (PeekToken(1) == "switch" || PeekToken(2) == "switch")))
{
// Get the struct/union from the code
token = GetToken();
throw CCompileException(token, "Cannot make a definition inside an union.");
}
// Stop processing if the case doesn't have any declaration (is followed by another case or a closing curled bracket).
if (token == "case" || token == "}")
{
// Determine whether the comments are preceding the token (either on the same line or the line before).
CTokenList lstPreComments = GetPreCommentTokenList();
if (!lstPreComments.empty()) SetCommentTokens(lstPreComments);
// Assign any succeeding comments
ProcessPostCommentTokenList(token.GetLine());
// Insert a semi-colon to identify that the statement is finished.
CToken tokenSep(";", ETokenType::token_separator);
PrependToken(tokenSep);
SetName(GetParserRef().GenerateAnonymousEntityName("case"));
return;
}
// Now let the variable process the declaration
CVariableEntity::Process();
}
void CCaseEntry::PostProcess()
{
CLog log("Post processing case label...");
// Get the base type of the enum entity and insert it in front of the declaration.
CUnionEntity* pUnionEntity = GetParentEntity()->Get<CUnionEntity>();
if (!pUnionEntity) throw CCompileException("Internal error: expected an union entity as parent.");
// Separate between case statement and default
if (m_bDefault)
{
if (!m_lstCaseValue.empty())
throw CCompileException("Default case label cannot have a value.");
log << "Default case label" << std::endl;
} else
{
// Get the switch case type
sdv::idl::EDeclType eSwitchCaseType = sdv::idl::EDeclType::decltype_unknown;
CEntityPtr ptrSwitchCaseType;
CValueNodePtr ptrSwitchCaseValue;
pUnionEntity->GetSwitchCaseType(eSwitchCaseType, ptrSwitchCaseType, ptrSwitchCaseValue);
// Insert the switch type specific assignment to the token list
m_lstCaseValue.push_front(CToken("="));
m_lstCaseValue.push_front(CToken("label"));
if (!ptrSwitchCaseType)
{
switch (eSwitchCaseType)
{
case sdv::idl::EDeclType::decltype_short: m_lstCaseValue.push_front(CToken("short")); break;
case sdv::idl::EDeclType::decltype_long: m_lstCaseValue.push_front(CToken("long")); break;
case sdv::idl::EDeclType::decltype_long_long: m_lstCaseValue.push_front(CToken("long long")); break;
case sdv::idl::EDeclType::decltype_unsigned_short: m_lstCaseValue.push_front(CToken("unsigned short")); break;
case sdv::idl::EDeclType::decltype_unsigned_long: m_lstCaseValue.push_front(CToken("unsigned long")); break;
case sdv::idl::EDeclType::decltype_unsigned_long_long: m_lstCaseValue.push_front(CToken("unsigned long long")); break;
case sdv::idl::EDeclType::decltype_octet: m_lstCaseValue.push_front(CToken("octet")); break;
case sdv::idl::EDeclType::decltype_char: m_lstCaseValue.push_front(CToken("char")); break;
case sdv::idl::EDeclType::decltype_char16: m_lstCaseValue.push_front(CToken("char16")); break;
case sdv::idl::EDeclType::decltype_char32: m_lstCaseValue.push_front(CToken("char32")); break;
case sdv::idl::EDeclType::decltype_wchar: m_lstCaseValue.push_front(CToken("wchar")); break;
case sdv::idl::EDeclType::decltype_boolean: m_lstCaseValue.push_front(CToken("boolean")); break;
case sdv::idl::EDeclType::decltype_native: m_lstCaseValue.push_front(CToken("native")); break;
default: throw CCompileException(m_tokenLabel, "Internal error: invalid switch case data type.");
}
}
else
m_lstCaseValue.push_front(CToken(ptrSwitchCaseType->GetScopedName()));
m_lstCaseValue.push_back(CToken(";", ETokenType::token_separator));
// Create the label value.
m_ptrLabel = std::make_shared<CVariableEntity>(GetContext(), shared_from_this(), m_lstCaseValue, true, false);
// Process the label variable (this will resolve any assigned value and constant).
m_ptrLabel->Process();
CVariableEntity* pLabelVariableEntity = m_ptrLabel->Get<CVariableEntity>();
if (!pLabelVariableEntity)
throw CCompileException("Internal error: expected a variable entity as case label.");
pLabelVariableEntity->PostProcess();
log << "Case label is: " << GetLabel() << std::endl;
}
// Post process the assignment
CVariableEntity::PostProcess();
}
CUnionEntity::CUnionEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
CStructEntity(rptrContext, ptrParent)
{}
sdv::interface_t CUnionEntity::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::IUnionEntity>())
return static_cast<sdv::idl::IUnionEntity*>(this);
return CStructEntity::GetInterface(idInterface);
}
sdv::idl::IUnionEntity::ESwitchInterpret CUnionEntity::GetSwitchInterpretation() const
{
return m_eSwitchInterpret;
}
void CUnionEntity::GetSwitchType(/*out*/ sdv::idl::EDeclType& reType, /*out*/ sdv::IInterfaceAccess*& rpType) const
{
reType = m_typedeclSwitch.GetBaseType();
rpType = m_typedeclSwitch.GetTypeDefinition();
}
void CUnionEntity::GetSwitchVar(/*out*/ sdv::u8string& rssVarStr, /*out*/ sdv::IInterfaceAccess*& rpVarEntity,
/*out*/ sdv::IInterfaceAccess*& rpVarContainer) const
{
rssVarStr.clear();
rpVarEntity = nullptr;
rpVarContainer = nullptr;
if (m_eSwitchInterpret == sdv::idl::IUnionEntity::ESwitchInterpret::switch_type) return;
rssVarStr = m_ssValueNode;
rpVarEntity = m_ptrSwitchValueNode ? m_ptrSwitchValueNode->GetDeclEntity()->Get<sdv::IInterfaceAccess>() : nullptr;
rpVarContainer = m_ptrContainer ? m_ptrContainer->Get<sdv::IInterfaceAccess>() : nullptr;
}
std::string CUnionEntity::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
return std::string("union ") + GetScopedName();
}
void CUnionEntity::Process()
{
// The definition and declaration can be defined:
// union <union_identifier>; --> forward declaration
// union <union_identifier> switch(<type>) {...}; --> union definition with type
// struct { <type> <var>; union <union_identifier> switch(<var>) {...};}; --> union definition with varaiable
// <union_identifier> <decl_identifier>; --> union variable declaration
// <union_identifier> <decl_identifier> = {...}; --> union variable declaration and assignment
// union <union_identifier> <decl_identifier>; --> union variable declaration
// union <union_identifier> <decl_identifier> = {...}; --> union variable declaration and assignment
// union <union_identifier> {...} <decl_identifier>; --> union definition and variable declaration
// union <union_identifier> {...} <decl_identifier> = {...}; --> union definition, variable declaration and assignment
// union {...} <decl_identifier>; --> anonymous union definition and variable declaration
// union {...} <decl_identifier> = {...}; --> anonymous union definition, variable declaration and assignment
// typedef <union_identifier> <type_identifier>;
// typedef union <union_identifier> { ... } <type_identifier>;
// typedef <union_identifier> <type_identifier>;
// typedef union <union_identifier> { ... } <type_identifier>;
// typedef union { ... } <type_identifier>;
// const union <union_identifier> <decl_identifier> = {...};
// const <union_identifier> <decl_identifier> = {...};
// const union {...} <decl_identifier> = {...};
// The declaration is as follows:
// <union_identifier> <decl_identifier>;
// union <union_identifier> <decl_identifier>;
// union <union_identifier> { ... } <decl_identifier>;
CStructEntity::Process();
}
void CUnionEntity::ProcessDefinitionAddendum()
{
//unions are defined as :
//union <union_identifier> switch (<type>)-- > type is a integral or enum type or a typedef of this
//{
//case <value>: <type> <var_identifier> -- > case values need to be unique
// ...
//};
//struct
//{
// <type> <var>;
// union <union_identifier> switch (<var>)-- > var is an integral or enum value, which is part of the struct
// {
// case <value>: <type> <var_identifier> -- > case values need to be unique
// ...
// };
//};
if (PeekToken() == ";") return; // Forward declaration
CToken token = GetToken();
if (token != "switch") throw CCompileException(token, "Expecting a switch statement following the union identifier.");
token = GetToken();
if (token != "(") throw CCompileException(token, "Expecting a left bracket '('.");
size_t nIndex = 0;
while (true)
{
token = PeekToken(nIndex++);
if (!token || token == ")") break;
m_ssSwitchVar += static_cast<std::string>(token);
}
m_typedeclSwitch = ProcessType(true);
token = GetToken();
while (token && token != ")")
{
m_ssValueNode += static_cast<std::string>(token);
token = GetToken();
}
if (token != ")") throw CCompileException(token, "Expecting a right bracket ')'.");
// Was it possible to resolve the type for the switch case. If not, the switch case is a variable?
if (m_typedeclSwitch.GetBaseType() == sdv::idl::EDeclType::decltype_unknown)
m_eSwitchInterpret = sdv::idl::IUnionEntity::ESwitchInterpret::switch_variable;
}
void CUnionEntity::PostProcess()
{
if (ForwardDeclaration()) return;
// Was the switch case type/variable found before? If not, check for a variable.
if (m_eSwitchInterpret == sdv::idl::IUnionEntity::ESwitchInterpret::switch_variable)
{
// Get the switch type object
CEntityPtr ptrSwitchVarBase = Find(m_typedeclSwitch.GetTypeString());
if (!ptrSwitchVarBase)
throw CCompileException(m_ssSwitchVar,
"The switch case must be determined by a predefined type or a member variable.");
// Proper relative name (without global scope)
std::string ssSwitchFullName = ptrSwitchVarBase->GetName() + m_ssValueNode;
m_ssValueNode = ssSwitchFullName;
// The type must be a variable type
CVariableEntity* pVarEntityBase = ptrSwitchVarBase->Get<CVariableEntity>();
if (!pVarEntityBase)
throw CCompileException(m_ssSwitchVar,
"The switch case is not determined by a member variable nor a predefined type.");
// The parent of the base is the common parent of both the switch var and the union.
m_ptrContainer = ptrSwitchVarBase->GetParentEntity();
if (!m_ptrContainer)
throw CCompileException(m_ssSwitchVar,
"The switch case variable and the union do not share a common parent.");
// Find the value node of the switch variable (in case it is a child some layers deep).
CValueNodePtr ptrSwitchValueNode;
if (!m_ssValueNode.empty())
m_ptrSwitchValueNode = pVarEntityBase->FindValue(m_ssValueNode);
else
m_ptrSwitchValueNode = pVarEntityBase->ValueRef();
if (!m_ptrSwitchValueNode)
throw CCompileException(m_ssSwitchVar, "Could not find the switch variable.");
// Change the variable type entity to a switch variable.
m_ptrSwitchValueNode->GetDeclEntity()->Get<CVariableEntity>()->UseAsSwitchVariable();
// Assign the type of the variable.
m_typedeclSwitch.SetTypeDefinitionEntityPtr(m_ptrSwitchValueNode->GetDeclEntity()->GetTypeEntity());
m_typedeclSwitch.SetBaseType(m_ptrSwitchValueNode->GetDeclEntity()->GetBaseType());
if (!IsIntegralDeclType(m_typedeclSwitch.GetBaseType()) &&
m_typedeclSwitch.GetBaseType() != sdv::idl::EDeclType::decltype_enum)
throw CCompileException(m_ssSwitchVar, "Expecting an integral or enum identifier type or variable.");
// Check whether one of the parents is a struct or a union and the variable is a struct member.
CEntityPtr ptrParent = GetParentEntity();
if (!ptrParent ||
((ptrParent->GetType() != sdv::idl::EEntityType::type_struct) &&
(ptrParent->GetType() != sdv::idl::EEntityType::type_exception)))
throw CCompileException(m_ssSwitchVar,
"The union needs to be part of a struct or an exception when used with a variable based switch case.");
while (ptrSwitchVarBase)
{
if (!ptrParent ||
((ptrParent->GetType() != sdv::idl::EEntityType::type_struct) &&
(ptrParent->GetType() != sdv::idl::EEntityType::type_exception)))
throw CCompileException(m_ssSwitchVar,
"The variable used in the switch case must be a member of the same or parent struct or exception the union "
"is declared in.");
if (ptrParent->GetType() == sdv::idl::EEntityType::type_struct &&
ptrParent == ptrSwitchVarBase->GetParentEntity())
break;
ptrParent = ptrParent->GetParentEntity();
}
}
// Post process all the case statements
std::set<std::string> setValues;
bool bDefaultFound = false;
for (CEntityPtr ptrEntity : m_lstDeclMembers)
{
CCaseEntry* pCaseEntry = ptrEntity->Get<CCaseEntry>();
if (!pCaseEntry) throw CCompileException("Internal error: unexpected entity stored at union.");
pCaseEntry->PostProcess();
// Differentiate between default and standard case label...
if (pCaseEntry->IsDefault())
{
if (bDefaultFound)
throw CCompileException(pCaseEntry->GetLabelToken(), "Duplicate default switch found.");
bDefaultFound = true;
} else
{
// Check for the existence of the label.
std::string ssLabel = pCaseEntry->GetLabel();
if (m_setValues.find(ssLabel) != m_setValues.end())
throw CCompileException(pCaseEntry->GetLabelToken(), "Duplicate switch case label found.");
m_setValues.insert(ssLabel);
}
}
// If supported create the value node for the definition (this allows assignments of values to this entity).
CreateValueNode();
}
bool CUnionEntity::Supports(EDefinitionSupport eSupport) const
{
switch (eSupport)
{
case EDefinitionSupport::support_case_declaration: return true;
case EDefinitionSupport::support_variable: return false;
case EDefinitionSupport::support_const_variable: return false;
case EDefinitionSupport::support_typedef: return false;
case EDefinitionSupport::support_struct: return false;
case EDefinitionSupport::support_union: return false;
case EDefinitionSupport::support_enum: return false;
default: return false;
}
}
void CUnionEntity::CreateValueNode()
{
// Create a compound type value node for this definition.
ValueRef() = std::make_shared<CCompoundTypeValueNode>(shared_from_this(), nullptr);
}
bool CUnionEntity::RequireDeclaration() const
{
// Require a declaration when unnamed union.
return !ForwardDeclaration() && IsUnnamed();
}
bool CUnionEntity::AllowAutoTransparentDeclaration() const
{
return !ForwardDeclaration() && IsUnnamed();
}
void CUnionEntity::GetSwitchCaseType(sdv::idl::EDeclType& reType, CEntityPtr& rptrType, CValueNodePtr& rptrValue)
{
reType = m_typedeclSwitch.GetBaseType();
rptrType = m_typedeclSwitch.GetTypeDefinitionEntityPtr();
if (m_ptrSwitchValueNode) rptrValue = m_ptrSwitchValueNode;
}

View File

@@ -0,0 +1,233 @@
#ifndef UNION_ENTITY_H
#define UNION_ENTITY_H
#include "definition_entity.h"
#include "struct_entity.h"
#include "variable_entity.h"
#include "entity_value.h"
#include <set>
/**
* @brief The enum entry declaration.
*/
class CCaseEntry : public CVariableEntity, public sdv::idl::ICaseEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] bDefault When set, the entry is the default case entry.
*/
CCaseEntry(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bDefault);
/**
* @brief Destructor
*/
virtual ~CCaseEntry() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get the case label string. Overload of sdv::idl::ICaseEntity::GetLabel.
* @return The label string.
*/
virtual sdv::u8string GetLabel() const override;
/**
* @brief Is the case a default cae entry. Overload of sdv::idl::ICaseEntity::IsDefault.
* @return Returns whether this is the default case entry.
*/
virtual bool IsDefault() const override { return m_bDefault; }
/**
* @brief Get the label token (used for error reporting).
* @return Returns a reference to the variable containing the label token.
*/
const CToken& GetLabelToken() const;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the union entity type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_case_entry; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns a string with union type.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Post process the case entry.
*/
void PostProcess();
/**
* @brief The entity doesn't support assignments. Overload of CDeclarationEntity::SupportAssignments.
* @return Returns whether assignments are supported (which is not the case).
*/
virtual bool SupportAssignments() const override { return false; }
private:
bool m_bDefault = false; ///< When set, the case entry is the default case entry.
CToken m_tokenLabel; ///< Label token.
CEntityPtr m_ptrLabel; ///< The case label entity.
CTokenList m_lstCaseValue; ///< Case value token list parsed during post processing.
};
/**
* @brief The struct definition of an IDL file.
* @details The struct section of the IDL file contains multiple declarations of members, as well as the definitions of structs
* and unions.
*/
class CUnionEntity : public CStructEntity, public sdv::idl::IUnionEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
*/
CUnionEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent);
/**
* @brief Destructor
*/
virtual ~CUnionEntity() override = default;
/**
* @brief Get access to another interface. Overload of sdv::IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Return the switch interpretation. Overload of sdv::idl::IUnionEntity::GetSwitchInterpretation.
* @return The interpretation of the switch case of this union.
*/
virtual sdv::idl::IUnionEntity::ESwitchInterpret GetSwitchInterpretation() const override;
/**
* @brief Return type information for the switch case. If the switch case is type base, this is the type information
* that is used to select. If the switch case is variable based, this is the type of the variable. Overload of
* sdv::idl::IUnionEntity::GetSwitchType.
* @param[out] reType Reference to the declaration type (either enum or an integral type).
* @param[out] rpType Reference to the type entity if existing.
*/
virtual void GetSwitchType(/*out*/ sdv::idl::EDeclType& reType, /*out*/ sdv::IInterfaceAccess*& rpType) const;
/**
* @brief Get the switch variable information if the switch case is variable based. Will be empty/NULL when the switch
* case is type based. Overload of sdv::idl::IUnionEntity::GetSwitchVar.
* @param[out] rssVarStr Reference to the string receiving the exact scoped declaration name of the switch variable if
* the interpretation is variable based. The variable name uses the scope separator '::' to define the common parent
* definition and the member separator '.' to define the variable declaration as member from the common parent.
* @param[out] rpVarEntity Reference to the variable entity if the interpretation is variable based.
* @param[out] rpVarContainer Reference to the variable entity of the container of both the switch variable and the
* union.
*/
virtual void GetSwitchVar(/*out*/ sdv::u8string& rssVarStr, /*out*/ sdv::IInterfaceAccess*& rpVarEntity,
/*out*/ sdv::IInterfaceAccess*& rpVarContainer) const;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the union type.
*/
virtual sdv::idl::EEntityType GetType() const override { return sdv::idl::EEntityType::type_union; }
/**
* @brief Get the declaration type of the entity as string. Overload of CEntity::GetDeclTypeStr.
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns the declaration type string.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Process the definition addendum.
* @details Process the definition addendum following the definition statement before the content definition. The default
* implementation checks for an inheritance list.
*/
virtual void ProcessDefinitionAddendum() override;
/**
* @brief Postprocess the switch/case assignments.
*/
void PostProcess();
/**
* @brief Request whether the definition supports the content. Overload of CDefintionEntity::Supports.
* @param[in] eSupport The type of support that is requested.
* @return Returns 'true' when the definition supports the content; 'false' otherwise.
*/
virtual bool Supports(EDefinitionSupport eSupport) const override;
/**
* @brief Does the entity support anonymous naming?
* @details The default implementation is that anonymous naming is not supported.
* @details Returns whether the entity supports inheritance.
* @return Returns whether anonymous naming is supported.
*/
virtual bool SupportsAnonymous() const override { return true; }
/**
* @brief Create the content value node. Overload of CDefinitionEntity::CreateValueNode.
* @details Create the value node and assign the value node to the ValueRef() reference..
*/
virtual void CreateValueNode() override;
/**
* @brief Does the definition require a declaration? Overload of CDefinitionEntity::RequireDeclaration.
* @return Returns whether a declaration is required.
*/
virtual bool RequireDeclaration() const override;
/**
* @brief Does the definition allow automatic transparent declaration if not present? Overload of
* CDefinitionEntity::AllowAutoTransparentDeclaration.
* @details When set an automatic transparent declaration is allowed without an explicit variable declaration. Currently this
* is only the case with unions with a variable based switch type.
* @return Returns whether the definition allows an automatic transparent declaration.
*/
virtual bool AllowAutoTransparentDeclaration() const override;
/**
* @brief Get switch case type.
* @param[out] reType Reference to the base type of the switch variable.
* @param[out] rptrType Reference to the type definition of the switch variable. Can be null if the switch uses a basic type.
* @param[out] rptrValue Reference to the value node of the switch variable. Can be null if the switch is determined by a type.
*/
void GetSwitchCaseType(sdv::idl::EDeclType& reType, CEntityPtr& rptrType, CValueNodePtr& rptrValue);
private:
/// Switch case interpretation.
sdv::idl::IUnionEntity::ESwitchInterpret m_eSwitchInterpret = sdv::idl::IUnionEntity::ESwitchInterpret::switch_type;
std::string m_ssSwitchVar; ///< Switch name
CTypeDeclaration m_typedeclSwitch; ///< The identifier within the switch case.
std::string m_ssValueNode; ///< Value node of the variable. If not existing the switch could either be
///< a type or a variable entity.
CEntityPtr m_ptrContainer; ///< The common container of both the switch variable and the union if the
///< switch case is variable based.
std::set<std::string> m_setValues; ///< Case entry values.
CValueNodePtr m_ptrSwitchValueNode; ///< Value node of the switch entity of a variable based switch.
};
#endif // !defined(UNION_ENTITY_H)

View File

@@ -0,0 +1,31 @@
#include "variable_entity.h"
#include "typedef_entity.h"
#include "../exception.h"
CVariableEntity::CVariableEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bConst, bool bAnonymous) :
CDeclarationEntity(rptrContext, ptrParent), m_bConst(bConst), m_bAnonymous(bAnonymous)
{}
CVariableEntity::CVariableEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CTokenList& rlstTokenList,
bool bConst, bool bAnonymous) :
CDeclarationEntity(rptrContext, ptrParent, rlstTokenList), m_bConst(bConst), m_bAnonymous(bAnonymous)
{}
std::string CVariableEntity::GetDeclTypeStr(bool bResolveTypedef) const
{
if (GetTypeEntity())
return GetTypeEntity()->GetDeclTypeStr(bResolveTypedef);
else
return DeclTypeToString(GetBaseType());
}
void CVariableEntity::Process()
{
CDeclarationEntity::Process();
// TODO: Const variables cannot contain:
// - dynamic arrays when no assignment is there
// - interfaces
// - structure or unions with unassigned dynamic arrays or interfaces
}

View File

@@ -0,0 +1,135 @@
#ifndef DECLARATOR_ENTITY_H
#define DECLARATOR_ENTITY_H
#include "declaration_entity.h"
/**
* @brief The variable declaration.
*/
class CVariableEntity : public CDeclarationEntity
{
public:
/**
* @brief Default constructor
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] bConst When set, the variable is defined as const.
* @param[in] bAnonymous When set, the variable is part of a struct and anonymous (unnamed and not declared) so its members are
* seen as members of the struct. For example, used with unions.
*/
CVariableEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, bool bConst, bool bAnonymous);
/**
* @brief Constructor using the provided token-list to process the code.
* @param[in] rptrContext Reference to the smart pointer holding the parse context. Must not be NULL.
* @param[in] ptrParent Pointer to the parent class holding this entity. This must not be NULL.
* @param[in] rlstTokenList Reference to the token list holding the tokens to process.
* @param[in] bConst When set, the variable is defined as const.
* @param[in] bAnonymous When set, the variable is part of a struct and anonymous (unnamed and not declared) so its members are
* seen as members of the struct. For example, used with unions.
*/
CVariableEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CTokenList& rlstTokenList, bool bConst,
bool bAnonymous);
/**
* @brief Destructor
*/
virtual ~CVariableEntity() override = default;
/**
* @brief Get the type of the entity. Overload of CEntity::GetType.
* @return Returns the entity type.
*/
virtual sdv::idl::EEntityType GetType() const override
{
return m_bPartOfSwitch ? sdv::idl::EEntityType::type_switch_variable : sdv::idl::EEntityType::type_variable;
}
/**
* @brief Get the qualified type of the entity. Overload of CEntity::GetDeclTypeStr.
* @attention To get the qualified type including array sizes, use the GetDeclTypeStr of the CEntityValueNode class.
* @details The qualified type consists of "<base type> <type identifier>".
* @param[in] bResolveTypedef When set, resolve the typedef type into the base type.
* @return Returns the type string.
*/
virtual std::string GetDeclTypeStr(bool bResolveTypedef) const override;
// Suppress cppcheck warning of a useless override. The function is here for better understanding.
// cppcheck-suppress uselessOverride
/**
* @brief Process the code. Overload of CEntity::Process.
*/
virtual void Process() override;
/**
* @brief Does the entity support assignments? Overload of CDeclarationEntity::SupportAssignments.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportAssignments() const override { return true; }
/**
* @brief Can the entity be used for assignments of complex types? Overload of
* CDeclarationEntity::CanSupportComplexTypeAssignments.
* @return Returns 'true' when the entity defined as declaration; 'false' otherwise.
*/
virtual bool CanSupportComplexTypeAssignments() const override { return true; }
/**
* @brief Does the entity support arrays? Overload of CDeclarationEntity::SupportArrays.
* @return Returns 'true' when the entity supports assignments; 'false' otherwise.
*/
virtual bool SupportArrays() const override { return true; }
/**
* @brief Is the entity readonly? Overload of IEntityInfo::IsReadOnly.
* @return Returns 'true' when the entity defined as readonly; 'false' otherwise.
*/
virtual bool IsReadOnly() const override { return m_bConst; }
/**
* @brief Does the entity require an assignment? Overload of CDeclarationEntity::RequiresAssignment.
* @return Returns 'true' when the entity requires an assignment; 'false' otherwise.
*/
virtual bool RequiresAssignment() const override { return m_bConst; }
/**
* @brief Does the entity support multiple declarations on one line of code? Overload of
* CDeclarationEntity::SupportMultipleDeclarations.
* @return Returns 'true' when the entity supports multiple declarations; 'false' otherwise.
*/
virtual bool SupportMultipleDeclarations() const override { return true; }
/**
* @brief Is the entity anonymous when used in a struct/union (unnamed and not declared)? Overload of
* IDeclarationEntity::IsAnonymous.
* @return Returns 'true' when the entity is anonymous; 'false' otherwise.
*/
virtual bool IsAnonymous() const override { return m_bAnonymous; }
/**
* @brief Enable the variable to be used in a union switch.
*/
void UseAsSwitchVariable() { m_bPartOfSwitch = true; }
protected:
/**
* @brief Set the variable as anonymous variable (unnamed and not declared).
*/
void SetAnonymous() { m_bAnonymous = true; }
/**
* @brief Does the entity support an interface as base type (non-const variables, operations and parameters do)?
* @details Returns whether the entity supports the an interface as base type base type. Default value is 'false'.
* @return Returns 'true' when the entity supports interfaces as base type; 'false' otherwise.
*/
virtual bool SupportInterface() const override { return !m_bConst; }
private:
bool m_bConst = false; ///< When set, the variable is defined as const.
bool m_bAnonymous = false; ///< When set, the variable declared anonymous (unnamed and not declared) so its members
///< are directly part of the parent struct.
bool m_bPartOfSwitch = false; ///< When set, the variable is used in a union switch.
};
#endif // !defined(DECLARATOR_ENTITY_H)

View File

@@ -0,0 +1,320 @@
#include "environment.h"
#include "lexer.h"
#include "codepos.h"
#include "token.h"
#include "codepos.h"
#include "lexer.h"
#include <cuchar>
#include <iostream>
#include <cstdio>
#include <algorithm>
CIdlCompilerEnvironment::CIdlCompilerEnvironment()
{}
CIdlCompilerEnvironment::CIdlCompilerEnvironment(const std::vector<std::string>& rvecArgs) :
m_cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character))
{
// Create a classic argument array
std::vector<const char*> vecArgPtrs;
for (const std::string& rssArg : rvecArgs)
vecArgPtrs.push_back(rssArg.c_str());
// Call the constructor
*this = CIdlCompilerEnvironment(vecArgPtrs.size(), &vecArgPtrs.front());
}
sdv::interface_t CIdlCompilerEnvironment::GetInterface(sdv::interface_id idInterface)
{
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
return static_cast<sdv::IInterfaceAccess*>(this);
if (idInterface == sdv::GetInterfaceId<sdv::idl::ICompilerOption>())
return static_cast<sdv::idl::ICompilerOption*>(this);
return nullptr;
}
std::filesystem::path CIdlCompilerEnvironment::GetNextFile()
{
m_iFileIndex++;
if (static_cast<size_t>(m_iFileIndex) >= m_vecFileNames.size()) return std::filesystem::path();
return m_vecFileNames[static_cast<size_t>(m_iFileIndex)];
}
void CIdlCompilerEnvironment::AddDefinition(const char* szDefinition)
{
CCodePos code(szDefinition);
SLexerDummyCallback sCallback;
CLexer lexer(&sCallback, CaseSensitiveTypeExtension());
// Read the macro name
CToken sNameToken = lexer.GetToken(code, nullptr);
if (!sNameToken || sNameToken.GetType() != ETokenType::token_identifier)
throw CCompileException(code.GetLocation(), "Definition name missing.");
// Check for the next character. If there is no space in between and the next token is a left parenthesis, there are
// parameters.
bool bHasParam = false;
std::vector<std::string> vecParams;
CToken sSymbolToken;
if (*code == '(')
{
// Get the left parenthesis as a token.
sSymbolToken = lexer.GetToken(code, nullptr);
// Parameter bracket has been provided
bHasParam = true;
if (sSymbolToken != "(")
throw CCompileException(sSymbolToken, "Invalid character for macro definition; expecting '('.");
// Read zero or more parameter
bool bDone = false;
bool bInitial = true;
while (!bDone)
{
// Check for closing bracket
sSymbolToken = lexer.GetToken(code, nullptr);
if (bInitial && sSymbolToken == ")")
bDone = true;
else if (sSymbolToken.GetType() != ETokenType::token_identifier)
throw CCompileException(sSymbolToken, "Expecting a parameter name.");
bInitial = false;
if (!bDone)
{
// The token should represent an identifier
CToken sIdentifierToken = sSymbolToken;
// Check for duplicates
if (std::find(vecParams.begin(), vecParams.end(), static_cast<std::string>(sIdentifierToken)) != vecParams.end())
throw CCompileException(sSymbolToken, "Duplicate parameter names for macro definition.");
// Add the parameter to the list
vecParams.push_back(sIdentifierToken);
// Check for a comma or a right parenthesis
sSymbolToken = lexer.GetToken(code, nullptr);
if (sSymbolToken == ")")
bDone = true;
else if (sSymbolToken != ",")
throw CCompileException(sSymbolToken, "Unexpected symbol in parameter list.");
}
}
}
// Check for an equal sign
sSymbolToken = lexer.GetToken(code, nullptr);
std::string ssValue;
if (sSymbolToken)
{
// Expect assignment symbol
if (sSymbolToken != "=")
throw CCompileException(sSymbolToken, "Invalid format.");
ssValue = static_cast<std::string>(code.GetLocation());
} else
{
// Expect no more code
if (!code.HasEOF())
throw CCompileException(sSymbolToken, "Invalid format.");
}
// Create and store the macro structure
CMacro macro(static_cast<std::string>(sNameToken).c_str(), bHasParam ? &vecParams : nullptr, ssValue.c_str());
// Add the macro
AddDefinition(code.GetLocation(), macro);
}
void CIdlCompilerEnvironment::AddDefinition(const CToken& rtoken, const CMacro& rMacro)
{
// Check for the macro to exist already; if so and different, throw exception. If not, add to macro map.
CMacroMap::iterator itMacro = m_mapMacros.find(rMacro.GetName());
if (itMacro != m_mapMacros.end())
{
if (itMacro->second != rMacro)
throw CCompileException(rtoken, "Redefinition of macro with different content.");
} else
m_mapMacros.emplace(std::move(CMacroMap::value_type(rMacro.GetName(), rMacro)));
}
void CIdlCompilerEnvironment::RemoveDefinition(const char* szMacro)
{
if (!szMacro) return;
CMacroMap::iterator itMacro = m_mapMacros.find(szMacro);
if (itMacro != m_mapMacros.end())
m_mapMacros.erase(itMacro);
}
bool CIdlCompilerEnvironment::Defined(const char* szMacro) const
{
if (!szMacro) return false;
return m_mapMacros.find(szMacro) != m_mapMacros.end();
}
bool CIdlCompilerEnvironment::TestAndExpand(const std::string& rssIdentifier, CCodePos& rcode, bool bInMacroExpansion /*= false*/,
CUsedMacroSet& rsetPreviousExpanded /*= CUsedMacroSet()*/) const
{
if (rssIdentifier.empty()) return false;
// Which macro set to use?
CUsedMacroSet& rsetUsedMacros = rsetPreviousExpanded.empty() ? m_setMacrosUsedInExpansion : rsetPreviousExpanded;
// If the provided identifier was created as part of a previous macro expansion (bInMacroExpansion is true) or the macro is
// part of the current expansion (rsetPreviousExpanded is not empty), check whether the identifier defines a macro that was
// used in the previous expansion, which is not allowed.
// If no previous expansion took place or current expansion takes place, clear the expansion set.
if (bInMacroExpansion || !rsetPreviousExpanded.empty())
{
if (rsetUsedMacros.find(rssIdentifier) != rsetUsedMacros.end())
return false;
} else
rsetUsedMacros.clear();
// Test for the existence of a macro.
CMacroMap::const_iterator itMacro = m_mapMacros.find(rssIdentifier);
if (itMacro == m_mapMacros.end()) return false;
// Does the macro need parameters?
CToken token = rcode.GetLocation();
std::vector<std::string> vecParams;
if (itMacro->second.ExpectParameters())
{
// Peek for left parenthesis. If not available, this is not an error. Do not deal with the macro
CCodePos codeTemp(rcode);
SLexerDummyCallback sCallback;
CLexer lexer(&sCallback, CaseSensitiveTypeExtension());
if (lexer.GetToken(codeTemp, nullptr) != "(")
return false;
// Get the parenthesis once more.
lexer.GetToken(rcode, nullptr);
std::string ssParam;
CToken locationParam = rcode.GetLocation();
auto fnTrimCheckAndAdd = [&]()
{
// Erase the whitespace from the beginning of the string
std::string::iterator itStart = std::find_if(ssParam.begin(), ssParam.end(),
[](char c)
{ return !std::isspace<char>(c, std::locale::classic()); });
ssParam.erase(ssParam.begin(), itStart);
// Ersase the whitespace form the end of the string
std::string::reverse_iterator itEnd = std::find_if(ssParam.rbegin(), ssParam.rend(),
[](char c)
{ return !std::isspace<char>(c, std::locale::classic()); });
ssParam.erase(itEnd.base(), ssParam.end());
// Check for a non-empty string
if (ssParam.empty())
throw CCompileException(locationParam, "Missing parameter for macro");
// Add the parameter to the vector.
vecParams.emplace_back(std::move(ssParam));
// Set the token for the new parameter
locationParam = rcode.GetLocation();
};
bool bDone = false;
size_t nInsideParenthesis = 0;
while (!bDone)
{
switch(*rcode)
{
case '(':
nInsideParenthesis++;
ssParam += *rcode;
token = rcode.GetLocation();
break;
case ')':
if (nInsideParenthesis)
{
nInsideParenthesis--;
ssParam += *rcode;
} else
{
fnTrimCheckAndAdd();
bDone = true;
}
break;
case ',':
if (nInsideParenthesis)
ssParam += *rcode;
else
fnTrimCheckAndAdd();
break;
case '\0':
throw CCompileException(rcode.GetLocation(), "Unexpected end of file while parsing macro parameters.");
break;
default:
ssParam += *rcode;
break;
}
++rcode;
}
}
// The macro will be expanded, add it to the expansion set
rsetUsedMacros.insert(rssIdentifier);
// Expand the macro and prepend the string to the code.
rcode.PrependCode(itMacro->second.Expand(*this, token, vecParams, rsetUsedMacros));
return true;
}
bool CIdlCompilerEnvironment::ResolveConst() const
{
return m_bResolveConst;
}
bool CIdlCompilerEnvironment::NoProxyStub() const
{
return m_bNoPS;
}
const std::string& CIdlCompilerEnvironment::GetProxStubCMakeTarget() const
{
return m_ssProxyStubLibName;
}
sdv::u8string CIdlCompilerEnvironment::GetOption(const sdv::u8string& rssOption) const
{
return GetOptionN(rssOption, 0);
}
uint32_t CIdlCompilerEnvironment::GetOptionCnt(const sdv::u8string& rssOption) const
{
if (rssOption.empty()) return 0;
if (rssOption == sdv::idl::ssOptionDevEnvDir)
return 1;
else if (rssOption == sdv::idl::ssOptionOutDir)
return 1;
else if (rssOption == sdv::idl::ssOptionFilename)
return 1;
else if (rssOption == sdv::idl::ssOptionFilePath)
return 1;
else if (rssOption == sdv::idl::ssOptionCodeGen)
return 10;
return 0;
}
sdv::u8string CIdlCompilerEnvironment::GetOptionN(const sdv::u8string& rssOption, uint32_t nIndex) const
{
if (rssOption.empty()) return sdv::u8string();
if (nIndex >= GetOptionCnt(rssOption)) return sdv::u8string();
sdv::u8string ssValue;
if (rssOption == sdv::idl::ssOptionDevEnvDir)
ssValue = m_pathCompilerPath.parent_path().generic_u8string();
else if (rssOption == sdv::idl::ssOptionOutDir)
ssValue = "";
else if (rssOption == sdv::idl::ssOptionFilename)
ssValue = "";
else if (rssOption == sdv::idl::ssOptionFilePath)
ssValue = "";
else if (rssOption == sdv::idl::ssOptionCodeGen)
ssValue = "";
return ssValue;
}

View File

@@ -0,0 +1,316 @@
#ifndef ENV_H
#define ENV_H
#include <filesystem>
#include <set>
#include <stack>
#include "logger.h"
#include "core_idl_backup.h"
#include "../global/cmdlnparser/cmdlnparser.h"
#include "includes.h"
#include "macro.h"
#include "token.h"
#include "exception.h"
/**
* @brief Parser environment management class.
*/
class CIdlCompilerEnvironment
: public sdv::idl::ICompilerOption
, public sdv::IInterfaceAccess
{
public:
/**
* @brief Default constructor
*/
CIdlCompilerEnvironment();
/**
* @brief Constructor with program arguments.
* @param[in] rvecArgs Reference to the vector with program arguments
*/
CIdlCompilerEnvironment(const std::vector<std::string>& rvecArgs);
/**
* @brief Constructor with program arguments.
* @tparam TCharType Character type.
* @param[in] nArgs The amount of arguments.
* @param[in] rgszArgs Array of arguments.
*/
template <typename TCharType>
CIdlCompilerEnvironment(size_t nArgs, const TCharType** rgszArgs);
/**
* @brief Get access to another interface. Overload of IInterfaceAccess::GetInterface.
* @param[in] idInterface The interface id to get access to.
* @return Returns a pointer to the interface or NULL when the interface is not supported.
*/
virtual sdv::interface_t GetInterface(sdv::interface_id idInterface) override;
/**
* @brief Get the path of the next file.
* @return Returns the path to the next file or an empty path.
*/
std::filesystem::path GetNextFile();
/**
* @brief Help was requested on the command line.
* @return Returns 'true' when help was requested. Otherwise returns 'false'.
*/
bool Help() const { return m_bCLHelp; }
/**
* @brief Show command line help.
*/
void ShowHelp() const { m_cmdln.PrintHelp(std::cout); }
/**
* @brief Version information was requested on the command line.
* @return Returns 'true' when version info was requested. Otherwise returns 'false'.
*/
bool Version() const { return m_bCLVersion; }
/**
* @brief Get the vector of include directories.
* @return Reference to the vector of include directories.
*/
const std::vector<std::filesystem::path>& GetIncludeDirs() const { return m_vecIncludeDirs; }
/**
* @brief Get the output directory.
* @return The output directory.
*/
std::filesystem::path GetOutputDir() const { return m_pathOutputDir; }
/**
* @brief Add macro definition in the form of name, name=value or name(arg1, arg2, arg3)=value.
* @param[in] szDefinition The definition string
*/
void AddDefinition(const char* szDefinition);
/**
* @brief Add macro definition.
* @param[in] rtoken Token in the source file of the provided macro.
* @param[in] rMacro Reference to the macro to add.
*/
void AddDefinition(const CToken& rtoken, const CMacro& rMacro);
/**
* @brief Remove macro definition.
* @param[in] szMacro The name of the macro.
* @remarks If the macro doesn#t exists, doesn't do anything.
*/
void RemoveDefinition(const char* szMacro);
/**
* @brief Check whether a macro definition exists.
* @param[in] szMacro The name of the macro.
* @returns Returns 'true' when the definition exists; 'false' otherwise.
*/
bool Defined(const char* szMacro) const;
/**
* @brief Test for a macro and replace the code.
* @details This function will check for the existence of the macro with the supplied name. If it does, it will read any
* parameter from the code (depends on whether the macro needs parameters) and create a string with the filled in parameters.
* This string, then, is prepended to the code replacing the identifiert and its optional parameters. Reparsing of the code
* needs to take place - the return value is 'true'. If there is no macro with the supplied name, the return value is 'false'
* and the supplied identifier is to be treated as an identifier.
* Macro parameters can have commas if they are parenthesized before.
* The value can have the '#' unary operator to stringificate the following parameter and the '##' operator to join the
* preceding or succeeding operator to the adjacent identifier.
* @param[in] rssIdentifier Reference to the string object containing the name of the identifier to test for macro definition.
* @param[in, out] rcode The code holding the potential parameters and to be replaced and prepended with the resolved macro.
* @param[in] bInMacroExpansion Set 'true' when the identifier was the (part) result of a macro expansion. If this is the
* case, previously used macros cannot be reused - to prevent circular expansion.
* @param[in, out] rsetPreviousExpanded Reference to the set of previously expanded macros, preventing circular expansion. If
* the used macro set is empty, use the default set of the environment. This set will b eextended with macros used within the
* macro expansion.
* @return Returns 'true' if macro replacement was successful, or 'false' when the identifier is not a macro.
*/
bool TestAndExpand(const std::string& rssIdentifier,
CCodePos& rcode,
bool bInMacroExpansion,
CUsedMacroSet& rsetPreviousExpanded) const;
/**
* @brief Test for a macro and replace the code.
* @details This function will check for the existence of the macro with the supplied name. If it does, it will read any
* parameter from the code (depends on whether the macro needs parameters) and create a string with the filled in parameters.
* This string, then, is prepended to the code replacing the identifiert and its optional parameters. Reparsing of the code
* needs to take place - the return value is 'true'. If there is no macro with the supplied name, the return value is 'false'
* and the supplied identifier is to be treated as an identifier.
* Macro parameters can have commas if they are parenthesized before.
* The value can have the '#' unary operator to stringificate the following parameter and the '##' operator to join the
* preceding or succeeding operator to the adjacent identifier.
* @param[in] rssIdentifier Reference to the string object containing the name of the identifier to test for macro definition.
* @param[in, out] rcode The code holding the potential parameters and to be replaced and prepended with the resolved macro.
* @param[in] bInMacroExpansion Set 'true' when the identifier was the (part) result of a macro expansion. If this is the
* case, previously used macros cannot be reused - to prevent circular expansion.
* @return Returns 'true' if macro replacement was successful, or 'false' when the identifier is not a macro.
*/
bool TestAndExpand(const std::string& rssIdentifier, CCodePos& rcode, bool bInMacroExpansion = false) const
{
CUsedMacroSet setDummy;
return TestAndExpand(rssIdentifier, rcode, bInMacroExpansion, setDummy);
}
/**
* @brief Resolve const expressions.
* @return Returns 'true' when const expressions should be resolved. Otherwise the expression should be exported during code
* generation.
*/
bool ResolveConst() const;
/**
* @brief Suppress the generation of proxy and stub code.
* @return Returns 'true' when proxy stub code should not be generated. Otherwise the proxy and stub code should be generated.
*/
bool NoProxyStub() const;
/**
* @brief Get library target name for the proxy stub cmake file.
* @return Returns a reference to the target name if set in the command line or an empty string otherwise.
*/
const std::string& GetProxStubCMakeTarget() const;
/**
* @brief Get the compiler option. Overload of sdv::idl::ICompilerOption::GetOption.
* @param[in] rssOption Reference to the string containing the name of the option to retrieve.
* @return The requested compiler option (if available).
*/
virtual sdv::u8string GetOption(/*in*/ const sdv::u8string& rssOption) const override;
/**
* @brief Get the amount of option values. Overload of sdv::idl::ICompilerOption::GetOptionCnt.
* @param[in] rssOption Reference to the string containing the name of the option to retrieve.
* @return The amount of option values.
*/
virtual uint32_t GetOptionCnt(/*in*/ const sdv::u8string& rssOption) const override;
/**
* @brief Get the compiler option. Overload of sdv::idl::ICompilerOption::GetOptionN.
* @param[in] rssOption Reference to the string containing the name of the option to retrieve.
* @param[in] uiIndex The index of the option value.
* @return The requested compiler option value.
*/
virtual sdv::u8string GetOptionN(/*in*/ const sdv::u8string& rssOption, /*in*/ uint32_t uiIndex) const override;
/**
* @{
* @brief Extensions
* @return Returns true when the extension is enabled; false when not.
*/
bool InterfaceTypeExtension() const { return m_bEnableInterfaceTypeExtension; }
bool ExceptionTypeExtension() const { return m_bEnableExceptionTypeExtension; }
bool PointerTypeExtension() const { return m_bEnablePointerTypeExtension; }
bool UnicodeExtension() const { return m_bEnableUnicodeExtension; }
bool CaseSensitiveTypeExtension() const { return m_bEnableCaseSensitiveNameExtension; }
bool ContextDependentNamesExtension() const { return m_bEnableContextDependentNamesExtension; }
bool MultiDimArrayExtension() const { return m_bEnableMultiDimArrayExtension; }
/**
* @}
*/
private:
CCommandLine m_cmdln; ///< Command line parsing class.
CMacroMap m_mapMacros; ///< Map containing macros.
std::vector<std::filesystem::path> m_vecIncludeDirs; ///< Vector containing all search paths for finding files.
std::vector<std::filesystem::path> m_vecFileNames; ///< Vector containing file paths of the files to process.
std::filesystem::path m_pathOutputDir; ///< Output directory.
mutable CUsedMacroSet m_setMacrosUsedInExpansion; ///< Set of macros used in the expansion to prevent circular expansion
///< when the macro being expanded generate a call to itself.
bool m_bCLHelp = false; ///< Help was requested at the command line.
bool m_bCLVersion = false; ///< Version info was request at the command line.
bool m_bResolveConst = false; ///< When set, store the value of a const assignment; otherwise, store
///< the expression of the const assignment.
bool m_bNoPS = false; ///< Doesn't generate proxy/stub code.
std::string m_ssProxyStubLibName = "proxystub"; ///< Proxy and stub library target name in the generated cmake file.
std::filesystem::path m_pathCompilerPath; ///< The path to the compiler.
int32_t m_iFileIndex = -1; ///< The IDL file to process.
bool m_bEnableInterfaceTypeExtension = true; ///< Enable the 'inteface_t', 'inteface_id' and 'null' keywords.
bool m_bEnableExceptionTypeExtension = true; ///< Enable the 'exception_id' keyword.
bool m_bEnablePointerTypeExtension = true; ///< Enable the 'pointer' keyword.
bool m_bEnableUnicodeExtension = true; ///< Enable UTF-8, UTF-16 and UTF-32 character and string extensions.
bool m_bEnableCaseSensitiveNameExtension = true; ///< Enable sensitive name restriction.
bool m_bEnableContextDependentNamesExtension = true; ///< Enable relaxed uniqueness extension.
bool m_bEnableMultiDimArrayExtension = true; ///< Enable multi-dimensional array support.
};
template <typename TCharType>
CIdlCompilerEnvironment::CIdlCompilerEnvironment(size_t nArgs, const TCharType** rgszArgs) :
m_cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character))
{
try
{
bool bSilent = false;
bool bVerbose = false;
bool bStrict = false;
std::vector<std::string> vecMacros;
auto& rArgHelpDef = m_cmdln.DefineOption("?", m_bCLHelp, "Show help");
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = m_cmdln.DefineOption("s", bSilent, "Do not show any information on STDOUT. Not compatible with 'verbose'.");
rArgSilentDef.AddSubOptionName("silent");
auto& rArgVerboseDef = m_cmdln.DefineOption("v", bVerbose, "Provide verbose information. Not compatible with 'silent'.");
rArgVerboseDef.AddSubOptionName("verbose");
m_cmdln.DefineSubOption("version", m_bCLVersion, "Show version information");
m_cmdln.DefineOption("I", m_vecIncludeDirs, "Set include directory");
m_cmdln.DefineOption("O", m_pathOutputDir, "Set output directory");
m_cmdln.DefineOption("D", vecMacros, "Set a macro definition in the form of macro, macro=<value>, macro(arg1,...)=<value>");
m_cmdln.DefineSubOption("resolve_const", m_bResolveConst, "Use the calculated value for const values, instead of the defined expression.");
m_cmdln.DefineSubOption("no_ps", m_bNoPS, "Do not create any proxy and stub code (interface definitions only).");
m_cmdln.DefineSubOption("ps_lib_name", m_ssProxyStubLibName, "Proxy and stub library target name in the generated cmake file (default=\"proxystub\").");
m_cmdln.DefineGroup("Extensions", "Enable/disable compatibility extensions.");
m_cmdln.DefineFlagSubOption("interface_type", m_bEnableInterfaceTypeExtension, "Enable/disable support of the interface type extensions 'interface_t' and 'interface_id'.Default enabled.");
m_cmdln.DefineFlagSubOption("exception_type", m_bEnableExceptionTypeExtension, "Enable/disable support of the exception type extension 'exception_id'. Default enabled.");
m_cmdln.DefineFlagSubOption("pointer_type", m_bEnablePointerTypeExtension, "Enable/disable support of the pointer type extension 'pointer'. Default enabled.");
m_cmdln.DefineFlagSubOption("unicode_char", m_bEnableUnicodeExtension, "Enable/disable support of the UTF-8, UTF-16 and UTF-32 Unicode extensions. Default enabled.");
m_cmdln.DefineFlagSubOption("case_sensitive", m_bEnableCaseSensitiveNameExtension, "Enable/disable case sensitive name restriction extension. Default enabled.");
m_cmdln.DefineFlagSubOption("context_names", m_bEnableContextDependentNamesExtension, "Enable/disable the use of context dependent names in declarations. Default enabled.");
m_cmdln.DefineFlagSubOption("multi_dimensional_array", m_bEnableMultiDimArrayExtension, "Enable/disable support of multi-dimensional arrays extension. Default enabled.");
m_cmdln.DefineSubOption("strict", bStrict, "Strictly maintaining OMG-IDL conformance; disabling extensions.");
m_cmdln.DefineDefaultArgument(m_vecFileNames, "IDL files");
m_cmdln.Parse(nArgs, rgszArgs);
// Add the macros.
for (const std::string& rssMacro : vecMacros)
{
try
{
AddDefinition(rssMacro.c_str());
}
catch (const sdv::idl::XCompileError&)
{
throw CCompileException("Invalid command line option: -D");
}
}
// If strict, disable all extensions.
if (bStrict)
{
m_bEnableInterfaceTypeExtension = false;
m_bEnableExceptionTypeExtension = false;
m_bEnablePointerTypeExtension = false;
m_bEnableUnicodeExtension = false;
m_bEnableCaseSensitiveNameExtension = false;
m_bEnableContextDependentNamesExtension = false;
m_bEnableMultiDimArrayExtension = false;
}
// Set the verbosity mode.
if (bSilent)
g_log_control.SetVerbosityMode(EVerbosityMode::report_none);
else if (bVerbose)
g_log_control.SetVerbosityMode(EVerbosityMode::report_all);
}
catch (const SArgumentParseException& rsExcept)
{
m_bCLHelp = true;
throw CCompileException(rsExcept.what());
}
}
#endif // !defined ENV_H

View File

@@ -0,0 +1,50 @@
#include "exception.h"
#include <cassert>
CCompileException::CCompileException(const sdv::idl::XCompileError& rxCompileError)
{
static_cast<sdv::idl::XCompileError&>(*this) = rxCompileError;
}
std::string CCompileException::GetPath() const
{
return ssFile;
}
std::string CCompileException::GetReason() const
{
return ssReason;
}
void CCompileException::SetPath(const std::filesystem::path& rpath)
{
if (ssFile.empty())
ssFile = rpath.generic_u8string();
}
void CCompileException::SetLocation(const CToken& rtoken)
{
uiLine = rtoken.GetLine();
uiCol = rtoken.GetCol();
ssToken = rtoken;
}
uint32_t CCompileException::GetLineNo() const
{
return uiLine;
}
uint32_t CCompileException::GetColNo() const
{
return uiCol;
}
std::string CCompileException::GetToken() const
{
return ssToken;
}
std::string CCompileException::GetLine() const
{
return ssLine;
}

View File

@@ -0,0 +1,170 @@
#ifndef EXCEPTION_H
#define EXCEPTION_H
#include "logger.h"
#include "token.h"
#include <exception>
#include <sstream>
#include <cassert>
#include <filesystem>
#include <iostream>
// Forward declaration
class CParser;
/**
* @brief Lexer exception
*/
struct CCompileException : public sdv::idl::XCompileError
{
friend class CParser;
public:
/**
* @brief Copy the compile error.
* @param[in] rxCompileError Reference to the compile error.
*/
CCompileException(const sdv::idl::XCompileError& rxCompileError);
/**
* @brief Constructor without token and path. The parser might add the last used token.
* @remarks The path can be added to the exception using the SetPath function.
* @param[in] szReason Zero terminated string containing the reason of the exception. Must not be NULL.
* @param[in] tAdditionalReasons Optional other reasons.
*/
template <class... TAdditionalReason>
explicit CCompileException(const char* szReason, TAdditionalReason... tAdditionalReasons);
/**
* @brief Constructor without path.
* @remarks The path can be added to the exception using the SetPath function.
* @param[in] rtoken Token in the source file that triggered the exception.
* @param[in] szReason Zero terminated string containing the reason of the exception. Must not be NULL.
* @param[in] tAdditionalReasons Optional other reasons.
*/
template <class... TAdditionalReason>
explicit CCompileException(const CToken& rtoken, const char* szReason, TAdditionalReason... tAdditionalReasons);
/**
* @brief Constructor with path.
* @param[in] rpath Reference to the source file path.
* @param[in] rtoken Token in the source file that triggered the exception.
* @param[in] szReason Zero terminated string containing the reason of the exception. Must not be NULL.
* @param[in] tAdditionalReasons Optional other reasons.
*/
template <class... TAdditionalReason>
explicit CCompileException(const std::filesystem::path& rpath, const CToken& rtoken,
const char* szReason, TAdditionalReason... tAdditionalReasons);
/**
* @brief Get the path.
* @return Reference to the path of the file causing the exception (if there is one).
*/
std::string GetPath() const;
/**
* @brief Get the reason.
* @return The explanatory text.
*/
std::string GetReason() const;
/**
* @brief Get the line number (starts at 1).
* @return The line number or 0 when there is no specific code causing this error.
*/
uint32_t GetLineNo() const;
/**
* @brief Get the column number (starts at 1).
* @return The column number or 0 when there is no specific code causing this error.
*/
uint32_t GetColNo() const;
/**
* @brief Get the token causing the compilation error.
* @return The token string or an empty string when no specific code is causing this error.
*/
std::string GetToken() const;
/**
* @brief Get the line containing the error.
* @return The line that contains the error or an empty string when no specific code is causiong this error.
*/
std::string GetLine() const;
private:
/**
* @brief Set the path if not assigned before.
* @param[in] rpath Reference to the source file path.
*/
void SetPath(const std::filesystem::path& rpath);
/**
* @brief Set the location if not assigned before.
* @param[in] rtoken The token that caused the exception.
*/
void SetLocation(const CToken& rtoken);
};
template <class TParam>
void AddParamListToString(std::stringstream& rsstream, TParam param)
{
rsstream << param;
}
template <class TParam, class... TAdditionalParams>
void AddParamListToString(std::stringstream& rsstream, TParam param, TAdditionalParams... tAdditionalParams)
{
rsstream << param;
AddParamListToString(rsstream, tAdditionalParams...);
}
template <class... TAdditionalReason>
CCompileException::CCompileException(const char* szReason, TAdditionalReason... tAdditionalReasons) : sdv::idl::XCompileError{}
{
assert(szReason);
if (szReason)
{
std::stringstream sstream;
AddParamListToString(sstream, szReason, tAdditionalReasons...);
ssReason = sstream.str();
}
CLog log;
log << "EXCEPTION: " << ssReason << std::endl;
}
template <class... TAdditionalReason>
CCompileException::CCompileException(const CToken& rtoken, const char* szReason, TAdditionalReason... tAdditionalReasons) :
sdv::idl::XCompileError{}
{
assert(szReason);
if (szReason)
{
std::stringstream sstream;
AddParamListToString(sstream, szReason, tAdditionalReasons...);
ssReason = sstream.str();
}
SetLocation(rtoken);
CLog log;
log << "EXCEPTION " << "[" << GetLineNo() << ", " << GetColNo() << "]: " << ssReason << std::endl;
}
template <class... TAdditionalReason>
CCompileException::CCompileException(const std::filesystem::path& rpath, const CToken& rtoken,
const char* szReason, TAdditionalReason... tAdditionalReasons) :
CCompileException::CCompileException(rtoken, szReason, tAdditionalReasons...)
{
assert(szReason);
if (szReason)
{
std::stringstream sstream;
AddParamListToString(sstream, szReason, tAdditionalReasons...);
ssReason = sstream.str();
}
SetLocation(rtoken);
ssFile = rpath.generic_u8string();
CLog log;
log << "EXCEPTION " << rpath.generic_u8string() << " [" << GetLineNo() << ", " << GetColNo() << "]: " << ssReason << std::endl;
}
#endif // !defined EXCEPTION_H

View File

@@ -0,0 +1,167 @@
#include "context.h"
#include "cmake_generator.h"
#include "../exception.h"
#include <cassert>
#include <cctype>
#include <fstream>
#include <algorithm>
#include <set>
#include <mutex>
CIdlCompilerCMakeGenerator::CIdlCompilerCMakeGenerator(sdv::IInterfaceAccess* pParser) : CGenContext(pParser), m_mtx("SDV_IDL_COMPILER_GENERATE_CMAKE")
{}
CIdlCompilerCMakeGenerator::~CIdlCompilerCMakeGenerator()
{}
bool CIdlCompilerCMakeGenerator::Generate(const std::string& ssTargetLibName)
{
// Synchronize CMake code generation among processes.
std::unique_lock<ipc::named_mutex> lock(m_mtx);
if (ssTargetLibName.empty())
throw CCompileException("No target library name defined for proxy/stub CMake file.");
// Create "proxy" directory
std::filesystem::path pathPSTarget = GetOutputDir() / "ps";
if (!std::filesystem::exists(pathPSTarget) && !std::filesystem::create_directory(pathPSTarget))
throw CCompileException("Cannot create proxy/stub directory: ", pathPSTarget.generic_u8string());
// The source string
std::string ssSource;
// File with "CMakeLists.txt" function; read completely if existing
std::filesystem::path pathFile = pathPSTarget / "CMakeLists.txt";
if (g_log_control.GetVerbosityMode() == EVerbosityMode::report_all)
std::cout << "Target file: " << pathFile.generic_u8string() << std::endl;
// Create or update CMakeLists.txt
if (std::filesystem::exists(pathFile))
{
std::ifstream stream;
stream.open(pathFile);
if (!stream.is_open()) throw CCompileException("Failed to open the CMakeLists.txt file for reading.");
// Read the complete source
std::stringstream sstream;
sstream << stream.rdbuf();
ssSource = std::move(sstream.str());
}
else // Create the file in memory
{
CKeywordMap mapKeywords;
mapKeywords.insert(std::make_pair("target_lib_name", ssTargetLibName));
ssSource = ReplaceKeywords(R"code(# Enforce CMake version 3.20 or newer needed for path function
cmake_minimum_required (VERSION 3.20)
# Use new policy for project version settings and default warning level
cmake_policy(SET CMP0048 NEW) # requires CMake 3.14
cmake_policy(SET CMP0092 NEW) # requires CMake 3.15
# Define project
project(%target_lib_name% VERSION 1.0 LANGUAGES CXX)
# Use C++17 support
set(CMAKE_CXX_STANDARD 17)
# Library symbols are hidden by default
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# Set target name.
set(TARGET_NAME %target_lib_name%)
# Set the SDV_FRAMEWORK_DEV_INCLUDE if not defined yet
if (NOT DEFINED SDV_FRAMEWORK_DEV_INCLUDE)
if (NOT DEFINED ENV{SDV_FRAMEWORK_DEV_INCLUDE})
message( FATAL_ERROR "The environment variable SDV_FRAMEWORK_DEV_INCLUDE needs to be pointing to the SDV V-API development include files location!")
endif()
set (SDV_FRAMEWORK_DEV_INCLUDE "$ENV{SDV_FRAMEWORK_DEV_INCLUDE}")
endif()
# Include link to export directory of SDV V-API development include files location
include_directories(${SDV_FRAMEWORK_DEV_INCLUDE})
# Set platform specific compile flags
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/W4 /WX /wd4996 /wd4100 /permissive- /Zc:rvalueCast)
else()
add_compile_options(-Werror -Wall -Wextra -Wshadow -Wpedantic -Wunreachable-code -fno-common)
endif()
# Add the dynamic library
add_library(${TARGET_NAME} SHARED)
# Set extension to .sdv
set_target_properties(${TARGET_NAME} PROPERTIES PREFIX "")
set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".sdv")
# TODO: set target name.
#add_dependencies(${TARGET_NAME} <add_cmake_target_this_depends_on>)
)code", mapKeywords);
}
// Search function for caseless finding in the string.
auto fnNCFind = [&](const std::string& rssText, size_t nPos = 0) -> size_t
{
auto it = std::search(ssSource.begin() + nPos, ssSource.end(), rssText.begin(), rssText.end(),
[](unsigned char ch1, unsigned char ch2) { return std::tolower(ch1) == std::tolower(ch2); }
);
if (it == ssSource.end()) return std::string::npos;
return std::distance(ssSource.begin(), it);
};
// Find the add_library function
size_t nPos = fnNCFind("add_library");
if (nPos == std::string::npos) throw CCompileException("Missing 'add_library' keyword.");
// Search for shared keyword
nPos = fnNCFind("shared", nPos);
if (nPos == std::string::npos) throw CCompileException("Missing 'shared' keyword.");
nPos += 6;
// Build set with files
size_t nStop = fnNCFind(")", nPos);
if (nStop == std::string::npos) throw CCompileException("Missing ')' closing the 'add_library' statement.");
std::set<std::string> setFiles;
while (nPos < nStop)
{
// Skip whitespace
while (std::isspace(ssSource[nPos])) nPos++;
// Read file name
size_t nFileBegin = nPos;
while (nPos < nStop && !std::isspace(ssSource[nPos])) nPos++;
// Store the file
setFiles.insert(ssSource.substr(nFileBegin, nPos - nFileBegin));
}
// Insert additional files if needed
size_t nSourceSize = ssSource.size();
std::filesystem::path pathPSFileBase = GetSource().filename();
pathPSFileBase.replace_extension("");
std::string ssFileBase = pathPSFileBase.generic_u8string();
if (setFiles.find(ssFileBase + "_stub.cpp") == setFiles.end())
ssSource.insert(nStop, std::string("\n ") + ssFileBase + "_stub.cpp");
if (setFiles.find(ssFileBase + "_stub.h") == setFiles.end())
ssSource.insert(nStop, std::string("\n ") + ssFileBase + "_stub.h");
if (setFiles.find(ssFileBase + "_proxy.cpp") == setFiles.end())
ssSource.insert(nStop, std::string("\n ") + ssFileBase + "_proxy.cpp");
if (setFiles.find(ssFileBase + "_proxy.h") == setFiles.end())
ssSource.insert(nStop, std::string("\n ") + ssFileBase + "_proxy.h");
// Write the file again if needed
if (nSourceSize != ssSource.size())
{
std::ofstream stream;
stream.open(pathFile, std::ofstream::trunc);
if (!stream.is_open()) throw CCompileException("Failed to open the CMakeLists.txt file for writing.");
// Write the complete source
stream << ssSource;
}
// Done!
return true;
}

View File

@@ -0,0 +1,34 @@
#ifndef CMAKE_GENERATOR_H
#define CMAKE_GENERATOR_H
#include "../../../global/ipc_named_mutex.h"
/**
* @brief CMake generator class.
*/
class CIdlCompilerCMakeGenerator : public CGenContext
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CIdlCompilerCMakeGenerator(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CIdlCompilerCMakeGenerator() override;
/**
* @brief Generate the definition.
* @param[in] ssTargetLibName Library target name to add in the cmake file.
* @return Returns whether the generation was successful.
*/
bool Generate(const std::string& ssTargetLibName);
private:
ipc::named_mutex m_mtx; ///< Guarantee exclusive access while writing the CMake file.
};
#endif // !defined CMAKE_GENERATOR_H

View File

@@ -0,0 +1,499 @@
#include "context.h"
#include "../exception.h"
#include <cassert>
#include <cctype>
#include <fstream>
#include <ctime>
#include <chrono>
#include <iomanip>
#include <functional>
#include <vector>
CGenContext::CGenContext(sdv::IInterfaceAccess* pParser) : m_pParser(pParser)
{
if (!m_pParser) throw CCompileException("Internal error: no valid parser pointer supplied to generator.");
m_pCompilerInfo = m_pParser->GetInterface<sdv::idl::ICompilerInfo>();
if (!m_pCompilerInfo) throw CCompileException("Internal error: compiler info is not available.");
m_pOption = m_pParser->GetInterface<sdv::idl::ICompilerOption>();
if (!m_pOption) throw CCompileException("Internal error: cannot access options interface.");
}
CGenContext::~CGenContext()
{}
std::filesystem::path CGenContext::GetSource() const
{
if (!m_pCompilerInfo) return std::filesystem::path();
std::filesystem::path pathSource = static_cast<std::string>(m_pCompilerInfo->GetFilePath());
if (pathSource.empty()) throw CCompileException("Internal error: file path is not available.");
return pathSource;
}
std::filesystem::path CGenContext::GetOutputDir() const
{
if (!m_pCompilerInfo) return std::filesystem::path();
std::filesystem::path pathOutputDir = static_cast<std::string>(m_pCompilerInfo->GetOutputDir());
if (pathOutputDir.empty())
pathOutputDir = GetSource().parent_path();
return pathOutputDir;
}
std::string CGenContext::Header(const std::filesystem::path& rpathFile,
const std::string& rssDescription /*= std::string()*/) const
{
std::stringstream sstream;
// Add file header
sstream << "/**" << std::endl;
sstream << " * @file " << rpathFile.filename().generic_u8string() << std::endl;
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
sstream << " * @date " << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X") << std::endl;
sstream << " * This file was generated by the SDV IDL compiler from '" << GetSource().filename().generic_u8string() << "'" <<
std::endl;
if (!rssDescription.empty())
{
// Insert the JavaDoc marks before each line
size_t nPos = 0;
while (nPos < rssDescription.size())
{
size_t nEnd = rssDescription.find_first_of("\r\n", nPos);
sstream << " * " << rssDescription.substr(nPos, nEnd == std::string::npos ? nEnd : nEnd - nPos) << std::endl;
nPos = nEnd;
if (nPos < rssDescription.size() && rssDescription[nPos] == '\r')
nPos++;
if (nPos < rssDescription.size() && rssDescription[nPos] == '\n')
nPos++;
}
}
sstream << " */" << std::endl;
sstream << std::endl;
return sstream.str();
}
std::string CGenContext::Safeguard(const std::filesystem::path& rpathFile, bool bInitial)
{
// Safeguards start with "__IDL_GENERATED__", add the file name, add the date and time and end with "__"
std::stringstream sstreamSafeguard;
sstreamSafeguard << "__IDL_GENERATED__";
std::string ssFile = rpathFile.filename().generic_u8string();
for (char c : ssFile)
{
if ((c < '0' || c > '9') &&
(c < 'a' || c > 'z') &&
(c < 'A' || c > 'Z'))
sstreamSafeguard << '_';
else
sstreamSafeguard << static_cast<char>(std::toupper(c));
}
sstreamSafeguard << "__";
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
sstreamSafeguard << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S") << "_";
sstreamSafeguard << std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
sstreamSafeguard << "__";
// Return the safeguard code
std::stringstream sstream;
if (bInitial)
sstream << "#ifndef " << sstreamSafeguard.str() << std::endl << "#define " << sstreamSafeguard.str() << std::endl << std::endl;
else
sstream << std::endl << "#endif // !defined(" << sstreamSafeguard.str() << ")" << std::endl;
return sstream.str();
}
std::string CGenContext::SmartIndent(const std::string& rssStr, const std::string& rssIndent)
{
// Determine the amount of whitespace at the beginning of the string and replace this whitespace by the current indent. Do
// this for every line.
// Use four spaces for every tab (to allow mixed tab and space usage).
size_t nDetectedIndentation = 0;
size_t nPos = 0;
enum class EState {init, skip_indent, code} eState = EState::init;
while (eState == EState::init && nPos < rssStr.size())
{
switch (rssStr[nPos])
{
case ' ':
nDetectedIndentation++;
nPos++;
break;
case '\t':
nDetectedIndentation += 4;
nPos++;
break;
default:
eState = EState::code;
break;
}
}
std::stringstream sstream;
while (nPos < rssStr.size())
{
// Skip indentation
size_t nSkip = 0;
while (eState == EState::skip_indent && nSkip < nDetectedIndentation)
{
switch (rssStr[nPos])
{
case ' ':
nSkip++;
nPos++;
break;
case '\t':
nSkip +=4;
nPos++;
break;
default:
eState = EState::code;
break;
}
}
eState = EState::code;
// Find the next newline
size_t nEnd = rssStr.find_first_of("\r\n", nPos);
std::string ssSubstr = rssStr.substr(nPos, nEnd == std::string::npos ? nEnd : nEnd - nPos);
// If the string didn't start with a number character, remove the line concatinating character if it's there.
if (rssStr[0] != '#' && !ssSubstr.empty() && *ssSubstr.rbegin() == '\\')
ssSubstr.resize(ssSubstr.size() - 1);
// Remove whitespace at the end of the string
while (!ssSubstr.empty() && std::isspace(*ssSubstr.rbegin()))
ssSubstr.resize(ssSubstr.size() - 1);
// Stream the sub-string with indentation if the string didn't start with a number character.
sstream << (rssStr[0] != '#' ? rssIndent : "") << ssSubstr;
// Stream and skip newline
nPos = nEnd;
if (nPos < rssStr.size())
{
if (rssStr[nPos] == '\r')
nPos++;
if (rssStr[nPos] == '\n')
nPos++;
eState = EState::skip_indent;
}
sstream << std::endl;
}
return sstream.str();
}
std::string CGenContext::QualifyName(const std::string& rssName)
{
std::stringstream sstream;
size_t nPos = 0;
while (nPos < rssName.size())
{
size_t nSeparator = rssName.find_first_of(":.[]", nPos);
sstream << rssName.substr(nPos, nSeparator == std::string::npos ? nSeparator : nSeparator - nPos);
nPos = nSeparator;
if (nPos != std::string::npos)
{
if (rssName[nPos] != ']')
sstream << "_";
nPos++;
}
}
return sstream.str();
}
std::string CGenContext::ReplaceKeywords(const std::string& rssStr, const CKeywordMap& rmapKeywords, char cMarker /*= '%'*/)
{
std::stringstream sstream;
size_t nPos = 0;
while (nPos < rssStr.size())
{
// Find the initial separator
size_t nSeparator = rssStr.find(cMarker, nPos);
sstream << rssStr.substr(nPos, nSeparator == std::string::npos ? nSeparator : nSeparator - nPos);
nPos = nSeparator;
if (nSeparator == std::string::npos) continue;
nPos++;
// Find the next separator.
nSeparator = rssStr.find(cMarker, nPos);
if (nSeparator == std::string::npos)
throw CCompileException("Internal error: missing second separator during code generation.");
// Find the keyword in the keyword map (between the separator and the position).
CKeywordMap::const_iterator itKeyword = rmapKeywords.find(rssStr.substr(nPos, nSeparator - nPos));
if (itKeyword == rmapKeywords.end())
{
std::stringstream sstreamError;
sstreamError << "Internal error: invalid keyword \"" << rssStr.substr(nPos, nSeparator - nPos) <<
"\" during code generation.";
throw CCompileException(sstreamError.str().c_str());
} else
sstream << itKeyword->second;
nPos = nSeparator + 1;
}
return sstream.str();
}
std::string CGenContext::GetIndentChars()
{
// Default indentation is 4 spaces
return " ";
}
CGenContext::SCDeclInfo CGenContext::GetCDeclTypeStr(sdv::IInterfaceAccess* pDeclTypeObj, const std::string& rssScope /*= std::string()*/, bool bScopedName /*= false*/) const
{
std::function<void(sdv::IInterfaceAccess*, CGenContext::SCDeclInfo&)> fnInterpretType =
[&, this](sdv::IInterfaceAccess* pTypeObj, CGenContext::SCDeclInfo& rsCDeclInfo)
{
if (!pTypeObj) throw CCompileException("Internal error: expecting a declaration type.");
const sdv::idl::IDeclarationType* pDeclType = pTypeObj->GetInterface<sdv::idl::IDeclarationType>();
if (!pDeclType) throw CCompileException("Internal error: expecting a declaration type.");
rsCDeclInfo.eBaseType = pDeclType->GetBaseType();
// Separate between system type and defined type.
if (pDeclType->GetTypeDefinition())
{
// Deal with anonymous definitions
const sdv::idl::IEntityInfo* pTypeInfo = GetInterface<sdv::idl::IEntityInfo>(pDeclType->GetTypeDefinition());
if (!pTypeInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
rsCDeclInfo.ssDeclType = bScopedName ?
GetRelativeScopedName(pTypeInfo->GetScopedName(), rssScope) :
static_cast<std::string>(pTypeInfo->GetName());
if (rsCDeclInfo.ssDeclType.empty()) throw CCompileException("Internal error: the intity doesn't have a name.");
}
else
rsCDeclInfo.ssDeclType = MapDeclType2CType(rsCDeclInfo.eBaseType);
// If the type is an interface, add a pointer to the type
// TODO: Check for derived type...
switch (rsCDeclInfo.eBaseType)
{
case sdv::idl::EDeclType::decltype_interface:
rsCDeclInfo.ssDeclType += "*";
rsCDeclInfo.bIsInterface = true;
rsCDeclInfo.bIsPointer = true;
break;
case sdv::idl::EDeclType::decltype_string:
case sdv::idl::EDeclType::decltype_u8string:
case sdv::idl::EDeclType::decltype_u16string:
case sdv::idl::EDeclType::decltype_u32string:
case sdv::idl::EDeclType::decltype_wstring:
rsCDeclInfo.bIsString = true;
rsCDeclInfo.bIsComplex = true;
if (pDeclType->GetFixedLength())
{
// Insert fixed after sdv:: and before the string name
rsCDeclInfo.ssDeclType.insert(5, "fixed_");
rsCDeclInfo.ssDeclType += "<" + std::to_string(pDeclType->GetFixedLength()) + ">";
rsCDeclInfo.bTemplated = true;
}
break;
case sdv::idl::EDeclType::decltype_sequence:
rsCDeclInfo.bIsComplex = true;
if (!pDeclType->GetValueType())
throw CCompileException("Internal error: expecting value type for template parameter.");
else
{
CGenContext::SCDeclInfo sCDeclInfoValueType;
fnInterpretType(pDeclType->GetValueType(), sCDeclInfoValueType);
rsCDeclInfo.ssDeclType += "<" + sCDeclInfoValueType.ssDeclType;
if (pDeclType->GetFixedLength())
rsCDeclInfo.ssDeclType += ", " + std::to_string(pDeclType->GetFixedLength());
rsCDeclInfo.ssDeclType += ">";
rsCDeclInfo.bTemplated = true;
}
break;
case sdv::idl::EDeclType::decltype_pointer:
rsCDeclInfo.bIsComplex = true;
if (!pDeclType->GetValueType())
throw CCompileException("Internal error: expecting value type for template parameter.");
else
{
CGenContext::SCDeclInfo sCDeclInfoValueType;
fnInterpretType(pDeclType->GetValueType(), sCDeclInfoValueType);
rsCDeclInfo.ssDeclType += "<" + sCDeclInfoValueType.ssDeclType;
if (pDeclType->GetFixedLength())
rsCDeclInfo.ssDeclType += ", " + std::to_string(pDeclType->GetFixedLength());
rsCDeclInfo.ssDeclType += ">";
rsCDeclInfo.bTemplated = true;
}
break;
case sdv::idl::EDeclType::decltype_struct:
case sdv::idl::EDeclType::decltype_union:
rsCDeclInfo.bIsComplex = true;
break;
default:
break;
}
};
// Fill the C++ type structure
CGenContext::SCDeclInfo sCDeclInfo;
if (!pDeclTypeObj) return sCDeclInfo;
fnInterpretType(pDeclTypeObj, sCDeclInfo);
// Exclude void as valid type
if (sCDeclInfo.ssDeclType != "void")
sCDeclInfo.bValidType = true;
return sCDeclInfo;
}
std::string CGenContext::MapEntityType2CType(sdv::idl::EEntityType eEntityType)
{
switch (eEntityType)
{
case sdv::idl::EEntityType::type_enum: return "enum class";
case sdv::idl::EEntityType::type_struct: return "struct";
case sdv::idl::EEntityType::type_union: return "union";
case sdv::idl::EEntityType::type_module: return "namespace";
case sdv::idl::EEntityType::type_interface: return "interface";
case sdv::idl::EEntityType::type_exception: return "except";
case sdv::idl::EEntityType::type_typedef: return "typedef";
case sdv::idl::EEntityType::type_attribute: return "";
case sdv::idl::EEntityType::type_operation: return "";
case sdv::idl::EEntityType::type_parameter: return "";
case sdv::idl::EEntityType::type_enum_entry: return "";
case sdv::idl::EEntityType::type_case_entry: return "";
default:
return "invalid";
}
}
std::string CGenContext::MapDeclType2CType(sdv::idl::EDeclType eDeclType)
{
switch (eDeclType)
{
case sdv::idl::EDeclType::decltype_short: return "int16_t";
case sdv::idl::EDeclType::decltype_long: return "int32_t";
case sdv::idl::EDeclType::decltype_long_long: return "int64_t";
case sdv::idl::EDeclType::decltype_unsigned_short: return "uint16_t";
case sdv::idl::EDeclType::decltype_unsigned_long: return "uint32_t";
case sdv::idl::EDeclType::decltype_unsigned_long_long: return "uint64_t";
case sdv::idl::EDeclType::decltype_float: return "float";
case sdv::idl::EDeclType::decltype_double: return "double";
case sdv::idl::EDeclType::decltype_long_double: return "long double";
case sdv::idl::EDeclType::decltype_fixed: return "uint32_t"; // TODO: Not implemented!
case sdv::idl::EDeclType::decltype_char: return "char";
case sdv::idl::EDeclType::decltype_char16: return "char16_t";
case sdv::idl::EDeclType::decltype_char32: return "char32_t";
case sdv::idl::EDeclType::decltype_wchar: return "wchar_t";
case sdv::idl::EDeclType::decltype_boolean: return "bool";
case sdv::idl::EDeclType::decltype_native: return "size_t";
case sdv::idl::EDeclType::decltype_octet: return "uint8_t";
case sdv::idl::EDeclType::decltype_string: return "sdv::string";
case sdv::idl::EDeclType::decltype_u8string: return "sdv::u8string";
case sdv::idl::EDeclType::decltype_u16string: return "sdv::u16string";
case sdv::idl::EDeclType::decltype_u32string: return "sdv::u32string";
case sdv::idl::EDeclType::decltype_wstring: return "sdv::wstring";
case sdv::idl::EDeclType::decltype_enum: return "enum class";
case sdv::idl::EDeclType::decltype_struct: return "struct";
case sdv::idl::EDeclType::decltype_union: return "union";
case sdv::idl::EDeclType::decltype_module: return "namespace";
case sdv::idl::EDeclType::decltype_interface: return "interface";
case sdv::idl::EDeclType::decltype_exception: return "struct";
case sdv::idl::EDeclType::decltype_attribute: return "";
case sdv::idl::EDeclType::decltype_operation: return "";
case sdv::idl::EDeclType::decltype_parameter: return "";
case sdv::idl::EDeclType::decltype_enum_entry: return "";
case sdv::idl::EDeclType::decltype_case_entry: return "";
case sdv::idl::EDeclType::decltype_typedef: return "typedef";
case sdv::idl::EDeclType::decltype_sequence: return "sdv::sequence";
case sdv::idl::EDeclType::decltype_pointer: return "sdv::pointer";
case sdv::idl::EDeclType::decltype_map: return "uint32_t"; // TODO: Not implemented!
case sdv::idl::EDeclType::decltype_bitset: return "uint32_t"; // TODO: Not implemented!
case sdv::idl::EDeclType::decltype_bitfield: return "uint32_t"; // TODO: Not implemented!
case sdv::idl::EDeclType::decltype_bitmask: return "uint32_t"; // TODO: Not implemented!
case sdv::idl::EDeclType::decltype_any: return "sdv::any_t";
case sdv::idl::EDeclType::decltype_interface_id: return "sdv::interface_id";
case sdv::idl::EDeclType::decltype_interface_type: return "sdv::interface_t";
case sdv::idl::EDeclType::decltype_exception_id: return "sdv::exception_id";
case sdv::idl::EDeclType::decltype_void: return "void";
case sdv::idl::EDeclType::decltype_unknown:
default:
return "invalid";
}
}
std::string CGenContext::GetRelativeScopedName(const std::string& ssScopedName, const std::string& rssScope)
{
if (rssScope.empty()) return ssScopedName;
// Splitting function
using CScopeVector = std::vector<std::string>;
auto fnSplitScopedName = [](const std::string& rssName) -> CScopeVector
{
CScopeVector vecSplittedName;
size_t nPos = 0;
while (nPos != std::string::npos)
{
size_t nStart = nPos;
nPos = rssName.find("::", nStart);
vecSplittedName.push_back(rssName.substr(nStart, nPos - nStart));
if (nPos != std::string::npos)
nPos += 2;
}
return vecSplittedName;
};
// Split the scoped name
CScopeVector vecScopedName = fnSplitScopedName(ssScopedName);
if (vecScopedName.empty()) return ssScopedName;
// Split the scope
CScopeVector vecScope = fnSplitScopedName(rssScope);
if (vecScope.empty()) return ssScopedName;
// Reverse find the starting point
auto itScope = vecScope.end();
auto itScopedName = vecScopedName.begin();
while (itScope != vecScope.begin())
{
itScope--;
if (*itScope == *itScopedName) break;
}
// As long as both iterators have identical scope names, increase the iterators.
auto itSavedScopedName = itScopedName;
while (itScope != vecScope.end() && itScopedName != vecScopedName.end() && *itScope == *itScopedName)
{
itScope++;
itSavedScopedName = itScopedName;
itScopedName++;
}
// If the next name scope is anywhere in the rest of the scope, use the save scope name instead. For example:
// Name = a::b::c::d
// Scope = a::b::x::c
// The iterator is pointing to:
// Name iterator = c::d
// Scope iterator = x::c
// If returning the relative name (which is "c::d") it will be relative to the scope (which is "a::b::x::c") and will then be
// a scoped name "a::b::x::c::d", which might not even exist. To solve this, insert the last scoped name part to the returned
// name if the name is used in the rest of the scope as well (so insert "b" to the name which leads to a relative name of
// "b::c::d").
while (itScope != vecScope.end())
{
if (*itScope == *itScopedName)
{
itScopedName = itSavedScopedName;
break;
}
itScope++;
}
// Create a new scoped name from the left over scope names in the scoped name vector.
std::string ssScopedNameNew;
while (itScopedName != vecScopedName.end())
{
if (!ssScopedNameNew.empty()) ssScopedNameNew += "::";
ssScopedNameNew += *itScopedName;
itScopedName++;
}
return ssScopedNameNew;
}

View File

@@ -0,0 +1,187 @@
#ifndef CONTEXT_H
#define CONTEXT_H
#include "../includes.h"
#include <filesystem>
#include <map>
/**
* @brief Generator context
*/
class CGenContext
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CGenContext(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CGenContext();
/**
*
* @brief Get the parser interface.
* @tparam TInterface The interface to request the interface from.
* @param[in] pObject Pointer to the object to request the interface from or NULL when the parser should be asked.
* @return Returns a pointer to the interface if available.
*/
template <typename TInterface>
TInterface* GetInterface(sdv::IInterfaceAccess* pObject) const;
/**
* @brief Return the interface to the parser.
* @return The interface to the parser or NULL when the parser is not available.
*/
sdv::IInterfaceAccess* GetParser() const { return m_pParser; }
/**
* @brief Get source path.
* @return Returns the source path.
*/
std::filesystem::path GetSource() const;
/**
* @brief Get the output directory.
* @remarks If there is no output directory defined, takes the parent directory of the source.
* @return Returns the output directory path.
*/
std::filesystem::path GetOutputDir() const;
/**
* @brief File header generation using JavaDoc C-style comments.
* @param[in] rpathFile Reference to the path to the file to generate the header for.
* @param[in] rssDescription Optional description to add to the file header.
* @return Returns the file header string.
*/
std::string Header(const std::filesystem::path& rpathFile, const std::string& rssDescription = std::string()) const;
/**
* @brief Creates safeguard C++ string that can be used to safeguard a C++ header file.
* @param[in] rpathFile Reference to the path to the file to use for safeguarding.
* @param[in] bInitial When set, creates initial lines, otherwise closing lines.
* @return Returns the safeguard string composed front the path.
*/
static std::string Safeguard(const std::filesystem::path& rpathFile, bool bInitial);
/**
* @brief Insert indentation before each text within a (multi-line) string.
* @details If the line doesn't start with a number sign insert an indentation before each line. Also a line-concatinating
* character (back-slash before end of line) will be removed. Independent of the number sign, any whitespace at the end of each
* line will be removed.
* @param[in] rssStr Reference to the string to adapt.
* @param[in] rssIndent Reference to the indentation string to insert.
* @return Returns the string with inserted indentations
*/
static std::string SmartIndent(const std::string& rssStr, const std::string& rssIndent);
/**
* @brief Make a qualified identifier from a fully scoped name with array brackets.
* @details A fully scoped name contains all the namespace and struct definitions that define the context of the supplied name.
* The names of each level is separated by the scope separator (::). Also any member declaration is separated by the member
* separator (.). The name could also contain square brackets to identify an array. This function replaces the scope operator by
* a double underscore (__), the array operator by a single underscore and the array brackets by a single underscore. In the
* end, the name results in a qualified C++ name.
* @param[in] rssName Reference to the name string to qualify.
* @return Returns the qualified name sstring.
*/
static std::string QualifyName(const std::string& rssName);
/**
* @brief Keyword map for keyword replacement in a string.
*/
typedef std::map<std::string, std::string> CKeywordMap;
/**
* @brief Vector containing the exceptions that might be thrown by the function.
*/
typedef std::vector<std::string> CExceptionVector;
/**
* @brief Replace keywords in a string.
* @param[in] rssStr Reference to the string containing the keywords.
* @param[in] rmapKeywords Map with keywords to replace.
* @param[in] cMarker Character to identify the keyword with (placed before and after the keyword; e.g. %keyword%).
* @return Returns the string with replacements.
*/
static std::string ReplaceKeywords(const std::string& rssStr, const CKeywordMap& rmapKeywords, char cMarker = '%');
/**
* @brief Get indentation string (represents one tab).
* @return Reference to the string with the indentation.
*/
static std::string GetIndentChars();
/**
* @brief Declaration information.
*/
struct SCDeclInfo
{
sdv::idl::EDeclType eBaseType = sdv::idl::EDeclType::decltype_unknown; ///< Base type
std::string ssDeclType; ///< Declaration type (incl. pointer addition for every array extend).
bool bIsPointer = false; ///< Type is represented as a pointer to dynamic data or an interface.
bool bIsComplex = false; ///< Complex data type; use by-ref for parameters.
bool bTemplated = false; ///< Type has template parameters
bool bIsInterface = false; ///< Type is an interface pointer.
bool bIsString = false; ///< Set when the type represents a string object.
bool bIsDynamic = false; ///< Type is dynamic
bool bValidType = false; ///< Type is not void
};
/**
* @brief Get the C++ declaration type as string.
* @attention Does not check for anonymous types.
* @param[in] pDeclTypeObj Pointer to the IInterfaceAccess interface of the declaration type object.
* @param[in] rssScope Reference to the string containing the current scope.
* @param[in] bScopedName When set, return the scoped name.
* @return The declaration information (or empty types when not available).
*/
SCDeclInfo GetCDeclTypeStr(sdv::IInterfaceAccess* pDeclTypeObj, const std::string& rssScope /*= std::string()*/, bool bScopedName = false) const;
/**
* @brief Map the IDL type to the C type (if possible).
* @param[in] eEntityType The entity type to map.
* @return Returns a string representing the C type or empty if there is no C type or "invalid" if the type is invalid.
*/
static std::string MapEntityType2CType(sdv::idl::EEntityType eEntityType);
/**
* @brief Map the IDL type to the C type (if possible).
* @param[in] eDeclType The declaration type to map.
* @return Returns a string representing the C type or empty if there is no C type or "invalid" if the type is invalid.
*/
static std::string MapDeclType2CType(sdv::idl::EDeclType eDeclType);
/**
* @brief Get a relative scoped name based on the provided scope entity.
* @param[in] ssScopedName Reference to the fully scoped name of the entity.
* @param[in] rssScope Reference to the current scope.
* @return String with the relatively scoped name.
*/
static std::string GetRelativeScopedName(const std::string& ssScopedName, const std::string& rssScope);
private:
sdv::IInterfaceAccess* m_pParser = nullptr; ///< Parse tree instance.
sdv::idl::ICompilerInfo* m_pCompilerInfo = nullptr; ///< Compiler information interface.
sdv::idl::ICompilerOption* m_pOption = nullptr; ///< Program options interface.
};
template <typename TInterface>
inline TInterface* CGenContext::GetInterface(sdv::IInterfaceAccess* pObject) const
{
if (!pObject) return nullptr;
return pObject->GetInterface(sdv::GetInterfaceId<TInterface>()).template get<TInterface>();
}
#ifndef DOXYGEN_IGNORE
template <>
inline sdv::IInterfaceAccess* CGenContext::GetInterface(sdv::IInterfaceAccess* pObject) const
{
return pObject;
}
#endif
#endif // !defined(CONTEXT_H)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,349 @@
#ifndef DEFINITION_GENERATOR_H
#define DEFINITION_GENERATOR_H
#include "definition_generator_base.h"
#include <list>
#include <fstream>
#include <set>
#include <sstream>
// TODO: Comment style overwrite: all javadoc, QT, C, Cpp, preceding, succeeding
// TODO: Tabs or spaces
// TODO: Tab size (default 4 characters)
// TODO: Succeeding comments
/**
* @brief Switch code context specifically for definition creation.
*/
struct SDefinitionSwitchCodeContext : SSwitchCodeContext
{
std::stringstream sstreamCode; ///< Code used to the actual switching
std::stringstream sstreamConstructorImpl; ///< Constructor content stream. Not applicable if the definition is a
///< namespace.
std::stringstream sstreamDestructorImpl; ///< Destructor content stream. Not applicable if the definition is a namespace.
std::stringstream sstreamConstructHelperImpl; ///< Constructor helper function for this switch variable.
std::stringstream sstreamCopyConstructHelperImpl; ///< Constructor content stream for copy construction. Not applicable if the
///< definition is a namespace.
std::stringstream sstreamMoveConstructHelperImpl; ///< Constructor content stream for move construction. Not applicable if the
///< definition is a namespace.
std::stringstream sstreamDestructHelperImpl; ///< Constructor helper function for this switch variable.
};
/**
* @brief Definition stream context.
*/
struct CDefinitionContext : CDefEntityContext<CDefinitionContext>
{
/**
* @brief Constructor assigning the generator context.
* @param[in] rGenContext Reference to the context to assign.
* @param[in] pEntity Pointer to the definition entity this context belongs to.
*/
CDefinitionContext(const CGenContext& rGenContext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Copy constructor assigning a new definition entity.
* @param[in] rcontext Original context to copy from.
* @param[in] pEntity Pointer to the definition entity this context belongs to.
*/
CDefinitionContext(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Join a context into this context. Overload of CDefEntityContext::operator<<.
* @param[in] rcontext Reference to the context to join.
* @return Reference to this context containing the joined result.
*/
virtual CDefinitionContext& operator<<(const CDefinitionContext& rcontext) override;
/**
* @brief Set the definition access to public (default).
*/
void SetDefAccessPublic();
/**
* @brief Set the definition access to private..
*/
void SetDefAccessPrivate();
/**
* @brief Get a reference to the preface stream.
* @return Reference to the preface stream object.
*/
std::stringstream& GetPrefaceStream();
/**
* @brief Get a reference to the definition code stream.
* @return Reference to the definition code stream object.
*/
std::stringstream& GetDefCodeStream();
/**
* @brief Get a reference to the preface or definition code stream dependable on the preface switch.
* @return Reference to the preface or definition code stream object.
*/
std::stringstream& GetAutoStream();
/**
* @brief Get definition code (this adds both prefacce and definition code stream content).
* @return Returns a string containing the definition code collected within this context.
*/
std::string GetDefinitionCode() const;
/**
* @brief Returns whether the preface switch is still activated.
* @return The current state of the preface switch for streaming.
*/
bool UsePreface() const;
/**
* @brief Disable the preface switch.
*/
void DisablePreface();
/**
* @brief Is construction needed?
* @return Returns whether construction is needed.
*/
bool NeedsConstruction() const;
/**
* @brief Set the construction needed flag.
*/
void SetConstructionNeeded();
/**
* @brief Newline-after-content-flag set?
* @return Returns whether a newline after the definition content is required.
*/
bool NeedsNewlineAfterContent() const;
/**
* @brief Set the newline-after-content-flag.
*/
void EnableNewlineAfterContent();
/**
* @brief Reset the newline-after-content-flag.
*/
void DisableNewlineAfterContent();
/**
* @brief Dies this entity have any friends?
* @return Returns whether this entity has any friends in the friend set.
*/
bool HasFriends() const;
/**
* @brief Get the set of friends.
* @return Returns the reference to the set of friends.
*/
const std::set<std::string>& GetFriendSet() const;
/**
* @brief Assign a the friend to this entity.
* @param[in] rssScopedName Reference to the scoped name of the friend entity.
*/
void AddFriend(const std::string& rssScopedName);
private:
std::stringstream m_sstreamPreface; ///< Preface stream (before the first code).
std::stringstream m_sstreamDefCode; ///< Definition code stream.
bool m_bPreface = false; ///< When set, streaming is done in the preface stream
///< instead of the definition code stream.
bool m_bConstructionCompulsory = false; ///< Constructor needed even if no content is available
///< (needed wen objects need to be initialized with default
///< initialization). Not applicable if the definition is a
///< namespace (should not occur).
bool m_bCurrentDefAccessPublic = true; ///< When set, the current definition access is public;
///< private when not set.
bool m_bNewlineAfterContent = false; ///< The next content that is streamed, should insert a
///< newline.
sdv::IInterfaceAccess* m_pDefEntity = nullptr; ///< The definition entity that defines this context.
std::set<std::string> m_setFriends; ///< Friend structures needed to allow access to the
///< private member.
};
/**
* @brief Definition generator class.
*/
class CDefinitionGenerator : public CDefinitionGeneratorBase<CDefinitionContext>
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CDefinitionGenerator(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CDefinitionGenerator() override;
private:
/**
* @brief Return the information for target file creation. Overload of CDefinitionGeneratorBase::GetTargetFileInfo.
* @param[out] rssTargetSubDir Reference to the string containing the target sub-directory to be added to the output directory.
* Could be empty to target the output directory.
* @param[out] rssTargetFileEnding Reference to string containing the file ending (file name and extension) to be placed at the
* end of the source file name replacing the extension.
*/
virtual void GetTargetFileInfo(std::string& rssTargetSubDir, std::string& rssTargetFileEnding) override;
/**
* @brief Return the file header text for automatic file generation. Overload of CDefinitionGeneratorBase::GetFileHeaderText.
* @return The header text to place into the file.
*/
virtual std::string GetFileHeaderText() const override;
/**
* @brief Stream the code into the file. Called once after processing. Overload of CDefinitionGeneratorBase::StreamIntoFile.
* @param[in, out] rcontext Reference to the stream context.
* @param[in, out] rfstream Reference to the file stream to stream into
*/
virtual void StreamIntoFile(CDefinitionContext& rcontext, std::ofstream& rfstream) override;
/**
* @brief Stream the include section for the file. Overload of CDefinitionGeneratorBase::StreamIncludeSection.
* @param[in, out] rcontext Reference to the stream context to stream into.
*/
virtual void StreamIncludeSection(CDefinitionContext& rcontext) override;
/**
* @brief Stream the meta entity. Overload of CDefinitionGeneratorBase::StreamMetaEntity.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the interface of the meta entity.
*/
virtual void StreamMetaEntity(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity) override;
/**
* @brief Compound comment enumerator
*/
enum class ECommentGroup { none, begin, end };
/**
* @brief Stream preceding comments if there are any.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in] eGroup Defines whether the comment groups several statements or comments a single statement.
*/
void StreamComments(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity,
ECommentGroup eGroup = ECommentGroup::none);
/**
* @brief Stream declaration if the entity is a declaration. Overload of CDefinitionGeneratorBase::StreamDeclaration.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @return Returns true when the streaming was successful or false when streaming was not successful and should be canceled.
*/
virtual bool StreamDeclaration(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity) override;
/**
* @brief Stream definition if the entity is a definition. Overload of CDefinitionGeneratorBase::StreamDefinition.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in] bInline When set the definition is part of a declaration.
* @param[in] bAnonymousDecl When set, the definition is part of an anonymous declaration (only valid for unions).
*/
virtual void StreamDefinition(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity, bool bInline = false,
bool bAnonymousDecl = false) override;
/**
* @brief Stream typedef declaration if the entity is a typedef.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamTypedef(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream attribute declaration if the entity is an attribute.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @details Attributes are implemented as getter- and setter-functions.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamAttribute(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream operation declaration if the entity is an operation.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamOperation(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream parameter declaration if the entity is a parameter.
* @details Stream parameter declarations. Input parameters are provided by-value unless the parameter is a complex parameter
* (a struct, union, string, pointer, sequence, map or an array); they are provided by-reference (const). Interfaces are
* provided by C/C++ pointer. Output parameters are always provided by reference. Pointers are defined in IDL as boundless
* arrays (var[]) and are implemented using a smart-pointer class (hence a complex parameter). Fixed-bound arrays are provided
* as C-arrays. Dynamic-bound arrays (arrays that have alength defined through a variable) are available only when the length
* is provided as an input parameter and the variable itself is allocated on input (hence either an input parameter or an in-
* and output parameter). In case dynamic bound arrays are required as output parameter, use a sequence instead.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in] bInitial When set, this is the first parameter of an operation.
*/
void StreamParameter(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity, bool bInitial);
/**
* @brief Stream enum entry declaration if the entity is an enum entry.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamEnumEntry(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream case entry declaration if the entity is an case entry.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamCaseEntry(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream the declaration type string.
* details The stream is created inline. In case the declaration contains an anonymous definition, the definition is inserted
* as well (hence the use of indentation).
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the declaration entity.
* @param[in] rbDefinitionStreamed When set, a definition was streamed and a newline should be inserted before the next
* declaration.
* @param[in] bAnonymousDecl When set, the definition is part of an anonymous declaration (only valid for unions).
* @param[in] bSkipInitialIndent When set, do not insert an additional indentation before the declaration of the type.
* @return Returns whether the declaration type was streamed or whether the type was unknown and could not be streamed.
*/
bool StreamDeclType(CDefinitionContext& rcontext, sdv::IInterfaceAccess* pEntity, bool& rbDefinitionStreamed,
bool bAnonymousDecl = false, bool bSkipInitialIndent = true);
/**
* @brief For a switch variable, process the joint container of both switch variable and union. Start with the highest parent
* running through all children. Overload of CDefinitionGeneratorBase::ProcessUnionJointContainerForSwitchVar.
* @param[in, out] rcontext Reference to the definition stream context of the switch variable to stream into.
* @param[in] pSwitchVarEntity Interface to the switch var declaration.
* @param[in] pContainerEntity Interface to the container definition.
*/
virtual void ProcessUnionJointContainerForSwitchVar(CDefinitionContext& rcontext,
sdv::IInterfaceAccess* pSwitchVarEntity, sdv::IInterfaceAccess* pContainerEntity) override;
/**
* @brief Process the union member that, together with the switch variable, has a mutual container from the entity in the
* context. Overload of CDefinitionGeneratorBase::ProcessUnionInContainerContext.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] rssMemberScopeUnionDecl Reference to the member scope of the union declaration (could be empty when the union
* switch is not variable based).
* @param[in] rssMemberScopeSwitchVar Reference to the member scope of the switch variable (could be empty when the union
* switch is not variable based).
* @param[in] pUnionDef Pointer to the union definition entity.
* @param[in] rvecArrayIndices Reference to the vector containing the array indices of the declaration to use for the detection.
* If the declaration is not declared as array, the vector should be empty. If the declaration is declared as an array and the
* amount of dimensions is below the amount of dimensions within the provided vector, the detection function is called for each
* array dimension extending the vector with the index to do the detection for.
*/
virtual void ProcessUnionInContainerContext(CDefinitionContext& rcontext, std::string rssMemberScopeUnionDecl,
std::string rssMemberScopeSwitchVar, sdv::IInterfaceAccess* pUnionDef,
const std::vector<SArrayIterationInfo>& rvecArrayIndices = std::vector<SArrayIterationInfo>()) override;
std::set<std::string> m_setHistory; ///< Set with a history of all added entity definitions.
std::set<std::string> m_setForwardDecl; ///< Set with with forward declared structure definitions.
};
#endif // !defined(DEFINITION_GENERATOR_H)

View File

@@ -0,0 +1,505 @@
#ifndef DEFINITION_GENERATOR_BASE_H
#define DEFINITION_GENERATOR_BASE_H
#include "context.h"
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <list>
/**
* @brief Multiple unions can be switched using one switch variable. The switch functions are implemented in the struct or
* exception containing the switch variable. This struct collects the switch information globally to allow the variable to be
* used in a switch case even if the union is several layers deep in the child hierarchy.
*/
struct SSwitchVarContext
{
sdv::IInterfaceAccess* pVarEntity = nullptr; ///< The switch variable entity pointer.
std::string ssType; ///< Switch var type
std::string ssName; ///< Switch var name
std::string ssScopedName; ///< Scoped name of the switch
std::vector<std::string> vecUnionDecl; ///< Vector containing the union names to switch for.
};
/**
* @brief Array iteration information contains the variable that is used for iteration and the array dimension expression to
* determine the upper boundary.
*/
struct SArrayIterationInfo
{
std::string ssArrayIterator; ///< The name of the variable used for the array iteration.
std::string ssCountExpression; ///< The expression used to identify the maximum array elements.
};
/**
* @brief Switch code context to be processed in the joined parent of both the union with variable based switch and the
* switch variable.
*/
struct SSwitchCodeContext
{
std::shared_ptr<SSwitchVarContext> ptrSwitchVar; ///< The switch variable context
std::string ssSwitchVarName; ///< Exact statement of the switch var as scoped member
///< relative to the joint container of both union and switch
///< variable.
std::string ssSwitchValue; ///< The first switch value (if available).
std::vector<SArrayIterationInfo> vecArrayIterationInfo; ///< The iteration information about any array of
///< declarations for the structures holding unions or for
///< unions themselves.
};
/**
* @brief Definition stream context.
* @tparam TDerivedContext The derived context class. Must be deriving from this class.
*/
template <typename TDerivedContext>
class CDefEntityContext
{
public:
// Forward declaration
struct SIterator;
/// Allow the iterator structure to access member variables.
friend SIterator;
/**
* @brief Constructor assigning the generator context.
* @param[in] rContext Reference to the context to assign.
* @param[in] pEntity Pointer to the definition entity this context belongs to.
*/
CDefEntityContext(const CGenContext& rContext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Copy constructor assigning a new definition entity.
* @param[in] rcontext Original context to copy from.
* @param[in] pEntity Pointer to the definition entity this context belongs to.
*/
CDefEntityContext(CDefEntityContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Virtual destructor allowing the destruction of derived class members as well.
*/
virtual ~CDefEntityContext();
/**
* @brief Join a context into this context.
* @param[in] rContext Reference to the context to join.
* @return Reference to this context containing the joined result.
*/
virtual TDerivedContext& operator<<(const TDerivedContext& rContext);
/**
* @brief Return the scope of the members of the definition entity (which is the scoped name of the definition).
* @return The scope if existing or empty if not.
*/
std::string GetScope() const;
/**
* @brief Current scope part of compound structure?
* @return Returns whether the current structure is part of a compound structure.
*/
bool IsCompound() const;
/**
* @brief Is this definition structural (struct, exception, union)?
* @return Returns whether the definition is structural.
*/
bool IsStructural() const;
/**
* @brief Get the stored indentation.
* @param[in] bDefBody When set, the indentation is for the definition body, keeping the deep indentation flag in consideration.
* @param[in] bFuncImpl When set, the indentation is for the function implementation, increasing the indentation.
* @return The current indentation.
*/
std::string GetIndent(bool bDefBody = true, bool bFuncImpl = false) const;
/**
* @brief Enable (if not enabled) and increase the indentation.
*/
void IncrIndent();
/**
* @brief Enable (if not enabled) and decrease the indentation (if possible).
*/
void DecrIndent();
/**
* @brief Disable the indentation if enabled.
*/
void DisableIndent();
/**
* @brief Enable the indentation if disabled.
*/
void EnableIndent();
/**
* @brief Enable deep indentation (one more indentation in definition part).
*/
void EnableDeepIndent();
/**
* @brief Get the interface to the definition entity.
* @return The interface to the definition or NULL when the context is representing the root definition.
*/
sdv::IInterfaceAccess* GetDefEntity() const;
/**
* @brief Templated function for getting an interface to the definition entity.
* @tparam TInterface The interface to get.
* @return Pointer to the interface or NULL when the definition is not available or doesn#t expose the interface.
*/
template <typename TInterface>
TInterface* GetDefEntity() const;
/**
* @brief Iterator structure (used for entity iteration).
*/
struct SIterator
{
/// The context class is allowed to access content directly.
friend CDefEntityContext<TDerivedContext>;
private:
/**
* @brief Constructor
* @param[in] rContextParam Reference to the entity context that holds the iterator list.
*/
SIterator(CDefEntityContext<TDerivedContext>& rContextParam);
public:
/**
* @brief No copy constructor
* @param[in] rsIterator Reference to the itertor to copy from.
*/
SIterator(const SIterator& rsIterator) = delete;
/**
* @brief Move constructor
* @param[in] rsIterator Reference to the itertor to move from.
*/
SIterator(SIterator&& rsIterator);
/**
* @brief Destructor
*/
~SIterator();
/**
* @brief Releases the iterator.
*/
void Release();
/**
* @brief Increase the iteration value by 1.
* @return Returns reference to the iterator object after incrementation.
*/
SIterator& operator++();
/**
* @brief Increase the iteration value by 1.
* @param[in] iVal Value is ignored.
* @return Returns the iteration value before incrementation.
*/
uint32_t operator++(int iVal);
/**
* @brief Get the current iteration value.
* @return The iteration index.
*/
operator uint32_t() const;
private:
CDefEntityContext<TDerivedContext>& rContext; ///< Reference to the entity context holding the iterator list.
bool bValid = false; ///< The iterator is only valid when set.
typename std::list<uint32_t>::iterator itPos{}; ///< Iterator position in the context iteration object list.
};
/**
* @brief Create a new iterator object and make it current.
* @remarks The iterator object is deleted automatically when out of scope or when explicitly triggered to release the iterator.
* @return The iterator object.
*/
SIterator CreateIterator();
/**
* @brief Get the iteration value of the current iterator.
* @return The value of the current iteration or 0 when no iterator was created.
*/
uint32_t GetCurrentIteration();
/**
* @brief Assign a switch variable context to the definition containing the switch variable.
* @param[in] rptrSwitchVarContext Reference to the smart pointer holding the context structure for the switch variable.
*/
void AssignSwitchVarContext(const std::shared_ptr<SSwitchVarContext>& rptrSwitchVarContext);
/**
* @brief Create or get a switch code context for the switch variable supplied.
* @tparam TSwitchCodeContext Type of switch code context structure. Must derive from SSwitchCodeContext.
* @param[in] rssSwitchVarName Reference to the scoped member name of the switch variable including array brackets.
* @param[in] rptrSwitchVar Reference to the smart pointer holding the switch variable context.
* @param[in] rvecArrayIndices Reference to the iterator information of any arrays being part of the switch variable name.
* @return Returns a shared pointer to an existing or a new switch code context.
*/
template <typename TSwitchCodeContext = SSwitchCodeContext>
std::shared_ptr<TSwitchCodeContext> GetOrCreateSwitchCodeContext(const std::string& rssSwitchVarName,
const std::shared_ptr<SSwitchVarContext>& rptrSwitchVar, const std::vector<SArrayIterationInfo>& rvecArrayIndices);
/**
* @brief Are multiple switch code contexts available?
* @return Returns whether more than one switch code contexts are available.
*/
bool HasMultipleSwitchCodeContexts() const;
/**
* @brief Get a vector with all the switch code contexts.
* @tparam TSwitchCodeContext Type of switch code context structure. Must derive from SSwitchCodeContext.
* @param[in] rssScopedSwitchVar Reference to the string containing the scoped variable name that should be used as a filter. If
* not set, all contexts are returned.
* @return Returns a vector with the stored switch code contexts.
*/
template <typename TSwitchCodeContext = SSwitchCodeContext>
std::vector<std::shared_ptr<TSwitchCodeContext>> GetSwitchCodeContexts(
const std::string& rssScopedSwitchVar = std::string()) const;
private:
/**
* @brief Create an iterator object and return the current position to the object.
* @return The iterator position in the iterator list.
*/
std::list<uint32_t>::iterator CreateIteratorObject();
/**
* @brief Remove the iterator object from the iterator list.
* @remarks This function is not protected by providing faulty positions.
* @param[in] itPos Iterator position in the iterator list.
*/
void RemoveIteratorObject(std::list<uint32_t>::iterator itPos);
const CGenContext& m_rGenContext; ///< Reference to te generator context.
sdv::IInterfaceAccess* m_pDefEntity = nullptr; ///< The definition entity that defines this context.
std::string m_ssIndent; ///< Current indentation for definition body (needs to be indented once more
///< for constructor and destructor implementations).
std::string m_ssIndentBackup; ///< Stored indent for disabled indentation support.
bool m_bDeepDefIndent = false; ///< When set, the definitions are one level deeper than the provided
///< indentation. This doesn't count for the functions. The boolean is
///< needed when the definition is implemented using two levels of
///< definitions, as is the case with unions using a type based switch.
uint32_t m_uiItIdx = 0; ///< Current iteration index during iteration.
/// Contained switch var contexts in this definition.
std::vector<std::shared_ptr<SSwitchVarContext>> m_vecSwitchVars;
/// Switch code implemented in this definition entity.
using TSwitchCodeMap = std::map<std::string, std::shared_ptr<SSwitchCodeContext>>;
/// Shareable switch code map.
std::shared_ptr<TSwitchCodeMap> m_ptrSwitchCodeMap;
/// List with iterators. The bottom iterator (latest added) is the current iterator.
std::list<uint32_t> m_lstIterators;
};
/**
* @brief Definition code generator base.
* @tparam TDefEntityContext Type of definition entity context to use. Must derive from CDefEntityContext.
*/
template <typename TDefEntityContext>
class CDefinitionGeneratorBase : public CGenContext
{
static_assert(std::is_base_of_v<CDefEntityContext<TDefEntityContext>, TDefEntityContext>);
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CDefinitionGeneratorBase(sdv::IInterfaceAccess* pParser);
/**
* @brief Generate the definition code.
* @return Returns whether generation was successful.
*/
virtual bool Generate();
protected:
/**
* @brief Return the information for target file creation.
* @param[out] rssTargetSubDir Reference to the string containing the target sub-directory to be added to the output directory.
* Could be empty to target the output directory.
* @param[out] rssTargetFileEnding Reference to string containing the file ending (file name and extension) to be placed at the
* end of the source file name replacing the extension.
*/
virtual void GetTargetFileInfo(std::string& rssTargetSubDir, std::string& rssTargetFileEnding) = 0;
/**
* @brief Return the file header text for automatic file generation.
* @return The header text to place into the file.
*/
virtual std::string GetFileHeaderText() const = 0;
/**
* @brief Stream the code into the file. Called once after processing.
* @param[in, out] rcontext Reference to the stream context.
* @param[in, out] rfstream Reference to the file stream to stream into
*/
virtual void StreamIntoFile(TDefEntityContext& rcontext, std::ofstream& rfstream);
/**
* @brief Stream the include section for the file.
* @param[in, out] rcontext Reference to the stream context to stream into.
*/
virtual void StreamIncludeSection(TDefEntityContext& rcontext);
/**
* @brief Process the entities.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pIterator Pointer to the iterator interface.
* @return Returns true when the streaming of declarations was successful or false when streaming of declarations was not
* successful and should be canceled.
*/
virtual bool ProcessEntities(TDefEntityContext& rcontext, sdv::idl::IEntityIterator* pIterator);
/**
* @brief Stream the meta entity.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the interface of the meta entity.
*/
virtual void StreamMetaEntity(TDefEntityContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream declaration if the entity is a declaration.
* @param[in, out] rcontext Reference to the stream context.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @return Returns true when the streaming was successful or false when streaming was not successful and should be canceled.
*/
virtual bool StreamDeclaration(TDefEntityContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream definition if the entity is a definition.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in] bInline When set the definition is part of a declaration.
* @param[in] bAnonymousDecl When set, the definition is part of an anonymous declaration (only valid for unions).
*/
virtual void StreamDefinition(TDefEntityContext& rcontext, sdv::IInterfaceAccess* pEntity, bool bInline = false,
bool bAnonymousDecl = false);
/**
* @brief Check for the existence of the switch variable and add the variable if not existing.
* @param[in] pSwitchVarEntity Interface pointer to the switch variable entity.
* @return Smart pointer to the switch variable.
*/
std::shared_ptr<SSwitchVarContext> GetOrCreateVarBasedSwitch(sdv::IInterfaceAccess* pSwitchVarEntity);
/**
* @brief Detect a declaration of a union using a variable based switch case. If the switch case variable is within the scope
* of the provided context, stream the functions needed to initialize and use the switch case. If the declaration is not a
* fitting union but is of a compound structure (struct, exception or union), go through the declaration members for further
* detection.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] rssMemberScope Reference to the string containing the declarative member scope. Each declaration is separated by
* a dot separator.
* @param[in] pDeclEntity Interface pointer to the declarative entity to check.
* @param[in] rvecArrayIndices Reference to the vector containing the array indices of the declaration to use for the detection.
* If the declaration is not declared as array, the vector should be empty. If the declaration is declared as an array and the
* amount of dimensions is below the amount of dimensions within the provided vector, the detection function is called for each
* array dimension extending the vector with the index to do the detection for.
*/
virtual void DetectUnionContainerForProcessing(TDefEntityContext& rcontext, const std::string& rssMemberScope,
sdv::IInterfaceAccess* pDeclEntity, const std::vector<SArrayIterationInfo>& rvecArrayIndices = {});
/**
* @brief Process the union member that, together with the switch variable, has a mutual container from the entity in the
* context.
* @details Union definitions and declarations are to be treated differently, dependable on the switch type and the way of the
* integration. The following differences are to be distingueshed:
* - Unions can be defined as named or as unnamed unions. Unnamed unions can only occur when followed by a declaration. They
* cannot be declared at global level due to the missing possibility to initialize/uninitialize using a member
* constructor/destructor.
* - Union declarations can be explicit or anonymous (not existing, but due to the unnamed nature of the union implicitly
* present). Anonymous declaration only occur for unnamed unions and are not allowed at root level. For named unions,
* there are no anonymous declarations.
*
* The following table defines the different types:
* +------------------+---------------------------------------------------------------------+---------------------------------------------------------------------+
* | | switch type base | switch var based |
* | +----------------------------------+----------------------------------+----------------------------------+----------------------------------+
* | | named | unnamed | named | unnamed |
* | +----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | | explicit decl. | anonymous decl. | explicit decl. | anonymous decl. | explicit decl. | anonymous decl. | explicit decl. | anonymous decl. |
* +==================+================+=================+================+=================+================+=================+================+=================+
* | definition code | Use struct to | Not existing | Use struct to | Not allowed/ | Define named | Not existing | Must be | Define unnamed |
* | | encapsulate | | encapsulate | not occurring. | union as | | followed by | union as part |
* | | switch var and | | switch var and | | member. | | decl. Cannot | of struct. |
* | | union def. | | union def. | | | | be global. | Will not be |
* | | | | Auto generate | | | | | followed by |
* | | | | struct name. | | | | | decl. |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | declaration code | Normal decl. | N.a. | Following def. | N.a. | Normal | N.a. | Following def. | Absent |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | switch variable | Part of struct | N.a. | Part of struct | N.a. | Part of | N.a. | Part of | Part of |
* | | | | | | container | | container | container |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | constructor | Part of struct | N.a. | Part of struct | N.a. | Part of | N.a. | Part of | Part of |
* | | | | | | container | | container | container |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | destructor | Part of struct | N.a. | Part of struct | N.a. | Part of | N.a. | Part of | Part of |
* | | | | | | container | | container | container |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | switch function | Part of struct | N.a. | Part of struct | N.a. | Part of | N.a. | Part of | Part of |
* | | | | | | container | | container | container |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
* | element access | Internal | N.a. | Internal | N.a. | Over member | N.a. | Over member | Direct access |
* | | | | | | var | | var | to elements |
* +------------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+----------------+-----------------+
*
* In case of a switch case based on a variable, this variable might be defined in a sub-structure of a mutual container of
* a union. The union content depends on the switch variable. Both need to be processed together in the same context of one of
* the containers.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] rssMemberScopeUnionDecl Reference to the member scope of the union declaration (could be empty when the union
* switch is not variable based).
* @param[in] rssMemberScopeSwitchVar Reference to the member scope of the switch variable (could be empty when the union
* switch is not variable based).
* @param[in] pUnionDef Pointer to the union definition entity.
* @param[in] rvecArrayIndices Reference to the vector containing the array indices of the declaration to use for the detection.
* If the declaration is not declared as array, the vector should be empty. If the declaration is declared as an array and the
* amount of dimensions is below the amount of dimensions within the provided vector, the detection function is called for each
* array dimension extending the vector with the index to do the detection for.
*/
virtual void ProcessUnionInContainerContext(TDefEntityContext& rcontext, std::string rssMemberScopeUnionDecl,
std::string rssMemberScopeSwitchVar, sdv::IInterfaceAccess* pUnionDef,
const std::vector<SArrayIterationInfo>& rvecArrayIndices = std::vector<SArrayIterationInfo>());
/**
* @brief For a switch variable, detect the joint container of both switch variable and union. Start with the highest parent
* running through all children.
* @param[in, out] rcontext Reference to the definition stream context of the switch variable to stream into.
* @param[in] pSwitchVarEntity Interface to the switch var declaration.
* @param[in] pEntity Interface to the definition to detect for the container.
*/
void DetectUnionJointContainerForSwitchVar(TDefEntityContext& rcontext, sdv::IInterfaceAccess* pSwitchVarEntity,
sdv::IInterfaceAccess* pEntity);
/**
* @brief For a switch variable, process the joint container of both switch variable and union. Start with the highest parent
* running through all children.
* @param[in, out] rcontext Reference to the definition stream context of the switch variable to stream into.
* @param[in] pSwitchVarEntity Interface to the switch var declaration.
* @param[in] pContainerEntity Interface to the container definition.
*/
virtual void ProcessUnionJointContainerForSwitchVar(TDefEntityContext& rcontext, sdv::IInterfaceAccess* pSwitchVarEntity,
sdv::IInterfaceAccess* pContainerEntity);
private:
/// Map with union switch variables. The switch variable scoped name is the key for the map.
std::map<std::string, std::shared_ptr<SSwitchVarContext>> m_mapSwitchFunc;
};
// Include the code as well
#include "definition_generator_base.inl"
#endif // !defined DEFINITION_GENERATOR_BASE_H

View File

@@ -0,0 +1,655 @@
#ifndef DEFINITION_GENERATOR_BASE_INL
#define DEFINITION_GENERATOR_BASE_INL
#ifndef DEFINITION_GENERATOR_BASE_H
#error Do not include this file directly. Include "definition_generator_base.h" instead.
#endif
#include "../exception.h"
#include <filesystem>
#include <thread>
#include <fstream>
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::CDefEntityContext(const CGenContext& rContext, sdv::IInterfaceAccess* pEntity) :
m_rGenContext(rContext), m_pDefEntity(pEntity), m_ptrSwitchCodeMap(std::make_shared<TSwitchCodeMap>())
{
static_assert(std::is_base_of_v<CDefEntityContext, TDerivedContext>);
}
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::CDefEntityContext(CDefEntityContext& rcontext, sdv::IInterfaceAccess* pEntity) :
m_rGenContext(rcontext.m_rGenContext), m_pDefEntity(pEntity), m_ssIndent(rcontext.m_ssIndent),
m_ptrSwitchCodeMap(pEntity == rcontext.m_pDefEntity ? rcontext.m_ptrSwitchCodeMap : std::make_shared<TSwitchCodeMap>())
{}
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::~CDefEntityContext()
{}
template <typename TDerivedContext>
TDerivedContext& CDefEntityContext<TDerivedContext>::operator<<(const TDerivedContext& /*rContext*/)
{
return static_cast<TDerivedContext&>(*this);
}
template <typename TDerivedContext>
inline std::string CDefEntityContext<TDerivedContext>::GetScope() const
{
if (!m_pDefEntity) return {};
const sdv::idl::IEntityInfo* pEntityInfo = m_pDefEntity->GetInterface<sdv::idl::IEntityInfo>();
if (!pEntityInfo) return {};
return pEntityInfo->GetScopedName();
}
template <typename TDerivedContext>
inline bool CDefEntityContext<TDerivedContext>::IsCompound() const
{
if (!m_pDefEntity) return false;
const sdv::idl::IEntityInfo* pEntityInfo = m_pDefEntity->GetInterface<sdv::idl::IEntityInfo>();
if (!pEntityInfo) return false;
return pEntityInfo->GetType() != sdv::idl::EEntityType::type_module;
}
template <typename TDerivedContext>
inline bool CDefEntityContext<TDerivedContext>::IsStructural() const
{
const sdv::idl::IEntityInfo* pEntityInfo = m_rGenContext.GetInterface<sdv::idl::IEntityInfo>(m_pDefEntity);
if (!pEntityInfo) return false;
switch (pEntityInfo->GetType())
{
case sdv::idl::EEntityType::type_struct:
case sdv::idl::EEntityType::type_union:
case sdv::idl::EEntityType::type_exception:
return true;
default:
return false;
}
}
template <typename TDerivedContext>
inline std::string CDefEntityContext<TDerivedContext>::GetIndent(bool bDefBody /*= true*/, bool bFuncImpl /*= false*/) const
{
return m_ssIndent + (((bDefBody && m_bDeepDefIndent) || bFuncImpl) ? m_rGenContext.GetIndentChars() : "");
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::IncrIndent()
{
EnableIndent();
m_ssIndent += m_rGenContext.GetIndentChars();
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::DecrIndent()
{
EnableIndent();
m_ssIndent.resize(m_ssIndent.size() - m_rGenContext.GetIndentChars().size());
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::DisableIndent()
{
if (!m_ssIndent.empty())
{
m_ssIndentBackup = m_ssIndent;
m_ssIndent.clear();
}
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::EnableIndent()
{
if (!m_ssIndentBackup.empty())
{
m_ssIndent = m_ssIndentBackup;
m_ssIndentBackup.clear();
}
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::EnableDeepIndent()
{
m_bDeepDefIndent = true;
}
template <typename TDerivedContext>
inline sdv::IInterfaceAccess* CDefEntityContext<TDerivedContext>::GetDefEntity() const
{
return m_pDefEntity;
}
template <typename TDerivedContext>
template <typename TInterface>
inline TInterface* CDefEntityContext<TDerivedContext>::GetDefEntity() const
{
return m_rGenContext.GetInterface<TInterface>(m_pDefEntity);
}
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::SIterator::SIterator(CDefEntityContext<TDerivedContext>& rContextParam) :
rContext(rContextParam), bValid(true), itPos(rContextParam.CreateIteratorObject())
{}
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::SIterator::SIterator(SIterator&& rsIterator) :
rContext(rsIterator.rContext), itPos(rsIterator.itPos), bValid(rsIterator.bValid)
{
// The iterator that was moved from is not valid any more.
rsIterator.bValid = false;
}
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::SIterator::~SIterator()
{
Release();
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::SIterator::Release()
{
if (!bValid) return;
rContext.RemoveIteratorObject(itPos);
bValid = false;
}
template <typename TDerivedContext>
inline typename CDefEntityContext<TDerivedContext>::SIterator& CDefEntityContext<TDerivedContext>::SIterator::operator++()
{
if (bValid) (*itPos)++;
return *this;
}
template <typename TDerivedContext>
inline uint32_t CDefEntityContext<TDerivedContext>::SIterator::operator++(int /*iVal*/)
{
if (!bValid) return 0;
uint32_t uiTemp = *itPos;
(*itPos)++;
return uiTemp;
}
template <typename TDerivedContext>
inline CDefEntityContext<TDerivedContext>::SIterator::operator uint32_t() const
{
if (!bValid) return 0;
return *itPos;
}
template <typename TDerivedContext>
inline typename CDefEntityContext<TDerivedContext>::SIterator CDefEntityContext<TDerivedContext>::CreateIterator()
{
return SIterator(*this);
}
template <typename TDerivedContext>
inline uint32_t CDefEntityContext<TDerivedContext>::GetCurrentIteration()
{
return m_lstIterators.empty() ? 0u : m_lstIterators.back();
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::AssignSwitchVarContext(const std::shared_ptr<SSwitchVarContext>& rptrSwitchVarContext)
{
m_vecSwitchVars.push_back(rptrSwitchVarContext);
}
template <typename TDerivedContext>
template <typename TSwitchCodeContext>
inline std::shared_ptr<TSwitchCodeContext> CDefEntityContext<TDerivedContext>::GetOrCreateSwitchCodeContext(const std::string& rssSwitchVarName,
const std::shared_ptr<SSwitchVarContext>& rptrSwitchVar, const std::vector<SArrayIterationInfo>& rvecArrayIndices)
{
static_assert(std::is_base_of_v<SSwitchCodeContext, TSwitchCodeContext>);
// Get the switch code map...
if (!m_ptrSwitchCodeMap) return {}; // Should not occur
auto itSwitchCodeContext = m_ptrSwitchCodeMap->find(rssSwitchVarName);
if (itSwitchCodeContext == m_ptrSwitchCodeMap->end())
{
itSwitchCodeContext = m_ptrSwitchCodeMap->emplace(rssSwitchVarName, std::make_shared<TSwitchCodeContext>()).first;
itSwitchCodeContext->second->ptrSwitchVar = rptrSwitchVar;
itSwitchCodeContext->second->ssSwitchVarName = rssSwitchVarName;
itSwitchCodeContext->second->vecArrayIterationInfo = rvecArrayIndices;
}
return std::static_pointer_cast<TSwitchCodeContext>(itSwitchCodeContext->second);
}
template <typename TDerivedContext>
inline bool CDefEntityContext<TDerivedContext>::HasMultipleSwitchCodeContexts() const
{
if (!m_ptrSwitchCodeMap) return false;
return m_ptrSwitchCodeMap->size() > 1u;
}
template <typename TDerivedContext>
template <typename TSwitchCodeContext>
inline std::vector<std::shared_ptr<TSwitchCodeContext>> CDefEntityContext<TDerivedContext>::GetSwitchCodeContexts(
const std::string& rssScopedSwitchVar /*= std::string()*/) const
{
static_assert(std::is_base_of_v<SSwitchCodeContext, TSwitchCodeContext>);
if (!m_ptrSwitchCodeMap) return {};
std::vector<std::shared_ptr<TSwitchCodeContext>> vecSwitchCodeContexts;
for (const auto& rptrSwitchCodeContext : *m_ptrSwitchCodeMap)
{
if (!rssScopedSwitchVar.empty() && rssScopedSwitchVar != rptrSwitchCodeContext.second->ptrSwitchVar->ssScopedName) continue;
vecSwitchCodeContexts.push_back(std::static_pointer_cast<TSwitchCodeContext>(rptrSwitchCodeContext.second));
}
return vecSwitchCodeContexts;
}
template <typename TDerivedContext>
inline std::list<uint32_t>::iterator CDefEntityContext<TDerivedContext>::CreateIteratorObject()
{
return m_lstIterators.insert(m_lstIterators.end(), 0);
}
template <typename TDerivedContext>
inline void CDefEntityContext<TDerivedContext>::RemoveIteratorObject(std::list<uint32_t>::iterator itPos)
{
m_lstIterators.erase(itPos);
}
template <typename TDefEntityContext>
inline CDefinitionGeneratorBase<TDefEntityContext>::CDefinitionGeneratorBase(sdv::IInterfaceAccess* pParser) : CGenContext(pParser)
{}
template <typename TDefEntityContext>
inline bool CDefinitionGeneratorBase<TDefEntityContext>::Generate()
{
// Get target file information for directory and file creation.
std::string ssTargetSubDir;
std::string ssTargetFileEnding;
GetTargetFileInfo(ssTargetSubDir, ssTargetFileEnding);
// Create target directory if it doesn't exist. Since race conditions could exist due to parallel processing, do this
// up to five times before reporting an error.
std::filesystem::path pathTargetDir = GetOutputDir();
if (!ssTargetSubDir.empty()) pathTargetDir /= ssTargetSubDir;
for (size_t nCnt = 0; nCnt < 5; nCnt++)
{
if (!std::filesystem::exists(pathTargetDir))
std::filesystem::create_directories(pathTargetDir);
else
break;
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
if (!std::filesystem::exists(GetOutputDir()))
throw CCompileException("Cannot create output directory: ", pathTargetDir.generic_u8string());
// Replace the extension by the new file ending;
std::filesystem::path pathFile = pathTargetDir / GetSource().filename();
pathFile.replace_extension("");
pathFile += ssTargetFileEnding;
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_all)
std::cout << "Target file: " << pathFile.generic_u8string() << std::endl;
// Open the file for writing
std::ofstream streamDefFile;
streamDefFile.open(pathFile);
if (!streamDefFile.is_open()) throw CCompileException("Failed to open the target file: ", pathFile.generic_u8string());
// Add file header
streamDefFile << Header(pathFile, GetFileHeaderText());
// Add safeguard
streamDefFile << Safeguard(pathFile, true);
// Include headers
TDefEntityContext sStreamContext(*this, GetParser());
StreamIncludeSection(sStreamContext);
// Run through the entities and process the entities...
sdv::idl::IDefinitionEntity* pDefinition = GetInterface<sdv::idl::IDefinitionEntity>(GetParser());
if (!pDefinition) throw CCompileException("Internal error: the parser doesn't have a root definition.");
sdv::idl::IEntityIterator* pIterator = pDefinition->GetChildren();
if (!pIterator) throw CCompileException("Internal error: the parser doesn't support entity iteration.");
ProcessEntities(sStreamContext, pIterator);
// Stream the result into the file
StreamIntoFile(sStreamContext, streamDefFile);
// End of safeguard
streamDefFile << Safeguard(pathFile, false);
// Finalize the stream
streamDefFile.close();
return true;
}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::StreamIntoFile(TDefEntityContext& /*rcontext*/,
std::ofstream& /*rfstream*/) {}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::StreamIncludeSection(TDefEntityContext& /*rcontext*/)
{}
template <typename TDefEntityContext>
inline bool CDefinitionGeneratorBase<TDefEntityContext>::ProcessEntities(TDefEntityContext& rcontext,
sdv::idl::IEntityIterator* pIterator)
{
if (!pIterator) throw CCompileException("Internal error: processing entities without iterator.");
bool bStreamDeclSuccess = true;
// Do detection first...
for (auto sIterator = rcontext.CreateIterator(); sIterator < pIterator->GetCount(); ++sIterator)
{
// Get the entity
sdv::IInterfaceAccess* pEntity = pIterator->GetEntityByIndex(sIterator);
if (!pEntity) throw CCompileException("Internal error: processing non-existent entity.");
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
const sdv::idl::IEntityContext* pContext = GetInterface<sdv::idl::IEntityContext>(pEntity);
if (!pContext) throw CCompileException("Internal error: the entity doesn't expose context information.");
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pEntity);
// Only process entities in the source code
if (pContext->GetLocation() != sdv::idl::IEntityContext::ELocation::source)
continue;
// Check whether the entity has a name.
if (pEntityInfo->GetName().empty())
throw CCompileException("Internal error: the entity doesn't have a name.");
if (pDeclaration) // Handle declarations
{
// Create a switch variable if not already available
if (pEntityInfo->GetType() == sdv::idl::EEntityType::type_switch_variable)
{
rcontext.AssignSwitchVarContext(GetOrCreateVarBasedSwitch(pEntity));
// Detect the container for the switch variable and the union using the variable.
sdv::IInterfaceAccess* pOldestContainer = pEntity;
while (true)
{
sdv::idl::IEntityInfo* pHighestContainerEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pOldestContainer);
sdv::IInterfaceAccess* pContainerEntity = pHighestContainerEntityInfo->GetParent();
const sdv::idl::IEntityInfo* pContainerEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pContainerEntity);
if (!pContainerEntityInfo) break;
pOldestContainer = pContainerEntity;
}
DetectUnionJointContainerForSwitchVar(rcontext, pEntity, pOldestContainer);
}
// If a declaration is a compound type and has declarations of unions that use switch case variables that are within the
// scope of the definition stream context provided to this function, add the switch functions to the definition.
DetectUnionContainerForProcessing(rcontext, pDeclaration->IsAnonymous() ? "" : pEntityInfo->GetName(), pEntity);
}
}
// Do the processing...
for (auto sIterator = rcontext.CreateIterator(); sIterator < pIterator->GetCount(); ++sIterator)
{
// Get the entity
sdv::IInterfaceAccess* pEntity = pIterator->GetEntityByIndex(sIterator);
if (!pEntity) throw CCompileException("Internal error: processing non-existent entity.");
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
const sdv::idl::IEntityContext* pContext = GetInterface<sdv::idl::IEntityContext>(pEntity);
if (!pContext) throw CCompileException("Internal error: the entity doesn't expose context information.");
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pEntity);
// Only process entities in the source code
if (pContext->GetLocation() != sdv::idl::IEntityContext::ELocation::source)
continue;
// Check whether the entity has a name.
if (pEntityInfo->GetName().empty())
throw CCompileException("Internal error: the entity doesn't have a name.");
// Process the entity
const sdv::idl::IMetaEntity* pMeta = GetInterface<sdv::idl::IMetaEntity>(pEntity);
if (pMeta) // Handle meta data
{
// Stream the meta entity.
StreamMetaEntity(rcontext, pEntity);
}
else if (pDeclaration) // Handle declarations
{
// Skip streaming of declarations when one declaration was already non-streamable
if (!bStreamDeclSuccess) continue;
// Stream the declaration
bStreamDeclSuccess &= StreamDeclaration(rcontext, pEntity);
}
else // Handle definitions
StreamDefinition(rcontext, pEntity);
}
return bStreamDeclSuccess;
}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::StreamMetaEntity(TDefEntityContext& /*rcontext*/,
sdv::IInterfaceAccess* /*pEntity*/)
{}
template <typename TDefEntityContext>
inline bool CDefinitionGeneratorBase<TDefEntityContext>::StreamDeclaration(TDefEntityContext& /*rcontext*/,
sdv::IInterfaceAccess* /*pEntity*/)
{
return false;
}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::StreamDefinition(TDefEntityContext& /*rcontext*/,
sdv::IInterfaceAccess* /*pEntity*/, bool /*bInline = false*/, bool /*bAnonymousDecl = false*/)
{
}
template <typename TDefEntityContext>
inline std::shared_ptr<SSwitchVarContext> CDefinitionGeneratorBase<TDefEntityContext>::GetOrCreateVarBasedSwitch(
sdv::IInterfaceAccess* pSwitchVarEntity)
{
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pSwitchVarEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
// Check for the existence of the variable
auto itSwitchVar = m_mapSwitchFunc.find(pEntityInfo->GetScopedName());
if (itSwitchVar != m_mapSwitchFunc.end()) return itSwitchVar->second;
// Get variable information
auto ptrSwitchVar = std::make_shared<SSwitchVarContext>();
ptrSwitchVar->pVarEntity = pSwitchVarEntity;
ptrSwitchVar->ssScopedName = pEntityInfo->GetScopedName();
ptrSwitchVar->ssName = pEntityInfo->GetName();
size_t nPos = pEntityInfo->GetScopedName().find_last_of(".:");
std::string ssScope = pEntityInfo->GetScopedName().substr(nPos == std::string::npos ? 0 : nPos + 1);
// Get the declaration type string
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pSwitchVarEntity);
if (!pDeclaration)
throw CCompileException("Internal error: the entity is a declaration, but doesn't expose declaration information.");
if (pEntityInfo->GetType() != sdv::idl::EEntityType::type_switch_variable)
throw CCompileException("Internal error: the entity to be used as switch variable is not declared as switch variable.");
SCDeclInfo sDeclInfo = GetCDeclTypeStr(pDeclaration->GetDeclarationType(), ssScope, true);
ptrSwitchVar->ssType = sDeclInfo.ssDeclType;
// Add the switch var context to the map
m_mapSwitchFunc[pEntityInfo->GetScopedName()] = ptrSwitchVar;
// Return the result
return ptrSwitchVar;
}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::DetectUnionContainerForProcessing(TDefEntityContext& rcontext,
const std::string& rssMemberScope, sdv::IInterfaceAccess* pDeclEntity,
const std::vector<SArrayIterationInfo>& rvecArrayIndices /*= {}*/)
{
// Get the declaration interface from the entity
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pDeclEntity);
if (!pDeclaration) return;
// Get the declaration type
sdv::idl::IDeclarationType* pDeclType = GetInterface<sdv::idl::IDeclarationType>(pDeclaration->GetDeclarationType());
if (!pDeclType) return;
// Get the declaration variable name.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pDeclEntity);
if (!pEntityInfo) return;
// Get the definition that belongs to this entity declaration (only entities with definitions are currently of interest).
sdv::IInterfaceAccess* pDefTypeEntity = pDeclType->GetTypeDefinition();
if (!pDefTypeEntity) return;
sdv::idl::IDefinitionEntity* pDefinition = GetInterface<sdv::idl::IDefinitionEntity>(pDefTypeEntity);
if (!pDefinition) return;
// Create the new member string
std::string ssDeclMemberScope = rssMemberScope;
// If the declaration is declared as an array, get the dimensions
std::vector<SArrayIterationInfo> vecNewInicesInfo = rvecArrayIndices;
if (pDeclaration->HasArray())
{
// Check whether iteration through each element is necessary (for each dimension of a multi-vector array).
sdv::sequence<sdv::idl::SArrayDimension> seqArrayDimensions = pDeclaration->GetArrayDimensions();
size_t nIndex = 0;
for (const sdv::idl::SArrayDimension& rsDimension : seqArrayDimensions)
{
// Create an index variable name
std::string ssIndexVarName = std::string("uiIndex_") + pEntityInfo->GetName();
if (seqArrayDimensions.size() > 1) ssIndexVarName += std::to_string(nIndex++);
vecNewInicesInfo.push_back(SArrayIterationInfo{ ssIndexVarName, rsDimension.ssExpression });
ssDeclMemberScope += std::string("[") + ssIndexVarName + "]";
}
}
// Detection in child declaration entities.
auto fnDetectInChildren = [&](sdv::idl::IDefinitionEntity* pLocalDefinition)
{
// Get the child iterator
sdv::idl::IEntityIterator* pChildIterator = pLocalDefinition->GetChildren();
if (!pChildIterator) return;
// Iterate through each child
for (uint32_t uiIndex = 0; uiIndex < pChildIterator->GetCount(); uiIndex++)
{
sdv::IInterfaceAccess* pChildEntity = pChildIterator->GetEntityByIndex(uiIndex);
const sdv::idl::IEntityInfo* pChildEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pChildEntity);
const sdv::idl::IDeclarationEntity* pChildDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pChildEntity);
if (!pChildEntity || !pChildEntityInfo || !pChildDeclaration) continue;
std::string ssChildMemberScope = ssDeclMemberScope;
if (!pChildDeclaration->IsAnonymous())
ssChildMemberScope += std::string(".") + static_cast<std::string>(pChildEntityInfo->GetName());
DetectUnionContainerForProcessing(rcontext, ssChildMemberScope, pChildEntity, vecNewInicesInfo);
}
};
// First deal with inheritance - iterate through each base type
sdv::idl::IEntityIterator* pInheritanceIterator = pDefinition->GetInheritance();
if (pInheritanceIterator)
{
for (uint32_t uiIndex = 0; uiIndex < pInheritanceIterator->GetCount(); uiIndex++)
{
sdv::idl::IDefinitionEntity* pDefinitionBase =
GetInterface<sdv::idl::IDefinitionEntity>(pInheritanceIterator->GetEntityByIndex(uiIndex));
if (!pDefinitionBase) continue;
fnDetectInChildren(pDefinitionBase);
}
}
// Further action depends on the type
switch (pDeclType->GetBaseType())
{
case sdv::idl::EDeclType::decltype_exception:
case sdv::idl::EDeclType::decltype_struct:
// Detect within the children
fnDetectInChildren(pDefinition);
return;
case sdv::idl::EDeclType::decltype_union:
// Union processing below...
break;
default:
return; // Other types don't contain unions.
}
// Union detected... check for variable based union
const sdv::idl::IUnionEntity* pUnion = GetInterface<sdv::idl::IUnionEntity>(pDefTypeEntity);
if (!pUnion || pUnion->GetSwitchInterpretation() != sdv::idl::IUnionEntity::ESwitchInterpret::switch_variable) return;
sdv::u8string ssVarDeclName;
sdv::IInterfaceAccess* pSwitchVar = nullptr;
sdv::IInterfaceAccess* pContainer = nullptr;
pUnion->GetSwitchVar(ssVarDeclName, pSwitchVar, pContainer);
// Check the scope names for the context entity and the container of the switch. If identical, this is the context to add
// union initialization for.
const sdv::idl::IEntityInfo* pSwitchParentEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pContainer);
const sdv::idl::IEntityInfo* pDefEntityInfo = rcontext.template GetDefEntity<sdv::idl::IEntityInfo>();
if (!pSwitchParentEntityInfo || !pDefEntityInfo ||
pSwitchParentEntityInfo->GetScopedName() != pDefEntityInfo->GetScopedName())
return;
// Stream union initialization functions
ProcessUnionInContainerContext(rcontext, ssDeclMemberScope, ssVarDeclName, pDefTypeEntity, vecNewInicesInfo);
}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::ProcessUnionInContainerContext(TDefEntityContext& /*rcontext*/, std::string /*rssMemberScopeUnionDecl*/,
std::string /*rssMemberScopeSwitchVar*/, sdv::IInterfaceAccess* /*pUnionDef*/,
const std::vector<SArrayIterationInfo>& /*rvecArrayIndices = std::vector<SArrayIterationInfo>()*/)
{}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::DetectUnionJointContainerForSwitchVar(TDefEntityContext& rcontext,
sdv::IInterfaceAccess* pSwitchVarEntity, sdv::IInterfaceAccess* pEntity)
{
// Check for valid definitions
sdv::idl::IEntityInfo* pDefEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
sdv::idl::IEntityInfo* pCurrentDefEntityInfo = rcontext.template GetDefEntity<sdv::idl::IEntityInfo>();
sdv::idl::IDefinitionEntity* m_pDefEntity = GetInterface<sdv::idl::IDefinitionEntity>(pEntity);
sdv::idl::IDefinitionEntity* pCurrentDefEntity = rcontext.template GetDefEntity<sdv::idl::IDefinitionEntity>();
if (!pDefEntityInfo || !pCurrentDefEntityInfo || !m_pDefEntity || !pCurrentDefEntity) return;
// Check for the current scope
std::string ssCurrentScopedDefName = pCurrentDefEntityInfo->GetScopedName();
// Run through the inherited entities and do detection.
sdv::idl::IEntityIterator* pInheritanceIterator = m_pDefEntity->GetInheritance();
if (pInheritanceIterator)
{
for (uint32_t uiIndex = 0; uiIndex < pInheritanceIterator->GetCount(); uiIndex++)
DetectUnionJointContainerForSwitchVar(rcontext, pSwitchVarEntity,
pInheritanceIterator->GetEntityByIndex(uiIndex));
}
// Detect for a union
sdv::idl::IUnionEntity* pUnion = GetInterface<sdv::idl::IUnionEntity>(pEntity);
if (pUnion && pUnion->GetSwitchInterpretation() == sdv::idl::IUnionEntity::ESwitchInterpret::switch_variable)
{
sdv::u8string ssVarName;
sdv::IInterfaceAccess* pVarEntity = nullptr;
sdv::IInterfaceAccess* pContainerEntity = nullptr;
pUnion->GetSwitchVar(ssVarName, pVarEntity, pContainerEntity);
sdv::idl::IEntityInfo* pContainerEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pContainerEntity);
sdv::idl::IEntityInfo* pVarEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pVarEntity);
sdv::idl::IEntityInfo* pVarEntityParentInfo =
GetInterface<sdv::idl::IEntityInfo>(pVarEntityInfo ? pVarEntityInfo->GetParent() : nullptr);
if (pContainerEntityInfo && pVarEntityParentInfo && pVarEntityParentInfo->GetScopedName() == ssCurrentScopedDefName)
ProcessUnionJointContainerForSwitchVar(rcontext, pSwitchVarEntity, pContainerEntity);
}
// Run through the child entities and do detection.
sdv::idl::IEntityIterator* pChildIterator = m_pDefEntity->GetChildren();
if (pChildIterator)
{
for (uint32_t uiIndex = 0; uiIndex < pChildIterator->GetCount(); uiIndex++)
DetectUnionJointContainerForSwitchVar(rcontext, pSwitchVarEntity, pChildIterator->GetEntityByIndex(uiIndex));
}
}
template <typename TDefEntityContext>
inline void CDefinitionGeneratorBase<TDefEntityContext>::ProcessUnionJointContainerForSwitchVar(
TDefEntityContext& /*rcontext*/, sdv::IInterfaceAccess* /*pSwitchVarEntity*/,
sdv::IInterfaceAccess* /*pContainerEntity*/)
{}
#endif // !defined DEFINITION_GENERATOR_BASE_INL

View File

@@ -0,0 +1,254 @@
#include "proxy_generator.h"
#include "../exception.h"
#include <fstream>
CProxyGenerator::CProxyGenerator(sdv::IInterfaceAccess* pParser) : CPSClassGeneratorBase(pParser)
{}
CProxyGenerator::~CProxyGenerator()
{}
std::string CProxyGenerator::GetNameAppendix() const
{
return "proxy";
}
std::string CProxyGenerator::GetClassDefFileComments() const
{
return "This file contains the proxy definition for the interfaces.";
}
std::string CProxyGenerator::GetClassImplFileComments() const
{
return "This file contains the proxy implementation for the interfaces.";
}
std::string CProxyGenerator::GetClassDefBegin(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
/**
* @brief Proxy class implementation for the %interface_name%.
*/
class %class_name% : public sdv::ps::CProxyHandler<%interface_name%>, public sdv::ps::IProxyControl
{
public:
/**
* @brief Constructor
*/
%class_name%();
/**
* @brief Destructor
*/
virtual ~%class_name%() override = default;
// Object class name
DECLARE_OBJECT_CLASS_NAME("Proxy_%interface_id%")
DECLARE_OBJECT_CLASS_ALIAS("Proxy_%alias_name%")
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_CHAIN_BASE(sdv::ps::CProxyHandler<%interface_name%>)
SDV_INTERFACE_ENTRY(IProxyControl)
END_SDV_INTERFACE_MAP()
/**
* @brief Get the target interface from the proxy object. Overload of sdv::ps::IProxyControl::GetTargetInterface.
* @return The target interface.
*/
sdv::interface_t GetTargetInterface() override;
/**
* @brief Direct access to the target interface from the proxy object.
* @return Reference to the target interface.
*/
%interface_name%& Access();
private:
/**
* @brief Interface access implementation
*/
class CInterfaceAccess : public %interface_name%
{
public:
/**
* @brief Constructor
*/
CInterfaceAccess(%class_name%& rHandler);
/**
* @brief Destructor
*/
~CInterfaceAccess() = default;
)code";
}
std::string CProxyGenerator::GetClassDefEnd(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
private:
%class_name%& m_rHandler; ///< Proxy handler class.
};
CInterfaceAccess m_access; ///< Interface access object.
};
DEFINE_SDV_OBJECT(%class_name%)
)code";
}
std::string CProxyGenerator::GetConstructImplBegin(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
%class_name%::%class_name%() : m_access(*this)
{}
sdv::interface_t %class_name%::GetTargetInterface()
{
return &Access();
}
%interface_name%& %class_name%::Access()
{
return m_access;
}
%class_name%::CInterfaceAccess::CInterfaceAccess(%class_name%& rHandler) : m_rHandler(rHandler)
{
)code";
}
std::string CProxyGenerator::GetConstructImplEnd(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(}
)code";
}
std::string CProxyGenerator::GetConstructFuncImpl(const SFuncInfo& /*rsFunc*/, CKeywordMap& /*rmapKeywords*/) const
{
// The proxy doesn't implement construction code.
return std::string();
}
std::string CProxyGenerator::GetFuncDef(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const
{
rmapKeywords.insert(std::make_pair("func_const", rsFunc.bIsConst ? " const" : ""));
return R"code(
/** Implementation of %func_name%. */
virtual %func_decl_type% %func_name%(%param_pack_def%)%func_const% override;
)code";
}
std::string CProxyGenerator::GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords, const CExceptionVector& rvecExceptions) const
{
rmapKeywords.insert(std::make_pair("func_const", rsFunc.bIsConst ? " const" : ""));
rmapKeywords.insert(std::make_pair("retval_init_comment", rsFunc.ssDecl != "void" ? R"code(
// Initialize return value)code" : ""));
rmapKeywords.insert(std::make_pair("call_return", rsFunc.nOutputParamCnt ? " = " : ""));
rmapKeywords.insert(std::make_pair("deserialize", rsFunc.nOutputParamCnt ? R"code(// Deserialize output parameters)code" : ""));
rmapKeywords.insert(std::make_pair("return_from_func", rsFunc.ssDecl != "void" ? R"code(
return return_value;)code" : ""));
std::stringstream sstreamExceptions;
sstreamExceptions << R"code(// Fire serialized exceptions caught during the call
sdv::exception_id except_id = 0;
desOutput.peek_front(except_id);
)code";
if (!rvecExceptions.empty())
sstreamExceptions << R"code(switch (except_id)
{
)code";
else
sstreamExceptions << R"code(sdv::XUnknownException exception;
exception.unknown_id = except_id;
throw exception;)code";
for (const std::string& rssException : rvecExceptions)
{
sstreamExceptions << "case sdv::GetExceptionId<" << rssException << R"code(>():
{
)code" << rssException << R"code( exception;
desOutput >> exception;
throw exception;
}
)code";
}
if (!rvecExceptions.empty())
sstreamExceptions << R"code(default:
{
sdv::XUnknownException exception;
exception.unknown_id = except_id;
throw exception;
}
})code";
rmapKeywords.insert(std::make_pair("exception_handling", sstreamExceptions.str()));
return R"code(
%func_decl_type% %class_name%::CInterfaceAccess::%func_name%(%param_pack_def%)%func_const%
{
// Clear raw data bypass (needed for streaming large data).
sdv::ps::GetRawDataBypass().clear();%retval_init_comment%%param_init%
// Serialize input parameters
sdv::serializer serInput;%stream_param_input%
// Execute a call to the interface stub.
sdv::deserializer desOutput;
sdv::ps::ECallResult eResult = m_rHandler.DoCall(%func_index%, serInput, desOutput);
if (eResult == sdv::ps::ECallResult::result_ok)
{
%deserialize%%stream_param_output%%return_from_func%
} else if (eResult == sdv::ps::ECallResult::result_exception)
{
%exception_handling%
} else
throw sdv::ps::XMarshallIntegrity();
}
)code";
}
std::string CProxyGenerator::GetFuncImplParamInit(const SFuncInfo& /*rsFunc*/, const SParamInfo& rsParam, CKeywordMap& /*rmapKeywords*/) const
{
return (rsParam.eDirection == SParamInfo::EDirection::ret && rsParam.bValidType) ?
R"code(
%param_decl_type% return_value = %param_default_val%;)code" : "";
}
std::string CProxyGenerator::GetFuncImplStreamParamInput(const SFuncInfo& /*rsFunc*/, const SParamInfo& rsParam, CKeywordMap& /*rmapKeywords*/) const
{
switch (rsParam.eDirection)
{
case SParamInfo::EDirection::inout:
case SParamInfo::EDirection::in:
return R"code(
serInput << %param_name%;)code";
break;
default:
return std::string();
break;
}
}
std::string CProxyGenerator::GetFuncImplStreamParamOutput(const SFuncInfo& /*rsFunc*/, const SParamInfo& rsParam, CKeywordMap& /*rmapKeywords*/) const
{
switch (rsParam.eDirection)
{
case SParamInfo::EDirection::ret:
return R"code(
desOutput >> return_value;)code";
break;
case SParamInfo::EDirection::inout:
case SParamInfo::EDirection::out:
return R"code(
desOutput >> %param_name%;)code";
break;
default:
break;
}
return std::string();
}
std::string CProxyGenerator::GetFuncImplParamTerm(const SFuncInfo& /*rsFunc*/, const SParamInfo& /*rsParam*/, CKeywordMap& /*rmapKeywords*/) const
{
return std::string();
}

View File

@@ -0,0 +1,108 @@
#ifndef PROXY_GENERATOR_H
#define PROXY_GENERATOR_H
#include "ps_class_generator_base.h"
/**
* @brief Proxy generator class.
*/
class CProxyGenerator : public CPSClassGeneratorBase
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CProxyGenerator(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CProxyGenerator() override;
private:
/**
* @brief Return the name addition to be added to the filename and class definition. Overload of
* CPSClassGeneratorBase::GetNameAppendix.
*/
virtual std::string GetNameAppendix() const override;
/**
* @brief Get definition file comments to be written in the file header. Overload of
* CPSClassGeneratorBase::GetClassDefFileComments.
*/
virtual std::string GetClassDefFileComments() const override;
/**
* @brief Get implementation file comments to be written in the file header. Overload of
* CPSClassGeneratorBase::GetClassImplFileComments.
*/
virtual std::string GetClassImplFileComments() const override;
/**
* @brief Get begin of class definition to be inserted into the header file. Overload of
* CPSClassGeneratorBase::GetClassDefBegin.
*/
virtual std::string GetClassDefBegin(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get end of class definition to be inserted into the header file. Overload of
* CPSClassGeneratorBase::GetClassDefEnd.
*/
virtual std::string GetClassDefEnd(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get begin of constructor implementation to be inserted into the cpp file. Overload of
* CPSClassGeneratorBase::GetConstructImplBegin.
*/
virtual std::string GetConstructImplBegin(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get end of constructor implementation to be inserted into the cpp file. Overload of
* CPSClassGeneratorBase::GetConstructImplEnd.
*/
virtual std::string GetConstructImplEnd(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get the constructor body for a function (attribute or operation). Overload of
* CPSClassGeneratorBase::GetConstructFuncImpl.
*/
virtual std::string GetConstructFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get the function definition (attribute or operation). Overload of CPSClassGeneratorBase::GetFuncDef.
*/
virtual std::string GetFuncDef(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImpl.
*/
virtual std::string GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords, const CExceptionVector& rvecExceptions) const override;
/**
* @brief Get parameter initialization of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplParamInit.
*/
virtual std::string GetFuncImplParamInit(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get input parameter streaming of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplStreamParamInput.
*/
virtual std::string GetFuncImplStreamParamInput(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get output parameter streaming of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplStreamParamOutput.
*/
virtual std::string GetFuncImplStreamParamOutput(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get parameter termination of the unpack portion of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplParamTerm.
*/
virtual std::string GetFuncImplParamTerm(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
};
#endif // !defined(PROXY_GENERATOR_H)

View File

@@ -0,0 +1,628 @@
#include "ps_class_generator_base.h"
#include "../exception.h"
#include <fstream>
#include <thread>
#include <chrono>
CPSClassGeneratorBase::CPSClassGeneratorBase(sdv::IInterfaceAccess* pParser) : CGenContext(pParser)
{}
bool CPSClassGeneratorBase::Generate()
{
// Create target directory if it doesn't exist. Since rqce conditions could exist due to parallel processing, do this
// five times.
std::filesystem::path pathPSTarget = GetOutputDir() / "ps";
for (size_t nCnt = 0; nCnt < 5; nCnt++)
{
if (!std::filesystem::exists(pathPSTarget))
std::filesystem::create_directories(pathPSTarget);
else
break;
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
if (!std::filesystem::exists(pathPSTarget))
throw CCompileException("Cannot create proxy/stub directory: ", pathPSTarget.generic_u8string());
std::filesystem::path pathSerDesTarget = GetOutputDir() / "serdes";
for (size_t nCnt = 0; nCnt < 5; nCnt++)
{
if (!std::filesystem::exists(pathSerDesTarget))
std::filesystem::create_directories(pathSerDesTarget);
else
break;
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
if (!std::filesystem::exists(pathSerDesTarget))
throw CCompileException("Cannot create serdes directory: ", pathSerDesTarget.generic_u8string());
// Add "_proxy" to the path and replace the extension by ".cpp" and ".h";
std::filesystem::path pathPSFileBase = pathPSTarget / GetSource().filename();
pathPSFileBase.replace_extension("");
std::filesystem::path pathFileNameDef = ".." / pathPSFileBase.filename();
pathFileNameDef += ".h";
pathPSFileBase += std::string("_") + GetNameAppendix();
std::filesystem::path pathFileCpp = pathPSFileBase;
pathFileCpp += ".cpp";
std::filesystem::path pathFileHdr = pathPSFileBase;
pathFileHdr += ".h";
std::filesystem::path pathSerDesFileBase = pathSerDesTarget / GetSource().filename();
pathSerDesFileBase.replace_extension("");
std::filesystem::path pathSerDesFile = pathSerDesFileBase;
pathSerDesFile += "_serdes.h";
if (g_log_control.GetVerbosityMode() == EVerbosityMode::report_all)
{
std::cout << "Target header file: " << pathFileHdr.generic_u8string() << std::endl;
std::cout << "Target source file: " << pathFileCpp.generic_u8string() << std::endl;
}
// Open the file for writing
std::ofstream streamCpp(pathFileCpp);
std::ofstream streamHdr(pathFileHdr);
// Add file headers
streamHdr << Header(pathFileHdr, GetClassDefFileComments());
streamCpp << Header(pathFileCpp, GetClassImplFileComments());
// Add safeguard
streamHdr << Safeguard(pathFileHdr, true);
// Include proxy stub base header
streamHdr << "// Proxy/stub interfaces." << std::endl;
streamHdr << "#include <interfaces/core_ps.h>" << std::endl;
streamHdr << "#include <support/pssup.h>" << std::endl;
// Include definition header in header file
streamHdr << "#include \"" << pathFileNameDef.generic_u8string() << "\"" << std::endl;
streamHdr << std::endl;
// Include definition header in cpp file
streamCpp << "#include \"" << pathFileHdr.filename().generic_u8string() << "\"" << std::endl;
// Include serdes header in cpp file
streamCpp << "#include \"../serdes/" << pathSerDesFile.filename().generic_u8string() << "\"" << std::endl;
streamCpp << "#include <support/serdes.h>" << std::endl;
streamCpp << "#include <support/pssup.h>" << std::endl;
streamCpp << std::endl;
// Run through the entities and create the proxy code of every interface...
sdv::idl::IDefinitionEntity* pDefinition = GetInterface<sdv::idl::IDefinitionEntity>(GetParser());
if (!pDefinition) throw CCompileException("Internal error: the parser doesn't have a root definition.");
sdv::idl::IEntityIterator* pIterator = pDefinition->GetChildren();
if (!pIterator) throw CCompileException("Internal error: the parser doesn't support entity iteration.");
ProcessEntities(streamHdr, streamCpp, pIterator);
// End of safeguard
streamHdr << Safeguard(pathFileHdr, false);
return true;
}
void CPSClassGeneratorBase::ProcessEntities(std::ostream& rstreamHdr, std::ostream& rstreamCpp,
sdv::idl::IEntityIterator* pIterator)
{
if (!pIterator) throw CCompileException("Internal error: processing entities without iterator.");
for (uint32_t uiIndex = 0; uiIndex < pIterator->GetCount(); uiIndex++)
{
// Get the entity
sdv::IInterfaceAccess* pEntity = pIterator->GetEntityByIndex(uiIndex);
if (!pEntity) throw CCompileException("Internal error: processing non-existent entity.");
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
const sdv::idl::IEntityContext* pContext = GetInterface<sdv::idl::IEntityContext>(pEntity);
if (!pContext) throw CCompileException("Internal error: the entity doesn't expose context information.");
sdv::idl::IDefinitionEntity* pDefinition = GetInterface<sdv::idl::IDefinitionEntity>(pEntity);
const sdv::idl::IInterfaceEntity* pInterface = GetInterface<sdv::idl::IInterfaceEntity>(pEntity);
// Only process entities in the source code
if (pContext->GetLocation() != sdv::idl::IEntityContext::ELocation::source)
continue;
// Forward declaration
if (pEntityInfo->ForwardDeclaration())
continue;
// Process interfaces only... but only when not local
if (pInterface && pInterface->IsLocal()) continue;
switch (pEntityInfo->GetType())
{
case sdv::idl::EEntityType::type_interface:
StreamInterface(rstreamHdr, rstreamCpp, pEntity);
break;
default:
break;
}
// Does the entity have children?
sdv::idl::IEntityIterator* pChildIterator = pDefinition ? pDefinition->GetChildren() : nullptr;
if (pChildIterator) ProcessEntities(rstreamHdr, rstreamCpp, pChildIterator);
}
}
void CPSClassGeneratorBase::StreamInterface(std::ostream& rstreamHdr, std::ostream& rstreamCpp,
sdv::IInterfaceAccess* pEntity)
{
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
std::string ssClassName = QualifyName(pEntityInfo->GetScopedName()) + "__" + GetNameAppendix();
// TODO: Deal with multiple inheritance
std::string ssAliasName = pEntityInfo->GetScopedName();
for (size_t nPos = ssAliasName.find("::"); nPos != std::string::npos; nPos = ssAliasName.find("::"))
ssAliasName.replace(nPos, 2, "_");
CKeywordMap mapKeywords = {
{"class_name", ssClassName},
{"alias_name", ssAliasName},
{"interface_name", pEntityInfo->GetScopedName()},
{"interface_id", std::to_string(pEntityInfo->GetId())}
};
// Generate class definition and constructor/destructor
rstreamHdr << ReplaceKeywords(GetClassDefBegin(mapKeywords), mapKeywords);
rstreamCpp << ReplaceKeywords(GetConstructImplBegin(mapKeywords), mapKeywords);
// Stream the interface content
std::stringstream sstreamImpl;
uint32_t uiFuncIndex = 0;
StreamInterfaceContent(rstreamHdr, rstreamCpp, sstreamImpl, mapKeywords, pEntity, uiFuncIndex);
// Finalize the interface
rstreamHdr << ReplaceKeywords(GetClassDefEnd(mapKeywords), mapKeywords);
rstreamCpp << ReplaceKeywords(GetConstructImplEnd(mapKeywords), mapKeywords);
rstreamCpp << sstreamImpl.str();
}
void CPSClassGeneratorBase::StreamInterfaceContent(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody,
std::ostream& rstreamClassImpl, const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pEntity, uint32_t& ruiFuncCnt)
{
// Stream base interfaces first
sdv::idl::IDefinitionEntity* pDefinition = GetInterface<sdv::idl::IDefinitionEntity>(pEntity);
sdv::idl::IEntityIterator* pInheritanceIterator = pDefinition ? pDefinition->GetInheritance() : nullptr;
for (uint32_t uiIndex = 0; pInheritanceIterator && uiIndex < pInheritanceIterator->GetCount(); uiIndex++)
{
sdv::IInterfaceAccess* pBaseEntity = pInheritanceIterator->GetEntityByIndex(uiIndex);
if (!pBaseEntity->GetInterface<sdv::idl::IEntityInfo>())
throw CCompileException("Internal error: the entity inherits from an unknown entity.");
StreamInterfaceContent(rstreamClassDef, rstreamConstrBody, rstreamClassImpl, rmapKeywords, pBaseEntity, ruiFuncCnt);
}
// Run through the children and process attributes and operations
sdv::idl::IEntityIterator* pChildIterator = pDefinition ? pDefinition->GetChildren() : nullptr;
for (uint32_t uiIndex = 0; pChildIterator && uiIndex < pChildIterator->GetCount(); uiIndex++)
{
// Get the entity
sdv::IInterfaceAccess* pChildEntity = pChildIterator->GetEntityByIndex(uiIndex);
if (!pEntity) throw CCompileException("Internal error: processing non-existent entity.");
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pChildEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
const sdv::idl::IEntityContext* pContext = GetInterface<sdv::idl::IEntityContext>(pChildEntity);
if (!pContext) throw CCompileException("Internal error: the entity doesn't expose context information.");
// Forward declaration
if (pEntityInfo->ForwardDeclaration())
continue;
switch (pEntityInfo->GetType())
{
case sdv::idl::EEntityType::type_attribute:
StreamAttribute(rstreamClassDef, rstreamConstrBody, rstreamClassImpl, rmapKeywords, pChildEntity, ruiFuncCnt);
break;
case sdv::idl::EEntityType::type_operation:
StreamOperation(rstreamClassDef, rstreamConstrBody, rstreamClassImpl, rmapKeywords, pChildEntity, ruiFuncCnt);
break;
default:
break;
}
}
}
void CPSClassGeneratorBase::StreamAttribute(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody,
std::ostream& rstreamClassImpl, const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pEntity, uint32_t& ruiFuncCnt)
{
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
if (pEntityInfo->GetType() != sdv::idl::EEntityType::type_attribute)
throw CCompileException("Internal error: the entity has incorrect type information.");
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pEntity);
if (!pDeclaration)
throw CCompileException("Internal error: the entity is a declaration, but doesn't expose declaration information.");
CExceptionVector vecReadExceptions;
sdv::idl::IAttributeEntity* pAttribute = GetInterface<sdv::idl::IAttributeEntity>(pEntity);
if (!pAttribute) throw CCompileException("Internal error: operation is not exposing operation information.");
sdv::idl::IEntityIterator* pExceptionIterator = pAttribute->GetReadExceptions();
for (uint32_t uiIndex = 0; pExceptionIterator && uiIndex < pExceptionIterator->GetCount(); uiIndex++)
{
// Get the parameter entity
sdv::IInterfaceAccess* pExceptionEntity = pExceptionIterator->GetEntityByIndex(uiIndex);
if (!pExceptionEntity) throw CCompileException("Internal error: processing non-existent exception entity.");
const sdv::idl::IEntityInfo* pExceptionEntityInfo = pExceptionEntity->GetInterface<sdv::idl::IEntityInfo>();
if (!pExceptionEntityInfo) throw CCompileException("Internal error: processing non-existent exception entity.");
vecReadExceptions.push_back(pExceptionEntityInfo->GetScopedName());
}
// Stream the getter function
CKeywordMap mapKeywordsGetter = rmapKeywords;
mapKeywordsGetter.insert(std::make_pair("func_name", std::string("get_") + pEntityInfo->GetName()));
StreamFunction(rstreamClassDef, rstreamConstrBody, rstreamClassImpl, mapKeywordsGetter, pEntity, ruiFuncCnt, true,
std::vector<sdv::IInterfaceAccess*>(), vecReadExceptions);
// Stream the setter function if not readonly.
if (!pDeclaration->IsReadOnly())
{
CExceptionVector vecWriteExceptions;
pExceptionIterator = pAttribute->GetWriteExceptions();
for (uint32_t uiIndex = 0; pExceptionIterator && uiIndex < pExceptionIterator->GetCount(); uiIndex++)
{
// Get the parameter entity
sdv::IInterfaceAccess* pExceptionEntity = pExceptionIterator->GetEntityByIndex(uiIndex);
if (!pExceptionEntity) throw CCompileException("Internal error: processing non-existent exception entity.");
const sdv::idl::IEntityInfo* pExceptionEntityInfo = pExceptionEntity->GetInterface<sdv::idl::IEntityInfo>();
if (!pExceptionEntityInfo) throw CCompileException("Internal error: processing non-existent exception entity.");
vecWriteExceptions.push_back(pExceptionEntityInfo->GetScopedName());
}
CKeywordMap mapKeywordsSetter = rmapKeywords;
mapKeywordsSetter.insert(std::make_pair("func_name", std::string("set_") + pEntityInfo->GetName()));
StreamFunction(rstreamClassDef, rstreamConstrBody, rstreamClassImpl, mapKeywordsSetter, nullptr, ruiFuncCnt, false,
std::vector<sdv::IInterfaceAccess*>({ pEntity }), vecWriteExceptions);
}
}
void CPSClassGeneratorBase::StreamOperation(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody,
std::ostream& rstreamClassImpl, const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pEntity, uint32_t& ruiFuncCnt)
{
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pEntity);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
if (pEntityInfo->GetType() != sdv::idl::EEntityType::type_operation)
throw CCompileException("Internal error: the entity has incorrect type information.");
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pEntity);
if (!pDeclaration)
throw CCompileException("Internal error: the entity is a declaration, but doesn't expose declaration information.");
// Build a parameter vector (if there are any).
std::vector<sdv::IInterfaceAccess*> vecParams;
sdv::idl::IOperationEntity* pOperation = GetInterface<sdv::idl::IOperationEntity>(pEntity);
if (!pOperation) throw CCompileException("Internal error: operation is not exposing operation information.");
sdv::idl::IEntityIterator* pParamIterator = pOperation->GetParameters();
for (uint32_t uiIndex = 0; pParamIterator && uiIndex < pParamIterator->GetCount(); uiIndex++)
{
// Get the parameter entity
sdv::IInterfaceAccess* pParamEntity = pParamIterator->GetEntityByIndex(uiIndex);
if (!pParamEntity) throw CCompileException("Internal error: processing non-existent parameter entity.");
const sdv::idl::IParameterEntity* pParameter = GetInterface<sdv::idl::IParameterEntity>(pParamEntity);
if (!pParameter)
throw CCompileException("Internal error: the entity is a parameter, but doesn't expose parameter information.");
vecParams.push_back(pParamEntity);
}
CExceptionVector vecExceptions;
sdv::idl::IEntityIterator* pExceptionIterator = pOperation ? pOperation->GetExceptions() : nullptr;
for (uint32_t uiIndex = 0; pExceptionIterator && uiIndex < pExceptionIterator->GetCount(); uiIndex++)
{
// Get the parameter entity
sdv::IInterfaceAccess* pExceptionEntity = pExceptionIterator->GetEntityByIndex(uiIndex);
if (!pExceptionEntity) throw CCompileException("Internal error: processing non-existent exception entity.");
const sdv::idl::IEntityInfo* pExceptionEntityInfo = pExceptionEntity->GetInterface<sdv::idl::IEntityInfo>();
if (!pExceptionEntityInfo) throw CCompileException("Internal error: processing non-existent exception entity.");
vecExceptions.push_back(pExceptionEntityInfo->GetScopedName());
}
// Stream the operation
CKeywordMap mapKeywordsOperation = rmapKeywords;
mapKeywordsOperation.insert(std::make_pair("func_name", pEntityInfo->GetName()));
StreamFunction(rstreamClassDef, rstreamConstrBody, rstreamClassImpl, mapKeywordsOperation, pEntity, ruiFuncCnt,
pDeclaration->IsReadOnly(), vecParams, vecExceptions);
}
void CPSClassGeneratorBase::StreamFunction(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody,
std::ostream& rstreamClassImpl, const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pRetParam,
uint32_t& ruiFuncCnt, bool bConst, const std::vector<sdv::IInterfaceAccess*>& rvecParams, const CExceptionVector& rvecExceptions)
{
// Get the parameter information and build the parameter pack definitions
std::vector<SParamInfo> vecParamInfos;
std::stringstream sstreamParamPackDef, sstreamParamPackUse;
SParamInfo sReturnInfo = GetParamInfo(pRetParam, true);
if (sReturnInfo.bValidType)
vecParamInfos.push_back(sReturnInfo);
size_t nInputCnt = 0;
size_t nOutputCnt = vecParamInfos.size();
for (sdv::IInterfaceAccess* pParam : rvecParams)
{
// Get parameter info
SParamInfo sParamInfo = GetParamInfo(pParam);
// Add to parameter pack definition
switch (sParamInfo.eDirection)
{
case SParamInfo::EDirection::in:
if (!sstreamParamPackDef.str().empty()) sstreamParamPackDef << ", ";
if (!sstreamParamPackUse.str().empty()) sstreamParamPackUse << ", ";
if ((sParamInfo.bIsPointer || sParamInfo.bIsComplex) && !sParamInfo.bIsInterface)
sstreamParamPackDef << "const ";
sstreamParamPackDef << sParamInfo.ssDeclType;
if (sParamInfo.bIsComplex && !sParamInfo.bIsInterface)
sstreamParamPackDef << "&";
sstreamParamPackDef << " " << sParamInfo.ssName;
sstreamParamPackUse << sParamInfo.ssName;
nInputCnt++;
break;
case SParamInfo::EDirection::inout:
nInputCnt++;
if (!sstreamParamPackDef.str().empty()) sstreamParamPackDef << ", ";
if (!sstreamParamPackUse.str().empty()) sstreamParamPackUse << ", ";
sstreamParamPackDef << sParamInfo.ssDeclType << "& " << sParamInfo.ssName;
sstreamParamPackUse << sParamInfo.ssName;
nOutputCnt++;
nInputCnt++;
break;
case SParamInfo::EDirection::out:
if (!sstreamParamPackDef.str().empty()) sstreamParamPackDef << ", ";
if (!sstreamParamPackUse.str().empty()) sstreamParamPackUse << ", ";
sstreamParamPackDef << sParamInfo.ssDeclType << "& " << sParamInfo.ssName;
sstreamParamPackUse << sParamInfo.ssName;
nOutputCnt++;
break;
default:
// Do not add anything
break;
}
// Store in vector
vecParamInfos.push_back(std::move(sParamInfo));
}
// In case there are no parameters and no return values, add at least one parameter to be able to receive exceptions.
if (vecParamInfos.empty())
vecParamInfos.push_back(SParamInfo());
// Get function information
SFuncInfo sFuncInfo{};
sFuncInfo.ssName = ReplaceKeywords("%func_name%", rmapKeywords);
sFuncInfo.ssDecl = sReturnInfo.ssDeclType;
sFuncInfo.ssDeclType = sReturnInfo.ssDeclType;
sFuncInfo.ssDefRetValue = sReturnInfo.ssDefaultValue;
sFuncInfo.bIsConst = bConst;
sFuncInfo.nInputParamCnt = nInputCnt;
sFuncInfo.nOutputParamCnt = nOutputCnt;
// Stream the getter function
CKeywordMap mapKeywordsFunction = rmapKeywords;
mapKeywordsFunction.insert(std::make_pair("func_decl_type", sReturnInfo.ssDeclType));
mapKeywordsFunction.insert(std::make_pair("func_default_ret_value", sReturnInfo.ssDefaultValue));
mapKeywordsFunction.insert(std::make_pair("func_index", std::to_string(ruiFuncCnt)));
mapKeywordsFunction.insert(std::make_pair("param_pack_def", sstreamParamPackDef.str()));
mapKeywordsFunction.insert(std::make_pair("param_pack_use", sstreamParamPackUse.str()));
mapKeywordsFunction.insert(std::make_pair("total_param_cnt", std::to_string(vecParamInfos.size())));
// Stream constructor implementation
rstreamConstrBody << ReplaceKeywords(GetConstructFuncImpl(sFuncInfo, mapKeywordsFunction), mapKeywordsFunction);
// Stream func prototype
rstreamClassDef << ReplaceKeywords(GetFuncDef(sFuncInfo, mapKeywordsFunction), mapKeywordsFunction);
// Stream the parameter init code.
std::stringstream sstreamParamInit;
size_t nIndex = 0;
for (const SParamInfo& rsParam : vecParamInfos)
{
CKeywordMap mapKeywordsParam = mapKeywordsFunction;
mapKeywordsParam.insert(std::make_pair("param_name", rsParam.ssName));
mapKeywordsParam.insert(std::make_pair("param_decl_type", rsParam.ssDeclType));
mapKeywordsParam.insert(std::make_pair("param_index", std::to_string(nIndex)));
mapKeywordsParam.insert(std::make_pair("param_default_val", rsParam.ssDefaultValue));
mapKeywordsParam.insert(std::make_pair("param_size", rsParam.ssSize));
sstreamParamInit << ReplaceKeywords(GetFuncImplParamInit(sFuncInfo, rsParam, mapKeywordsParam), mapKeywordsParam);
nIndex++;
}
// Stream the parameter input code.
std::stringstream sstreamParamInput;
nIndex = 0;
for (const SParamInfo& rsParam : vecParamInfos)
{
CKeywordMap mapKeywordsParam = mapKeywordsFunction;
mapKeywordsParam.insert(std::make_pair("param_name", rsParam.ssName));
mapKeywordsParam.insert(std::make_pair("param_decl_type", rsParam.ssDeclType));
mapKeywordsParam.insert(std::make_pair("param_index", std::to_string(nIndex)));
mapKeywordsParam.insert(std::make_pair("param_default_val", rsParam.ssDefaultValue));
mapKeywordsParam.insert(std::make_pair("param_size", rsParam.ssSize));
sstreamParamInput << ReplaceKeywords(GetFuncImplStreamParamInput(sFuncInfo, rsParam, mapKeywordsParam), mapKeywordsParam);
nIndex++;
}
// Stream the parameter input code.
std::stringstream sstreamParamOutput;
nIndex = 0;
for (const SParamInfo& rsParam : vecParamInfos)
{
CKeywordMap mapKeywordsParam = mapKeywordsFunction;
mapKeywordsParam.insert(std::make_pair("param_name", rsParam.ssName));
mapKeywordsParam.insert(std::make_pair("param_decl_type", rsParam.ssDeclType));
mapKeywordsParam.insert(std::make_pair("param_index", std::to_string(nIndex)));
mapKeywordsParam.insert(std::make_pair("param_default_val", rsParam.ssDefaultValue));
mapKeywordsParam.insert(std::make_pair("param_size", rsParam.ssSize));
sstreamParamOutput << ReplaceKeywords(GetFuncImplStreamParamOutput(sFuncInfo, rsParam, mapKeywordsParam), mapKeywordsParam);
nIndex++;
}
// Stream the parameter term code.
std::stringstream sstreamParamTerm;
nIndex = 0;
for (const SParamInfo& rsParam : vecParamInfos)
{
CKeywordMap mapKeywordsParam = mapKeywordsFunction;
mapKeywordsParam.insert(std::make_pair("param_name", rsParam.ssName));
mapKeywordsParam.insert(std::make_pair("param_decl_type", rsParam.ssDeclType));
mapKeywordsParam.insert(std::make_pair("param_index", std::to_string(nIndex)));
mapKeywordsParam.insert(std::make_pair("param_default_val", rsParam.ssDefaultValue));
mapKeywordsParam.insert(std::make_pair("param_size", rsParam.ssSize));
sstreamParamInit << ReplaceKeywords(GetFuncImplParamTerm(sFuncInfo, rsParam, mapKeywordsParam), mapKeywordsParam);
nIndex++;
}
// Stream func implementation
CKeywordMap mapKeywordsFunctionImpl = mapKeywordsFunction;
mapKeywordsFunctionImpl.insert(std::make_pair("param_init", sstreamParamInit.str()));
mapKeywordsFunctionImpl.insert(std::make_pair("stream_param_input", sstreamParamInput.str()));
mapKeywordsFunctionImpl.insert(std::make_pair("stream_param_output", sstreamParamOutput.str()));
mapKeywordsFunctionImpl.insert(std::make_pair("param_term", sstreamParamTerm.str()));
rstreamClassImpl << ReplaceKeywords(GetFuncImpl(sFuncInfo, mapKeywordsFunctionImpl, rvecExceptions), mapKeywordsFunctionImpl);
// Increase the function index
ruiFuncCnt++;
}
CPSClassGeneratorBase::SParamInfo CPSClassGeneratorBase::GetParamInfo(sdv::IInterfaceAccess* pParam, bool bIsRetValue /*= false*/) const
{
SParamInfo sInfo;
if (bIsRetValue)
sInfo.eDirection = SParamInfo::EDirection::ret;
if (!pParam)
{
// Special case... void return parameter
if (!bIsRetValue) throw CCompileException("Internal error: function parameter cannot be void.");
sInfo.ssDeclType = "void";
sInfo.eDirection = SParamInfo::EDirection::ret;
return sInfo;
}
// Get the entity interfaces.
const sdv::idl::IEntityInfo* pEntityInfo = GetInterface<sdv::idl::IEntityInfo>(pParam);
if (!pEntityInfo) throw CCompileException("Internal error: the entity doesn't expose information.");
const sdv::idl::IDeclarationEntity* pDeclaration = GetInterface<sdv::idl::IDeclarationEntity>(pParam);
if (!pDeclaration)
throw CCompileException("Internal error: the entity is a declaration, but doesn't expose declaration information.");
const sdv::idl::IParameterEntity* pParameter = GetInterface<sdv::idl::IParameterEntity>(pParam);
sInfo.ssName = bIsRetValue ? "return_value" : pEntityInfo->GetName();
static_cast<SCDeclInfo&>(sInfo) = GetCDeclTypeStr(pDeclaration->GetDeclarationType(), std::string(), true);
if (sInfo.bIsDynamic)
{
sInfo.ssSize = std::string("(") + sInfo.ssName + " ? static_cast<uint32_t>(std::char_traits<" + sInfo.ssDeclType +
">::length(" + sInfo.ssName + ") + 1) * sizeof(" + sInfo.ssDeclType + ") : 0)";
sInfo.eAllocType = SParamInfo::EAllocType::indirect;
} else
sInfo.ssSize = std::string("static_cast<uint32_t>(sizeof(") + sInfo.ssDeclType + "))";
if (!bIsRetValue || sInfo.ssDeclType != "void")
{
// GCC-issue: Types defined as "long long", "long int", "long long int", "long double" cannot be initialized using brackets.
if (sInfo.ssDeclType.substr(0, 4) == "long")
sInfo.ssDefaultValue = sInfo.bIsPointer ? "nullptr" : std::string("static_cast<") + sInfo.ssDeclType + ">(0)";
else
sInfo.ssDefaultValue = sInfo.bIsPointer ? "nullptr" : sInfo.ssDeclType + "{}";
}
if (!bIsRetValue)
{
// Stream the parameter direction. All but the input parameter need to support the ouput of values.
switch (pParameter ? pParameter->GetDirection() : sdv::idl::IParameterEntity::EParameterDirection::input)
{
case sdv::idl::IParameterEntity::EParameterDirection::output:
sInfo.eDirection = SParamInfo::EDirection::out;
break;
case sdv::idl::IParameterEntity::EParameterDirection::in_out:
sInfo.eDirection = SParamInfo::EDirection::inout;
break;
case sdv::idl::IParameterEntity::EParameterDirection::input:
default:
sInfo.eDirection = SParamInfo::EDirection::in;
break;
}
}
return sInfo;
}
void CPSClassGeneratorBase::StreamMarshallDecl(std::ofstream& rstream, const CKeywordMap& rmapKeywords, uint32_t uiFuncIndex,
uint32_t uiParamCnt)
{
CKeywordMap mapKeywordsMarshall = rmapKeywords;
mapKeywordsMarshall.insert(std::make_pair("index", std::to_string(uiFuncIndex)));
mapKeywordsMarshall.insert(std::make_pair("paramcnt", std::to_string(uiParamCnt)));
// Code generation
std::string ssDeclCode = R"code( // Declare the marshall structure
sdv::core::SMarshall sPacket{};
union { uint16_t uiWord; uint8_t rguiBytes[2]; } uEndian = {1};
sPacket.uiEndian = uEndian.rguiBytes[1];
sPacket.uiPadding = 0;
sPacket.uiVersion = 100;
sPacket.tIfcId = %interface_name%::_id;
sPacket.uiFuncIndex = %index%;
)code";
rstream << ReplaceKeywords(ssDeclCode, mapKeywordsMarshall);
if (uiParamCnt)
{
std::string ssParamDeclCode = R"code( sPacket.uiCount = %paramcnt%;
sdv::core::SMarshall::SParam rgsParams[%paramcnt%] = {};
sPacket.rgsParams = rgsParams;
)code";
rstream << ReplaceKeywords(ssParamDeclCode, mapKeywordsMarshall);
}
}
bool CPSClassGeneratorBase::RuntimeProcessingRequired(sdv::IInterfaceAccess* pEntity)
{
// Get the entity interfaces.
sdv::idl::IDeclarationEntity* pDeclaration = pEntity->GetInterface<sdv::idl::IDeclarationEntity>();
if (!pDeclaration) throw CCompileException("Internal error: expecting a declaration.");
// Request the type
sdv::IInterfaceAccess* pTypeObj = pDeclaration->GetDeclarationType();
if (!pTypeObj) throw CCompileException("Internal error: expecting a declaration type.");
const sdv::idl::IDeclarationType* pDeclType = pTypeObj->GetInterface<sdv::idl::IDeclarationType>();
if (!pDeclType) throw CCompileException("Internal error: expecting a declaration type.");
sdv::idl::EDeclType eType = pDeclType->GetBaseType();
sdv::IInterfaceAccess* pType = pDeclType->GetTypeDefinition();
// Check whether the entity requires runtime processing.
switch (eType)
{
case sdv::idl::EDeclType::decltype_interface:
case sdv::idl::EDeclType::decltype_string:
case sdv::idl::EDeclType::decltype_u8string:
case sdv::idl::EDeclType::decltype_u16string:
case sdv::idl::EDeclType::decltype_u32string:
case sdv::idl::EDeclType::decltype_wstring:
return true;
default:
break;
}
// If there is a pType, this can either be a typedef or a definition.
const sdv::idl::IDeclarationEntity* pTypedefDeclaration = pType->GetInterface<sdv::idl::IDeclarationEntity>();
// Forward the request in case the type is a declaration
if (pTypedefDeclaration) return RuntimeProcessingRequired(pType);
// Get the definition and check for children
sdv::idl::IDefinitionEntity* pDefinition = pType->GetInterface<sdv::idl::IDefinitionEntity>();
sdv::idl::IEntityIterator* pChildIterator =
pDefinition ? pType->GetInterface<sdv::idl::IEntityIterator>() : nullptr;
for (uint32_t uiIndex = 0; pChildIterator && uiIndex < pChildIterator->GetCount(); uiIndex++)
{
sdv::IInterfaceAccess* pChildEntity = pChildIterator->GetEntityByIndex(uiIndex);
if (!pChildEntity) throw CCompileException("Internal error: definition doesn't have a valid child entity.");
const sdv::idl::IEntityInfo* pChildEntityInfo = pChildEntity->GetInterface<sdv::idl::IEntityInfo>();
if (!pChildEntityInfo) throw CCompileException("Internal error: definition doesn't have valid child entity info.");
if (pChildEntityInfo->GetType() != sdv::idl::EEntityType::type_variable) continue; // Only variables are of interest.
const sdv::idl::IDeclarationEntity* pChildDeclaration = pChildEntity->GetInterface<sdv::idl::IDeclarationEntity>();
if (!pChildDeclaration) throw CCompileException("Internal error: variable doesn't expose a declaration interface.");
if (pChildDeclaration->IsReadOnly()) continue; // Static const variables are counting.
// Check the child
if (RuntimeProcessingRequired(pChildEntity))
return true; // At least one member of the variable requires runtime processing.
}
// No variables requiring runtime processing detected
return false;
}

View File

@@ -0,0 +1,325 @@
#ifndef PS_CLASS_GENERATOR_BASE_H
#define PS_CLASS_GENERATOR_BASE_H
#include "context.h"
#include <vector>
/**
* @brief Proxy/stub class generator base implementation.
*/
class CPSClassGeneratorBase : public CGenContext
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CPSClassGeneratorBase(sdv::IInterfaceAccess* pParser);
/**
* @brief Generate the proxy.
* @return Returns whether generation was successful.
*/
virtual bool Generate();
protected:
/**
* @brief Return the name addition to be added to the filename and class definition.
* @return The name appendix string.
*/
virtual std::string GetNameAppendix() const = 0;
/**
* @brief Get definition file comments to be written in the file header.
* @return String with class definition comments.
*/
virtual std::string GetClassDefFileComments() const = 0;
/**
* @brief Get implementation file comments to be written in the file header.
* @return String with class implementation comments.
*/
virtual std::string GetClassImplFileComments() const = 0;
/**
* @brief Get begin of class definition to be inserted into the header file.
* @remarks The following keywords are defined: %class_name% and %interface_name%.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with class definition begin.
*/
virtual std::string GetClassDefBegin(CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get end of class definition to be inserted into the header file.
* @remarks The following keywords are defined: %class_name% and %interface_name%.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with class definition end.
*/
virtual std::string GetClassDefEnd(CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get begin of constructor implementation to be inserted into the cpp file.
* @remarks The following keywords are defined: %class_name% and %interface_name%.
* @param[in, out] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with constructor implementation begin.
*/
virtual std::string GetConstructImplBegin(CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get end of constructor implementation to be inserted into the cpp file.
* @remarks The following keywords are defined: %class_name% and %interface_name%.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with constructor implementation end.
*/
virtual std::string GetConstructImplEnd(CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Function information structure
*/
struct SFuncInfo
{
std::string ssName; ///< Function name.
std::string ssDecl; ///< Return value declaration type (could be void).
std::string ssDeclType; ///< Return value declaration base type.
std::string ssDefRetValue; ///< Default return value (or empty if decl type is void).
bool bIsConst = false; ///< Set when the function is declared as const function.
size_t nInputParamCnt = 0; ///< Input parameter count.
size_t nOutputParamCnt = 0; ///< Output parameter count.
};
/**
* @brief Parameter information structure
*/
struct SParamInfo : SCDeclInfo
{
std::string ssName; ///< Parameter name
std::string ssDefaultValue; ///< Parameter default value (or empty for void return value)
std::string ssSize; ///< Parameter size
enum class EDirection { in, out, inout, ret, ignored } eDirection = EDirection::ignored; ///< Parameter direction or return value
enum class EAllocType { direct, indirect, ifc} eAllocType = EAllocType::direct; ///< Parameter allocation type
};
/**
* @brief Get the constructor body for a function (attribute or operation).
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* and %out_param_cnt%.
* @param[in] rsFunc Reference to the function information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with constructor function body.
*/
virtual std::string GetConstructFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get the function definition (attribute or operation).
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* and %out_param_cnt%.
* @remarks The %out_param_cnt% includes the return parameter.
* @param[in] rsFunc Reference to the function information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with the function definition.
*/
virtual std::string GetFuncDef(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get the function implementation (attribute or operation).
* @details The function implementation uses the specific keywords %param_init%, %stream_input_param%, %stream_output_param%,
* and %param_term% to insert code from the parameter streaming functions.
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* and %out_param_cnt%.
* @remarks The %out_param_cnt% includes the return parameter.
* @param[in] rsFunc Reference to the function information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @param[in] rvecExceptions Vector containing the exceptions defined for this function.
* @return String with the function implementation.
*/
virtual std::string GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords, const CExceptionVector& rvecExceptions) const = 0;
/**
* @brief Get parameter initialization of the function implementation (attribute or operation). The content of this function
* is paced in the keyword %param_init% of the GetFuncImpl function.
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* %out_param_cnt%, %param_name%, %param_decl_type%, %param_index%, %param_default_val%, %param_size% and %param_cnt%.
* @remarks The %out_param_cnt% includes the return parameter.
* @remarks The %param_index% is the parameter index incl. optional return value at index 0.
* @param[in] rsFunc Reference to the function information structure.
* @param[in] rsParam Reference to a parameter information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with the function parameter initialization.
*/
virtual std::string GetFuncImplParamInit(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get input parameter streaming of the function implementation (attribute or operation). The content of this function
* is paced in the keyword %stream_param_input% of the GetFuncImpl function.
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* %out_param_cnt%, %param_name%, %param_decl_type%, %param_index%, %param_default_val%, %param_size% and %param_cnt%.
* @remarks The %out_param_cnt% includes the return parameter.
* @remarks The %param_index% is the parameter index incl. optional return value at index 0.
* @param[in] rsFunc Reference to the function information structure.
* @param[in] rsParam Reference to a parameter information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with the function input parameters.
*/
virtual std::string GetFuncImplStreamParamInput(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get output parameter streaming of the function implementation (attribute or operation). The content of this function
* is paced in the keyword %stream_param_output% of the GetFuncImpl function.
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* %out_param_cnt%, %param_name%, %param_decl_type%, %param_index%, %param_default_val%, %param_size% and %param_cnt%.
* @remarks The %out_param_cnt% includes the return parameter.
* @remarks The %param_index% is the parameter index incl. optional return value at index 0.
* @param[in] rsFunc Reference to the function information structure.
* @param[in] rsParam Reference to a parameter information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with the function output parameters.
*/
virtual std::string GetFuncImplStreamParamOutput(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const = 0;
/**
* @brief Get parameter termination of the function implementation (attribute or operation). The content of this function
* is paced in the keyword %param_term% of the GetFuncImpl function.
* @remarks The following keywords are defined: %class_name%, %interface_name%, %func_decl_type%,
* %func_default_ret_value%, %func_name%, %func_index%, %param_pack_def%, %param_pack_use%, %total_param_cnt%, %in_param_cnt%,
* %out_param_cnt%, %param_name%, %param_decl_type%, %param_index%, %param_default_val%, %param_size% and %param_cnt%.
* @remarks The %out_param_cnt% includes the return parameter.
* @remarks The %param_index% is the parameter index incl. optional return value at index 0.
* @param[in] rsFunc Reference to the function information structure.
* @param[in] rsParam Reference to a parameter information structure.
* @param[inout] rmapKeywords Reference to the keyword map. This allows inserting additional keywords local for the returned
* code snippet.
* @return String with the function parameter termination.
*/
virtual std::string GetFuncImplParamTerm(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const = 0;
private:
/**
* @brief Process the entities.
* @param[in] rstreamHdr Reference to the stream of the target header file.
* @param[in] rstreamCpp Reference to the stream of the target C++ file.
* @param[in] pIterator Pointer to the iterator interface.
*/
void ProcessEntities(std::ostream& rstreamHdr, std::ostream& rstreamCpp, sdv::idl::IEntityIterator* pIterator);
/**
* @brief Stream definition and implementation if the entity is an interface definition.
* @param[in] rstreamHdr Reference to the stream of the target header file.
* @param[in] rstreamCpp Reference to the stream of the target C++ file.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamInterface(std::ostream& rstreamHdr, std::ostream& rstreamCpp, sdv::IInterfaceAccess* pEntity);
/**
* @brief Stream attributes and operations of an interface definition and all derived interface definitions.
* @param[in] rstreamClassDef Reference to the class definition stream.
* @param[in] rstreamConstrBody Reference to the constructor body stream.
* @param[in] rstreamClassImpl Reference to the class implementation stream.
* @param[in] rmapKeywords Map with keywords to replace. Keyword "class" and "interface" are defined.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in, out] ruiFuncCnt Reference to the function counter used to set the index. Will be updated.
*/
void StreamInterfaceContent(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody, std::ostream& rstreamClassImpl,
const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pEntity, uint32_t& ruiFuncCnt);
/**
* @brief Stream attribute declaration if the entity is an attribute.
* attention Comments are not streamed for parameters.
* @param[in] rstreamClassDef Reference to the class definition stream.
* @param[in] rstreamConstrBody Reference to the constructor body stream.
* @param[in] rstreamClassImpl Reference to the class implementation stream.
* @param[in] rmapKeywords Map with keywords to replace. Keyword "class" and "interface" are defined.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in, out] ruiFuncCnt Reference to the function counter used to set the index. Will be updated.
*/
void StreamAttribute(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody, std::ostream& rstreamClassImpl,
const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pEntity, uint32_t& ruiFuncCnt);
/**
* @brief Stream operation declaration if the entity is an operation.
* @param[in] rstreamClassDef Reference to the class definition stream.
* @param[in] rstreamConstrBody Reference to the constructor body stream.
* @param[in] rstreamClassImpl Reference to the class implementation stream.
* @param[in] rmapKeywords Map with keywords to replace. Keyword "class" and "interface" are defined.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in, out] ruiFuncCnt Reference to the function counter used to set the index. Will be updated.
*/
void StreamOperation(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody, std::ostream& rstreamClassImpl,
const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pEntity, uint32_t& ruiFuncCnt);
/**
* @brief Stream function declaration (for attributes and operations).
* @details Functions are streamed in this order:
* - Constructor function impl
* - Function definition
* - Function impl begin
* - For each parameter, param init impl
* - Pass #1 impl begin
* - For each parameter, pass #1 impl
* - Pass #1 impl end
* - Pass #2 impl begin
* - For each parameter, pass #2 impl
* - Pass #2 impl end
* - Function impl end
* @param[in] rstreamClassDef Reference to the class definition stream.
* @param[in] rstreamConstrBody Reference to the constructor body stream.
* @param[in] rstreamClassImpl Reference to the class implementation stream.
* @param[in] rmapKeywords Map with keywords to replace. Additional keyword "func_name" is defined.
* @param[in] pRetParam Pointer to the IInterfaceAccess pointer of the entity holding the return parameter.
* @param[in, out] ruiFuncCnt Reference to the function counter used to set the index. Will be updated.
* @param[in] bConst When set, the function is marked as a const-function.
* @param[in] rvecParams Reference to the vector containing the parameter entities defined for this function.
* @param[in] rvecExceptions Reference to the vector containing the exceptions defined for this function.
*/
void StreamFunction(std::ostream& rstreamClassDef, std::ostream& rstreamConstrBody, std::ostream& rstreamClassImpl,
const CKeywordMap& rmapKeywords, sdv::IInterfaceAccess* pRetParam, uint32_t& ruiFuncCnt, bool bConst,
const std::vector<sdv::IInterfaceAccess*>& rvecParams, const CExceptionVector& rvecExceptions);
/**
* @brief Get parameter information.
* @param[in] pParam Entity representing the parameter (not necessarily a parameter entity). If NULL, expecting it to be a
* "void" return value.
* @param[in] bIsRetValue When set, the parameter is marked as return value.
* @return The parameter information structure.
*/
SParamInfo GetParamInfo(sdv::IInterfaceAccess* pParam, bool bIsRetValue = false) const;
/**
* @brief Stream SMarshall declaration.
* @param[in] rstream Reference to the stream of the target C++ file.
* @param[in] rmapKeywords Map with keywords to replace.
* @param[in] uiFuncIndex Index of the function within the interface.
* @param[in] uiParamCnt Parameter count.
*/
void StreamMarshallDecl(std::ofstream& rstream, const CKeywordMap& rmapKeywords, uint32_t uiFuncIndex, uint32_t uiParamCnt);
/**
* @brief Does the provided entity or any contained entities data types where the interpretation can only be done during
* runtime?
* @details Data types that require runtime processing are dynamic arrays (arrays where the size is determined by another
* entity), interfaces, strings and sequences.
* @param[in] pEntity Pointer to the entity to check for runtime processing requirements.
* @return Returns whether runtime processing is required.
*/
static bool RuntimeProcessingRequired(sdv::IInterfaceAccess* pEntity);
};
#endif // !defined(PS_CLASS_GENERATOR_BASE_H)

View File

@@ -0,0 +1,118 @@
#include "context.h"
#include "ps_cpp_generator.h"
#include "../exception.h"
#include <cassert>
#include <cctype>
#include <fstream>
#include <algorithm>
#include <set>
#include <mutex>
CPsCppGenerator::CPsCppGenerator(sdv::IInterfaceAccess* pParser) : CGenContext(pParser), m_mtx("SDV_IDL_COMPILER_GENERATE_PS")
{}
CPsCppGenerator::~CPsCppGenerator()
{}
bool CPsCppGenerator::Generate()
{
// Synchronize proxy/stub code generation among processes.
std::unique_lock<ipc::named_mutex> lock(m_mtx);
// Create "proxy" directory
std::filesystem::path pathPSTarget = GetOutputDir() / "ps";
if (!std::filesystem::exists(pathPSTarget) && !std::filesystem::create_directory(pathPSTarget))
throw CCompileException("Cannot create proxy/stub directory: ", pathPSTarget.generic_u8string());
// The source string
std::string ssSource;
// File with "CMakeLists.txt" function; read completely if existing
std::filesystem::path pathFile = pathPSTarget / "proxystub.cpp";
if (std::filesystem::exists(pathFile))
{
std::ifstream stream;
stream.open(pathFile);
if (!stream.is_open()) throw CCompileException("Failed to open the proxystub.cpp file for reading.");
// Read the complete source
std::stringstream sstream;
sstream << stream.rdbuf();
ssSource = std::move(sstream.str());
}
// Create the file in memory
if (ssSource.empty())
{
std::stringstream sstream;
// Add file header
sstream << "/**" << std::endl;
sstream << " * @file proxstub.cpp" << std::endl;
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
sstream << " * @date " << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X") << std::endl;
sstream << " * This file was generated by the SDV IDL compiler" << std::endl;
sstream << " *" << std::endl;
sstream << " * Allow including all proxy-stub source code files to be included through this file." << std::endl;
sstream << " */" << std::endl;
sstream << std::endl;
ssSource = std::move(sstream.str());
}
// Search for each #include preprocessor directive
// And build a map of text includes
struct SChunkPos { size_t nPos = 0; size_t nLen = 0; };
std::set<std::string> setFiles;
size_t nPos = 0;
while (nPos != std::string::npos)
{
// Find the directive
nPos = ssSource.find("#include", nPos);
if (nPos == std::string::npos) continue;
nPos += 8;
// Skip whitespace
while (nPos < ssSource.size() && std::isspace(ssSource[nPos])) nPos++;
if (nPos >= ssSource.size()) continue;
// Check for quote
if (ssSource[nPos] != '\"') continue;
nPos++;
// Extract the include file and add to the set
size_t nFilePos = nPos;
while (nPos < ssSource.size() && ssSource[nPos] != '\"') nPos++;
if (nPos >= ssSource.size()) continue;
setFiles.insert(ssSource.substr(nFilePos, nPos - nFilePos));
}
// Insert additional files if needed
size_t nSourceSize = ssSource.size();
std::filesystem::path pathPSFileBase = GetSource().filename();
pathPSFileBase.replace_extension("");
std::string ssFileBase = pathPSFileBase.generic_u8string();
if (setFiles.find(ssFileBase + "_stub.cpp") == setFiles.end())
{
std::stringstream sstream;
sstream << std::endl;
sstream << "// Adding proxy and stub code for " << GetSource().filename().generic_u8string() << std::endl;
sstream << "#include \"" << ssFileBase << "_stub.cpp" << "\"" << std::endl;
sstream << "#include \"" << ssFileBase << "_proxy.cpp" << "\"" << std::endl;
ssSource += sstream.str();
}
// Write the file again if needed
if (nSourceSize != ssSource.size())
{
std::ofstream stream;
stream.open(pathFile, std::ofstream::trunc);
if (!stream.is_open()) throw CCompileException("Failed to open the proxystub.cpp file for writing.");
// Write the complete source
stream << ssSource;
}
// Done!
return true;
}

View File

@@ -0,0 +1,33 @@
#ifndef PS_CPP_GENERATOR_H
#define PS_CPP_GENERATOR_H
#include "../../../global/ipc_named_mutex.h"
/**
* @brief Prox/stub CPP file generator class.
*/
class CPsCppGenerator : public CGenContext
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CPsCppGenerator(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CPsCppGenerator() override;
/**
* @brief Generate the definition.
* @return Returns whether the generation was successful.
*/
bool Generate();
private:
ipc::named_mutex m_mtx; ///< Guarantee exclusive access while writing the PS file.
};
#endif // !defined PS_CPP_GENERATOR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,289 @@
#ifndef SERDES_GENERATOR_H
#define SERDES_GENERATOR_H
#include "definition_generator_base.h"
#include <set>
#include <fstream>
/**
* @brief Definition stream context.
*/
class CSerdesContext : public CDefEntityContext<CSerdesContext>
{
public:
/**
* @brief Constructor assigning the generator context.
* @param[in] rGenContext Reference to the context to assign.
* @param[in] pEntity Pointer to the definition entity this context belongs to.
*/
CSerdesContext(const CGenContext& rGenContext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Copy constructor assigning a new definition entity.
* @param[in] rcontext Original context to copy from.
* @param[in] pEntity Pointer to the definition entity this context belongs to.
* @param[in] rssDeclName Reference to the declaration name to be added to the member scope.
*/
CSerdesContext(CSerdesContext& rcontext, sdv::IInterfaceAccess* pEntity, const std::string& rssDeclName = std::string());
/**
* @brief Join a context into this context. Overload of CDefEntityContext::operator<<.
* @param[in] rcontext Reference to the context to join.
* @return Reference to this context containing the joined result.
*/
virtual CSerdesContext& operator<<(const CSerdesContext& rcontext) override;
/**
* @brief Get the member scoped name when streaming declarations using a container higher in the hierarchy.
* @return The member scope to use for streaming. Types are separated by a scope-serparator '::' and members are separated by a
* dot '.' separation character.
* @param[in] rssDeclName Reference to the string holding the declaration name to use for a full scoped name.
* @param[in] bFullScope When set, add the container scoped name as well. Otherwise omit the container scoped name.
*/
std::string ComposeMemberScope(const std::string& rssDeclName, bool bFullScope = false) const;
/**
* @brief All following code is part of the serdes namespace.
*/
void EnableSerDesNamespace();
/**
* @brief All following code is not part of the serdes namespace.
*/
void DisableSerDesNamespace();
/**
* @brief Get a reference to the serializer/deserializer code stream.
* @return Reference to the definition body stream object.
*/
std::stringstream& GetSerDesCodeStream();
/**
* @brief Get serializer/deserializer code.
* @return Returns a string containing the serializer/deserializer code collected within this context.
*/
std::string GetSerDesCode() const;
/**
* @brief Function part enumeration
*/
enum class EFuncStreamPart
{
header, ///< Function header
body, ///< Function body
footer, ///< Function footer
};
/**
* @brief Get a reference to the size function stream.
* @param[in] ePart The function part to return.
* @return Reference to the serializer function stream object.
*/
std::stringstream& GetSizeFuncStream(EFuncStreamPart ePart = EFuncStreamPart::body);
/**
* @brief Get a reference to the serializer function stream.
* @param[in] ePart The function part to return.
* @return Reference to the serializer function stream object.
*/
std::stringstream& GetSerFuncStream(EFuncStreamPart ePart = EFuncStreamPart::body);
/**
* @brief Get a reference to the deserializer function stream.
* @param[in] ePart The function part to return.
* @return Reference to the deserializer function stream object.
*/
std::stringstream& GetDesFuncStream(EFuncStreamPart ePart = EFuncStreamPart::body);
/**
* @brief Get the size function code.
* @param[in] ePart The function part to return.
* @return String with the function code to.
*/
std::string GetSizeFuncCode(EFuncStreamPart ePart = EFuncStreamPart::body) const;
/**
* @brief Get the serializer function code.
* @param[in] ePart The function part to return.
* @return String with the function code to.
*/
std::string GetSerFuncCode(EFuncStreamPart ePart = EFuncStreamPart::body) const;
/**
* @brief Get a reference to the deserializer function stream.
* @param[in] ePart The function part to return.
* @return String with the function code to.
*/
std::string GetDesFuncCode(EFuncStreamPart ePart = EFuncStreamPart::body) const;
/**
* @brief Join the serialization and deserialization function body content to the body content within this stream.
* @param[in] rcontext Reference to the context containing of the function bodies.
* @param[in] bDoNotIncludeNewline When set, do not insert a newline if needed.
*/
void JoinFuncBodyStreams(const CSerdesContext& rcontext, bool bDoNotIncludeNewline = false);
/**
* @brief Stream the class definition with inserted serializer and deserializer functions to the definition stream and clear the
* serializer and deserializer streams when finished.
* @param[in] rssClassBegin The class definition part to stream before the serializer functions are streamed.
* @param[in] rssClassEnd The class definition part to stream after the serializer functions are streamed.
* @param[in] rmapKeywords Use the map of keywords for keyword replacement within the texts.
*/
void StreamAndClearSerFuncStreams(const std::string& rssClassBegin, const std::string& rssClassEnd,
const CGenContext::CKeywordMap& rmapKeywords);
/**
* @brief Does the function need variable streaming?
* @return Returns whether variable streaming is required.
*/
bool NeedsVariableStreaming() const;
private:
bool m_bSerDesByContainer = false; ///< When one of the member declarations requires serialization by a
///< container that declares this definition, this boolean is set.
std::stringstream m_sstreamSerDesCode; ///< Serializer/deserializer code stream.
std::stringstream m_sstreamSizeFuncHdr; ///< Content of the size function header.
std::stringstream m_sstreamSizeFuncFtr; ///< Content of the size function footer.
std::stringstream m_sstreamSizeFunc; ///< Content of the size function.
std::stringstream m_sstreamSerFuncHdr; ///< Content of the serializer function header.
std::stringstream m_sstreamSerFuncFtr; ///< Content of the serializer function footer.
std::stringstream m_sstreamSerFunc; ///< Content of the serializer function.
std::stringstream m_sstreamDesFuncHdr; ///< Content of the deserializer function header.
std::stringstream m_sstreamDesFuncFtr; ///< Content of the deserializer function footer.
std::stringstream m_sstreamDesFunc; ///< Content of the deserializer function.
size_t m_nTempDeclCnt = 0; ///< Temporary variable declaration counter. Used to identify generate
///< unique temporary variables.
std::string m_ssMemberScope; ///< The member scope to use when streaming declarations.
bool m_bSerDesNamespace = false; ///< When set, the serdes namespace is enabled.
bool m_bNotStreamable = false; ///< When set, this definition entity is not streamable directly and
///< should be part of the streaming of any container holding the
///< declaration of this entity.
};
/**
* @brief Serializer/deserializer code generator class.
*/
class CSerdesGenerator : public CDefinitionGeneratorBase<CSerdesContext>
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CSerdesGenerator(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CSerdesGenerator() override;
private:
/**
* @brief Return the information for target file creation. Overload of CDefinitionGeneratorBase::GetTargetFileInfo.
* @param[out] rssTargetSubDir Reference to the string containing the target sub-directory to be added to the output directory.
* Could be empty to target the output directory.
* @param[out] rssTargetFileEnding Reference to string containing the file ending (file name and extension) to be placed at the
* end of the source file name replacing the extension.
*/
virtual void GetTargetFileInfo(std::string& rssTargetSubDir, std::string& rssTargetFileEnding) override;
/**
* @brief Return the file header text for automatic file generation. Overload of CDefinitionGeneratorBase::GetFileHeaderText.
* @return The header text to place into the file.
*/
virtual std::string GetFileHeaderText() const override;
/**
* @brief Stream the code into the file. Called once after processing. Overload of CDefinitionGeneratorBase::StreamIntoFile.
* @param[in, out] rcontext Reference to the stream context.
* @param[in, out] rfstream Reference to the file stream to stream into
*/
virtual void StreamIntoFile(CSerdesContext& rcontext, std::ofstream& rfstream) override;
/**
* @brief Stream the include section for the file. Overload of CDefinitionGeneratorBase::StreamIncludeSection.
* @param[in, out] rcontext Reference to the stream context to stream into.
*/
virtual void StreamIncludeSection(CSerdesContext& rcontext) override;
/**
* @brief Stream the meta entity. Overload of CDefinitionGeneratorBase::StreamMetaEntity.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the interface of the meta entity.
*/
virtual void StreamMetaEntity(CSerdesContext& rcontext, sdv::IInterfaceAccess* pEntity) override;
/**
* @brief Stream declaration if the entity is a declaration. Overload of CDefinitionGeneratorBase::StreamDeclaration.
* @param[in, out] rcontext Reference to the stream context.
* @param[in] pEntity Pointer to the interface of the declaration entity.
* @return Returns true when the streaming was successful or false when streaming was not successful and should be canceled.
*/
virtual bool StreamDeclaration(CSerdesContext& rcontext, sdv::IInterfaceAccess* pEntity) override;
/**
* @brief Stream definition if the entity is a definition. Overload of CDefinitionGeneratorBase::StreamDefinition.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in] bInline When set the definition is part of a declaration.
* @param[in] bAnonymousDecl When set, the definition is part of an anonymous declaration (only valid for unions).
*/
virtual void StreamDefinition(CSerdesContext& rcontext, sdv::IInterfaceAccess* pEntity, bool bInline = false,
bool bAnonymousDecl = false) override;
/**
* @brief Stream definition content (the declaration within the definition).
* @param[in, out] rcontext Reference to the stream context.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
* @param[in] bSuppressComments When set, do not stream the comment.
* @return Returns true when the streaming was successful or false when streaming was not successful and should be canceled.
*/
bool StreamDefinitionContent(CSerdesContext& rcontext, sdv::IInterfaceAccess* pEntity, bool bSuppressComments = false);
/**
* @brief Stream interface definition.
* @param[in, out] rcontext Reference to the stream context.
* @param[in] pEntity Pointer to the IInterfaceAccess pointer of the entity.
*/
void StreamInterface(CSerdesContext& rcontext, sdv::IInterfaceAccess* pEntity);
/**
* @brief Process the union member that, together with the switch variable, has a mutual container from the entity in the
* context. Overload of CDefinitionGeneratorBase::ProcessUnionInContainerContext.
* @param[in, out] rcontext Reference to the stream context to stream into.
* @param[in] rssMemberScopeUnionDecl Reference to the member scope of the union declaration (could be empty when the union
* switch is not variable based).
* @param[in] rssMemberScopeSwitchVar Reference to the member scope of the switch variable (could be empty when the union
* switch is not variable based).
* @param[in] pUnionDef Pointer to the union definition entity.
* @param[in] rvecArrayIndices Reference to the vector containing the array indices of the declaration to use for the detection.
* If the declaration is not declared as array, the vector should be empty. If the declaration is declared as an array and the
* amount of dimensions is below the amount of dimensions within the provided vector, the detection function is called for each
* array dimension extending the vector with the index to do the detection for.
*/
virtual void ProcessUnionInContainerContext(CSerdesContext& rcontext, std::string rssMemberScopeUnionDecl,
std::string rssMemberScopeSwitchVar, sdv::IInterfaceAccess* pUnionDef,
const std::vector<SArrayIterationInfo>& rvecArrayIndices = std::vector<SArrayIterationInfo>()) override;
/**
* @brief For a switch variable, process the joint container of both switch variable and union. Start with the highest parent
* running through all children. Overload of CDefinitionGeneratorBase::ProcessUnionJointContainerForSwitchVar.
* @param[in, out] rcontext Reference to the definition stream context of the switch variable to stream into.
* @param[in] pSwitchVarEntity Interface to the switch var declaration.
* @param[in] pContainerEntity Interface to the container definition.
*/
virtual void ProcessUnionJointContainerForSwitchVar(CSerdesContext& rcontext,
sdv::IInterfaceAccess* pSwitchVarEntity, sdv::IInterfaceAccess* pContainerEntity) override;
std::set<std::string> m_setHistory; ///< Set of all the scoped names that have been processed.
std::set<std::string> m_setNonStreamableDef; ///< Set of non-streamable definition entities. Streaming should take
///< place in the container implementing the declaration.
/// Definition entities that cannot be streamed directly (in case of a direct or indirect declaration of a union entity or a
/// switch variable entity) should be streamed in a container instead. The key in the map represents the definition scoped name.
/// The value contains a list of container entities (scoped names).
std::map<std::string, std::list<std::string>> m_mapInlineDef;
};
#endif // !defined SERDES_GENERATOR_H

View File

@@ -0,0 +1,262 @@
#include "stub_generator.h"
#include "../exception.h"
#include <fstream>
CStubGenerator::CStubGenerator(sdv::IInterfaceAccess* pParser) : CPSClassGeneratorBase(pParser)
{}
CStubGenerator::~CStubGenerator()
{}
std::string CStubGenerator::GetNameAppendix() const
{
return "stub";
}
std::string CStubGenerator::GetClassDefFileComments() const
{
return "This file contains the stub definition for the interfaces.";
}
std::string CStubGenerator::GetClassImplFileComments() const
{
return "This file contains the stub implementation for the interfaces.";
}
std::string CStubGenerator::GetClassDefBegin(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
/**
* @brief Proxy class implementation for the %interface_name%.
*/
class %class_name% : public sdv::ps::CStubHandler<%interface_name%>, public sdv::ps::IStubLink
{
public:
/**
* @brief Constructor
* @param[in] pInterface Pointer to the target interface this proxy has to operate. Must not be NULL.
*/
%class_name%();
/**
* @brief Destructor
*/
virtual ~%class_name%() override = default;
// Object class name
DECLARE_OBJECT_CLASS_NAME("Stub_%interface_id%")
DECLARE_OBJECT_CLASS_ALIAS("Stub_%alias_name%")
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_CHAIN_BASE(sdv::ps::CStubHandler<%interface_name%>)
SDV_INTERFACE_ENTRY(IStubLink)
END_SDV_INTERFACE_MAP()
/**
* @brief Link the object target interface to the stub-object. Overload of IStubLink::Link.
* @remarks Only one link can exists at the time.
* @param[in] pInterface Interface to be linked.
*/
void Link(/*in*/ sdv::interface_t ifc) override;
/**
* @brief Unlink the linked interface. Overload of IStubLink::Unlink.
*/
void Unlink() override;
)code";
}
std::string CStubGenerator::GetClassDefEnd(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
private:
%interface_name%* m_ifc = nullptr; ///< Pointer to the marshalled interface.
};
DEFINE_SDV_OBJECT(%class_name%)
)code";
}
std::string CStubGenerator::GetConstructImplBegin(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
%class_name%::%class_name%()
{
)code";
}
std::string CStubGenerator::GetConstructImplEnd(CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
}
void %class_name%::Link(/*in*/ sdv::interface_t ifc)
{
m_ifc = ifc.template get<%interface_name%>();
assert(m_ifc);
}
void %class_name%::Unlink()
{
m_ifc = nullptr;
}
)code";
}
std::string CStubGenerator::GetConstructFuncImpl(const SFuncInfo& /*rsFunc*/, CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
RegisterDispatchFunc([this](sdv::EEndian eEndian, const sdv::pointer<uint8_t>& rptrInputParams, sdv::pointer<uint8_t>& rptrOutputParams) -> sdv::ps::ECallResult
{
return stub_%func_name%(eEndian, rptrInputParams, rptrOutputParams);
});
)code";
}
std::string CStubGenerator::GetFuncDef(const SFuncInfo& /*rsFunc*/, CKeywordMap& /*rmapKeywords*/) const
{
return R"code(
/** Implementation of stub_%func_name%. */
sdv::ps::ECallResult stub_%func_name%(sdv::EEndian eEndian, const sdv::pointer<uint8_t>& rptrInputParams, sdv::pointer<uint8_t>&rptrOutputParams);
)code";
}
std::string CStubGenerator::GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords, const CExceptionVector& rvecExceptions) const
{
rmapKeywords.insert(std::make_pair("func_return", rsFunc.ssDeclType != "void" ? "return_value = " : ""));
rmapKeywords.insert(std::make_pair("argument_endianess", rsFunc.nInputParamCnt || rsFunc.nOutputParamCnt || rvecExceptions.size() ? " eEndian" : ""));
rmapKeywords.insert(std::make_pair("argument_input_params", rsFunc.nInputParamCnt ? " rptrInputParams" : ""));
rmapKeywords.insert(std::make_pair("argument_output_params", rsFunc.nOutputParamCnt || rvecExceptions.size() ? " rptrOutputParams" : ""));
rmapKeywords.insert(std::make_pair("param_init_comments", rsFunc.nInputParamCnt || rsFunc.nOutputParamCnt ? R"code(
// Initialize parameters
// CppCheck warns about parameter that could be declared as const. This is not wanted here. Suppress the warning.
// cppcheck-suppress constVariablePointer)code" : ""));
std::stringstream sstream;
sstream << R"code(
sdv::ps::ECallResult %class_name%::stub_%func_name%(sdv::EEndian%argument_endianess%, const sdv::pointer<uint8_t>&%argument_input_params%, sdv::pointer<uint8_t>&%argument_output_params%)
{
if (!m_ifc) throw sdv::XNoInterface(); // Error, interface must be assigned.%param_init_comments%%param_init%)code";
if (rsFunc.nInputParamCnt)
sstream << R"code(
// Deserialize parameters
if (eEndian == sdv::EEndian::big_endian)
{
sdv::deserializer<sdv::EEndian::big_endian> desInput;
desInput.attach(rptrInputParams);%stream_param_input%
} else
{
sdv::deserializer<sdv::EEndian::little_endian> desInput;
desInput.attach(rptrInputParams);%stream_param_input%
})code";
// Call function. Add try/catch if exceptions are used.
if (rvecExceptions.size())
{
sstream << R"code(
try
{
// Call the function
%func_return%m_ifc->%func_name%(%param_pack_use%);
})code";
for (const std::string& rssException : rvecExceptions)
{
sstream << " catch (const " << rssException << "& rexcept)";
sstream << R"code(
{
if (eEndian == sdv::EEndian::big_endian)
{
sdv::serializer<sdv::EEndian::big_endian> serOutput;
serOutput << rexcept;
rptrOutputParams = std::move(serOutput.buffer());
return sdv::ps::ECallResult::result_exception;
} else
{
sdv::serializer<sdv::EEndian::little_endian> serOutput;
serOutput << rexcept;
rptrOutputParams = std::move(serOutput.buffer());
return sdv::ps::ECallResult::result_exception;
}
})code";
}
} else
{
sstream << R"code(
// Call the function
%func_return%m_ifc->%func_name%(%param_pack_use%);)code";
}
if (rsFunc.nOutputParamCnt) sstream << R"code(
// Serializer
if (eEndian == sdv::EEndian::big_endian)
{
sdv::serializer<sdv::EEndian::big_endian> serOutput;%stream_param_output%
rptrOutputParams = std::move(serOutput.buffer());
return sdv::ps::ECallResult::result_ok;
} else
{
sdv::serializer<sdv::EEndian::little_endian> serOutput;%stream_param_output%
rptrOutputParams = std::move(serOutput.buffer());
return sdv::ps::ECallResult::result_ok;
})code";
else
sstream << R"code(
return sdv::ps::ECallResult::result_ok;)code";
sstream << R"code(
}
)code";
return sstream.str();
}
std::string CStubGenerator::GetFuncImplParamInit(const SFuncInfo& /*rsFunc*/, const SParamInfo& rsParam, CKeywordMap& /*rmapKeywords*/) const
{
if (!rsParam.bValidType) return {};
// Do not initialize the return value; this will be initialized later.
if (rsParam.ssName == "return_value")
return R"code(
%param_decl_type% %param_name%;)code";
else
return R"code(
%param_decl_type% %param_name% = %param_default_val%;)code";
}
std::string CStubGenerator::GetFuncImplStreamParamInput(const SFuncInfo& /*rsFunc*/, const SParamInfo& rsParam, CKeywordMap& /*rmapKeywords*/) const
{
if (!rsParam.bValidType) return "";
switch (rsParam.eDirection)
{
case SParamInfo::EDirection::in:
case SParamInfo::EDirection::inout:
return R"code(
desInput >> %param_name%;)code";
default:
return "";
}
}
std::string CStubGenerator::GetFuncImplStreamParamOutput(const SFuncInfo& /*rsFunc*/, const SParamInfo& rsParam, CKeywordMap& /*rmapKeywords*/) const
{
if (!rsParam.bValidType) return "";
switch (rsParam.eDirection)
{
case SParamInfo::EDirection::ret:
case SParamInfo::EDirection::out:
case SParamInfo::EDirection::inout:
return R"code(
serOutput << %param_name%;)code";
default:
return "";
}
}
std::string CStubGenerator::GetFuncImplParamTerm(const SFuncInfo& /*rsFunc*/, const SParamInfo& /*rsParam*/, CKeywordMap& /*rmapKeywords*/) const
{
return std::string();
}

View File

@@ -0,0 +1,108 @@
#ifndef STUB_GTENERATOR_H
#define STUB_GTENERATOR_H
#include "ps_class_generator_base.h"
/**
* @brief Stub generator class.
*/
class CStubGenerator : public CPSClassGeneratorBase
{
public:
/**
* @brief Constructor
* @param[in] pParser Pointer to the parser object.
*/
CStubGenerator(sdv::IInterfaceAccess* pParser);
/**
* @brief Destructor
*/
virtual ~CStubGenerator() override;
private:
/**
* @brief Return the name addition to be added to the filename and class definition. Overload of
* CPSClassGeneratorBase::GetNameAppendix.
*/
virtual std::string GetNameAppendix() const override;
/**
* @brief Get definition file comments to be written in the file header. Overload of
* CPSClassGeneratorBase::GetClassDefFileComments.
*/
virtual std::string GetClassDefFileComments() const override;
/**
* @brief Get implementation file comments to be written in the file header. Overload of
* CPSClassGeneratorBase::GetClassImplFileComments.
*/
virtual std::string GetClassImplFileComments() const override;
/**
* @brief Get begin of class definition to be inserted into the header file. Overload of
* CPSClassGeneratorBase::GetClassDefBegin.
*/
virtual std::string GetClassDefBegin(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get end of class definition to be inserted into the header file. Overload of
* CPSClassGeneratorBase::GetClassDefEnd.
*/
virtual std::string GetClassDefEnd(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get begin of constructor implementation to be inserted into the cpp file. Overload of
* CPSClassGeneratorBase::GetConstructImplBegin.
*/
virtual std::string GetConstructImplBegin(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get end of constructor implementation to be inserted into the cpp file. Overload of
* CPSClassGeneratorBase::GetConstructImplEnd.
*/
virtual std::string GetConstructImplEnd(CKeywordMap& rmapKeywords) const override;
/**
* @brief Get the constructor body for a function (attribute or operation). Overload of
* CPSClassGeneratorBase::GetConstructFuncImpl.
*/
virtual std::string GetConstructFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get the function definition (attribute or operation). Overload of CPSClassGeneratorBase::GetFuncDef.
*/
virtual std::string GetFuncDef(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImpl.
*/
virtual std::string GetFuncImpl(const SFuncInfo& rsFunc, CKeywordMap& rmapKeywords, const CExceptionVector& rvecExceptions) const override;
/**
* @brief Get parameter initialization of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplParamInit.
*/
virtual std::string GetFuncImplParamInit(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get input parameter streaming of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplStreamParamInput.
*/
virtual std::string GetFuncImplStreamParamInput(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get output parameter streaming of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplStreamParamOutput.
*/
virtual std::string GetFuncImplStreamParamOutput(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
/**
* @brief Get parameter termination of the unpack portion of the function implementation (attribute or operation). Overload of
* CPSClassGeneratorBase::GetFuncImplParamTerm.
*/
virtual std::string GetFuncImplParamTerm(const SFuncInfo& rsFunc, const SParamInfo& rsParam, CKeywordMap& rmapKeywords) const override;
};
#endif // !defined(STUB_GTENERATOR_H)

View File

@@ -0,0 +1,17 @@
#ifndef INCLUDES_IDL_COMPILER_H
#define INCLUDES_IDL_COMPILER_H
// Comment the following file to use the backup file. Uncomment to use the IDL file. Both files should contain identical
// definitions and declarations.
//#include <interfaces/core_idl.h>
// REMARKS: Using the "core_idl.h" works once; after that CMake detects circular dependencies and doesn't want to build any
// more. Therefore, it is advisable to use it only for checking the interfaces.
// To re-allow building again after a circular reference, clear the CMake cache (in the project-menu item).
#ifndef __IDL_GENERATED__CORE_IDL_DEF_H__
// BACKUP MODE - IN CASE "core_idl.h" is destroyed or doesn't exist
#include "core_idl_backup.h"
#endif
#endif // !defined(INCLUDES_IDL_COMPILER_H)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,269 @@
#ifndef LEXER_H
#define LEXER_H
#include "lexerbase.h"
#include "token.h"
#include "tokenlist.h"
#include "codepos.h"
#include <cinttypes>
#include <exception>
#include <string>
#include <cassert>
/**
* @brief OMG IDL 4.2 keywords
* @remarks Dependable on the extension settings, the keywords are extended with: char16, char32, u8string, u16string, u32string,
* null, pointer, interface_id, interface_t and exception_id.
*/
const std::vector<std::string> g_vecOmgIdlKeywords = {
"abstract", "any", "alias", "attribute", "bitfield", "bitmask", "bitset", "boolean", "case", "char", "component", "connector",
"const", "consumes", "context", "custom", "default", "double", "exception", "emits", "enum", "eventtype", "factory", "finder",
"fixed", "float", "getraises", "home", "import", "in", "inout", "interface", "local", "long", "manages", "map", "mirrorport",
"module", "multiple", "native", "Object", "octet", "oneway", "out", "primarykey", "private", "port", "porttype", "provides",
"public", "publishes", "raises", "readonly", "setraises", "sequence", "short", "string", "struct", "supports", "switch",
"truncatable", "typedef", "typeid", "typename", "typeprefix", "unsigned", "union", "uses", "ValueBase", "valuetype", "void",
"wchar", "wstring", "int8", "uint8", "int16", "int32", "int64", "uint16", "uint32", "uint64"
};
/**
* @brief SDV IDL lexer class
*/
class CLexer
{
public:
/**
* @brief Lexing mode enumerator.
*/
enum class ELexingMode
{
lexing_idl, ///< Lexing IDL code.
lexing_preproc, ///< Lexing preprocessor directives (valid for the current line only).
};
/**
* @brief Constructor
* @param[in] pCallback Pointer to the lexer callback interface. Must not be NULL.
* @param[in] bCaseSensitive When set, allow identical names that only differ in case.
* @param[in] eLexingMode The lexing mode the lexer should run in. This determines the rule-set to use while lexing.
*/
CLexer(ILexerCallback* pCallback, bool bCaseSensitive, ELexingMode eLexingMode = ELexingMode::lexing_idl);
/**
* @brief Destructor
*/
virtual ~CLexer() = default;
/**
* Add keywords to the reserved keyword list (based on the enabled extension).
* @param[in] rssKeyword Reference to the keyword string to add.
*/
void AddKeyword(const std::string& rssKeyword);
/**
* @brief Get a token from the code.
* @param[in, out] rCode Reference to the code to be parsed.
* @param[in] rptrContext Reference to the smart pointer to the source code context.
* @throws Throws CCompileException on parse error.
* @remarks Whitespace, comments and preprocessor directions are not provided as a result, but are provided through the
* callback interface.
* @post Updates the position to the token following the token.
* @return Returns the read token or an empty token when OEF has been reached.
*/
CToken GetToken(CCodePos& rCode, const CContextPtr& rptrContext) const;
/**
* @brief Get the last valid token.
* @return Returns a reference to the member variable containing the last read token or an empty token when no token was read
* before.
*/
const CToken& GetLastValidToken() const;
/**
* @brief Read until the provided symbol or the end of the text.
* @param[in, out] rCode Reference to the code to be parsed.
* @param[in] cSymbol The symbol to mark the end.
* @post Updates the position to the token of the symbol.
* @return Returns the token until the symbol or end of text has been reached.
*/
CToken GetCustom(CCodePos& rCode, char cSymbol) const;
/**
* @brief Skip the rest of the line.
* @remarks End position is at the carriage return or newline for preprocessing directives and past the carriage return or
* newline for code.
* @remarks Sets the newline-occurred flag for code.
* @remarks Back-slash at the end of the line causes the inclusion of the next line.
* @param[in, out] rCode Reference to the code to be parsed.
*/
void SkipLine(CCodePos& rCode) const;
/**
* @brief Enable preproc processing.
* @details While parsing preprocessor directive, further preprocessor processing is disabled. If including an additional
* file, preprocessing needs to be enabled again.
*/
void EnablePreprocProcessing();
private:
/**
* @brief Get whitespace (space, form feed, line feed, carriage return, horizontal tab, vertical tab) until there is no white
* space any more.
* @param[in, out] rCode Reference to the code to be parsed.
* @param[out] rbNewline Set when a newline has been passed.
* @throws Throws CCompileException on parse error.
* @post Updates the position to the token following the whitespace.
*/
CToken GetWhitespace(CCodePos& rCode, bool& rbNewline) const;
/**
* @brief Get C-style and C++ comments.
* @param[in, out] rCode Reference to the code to be parsed.
* @throws Throws CCompileException on parse error.
* @post Updates the position to the token following the comment.
*/
CToken GetComments(CCodePos& rCode) const;
/**
* @brief Get the identifier or keyword.
* @details Get the identifier or keyword. A keyword corresponds to the list of keywords defined by the OMG IDL. All others
* are identifiers. Identifiers that differ from a keyword through case differences are illegal.
* @param[in, out] rCode Reference to the code to be parsed.
* @throws Throws CCompileException on parse error.
* @post Updates the position to the token following the identifier.
* @return Returns the identifier token or an empty token when OEF has been reached.
*/
CToken GetIdentifierOrKeyword(CCodePos& rCode) const;
/**
* @brief Get the separator: { } ( ) : :: ; .
* @param[in, out] rCode Reference to the code to be parsed.
* @post Updates the position to the token following the separator.
* @return Returns the separator token or an empty token when no separator is at the requested token.
*/
CToken GetSeparator(CCodePos& rCode) const;
/**
* @brief Get the operator: = + - * / % ^ ! ~ , | # || & && == != < <= > >= ?
* @remarks The '#' symbol is only an operator when lexing preprocessor directives. This is covered by the GetToken function.
* @param[in, out] rCode Reference to the code to be parsed.
* @post Updates the position to the token following the operator.
* @return Returns the operator token or an empty token when no operator is at the requested token.
*/
CToken GetOperator(CCodePos& rCode) const;
/**
* @brief Get the literal: hex, decimal or octal number, floating/fixed point number, character, strings, boolean and nullptr
* values.
* @remarks The following extension on the OMG IDL specification are possible:
* - binary integer literal as is part of the C++ 14 standard.
* - integer suffixes as are part of the C++ standard.
* - unicode UTF-16 and UTF-32 character literals as part of the C++ 11 standard.
* - ASCII and wide string sequences (2, 4 or 8 characters) as part oft the C++ standard.
* - Unicode UTF-8, UTF-16, UTF-32 and wide string literals as part of the C++ 11 standard.
* - raw ASCII, UTF-8, UTF-16, UTF-32 and wide string literals as is part of the C++ 11 standard.
* - character and string escape sequence using 8 digits to specify a Unicode character as is part of the C++ standard.
* - hexadecimal floating points as are part of the C++ 17 standard.
* - floating point suffixes as are part of the C++ standard.
* - boolean literal as part of the C++ standard (the values 'true' and 'false' as well as 'TRUE' and 'FALSE').
* - nullptr literal as part of the C++ 11 standard (the values 'nullptr' and 'NULL').
* @param[in, out] rCode Reference to the code to be parsed.
* @throws Throws CCompileException on parse error.
* @post Updates the position to the token following the literal.
* @return Returns the literal token or an empty token when OEF has been reached.
*/
CToken GetLiteral(CCodePos& rCode) const;
ILexerCallback* m_pLexerCallback = nullptr; ///< The lexer callback for inserting comments
bool m_bCaseSensitive = true; ///< Case sensitivity during name comparison.
ELexingMode m_eLexingMode = ELexingMode::lexing_idl; ///< Lexing mode (changes the lexing rules).
mutable bool m_bNewlineOccurred = true; ///< A newline occurred - a preprocessor directive could take place.
mutable CToken m_tokenLastValid; ///< The last valid token.
std::vector<std::string> m_vecReservedKeywords; ///< List of reserved keywords.
};
/**
* @brief Tokenize a string into a token list.
* @param[in] szCode The code to tokenize.
* @param[in] rptrContext Reference to the smart pointer to the source code context.
* @return The tokenized string.
*/
CTokenList Tokenize(const char* szCode, const CContextPtr& rptrContext);
/**
* @brief Tokenize a string into a token list.
* @param[in] rssCode The code to tokenize.
* @param[in] rptrContext Reference to the smart pointer to the source code context.
* @return The tokenized string.
*/
CTokenList Tokenize(const std::string& rssCode, const CContextPtr& rptrContext);
/**
* @brief Dummy callback interface for the lexer ignoring all calls.
*/
struct SLexerDummyCallback : public ILexerCallback
{
/**
* @brief Insert whitespace. Dummy implementation. Overload of ILexerCallback::InsertWhitespace.
*/
virtual void InsertWhitespace(const CToken& /*rtoken*/) override {}
/**
* @brief Insert a comment. Dummy implementation. Overload of ILexerCallback::InsertComment.
*/
virtual void InsertComment(const CToken& /*rtoken*/) override {}
/**
* @brief Process a preprocessor directive. Overload of ILexerCallback::ProcessPreprocDirective.
* @param[in] rCode Reference to the source code to process the preproc directive for.
*/
virtual void ProcessPreprocDirective(CCodePos& rCode) override
{ SLexerDummyCallback sCallback; CLexer(&sCallback, true).SkipLine(rCode); }
};
/**
* @brief Callback interface for the lexer storing the provided information.
*/
struct SLexerStoreCallback : public ILexerCallback
{
/**
* @brief Clear the value of the callback structure.
*/
void Clear()
{
tokenWhitespace = CToken();
tokenComment = CToken();
ssPreprocLine.clear();
}
/**
* @brief Insert whitespace. Overload of ILexerCallback::InsertWhitespace.
* @param[in] rtoken Reference to token containing the whitespace.
*/
virtual void InsertWhitespace(const CToken &rtoken) override { tokenWhitespace = rtoken; }
/**
* @brief Insert a comment. Overload of ILexerCallback::InsertComment.
* @param[in] rtoken Reference to the token containing the comment.
*/
virtual void InsertComment(const CToken &rtoken) override { tokenComment = rtoken; }
/**
* @brief Process a preprocessor directive. Overload of ILexerCallback::ProcessPreprocDirective.
* @param[in] rCode Reference to the source code to process the preproc directive for.
*/
virtual void ProcessPreprocDirective(CCodePos &rCode) override
{
SLexerDummyCallback sCallback;
CLexer lexer(&sCallback, true);
CToken token = rCode.GetLocation();
lexer.SkipLine(rCode);
rCode.UpdateLocation(token);
ssPreprocLine = static_cast<std::string>(token);
}
CToken tokenWhitespace; ///< Token holding whitespace.
CToken tokenComment; ///< Token holding comment.
std::string ssPreprocLine; ///< String holding preprocessing line.
};
#endif // !defined LEXER_H

View File

@@ -0,0 +1,89 @@
#ifndef LEXERBASE_H
#define LEXERBASE_H
/**
* @brief Token type
*/
enum class ETokenType
{
token_none, ///< No token
token_whitespace, ///< Token is whitespace
token_comments, ///< Token is C-style or C++ comments
token_identifier, ///< Token is an identifier
token_keyword, ///< Token is a keyword
token_literal, ///< Token is a literal
token_separator, ///< Token is a separator
token_operator, ///< Token is a operator
token_meta, ///< Token contains meta data
};
/**
* @brief Token literal type (valid when token is a literal token).
*/
enum class ETokenLiteralType
{
token_undefined, ///< No literal type defined
token_literal_dec_integer, ///< Decimal integer literal
token_literal_oct_integer, ///< Octal integer literal
token_literal_hex_integer, ///< IsHexadecimal() integer literal
token_literal_bin_integer, ///< Binary integer literal
token_literal_dec_floating_point, ///< Decimal floating point literal
token_literal_hex_floating_point, ///< IsHexadecimal() floating point literal
token_literal_fixed_point, ///< Fixed point literal
token_literal_string, ///< String literal (ASCII ISO Latin-1 (8859-1) character set, UTF-8, UTF-16,
///< UTF-32, wide - platform specific)
token_literal_raw_string, ///< Raw string literal (ASCII ISO Latin-1 (8859-1) character set, UTF-8, UTF-16,
///< UTF-32, wide - platform specific)
token_literal_character, ///< Character literal (ASCII ISO Latin-1 (8859-1) character set, UTF-16, UTF-32,
///< wide - platform specific)
token_literal_character_sequence, ///< Character sequence literal (ASCII or wide - both platform cpecific)
token_literal_boolean, ///< Boolean literal
token_literal_nullptr, ///< Nullptr literal
};
/**
* @brief Token meta type (valid when token is a metza token).
*/
enum class ETokenMetaType
{
token_undefined, ///< No meta type defined
token_meta_include_local, ///< Local include file
token_meta_include_global, ///< Global include file
token_meta_define, ///< Definition
token_meta_undef, ///< Remove definition
token_meta_verbatim, ///< Verbatim text to be inserted by the generator
};
// Forward declarations
class CToken;
class CCodePos;
/**
* @brief Lexer callback interface
*/
struct ILexerCallback
{
/**
* @brief Insert whitespace - used to preserve whitespace.
* @param[in] rtoken Reference to the token structure pointing to the whitespace.
*/
virtual void InsertWhitespace(const CToken &rtoken) = 0;
/**
* @brief Insert a comment, either standalone, belonging to the last statement or belonging to the next statement.
* @param[in] rtoken Reference to the token structure pointing to the comment.
*/
virtual void InsertComment(const CToken& rtoken) = 0;
/**
* @brief Process a preprocessor directive. Preprocessor directives occur at the beginning of a line and can be preceded by
* whitespace. The directive starts with the '#' character.
* @param[in] rCode Reference to the current code position starting the directive.
*/
virtual void ProcessPreprocDirective(CCodePos& rCode) = 0;
};
#endif // !defined LEXERBASE_H

View File

@@ -0,0 +1,109 @@
#include "logger.h"
#ifdef _MSC_VER
#pragma push_macro("interface")
#undef interface
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#pragma execution_character_set( "utf-8" )
#ifdef GetClassInfo
#undef GetClassInfo
#endif
#pragma pop_macro("interface")
#endif
CLogControl g_log_control;
CLogControl::CLogControl()
{
#ifdef _MSC_VER
SetConsoleOutputCP( 65001 );
#endif
}
void CLogControl::SetVerbosityMode(EVerbosityMode eMode)
{
m_eVerbosityMode = eMode;
}
EVerbosityMode CLogControl::GetVerbosityMode() const
{
return m_eVerbosityMode;
}
void CLogControl::IncreaseIndent()
{
m_iIndent++;
}
void CLogControl::DecreaseIndent()
{
m_iIndent--;
}
void CLogControl::Log(const std::string& rssText, bool bError) const
{
// Reporting is determined by the verbosity mode.
switch (m_eVerbosityMode)
{
case EVerbosityMode::report_none:
// No reporting wanted
return;
case EVerbosityMode::report_errors:
// Only report errors
if (!bError) return;
break;
case EVerbosityMode::report_all:
default:
break;
}
// Create indentation string
std::string ssIndent;
for (int32_t iCnt = 0; !bError && iCnt < m_iIndent; iCnt++)
ssIndent += " ";
// Replace all the newlines by the newline + indentation
size_t nPos = 0;
while (nPos < rssText.size() && nPos != std::string::npos)
{
// Find the next newline
size_t nStart = nPos;
nPos = rssText.find('\n');
// Log indentation?
if (m_bNewline) std::clog << ssIndent;
m_bNewline = false;
// Newline not found, log the text
if (nPos == std::string::npos)
{
std::clog << rssText.substr(nStart);
break;
}
// Include the newline
nPos++;
m_bNewline = true;
// Log the text
std::clog << rssText.substr(nStart, nPos - nStart);
}
}
CLog::~CLog()
{
if (!m_ssTask.empty())
*this << "Leaving: " << m_ssTask << std::endl;
g_log_control.DecreaseIndent();
}
int CLog::CLogStringBuf::sync()
{
g_log_control.Log(str());
str(std::string());
return 0;
}

View File

@@ -0,0 +1,141 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <iostream>
#include <sstream>
#include <cstdint>
enum class EVerbosityMode
{
report_none = -1, ///< Do not report anything.
report_errors = 0, ///< Report errors only (default).
report_all = 10, ///< Report all intermediate stepts.
};
/**
* @brief Log control class used to control the log data.
* @remarks Logging is done to std::clog ostream.
*/
class CLogControl
{
public:
/**
* @brief Constructor
*/
CLogControl();
/**
* @brief Set verbosity mode.
* @param[in] eMode The verbosity mode to set.
*/
void SetVerbosityMode(EVerbosityMode eMode);
/**
* @brief Get verbosity mode.
* @return Returns the current verbosity mode.
*/
EVerbosityMode GetVerbosityMode() const;
/**
* @{
* @brief Increase and decrease indentation.
*/
void IncreaseIndent();
void DecreaseIndent();
/**
* @}
*/
/**
* @brief Report information; indentation will be inserted automatically after a newline.
* @remarks Will only be shown when the verbosity mode is on report_all. Errors will be shown when the verbosity mode is on
* report_all and report_errors.
* @param[in] rssText Reference to the string to log.
* @param[in] bError When set, the text indicates an error.
*/
void Log(const std::string& rssText, bool bError = false) const;
private:
int32_t m_iIndent = -1; ///< Indentation after newline
mutable bool m_bNewline = true; ///< When set, start inserting an indentation
EVerbosityMode m_eVerbosityMode = EVerbosityMode::report_errors; ///< The level of verbosity while reporting
};
/**
* @brief One global instance of log control.
*/
extern CLogControl g_log_control;
/**
* @brief Logger class responsible for reporting the progress and errors. The logger class derives from std::ostream to provide
* stream access reporting.
*/
class CLog : public std::ostream
{
public:
/**
* @brief Constructor forming a test string by streaming.
* @tparam TArgs Argument types within the parameter pack.
* @param[in] tArgs Arguments forming the task description.
*/
template <typename... TArgs>
CLog(TArgs... tArgs);
/**
* @brief Destructor
*/
~CLog();
private:
/**
* @brief Compose a string without any arguments; doing nothing... ending the unpacking of the parameter pack of the variadic
* template.
* @param[in, out] rss Reference to the string stream to fill.
*/
static void ComposeString([[maybe_unused]] std::stringstream& rss) {}
/**
* @brief Compose a string with one or more arguments.
* @tparam TArg Type of the first argument.
* @tparam TArgs Types of additional arguments
* @param[in, out] rss Reference to the string stream to fill.
* @param[in] tArg First argument.
* @param[in] tAdditionalArgs Optional additional arguments as parameter pack.
*/
template <typename TArg, typename... TArgs>
static void ComposeString(std::stringstream& rss, TArg tArg, TArgs... tAdditionalArgs)
{
rss << tArg;
ComposeString(rss, tAdditionalArgs...);
}
/**
* @brief Logger string buf implementing dedicated streaming to the log using std::ostream.
*/
class CLogStringBuf : public std::stringbuf
{
protected:
/**
* @brief Synchronize the controlled character sequence. Overload of std::stringbuf::sync.
* @return int Returns 0 on success; -1 otherwise.
*/
virtual int sync() override;
};
std::string m_ssTask; ///< Task description
CLogStringBuf m_buffer; ///< The logger specific string buffer.
};
template <typename... TArgs>
inline CLog::CLog(TArgs... tArgs) : std::ostream(&m_buffer)
{
std::stringstream sstreamTaskDescr;
ComposeString(sstreamTaskDescr, tArgs...);
m_ssTask = sstreamTaskDescr.str();
g_log_control.IncreaseIndent();
if (!m_ssTask.empty())
*this << "Entering: " << m_ssTask << std::endl;
}
#endif // !defined(LOGGER_H)

View File

@@ -0,0 +1,342 @@
#include "macro.h"
#include "exception.h"
#include "lexer.h"
#include "environment.h"
#include <algorithm>
/**
* @brief Callback class for the lexer allowing to insert the whitespace and comments into the provided stream.
*/
class CMacroResolveCallback : public ILexerCallback
{
public:
/**
* @brief Constructor providing a reference of the resolved string to use for concatenating comments and whitepace.
* @param[in] rssName Reference to the macro name.
* @param[in] rssTargetValue Reference to the value.
* @param[in] rbSuppressWhitespace Reference to a boolean stating whether to suppress whitespace.
*/
CMacroResolveCallback(const std::string& rssName, std::string& rssTargetValue, const bool& rbSuppressWhitespace) :
m_ssName(rssName), m_rssTargetValue(rssTargetValue), m_rbSuppressWhitespace(rbSuppressWhitespace)
{}
/**
* @brief Insert whitespace. Overload of ILexerCallback::InsertWhitespace.
*/
virtual void InsertWhitespace(const CToken&) override
{
// Multiple spaces are reduced to one space.
if (!m_rbSuppressWhitespace)
m_rssTargetValue += ' ';
}
/**
* @brief Insert a comment. Overload of ILexerCallback::InsertComment.
*/
virtual void InsertComment(const CToken&) override
{
// Comments are ignored
}
/**
* @brief Process a preprocessor directive. Overload of ILexerCallback::ProcessPreprocDirective.
*/
virtual void ProcessPreprocDirective(CCodePos& /*rCode*/) override
{
// Should not be called.
throw CCompileException("Internal error trying to resolve the ", m_ssName, " macro.");
}
private:
std::string m_ssName; //!< Macro name.
std::string& m_rssTargetValue; //!< Reference to the string to concatenate the comments and whitespace onto.
const bool& m_rbSuppressWhitespace; //!< Reference to a boolean stating whether to prevent concatination of whitespace.
};
CMacro::CMacro(const char* szName, const std::vector<std::string>* pvecParams, const char* szValue) :
m_ssName(szName),
m_bExpectParams(pvecParams ? true : false),
m_vecParamDefs(pvecParams ? *pvecParams : std::vector<std::string>()),
m_ssValue(szValue ? szValue : "")
{
// Remove any whitespace from the end of the value.
while (m_ssValue.size() && std::isspace(m_ssValue.back()))
m_ssValue.erase(m_ssValue.size() - 1);
// Remove any whitespace from the beginning of the value.
while (m_ssValue.size() && std::isspace(m_ssValue.front()))
m_ssValue.erase(0, 1);
}
CMacro::CMacro(const CMacro& rMacro) :
m_ssName(rMacro.m_ssName),
m_bExpectParams(rMacro.m_bExpectParams),
m_vecParamDefs(rMacro.m_vecParamDefs),
m_ssValue(rMacro.m_ssValue)
{}
CMacro::CMacro(CMacro&& rMacro) noexcept :
m_ssName(std::move(rMacro.m_ssName)),
m_bExpectParams(rMacro.m_bExpectParams),
m_vecParamDefs(std::move(rMacro.m_vecParamDefs)),
m_ssValue(std::move(rMacro.m_ssValue))
{
rMacro.m_bExpectParams = false;
}
CMacro& CMacro::operator=(const CMacro& rMacro)
{
m_ssName = rMacro.m_ssName;
m_bExpectParams = rMacro.m_bExpectParams;
m_vecParamDefs = rMacro.m_vecParamDefs;
m_ssValue = rMacro.m_ssValue;
return *this;
}
CMacro& CMacro::operator=(CMacro&& rMacro) noexcept
{
m_ssName = std::move(rMacro.m_ssName);
m_bExpectParams = rMacro.m_bExpectParams;
m_vecParamDefs = std::move(rMacro.m_vecParamDefs);
m_ssValue = std::move(rMacro.m_ssValue);
rMacro.m_bExpectParams = false;
return *this;
}
bool CMacro::operator==(const CMacro& rMacro)
{
if (m_bExpectParams != rMacro.m_bExpectParams) return false;
if (m_ssName != rMacro.m_ssName) return false;
if (m_bExpectParams && m_vecParamDefs != rMacro.m_vecParamDefs) return false;
if (m_ssValue != rMacro.m_ssValue) return false;
return true;
}
bool CMacro::operator!=(const CMacro& rMacro)
{
return !operator==(rMacro);
}
const std::string& CMacro::GetName() const
{
return m_ssName;
}
bool CMacro::ExpectParameters() const
{
return m_bExpectParams;
}
std::string CMacro::Expand(const CIdlCompilerEnvironment& renv, const CToken& rtoken, const std::vector<std::string>& rvecParams,
CUsedMacroSet& rsetUsedMacros) const
{
// Parameters in function like macros are expanded before being inserted into the value if not part of a stringification
// operation.
// Commments are ignored in a macro value.
// Multiple spaces in the value are reduced to one space.
// The result of the macro is expanded before being returned.
// Circular references to macros within the expanded code are prevented for
// - each paramerer separately with the used macro list provided by the function.
// - macro expansion result with the used macros from all the parameters and the used macro list provided to the function.
// Macros used in the expansion of the paramters and the results are added to the used macro set provided to this function.
// Check whether the amount of provided params corresponds to the amount of param definitions.
if (rvecParams.size() < m_vecParamDefs.size())
throw CCompileException(rtoken, "Missing parameters while calling macro");
if (rvecParams.size() > m_vecParamDefs.size())
throw CCompileException(rtoken, "Provided too many parameters while calling macro");
std::string ssTargetValue;
bool bConcatenateNext = false;
bool bStringificateNext = false;
CMacroResolveCallback callback(m_ssName, ssTargetValue, bConcatenateNext);
CLexer lexer(&callback, renv.CaseSensitiveTypeExtension(), CLexer::ELexingMode::lexing_preproc);
CCodePos codeValue(m_ssValue.c_str());
// The set with used macros for all parameters should stay the same, wheresas the provided set should be extended.
CUsedMacroSet setUsedMacrosStored = rsetUsedMacros;
// Stringificate the supplied string.
auto fnStringificate = [](const std::string &rss)
{
std::string ssTarget;
ssTarget += '\"';
for (char cVal : rss)
{
switch (cVal)
{
case '\"':
ssTarget += "\\\"";
break;
case '\'':
ssTarget += "\\\'";
break;
case '\a':
ssTarget += "\\a";
break;
case '\b':
ssTarget += "\\b";
break;
case '\f':
ssTarget += "\\f";
break;
case '\n':
ssTarget += "\\n";
break;
case '\r':
ssTarget += "\\r";
break;
case '\t':
ssTarget += "\\t";
break;
case '\v':
ssTarget += "\\v";
break;
default:
ssTarget += cVal;
break;
}
}
ssTarget += '\"';
return ssTarget;
};
// Expand the supplied code
auto fnExpand = [&](const char* szCode) -> std::string
{
// Expand the parameter before inserting into the value.
std::string ssTarget;
bool bConcatenateDummy = false;
CMacroResolveCallback callbackParam(m_ssName, ssTarget, bConcatenateDummy);
CLexer lexerLocal(&callbackParam, renv.CaseSensitiveTypeExtension(), CLexer::ELexingMode::lexing_preproc);
CCodePos code(szCode);
// Create a copy of the set of used macros to provide this to the parameter expansion. These are the stored
// original used macros.
CUsedMacroSet setUsedMacrosParam = setUsedMacrosStored;
// Expand the parameter
while (!code.HasEOF())
{
// If the current position is not part of the macro expansion, reset the set of macros used in a expansion.
bool bInMacroExpansion = code.CurrentPositionInMacroExpansion();
// In case the code is not part of the macro expansion, use the stored set of used macros provided to this function.
// Otherwise the set still contains the macros used in the expansion.
if (!bInMacroExpansion)
setUsedMacrosParam = setUsedMacrosStored;
// Get a token.
CToken token = lexerLocal.GetToken(code, rtoken.GetContext());
// Check whether the token is an identifier, if so, check for any macro
if (token.GetType() == ETokenType::token_identifier)
{
// Test and expand the
if (renv.TestAndExpand(static_cast<std::string>(token).c_str(), code, bInMacroExpansion, setUsedMacrosParam))
{
// Add all the used macros of the parameter expansion to the set of used macros provided to this function.
for (const std::string& rssMacro : setUsedMacrosStored)
rsetUsedMacros.insert(rssMacro);
continue; // macro was replaced, get a token again.
}
}
ssTarget += static_cast<std::string>(token);
}
return ssTarget;
};
// Parse through the value and deal with parameter names, with stringification operators '#' and concatenating operators '##'.
while (!codeValue.HasEOF())
{
CToken token = lexer.GetToken(codeValue, rtoken.GetContext());
// Check for concatinating token
if (token == "##")
{
// Concatination and stringification is not allowed to be selected already before.
if (bConcatenateNext)
throw CCompileException(rtoken, "Double concatinating operator while resolving macro ", m_ssName);
if (bStringificateNext)
throw CCompileException(rtoken, "Cannot stringificate and then concatenate the result while resolving macro ",
m_ssName);
// Remove whitespace from end of the target value
std::string::reverse_iterator itEnd = std::find_if(ssTargetValue.rbegin(), ssTargetValue.rend(),
[](char c)
{ return !std::isspace<char>(c, std::locale::classic()); });
ssTargetValue.erase(itEnd.base(), ssTargetValue.end());
// Set the concatination flag (this will also prevent concatinating whitespace to the target).
bConcatenateNext = true;
// Next processing
continue;
}
// Check for stringification token
if (token == "#")
{
// Multiple stringification tokens following each other is not allowed.
if (bStringificateNext)
throw CCompileException(rtoken, "Double stringification operator while resolving macro ", m_ssName);
// Set the stringification flag and enable concatination to prevent whitespace to be inserted.
bStringificateNext = true;
bConcatenateNext = true;
// Next processing
continue;
}
// Check whether the token represents a parameter name
if (token.GetType() == ETokenType::token_identifier)
{
// Check if the token corresponds to one of the parameters.
size_t nParamPos = 0;
for (; nParamPos < m_vecParamDefs.size(); nParamPos++)
if (m_vecParamDefs[nParamPos] == static_cast<std::string>(token))
break;
if (nParamPos < m_vecParamDefs.size())
{
// Add the parameter content to the target. Stringitificate if needed.
if (bStringificateNext)
ssTargetValue += fnStringificate(rvecParams[nParamPos]);
else
ssTargetValue += fnExpand(rvecParams[nParamPos].c_str());
} else
{
// Stringification is not allowed when not using a parameter.
if (bStringificateNext)
throw CCompileException(rtoken, "Cannot stringificate while resolving macro ", m_ssName);
// Add the token to the target
ssTargetValue += static_cast<std::string>(token);
}
} else
{
// Stringification is not allowed when not using a parameter.
if (bConcatenateNext)
throw CCompileException(rtoken, "Cannot stringificate while resolving macro ", m_ssName);
// Add the token to the target
ssTargetValue += static_cast<std::string>(token);
}
// Stringification and concatenating finalized.
bConcatenateNext = false;
bStringificateNext = false;
}
// The provided set of used macros now contain all the used macros by the parent, this macro and the macros of any of the
// parameters. Use this set for the expansion of the result - this will prevent circular calling of macros already used
// in parameters.
setUsedMacrosStored = rsetUsedMacros;
// Return the expanded the macro result. This will automatically add any macros expanded in the result to the used macro set.
return fnExpand(ssTargetValue.c_str());
}

Some files were not shown because too many files have changed in this diff Show More