mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-18 10:38:16 +00:00
69
sdv_executables/sdv_idl_compiler/CMakeLists.txt
Normal file
69
sdv_executables/sdv_idl_compiler/CMakeLists.txt
Normal 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)
|
||||
314
sdv_executables/sdv_idl_compiler/codepos.cpp
Normal file
314
sdv_executables/sdv_idl_compiler/codepos.cpp
Normal 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;
|
||||
}
|
||||
200
sdv_executables/sdv_idl_compiler/codepos.h
Normal file
200
sdv_executables/sdv_idl_compiler/codepos.h
Normal 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
|
||||
439
sdv_executables/sdv_idl_compiler/constvariant.cpp
Normal file
439
sdv_executables/sdv_idl_compiler/constvariant.cpp
Normal 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;
|
||||
});
|
||||
}
|
||||
550
sdv_executables/sdv_idl_compiler/constvariant.h
Normal file
550
sdv_executables/sdv_idl_compiler/constvariant.h
Normal 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)
|
||||
343
sdv_executables/sdv_idl_compiler/constvariant.inl
Normal file
343
sdv_executables/sdv_idl_compiler/constvariant.inl
Normal 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)
|
||||
1458
sdv_executables/sdv_idl_compiler/core_idl_backup.h
Normal file
1458
sdv_executables/sdv_idl_compiler/core_idl_backup.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
99
sdv_executables/sdv_idl_compiler/entities/attribute_entity.h
Normal file
99
sdv_executables/sdv_idl_compiler/entities/attribute_entity.h
Normal 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)
|
||||
749
sdv_executables/sdv_idl_compiler/entities/declaration_entity.cpp
Normal file
749
sdv_executables/sdv_idl_compiler/entities/declaration_entity.cpp
Normal 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";
|
||||
}
|
||||
|
||||
372
sdv_executables/sdv_idl_compiler/entities/declaration_entity.h
Normal file
372
sdv_executables/sdv_idl_compiler/entities/declaration_entity.h
Normal 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 <name>.
|
||||
* @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)
|
||||
765
sdv_executables/sdv_idl_compiler/entities/definition_entity.cpp
Normal file
765
sdv_executables/sdv_idl_compiler/entities/definition_entity.cpp
Normal 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);
|
||||
}
|
||||
249
sdv_executables/sdv_idl_compiler/entities/definition_entity.h
Normal file
249
sdv_executables/sdv_idl_compiler/entities/definition_entity.h
Normal 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)
|
||||
1708
sdv_executables/sdv_idl_compiler/entities/entity_base.cpp
Normal file
1708
sdv_executables/sdv_idl_compiler/entities/entity_base.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1066
sdv_executables/sdv_idl_compiler/entities/entity_base.h
Normal file
1066
sdv_executables/sdv_idl_compiler/entities/entity_base.h
Normal file
File diff suppressed because it is too large
Load Diff
803
sdv_executables/sdv_idl_compiler/entities/entity_value.cpp
Normal file
803
sdv_executables/sdv_idl_compiler/entities/entity_value.cpp
Normal 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();
|
||||
}
|
||||
584
sdv_executables/sdv_idl_compiler/entities/entity_value.h
Normal file
584
sdv_executables/sdv_idl_compiler/entities/entity_value.h
Normal 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)
|
||||
213
sdv_executables/sdv_idl_compiler/entities/enum_entity.cpp
Normal file
213
sdv_executables/sdv_idl_compiler/entities/enum_entity.cpp
Normal 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);
|
||||
}
|
||||
178
sdv_executables/sdv_idl_compiler/entities/enum_entity.h
Normal file
178
sdv_executables/sdv_idl_compiler/entities/enum_entity.h
Normal 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)
|
||||
@@ -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);
|
||||
}
|
||||
62
sdv_executables/sdv_idl_compiler/entities/exception_entity.h
Normal file
62
sdv_executables/sdv_idl_compiler/entities/exception_entity.h
Normal 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)
|
||||
39
sdv_executables/sdv_idl_compiler/entities/hash_calc.cpp
Normal file
39
sdv_executables/sdv_idl_compiler/entities/hash_calc.cpp
Normal 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;
|
||||
}
|
||||
52
sdv_executables/sdv_idl_compiler/entities/hash_calc.h
Normal file
52
sdv_executables/sdv_idl_compiler/entities/hash_calc.h
Normal 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)
|
||||
@@ -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);
|
||||
}
|
||||
96
sdv_executables/sdv_idl_compiler/entities/interface_entity.h
Normal file
96
sdv_executables/sdv_idl_compiler/entities/interface_entity.h
Normal 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)
|
||||
65
sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp
Normal file
65
sdv_executables/sdv_idl_compiler/entities/meta_entity.cpp
Normal 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;
|
||||
}
|
||||
68
sdv_executables/sdv_idl_compiler/entities/meta_entity.h
Normal file
68
sdv_executables/sdv_idl_compiler/entities/meta_entity.h
Normal 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)
|
||||
49
sdv_executables/sdv_idl_compiler/entities/module_entity.cpp
Normal file
49
sdv_executables/sdv_idl_compiler/entities/module_entity.cpp
Normal 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;
|
||||
}
|
||||
86
sdv_executables/sdv_idl_compiler/entities/module_entity.h
Normal file
86
sdv_executables/sdv_idl_compiler/entities/module_entity.h
Normal 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)
|
||||
@@ -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();
|
||||
}
|
||||
128
sdv_executables/sdv_idl_compiler/entities/operation_entity.h
Normal file
128
sdv_executables/sdv_idl_compiler/entities/operation_entity.h
Normal 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)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
92
sdv_executables/sdv_idl_compiler/entities/parameter_entity.h
Normal file
92
sdv_executables/sdv_idl_compiler/entities/parameter_entity.h
Normal 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)
|
||||
30
sdv_executables/sdv_idl_compiler/entities/root_entity.cpp
Normal file
30
sdv_executables/sdv_idl_compiler/entities/root_entity.cpp
Normal 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;
|
||||
}
|
||||
57
sdv_executables/sdv_idl_compiler/entities/root_entity.h
Normal file
57
sdv_executables/sdv_idl_compiler/entities/root_entity.h
Normal 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)
|
||||
71
sdv_executables/sdv_idl_compiler/entities/struct_entity.cpp
Normal file
71
sdv_executables/sdv_idl_compiler/entities/struct_entity.cpp
Normal 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);
|
||||
}
|
||||
75
sdv_executables/sdv_idl_compiler/entities/struct_entity.h
Normal file
75
sdv_executables/sdv_idl_compiler/entities/struct_entity.h
Normal 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)
|
||||
44
sdv_executables/sdv_idl_compiler/entities/typedef_entity.cpp
Normal file
44
sdv_executables/sdv_idl_compiler/entities/typedef_entity.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
79
sdv_executables/sdv_idl_compiler/entities/typedef_entity.h
Normal file
79
sdv_executables/sdv_idl_compiler/entities/typedef_entity.h
Normal 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)
|
||||
434
sdv_executables/sdv_idl_compiler/entities/union_entity.cpp
Normal file
434
sdv_executables/sdv_idl_compiler/entities/union_entity.cpp
Normal 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;
|
||||
}
|
||||
233
sdv_executables/sdv_idl_compiler/entities/union_entity.h
Normal file
233
sdv_executables/sdv_idl_compiler/entities/union_entity.h
Normal 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)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
135
sdv_executables/sdv_idl_compiler/entities/variable_entity.h
Normal file
135
sdv_executables/sdv_idl_compiler/entities/variable_entity.h
Normal 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)
|
||||
320
sdv_executables/sdv_idl_compiler/environment.cpp
Normal file
320
sdv_executables/sdv_idl_compiler/environment.cpp
Normal 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;
|
||||
}
|
||||
|
||||
316
sdv_executables/sdv_idl_compiler/environment.h
Normal file
316
sdv_executables/sdv_idl_compiler/environment.h
Normal 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
|
||||
50
sdv_executables/sdv_idl_compiler/exception.cpp
Normal file
50
sdv_executables/sdv_idl_compiler/exception.cpp
Normal 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;
|
||||
}
|
||||
170
sdv_executables/sdv_idl_compiler/exception.h
Normal file
170
sdv_executables/sdv_idl_compiler/exception.h
Normal 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
|
||||
167
sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp
Normal file
167
sdv_executables/sdv_idl_compiler/generator/cmake_generator.cpp
Normal 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;
|
||||
}
|
||||
34
sdv_executables/sdv_idl_compiler/generator/cmake_generator.h
Normal file
34
sdv_executables/sdv_idl_compiler/generator/cmake_generator.h
Normal 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
|
||||
499
sdv_executables/sdv_idl_compiler/generator/context.cpp
Normal file
499
sdv_executables/sdv_idl_compiler/generator/context.cpp
Normal 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;
|
||||
}
|
||||
187
sdv_executables/sdv_idl_compiler/generator/context.h
Normal file
187
sdv_executables/sdv_idl_compiler/generator/context.h
Normal 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)
|
||||
1813
sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp
Normal file
1813
sdv_executables/sdv_idl_compiler/generator/definition_generator.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
254
sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp
Normal file
254
sdv_executables/sdv_idl_compiler/generator/proxy_generator.cpp
Normal 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();
|
||||
}
|
||||
|
||||
108
sdv_executables/sdv_idl_compiler/generator/proxy_generator.h
Normal file
108
sdv_executables/sdv_idl_compiler/generator/proxy_generator.h
Normal 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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
118
sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.cpp
Normal file
118
sdv_executables/sdv_idl_compiler/generator/ps_cpp_generator.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
1167
sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp
Normal file
1167
sdv_executables/sdv_idl_compiler/generator/serdes_generator.cpp
Normal file
File diff suppressed because it is too large
Load Diff
289
sdv_executables/sdv_idl_compiler/generator/serdes_generator.h
Normal file
289
sdv_executables/sdv_idl_compiler/generator/serdes_generator.h
Normal 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
|
||||
262
sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp
Normal file
262
sdv_executables/sdv_idl_compiler/generator/stub_generator.cpp
Normal 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();
|
||||
}
|
||||
108
sdv_executables/sdv_idl_compiler/generator/stub_generator.h
Normal file
108
sdv_executables/sdv_idl_compiler/generator/stub_generator.h
Normal 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)
|
||||
17
sdv_executables/sdv_idl_compiler/includes.h
Normal file
17
sdv_executables/sdv_idl_compiler/includes.h
Normal 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)
|
||||
1102
sdv_executables/sdv_idl_compiler/lexer.cpp
Normal file
1102
sdv_executables/sdv_idl_compiler/lexer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
269
sdv_executables/sdv_idl_compiler/lexer.h
Normal file
269
sdv_executables/sdv_idl_compiler/lexer.h
Normal 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
|
||||
89
sdv_executables/sdv_idl_compiler/lexerbase.h
Normal file
89
sdv_executables/sdv_idl_compiler/lexerbase.h
Normal 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
|
||||
109
sdv_executables/sdv_idl_compiler/logger.cpp
Normal file
109
sdv_executables/sdv_idl_compiler/logger.cpp
Normal 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;
|
||||
}
|
||||
141
sdv_executables/sdv_idl_compiler/logger.h
Normal file
141
sdv_executables/sdv_idl_compiler/logger.h
Normal 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)
|
||||
342
sdv_executables/sdv_idl_compiler/macro.cpp
Normal file
342
sdv_executables/sdv_idl_compiler/macro.cpp
Normal 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());
|
||||
}
|
||||
|
||||
111
sdv_executables/sdv_idl_compiler/macro.h
Normal file
111
sdv_executables/sdv_idl_compiler/macro.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef MACRO_H
|
||||
#define MACRO_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "token.h"
|
||||
|
||||
// Forward declarations
|
||||
class CIdlCompilerEnvironment;
|
||||
|
||||
/**
|
||||
* @brief A set of macro names used previously in macro expansion.
|
||||
*/
|
||||
using CUsedMacroSet = std::set<std::string>;
|
||||
|
||||
/**
|
||||
* @brief Macro processing class.
|
||||
*/
|
||||
class CMacro
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] szName The name of the macro.
|
||||
* @param[in] pvecParams Pointer to the list of zero or more param definitions or NULL when the macro doesn't have param
|
||||
* definitions.
|
||||
* @param[in] szValue Zero terminated string to the value or NULL when the macro doesn't have a value.
|
||||
*/
|
||||
CMacro(const char* szName, const std::vector<std::string>* pvecParams, const char* szValue);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* @param[in] rMacro Reference to the macro to copy from.
|
||||
*/
|
||||
CMacro(const CMacro& rMacro);
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
* @param[in] rMacro Reference to the macro to copy from.
|
||||
*/
|
||||
CMacro(CMacro&& rMacro) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Assignment operator
|
||||
* @param[in] rMacro Reference to the macro to copy from.
|
||||
* @return Reference to this macro.
|
||||
*/
|
||||
CMacro& operator=(const CMacro& rMacro);
|
||||
|
||||
/**
|
||||
* @brief Move operator
|
||||
* @param[in] rMacro Reference to the macro to copy from.
|
||||
* @return Reference to this macro.
|
||||
*/
|
||||
CMacro& operator=(CMacro&& rMacro) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Equality operator
|
||||
* @param[in] rMacro Reference to the macro to copy from.
|
||||
* @return Returns true if the macros are identical; otherwise returns false.
|
||||
*/
|
||||
bool operator==(const CMacro& rMacro);
|
||||
|
||||
/**
|
||||
* @brief Inequality operator
|
||||
* @param[in] rMacro Reference to the macro to copy from.
|
||||
* @return Returns true if the macros are not identical; otherwise returns false.
|
||||
*/
|
||||
bool operator!=(const CMacro& rMacro);
|
||||
|
||||
/**
|
||||
* @brief Get the name of the macro.
|
||||
* @return Reference to the string containing the macro name.
|
||||
*/
|
||||
const std::string& GetName() const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether the macro expects zero or more parameters (uses parenthesis for its parameters).
|
||||
* @attention A macro could return 'true' even when it expects zero parameters. In that case, it uses at least parenthesis.
|
||||
* If no parenthesis is defined for this macro, it would returns false.
|
||||
* @return Returns 'true' when the macro expects parameters; false when not.
|
||||
*/
|
||||
bool ExpectParameters() const;
|
||||
|
||||
/**
|
||||
* @brief Expand the macro with the provided parameters. Returns the value string of the macro with the filled in parameters.
|
||||
* @param[in] renv Reference to the current environment needed to expand parameters.
|
||||
* @param[in] rtoken Token of the parameters in the code. Used for throwing errors.
|
||||
* @param[in] rvecParams Vector with the parameters.
|
||||
* @param[in, out] rsetUsedMacros Reference to the set of previously expanded macros, preventing circular expansion. This set
|
||||
* will be extended if macros
|
||||
* @return Returns the macro string with the resolved parameters.
|
||||
*/
|
||||
std::string Expand(const CIdlCompilerEnvironment& renv, const CToken& rtoken, const std::vector<std::string>& rvecParams,
|
||||
CUsedMacroSet& rsetUsedMacros) const;
|
||||
|
||||
private:
|
||||
std::string m_ssName; ///< Name of the macro (provided through the define-direction).
|
||||
bool m_bExpectParams; ///< When set, the macro expects parenthesis and zero or more parameters.
|
||||
std::vector<std::string> m_vecParamDefs; ///< List of parameters.
|
||||
std::string m_ssValue; ///< The macro value to replace the name with.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Map containing the associations of macro name and value.
|
||||
*/
|
||||
using CMacroMap = std::map<std::string, CMacro>;
|
||||
|
||||
#endif // !defined MACRO_H
|
||||
138
sdv_executables/sdv_idl_compiler/main.cpp
Normal file
138
sdv_executables/sdv_idl_compiler/main.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "../../global/process_watchdog.h"
|
||||
#include "parser.h"
|
||||
#include "core_idl_backup.h"
|
||||
#include "../../global/cmdlnparser/cmdlnparser.cpp"
|
||||
#include "exception.h"
|
||||
#include "environment.h"
|
||||
#include "generator/definition_generator.h"
|
||||
#include "generator/proxy_generator.h"
|
||||
#include "generator/stub_generator.h"
|
||||
#include "generator/serdes_generator.h"
|
||||
#include "generator/ps_cpp_generator.h"
|
||||
#include "generator/cmake_generator.h"
|
||||
#include <iostream>
|
||||
#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;
|
||||
|
||||
std::cout << "SDV IDL Compiler" << std::endl;
|
||||
std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl;
|
||||
std::cout << "Author: Erik Verhoeven" << std::endl;
|
||||
|
||||
// Create a list of UTF-8 arguments
|
||||
std::vector<std::string> vecArgs;
|
||||
for (int iIndex = 0; iIndex < iArgc; iIndex++)
|
||||
vecArgs.push_back(sdv::MakeUtf8String(rgszArgv[iIndex]));
|
||||
|
||||
// Parse the command line
|
||||
bool bCompilerError = false;
|
||||
CIdlCompilerEnvironment environment;
|
||||
try
|
||||
{
|
||||
environment = CIdlCompilerEnvironment(iArgc, rgszArgv);
|
||||
} catch (const sdv::idl::XCompileError& rexcept)
|
||||
{
|
||||
bCompilerError = true;
|
||||
std::stringstream sstreamError;
|
||||
sstreamError << "error: " << rexcept.ssReason << std::endl;
|
||||
g_log_control.Log(sstreamError.str(), true);
|
||||
}
|
||||
|
||||
// Show help?
|
||||
if (environment.Help())
|
||||
{
|
||||
environment.ShowHelp();
|
||||
return bCompilerError ? CMDLN_ARG_ERR : NO_ERROR;
|
||||
}
|
||||
|
||||
// Process files.
|
||||
size_t nCnt = 0;
|
||||
do
|
||||
{
|
||||
std::filesystem::path pathFile = environment.GetNextFile();
|
||||
if (pathFile.empty())
|
||||
{
|
||||
if (!nCnt)
|
||||
std::cout << "No files provided; nothing to do. Use \"idl_compiler --help\" to request command line information." <<
|
||||
std::endl;
|
||||
break;
|
||||
}
|
||||
nCnt++;
|
||||
|
||||
try
|
||||
{
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Processing: " << pathFile.generic_u8string() << std::endl;
|
||||
|
||||
// Parse file
|
||||
CParser parser(pathFile, environment);
|
||||
parser.Parse();
|
||||
|
||||
// Generate definition
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Generating interface definition..."<< std::endl;
|
||||
CDefinitionGenerator defgen(&parser);
|
||||
defgen.Generate();
|
||||
|
||||
if (!environment.NoProxyStub())
|
||||
{
|
||||
// Proxy code
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Generating proxy..." << std::endl;
|
||||
CProxyGenerator proxygen(&parser);
|
||||
proxygen.Generate();
|
||||
|
||||
// Stub code
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Generating stub..." << std::endl;
|
||||
CStubGenerator stubgen(&parser);
|
||||
stubgen.Generate();
|
||||
|
||||
// Proxy and stub CPP generation
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Generating proxy/stub CPP file..." << std::endl;
|
||||
CPsCppGenerator pscppgen(&parser);
|
||||
pscppgen.Generate();
|
||||
|
||||
// CMake code generation
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Generating CMake file..." << std::endl;
|
||||
CIdlCompilerCMakeGenerator cmakegen(&parser);
|
||||
cmakegen.Generate(environment.GetProxStubCMakeTarget());
|
||||
}
|
||||
|
||||
// Serdes code
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Generating serdes functions..." << std::endl;
|
||||
CSerdesGenerator serdesgen(&parser);
|
||||
serdesgen.Generate();
|
||||
}
|
||||
catch (const sdv::idl::XCompileError& rexcept)
|
||||
{
|
||||
bCompilerError = true;
|
||||
std::stringstream sstreamError;
|
||||
if (!rexcept.ssFile.empty())
|
||||
sstreamError << rexcept.ssFile << "(line=" << rexcept.uiLine << ", col=" << rexcept.uiCol <<
|
||||
") error: " << rexcept.ssReason << std::endl;
|
||||
else
|
||||
sstreamError << "error: " << rexcept.ssReason << std::endl;
|
||||
g_log_control.Log(sstreamError.str(), true);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (g_log_control.GetVerbosityMode() != EVerbosityMode::report_none)
|
||||
std::cout << "Done..." << std::endl;
|
||||
|
||||
return bCompilerError ? COMPILE_ERROR : NO_ERROR;
|
||||
}
|
||||
47
sdv_executables/sdv_idl_compiler/parsecontext.cpp
Normal file
47
sdv_executables/sdv_idl_compiler/parsecontext.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "parsecontext.h"
|
||||
|
||||
CContext::CContext(const std::filesystem::path& rpath,
|
||||
sdv::idl::IEntityContext::ELocation eLocation /*= sdv::idl::IEntityContext::ELocation::source*/) :
|
||||
m_eLocation(eLocation)
|
||||
{
|
||||
if (rpath.is_absolute())
|
||||
m_source = rpath;
|
||||
else
|
||||
m_source = std::filesystem::current_path() / rpath;
|
||||
m_code = m_source.GetCodeRef().c_str();
|
||||
}
|
||||
|
||||
CContext::CContext(const char* szCode) :
|
||||
m_source(szCode), m_code(m_source.GetCodeRef().c_str()),
|
||||
m_eLocation(sdv::idl::IEntityContext::ELocation::source)
|
||||
{}
|
||||
|
||||
CContext::CContext(CContext&& rContext) noexcept:
|
||||
m_source(std::move(rContext.m_source)), m_code(m_source.GetCodeRef().c_str())
|
||||
{}
|
||||
|
||||
CContext::~CContext()
|
||||
{}
|
||||
|
||||
CContext& CContext::operator=(CContext&& rContext) noexcept
|
||||
{
|
||||
m_source = std::move(rContext.m_source);
|
||||
m_code = std::move(CCodePos(m_source.GetCodeRef().c_str()));
|
||||
m_eLocation = rContext.m_eLocation;
|
||||
rContext.m_eLocation = sdv::idl::IEntityContext::ELocation::source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CContext::operator==(const CContext& rContext) const
|
||||
{
|
||||
if (&rContext == this) return true;
|
||||
if (!m_source.GetPathRef().empty() &&
|
||||
m_source.GetPathRef() == rContext.m_source.GetPathRef()) return true;
|
||||
return m_source.GetCodeRef() == rContext.m_source.GetCodeRef();
|
||||
}
|
||||
|
||||
bool CContext::operator!=(const CContext& rContext) const
|
||||
{
|
||||
return !operator==(rContext);
|
||||
}
|
||||
|
||||
112
sdv_executables/sdv_idl_compiler/parsecontext.h
Normal file
112
sdv_executables/sdv_idl_compiler/parsecontext.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef PARSE_CONTEXT_H
|
||||
#define PARSE_CONTEXT_H
|
||||
|
||||
#include "includes.h"
|
||||
#include "source.h"
|
||||
#include "codepos.h"
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Source code context
|
||||
*/
|
||||
class CContext
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor using a path
|
||||
* @param[in] rpath Reference to the path of the file.
|
||||
* @param[in] eLocation The location of the file to parse.
|
||||
*/
|
||||
CContext(const std::filesystem::path& rpath, sdv::idl::IEntityContext::ELocation eLocation = sdv::idl::IEntityContext::ELocation::source);
|
||||
|
||||
/**
|
||||
* @brief Constructor using code
|
||||
* @param[in] szCode Zero terminated string containing the code to set.
|
||||
*/
|
||||
CContext(const char* szCode);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
CContext(const CContext&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
* @param[in, out] rContext Reference to the source code context.
|
||||
*/
|
||||
CContext(CContext&& rContext) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CContext();
|
||||
|
||||
/**
|
||||
* @brief Assignment operator
|
||||
* @return Returns a reference to this object.
|
||||
*/
|
||||
CContext& operator=(const CContext&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move operator
|
||||
* @param[in, out] rContext Reference to the source code structure.
|
||||
* @return Returns a reference to this object.
|
||||
*/
|
||||
CContext& operator=(CContext&& rContext) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Compare for equal context.
|
||||
* @param[in] rContext Reference to the source code structure.
|
||||
* @return Returns the comparison result.
|
||||
*/
|
||||
bool operator==(const CContext& rContext) const;
|
||||
|
||||
/**
|
||||
* @brief Compare for inequal context.
|
||||
* @param[in] rContext Reference to the source code structure.
|
||||
* @return Returns the comparison result.
|
||||
*/
|
||||
bool operator!=(const CContext& rContext) const;
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Get source access.
|
||||
* @return Reference to the source code.
|
||||
*/
|
||||
CSource& Source() { return m_source; }
|
||||
const CSource& Source() const { return m_source; }
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Get code access.
|
||||
* @return Reference to the code position.
|
||||
*/
|
||||
CCodePos& Code() { return m_code; }
|
||||
const CCodePos& Code() const { return m_code; }
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the code location.
|
||||
* @return Returns the code location.
|
||||
*/
|
||||
sdv::idl::IEntityContext::ELocation GetLocation() const { return m_eLocation; }
|
||||
|
||||
private:
|
||||
CSource m_source; ///< Source file holding code
|
||||
CCodePos m_code; ///< Code navigation
|
||||
sdv::idl::IEntityContext::ELocation m_eLocation = sdv::idl::IEntityContext::ELocation::source; ///< Location of the code.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Context shared pointer.
|
||||
*/
|
||||
using CContextPtr = std::shared_ptr<CContext>;
|
||||
|
||||
#endif // !defined(PARSE_CONTEXT_H)
|
||||
403
sdv_executables/sdv_idl_compiler/parser.cpp
Normal file
403
sdv_executables/sdv_idl_compiler/parser.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
#include "parser.h"
|
||||
#include "exception.h"
|
||||
|
||||
CParser::CParser(const char* szCode, const CIdlCompilerEnvironment& renv /*= CIdlCompilerEnvironment()*/) :
|
||||
CPreprocessor(*this), m_lexer(this, renv.CaseSensitiveTypeExtension()), m_environment(renv)
|
||||
{
|
||||
// Create soure code context object
|
||||
CContextPtr ptrContext = std::make_shared<CContext>(szCode);
|
||||
|
||||
// Add additional keywords to the lexer.
|
||||
if (renv.InterfaceTypeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("interface_id");
|
||||
m_lexer.AddKeyword("interface_t");
|
||||
m_lexer.AddKeyword("null");
|
||||
}
|
||||
if (renv.ExceptionTypeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("exception_id");
|
||||
}
|
||||
if (renv.PointerTypeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("pointer");
|
||||
}
|
||||
if (renv.UnicodeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("char16");
|
||||
m_lexer.AddKeyword("char32");
|
||||
m_lexer.AddKeyword("u8string");
|
||||
m_lexer.AddKeyword("u16string");
|
||||
m_lexer.AddKeyword("u32string");
|
||||
}
|
||||
|
||||
// Parse the source
|
||||
ParserPrepare(ptrContext);
|
||||
}
|
||||
|
||||
CParser::CParser(const std::filesystem::path& rpath, const CIdlCompilerEnvironment& renv /*= CIdlCompilerEnvironment()*/) :
|
||||
CPreprocessor(*this), m_lexer(this, renv.CaseSensitiveTypeExtension()), m_environment(renv), m_pathFile(rpath)
|
||||
{
|
||||
// Create soure code context object
|
||||
CContextPtr ptrContext = std::make_shared<CContext>(rpath);
|
||||
|
||||
// Add additional keywords to the lexer.
|
||||
if (renv.InterfaceTypeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("interface_id");
|
||||
m_lexer.AddKeyword("interface_t");
|
||||
m_lexer.AddKeyword("null");
|
||||
}
|
||||
if (renv.ExceptionTypeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("exception_id");
|
||||
}
|
||||
if (renv.PointerTypeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("pointer");
|
||||
}
|
||||
if (renv.UnicodeExtension())
|
||||
{
|
||||
m_lexer.AddKeyword("char16");
|
||||
m_lexer.AddKeyword("char32");
|
||||
m_lexer.AddKeyword("u8string");
|
||||
m_lexer.AddKeyword("u16string");
|
||||
m_lexer.AddKeyword("u32string");
|
||||
}
|
||||
|
||||
// Parse the source
|
||||
ParserPrepare(ptrContext);
|
||||
}
|
||||
|
||||
sdv::interface_t CParser::GetInterface(sdv::interface_id idInterface)
|
||||
{
|
||||
// Own interfaces first
|
||||
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
|
||||
return static_cast<sdv::IInterfaceAccess*>(this);
|
||||
if (idInterface == sdv::GetInterfaceId<sdv::idl::ICompilerInfo>())
|
||||
return static_cast<sdv::idl::ICompilerInfo*>(this);
|
||||
|
||||
// Then ask the environment
|
||||
sdv::interface_t ifc = m_environment.GetInterface(idInterface);
|
||||
|
||||
// Then as the root
|
||||
if (ifc == nullptr && m_ptrRoot != nullptr)
|
||||
ifc = m_ptrRoot->GetInterface(idInterface);
|
||||
|
||||
return ifc;
|
||||
}
|
||||
|
||||
sdv::u8string CParser::GetFilePath() const
|
||||
{
|
||||
return m_pathFile.u8string();
|
||||
}
|
||||
|
||||
sdv::u8string CParser::GetOutputDir() const
|
||||
{
|
||||
return m_environment.GetOutputDir().generic_u8string();
|
||||
}
|
||||
|
||||
const CLexer& CParser::GetLexer() const
|
||||
{
|
||||
return m_lexer;
|
||||
}
|
||||
|
||||
void CParser::IncludeFile(const std::filesystem::path& rpath, bool bLocal)
|
||||
{
|
||||
// Create soure code context object
|
||||
CContextPtr ptrContext = std::make_shared<CContext>(rpath,
|
||||
bLocal ? sdv::idl::IEntityContext::ELocation::local_include : sdv::idl::IEntityContext::ELocation::global_include);
|
||||
|
||||
// Prepare for parsing the file
|
||||
ParserPrepare(ptrContext);
|
||||
}
|
||||
|
||||
CIdlCompilerEnvironment& CParser::GetEnvironment()
|
||||
{
|
||||
return m_environment;
|
||||
}
|
||||
|
||||
CToken CParser::GetToken()
|
||||
{
|
||||
// Check the token cache.
|
||||
if (!m_lstCacheTokenList.empty())
|
||||
{
|
||||
CToken token = m_lstCacheTokenList.front();
|
||||
m_lstCacheTokenList.pop_front();
|
||||
return token;
|
||||
}
|
||||
|
||||
// No token in the cache. Get the token from the lexer.
|
||||
CToken token = GetTokenFromLexer(false);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
CToken CParser::GetLastValidToken() const
|
||||
{
|
||||
return m_lexer.GetLastValidToken();
|
||||
}
|
||||
|
||||
CToken CParser::PeekToken(size_t nIndex /*= 0*/)
|
||||
{
|
||||
CToken token;
|
||||
|
||||
// Check the token cache. If not large enough, fill it up.
|
||||
while (m_lstCacheTokenList.size() <= nIndex)
|
||||
{
|
||||
token = GetTokenFromLexer(true);
|
||||
if (!token) return token;
|
||||
m_lstCacheTokenList.push_back(token);
|
||||
}
|
||||
|
||||
CTokenList::iterator itPos = m_lstCacheTokenList.begin();
|
||||
for (size_t nCnt = 0; nCnt < nIndex; ++nCnt) ++itPos;
|
||||
token = *itPos;
|
||||
return token;
|
||||
}
|
||||
|
||||
CTokenList CParser::GetComments()
|
||||
{
|
||||
// If there are no comments, request the next token from the lexer (peeking only); this will request comments.
|
||||
if (m_lstComments.empty())
|
||||
PeekToken();
|
||||
return m_lstComments;
|
||||
}
|
||||
|
||||
void CParser::PrependToken(const CToken& rToken)
|
||||
{
|
||||
// Add the token at the front of to the cache list
|
||||
if (!rToken) return;
|
||||
m_lstCacheTokenList.push_front(rToken);
|
||||
}
|
||||
|
||||
void CParser::SkipAdjacentComments()
|
||||
{
|
||||
PeekToken();
|
||||
uint32_t uiLine = 0;
|
||||
while (!m_lstComments.empty())
|
||||
{
|
||||
const CToken token = m_lstComments.front();
|
||||
if (!uiLine || token.GetLine() <= uiLine + 1)
|
||||
{
|
||||
uiLine = token.GetLine();
|
||||
m_lstComments.pop_front();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CParser& CParser::LexicalCheck()
|
||||
{
|
||||
while (GetToken());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CParser& CParser::Parse()
|
||||
{
|
||||
// Stack is empty if there is no code available (or everything was parsed already). This is not an error.
|
||||
if (m_stackCode.empty()) return *this;
|
||||
|
||||
// Create the root entity.
|
||||
m_ptrRoot = std::make_shared<CRootEntity>(*this, m_stackCode.top());
|
||||
CRootEntity* pRoot = m_ptrRoot->Get<CRootEntity>();
|
||||
if (!pRoot) throw CCompileException("Internal error: cannot get access to the root object.");
|
||||
|
||||
try
|
||||
{
|
||||
// Parse the root entity.
|
||||
pRoot->Process();
|
||||
}
|
||||
catch (sdv::idl::XCompileError& rexcept)
|
||||
{
|
||||
// Use the class for easy access.
|
||||
CCompileException exception(rexcept);
|
||||
|
||||
// Add the line number if not existing
|
||||
if (!exception.GetLineNo())
|
||||
exception.SetLocation(m_lexer.GetLastValidToken());
|
||||
|
||||
// Add the path of the code causing the exception to the exception.
|
||||
if (exception.GetPath().empty())
|
||||
exception.SetPath(m_stackCode.empty() ? m_pathFile : m_stackCode.top()->Source().GetPathRef());
|
||||
|
||||
// Assign the changed values. Needed to keep the additional information that the derived class might have.
|
||||
rexcept = exception;
|
||||
|
||||
// Rethrow the original derived class.
|
||||
throw;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const CRootEntity* CParser::Root() const
|
||||
{
|
||||
return m_ptrRoot ? m_ptrRoot->Get<CRootEntity>() : nullptr;
|
||||
}
|
||||
|
||||
std::string CParser::GenerateAnonymousEntityName(const std::string& rssPrefix)
|
||||
{
|
||||
// Find the prefix in the map
|
||||
auto itPrefix = m_mapAutoNameCount.find(rssPrefix);
|
||||
if (itPrefix == m_mapAutoNameCount.end()) // Prefix not know; add a new prefix
|
||||
itPrefix = m_mapAutoNameCount.emplace(rssPrefix, 0).first;
|
||||
return rssPrefix + "_" + std::to_string(itPrefix->second++);
|
||||
}
|
||||
|
||||
std::list<CParser::SMetaToken> CParser::GetAndRemoveMeta()
|
||||
{
|
||||
return std::move(m_lstMeta);
|
||||
}
|
||||
|
||||
void CParser::ParserPrepare(CContextPtr& rptrContext)
|
||||
{
|
||||
// Check whether the file was already added to the parser (do not include the same file multipe times).
|
||||
if (m_setProcessedFiles.find(rptrContext->Source().GetPathRef()) != m_setProcessedFiles.end()) return;
|
||||
m_setProcessedFiles.insert(rptrContext->Source().GetPathRef());
|
||||
|
||||
// Check for maximum parsing depth.
|
||||
if (m_stackCode.size() >= m_nMaxDepth)
|
||||
throw CCompileException("Passed maximum amount of nested files - circular inclusion of files?");
|
||||
|
||||
// Add the source code context to the stack
|
||||
m_stackCode.push(rptrContext);
|
||||
|
||||
// A new file will be processed. Enable preprocessing on the lexer.
|
||||
m_lexer.EnablePreprocProcessing();
|
||||
}
|
||||
|
||||
void CParser::InsertWhitespace(const CToken&)
|
||||
{}
|
||||
|
||||
void CParser::InsertComment(const CToken& rtoken)
|
||||
{
|
||||
// Assign the context to the token (this prevents the context from unloading when the token is still in use).
|
||||
CToken tokenComment = rtoken;
|
||||
tokenComment.SetContext(m_stackCode.top());
|
||||
|
||||
// Comments can be invalid after the next token is read (in case the next token is in another file). Cache the comment
|
||||
// text by requesting the string.
|
||||
m_lstComments.push_back(std::move(tokenComment));
|
||||
}
|
||||
|
||||
void CParser::ProcessPreprocDirective(CCodePos& rCode)
|
||||
{
|
||||
// Get the current parse context
|
||||
CContextPtr ptrContext = m_stackCode.top();
|
||||
|
||||
// Callback inserting preprocessor directives
|
||||
CToken tokenMeta = ProcessPreproc(rCode, ptrContext);
|
||||
if (tokenMeta)
|
||||
{
|
||||
// Check for adjacent preceding comments
|
||||
uint32_t uiLine = tokenMeta.GetLine();
|
||||
CTokenList lstMetaComments;
|
||||
while (m_lstComments.size())
|
||||
{
|
||||
uint32_t uiCommentLine = m_lstComments.back().GetEndLine();
|
||||
if (uiCommentLine < uiLine - 1) break; // Not connecting to the meta data
|
||||
if (uiCommentLine > uiLine) break; // Not preceding the meta data
|
||||
uiLine = uiCommentLine;
|
||||
lstMetaComments.push_front(m_lstComments.back());
|
||||
m_lstComments.pop_back();
|
||||
}
|
||||
|
||||
m_lstMeta.push_back(SMetaToken{ tokenMeta, lstMetaComments });
|
||||
}
|
||||
}
|
||||
|
||||
CToken CParser::GetTokenFromLexer(bool /*bPeekOnly*/)
|
||||
{
|
||||
// Clear the comments
|
||||
m_lstComments.clear();
|
||||
|
||||
// Check for code
|
||||
if (m_stackCode.empty())
|
||||
return CToken();
|
||||
|
||||
// Get the token from the code
|
||||
CToken token;
|
||||
try
|
||||
{
|
||||
// Get a token as long as no token was found or the stack
|
||||
while (!m_stackCode.empty())
|
||||
{
|
||||
CCodePos &rCode = m_stackCode.top()->Code();
|
||||
|
||||
// If the current position is not part of the macro expansion, reset the set of macros used in a expansion.
|
||||
bool bInMacroExpansion = rCode.CurrentPositionInMacroExpansion();
|
||||
|
||||
// Get a token. This will automatically trigger preprocessing control.
|
||||
// Assign the context to the token (this prevents the context from unloading when the token is still in use).
|
||||
token = m_lexer.GetToken(rCode, m_stackCode.top());
|
||||
|
||||
// Should this code be processed. If not, skip a line and try again...
|
||||
if (!CurrentSectionEnabled())
|
||||
{
|
||||
// Check for EOF
|
||||
if (token) // Not EOF
|
||||
m_lexer.SkipLine(rCode);
|
||||
else // EOF, process the next file
|
||||
{
|
||||
// Check for EOF. If so, close the file.
|
||||
// ATTENTION Do not use rCode, since the stack might have changed through a preprocessor include directive.
|
||||
if (m_stackCode.top()->Code().HasEOF())
|
||||
{
|
||||
// Trigger final preproc processing (to check for correct conditional inclusion boundaries).
|
||||
FinalProcessing(m_stackCode.top()->Code());
|
||||
|
||||
// Remove the top file from the stack
|
||||
m_stackCode.pop();
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check whether the token is an identifier, if so, check for any macro
|
||||
if (token.GetType() == ETokenType::token_identifier || token.GetType() == ETokenType::token_keyword)
|
||||
{
|
||||
if (GetEnvironment().TestAndExpand(static_cast<std::string>(token).c_str(), rCode, bInMacroExpansion))
|
||||
continue; // macro was replaced, get a token again.
|
||||
}
|
||||
|
||||
// Return the token if one was returned.
|
||||
if (token)
|
||||
return token;
|
||||
|
||||
// Check for EOF. If so, close the file.
|
||||
// ATTENTION Do not use rCode, since the stack might have changed through a preprocessor include directive.
|
||||
if (!m_stackCode.empty() && m_stackCode.top()->Code().HasEOF())
|
||||
{
|
||||
// Trigger final preproc processing (to check for correct conditional inclusion boundaries).
|
||||
FinalProcessing(m_stackCode.top()->Code());
|
||||
|
||||
// Remove the top file from the stack
|
||||
m_stackCode.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (sdv::idl::XCompileError& rexcept)
|
||||
{
|
||||
// Use the class for easy access.
|
||||
CCompileException exception(rexcept);
|
||||
|
||||
// Add the token to the code if no token was assigned.
|
||||
if (token) exception.SetLocation(token);
|
||||
|
||||
// Add the path of the code causing the exception to the exception.
|
||||
exception.SetPath(m_stackCode.top()->Source().GetPathRef());
|
||||
|
||||
// Assign the changed values. Needed to keep the additional information that the derived class might have.
|
||||
rexcept = exception;
|
||||
|
||||
// Rethrow the original derived class.
|
||||
throw;
|
||||
}
|
||||
|
||||
// At this point, all files were processed. There is no more token.
|
||||
return CToken();
|
||||
}
|
||||
|
||||
237
sdv_executables/sdv_idl_compiler/parser.h
Normal file
237
sdv_executables/sdv_idl_compiler/parser.h
Normal file
@@ -0,0 +1,237 @@
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include "lexer.h"
|
||||
#include "preproc.h"
|
||||
#include "source.h"
|
||||
#include "environment.h"
|
||||
#include <stack>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <filesystem>
|
||||
#include "entities/root_entity.h"
|
||||
#include "parsecontext.h"
|
||||
|
||||
enum class EEntityType : uint32_t
|
||||
{
|
||||
entity_comment,
|
||||
entity_include,
|
||||
entity_module,
|
||||
entity_interface, // Part of a module
|
||||
entity_valuetype, // Part of an interface
|
||||
entity_function, // part of an interface
|
||||
entity_typedef, // Part of an interface
|
||||
entity_eof
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SDV IDL lexer class
|
||||
*/
|
||||
class CParser : public CPreprocessor, public ILexerCallback, public sdv::idl::ICompilerInfo, public sdv::IInterfaceAccess
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor used to inject code directly into the parser (mainly for unit tests).
|
||||
* @param[in] szCode Zero terminated string containing the IDL code. Must not be NULL.
|
||||
* @param[in] renv Reference to the environment to start using as a base for search dirs and macros.
|
||||
* @attention The pointer to the source file code needs to stay valid during the duration of the processing.
|
||||
* @throws Throws CCompileException on parse error.
|
||||
*/
|
||||
CParser(const char* szCode, const CIdlCompilerEnvironment& renv = CIdlCompilerEnvironment());
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rpath Reference to the source file path. Must be a valid path.
|
||||
* @param[in] renv Reference to the environment to start using as a base for search dirs and macros.
|
||||
* @throws Throws CCompileException on parse error.
|
||||
*/
|
||||
CParser(const std::filesystem::path& rpath, const CIdlCompilerEnvironment& renv = CIdlCompilerEnvironment());
|
||||
|
||||
/**
|
||||
* @brief Default destructor
|
||||
*/
|
||||
virtual ~CParser() = 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 path of the processed file. Overload of sdv::idl::ICompilerInfo::GetFilePath.
|
||||
* @return The path string.
|
||||
*/
|
||||
virtual sdv::u8string GetFilePath() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the path to the output directory. Overload of sdv::idl::ICompilerInfo::GetOutputDir.
|
||||
* @return The directory string.
|
||||
*/
|
||||
virtual sdv::u8string GetOutputDir() const override;
|
||||
|
||||
/**
|
||||
* @brief Get access to the underlying lexer.
|
||||
* @return Reference to the lexer.
|
||||
*/
|
||||
const CLexer& GetLexer() const;
|
||||
|
||||
/**
|
||||
* @brief Include a file. Overload of CPreprocessor::IncludeFile.
|
||||
* @param[in] rpath Reference to the source file path. Must be a valid path.
|
||||
* @param[in] bLocal When set, the include file is a local include. Otherwise it represents a global include.
|
||||
*/
|
||||
virtual void IncludeFile(const std::filesystem::path& rpath, bool bLocal) override;
|
||||
|
||||
/**
|
||||
* @brief Get the environment. Overload of CPreprocessor::GetEnvironment.
|
||||
* @return Reference to the environment.
|
||||
*/
|
||||
virtual CIdlCompilerEnvironment& GetEnvironment() override;
|
||||
|
||||
/**
|
||||
* @brief Get a token from the lexer with moving the current position.
|
||||
* @details Check the token cache for any unprocessed tokens. If empty, get a token from the lexer for the currently open
|
||||
* file. Preprocessor directives are automatically executed causing conditionally parsing the file, defining and undefining
|
||||
* macros and opening include files.
|
||||
* @throws Throws CCompileException on parse error.
|
||||
* @return Returns the token if there is any or an empty token when there is no token any more.
|
||||
*/
|
||||
CToken GetToken();
|
||||
|
||||
/**
|
||||
* @brief Get the last valid token.
|
||||
* @return Returns the last read token or an empty token when no token was read before.
|
||||
*/
|
||||
CToken GetLastValidToken() const;
|
||||
|
||||
/**
|
||||
* @brief Get a token from the parser without moving the current position.
|
||||
* @param[in] nIndex The amount of tokens to skip before returning the token.
|
||||
* @return Returns the token if there is any or an empty token when there is no token any more.
|
||||
*/
|
||||
CToken PeekToken(size_t nIndex = 0);
|
||||
|
||||
/**
|
||||
* @brief Get the comment list gathered until the next token.
|
||||
* @return Token list with comments.
|
||||
*/
|
||||
CTokenList GetComments();
|
||||
|
||||
/**
|
||||
* @brief Insert a token in from of the current token cache.
|
||||
* @details This function prepends the current token at the current position.
|
||||
* @param[in] rToken Reference to token object containing the code to insert.
|
||||
*/
|
||||
void PrependToken(const CToken& rToken);
|
||||
|
||||
/**
|
||||
* @brief Skip adjacent comments - this prevents succeeding comments to be interpreted as preceding comments for the next
|
||||
* token.
|
||||
*/
|
||||
void SkipAdjacentComments();
|
||||
|
||||
/**
|
||||
* @brief Lexical check.
|
||||
* @details Parse through the file and belonging include files without interpretation to check for lexical errors.
|
||||
* @throws Throws CCompileException on parse error.
|
||||
* @return Returns a reference to this parser.
|
||||
*/
|
||||
CParser& LexicalCheck();
|
||||
|
||||
/**
|
||||
* @brief Parse the code.
|
||||
* @details Parse through the file and belonging include files and build a parse tree.
|
||||
* @throws Throws CCompileException on parse error.
|
||||
* @return Returns a reference to this parser.
|
||||
*/
|
||||
CParser& Parse();
|
||||
|
||||
/**
|
||||
* @brief Get the root entity (after parsing).
|
||||
* @return Pointer to the root entity or NULL when there is no root entity.
|
||||
*/
|
||||
const CRootEntity* Root() const;
|
||||
|
||||
/**
|
||||
* @brief Generate a name for anonymous entities (structs, unions and enums with declarations can be anonymous; unions within
|
||||
* structs can be transparent - having an anonymous declaration).
|
||||
* @param[in] rssPrefix The prefix to use to generate an automatic name.
|
||||
* @return Returns the name of the anonymous entity consisting of a name part "prefix_" with a unique number.
|
||||
*/
|
||||
std::string GenerateAnonymousEntityName(const std::string& rssPrefix);
|
||||
|
||||
/**
|
||||
* @brief Structure containing the meta data token
|
||||
*/
|
||||
struct SMetaToken
|
||||
{
|
||||
CToken tokenMeta; ///< Meta data token
|
||||
CTokenList lstComments; ///< If available list of preceding comments.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get and remove the meta data collected until this point.
|
||||
* @return List of meta tokens.
|
||||
*/
|
||||
std::list<SMetaToken> GetAndRemoveMeta();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Prepare the parsing
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ParserPrepare(CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Insert whitespace. Overload of ILexerCallback::InsertWhitespace.
|
||||
* @param[in] rtoken Reference to the token structure pointing to the whitespace.
|
||||
*/
|
||||
virtual void InsertWhitespace(const CToken &rtoken) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a comment, either standalone, belonging to the last statement or belonging to the next statement. Overload
|
||||
* of ILexerCallback::InsertComment.
|
||||
* @param[in] rtoken Reference to the token structure pointing to the comment.
|
||||
*/
|
||||
virtual void InsertComment(const CToken& rtoken) override;
|
||||
|
||||
/**
|
||||
* @brief Process a preprocessor directive. Overload of ILexerCallback::ProcessPreprocDirective.
|
||||
* @param[in] rCode Reference to the code.
|
||||
*/
|
||||
virtual void ProcessPreprocDirective(CCodePos& rCode) override;
|
||||
|
||||
/**
|
||||
* @brief Get a token from the lexer with moving the current position.
|
||||
* @details Get a token from the lexer for the currently open file. Preprocessor directives are automatically executed causing
|
||||
* conditionally parsing the file, defining and undefining macros and opening include files.
|
||||
* @throws Throws CCompileException on parse error.
|
||||
* @param[in] bPeekOnly When set, the function doesn't throw an error, but only returns an empty token when no code is
|
||||
* available any more.
|
||||
* @return Returns the token if there is any or an empty token when there is no token any more.
|
||||
*/
|
||||
CToken GetTokenFromLexer(bool bPeekOnly);
|
||||
|
||||
CLexer m_lexer; ///< The lexer.
|
||||
std::stack<CContextPtr> m_stackCode; ///< Stack with source code.
|
||||
std::set<std::filesystem::path> m_setProcessedFiles; ///< Set with processed files to prevent recurrency.
|
||||
std::vector<std::filesystem::path> m_vecSearchDirs; ///< Global search directories.
|
||||
const size_t m_nMaxDepth = 48; ///< At the most 48 files can be nested.
|
||||
CIdlCompilerEnvironment m_environment; ///< The environment that is used.
|
||||
CEntityPtr m_ptrRoot; ///< Root entity pointer.
|
||||
std::map<std::string, size_t> m_mapAutoNameCount; ///< Automatic name generator counter (for a specific
|
||||
///< prefix).
|
||||
CTokenList m_lstCacheTokenList; ///< Token list with pre-inserted tokens before the
|
||||
///< current position.
|
||||
std::filesystem::path m_pathFile; ///< Path to the file.
|
||||
CTokenList m_lstComments; ///< Comments are collected. Collection starts newly
|
||||
///< after each token.
|
||||
std::list<SMetaToken> m_lstMeta; ///< List of meta tokens and preceding comments.
|
||||
};
|
||||
|
||||
#endif // !defined PARSER_H
|
||||
733
sdv_executables/sdv_idl_compiler/preproc.cpp
Normal file
733
sdv_executables/sdv_idl_compiler/preproc.cpp
Normal file
@@ -0,0 +1,733 @@
|
||||
#include "preproc.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "exception.h"
|
||||
#include "source.h"
|
||||
#include <functional>
|
||||
#include "logger.h"
|
||||
|
||||
CPreprocessor::CPreprocessor(CParser& rParser) : m_rParser(rParser)
|
||||
{}
|
||||
|
||||
CToken CPreprocessor::ProcessPreproc(CCodePos& rCode, const CContextPtr& rptrContext)
|
||||
{
|
||||
// Use a local copy of the code (used when parsing preprocessor commands)
|
||||
CCodePos codePreproc(rCode);
|
||||
SLexerDummyCallback sCallback;
|
||||
CLexer lexerPreproc(&sCallback, m_rParser.GetEnvironment().CaseSensitiveTypeExtension(), CLexer::ELexingMode::lexing_preproc);
|
||||
CToken tokenMeta;
|
||||
|
||||
// // Check for a preprocessor directive
|
||||
// CToken sPreprocLine = m_rParser.GetLexer().HasPreprocessor(rCode);
|
||||
// if (!sPreprocLine) return;
|
||||
|
||||
// Expecting the number sign
|
||||
if (lexerPreproc.GetToken(codePreproc, rptrContext) != "#")
|
||||
throw CCompileException(codePreproc.GetLocation(), "Expected preprocessor directive sign.");
|
||||
|
||||
// Get an identifier
|
||||
CToken sKeyword = lexerPreproc.GetToken(codePreproc, rptrContext);
|
||||
if (sKeyword == "define")
|
||||
{
|
||||
// Skip whitespace (space and tab)
|
||||
while (*rCode == ' ' || *rCode == '\t') ++rCode;
|
||||
|
||||
tokenMeta = codePreproc.GetLocation(ETokenType::token_meta);
|
||||
tokenMeta.SetContext(rptrContext);
|
||||
ProcessDefine(lexerPreproc, codePreproc, rptrContext);
|
||||
codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_define);
|
||||
}
|
||||
else if (sKeyword == "if")
|
||||
{
|
||||
ProcessIf(lexerPreproc, codePreproc, rptrContext);
|
||||
}
|
||||
else if (sKeyword == "ifdef")
|
||||
{
|
||||
ProcessIfDef(lexerPreproc, codePreproc, rptrContext, false);
|
||||
}
|
||||
else if (sKeyword == "ifndef")
|
||||
{
|
||||
ProcessIfDef(lexerPreproc, codePreproc, rptrContext, true);
|
||||
}
|
||||
else if (sKeyword == "elif")
|
||||
{
|
||||
ProcessElif(lexerPreproc, codePreproc, rptrContext);
|
||||
}
|
||||
else if (sKeyword == "else")
|
||||
{
|
||||
ProcessElse(lexerPreproc, codePreproc, rptrContext);
|
||||
}
|
||||
else if (sKeyword == "endif")
|
||||
{
|
||||
ProcessEndif(lexerPreproc, codePreproc, rptrContext);
|
||||
}
|
||||
else if (sKeyword == "undef")
|
||||
{
|
||||
// Skip whitespace (space and tab)
|
||||
while (*rCode == ' ' || *rCode == '\t') ++rCode;
|
||||
|
||||
tokenMeta = codePreproc.GetLocation(ETokenType::token_meta);
|
||||
tokenMeta.SetContext(rptrContext);
|
||||
ProcessUndef(lexerPreproc, codePreproc, rptrContext);
|
||||
codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_undef);
|
||||
}
|
||||
else if (sKeyword == "include")
|
||||
{
|
||||
bool bLocal = false;
|
||||
tokenMeta = codePreproc.GetLocation(ETokenType::token_meta);
|
||||
tokenMeta.SetContext(rptrContext);
|
||||
ProcessInclude(lexerPreproc, codePreproc, rptrContext, bLocal);
|
||||
codePreproc.UpdateLocation(tokenMeta,
|
||||
bLocal ? ETokenMetaType::token_meta_include_local : ETokenMetaType::token_meta_include_global);
|
||||
}
|
||||
else if (sKeyword == "verbatim")
|
||||
{
|
||||
// Skip whitespace (space and tab)
|
||||
while (*codePreproc == ' ' || *codePreproc == '\t') ++codePreproc;
|
||||
|
||||
// The rest of the line is verbatim text.
|
||||
tokenMeta = codePreproc.GetLocation(ETokenType::token_meta);
|
||||
tokenMeta.SetContext(rptrContext);
|
||||
ProcessVerbatim(lexerPreproc, codePreproc, rptrContext);
|
||||
codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_verbatim);
|
||||
}
|
||||
else if (sKeyword == "verbatim_begin")
|
||||
{
|
||||
// Skip the rest of the line and the newline
|
||||
lexerPreproc.SkipLine(codePreproc);
|
||||
if (*codePreproc == '\r') ++codePreproc;
|
||||
if (*codePreproc == '\n') ++codePreproc;
|
||||
|
||||
// The rest until the end of the block is verbatim.
|
||||
tokenMeta = codePreproc.GetLocation(ETokenType::token_meta);
|
||||
tokenMeta.SetContext(rptrContext);
|
||||
ProcessVerbatimBlock(lexerPreproc, codePreproc, rptrContext);
|
||||
codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_verbatim);
|
||||
|
||||
// Skip the "#verbatim_end" directive.
|
||||
lexerPreproc.SkipLine(codePreproc);
|
||||
}
|
||||
else if (sKeyword == "verbatim_end")
|
||||
{
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (CurrentSectionEnabled())
|
||||
throw CCompileException(sKeyword, "Preprocessor directive \"#verbatim_end\" without \"verbatim_begin\" found.");
|
||||
}
|
||||
else if (sKeyword == "pragma")
|
||||
{
|
||||
// TODO: Add line to code generating target
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (CurrentSectionEnabled())
|
||||
throw CCompileException(sKeyword, "Unsupported preprocessor directive \"#pragma\" found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (CurrentSectionEnabled())
|
||||
throw CCompileException(sKeyword, "Unknown preprocessor directive.");
|
||||
}
|
||||
|
||||
// Skip the rest of the line
|
||||
lexerPreproc.SkipLine(codePreproc);
|
||||
|
||||
// Store the new position
|
||||
rCode = codePreproc;
|
||||
|
||||
// Return the meta token if available
|
||||
return CurrentSectionEnabled() ? tokenMeta : CToken();
|
||||
}
|
||||
|
||||
void CPreprocessor::FinalProcessing(const CCodePos& rCode)
|
||||
{
|
||||
if (!m_stackConditional.empty())
|
||||
throw CCompileException(rCode.GetLocation(), "Missing '#endif' directive.");
|
||||
}
|
||||
|
||||
bool CPreprocessor::CurrentSectionEnabled() const
|
||||
{
|
||||
return m_stackConditional.empty() || m_stackConditional.top().m_eProcessingControl == EConditionalProcessing::current;
|
||||
}
|
||||
|
||||
// Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore
|
||||
// the parameter cannot be a const reference.
|
||||
// cppcheck-suppress constParameterReference
|
||||
void CPreprocessor::ProcessDefine(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext)
|
||||
{
|
||||
CLog log("Preprocess #define...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (!CurrentSectionEnabled())
|
||||
{
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the macro name
|
||||
CToken tokenName = rLexer.GetToken(rCode, rptrContext);
|
||||
if (!tokenName || tokenName.GetType() != ETokenType::token_identifier)
|
||||
throw CCompileException(rCode.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 tokenSymbol;
|
||||
if (*rCode == '(')
|
||||
{
|
||||
// Get the left parenthesis as a token.
|
||||
tokenSymbol = rLexer.GetToken(rCode, rptrContext);
|
||||
|
||||
// Parameter bracket has been provided
|
||||
bHasParam = true;
|
||||
if (tokenSymbol != "(")
|
||||
throw CCompileException(tokenSymbol, "Invalid character for macro definition; expecting '('.");
|
||||
|
||||
// Read zero or more parameter
|
||||
bool bDone = false;
|
||||
bool bInitial = true;
|
||||
while (!bDone)
|
||||
{
|
||||
// Check for closing bracket
|
||||
tokenSymbol = rLexer.GetToken(rCode, rptrContext);
|
||||
if (bInitial && tokenSymbol == ")")
|
||||
bDone = true;
|
||||
else if (tokenSymbol.GetType() != ETokenType::token_identifier)
|
||||
throw CCompileException(tokenSymbol, "Expecting a parameter name.");
|
||||
|
||||
bInitial = false;
|
||||
if (!bDone)
|
||||
{
|
||||
// The token should represent an identifier
|
||||
CToken sIdentifierToken = tokenSymbol;
|
||||
|
||||
// Check for duplicates
|
||||
if (std::find(vecParams.begin(), vecParams.end(), static_cast<std::string>(sIdentifierToken)) != vecParams.end())
|
||||
throw CCompileException(tokenSymbol, "Duplicate parameter names for macro definition.");
|
||||
|
||||
// Add the parameter to the list
|
||||
vecParams.push_back(sIdentifierToken);
|
||||
|
||||
// Check for a comma or a right parenthesis
|
||||
tokenSymbol = rLexer.GetToken(rCode, rptrContext);
|
||||
if (tokenSymbol == ")")
|
||||
bDone = true;
|
||||
else if (tokenSymbol != ",")
|
||||
throw CCompileException(tokenSymbol, "Unexpected symbol in parameter list.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and store the macro structure
|
||||
CToken tokenValue = rCode.GetLocation();
|
||||
rLexer.SkipLine(rCode);
|
||||
rCode.UpdateLocation(tokenValue);
|
||||
CMacro macro(static_cast<std::string>(tokenName).c_str(),
|
||||
bHasParam ? &vecParams : nullptr, static_cast<std::string>(tokenValue).c_str());
|
||||
|
||||
// Log information
|
||||
log << "Macro definition = " << static_cast<std::string>(tokenName);
|
||||
if (bHasParam)
|
||||
{
|
||||
log << "(";
|
||||
bool bInitial = true;
|
||||
for (const std::string& rssParam : vecParams)
|
||||
{
|
||||
if (bInitial) log << ", ";
|
||||
log << rssParam;
|
||||
bInitial = false;
|
||||
}
|
||||
log << ")";
|
||||
}
|
||||
log << " " << static_cast<std::string>(tokenValue) << std::endl;
|
||||
|
||||
// Add the macro
|
||||
GetEnvironment().AddDefinition(rCode.GetLocation(), macro);
|
||||
}
|
||||
|
||||
// Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore
|
||||
// the parameter cannot be a const reference.
|
||||
// cppcheck-suppress constParameterReference
|
||||
void CPreprocessor::ProcessUndef(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext)
|
||||
{
|
||||
CLog log("Preprocess #undef...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (!CurrentSectionEnabled())
|
||||
{
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the macro name
|
||||
CToken tokenName = rLexer.GetToken(rCode, rptrContext);
|
||||
if (!tokenName || tokenName.GetType() != ETokenType::token_identifier)
|
||||
throw CCompileException(rCode.GetLocation(), "Definition name missing.");
|
||||
|
||||
// Log information
|
||||
log << "Macro definition = " << static_cast<std::string>(tokenName) << std::endl;
|
||||
|
||||
GetEnvironment().RemoveDefinition(static_cast<std::string>(tokenName).c_str());
|
||||
}
|
||||
|
||||
void CPreprocessor::ProcessInclude(CLexer& /*rLexer*/, CCodePos& rCode, const CContextPtr& rptrContext, bool& rbLocal)
|
||||
{
|
||||
CLog log("Preprocess #include...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (!CurrentSectionEnabled())
|
||||
{
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: The file name could contain characters that might be interpreted as escape character. Do not use the lexer to read
|
||||
// the path.
|
||||
|
||||
// Skip whitespace (tab and space)
|
||||
while (*rCode == ' ' || *rCode == '\t')
|
||||
++rCode;
|
||||
|
||||
// Get the next symbol
|
||||
CToken tokenSymbol = rCode.GetLocation();
|
||||
tokenSymbol.SetContext(rptrContext);
|
||||
switch (*rCode)
|
||||
{
|
||||
case '<': rbLocal = false; break;
|
||||
case '\"': rbLocal = true; break;
|
||||
default:
|
||||
throw CCompileException(tokenSymbol, "Invalid symbol.");
|
||||
}
|
||||
++rCode;
|
||||
|
||||
// Get the path
|
||||
CToken tokenPath = rCode.GetLocation();
|
||||
tokenPath.SetContext(rptrContext);
|
||||
bool bDone = false;
|
||||
while (!bDone)
|
||||
{
|
||||
switch (*rCode)
|
||||
{
|
||||
case '\0':
|
||||
throw CCompileException(tokenPath, "Unexpected end of file.");
|
||||
case '\r':
|
||||
case '\n':
|
||||
throw CCompileException(tokenPath, "Unexpected end of line.");
|
||||
case '\"':
|
||||
if (rbLocal)
|
||||
bDone = true;
|
||||
else
|
||||
++rCode;
|
||||
break;
|
||||
case '>':
|
||||
if (!rbLocal)
|
||||
bDone = true;
|
||||
else
|
||||
++rCode;
|
||||
break;
|
||||
default:
|
||||
++rCode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rCode.UpdateLocation(tokenPath);
|
||||
if (!tokenPath) throw CCompileException(tokenPath, "No filename supplied.");
|
||||
|
||||
// Skipping closing symbol
|
||||
++rCode;
|
||||
|
||||
// Find the file
|
||||
std::filesystem::path pathConfirmedFile;
|
||||
|
||||
// If a local filename has been provided, look locally first
|
||||
if (rbLocal)
|
||||
{
|
||||
// Determine the local path.
|
||||
std::filesystem::path pathSourceDir = tokenSymbol.GetContext()->Source().GetPathRef().parent_path();
|
||||
std::filesystem::path pathIncludeFile = static_cast<std::string>(tokenPath);
|
||||
std::filesystem::path pathLocalInclude = pathIncludeFile.is_absolute() ? pathIncludeFile : pathSourceDir / pathIncludeFile;
|
||||
|
||||
if (std::filesystem::exists(pathLocalInclude))
|
||||
pathConfirmedFile = pathLocalInclude;
|
||||
}
|
||||
|
||||
// If no file yet, run through the include directories and try to find a fitting file.
|
||||
if (pathConfirmedFile.empty())
|
||||
{
|
||||
std::vector<std::filesystem::path> vecIncludeDirs = GetEnvironment().GetIncludeDirs();
|
||||
for (const std::filesystem::path &rpathDir : vecIncludeDirs)
|
||||
{
|
||||
std::filesystem::path pathFile = rpathDir / static_cast<std::string>(tokenPath);
|
||||
if (std::filesystem::exists(pathFile))
|
||||
{
|
||||
pathConfirmedFile = pathFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// File found?
|
||||
if (pathConfirmedFile.empty()) throw CCompileException(tokenSymbol, "Include file not found.");
|
||||
|
||||
// Log information
|
||||
log << "Statement = " << (rbLocal ? "\"" : "<") << static_cast<std::string>(tokenPath) << (rbLocal ? "\"" : ">") << std::endl;
|
||||
log << "Path = " << pathConfirmedFile.generic_u8string() << std::endl;
|
||||
|
||||
// Parse the source file
|
||||
IncludeFile(pathConfirmedFile, rbLocal);
|
||||
}
|
||||
|
||||
void CPreprocessor::ProcessIf(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext)
|
||||
{
|
||||
CLog log("Preprocess #if...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
EConditionalProcessing eProcessingControl = EConditionalProcessing::disabled;
|
||||
if (CurrentSectionEnabled())
|
||||
{
|
||||
// Calculate the conditional value
|
||||
CToken token = rCode.GetLocation();
|
||||
bool bResult = ConditionalCalc(rLexer, rCode, rptrContext) ? true : false;
|
||||
rCode.UpdateLocation(token);
|
||||
eProcessingControl = bResult ? EConditionalProcessing::current : EConditionalProcessing::future;
|
||||
|
||||
log << "Condition = " << static_cast<std::string>(token) << std::endl;
|
||||
log << "State = " << (bResult ? "currently processed" : "currently not processed");
|
||||
} else
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
|
||||
// Create a new section control
|
||||
m_stackConditional.push(SConditionalControl{EConditionalInclusion::if_section, eProcessingControl});
|
||||
}
|
||||
|
||||
// Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore
|
||||
// the parameter cannot be a const reference.
|
||||
// cppcheck-suppress constParameterReference
|
||||
void CPreprocessor::ProcessIfDef(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, bool bInverted)
|
||||
{
|
||||
CLog log("Preprocess #ifdef...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
EConditionalProcessing eProcessingControl = EConditionalProcessing::disabled;
|
||||
if (CurrentSectionEnabled())
|
||||
{
|
||||
// Check for the macro name
|
||||
CToken tokenName = rLexer.GetToken(rCode, rptrContext);
|
||||
if (!tokenName || tokenName.GetType() != ETokenType::token_identifier)
|
||||
throw CCompileException(rCode.GetLocation(), "Definition name missing.");
|
||||
bool bDefined = GetEnvironment().Defined(static_cast<std::string>(tokenName).c_str());
|
||||
eProcessingControl = (bInverted ? !bDefined : bDefined) ?
|
||||
EConditionalProcessing::current : EConditionalProcessing::future;
|
||||
|
||||
log << "Definition name = " << static_cast<std::string>(tokenName) << std::endl;
|
||||
log << "Definition " << (bDefined ? "exists" : "doesn't exist") << std::endl;
|
||||
} else
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
|
||||
// Create a new section control
|
||||
m_stackConditional.push(SConditionalControl{EConditionalInclusion::if_section, eProcessingControl});
|
||||
}
|
||||
|
||||
void CPreprocessor::ProcessElif(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext)
|
||||
{
|
||||
CLog log("Preprocess #elif...");
|
||||
|
||||
// Currently in a conditional section?
|
||||
if (m_stackConditional.empty())
|
||||
throw CCompileException(rCode.GetLocation(), "Unexpected '#elif' directive.");
|
||||
|
||||
// Currently in an else-section?
|
||||
if (m_stackConditional.top().m_eInclusionControl == EConditionalInclusion::else_section)
|
||||
throw CCompileException(rCode.GetLocation(), "Duplicate '#elif' directive.");
|
||||
|
||||
switch (m_stackConditional.top().m_eProcessingControl)
|
||||
{
|
||||
case EConditionalProcessing::future:
|
||||
{
|
||||
CToken token = rCode.GetLocation();
|
||||
bool bResult = ConditionalCalc(rLexer, rCode, rptrContext) ? true : false;
|
||||
rCode.UpdateLocation(token);
|
||||
log << "Condition = " << static_cast<std::string>(token) << std::endl;
|
||||
// Check whether the condition is true
|
||||
if (bResult)
|
||||
{
|
||||
m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::current;
|
||||
log << "State = currently processed" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
log << "State = currently not processed" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConditionalProcessing::current:
|
||||
m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::previous;
|
||||
log << "State = currently not processed" << std::endl;
|
||||
break;
|
||||
default: // Do nothing... disabled and previous are valid values.
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPreprocessor::ProcessElse(CLexer& /*rLexer*/, const CCodePos& rCode, const CContextPtr& /*rptrContext*/)
|
||||
{
|
||||
CLog log("Preprocess #else...");
|
||||
|
||||
// Currently in a conditional section?
|
||||
if (m_stackConditional.empty())
|
||||
throw CCompileException(rCode.GetLocation(), "Unexpected '#else' directive.");
|
||||
|
||||
// Currently in an else-section?
|
||||
if (m_stackConditional.top().m_eInclusionControl == EConditionalInclusion::else_section)
|
||||
throw CCompileException(rCode.GetLocation(), "Duplicate '#else' directive.");
|
||||
|
||||
// Update the current state
|
||||
m_stackConditional.top().m_eInclusionControl = EConditionalInclusion::else_section;
|
||||
switch (m_stackConditional.top().m_eProcessingControl)
|
||||
{
|
||||
case EConditionalProcessing::future:
|
||||
m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::current;
|
||||
log << "State = currently processed" << std::endl;
|
||||
break;
|
||||
case EConditionalProcessing::current:
|
||||
m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::previous;
|
||||
log << "State = currently not processed" << std::endl;
|
||||
break;
|
||||
default: // Do nothing... disabled and previous are valid values.
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPreprocessor::ProcessEndif(CLexer& /*rLexer*/, const CCodePos& rCode, const CContextPtr& /*rptrContext*/)
|
||||
{
|
||||
CLog log("Preprocess #endif...");
|
||||
|
||||
// Currently in a conditional section?
|
||||
if (m_stackConditional.empty())
|
||||
throw CCompileException(rCode.GetLocation(), "Unexpected '#endif' directive.");
|
||||
|
||||
// End the section
|
||||
m_stackConditional.pop();
|
||||
}
|
||||
|
||||
// Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore
|
||||
// the parameter cannot be a const reference.
|
||||
// Older version: const parameter for rLexer is also not possible.
|
||||
// cppcheck-suppress constParameterReference
|
||||
// cppcheck-suppress constParameter
|
||||
void CPreprocessor::ProcessVerbatim(CLexer& rLexer, CCodePos& rCode, const CContextPtr& /*rptrContext*/)
|
||||
{
|
||||
CLog log("Preprocess #verbatim...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (!CurrentSectionEnabled())
|
||||
{
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
rLexer.SkipLine(rCode);
|
||||
}
|
||||
|
||||
// Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore
|
||||
// the parameter cannot be a const reference.
|
||||
// cppcheck-suppress constParameterReference
|
||||
void CPreprocessor::ProcessVerbatimBlock(CLexer& rLexer, CCodePos& rCode, const CContextPtr& /*rptrContext*/)
|
||||
{
|
||||
CLog log("Preprocess #verbatim_begin - #verbatim_end...");
|
||||
|
||||
// Check whether to ignore directive based on the current section.
|
||||
if (!CurrentSectionEnabled())
|
||||
{
|
||||
log << "Not included in the current section..." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check until end of code or end of file
|
||||
while (true)
|
||||
{
|
||||
// Check when done
|
||||
if (!static_cast<const char*>(rCode)) throw CCompileException(rCode.GetLocation(), "Missing '#verbatim_end' directive.");
|
||||
if (!*rCode) throw CCompileException(rCode.GetLocation(), "Missing '#verbatim_end' directive.");
|
||||
|
||||
// Store the current position at the beginning of the line
|
||||
CCodePos posBackup = rCode;
|
||||
|
||||
// Check for #verbatim_end (whitespace could exist before and after the number sign '#').
|
||||
while (*rCode == ' ' || *rCode == '\t') ++rCode;
|
||||
if (*rCode == '#')
|
||||
{
|
||||
++rCode;
|
||||
while (*rCode == ' ' || *rCode == '\t') ++rCode;
|
||||
if (std::strncmp(rCode, "verbatim_end", 12) == 0)
|
||||
{
|
||||
rCode = posBackup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the current line
|
||||
rLexer.SkipLine(rCode);
|
||||
while (*rCode == '\r' || *rCode == '\n') ++rCode;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t CPreprocessor::ConditionalCalc(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, uint32_t uiPrecedence /*= 100*/)
|
||||
{
|
||||
// Read a token
|
||||
CToken token = rLexer.GetToken(rCode, rptrContext);
|
||||
|
||||
// Token can be either a left parenthesis, a unary operator (precedence 0), an identifier (defined, macro or unknown), or a
|
||||
// number.
|
||||
int64_t iLValue = 0;
|
||||
if (token == "(")
|
||||
{
|
||||
// Calculate the content between the parenthesis.
|
||||
iLValue = ConditionalCalc(rLexer, rCode, rptrContext);
|
||||
|
||||
// Expecting a closing parenthesis
|
||||
token = rLexer.GetToken(rCode, rptrContext);
|
||||
if (token != ")")
|
||||
throw CCompileException(token, "Expecting a right parenthesis.");
|
||||
} else if (token == "!")
|
||||
{
|
||||
// Calculate the next token an use logical NOT on this.
|
||||
iLValue = ConditionalCalc(rLexer, rCode, 0) ? 0 : 1;
|
||||
} else if (token == "~")
|
||||
{
|
||||
// Calculate the next token an use bitwise NOT on this.
|
||||
iLValue = ~ConditionalCalc(rLexer, rCode, 0);
|
||||
} else if (token == "defined")
|
||||
{
|
||||
// The 'defined' identifier is used in two ways
|
||||
// - function line: defined(macro)
|
||||
// - unary operator: defined macro
|
||||
token = rLexer.GetToken(rCode, rptrContext);
|
||||
if (token == "(")
|
||||
{
|
||||
// Get the macro name
|
||||
token = rLexer.GetToken(rCode, rptrContext);
|
||||
if (token.GetType() != ETokenType::token_identifier)
|
||||
throw CCompileException(token, "Expecting macro name between parenthesis of defined operator");
|
||||
iLValue = GetEnvironment().Defined(static_cast<std::string>(token).c_str()) ? 1 : 0;
|
||||
token = rLexer.GetToken(rCode, rptrContext);
|
||||
if (token != ")") throw CCompileException(token, "Expecting right parenthesis");
|
||||
} else
|
||||
{
|
||||
if (token.GetType() != ETokenType::token_identifier)
|
||||
throw CCompileException(token, "Expecting macro name following defined operator");
|
||||
iLValue = GetEnvironment().Defined(static_cast<std::string>(token).c_str()) ? 1 : 0;
|
||||
}
|
||||
} else if (token == "true" || token == "TRUE")
|
||||
{
|
||||
iLValue = 1;
|
||||
} else if (token == "false" || token == "FALSE")
|
||||
{
|
||||
iLValue = 0;
|
||||
} else if (token.GetType() == ETokenType::token_identifier)
|
||||
{
|
||||
// Test and expand any macros
|
||||
if (GetEnvironment().TestAndExpand(token, rCode))
|
||||
iLValue = ConditionalCalc(rLexer, rCode, rptrContext, uiPrecedence);
|
||||
else
|
||||
iLValue = 0;
|
||||
} else if (token.GetType() == ETokenType::token_literal)
|
||||
{
|
||||
// Only integer and character literals can be interpreted. Additional interpretation characters are not supported.
|
||||
// Anything other will result in the value 0.
|
||||
if (token.c_str()[0] == '\'')
|
||||
{
|
||||
if (token.c_str()[1] == '\\')
|
||||
{
|
||||
switch (token.c_str()[2])
|
||||
{
|
||||
case '\"': iLValue = static_cast<int64_t>('\"'); break;
|
||||
case '\'': iLValue = static_cast<int64_t>('\''); break;
|
||||
case '?': iLValue = static_cast<int64_t>('?'); break;
|
||||
case '\\': iLValue = static_cast<int64_t>('\\'); break;
|
||||
case 'a': iLValue = static_cast<int64_t>('a'); break;
|
||||
case 'b': iLValue = static_cast<int64_t>('b'); break;
|
||||
case 'f': iLValue = static_cast<int64_t>('f'); break;
|
||||
case 'n': iLValue = static_cast<int64_t>('n'); break;
|
||||
case 'r': iLValue = static_cast<int64_t>('r'); break;
|
||||
case 't': iLValue = static_cast<int64_t>('t'); break;
|
||||
case 'v': iLValue = static_cast<int64_t>('v'); break;
|
||||
default:
|
||||
iLValue = 0;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
iLValue = static_cast<int64_t>(token.c_str()[1]);
|
||||
} else
|
||||
iLValue = static_cast<int64_t>(std::atoll(token.c_str()));
|
||||
} else
|
||||
throw CCompileException(token, "Unexpected token '", static_cast<std::string>(token), "'.");
|
||||
|
||||
// Run until all operations at this or any higher precedence have been executed
|
||||
bool bDone = false;
|
||||
while (!bDone)
|
||||
{
|
||||
// Peek for the next operator and decide on precedence...
|
||||
token = rLexer.GetToken(rCode, rptrContext);
|
||||
|
||||
// If no token, we're done.
|
||||
if (!token) return iLValue;
|
||||
|
||||
// If right parenthesis, replace the parenthesis and we're done.
|
||||
if (token == ")")
|
||||
{
|
||||
rCode.PrependCode(token);
|
||||
break;
|
||||
}
|
||||
|
||||
// Conditionally calculate the result based on the supplied operator function and the precedence level.
|
||||
auto fnCalculate = [&](std::function<int64_t(int64_t, int64_t)> fnCalculation, uint32_t uiOperatorPrecedence,
|
||||
bool bCheckDiv = false)
|
||||
{
|
||||
// If operator has höhere oder gleiche precedence, replace operator and we're done.
|
||||
if (uiOperatorPrecedence >= uiPrecedence)
|
||||
{
|
||||
rCode.PrependCode(token);
|
||||
bDone = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If operator has niedriger precedence, get next token and calculate result.
|
||||
CToken tokenTemp = token;
|
||||
int64_t iRValue = ConditionalCalc(rLexer, rCode, rptrContext, uiOperatorPrecedence);
|
||||
|
||||
// Check for division by zero.
|
||||
if (bCheckDiv && !iRValue) throw CCompileException(tokenTemp, "Division by zero.");
|
||||
|
||||
// Calculate the result.
|
||||
iLValue = fnCalculation(iLValue, iRValue);
|
||||
};
|
||||
|
||||
// Check for the operator and calculate the result
|
||||
if (token == "*") { fnCalculate([](int64_t i1, int64_t i2){ return i1 * i2; }, 1); continue; }
|
||||
if (token == "/") { fnCalculate([](int64_t i1, int64_t i2){ return i1 / i2; }, 1, true); continue; }
|
||||
if (token == "%") { fnCalculate([](int64_t i1, int64_t i2){ return i1 % i2; }, 1, true); continue; }
|
||||
if (token == "+") { fnCalculate([](int64_t i1, int64_t i2){ return i1 + i2; }, 2); continue; }
|
||||
if (token == "-") { fnCalculate([](int64_t i1, int64_t i2){ return i1 - i2; }, 2); continue; }
|
||||
if (token == "<<") { fnCalculate([](int64_t i1, int64_t i2){ return i1 << i2; }, 3); continue; }
|
||||
if (token == ">>") { fnCalculate([](int64_t i1, int64_t i2){ return i1 >> i2; }, 3); continue; }
|
||||
if (token == "<") { fnCalculate([](int64_t i1, int64_t i2){ return i1 < i2; }, 4); continue; }
|
||||
if (token == "<=") { fnCalculate([](int64_t i1, int64_t i2){ return i1 <= i2; }, 4); continue; }
|
||||
if (token == ">") { fnCalculate([](int64_t i1, int64_t i2){ return i1 > i2; }, 4); continue; }
|
||||
if (token == ">=") { fnCalculate([](int64_t i1, int64_t i2){ return i1 >= i2; }, 4); continue; }
|
||||
if (token == "==") { fnCalculate([](int64_t i1, int64_t i2){ return i1 == i2; }, 5); continue; }
|
||||
if (token == "!=") { fnCalculate([](int64_t i1, int64_t i2){ return i1 != i2; }, 5); continue; }
|
||||
if (token == "&") { fnCalculate([](int64_t i1, int64_t i2){ return i1 & i2; }, 6); continue; }
|
||||
if (token == "^") { fnCalculate([](int64_t i1, int64_t i2){ return i1 ^ i2; }, 7); continue; }
|
||||
if (token == "|") { fnCalculate([](int64_t i1, int64_t i2){ return i1 | i2; }, 8); continue; }
|
||||
if (token == "&&") { fnCalculate([](int64_t i1, int64_t i2){ return i1 && i2; }, 9); continue; }
|
||||
if (token == "||") { fnCalculate([](int64_t i1, int64_t i2){ return i1 || i2; }, 10); continue; }
|
||||
|
||||
// If not an operator, this is an error
|
||||
throw CCompileException(token, "Expecting operator");
|
||||
};
|
||||
|
||||
return iLValue;
|
||||
}
|
||||
|
||||
239
sdv_executables/sdv_idl_compiler/preproc.h
Normal file
239
sdv_executables/sdv_idl_compiler/preproc.h
Normal file
@@ -0,0 +1,239 @@
|
||||
#ifndef PREPROC_H
|
||||
#define PREPROC_H
|
||||
|
||||
#include "token.h"
|
||||
#include "codepos.h"
|
||||
#include "environment.h"
|
||||
#include <filesystem>
|
||||
|
||||
// Forward declaration
|
||||
class CLexer;
|
||||
class CParser;
|
||||
|
||||
/**
|
||||
* @brief Preprocessing class
|
||||
*/
|
||||
class CPreprocessor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rParser Reference to the parser.
|
||||
*/
|
||||
CPreprocessor(CParser& rParser);
|
||||
|
||||
/**
|
||||
* @brief Process preprocessor line
|
||||
* @param[in] rCode Reference to the code holding the line the might contain a preprocessor direction.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
* @return Returns a meta token if one is available; an empty token otherwise.
|
||||
*/
|
||||
CToken ProcessPreproc(CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Checks whether currently a conditional section is being processed. If so, this function will trigger an exception
|
||||
* for a missing @c \#endif' directive.
|
||||
* @param[in] rCode Reference to the code holding the line the might contain a preprocessor direction.
|
||||
*/
|
||||
void FinalProcessing(const CCodePos& rCode);
|
||||
|
||||
/**
|
||||
* @brief Processing enabled. Part of conditional inclusion.
|
||||
* @remarks If processing is disabled, lines should be skipped. Preprocessor directives should be processed anyway.
|
||||
* @return Returns whether to process current code.
|
||||
*/
|
||||
bool CurrentSectionEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief Include a file. Function prototype to be implemented by the parser.
|
||||
* @param[in] rpath Reference to the source file path. Must be a valid path.
|
||||
* @param[in] bLocal When set, the include file is a local include. Otherwise it represents a global include.
|
||||
*/
|
||||
virtual void IncludeFile(const std::filesystem::path& rpath, bool bLocal) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the environment. Function prototype to be implemented by the parser.
|
||||
* @return Reference to the environment.
|
||||
*/
|
||||
virtual CIdlCompilerEnvironment& GetEnvironment() = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Process the define macro directive.
|
||||
* @details The code contains the 'define' directive followed by a macro name and optionally by zero or more parameters
|
||||
* between parenthesis and optionally the value the macro should be replaced with. The value could contain the operator '#'
|
||||
* preceding the parameter to stringificate the parameter content and the operator '##' preceding or succeeding the parameter
|
||||
* to concatenate the result to the identifier adjacent identifier.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the define keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessDefine(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the undef macro directive.
|
||||
* @details The code contains the 'undef' directive followed by a macro name.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the define keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessUndef(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the include directive.
|
||||
* @details The code contains 'include' directive followed by the file to include between quotes or between angle-brackets.
|
||||
* Filenames between quotes can be relative using the current directory as a start or absolute. First the current directory
|
||||
* is used to search for the file, then the directories of the provided search path is used to search for the file. Filenames
|
||||
* between angle-brackets must be relative. The search path is used to search for the file.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the define keyword.
|
||||
* @param[out] rbLocal When set, the include statement was referring to a local file; otherwise global include.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessInclude(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, bool& rbLocal);
|
||||
|
||||
/**
|
||||
* @brief Process the if directive.
|
||||
* @details The code contains the 'if' directive followed by a conditional expression.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the if or elif keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessIf(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the ifdef or ifndef directive.
|
||||
* @details The code contains the 'ifdef' or the 'ifndef' directive followed by a macro name.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the ifdef or ifndef keyword.
|
||||
* @param[in] bInverted When set, the ifndef directive should be used; otherwise the ifdef directive.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessIfDef(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, bool bInverted);
|
||||
|
||||
/**
|
||||
* @brief Process the elif directive.
|
||||
* @details The code contains the 'elif' directive followed by a conditional expression.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the if or elif keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessElif(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the else directive.
|
||||
* @details The code contains the 'else' directive.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the else keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessElse(CLexer& rLexer, const CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the endif directive.
|
||||
* @details The code contains the 'endif' directive.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the endif keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessEndif(CLexer& rLexer, const CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the verbatim directive.
|
||||
* @details The code contains the 'verbatim' directive.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the endif keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessVerbatim(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Process the verbatim block directives.
|
||||
* @details The code contains the 'verbatim_begin' and "verbatim_end" directives.
|
||||
* @param[in, out] rLexer Reference to the lexer to use for parsing.
|
||||
* @param[in, out] rCode Reference to the code following the endif keyword.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void ProcessVerbatimBlock(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext);
|
||||
|
||||
/**
|
||||
* @brief Calculate the conditional value based on the precedence of operators.
|
||||
* @details Parse through the condition string and calculate a value. Values and arithmetic operators are read and a result
|
||||
* is calculated as long as the operator precedence doesn't undercut the current precedence.
|
||||
* - macros are expanded
|
||||
* - defined operator can be used to check for the existence of a macro
|
||||
* - unknown identifiers are zero
|
||||
* - integers are casted to int64_t
|
||||
* - chars are casted to int64_t
|
||||
* - operators that are allowed + - / % * & | ^ ~ << >> && || ! > >= < <= == !=
|
||||
* - parenthesis are allowed
|
||||
*
|
||||
* Operator precedence:
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | precedence | operators | description |
|
||||
* +============+=================+===========================================+
|
||||
* | 0 | ~ ! ( ) defined | Bitwise NOT, logical NOT and parenthesis |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 1 | * / % | Multiplication, division, and remainder |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 2 | + - | Addition and subtraction |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 3 | << >> | Bitwise left shift and right shift |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 4 | < <= > >= | Relational operators |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 5 | == != | Equality operators |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 6 | & | Bitwise AND |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 7 | ^ | Bitwise XOR |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 8 | | | Bitwise OR |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 9 | && | Logical AND |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* | 10 | || | Logical OR |
|
||||
* +------------+-----------------+-------------------------------------------+
|
||||
* @param[in, out] rLexer Reference to the lexer.
|
||||
* @param[in, out] rCode Reference to the code. The code position will be updated.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
* @param[in] uiPrecedence [in] Current precedence level (default 100).
|
||||
* @return Returns the calculated value
|
||||
*/
|
||||
int64_t ConditionalCalc(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, uint32_t uiPrecedence = 100);
|
||||
|
||||
/**
|
||||
* @brief Conditional inclusion control
|
||||
*/
|
||||
enum class EConditionalInclusion
|
||||
{
|
||||
if_section, ///< Within if section: endif, elif, else could be defined
|
||||
else_section ///< Within else section: endif could be defined
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Conditional processing control
|
||||
*/
|
||||
enum class EConditionalProcessing
|
||||
{
|
||||
disabled, ///< Disabled by a parent conditional section.
|
||||
current, ///< The current conditional section is being processed.
|
||||
previous, ///< A previous conditional section was processed.
|
||||
future ///< A future conditional section might be processed.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Conditional processing control.
|
||||
*/
|
||||
struct SConditionalControl
|
||||
{
|
||||
EConditionalInclusion m_eInclusionControl; ///< The current level of inclusion
|
||||
EConditionalProcessing m_eProcessingControl; ///< The current level of processing.
|
||||
};
|
||||
|
||||
CParser& m_rParser; ///< Reference to the parser
|
||||
std::stack<SConditionalControl> m_stackConditional; ///< Stack with the current valid
|
||||
};
|
||||
|
||||
|
||||
#endif // !defined PREPROC_H
|
||||
175
sdv_executables/sdv_idl_compiler/source.cpp
Normal file
175
sdv_executables/sdv_idl_compiler/source.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#ifdef GetClassInfo
|
||||
#undef GetClassInfo
|
||||
#endif
|
||||
#pragma pop_macro("interface")
|
||||
#endif
|
||||
|
||||
#include "source.h"
|
||||
#include "codepos.h"
|
||||
#include "exception.h"
|
||||
#include "token.h"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <cuchar>
|
||||
|
||||
CSource::CSource()
|
||||
{}
|
||||
|
||||
CSource::CSource(const std::filesystem::path& rpath)
|
||||
{
|
||||
ReadFile(rpath);
|
||||
}
|
||||
|
||||
CSource::CSource(const char* szCode)
|
||||
{
|
||||
if (!szCode) throw CCompileException("Cannot set code");
|
||||
|
||||
m_ssSource = szCode;
|
||||
m_path = std::filesystem::current_path() / "unknown.idl";
|
||||
}
|
||||
|
||||
CSource::CSource(CSource&& rSource) noexcept: m_path(std::move(rSource.m_path)), m_ssSource(std::move(rSource.m_ssSource))
|
||||
{}
|
||||
|
||||
CSource& CSource::operator=(CSource&& rSource) noexcept
|
||||
{
|
||||
m_path = std::move(rSource.m_path);
|
||||
m_ssSource = std::move(rSource.m_ssSource);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::filesystem::path& CSource::GetPathRef() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
const std::string& CSource::GetCodeRef() const
|
||||
{
|
||||
return m_ssSource;
|
||||
}
|
||||
|
||||
void CSource::ReadFile(const std::filesystem::path& rpath)
|
||||
{
|
||||
// Set the locale to work with UTF8
|
||||
std::setlocale(LC_ALL, "en_US.utf8");
|
||||
|
||||
if (rpath.empty())
|
||||
throw CCompileException("File name missing");
|
||||
|
||||
// Store the file name
|
||||
m_path = std::filesystem::absolute(rpath);
|
||||
|
||||
// Open the file
|
||||
std::ifstream fstream(m_path, std::ios_base::in | std::ios::binary);
|
||||
|
||||
// Is the file opened
|
||||
if (!fstream.is_open())
|
||||
throw CCompileException(m_path, CToken(), "Cannot open file");
|
||||
|
||||
// Get the size of the file
|
||||
fstream.seekg(0, std::ios::end);
|
||||
size_t nSize = fstream.tellg();
|
||||
|
||||
// Allocate space for the content
|
||||
std::unique_ptr<uint8_t[]> ptruiRawContent = std::make_unique<uint8_t[]>(nSize);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
||||
// exception was triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptruiRawContent)
|
||||
throw CCompileException(m_path, CToken(), "Cannot allocate buffer for reading the file");
|
||||
|
||||
// Read the file
|
||||
fstream.seekg(0);
|
||||
fstream.read(reinterpret_cast<char*>(ptruiRawContent.get()), nSize);
|
||||
if (static_cast<size_t>(fstream.gcount()) != nSize)
|
||||
throw CCompileException(m_path, CToken(), "Could not read complete file");
|
||||
|
||||
// Determine the BOM
|
||||
if (nSize >= 3 && ptruiRawContent[0] == 0xEF && ptruiRawContent[1] == 0xBB && ptruiRawContent[2] == 0xBF)
|
||||
{
|
||||
// UTF8 BOM - assign the string directly to the member
|
||||
if (nSize > 3)
|
||||
m_ssSource = std::string(reinterpret_cast<const char*>(ptruiRawContent.get()) + 3, nSize - 3);
|
||||
} else
|
||||
if (nSize >= 4 && ptruiRawContent[0] == 0x00 && ptruiRawContent[1] == 0x00 && ptruiRawContent[2] == 0xFE && ptruiRawContent[3] == 0xFF)
|
||||
{
|
||||
// UTF32 BE BOM - conversion needed
|
||||
char32_t* szBuffer = reinterpret_cast<char32_t*>(ptruiRawContent.get()) + 1;
|
||||
nSize = nSize / sizeof(char32_t) - 1;
|
||||
PotentialSwapBuffer(szBuffer, nSize, true);
|
||||
m_ssSource = ConvertToUTF8(szBuffer, nSize);
|
||||
} else
|
||||
if (nSize >= 4 && ptruiRawContent[0] == 0xFF && ptruiRawContent[1] == 0xFE && ptruiRawContent[2] == 0x00 && ptruiRawContent[3] == 0x00)
|
||||
{
|
||||
// UTF32 LE BOM - conversion needed
|
||||
char32_t* szBuffer = reinterpret_cast<char32_t*>(ptruiRawContent.get()) + 1;
|
||||
nSize = nSize / sizeof(char32_t) - 1;
|
||||
PotentialSwapBuffer(szBuffer, nSize, false);
|
||||
m_ssSource = ConvertToUTF8(szBuffer, nSize);
|
||||
} else
|
||||
if (nSize >= 2 && ptruiRawContent[0] == 0xFE && ptruiRawContent[1] == 0xFF)
|
||||
{
|
||||
// UTF16 BE BOM - conversion needed
|
||||
char16_t* szBuffer = reinterpret_cast<char16_t*>(ptruiRawContent.get()) + 1;
|
||||
nSize = nSize / sizeof(char16_t) - 1;
|
||||
PotentialSwapBuffer(szBuffer, nSize, true);
|
||||
m_ssSource = ConvertToUTF8(szBuffer, nSize);
|
||||
} else
|
||||
if (nSize >= 2 && ptruiRawContent[0] == 0xFF && ptruiRawContent[1] == 0xFE)
|
||||
{
|
||||
// UTF16 LE BOM - conversion needed
|
||||
char16_t* szBuffer = reinterpret_cast<char16_t*>(ptruiRawContent.get()) + 1;
|
||||
nSize = nSize / sizeof(char16_t) - 1;
|
||||
PotentialSwapBuffer(szBuffer, nSize, false);
|
||||
m_ssSource = ConvertToUTF8(szBuffer, nSize);
|
||||
} else
|
||||
{
|
||||
// No BOM - ANSI character set assumed
|
||||
m_ssSource = std::string(reinterpret_cast<const char*>(ptruiRawContent.get()), nSize);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool CSource::IsSystemBigEndian()
|
||||
{
|
||||
// Use a constant value to check for the byte order
|
||||
// False cppcheck warning; uiVal is seen as unused. This is not the case. Suppress warning
|
||||
// cppcheck-suppress unusedStructMember
|
||||
const union {uint16_t uiVal; uint8_t rguiBytes[2];} uTest{0x0011};
|
||||
|
||||
// Check for big-endian byte order.
|
||||
return uTest.rguiBytes[0] == 0x00 && uTest.rguiBytes[1] == 0x11;
|
||||
}
|
||||
|
||||
constexpr bool CSource::IsSystemLittleEndian()
|
||||
{
|
||||
// Use a constant value to check for the byte order
|
||||
// False cppcheck warning; uiVal is seen as unused. This is not the case. Suppress warning
|
||||
// cppcheck-suppress unusedStructMember
|
||||
const union {uint16_t uiVal; uint8_t rguiBytes[2];} uTest{0x0011};
|
||||
|
||||
// Check for big-endian byte order.
|
||||
return uTest.rguiBytes[0] == 0x11 && uTest.rguiBytes[1] == 0x00;
|
||||
}
|
||||
|
||||
std::string CSource::ConvertToUTF8(const char16_t* szBuffer, size_t nSize)
|
||||
{
|
||||
if (!szBuffer) return std::string();
|
||||
|
||||
// Return the result.
|
||||
return sdv::MakeUtf8String(szBuffer, nSize);
|
||||
}
|
||||
|
||||
std::string CSource::ConvertToUTF8(const char32_t* szBuffer, size_t nSize)
|
||||
{
|
||||
if (!szBuffer) return std::string();
|
||||
|
||||
// Return the result.
|
||||
return sdv::MakeUtf8String(szBuffer, nSize);
|
||||
}
|
||||
163
sdv_executables/sdv_idl_compiler/source.h
Normal file
163
sdv_executables/sdv_idl_compiler/source.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#ifndef SOURCE_H
|
||||
#define SOURCE_H
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief This class covers the source file reading
|
||||
*/
|
||||
class CSource
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CSource();
|
||||
|
||||
/**
|
||||
* @brief Constructor reading the source file.
|
||||
* @param[in] rpath Path to the source file.
|
||||
*/
|
||||
CSource(const std::filesystem::path& rpath);
|
||||
|
||||
/**
|
||||
* @brief Construct using the code, bypassing the reading of the file.
|
||||
* @param[in] szCode Zero terminated string containing the code.
|
||||
*/
|
||||
CSource(const char* szCode);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param[in] rSource Reference to the source object.
|
||||
*/
|
||||
CSource(const CSource& rSource) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rSource Reference to the source object.
|
||||
*/
|
||||
CSource(CSource&& rSource) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Assignment operator
|
||||
* @param[in] rSource Reference to the source object.
|
||||
* @return Reference to the source object.
|
||||
*/
|
||||
CSource& operator=(const CSource& rSource) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move operator
|
||||
* @param[in] rSource Reference to the source object.
|
||||
* @return Reference to the source object.
|
||||
*/
|
||||
CSource& operator=(CSource&& rSource) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the source file path (as reference).
|
||||
* @return The source file path.
|
||||
*/
|
||||
const std::filesystem::path& GetPathRef() const;
|
||||
|
||||
/**
|
||||
* @brief Get the source code (as reference).
|
||||
* @return The source code.
|
||||
*/
|
||||
const std::string& GetCodeRef() const;
|
||||
|
||||
/**
|
||||
* @brief Read a source file.
|
||||
* @param[in] rpath Path to the source file.
|
||||
*/
|
||||
void ReadFile(const std::filesystem::path& rpath);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Determine whether the system endianness is big-endian.
|
||||
* @return Returns 'true' when the system is a big-endian system. Otherwise returns 'false'.
|
||||
*/
|
||||
constexpr static bool IsSystemBigEndian();
|
||||
|
||||
/**
|
||||
* @brief Determine whether the system endianness is little-endian.
|
||||
* @return Returns 'true' when the system is a little-endian system. Otherwise returns 'false'.
|
||||
*/
|
||||
constexpr static bool IsSystemLittleEndian();
|
||||
|
||||
/**
|
||||
* @brief Swap the endianness of the character.
|
||||
* @param[in, out] rc16 Reference to the UTF16 character.
|
||||
*/
|
||||
static void EndianSwap(char16_t& rc16);
|
||||
|
||||
/**
|
||||
* @brief Swap the endianness of the character.
|
||||
* @param[in, out] rc32 Reference to the UTF32 character.
|
||||
*/
|
||||
static void EndianSwap(char32_t& rc32);
|
||||
|
||||
/**
|
||||
* @brief Potentially in-place swap the data in a buffer if necessary.
|
||||
* @tparam TChar the character type to use for swapping.
|
||||
* @param[in, out] szBuffer The buffer to swap the data for.
|
||||
* @param[in] nSize The size of the buffer in characters.
|
||||
* @param[in] bIsSourceBigEndian When set, the source data in the buffer is organized big-endian.
|
||||
*/
|
||||
template <typename TChar>
|
||||
void PotentialSwapBuffer(TChar* szBuffer, size_t nSize, bool bIsSourceBigEndian);
|
||||
|
||||
/**
|
||||
* \brief Convert a UTF16 string to UTF-8.
|
||||
* @param[in] szBuffer The source string.
|
||||
* @param[in] nSize The length of the string (or zero terminating string when supplied as 0).
|
||||
* @return Returns the string as UTF8 std::string object.
|
||||
*/
|
||||
static std::string ConvertToUTF8(const char16_t* szBuffer, size_t nSize);
|
||||
|
||||
/**
|
||||
* \brief Convert a UTF32 string to UTF-8.
|
||||
* @param[in] szBuffer The source string.
|
||||
* @param[in] nSize The length of the string (or zero terminating string when supplied as 0).
|
||||
* @return Returns the string as UTF8 std::string object.
|
||||
*/
|
||||
static std::string ConvertToUTF8(const char32_t* szBuffer, size_t nSize);
|
||||
|
||||
std::filesystem::path m_path; ///< Path to the file.
|
||||
std::string m_ssSource; ///< The source file content.
|
||||
};
|
||||
|
||||
inline void CSource::EndianSwap(char16_t& rc16)
|
||||
{
|
||||
uint8_t uiTemp = reinterpret_cast<uint8_t*>(&rc16)[0];
|
||||
reinterpret_cast<uint8_t*>(&rc16)[0] = reinterpret_cast<uint8_t*>(&rc16)[1];
|
||||
reinterpret_cast<uint8_t*>(&rc16)[1] = uiTemp;
|
||||
}
|
||||
|
||||
inline void CSource::EndianSwap(char32_t& rc32)
|
||||
{
|
||||
uint8_t uiTemp = reinterpret_cast<uint8_t*>(&rc32)[0];
|
||||
reinterpret_cast<uint8_t*>(&rc32)[0] = reinterpret_cast<uint8_t*>(&rc32)[3];
|
||||
reinterpret_cast<uint8_t*>(&rc32)[3] = uiTemp;
|
||||
reinterpret_cast<uint8_t*>(&rc32)[1] = reinterpret_cast<uint8_t*>(&rc32)[2];
|
||||
reinterpret_cast<uint8_t*>(&rc32)[2] = uiTemp;
|
||||
}
|
||||
|
||||
template <typename TChar>
|
||||
inline void CSource::PotentialSwapBuffer(TChar* szBuffer, size_t nSize, bool bIsSourceBigEndian)
|
||||
{
|
||||
// Only works for two or four byte characters
|
||||
static_assert(sizeof(TChar) == 2 || sizeof(TChar) == 4);
|
||||
|
||||
// Check for a valid buffer
|
||||
if (!szBuffer) return;
|
||||
|
||||
// Only swap when the endianness of the data is not corresponding to the character endianness of the system.
|
||||
if (bIsSourceBigEndian == IsSystemBigEndian()) return;
|
||||
|
||||
// Swap all bytes
|
||||
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
|
||||
EndianSwap(szBuffer[nIndex]);
|
||||
}
|
||||
|
||||
|
||||
#endif // !defined SOURCE_H
|
||||
457
sdv_executables/sdv_idl_compiler/support.h
Normal file
457
sdv_executables/sdv_idl_compiler/support.h
Normal file
@@ -0,0 +1,457 @@
|
||||
#ifndef SUPPORT_H
|
||||
#define SUPPORT_H
|
||||
|
||||
#include "exception.h"
|
||||
#include "codepos.h"
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
|
||||
/**
|
||||
* @brief Get the characters within a string or character (sequence) literal.
|
||||
* @details Get the characters for a string or a character sequence (a single character is a character sequence of one
|
||||
* character). The characters in the string that are represented through escape sequences are interpreted. This includes C-style
|
||||
* string standard characters used for whitespace and quotation markers as well as special Unicode characters only presented by
|
||||
* Unicode points within the string. Unicode codepoints could be needing more than one physical character to be represented.
|
||||
* @tparam TLiteral The type of the variable holding the literal (either const char* or CCodePos).
|
||||
* @tparam TCharType The character type to use for the text generation.
|
||||
* @param[in, out] rtLiteral The literal text to get the characters for (not including the prefix and quote symbol).
|
||||
* @param[in] szDelim Delimiter to use for detection of the end of the string (will not be included in the result). If nullptr
|
||||
* interprets the complete string.
|
||||
* @param[out] rssText Reference to the string receiving the characters.
|
||||
* @param[out] ruiByteCnt Reference to the variable receiving the byte count.
|
||||
* @param[in] bIgnoreEscape When set, do not interpret escape characters.
|
||||
* @param[in] bNotUnicode When set, the encoding is ASCII. Only valid with 'char' data type.
|
||||
* @throws Can throw a CCompileException exception if invalid combinations where detected.
|
||||
* @returns Returns a vector with the characters that have been read and interpreted.
|
||||
*/
|
||||
template <typename TLiteral, typename TCharType>
|
||||
inline void InterpretCText(TLiteral rtLiteral, const char* szDelim, std::basic_string<TCharType>& rssText, uint32_t& ruiByteCnt,
|
||||
bool bIgnoreEscape = false, bool bNotUnicode = false)
|
||||
{
|
||||
if (!rtLiteral)
|
||||
throw CCompileException("Internal error: invalid parameter.");
|
||||
TLiteral rtLiteralLocal = rtLiteral; // Prevent changing the local variable (some compilers don't like this).
|
||||
rssText.clear();
|
||||
ruiByteCnt = 0;
|
||||
|
||||
// Check to see an incomptiblity with the parameter 'bNotUnicode'.
|
||||
if (bNotUnicode && sizeof(TCharType) != 1)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Internal error: invalid parameter.");
|
||||
|
||||
// Get the octal number for correct presentation.
|
||||
auto fnGetOctalChar = [&]()
|
||||
{
|
||||
// Octal number of three digits not exceeding the character capacity
|
||||
std::string ssNumber;
|
||||
size_t n = 0;
|
||||
do
|
||||
{
|
||||
if (*rtLiteralLocal < '0' || *rtLiteralLocal > '7')
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(),
|
||||
"Invalid escape sequence in string; expecting a valid octal number.");
|
||||
ssNumber += *rtLiteralLocal;
|
||||
ruiByteCnt++;
|
||||
rtLiteralLocal++;
|
||||
} while (++n < 3 && *rtLiteralLocal >= '0' && *rtLiteralLocal <= '7');
|
||||
uint32_t uiResult = static_cast<uint32_t>(std::stoul(ssNumber, nullptr, 8));
|
||||
|
||||
// Byte character literals cannot have a value higher than 0377 (0xff).
|
||||
if (uiResult > 0377)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(),
|
||||
"Invalid escape sequence in string; expecting a valid octal number of at the most 0377.");
|
||||
|
||||
return uiResult;
|
||||
};
|
||||
|
||||
// Get the hexadecimal number for correct presentation.
|
||||
auto fnGetHexChar = [&](size_t nFixedDigits = 0)
|
||||
{
|
||||
// Determine the amount of digits
|
||||
size_t nFixedDigitsLocal = nFixedDigits ? nFixedDigits : sizeof(TCharType) * 2;
|
||||
|
||||
// Check the amount of digits
|
||||
std::string ssNumber;
|
||||
size_t n = 0;
|
||||
do
|
||||
{
|
||||
if (!std::isxdigit(*rtLiteralLocal))
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; expecting a "
|
||||
"valid hexadecimal number of ", nFixedDigitsLocal, " digits.");
|
||||
ssNumber += *rtLiteralLocal;
|
||||
rtLiteralLocal++;
|
||||
ruiByteCnt++;
|
||||
} while (++n < nFixedDigitsLocal && std::isxdigit(*rtLiteralLocal));
|
||||
if (nFixedDigits && n != nFixedDigits)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; expecting a "
|
||||
"valid hexadecimal number of ", nFixedDigitsLocal, " digits.");
|
||||
|
||||
return static_cast<uint32_t>(std::stoul(ssNumber, nullptr, 16));
|
||||
};
|
||||
|
||||
// Check for the correct unicode presentation and add the characters to the vector.
|
||||
auto fnAddUnicodeCharacter = [&](bool b32BitCodePoint = false)
|
||||
{
|
||||
uint32_t uiCodePoint = fnGetHexChar(b32BitCodePoint ? 8 : 4);
|
||||
|
||||
if (uiCodePoint >= 0xD800 && uiCodePoint < 0xE000) // Range U+D800...U+DFFF are reserved and not assigned any character.
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; Unicode range"
|
||||
" U+D800...U+DFFF are not valid code point.");
|
||||
|
||||
// Check whether the number fits (wide characters are considered to correspond to a UTF-16 or UTF-32
|
||||
// character).
|
||||
if constexpr (sizeof(TCharType) == 4) // UTF-32
|
||||
rssText.push_back(uiCodePoint);
|
||||
else if constexpr (sizeof(TCharType) == 2) // UTF-16
|
||||
{
|
||||
if (uiCodePoint < 0xD800) // Range U+0000...U+D7FF fit in 16 bits.
|
||||
rssText.push_back(static_cast<TCharType>(uiCodePoint));
|
||||
else if (uiCodePoint >= 0xE000 && uiCodePoint < 0x10000) // Range U+E000...U+FFFF fit in 16 bits.
|
||||
rssText.push_back(static_cast<TCharType>(uiCodePoint));
|
||||
else if (uiCodePoint >= 0x10000 && uiCodePoint < 0x110000) // Range U+10000...U+10FFFF fit in 2X 16 bits.
|
||||
{
|
||||
// Subtract 0x10000 from the code point
|
||||
uiCodePoint -= 0x10000;
|
||||
// High ten bits form the first character
|
||||
rssText.push_back(static_cast<TCharType>(0xD800 | ((uiCodePoint >> 10) & 0x3ff)));
|
||||
// Low ten bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0xDC00 | (uiCodePoint & 0x3FF)));
|
||||
}
|
||||
else if (uiCodePoint >= 0x110000) // Range above U+10FFFF are invalid.
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; expecting a"
|
||||
" valid Unicode code point.");
|
||||
}
|
||||
else if constexpr (sizeof(TCharType) == 1) // ASCII and UTF-8
|
||||
{
|
||||
if (uiCodePoint < 0x0080) // Range between U+0000 and U+007F fit in 8 bits.
|
||||
rssText.push_back(static_cast<TCharType>(uiCodePoint));
|
||||
else if (/*uiCodePoint >= 0x0080 &&*/ uiCodePoint < 0x009F) // Range U+0080...U+009F fit in 2X 8 bits (only UTF-8).
|
||||
{
|
||||
if (bNotUnicode)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; Unicode"
|
||||
" code point doesn't fit into ASCII character.");
|
||||
// High 5 bits form the first character
|
||||
rssText.push_back(static_cast<TCharType>(0xC0 | ((uiCodePoint >> 6) & 0x1f)));
|
||||
// Low 6 bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | (uiCodePoint & 0x3F)));
|
||||
}
|
||||
else if (/*uiCodePoint >= 0x00A0 &&*/ uiCodePoint < 0x0100) // Range U+00A0...U+00FF fit in 2X 8 bits or 1X 8 bits.
|
||||
{
|
||||
if (bNotUnicode) // ASCII character
|
||||
rssText.push_back(static_cast<TCharType>(uiCodePoint));
|
||||
else
|
||||
{
|
||||
// High 5 bits form the first character
|
||||
rssText.push_back(static_cast<TCharType>(0xC0 | ((uiCodePoint >> 6) & 0x1f)));
|
||||
// Low 6 bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | (uiCodePoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
else if (/*uiCodePoint >= 0x0100 &&*/ uiCodePoint < 0x0800) // Range U+0100...U+07FF fit in 2X 8 bits (only UTF-8).
|
||||
{
|
||||
if (bNotUnicode)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; Unicode"
|
||||
" code point doesn't fit into ASCII character.");
|
||||
// High 5 bits form the first character
|
||||
rssText.push_back(static_cast<TCharType>(0xC0 | ((uiCodePoint >> 6) & 0x1f)));
|
||||
// Low 6 bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | (uiCodePoint & 0x3F)));
|
||||
}
|
||||
else if (/*uiCodePoint >= 0x0800 &&*/ uiCodePoint < 0xFFFF) // Range U+0800...U+FFFF fit in 3X 8 bits (only UTF-8).
|
||||
{
|
||||
if (bNotUnicode)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; Unicode"
|
||||
" code point doesn't fit into ASCII character.");
|
||||
// High 4 bits form the first character
|
||||
rssText.push_back(static_cast<TCharType>(0xE0 | ((uiCodePoint >> 12) & 0x0f)));
|
||||
// Next 6 bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | ((uiCodePoint >> 6) & 0x3f)));
|
||||
// Low 6 bits form the third character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | (uiCodePoint & 0x3F)));
|
||||
}
|
||||
else if (/*uiCodePoint >= 0x01000 &&*/ uiCodePoint < 0x10FFFF) // Range U+10000...U+10FFFF fit in 4x 8 bits (only UTF-8).
|
||||
{
|
||||
if (bNotUnicode)
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; Unicode"
|
||||
" code point doesn't fit into ASCII character.");
|
||||
// High 3 bits form the first character
|
||||
rssText.push_back(static_cast<TCharType>(0xF0 | ((uiCodePoint >> 18) & 0x07)));
|
||||
// Next 6 bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | ((uiCodePoint >> 12) & 0x3f)));
|
||||
// Next 6 bits form the second character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | ((uiCodePoint >> 6) & 0x3f)));
|
||||
// Low 6 bits form the third character
|
||||
rssText.push_back(static_cast<TCharType>(0x80 | (uiCodePoint & 0x3F)));
|
||||
}
|
||||
else if (uiCodePoint >= 0x110000) // Range above U+10FFF are invalid.
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string; expecting a"
|
||||
" valid Unicode code point.");
|
||||
}
|
||||
};
|
||||
|
||||
// Check whether to continue processing based on the delimiter.
|
||||
auto fnContinueProcessing = [&]() -> bool
|
||||
{
|
||||
// In case there is no delimiter, end processing when the literal is '\0'.
|
||||
if (!szDelim) return *rtLiteralLocal ? true : false;
|
||||
|
||||
// There is a delimiter. Continue processing when the delimiter pattern hasn't been detected.
|
||||
uint32_t uiIndex = 0;
|
||||
do
|
||||
{
|
||||
if (szDelim[uiIndex] != rtLiteralLocal[uiIndex])
|
||||
return true;
|
||||
} while (szDelim[++uiIndex] != '\0');
|
||||
return false;
|
||||
};
|
||||
|
||||
while (fnContinueProcessing())
|
||||
{
|
||||
switch (*rtLiteralLocal)
|
||||
{
|
||||
case '\0':
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid string; unexpected end of file detected.");
|
||||
break;
|
||||
case '\\':
|
||||
// Skip backslash
|
||||
rtLiteralLocal++;
|
||||
ruiByteCnt++;
|
||||
|
||||
// Handle as escape or as character
|
||||
if (bIgnoreEscape)
|
||||
{
|
||||
rssText.push_back('\\');
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle escape
|
||||
if (std::isdigit(*rtLiteralLocal))
|
||||
{
|
||||
// Octal character value
|
||||
rssText.push_back(static_cast<TCharType>(fnGetOctalChar()));
|
||||
}
|
||||
else
|
||||
{
|
||||
char cEscapeChar = *rtLiteralLocal;
|
||||
rtLiteralLocal++;
|
||||
ruiByteCnt++;
|
||||
switch (cEscapeChar)
|
||||
{
|
||||
case '\'': // Single quote
|
||||
case '\"': // Double quote
|
||||
case '?': // Question mark
|
||||
case '\\': // Backslash
|
||||
rssText.push_back(cEscapeChar);
|
||||
break;
|
||||
case 'a': // Bell
|
||||
rssText.push_back('\a');
|
||||
break;
|
||||
case 'b': // Backspace
|
||||
rssText.push_back('\b');
|
||||
break;
|
||||
case 'f': // Form feed
|
||||
rssText.push_back('\f');
|
||||
break;
|
||||
case 'n': // Line feed
|
||||
rssText.push_back('\n');
|
||||
break;
|
||||
case 'r': // Carriage return
|
||||
rssText.push_back('\r');
|
||||
break;
|
||||
case 't': // Horizontal tab
|
||||
rssText.push_back('\t');
|
||||
break;
|
||||
case 'v': // Vertical tab
|
||||
rssText.push_back('\v');
|
||||
break;
|
||||
case 'x': // Hex character value
|
||||
rssText.push_back(static_cast<TCharType>(fnGetHexChar()));
|
||||
break;
|
||||
case 'u': // 4 digit Unicode character
|
||||
fnAddUnicodeCharacter();
|
||||
break;
|
||||
case 'U': // 8 digit Unicode character
|
||||
fnAddUnicodeCharacter(true);
|
||||
break;
|
||||
default:
|
||||
throw CCompileException(CCodePos(rtLiteralLocal).GetLocation(), "Invalid escape sequence in string");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Skip char
|
||||
rssText.push_back(*rtLiteralLocal);
|
||||
rtLiteralLocal++;
|
||||
ruiByteCnt++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate an ASCII C/C++ character string from a text with zero or more characters.
|
||||
* @tparam TCharType The character type to use for the text interpretation.
|
||||
* @param[in] szText The text to translate into C/C++ style text (inserting escape characters if necessary).
|
||||
* @param[in] uiLen The length of the text or 0xFFFFFFFF when to use a zero terminated string.
|
||||
* @param[in] bNotUnicode When set, the encoding is ASCII. Only valid with 'char' data type.
|
||||
* @return std::string
|
||||
*/
|
||||
template <typename TCharType>
|
||||
inline std::string GenerateCText(const TCharType* szText, uint32_t uiLen = 0xFFFFFFFF, bool bNotUnicode = false)
|
||||
{
|
||||
std::stringstream sstreamResult;
|
||||
if (!szText) return sstreamResult.str();
|
||||
|
||||
// Check to see an incomptiblity with the parameter 'bNotUnicode'.
|
||||
if (bNotUnicode && sizeof(TCharType) != 1)
|
||||
throw CCompileException("Internal error: invalid parameter.");
|
||||
|
||||
// Standard ASCII code generation (until the value of 128)
|
||||
auto fnGenerateASCII = [&](uint32_t c) -> bool
|
||||
{
|
||||
// Check for low level...
|
||||
switch (c)
|
||||
{
|
||||
case '\'': sstreamResult << "\\\'"; break;
|
||||
case '\"': sstreamResult << "\\\""; break;
|
||||
case '\\': sstreamResult << "\\\\"; break;
|
||||
case '\a': sstreamResult << "\\a"; break;
|
||||
case '\b': sstreamResult << "\\b"; break;
|
||||
case '\f': sstreamResult << "\\f"; break;
|
||||
case '\n': sstreamResult << "\\n"; break;
|
||||
case '\r': sstreamResult << "\\r"; break;
|
||||
case '\t': sstreamResult << "\\t"; break;
|
||||
case '\v': sstreamResult << "\\v"; break;
|
||||
default:
|
||||
if (c < 0x20)
|
||||
sstreamResult << "\\" << std::oct << c;
|
||||
else if (c < 0x80)
|
||||
sstreamResult << static_cast<char>(c);
|
||||
else return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const TCharType* szTextPos = szText;
|
||||
size_t n = 0;
|
||||
if constexpr (sizeof(TCharType) == 4) // UTF-32 and possibly wide character
|
||||
{
|
||||
while (*szTextPos && n++ < uiLen)
|
||||
{
|
||||
uint32_t uiChar = static_cast<uint32_t>(*szTextPos);
|
||||
if (!fnGenerateASCII(uiChar))
|
||||
{
|
||||
// Generate an UNICODE character
|
||||
if (uiChar < 0x10000)
|
||||
sstreamResult << "\\u" << std::hex << std::setfill('0') << std::setw(4) << uiChar;
|
||||
else
|
||||
sstreamResult << "\\U" << std::hex << std::setfill('0') << std::setw(8) << uiChar;
|
||||
}
|
||||
szTextPos++;
|
||||
}
|
||||
} else if constexpr (sizeof(TCharType) == 2) // UTF-16 and possibly wide character
|
||||
{
|
||||
while (*szTextPos && n++ < uiLen)
|
||||
{
|
||||
uint16_t uiChar = static_cast<uint16_t>(*szTextPos);
|
||||
if (!fnGenerateASCII(uiChar))
|
||||
{
|
||||
// Generate an UNICODE character
|
||||
if (uiChar < 0xD800 || uiChar >= 0xE000) // One character
|
||||
sstreamResult << "\\u" << std::hex << std::setfill('0') << std::setw(4) << static_cast<uint32_t>(uiChar);
|
||||
else // Two characters
|
||||
{
|
||||
uint32_t uiUnicodeCodePoint = 0x10000 + ((static_cast<uint32_t>(uiChar) & 0x3ff) << 10);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint16_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= static_cast<uint32_t>(uiChar) & 0x3ff;
|
||||
sstreamResult << "\\U" << std::hex << std::setfill('0') << std::setw(8) << uiUnicodeCodePoint;
|
||||
}
|
||||
}
|
||||
szTextPos++;
|
||||
}
|
||||
}
|
||||
else // UTF-8 or ASCII
|
||||
{
|
||||
while (*szTextPos && n++ < uiLen)
|
||||
{
|
||||
uint8_t uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
if (!fnGenerateASCII(static_cast<uint8_t>(uiChar)))
|
||||
{
|
||||
// Generate ASCII character
|
||||
if (bNotUnicode)
|
||||
{
|
||||
sstreamResult << "\\" << std::oct << static_cast<uint32_t>(uiChar);
|
||||
} else // Generate an UNICODE character
|
||||
{
|
||||
if (uiChar < 0x80) // One character
|
||||
sstreamResult << "\\u" << std::hex << std::setfill('0') << std::setw(4) << static_cast<uint32_t>(uiChar);
|
||||
else if (uiChar < 0xE0) // Two characters
|
||||
{
|
||||
uint32_t uiUnicodeCodePoint = ((static_cast<uint32_t>(uiChar) & ~0xE0) << 6);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= static_cast<uint32_t>(uiChar) & ~0xC0;
|
||||
sstreamResult << "\\u" << std::hex << std::setfill('0') << std::setw(4) << uiUnicodeCodePoint;
|
||||
}
|
||||
else if (/*uiChar >= 0xE0 &&*/ uiChar < 0xF0) // Three characters
|
||||
{
|
||||
uint32_t uiUnicodeCodePoint = ((static_cast<uint32_t>(uiChar) & ~0xF0) << 12);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= ((static_cast<uint32_t>(uiChar) & ~0xC0) << 6);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= static_cast<uint32_t>(uiChar) & ~0xC0;
|
||||
sstreamResult << "\\u" << std::hex << std::setfill('0') << std::setw(4) << uiUnicodeCodePoint;
|
||||
}
|
||||
else /*if (uiChar >= 0xF0)*/ // Four characters
|
||||
{
|
||||
uint32_t uiUnicodeCodePoint = ((static_cast<uint32_t>(uiChar) & ~0xF0) << 18);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= ((static_cast<uint32_t>(uiChar) & ~0xC0) << 12);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= ((static_cast<uint32_t>(uiChar) & ~0xC0) << 6);
|
||||
szTextPos++;
|
||||
uiChar = static_cast<uint8_t>(*szTextPos);
|
||||
uiUnicodeCodePoint |= static_cast<uint32_t>(uiChar) & ~0xC0;
|
||||
sstreamResult << "\\U" << std::hex << std::setfill('0') << std::setw(8) << uiUnicodeCodePoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
szTextPos++;
|
||||
}
|
||||
}
|
||||
return sstreamResult.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Generate an ASCII C/C++ character string from a text with zero or more characters.
|
||||
* @tparam TCharType The character type to use for the text interpretation.
|
||||
* @param[in] cChar The character to translate into C/C++ style text (inserting escape characters if necessary).
|
||||
* @param[in] bNotUnicode When set, the encoding is ASCII. Only valid with 'char' data type.
|
||||
* @return std::string
|
||||
*/
|
||||
inline std::string GenerateCText(char cChar, bool bNotUnicode = false)
|
||||
{
|
||||
return GenerateCText(&cChar, 1, bNotUnicode);
|
||||
}
|
||||
inline std::string GenerateCText(char16_t cChar, bool bNotUnicode = false)
|
||||
{
|
||||
return GenerateCText(&cChar, 1, bNotUnicode);
|
||||
}
|
||||
inline std::string GenerateCText(char32_t cChar, bool bNotUnicode = false)
|
||||
{
|
||||
return GenerateCText(&cChar, 1, bNotUnicode);
|
||||
}
|
||||
inline std::string GenerateCText(wchar_t cChar, bool bNotUnicode = false)
|
||||
{
|
||||
return GenerateCText(&cChar, 1, bNotUnicode);
|
||||
}
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif // !defined(SUPPORT_H)
|
||||
979
sdv_executables/sdv_idl_compiler/token.cpp
Normal file
979
sdv_executables/sdv_idl_compiler/token.cpp
Normal file
@@ -0,0 +1,979 @@
|
||||
#include "token.h"
|
||||
#include "support.h"
|
||||
#include "constvariant.inl"
|
||||
#include "parsecontext.h"
|
||||
|
||||
CToken::CToken(const std::string& rssText, ETokenType eType /*= ETokenType::token_identifier*/) :
|
||||
m_eType(eType), m_ssCache(rssText)
|
||||
{}
|
||||
|
||||
CToken::CToken(const std::string& rssText, ETokenLiteralType eLiteralType) :
|
||||
m_eType(ETokenType::token_literal), m_eLiteralType(eLiteralType), m_ssCache(rssText)
|
||||
{}
|
||||
|
||||
CToken::CToken(const std::string& rssText, ETokenMetaType eMetaType) :
|
||||
m_eType(ETokenType::token_meta), m_eMetaType(eMetaType), m_ssCache(rssText)
|
||||
{}
|
||||
|
||||
void CToken::MoveStart(int32_t iOffset)
|
||||
{
|
||||
// Correct boundaries and set the new position
|
||||
int32_t iLocalOffset = iOffset;
|
||||
if (iOffset < 0)
|
||||
{
|
||||
// Not before the beginning
|
||||
if (m_uiCol < static_cast<uint32_t>(-iOffset))
|
||||
iLocalOffset = -static_cast<int32_t>(m_uiCol);
|
||||
|
||||
// Adjust the position and length
|
||||
m_uiCol -= static_cast<uint32_t>(-iLocalOffset);
|
||||
if (m_uiLen != 0xffffffff)
|
||||
m_uiLen += static_cast<uint32_t>(-iLocalOffset);
|
||||
} else
|
||||
{
|
||||
// Not past the end
|
||||
if (static_cast<uint32_t>(iOffset) > m_uiLen)
|
||||
iLocalOffset = static_cast<int32_t>(m_uiLen);
|
||||
|
||||
// Check for any new lines occurring and adjust the position
|
||||
for (int32_t iIdx = 0; iIdx < iLocalOffset; iIdx++)
|
||||
{
|
||||
if (m_szCode[iIdx] == '\n')
|
||||
{
|
||||
m_uiLine++;
|
||||
m_uiCol = 1;
|
||||
}
|
||||
else
|
||||
m_uiCol++;
|
||||
}
|
||||
|
||||
// Adjust the length
|
||||
if (m_uiLen != 0xffffffff)
|
||||
m_uiLen -= static_cast<uint32_t>(iLocalOffset);
|
||||
}
|
||||
|
||||
// Set the new code token
|
||||
m_szCode += iLocalOffset;
|
||||
|
||||
// Clear the cache
|
||||
m_ssCache.clear();
|
||||
}
|
||||
|
||||
void CToken::MoveEnd(int32_t iOffset)
|
||||
{
|
||||
// Only works when there is a fixed length
|
||||
if (m_uiLen == 0xffffffff) return;
|
||||
|
||||
// Correct boundaries and set new length
|
||||
if (iOffset < 0)
|
||||
{
|
||||
if (static_cast<uint32_t>(-iOffset) > m_uiLen)
|
||||
m_uiLen = 0;
|
||||
else
|
||||
m_uiLen -= static_cast<uint32_t>(-iOffset);
|
||||
} else
|
||||
m_uiLen += static_cast<uint32_t>(iOffset);
|
||||
|
||||
// Clear the cache
|
||||
m_ssCache.clear();
|
||||
}
|
||||
|
||||
bool CToken::operator==(const CToken& rtoken) const
|
||||
{
|
||||
return rtoken.m_szCode == m_szCode;
|
||||
}
|
||||
|
||||
bool CToken::operator!=(const CToken& rtoken) const
|
||||
{
|
||||
return rtoken.m_szCode != m_szCode;
|
||||
}
|
||||
|
||||
uint32_t CToken::GetLine() const
|
||||
{
|
||||
return m_uiLine;
|
||||
}
|
||||
|
||||
uint32_t CToken::GetCol() const
|
||||
{
|
||||
return m_uiCol;
|
||||
}
|
||||
|
||||
uint32_t CToken::GetEndLine() const
|
||||
{
|
||||
uint32_t uiEndLine = m_uiLine;
|
||||
for (uint32_t uiIndex = 0; m_uiLen != 0xffffffff && uiIndex < m_uiLen; uiIndex++)
|
||||
{
|
||||
if (m_szCode[uiIndex] == '\n')
|
||||
{
|
||||
// Only increase the line count when this is not the last character of the snippet.
|
||||
if (uiIndex < m_uiLen - 1) uiEndLine++;
|
||||
}
|
||||
}
|
||||
return uiEndLine;
|
||||
}
|
||||
|
||||
uint32_t CToken::GetEndCol() const
|
||||
{
|
||||
uint32_t uiEndCol = m_uiCol;
|
||||
for (uint32_t uiIndex = 0; m_uiLen != 0xffffffff && uiIndex < m_uiLen; uiIndex++)
|
||||
{
|
||||
if (uiIndex) uiEndCol++; // Do not count the first column
|
||||
if (m_szCode[uiIndex] == '\n')
|
||||
{
|
||||
// Only increase the line count when this is not the last character of the snippet.
|
||||
if (uiIndex < m_uiLen - 1) uiEndCol = 0;
|
||||
}
|
||||
}
|
||||
return uiEndCol;
|
||||
}
|
||||
|
||||
uint32_t CToken::GetLength() const
|
||||
{
|
||||
return m_uiLen;
|
||||
}
|
||||
|
||||
ETokenType CToken::GetType() const
|
||||
{
|
||||
return m_eType;
|
||||
}
|
||||
|
||||
ETokenLiteralType CToken::GetLiteralType() const
|
||||
{
|
||||
return m_eLiteralType;
|
||||
}
|
||||
|
||||
ETokenMetaType CToken::GetMetaType() const
|
||||
{
|
||||
return m_eMetaType;
|
||||
}
|
||||
|
||||
CToken::operator bool() const
|
||||
{
|
||||
// Is there any cache?
|
||||
if (!m_ssCache.empty()) return true;
|
||||
|
||||
// Validity checks on the token
|
||||
if (!m_szCode) return false;
|
||||
|
||||
// Sanity check by getting the value
|
||||
if (m_eType == ETokenType::token_literal && m_eLiteralType != ETokenLiteralType::token_undefined)
|
||||
ValueRef();
|
||||
|
||||
// No invalidity detected.
|
||||
return true;
|
||||
}
|
||||
|
||||
CToken::operator const std::string&() const
|
||||
{
|
||||
if (m_ssCache.empty())
|
||||
{
|
||||
if (!m_szCode)
|
||||
return m_ssCache;
|
||||
if (!m_uiCol)
|
||||
return m_ssCache;
|
||||
if (m_uiLen != 0xffffffff)
|
||||
m_ssCache = std::string(m_szCode, m_uiLen);
|
||||
else
|
||||
m_ssCache = std::string(m_szCode);
|
||||
}
|
||||
return m_ssCache;
|
||||
}
|
||||
|
||||
const char* CToken::c_str() const
|
||||
{
|
||||
if (m_ssCache.empty())
|
||||
{
|
||||
if (!m_szCode)
|
||||
return m_ssCache.c_str();
|
||||
if (!m_uiCol)
|
||||
return m_ssCache.c_str();
|
||||
if (m_uiLen != 0xffffffff)
|
||||
m_ssCache = std::string(m_szCode, m_uiLen);
|
||||
else
|
||||
m_ssCache = std::string(m_szCode);
|
||||
}
|
||||
return m_ssCache.c_str();
|
||||
}
|
||||
|
||||
std::string CToken::Left(uint32_t uiLen) const
|
||||
{
|
||||
if (!m_ssCache.empty()) return m_ssCache.substr(0, uiLen);
|
||||
if (!m_szCode) return std::string();
|
||||
if (!m_uiCol) return std::string();
|
||||
uint32_t uiLocalLen = m_uiLen != 0xffffffff ? m_uiLen : static_cast<uint32_t>(std::strlen(m_szCode));
|
||||
return std::string(m_szCode, std::min(uiLen, uiLocalLen));
|
||||
}
|
||||
|
||||
std::string CToken::Right(uint32_t uiLen) const
|
||||
{
|
||||
if (!m_ssCache.empty())
|
||||
{
|
||||
if (uiLen > m_ssCache.size()) return m_ssCache;
|
||||
return m_ssCache.substr(m_ssCache.size() - uiLen);
|
||||
}
|
||||
if (!m_szCode)
|
||||
return std::string();
|
||||
if (!m_uiCol) return std::string();
|
||||
uint32_t uiLocalLen = m_uiLen != 0xffffffff ? m_uiLen : static_cast<uint32_t>(std::strlen(m_szCode));
|
||||
uint32_t uiOffset = uiLocalLen < uiLen ? 0 : uiLocalLen - uiLen;
|
||||
return std::string(m_szCode + uiOffset, std::min(uiLen, uiLocalLen));
|
||||
}
|
||||
|
||||
bool CToken::operator==(const char* szString) const
|
||||
{
|
||||
if (!m_ssCache.empty()) return m_ssCache == szString;
|
||||
if (!m_szCode && szString) return false;
|
||||
if (!szString) return false;
|
||||
return static_cast<std::string>(*this) == szString;
|
||||
}
|
||||
|
||||
bool CToken::operator!=(const char* szString) const
|
||||
{
|
||||
if (!m_ssCache.empty()) return m_ssCache != szString;
|
||||
if (!m_szCode && szString) return true;
|
||||
if (!szString) return true;
|
||||
return static_cast<std::string>(*this) != szString;
|
||||
}
|
||||
|
||||
bool CToken::operator==(const std::string& rssString) const
|
||||
{
|
||||
if (!m_ssCache.empty()) return m_ssCache == rssString;
|
||||
if (!m_szCode && !rssString.empty()) return false;
|
||||
if (rssString.empty()) return false;
|
||||
return rssString == static_cast<std::string>(*this);
|
||||
}
|
||||
|
||||
bool CToken::operator!=(const std::string& rssString) const
|
||||
{
|
||||
if (!m_ssCache.empty()) return m_ssCache != rssString;
|
||||
if (!m_szCode && !rssString.empty()) return true;
|
||||
if (rssString.empty()) return true;
|
||||
return rssString != static_cast<std::string>(*this);
|
||||
}
|
||||
|
||||
bool CToken::IsLiteral() const
|
||||
{
|
||||
return m_eType == ETokenType::token_literal &&
|
||||
m_eLiteralType != ETokenLiteralType::token_undefined;
|
||||
}
|
||||
|
||||
bool CToken::IsInteger() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_integer:
|
||||
case ETokenLiteralType::token_literal_oct_integer:
|
||||
case ETokenLiteralType::token_literal_hex_integer:
|
||||
case ETokenLiteralType::token_literal_bin_integer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsSigned() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_integer:
|
||||
case ETokenLiteralType::token_literal_oct_integer:
|
||||
case ETokenLiteralType::token_literal_hex_integer:
|
||||
case ETokenLiteralType::token_literal_bin_integer:
|
||||
// Check the string for not having the suffix U or u
|
||||
return !Contains('U', 'u');
|
||||
case ETokenLiteralType::token_literal_dec_floating_point:
|
||||
case ETokenLiteralType::token_literal_hex_floating_point:
|
||||
case ETokenLiteralType::token_literal_fixed_point:
|
||||
// Always signed
|
||||
return true;
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
// True for 'char' type; false for all others
|
||||
return IsAscii();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsUnsigned() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_integer:
|
||||
case ETokenLiteralType::token_literal_oct_integer:
|
||||
case ETokenLiteralType::token_literal_hex_integer:
|
||||
case ETokenLiteralType::token_literal_bin_integer:
|
||||
// Check the string for having the suffix U or u
|
||||
return Contains('U', 'u');
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
// True for all but 'char' type
|
||||
return !IsAscii();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsLong() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_integer:
|
||||
case ETokenLiteralType::token_literal_oct_integer:
|
||||
case ETokenLiteralType::token_literal_hex_integer:
|
||||
case ETokenLiteralType::token_literal_bin_integer:
|
||||
case ETokenLiteralType::token_literal_dec_floating_point:
|
||||
case ETokenLiteralType::token_literal_hex_floating_point:
|
||||
// Check the string for having the suffix L or l
|
||||
return Contains('L', 'l') && !Contains("LL", "ll");
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
// The length of all characters in the sequence should fit the long
|
||||
if (IsWide())
|
||||
{
|
||||
std::wstring ssText;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode, "\'", ssText, uiByteCnt);
|
||||
return ssText.size() * sizeof(wchar_t) == sizeof(long);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ssText;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode, "\'", ssText, uiByteCnt, false, IsAscii());
|
||||
return ssText.size() == sizeof(long);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsLongLong() const
|
||||
{
|
||||
uint32_t uiByteCnt = 0;
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_integer:
|
||||
case ETokenLiteralType::token_literal_oct_integer:
|
||||
case ETokenLiteralType::token_literal_hex_integer:
|
||||
case ETokenLiteralType::token_literal_bin_integer:
|
||||
// Check the string for having the suffix LL or l
|
||||
return Contains("LL", "ll");
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
// The length of all characters in the sequence should fit the long
|
||||
if (IsWide())
|
||||
{
|
||||
std::wstring ssText;
|
||||
uiByteCnt = 0;
|
||||
InterpretCText(m_szCode, "\'", ssText, uiByteCnt);
|
||||
return ssText.size() * sizeof(wchar_t) == sizeof(long long);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ssText;
|
||||
uiByteCnt = 0;
|
||||
InterpretCText(m_szCode, "\'", ssText, uiByteCnt, false, IsAscii());
|
||||
return ssText.size() == sizeof(long long);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsDecimal() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_integer:
|
||||
case ETokenLiteralType::token_literal_dec_floating_point:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsHexadecimal() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_hex_integer:
|
||||
case ETokenLiteralType::token_literal_hex_floating_point:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsOctal() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_oct_integer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsBinary() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_bin_integer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsFloatingPoint() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_dec_floating_point:
|
||||
case ETokenLiteralType::token_literal_hex_floating_point:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsFixedPoint() const
|
||||
{ switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_fixed_point:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsCharacter() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsCharacterSequence() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsString() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_string:
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsAscii() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
return m_szCode[0] == '\'';
|
||||
case ETokenLiteralType::token_literal_string:
|
||||
return m_szCode[0] == '\"';
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return m_szCode[1] == '\"'; // String starting with R"(....)
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsUtf8() const
|
||||
{
|
||||
// NOTE: UTF-8 character is not defined for C++17.
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_string:
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return m_szCode[0] == 'u' && m_szCode[1] == '8'; // String starting with u8" or u8R"(....)
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsUtf16() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_string:
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return m_szCode[0] == 'u' && m_szCode[1] != '8'; // String starting with u" or uR"(....)
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsUtf32() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_string:
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return m_szCode[0] == 'U'; // String starting with U" or UR"(....)
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsWide() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_character:
|
||||
case ETokenLiteralType::token_literal_character_sequence:
|
||||
case ETokenLiteralType::token_literal_string:
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return m_szCode[0] == 'L'; // String starting with L" or LR"(....)
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsRawString() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_raw_string:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsBoolean() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_boolean:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToken::IsNullptr() const
|
||||
{
|
||||
switch (m_eLiteralType)
|
||||
{
|
||||
case ETokenLiteralType::token_literal_nullptr:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueRef() const
|
||||
{
|
||||
if (IsInteger() || IsCharacter() || IsCharacterSequence())
|
||||
{
|
||||
if (IsSigned())
|
||||
return ValueSignedInteger();
|
||||
else
|
||||
return ValueUnsignedInteger();
|
||||
} else if (IsFloatingPoint() || IsFixedPoint())
|
||||
return ValueFloat();
|
||||
else if (IsString())
|
||||
return ValueString();
|
||||
else if (IsBoolean())
|
||||
return ValueBoolean();
|
||||
else if (IsNullptr())
|
||||
return ValueNullptr();
|
||||
else
|
||||
throw CCompileException(*this, "Internal error: invalid value type.");
|
||||
}
|
||||
|
||||
template <typename... TChar>
|
||||
bool CToken::Contains(char c, TChar... tChars) const
|
||||
{
|
||||
// Dependable on the existence of a length use the memchr or the strchr function.
|
||||
if (m_uiLen == 0xFFFFFFFF)
|
||||
{
|
||||
if (std::strchr(m_szCode, c))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::memchr(m_szCode, c, static_cast<size_t>(m_uiLen)))
|
||||
return true;
|
||||
}
|
||||
return Contains(tChars...);
|
||||
}
|
||||
|
||||
template <typename... TString>
|
||||
bool CToken::Contains(const char* sz, TString... tStrings) const
|
||||
{
|
||||
// Implementation of strnstr (not part of the C/C++ library).
|
||||
auto fnstrnstr = [](const char *haystack, const char *needle, size_t len) -> char*
|
||||
{
|
||||
int i;
|
||||
size_t needle_len;
|
||||
|
||||
if (0 == (needle_len = strnlen(needle, len)))
|
||||
return (char *)haystack;
|
||||
|
||||
for (i=0; i<=(int)(len-needle_len); i++)
|
||||
{
|
||||
if ((haystack[0] == needle[0]) &&
|
||||
(0 == strncmp(haystack, needle, needle_len)))
|
||||
return (char *)haystack;
|
||||
|
||||
haystack++;
|
||||
}
|
||||
return NULL;
|
||||
};
|
||||
|
||||
// Dependable on the existence of a length use the strnstr or the strstr function.
|
||||
if (m_uiLen == 0xFFFFFFFF)
|
||||
{
|
||||
if (std::strstr(m_szCode, sz))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fnstrnstr(m_szCode, sz, static_cast<size_t>(m_uiLen)))
|
||||
return true;
|
||||
}
|
||||
return Contains(tStrings...);
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueSignedInteger() const
|
||||
{
|
||||
int64_t iValue = 0ll;
|
||||
if (IsInteger())
|
||||
{
|
||||
// Reset errno... will not be cleared otherwise.
|
||||
errno = 0;
|
||||
if (IsOctal())
|
||||
iValue = static_cast<int64_t>(std::strtoll(m_szCode + 1, nullptr, 8));
|
||||
else if (IsHexadecimal())
|
||||
iValue = static_cast<int64_t>(std::strtoll(m_szCode + 2, nullptr, 16));
|
||||
else if (IsBinary())
|
||||
iValue = static_cast<int64_t>(std::strtoll(m_szCode + 2, nullptr, 2));
|
||||
else
|
||||
iValue = static_cast<int64_t>(std::strtoll(m_szCode, nullptr, 10));
|
||||
if (errno == ERANGE)
|
||||
throw CCompileException(*this, "The provided value exceeds the maximum possible value of a long long integer.");
|
||||
}
|
||||
if (IsCharacter())
|
||||
{
|
||||
if (IsAscii())
|
||||
{
|
||||
std::string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 1, "\'", ssValue, uiByteCnt, false, true);
|
||||
if (!ssValue.empty()) iValue = ssValue[0];
|
||||
}
|
||||
if (IsUtf16())
|
||||
{
|
||||
std::u16string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
if (!ssValue.empty()) iValue = ssValue[0];
|
||||
}
|
||||
if (IsUtf32())
|
||||
{
|
||||
std::u32string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
if (!ssValue.empty()) iValue = ssValue[0];
|
||||
}
|
||||
if (IsWide())
|
||||
{
|
||||
std::wstring ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
if (!ssValue.empty()) iValue = ssValue[0];
|
||||
}
|
||||
}
|
||||
if (IsCharacterSequence())
|
||||
{
|
||||
if (IsAscii())
|
||||
{
|
||||
std::string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 1, "\'", ssValue, uiByteCnt, false, true);
|
||||
for (char c : ssValue)
|
||||
iValue = iValue << 8 | static_cast<uint8_t>(c);
|
||||
}
|
||||
if (IsWide())
|
||||
{
|
||||
std::wstring ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
for (wchar_t c : ssValue)
|
||||
{
|
||||
if constexpr (sizeof(wchar_t) == sizeof(int16_t))
|
||||
iValue = iValue << 16 | static_cast<uint16_t>(c);
|
||||
else
|
||||
iValue = iValue << 32 | static_cast<uint32_t>(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CConstVariant varValue;
|
||||
if (iValue > std::numeric_limits<int8_t>::min() && iValue < std::numeric_limits<int8_t>::max())
|
||||
varValue = static_cast<int8_t>(iValue);
|
||||
else if (iValue > std::numeric_limits<int16_t>::min() && iValue < std::numeric_limits<int16_t>::max())
|
||||
varValue = static_cast<int16_t>(iValue);
|
||||
else if (iValue > std::numeric_limits<int32_t>::min() && iValue < std::numeric_limits<int32_t>::max())
|
||||
varValue = static_cast<int32_t>(iValue);
|
||||
else
|
||||
varValue = iValue;
|
||||
return varValue;
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueUnsignedInteger() const
|
||||
{
|
||||
uint64_t uiValue = 0ull;
|
||||
if (IsInteger())
|
||||
{
|
||||
// Reset errno... will not be cleared otherwise.
|
||||
errno = 0;
|
||||
if (IsOctal())
|
||||
uiValue = static_cast<uint64_t>(std::strtoull(m_szCode + 1, nullptr, 8));
|
||||
else if (IsHexadecimal())
|
||||
uiValue = static_cast<uint64_t>(std::strtoull(m_szCode + 2, nullptr, 16));
|
||||
else if (IsBinary())
|
||||
uiValue = static_cast<uint64_t>(std::strtoull(m_szCode + 2, nullptr, 2));
|
||||
else
|
||||
uiValue = static_cast<uint64_t>(std::strtoull(m_szCode, nullptr, 10));
|
||||
if (errno == ERANGE)
|
||||
throw CCompileException(*this,
|
||||
"The provided value exceeds the maximum possible value of an unsigned long long integer.");
|
||||
}
|
||||
if (IsCharacter())
|
||||
{
|
||||
if (IsAscii())
|
||||
{
|
||||
std::string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 1, "\'", ssValue, uiByteCnt, false, true);
|
||||
if (!ssValue.empty()) uiValue = static_cast<uint8_t>(ssValue[0]);
|
||||
}
|
||||
if (IsUtf16())
|
||||
{
|
||||
std::u16string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
if (!ssValue.empty()) uiValue = static_cast<uint16_t>(ssValue[0]);
|
||||
}
|
||||
if (IsUtf32())
|
||||
{
|
||||
std::u32string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
if (!ssValue.empty()) uiValue = static_cast<uint32_t>(ssValue[0]);
|
||||
}
|
||||
if (IsWide())
|
||||
{
|
||||
std::wstring ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
if (!ssValue.empty())
|
||||
uiValue = sizeof(wchar_t) == 2 ? static_cast<uint16_t>(ssValue[0]) : static_cast<uint32_t>(ssValue[0]);
|
||||
}
|
||||
}
|
||||
if (IsCharacterSequence())
|
||||
{
|
||||
uiValue = 0;
|
||||
if (IsAscii())
|
||||
{
|
||||
std::string ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 1, "\'", ssValue, uiByteCnt, false, true);
|
||||
for (char c : ssValue)
|
||||
uiValue = uiValue << 8 | static_cast<uint8_t>(c);
|
||||
}
|
||||
if (IsWide())
|
||||
{
|
||||
std::wstring ssValue;
|
||||
uint32_t uiByteCnt = 0;
|
||||
InterpretCText(m_szCode + 2, "\'", ssValue, uiByteCnt);
|
||||
for (wchar_t c : ssValue)
|
||||
{
|
||||
if constexpr (sizeof(wchar_t) == sizeof(int16_t))
|
||||
uiValue = uiValue << 16 | static_cast<uint16_t>(c);
|
||||
else
|
||||
uiValue = uiValue << 32 | static_cast<uint32_t>(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CConstVariant varValue;
|
||||
if (uiValue < static_cast<uint64_t>(std::numeric_limits<uint8_t>::max()))
|
||||
varValue = static_cast<uint8_t>(uiValue);
|
||||
else if (uiValue < static_cast<uint64_t>(std::numeric_limits<int16_t>::max()))
|
||||
varValue = static_cast<int16_t>(uiValue);
|
||||
else if (uiValue < static_cast<uint64_t>(std::numeric_limits<int32_t>::max()))
|
||||
varValue = static_cast<int32_t>(uiValue);
|
||||
else
|
||||
varValue = uiValue;
|
||||
return varValue;
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueFloat() const
|
||||
{
|
||||
long double ldValue = 0.0;
|
||||
|
||||
// Only integer, character, character sequence and boolean can be assigned.
|
||||
if (IsInteger() || IsCharacter() || IsCharacterSequence())
|
||||
{
|
||||
if (IsSigned())
|
||||
ldValue = ValueSignedInteger().Get<long double>();
|
||||
else // unsigned
|
||||
ldValue = ValueUnsignedInteger().Get<long double>();
|
||||
}
|
||||
if (IsFloatingPoint())
|
||||
{
|
||||
errno = 0;
|
||||
ldValue = std::strtold(m_szCode, nullptr);
|
||||
if (errno == ERANGE)
|
||||
throw CCompileException(*this, "Floating point doesn't fit into the data type.");
|
||||
}
|
||||
if (IsFixedPoint())
|
||||
{
|
||||
// A fixed point data type is not supported by C++ at the moment. Use the double data type to calculate and limit the
|
||||
// result to 31 bits for the value. The definition is not clear how these 31 bits divide between digits and fraction.
|
||||
errno = 0;
|
||||
ldValue = std::strtod(m_szCode, nullptr);
|
||||
if (errno == ERANGE)
|
||||
throw CCompileException(*this, "Floating point doesn't fit into the data type.");
|
||||
if (ldValue < -2147483647) // 31 bits
|
||||
throw CCompileException(*this, "The value falls below the smallest possible value of the type.");
|
||||
if (ldValue > 2147483648) // 31 bits
|
||||
throw CCompileException(*this, "The value exceeds the highest possible value of the type.");
|
||||
int iExpValue = 0;
|
||||
static int iExpMin = 0;
|
||||
long double ldDigitsValue = std::fabs(std::frexp(ldValue, &iExpValue));
|
||||
static long double ldDigitsMin = std::frexp(1.0 / static_cast<double>(1ll << 31), &iExpMin);
|
||||
if ((iExpValue < iExpMin) || ((iExpValue == iExpMin) && ldDigitsValue < ldDigitsMin))
|
||||
throw CCompileException(*this, "The value precision falls below the smallest possible precision of the type.");
|
||||
}
|
||||
if (IsString())
|
||||
throw CCompileException(*this, "Cannot assign a string to a floating/fixed point value.");
|
||||
if (IsBoolean())
|
||||
ldValue = ValueBoolean().Get<long double>();
|
||||
if (IsNullptr())
|
||||
ldValue = ValueNullptr().Get<long double>();
|
||||
|
||||
CConstVariant varValue;
|
||||
static int iExpValue = 0, iExpFloatMin = 0, iExpDoubleMin = 0/*, iExpLongDoubleMin = 0*/;
|
||||
static float ldDigitsFloatMin = std::frexp(std::numeric_limits<float>::min(), &iExpFloatMin);
|
||||
static double ldDigitsDoubleMin = std::frexp(std::numeric_limits<double>::min(), &iExpDoubleMin);
|
||||
// static long double ldDigitsLongDoubleMin = std::frexp(std::numeric_limits<long double>::min(), &iExpLongDoubleMin);
|
||||
long double ldDigitsValue = std::fabs(std::frexp(ldValue, &iExpValue));
|
||||
if (ldValue < std::numeric_limits<float>::max() && ldValue > std::numeric_limits<float>::lowest() &&
|
||||
((iExpValue > iExpFloatMin) || ((iExpValue == iExpFloatMin) && ldDigitsValue > ldDigitsFloatMin)))
|
||||
varValue = static_cast<float>(ldValue);
|
||||
else if (ldValue < std::numeric_limits<double>::max() && ldValue > std::numeric_limits<double>::lowest() &&
|
||||
((iExpValue > iExpDoubleMin) || ((iExpValue == iExpDoubleMin) && ldDigitsValue > ldDigitsDoubleMin)))
|
||||
varValue = static_cast<double>(ldValue);
|
||||
else
|
||||
varValue = ldValue;
|
||||
|
||||
return varValue;
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueBoolean() const
|
||||
{
|
||||
if (IsBoolean())
|
||||
{
|
||||
if (*this == "true") return CConstVariant(true);
|
||||
if (*this == "TRUE") return CConstVariant(true);
|
||||
if (*this == "false") return CConstVariant(false);
|
||||
if (*this == "FALSE") return CConstVariant(false);
|
||||
throw CCompileException(*this, "Expecting a boolean value.");
|
||||
}
|
||||
if (IsInteger() || IsCharacter() || IsCharacterSequence())
|
||||
{
|
||||
int64_t iValue = ValueSignedInteger().Get<int64_t>();
|
||||
if (iValue == 1) return CConstVariant(true);
|
||||
if (iValue == 0) return CConstVariant(false);
|
||||
throw CCompileException(*this, "Expecting a boolean value or an integer value of 1 or 0.");
|
||||
}
|
||||
if (IsNullptr())
|
||||
{
|
||||
if (ValueNullptr().Get<uint64_t>() == 0) return CConstVariant(false);
|
||||
throw CCompileException(*this, "Expecting a nullptr value.");
|
||||
}
|
||||
throw CCompileException(*this, "Expecting a boolean value.");
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueNullptr() const
|
||||
{
|
||||
if (IsNullptr()) return CConstVariant(0);
|
||||
if (IsInteger() || IsCharacter() || IsCharacterSequence())
|
||||
{
|
||||
int64_t iValue = ValueSignedInteger().Get<int64_t>();
|
||||
if (iValue == 0) return CConstVariant(0);
|
||||
throw CCompileException(*this, "Expecting a nullptr value or the integer value of 0.");
|
||||
}
|
||||
throw CCompileException(*this, "Expecting a nullptr value.");
|
||||
}
|
||||
|
||||
CConstVariant CToken::ValueString() const
|
||||
{
|
||||
|
||||
if (IsString())
|
||||
{
|
||||
if (IsAscii())
|
||||
{
|
||||
uint32_t uiByteCnt = 0;
|
||||
std::string ssValue;
|
||||
InterpretCText(m_szCode + 1, "\"", ssValue, uiByteCnt, false, true);
|
||||
return CConstVariant(ssValue);
|
||||
}
|
||||
if (IsUtf8())
|
||||
{
|
||||
uint32_t uiByteCnt = 0;
|
||||
std::string ssValue;
|
||||
InterpretCText(m_szCode + 3, "\"", ssValue, uiByteCnt);
|
||||
return CConstVariant(ssValue);
|
||||
}
|
||||
if (IsUtf16())
|
||||
{
|
||||
uint32_t uiByteCnt = 0;
|
||||
std::u16string ssValue;
|
||||
InterpretCText(m_szCode + 2, "\"", ssValue, uiByteCnt);
|
||||
return CConstVariant(ssValue);
|
||||
}
|
||||
if (IsUtf32())
|
||||
{
|
||||
uint32_t uiByteCnt = 0;
|
||||
std::u32string ssValue;
|
||||
InterpretCText(m_szCode + 2, "\"", ssValue, uiByteCnt);
|
||||
return CConstVariant(ssValue);
|
||||
}
|
||||
if (IsWide())
|
||||
{
|
||||
uint32_t uiByteCnt = 0;
|
||||
std::wstring ssValue;
|
||||
InterpretCText(m_szCode + 2, "\"", ssValue, uiByteCnt);
|
||||
return CConstVariant(ssValue);
|
||||
}
|
||||
}
|
||||
|
||||
throw CCompileException(*this, "Cannot convert type to string.");
|
||||
}
|
||||
332
sdv_executables/sdv_idl_compiler/token.h
Normal file
332
sdv_executables/sdv_idl_compiler/token.h
Normal file
@@ -0,0 +1,332 @@
|
||||
#ifndef LOCATION_H
|
||||
#define LOCATION_H
|
||||
|
||||
#include "constvariant.h"
|
||||
#include "lexerbase.h"
|
||||
#include "parsecontext.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
// Forward declaration
|
||||
class CCodePos;
|
||||
|
||||
/**
|
||||
* @brief Token within the source string.
|
||||
*/
|
||||
class CToken
|
||||
{
|
||||
friend CCodePos;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CToken() = default;
|
||||
|
||||
/**
|
||||
* @brief Specific constructor for code insertion.
|
||||
* @details During processing, it sometimes is necessary to add created tokens to token lists.
|
||||
* @attention The constructor doesn't check for validity of the token text and types.
|
||||
* @param[in] rssText The text the token represents.
|
||||
* @param[in] eType The token type (default identifier).
|
||||
*/
|
||||
CToken(const std::string& rssText, ETokenType eType = ETokenType::token_identifier);
|
||||
|
||||
/**
|
||||
* @brief Specific constructor for code insertion.
|
||||
* @details During processing, it sometimes is necessary to add created tokens to token lists.
|
||||
* @attention The constructor doesn't check for validity of the token text and types.
|
||||
* @param[in] rssText The text the token represents.
|
||||
* @param[in] eLiteralType The token literal type (default undefined).
|
||||
*/
|
||||
CToken(const std::string& rssText, ETokenLiteralType eLiteralType);
|
||||
|
||||
/**
|
||||
* @brief Specific constructor for code insertion.
|
||||
* @details During processing, it sometimes is necessary to add created tokens to token lists.
|
||||
* @attention The constructor doesn't check for validity of the token text and types.
|
||||
* @param[in] rssText The text the token represents.
|
||||
* @param[in] eMetaType The token meta type (default undefined).
|
||||
*/
|
||||
CToken(const std::string& rssText, ETokenMetaType eMetaType);
|
||||
|
||||
/**
|
||||
* @brief Move start; adjusts the start position of the token. Does not adjust the end.
|
||||
* @param[in] iOffset The amount of characters to adjust. A negative offset moves to the left.
|
||||
* @attention Does move before the beginning of the line.
|
||||
* @attention Does not move past the end of the token.
|
||||
*/
|
||||
void MoveStart(int32_t iOffset);
|
||||
|
||||
/**
|
||||
* @brief Move end; adjusts the end position of the token. Does not adjust the start.
|
||||
* @param[in] iOffset The amount of characters to adjust. A negative offset moves to the left.
|
||||
* @attention Does move before the start of the token.
|
||||
*/
|
||||
void MoveEnd(int32_t iOffset);
|
||||
|
||||
/**
|
||||
* @brief Equality operator.
|
||||
* @param[in] rtoken The token to compare with.
|
||||
* @return Returns 'true' if the provided token and this token are identical. Otherwise returns 'false'.
|
||||
*/
|
||||
bool operator==(const CToken& rtoken) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality operator.
|
||||
* @param[in] rtoken The token to compare with.
|
||||
* @return Returns 'true' if the provided token and this token are not identical. Otherwise returns 'false'.
|
||||
*/
|
||||
bool operator!=(const CToken& rtoken) const;
|
||||
|
||||
/**
|
||||
* @brief Return the line number.
|
||||
* @return The line number.
|
||||
*/
|
||||
uint32_t GetLine() const;
|
||||
|
||||
/**
|
||||
* @brief Return the column number.
|
||||
* @return Get column number.
|
||||
*/
|
||||
uint32_t GetCol() const;
|
||||
|
||||
/**
|
||||
* @brief Get the last line this token on.
|
||||
* @return The line number.
|
||||
*/
|
||||
uint32_t GetEndLine() const;
|
||||
|
||||
/**
|
||||
* @brief Get the last column this token is on.
|
||||
* @return The column number.
|
||||
*/
|
||||
uint32_t GetEndCol() const;
|
||||
|
||||
/**
|
||||
* @brief Return the length of the current token snippet. Or 0xFFFFFFFF when the rest of the code is part of this snippet.
|
||||
* @return Returns the length of the token snippet.
|
||||
*/
|
||||
uint32_t GetLength() const;
|
||||
|
||||
/**
|
||||
* @brief Get the token type.
|
||||
* @return The token type of this token.
|
||||
*/
|
||||
ETokenType GetType() const;
|
||||
|
||||
/**
|
||||
* @brief Get the token's literal type (if the token is a literal token).
|
||||
* @return The literal type of this token when the token is a literal token. ETokenLiteralType::token_undefined otherwise.
|
||||
*/
|
||||
ETokenLiteralType GetLiteralType() const;
|
||||
|
||||
/**
|
||||
* @brief Get the token's meta type (if the token is a meta token).
|
||||
* @return The meta type of this token when the token is a meta token. ETokenMetaType::token_undefined otherwise.
|
||||
*/
|
||||
ETokenMetaType GetMetaType() const;
|
||||
|
||||
/**
|
||||
* @brief Validity operator.
|
||||
* @return Returns 'true' when the token is valid. Otherwise returns 'false'.
|
||||
*/
|
||||
operator bool() const;
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Returns the code chunk that the token points to. When a length is part of the token the string is limited to
|
||||
* the length. Otherwise the chunk contains the complete string starting at the token.
|
||||
* @return Returns the string chunk.
|
||||
*/
|
||||
operator const std::string&() const;
|
||||
const char* c_str() const;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the left part of the string.
|
||||
* @param[in] uiLen The amount of characters to return.
|
||||
* @return Returns the left part of the string chunk.
|
||||
*/
|
||||
std::string Left(uint32_t uiLen) const;
|
||||
|
||||
/**
|
||||
* @brief Get the right part of the string.
|
||||
* @param[in] uiLen The amount of characters to return.
|
||||
* @return Returns the right part of the string chunk.
|
||||
*/
|
||||
std::string Right(uint32_t uiLen) const;
|
||||
|
||||
/**
|
||||
* @brief String equality operator.
|
||||
* @param[in] szString Zero terminated string to use for the comparison.
|
||||
* @return Returns 'true' if the string chunk of this token is identical with the provided string. Otherwise returns
|
||||
* 'false'.
|
||||
*/
|
||||
bool operator==(const char* szString) const;
|
||||
|
||||
/**
|
||||
* @brief String inequality operator.
|
||||
* @param[in] szString Zero terminated string to use for the comparison.
|
||||
* @return Returns 'true' if the string chunk of this token is not identical with the provided string. Otherwise returns
|
||||
* 'false'.
|
||||
*/
|
||||
bool operator!=(const char* szString) const;
|
||||
|
||||
/**
|
||||
* @brief String equality operator.
|
||||
* @param[in] rssString String object to use for the comparison.
|
||||
* @return Returns 'true' if the string chunk of this token is identical with the provided string. Otherwise returns
|
||||
* 'false'.
|
||||
*/
|
||||
bool operator==(const std::string& rssString) const;
|
||||
|
||||
/**
|
||||
* @brief String inequality operator.
|
||||
* @param[in] rssString String object to use for the comparison.
|
||||
* @return Returns 'true' if the string chunk of this token is identical with the provided string. Otherwise returns
|
||||
* 'false'.
|
||||
*/
|
||||
bool operator!=(const std::string& rssString) const;
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Information functions.
|
||||
* @return Return the information.
|
||||
*/
|
||||
bool IsLiteral() const;
|
||||
bool IsInteger() const;
|
||||
bool IsSigned() const;
|
||||
bool IsUnsigned() const;
|
||||
bool IsLong() const;
|
||||
bool IsLongLong() const;
|
||||
bool IsDecimal() const;
|
||||
bool IsHexadecimal() const;
|
||||
bool IsOctal() const;
|
||||
bool IsBinary() const;
|
||||
bool IsFloatingPoint() const;
|
||||
bool IsFixedPoint() const;
|
||||
bool IsCharacter() const;
|
||||
bool IsCharacterSequence() const;
|
||||
bool IsString() const;
|
||||
bool IsAscii() const;
|
||||
bool IsUtf8() const;
|
||||
bool IsUtf16() const;
|
||||
bool IsUtf32() const;
|
||||
bool IsWide() const;
|
||||
bool IsRawString() const;
|
||||
bool IsBoolean() const;
|
||||
bool IsNullptr() const;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the value of the token in the smallest fitting data type.
|
||||
* @throw The function throws an exception in case the assignment is not possible.
|
||||
* @return Returns the value as const variant.
|
||||
*/
|
||||
CConstVariant ValueRef() const;
|
||||
|
||||
/**
|
||||
* @brief Set source code context.
|
||||
* @details The parser can attach source code context. The context contains all the allocated memory this token is pointing
|
||||
* to and holding the context in the token prevents unexpected disappearance of the token during post-processing.
|
||||
* @param[in] rptrContext Reference to the smart pointer to the source code context.
|
||||
*/
|
||||
void SetContext(const CContextPtr& rptrContext) { m_ptrContext = rptrContext; }
|
||||
|
||||
/**
|
||||
* @brief Get source code context.
|
||||
* @return Pointer to the source code context.
|
||||
*/
|
||||
CContextPtr GetContext() const { return m_ptrContext; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Returns if an occurrence of one or more characters exist in the code chunk.
|
||||
* @tparam TChar The type of the character to check (must be 'char' to work).
|
||||
* @param[in] c The character to check to exists within the code chunk.
|
||||
* @param[in] tChars Optionally zero or more characters to check to exists within the code chunk.
|
||||
*/
|
||||
template <typename... TChar>
|
||||
bool Contains(char c, TChar... tChars) const;
|
||||
|
||||
/**
|
||||
* @brief Returns if an occurrence of one or more strings exist in the code chunk.
|
||||
* @tparam TString The type of the string to check (must be 'const char*' to work).
|
||||
* @param[in] sz The string to check to exists within the code chunk.
|
||||
* @param[in] tStrings Optionally zero or more strings to check to exists within the code chunk.
|
||||
*/
|
||||
template <typename... TString>
|
||||
bool Contains(const char* sz, TString... tStrings) const;
|
||||
|
||||
/**
|
||||
* @brief Dummy contains function when there are no more arguments any more.
|
||||
*/
|
||||
bool Contains() const { return false; }
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Get the value of the token in supplied integer data type. This counts for integers, characters and character
|
||||
* sequences.
|
||||
* @throw The function throws an exception when the value exceeds or falls below the max or min value of the requested type.
|
||||
* @return Returns the value.
|
||||
*/
|
||||
CConstVariant ValueSignedInteger() const;
|
||||
CConstVariant ValueUnsignedInteger() const;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the value of the token in supplied floating data type.
|
||||
* @throw The function throws an exception when the value exceeds or falls below the max or min value of the requested type.
|
||||
* @return Returns the value.
|
||||
*/
|
||||
CConstVariant ValueFloat() const;
|
||||
|
||||
/**
|
||||
* @brief Get the value of the token for a boolean data type. The boolean data type accepts the C++ style "true" and "false"
|
||||
* as well as the IDL style "TRUE" and "FALSE". Integer and character and character sequence types can only be assigned to a
|
||||
* boolean if they return the value 1 or 0.
|
||||
* @return Returns the boolean value of the token.
|
||||
*/
|
||||
CConstVariant ValueBoolean() const;
|
||||
|
||||
/**
|
||||
* @brief Get the value of the token for a nullptr data type. The nullptr data type accepts the C style "NULL" and the C++
|
||||
* style "nullptr". Nullptr will be interpreted as the value 0 when assigned to a an integral data type.
|
||||
* @return Returns the nullptr value of the token (which is always 0).
|
||||
*/
|
||||
CConstVariant ValueNullptr() const;
|
||||
|
||||
/**
|
||||
* @brief Get the value of the token for a string data type.
|
||||
* @return Returns the string value of the token.
|
||||
*/
|
||||
CConstVariant ValueString() const;
|
||||
|
||||
private:
|
||||
CContextPtr m_ptrContext; ///< Smart pointer holding a reference
|
||||
///< to the source file.
|
||||
const char* m_szCode = nullptr; ///< Start of the token in IDL code.
|
||||
uint32_t m_uiLen = 0xffffffff; ///< Length of the token or 0xffffffff
|
||||
///< for the whole string.
|
||||
uint32_t m_uiLine = 0; ///< Current line of the token.
|
||||
uint32_t m_uiCol = 0; ///< Current column of the token.
|
||||
ETokenType m_eType = ETokenType::token_none; ///< The type of the token.
|
||||
ETokenLiteralType m_eLiteralType = ETokenLiteralType::token_undefined; ///< The type for literal tokens.
|
||||
ETokenMetaType m_eMetaType = ETokenMetaType::token_undefined; ///< The type for meta tokens.
|
||||
mutable std::string m_ssCache; ///< Cached string created when
|
||||
///< requesting a string chunk.
|
||||
};
|
||||
|
||||
#endif // !defined LOCATION_H
|
||||
206
sdv_executables/sdv_idl_compiler/tokenlist.cpp
Normal file
206
sdv_executables/sdv_idl_compiler/tokenlist.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "tokenlist.h"
|
||||
|
||||
CTokenList::CTokenList() : m_itCurrent(end())
|
||||
{}
|
||||
|
||||
CTokenList::CTokenList(const CTokenList& rlstTokens) :
|
||||
std::list<CToken>(rlstTokens)
|
||||
{
|
||||
// Determine at what token the iterator points to
|
||||
CTokenList::const_iterator itTemp = rlstTokens.begin();
|
||||
if (rlstTokens.empty())
|
||||
{
|
||||
m_itCurrent = end();
|
||||
return;
|
||||
}
|
||||
m_itCurrent = begin();
|
||||
while (itTemp != rlstTokens.m_itCurrent)
|
||||
{
|
||||
++itTemp;
|
||||
++m_itCurrent;
|
||||
if (itTemp == rlstTokens.end() || m_itCurrent == end())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CTokenList::CTokenList(CTokenList&& rlstTokens) noexcept :
|
||||
std::list<CToken>(std::move(rlstTokens)), m_itCurrent(rlstTokens.m_itCurrent)
|
||||
{
|
||||
rlstTokens.m_itCurrent = rlstTokens.end();
|
||||
}
|
||||
|
||||
CTokenList& CTokenList::operator=(const CTokenList& rlstTokens)
|
||||
{
|
||||
static_cast<std::list<CToken>&>(*this) = rlstTokens;
|
||||
if (rlstTokens.empty()) return *this;
|
||||
|
||||
// Determine at what token the iterator points to
|
||||
CTokenList::const_iterator itTemp = rlstTokens.begin();
|
||||
m_itCurrent = begin();
|
||||
while (itTemp != rlstTokens.m_itCurrent)
|
||||
{
|
||||
++itTemp;
|
||||
++m_itCurrent;
|
||||
if (itTemp == rlstTokens.end() || m_itCurrent == end())
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CTokenList& CTokenList::operator=(CTokenList&& rlstTokens) noexcept
|
||||
{
|
||||
static_cast<std::list<CToken>&>(*this) = std::move(rlstTokens);
|
||||
m_itCurrent = rlstTokens.m_itCurrent;
|
||||
rlstTokens.m_itCurrent = rlstTokens.end();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const CToken& CTokenList::Current(size_t nIndex /*= 0*/) const
|
||||
{
|
||||
static const CToken tokenDummy;
|
||||
std::list<CToken>::iterator itPos = m_itCurrent;
|
||||
for (size_t nCnt = 0; nCnt < nIndex; nCnt++)
|
||||
{
|
||||
if (itPos == end()) return tokenDummy;
|
||||
++itPos;
|
||||
}
|
||||
if (itPos != end())
|
||||
return *itPos;
|
||||
else
|
||||
return tokenDummy;
|
||||
}
|
||||
|
||||
const CToken& CTokenList::LastValid() const
|
||||
{
|
||||
static const CToken tokenDummy;
|
||||
if (empty()) return tokenDummy;
|
||||
if (m_itCurrent != end())
|
||||
return *m_itCurrent;
|
||||
else
|
||||
return *rbegin();
|
||||
}
|
||||
|
||||
CTokenList CTokenList::operator++(int) const
|
||||
{
|
||||
CTokenList tl(*this);
|
||||
operator++();
|
||||
return tl;
|
||||
}
|
||||
|
||||
const CTokenList& CTokenList::operator++() const
|
||||
{
|
||||
if (!empty() && m_itCurrent != end()) ++m_itCurrent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CTokenList::End() const
|
||||
{
|
||||
return empty() || m_itCurrent == end();
|
||||
}
|
||||
|
||||
void CTokenList::push_front(CToken&& rToken)
|
||||
{
|
||||
bool bEmpty = empty();
|
||||
bool bEnd = bEmpty || m_itCurrent == end();
|
||||
bool bBegin = !bEmpty && m_itCurrent == begin();
|
||||
std::list<CToken>::push_front(rToken);
|
||||
if (bEmpty) m_itCurrent = begin();
|
||||
else if (bEnd) m_itCurrent = end();
|
||||
else if (bBegin) m_itCurrent = begin();
|
||||
}
|
||||
|
||||
void CTokenList::push_front(const CToken& rToken)
|
||||
{
|
||||
bool bEmpty = empty();
|
||||
bool bEnd = bEmpty || m_itCurrent == end();
|
||||
bool bBegin = !bEmpty && m_itCurrent == begin();
|
||||
std::list<CToken>::push_front(rToken);
|
||||
if (bEmpty) m_itCurrent = begin();
|
||||
else if (bEnd) m_itCurrent = end();
|
||||
else if (bBegin) m_itCurrent = begin();
|
||||
}
|
||||
|
||||
void CTokenList::pop_front()
|
||||
{
|
||||
if (empty()) return;
|
||||
bool bEnd = m_itCurrent == end();
|
||||
bool bBegin = m_itCurrent == begin();
|
||||
std::list<CToken>::pop_front();
|
||||
if (bEnd) m_itCurrent = end();
|
||||
else if (bBegin) m_itCurrent = begin();
|
||||
}
|
||||
|
||||
void CTokenList::push_back(const CToken& rToken)
|
||||
{
|
||||
bool bEmpty = empty();
|
||||
bool bEnd = bEmpty || m_itCurrent == end();
|
||||
std::list<CToken>::push_back(rToken);
|
||||
if (bEmpty) m_itCurrent = begin();
|
||||
else if (bEnd)
|
||||
{
|
||||
m_itCurrent = end();
|
||||
--m_itCurrent;
|
||||
}
|
||||
}
|
||||
|
||||
void CTokenList::push_back(CToken&& rToken)
|
||||
{
|
||||
bool bEmpty = empty();
|
||||
bool bEnd = bEmpty || m_itCurrent == end();
|
||||
std::list<CToken>::push_back(std::move(rToken));
|
||||
if (bEmpty) m_itCurrent = begin();
|
||||
else if (bEnd)
|
||||
{
|
||||
m_itCurrent = end();
|
||||
--m_itCurrent;
|
||||
}
|
||||
}
|
||||
|
||||
void CTokenList::pop_back()
|
||||
{
|
||||
if (empty()) return;
|
||||
bool bEnd = m_itCurrent == end();
|
||||
std::list<CToken>::pop_back();
|
||||
if (bEnd) m_itCurrent = end();
|
||||
}
|
||||
|
||||
void CTokenList::insert(const CToken& rToken)
|
||||
{
|
||||
if (empty()) m_itCurrent = end();
|
||||
|
||||
// Insert only if the token is not equal to the last token in the list
|
||||
if (m_itCurrent != begin())
|
||||
{
|
||||
iterator itPrev = m_itCurrent;
|
||||
--itPrev;
|
||||
if (*itPrev == rToken)
|
||||
{
|
||||
m_itCurrent = itPrev;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// New token; insert the token in the list
|
||||
m_itCurrent = std::list<CToken>::insert(m_itCurrent, rToken);
|
||||
}
|
||||
|
||||
void CTokenList::insert(CToken&& rToken)
|
||||
{
|
||||
if (empty()) m_itCurrent = end();
|
||||
|
||||
// Insert only if the token is not equal to the last token in the list
|
||||
if (m_itCurrent != begin())
|
||||
{
|
||||
iterator itPrev = m_itCurrent;
|
||||
--itPrev;
|
||||
if (*itPrev == rToken)
|
||||
{
|
||||
m_itCurrent = itPrev;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// New token; insert the token in the list
|
||||
m_itCurrent = std::list<CToken>::insert(m_itCurrent, std::move(rToken));
|
||||
}
|
||||
148
sdv_executables/sdv_idl_compiler/tokenlist.h
Normal file
148
sdv_executables/sdv_idl_compiler/tokenlist.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#ifndef TOKENLIST_H
|
||||
#define TOKENLIST_H
|
||||
|
||||
#include "token.h"
|
||||
#include <list>
|
||||
|
||||
/**
|
||||
* @brief Token list class with extra functions to navigate through the list safely.
|
||||
*/
|
||||
class CTokenList : public std::list<CToken>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
CTokenList();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* @param[in] rlstTokens Reference to the token list.
|
||||
*/
|
||||
CTokenList(const CTokenList& rlstTokens);
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
* @param[in] rlstTokens Reference to the token list.
|
||||
*/
|
||||
CTokenList(CTokenList&& rlstTokens) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Assignment operator
|
||||
* @param[in] rlstTokens Reference to the token list.
|
||||
* @return Reference to this tokenlist.
|
||||
*/
|
||||
CTokenList& operator=(const CTokenList& rlstTokens);
|
||||
|
||||
/**
|
||||
* @brief Move operator
|
||||
* @param[in] rlstTokens Reference to the token list.
|
||||
* @return Reference to this tokenlist.
|
||||
*/
|
||||
CTokenList& operator=(CTokenList&& rlstTokens) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the token relative to the current position or if not valid an empty token.
|
||||
* @param[in] nIndex The amount of tokens to skip before returning the token.
|
||||
* @return Reference to the token.
|
||||
*/
|
||||
const CToken& Current(size_t nIndex = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Get the current or last valid token. If there is no token, get an empty token.
|
||||
* @return Reference to the current token.
|
||||
*/
|
||||
const CToken& LastValid() const;
|
||||
|
||||
/**
|
||||
* @brief Increase the current token to the next position in the tokenlist.
|
||||
* @returns Copy of this tokenlist before incrementation.
|
||||
*/
|
||||
CTokenList operator++(int) const;
|
||||
|
||||
/**
|
||||
* @brief Increase the current token to the next position in the tokenlist.
|
||||
* @returns Reference to this tokenlist.
|
||||
*/
|
||||
const CTokenList& operator++() const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether the current token has reached the end of the list or whether the list is empty.
|
||||
* @return Returns 'true' when the end of the token list has been reached; otherwise 'false'.
|
||||
*/
|
||||
bool End() const;
|
||||
|
||||
/**
|
||||
* \{
|
||||
* @brief Add a token to the front of the list. Overload of std::list::push_front.
|
||||
* @param[in, out] rToken Reference to the token to insert.
|
||||
*/
|
||||
void push_front(const CToken& rToken);
|
||||
void push_front(CToken&& rToken);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Removes the first element of the container. Overload of std::list::pop_front.
|
||||
*/
|
||||
void pop_front();
|
||||
|
||||
/**
|
||||
* \{
|
||||
* @brief Add a token to the end of the list. Overload of std::list::push_back.
|
||||
* @param[in, out] rToken Reference to the token to insert.
|
||||
*/
|
||||
void push_back(const CToken& rToken);
|
||||
void push_back(CToken&& rToken);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Removes the last element of the container. Overload of std::list::pop_front.
|
||||
*/
|
||||
void pop_back();
|
||||
|
||||
/**
|
||||
* \{
|
||||
* @brief Insert a token at the current position in the list, moving the current position to the newly inserted token. Loose
|
||||
* overload of std::list::insert.
|
||||
* @param[in, out] rToken Reference to the token to insert.
|
||||
*/
|
||||
void insert(const CToken& rToken);
|
||||
void insert(CToken&& rToken);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a token with the supplied arguments and place it at the end of the list.
|
||||
* @tparam Args The argument types
|
||||
* @param[in] args The arguments
|
||||
* @return Reference to the token added.
|
||||
*/
|
||||
template<class... Args>
|
||||
CToken& emplace_back(Args&&... args);
|
||||
|
||||
private:
|
||||
mutable std::list<CToken>::iterator m_itCurrent; ///< Current iterator
|
||||
};
|
||||
|
||||
template<class... Args>
|
||||
CToken& CTokenList::emplace_back(Args&&... args)
|
||||
{
|
||||
bool bEmpty = empty();
|
||||
bool bEnd = bEmpty || m_itCurrent == end();
|
||||
CToken& rToken = std::list<CToken>::emplace_back(args...);
|
||||
if (bEmpty) m_itCurrent = begin();
|
||||
else if (bEnd)
|
||||
{
|
||||
m_itCurrent = end();
|
||||
--m_itCurrent;
|
||||
}
|
||||
return rToken;
|
||||
}
|
||||
|
||||
#endif // !defined(TOKENLIST_H)
|
||||
Reference in New Issue
Block a user