Files
openvehicle-api/sdv_executables/sdv_idl_compiler/generator/context.cpp
tompzf 6ed4b1534e Precommit (#1)
* first commit

* cleanup
2025-11-04 13:28:06 +01:00

500 lines
21 KiB
C++

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