#include "context.h" #include "../exception.h" #include #include #include #include #include #include #include #include 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(); if (!m_pCompilerInfo) throw CCompileException("Internal error: compiler info is not available."); m_pOption = m_pParser->GetInterface(); 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(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(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(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(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 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(); 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(pDeclType->GetTypeDefinition()); if (!pTypeInfo) throw CCompileException("Internal error: the entity doesn't expose information."); rsCDeclInfo.ssDeclType = bScopedName ? GetRelativeScopedName(pTypeInfo->GetScopedName(), rssScope) : static_cast(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; 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; }