mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
500 lines
21 KiB
C++
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;
|
|
}
|