Precommit (#1)

* first commit

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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;
}

View 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);
}

View 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)

View 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();
}

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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)

View 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.");
}

View 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

View 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));
}

View 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)