From 6005f8dcbdbf3b47b73262c5d8420957758954b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D1=80=D0=BE=D0=BB=D1=8C=20=D0=93=D0=BB=D0=B5?= =?UTF-8?q?=D0=B1?= Date: Wed, 14 May 2025 21:12:56 +0300 Subject: [PATCH 1/4] [builder][tools] Add gwf-to-scs flag --- sc-tools/sc-builder/src/sc_builder_runner.cpp | 40 +++- sc-tools/sc-builder/src/sc_scs_writer.cpp | 182 ------------------ sc-tools/sc-builder/src/sc_scs_writer.hpp | 54 ------ 3 files changed, 39 insertions(+), 237 deletions(-) delete mode 100644 sc-tools/sc-builder/src/sc_scs_writer.cpp delete mode 100644 sc-tools/sc-builder/src/sc_scs_writer.hpp diff --git a/sc-tools/sc-builder/src/sc_builder_runner.cpp b/sc-tools/sc-builder/src/sc_builder_runner.cpp index 6cecfaaea..63665da86 100644 --- a/sc-tools/sc-builder/src/sc_builder_runner.cpp +++ b/sc-tools/sc-builder/src/sc_builder_runner.cpp @@ -7,12 +7,14 @@ #include "sc_builder_runner.hpp" #include +#include #include #include #include #include "sc-builder/builder.hpp" +#include "gwf_translator.hpp" void PrintHelpMessage(std::string const & binaryName) { @@ -44,7 +46,10 @@ void PrintHelpMessage(std::string const & binaryName) << " --clear Run sc-builder in a mode that overwrites existing knowledge base " "binaries.\n" << " --version Display the version of " << binaryName << ".\n" - << " --help Display this help message.\n"; + << " --help Display this help message.\n" + << " --gwf-to-scs Translate a GWF file to SCs and save the result to a specified file.\n" + << " --gwf-output Specify the output file for the GWF-to-SCs translation (default: .scs).\n" + << " --verbose|-v Enable verbose logging.\n"; } sc_int RunBuilder(sc_int argc, sc_char * argv[]) @@ -65,6 +70,36 @@ try return EXIT_SUCCESS; } + if (options.Has({"gwf-to-scs"})) + { + std::string gwfFile = options[{"gwf-to-scs"}].second; + std::string outputFile = options.Has({"gwf-output"}) ? options[{"gwf-output"}].second : gwfFile + ".scs"; + + // Инициализация контекста для транслятора + ScMemoryContext ctx; + GWFTranslator translator(ctx); + std::string scsText = translator.TranslateXMLFileContentToSCs(gwfFile); + + // Сохранение результата в файл + std::ofstream output(outputFile, std::ios::binary); + if (!output.is_open()) + { + std::cout << "Error: Cannot open output file `" << outputFile << "`.\n"; + return EXIT_FAILURE; + } + output << scsText; + output.close(); + + if (output.fail()) + { + std::cout << "Error: Failed to write to output file `" << outputFile << "`.\n"; + return EXIT_FAILURE; + } + + std::cout << "GWF file `" << gwfFile << "` successfully translated to SCs and saved as `" << outputFile << "`.\n"; + return EXIT_SUCCESS; + } + BuilderParams params; if (options.Has({"input", "i"})) params.m_inputPath = options[{"input", "i"}].second; @@ -155,8 +190,11 @@ try "[sc-builder] group.\n" << "Make sure this path is a valid directory where you have write permissions and that it exists.\n" << "For more information, run with --help.\n"; + + return EXIT_FAILURE; } + params.m_resultStructureUpload = formedMemoryParams.init_memory_generated_upload; if (formedMemoryParams.init_memory_generated_structure != nullptr) params.m_resultStructureSystemIdtf = formedMemoryParams.init_memory_generated_structure; diff --git a/sc-tools/sc-builder/src/sc_scs_writer.cpp b/sc-tools/sc-builder/src/sc_scs_writer.cpp deleted file mode 100644 index 8fdde8367..000000000 --- a/sc-tools/sc-builder/src/sc_scs_writer.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ - -#include "sc_scs_writer.hpp" - -#include - -#include - -#include "sc_scg_element.hpp" -#include "sc_scs_element.hpp" - -using namespace Constants; - -std::string SCsWriter::MakeAlias(std::string const & prefix, std::string const & elementId) -{ - return ALIAS_PREFIX + prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); -} - -bool SCsWriter::IsVariable(std::string const & elementType) -{ - return elementType.find(VAR) != std::string::npos; -} - -bool SCsWriter::SCgIdentifierCorrector::IsRussianIdentifier(std::string const & identifier) -{ - std::regex identifierPatternRus(R"(^[0-9a-zA-Z_\xD0\x80-\xD1\x8F\xD1\x90-\xD1\x8F\xD1\x91\xD0\x81*' ]*$)"); - return std::regex_match(identifier, identifierPatternRus); -} - -bool SCsWriter::SCgIdentifierCorrector::IsEnglishIdentifier(std::string const & identifier) -{ - std::regex identifierPatternEng("^[0-9a-zA-Z_]*$"); - return std::regex_match(identifier, identifierPatternEng); -} - -std::string SCsWriter::SCgIdentifierCorrector::GenerateCorrectedIdentifier( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar) -{ - if (systemIdentifier.empty()) - return GenerateIdentifierForUnresolvedCharacters(systemIdentifier, elementId, isVar); - - if (isVar && systemIdentifier[0] != UNDERSCORE[0]) - return GenerateSCsIdentifierForVariable(systemIdentifier); - - return systemIdentifier; -} - -std::string SCsWriter::SCgIdentifierCorrector::GenerateIdentifierForUnresolvedCharacters( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar) -{ - std::string const & prefix = isVar ? EL_VAR_PREFIX : EL_PREFIX; - systemIdentifier = prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); - return systemIdentifier; -} - -std::string SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifierForVariable(std::string & systemIdentifier) -{ - return UNDERSCORE + systemIdentifier; -} - -void SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifier( - SCgElementPtr const & scgElement, - SCsElementPtr & scsElement) -{ - scsElement->SetIdentifierForSCs(scgElement->GetIdentifier()); - bool isVar = SCsWriter::IsVariable(scgElement->GetType()); - - std::string const & systemIdentifier = scsElement->GetIdentifierForSCs(); - if (!IsEnglishIdentifier(systemIdentifier)) - { - if (IsRussianIdentifier(systemIdentifier)) - scsElement->SetMainIdentifier(systemIdentifier); - - scsElement->SetIdentifierForSCs(""); - } - - std::string id = scgElement->GetId(); - std::string correctedIdentifier = scsElement->GetIdentifierForSCs(); - correctedIdentifier = GenerateCorrectedIdentifier(correctedIdentifier, id, isVar); - scsElement->SetIdentifierForSCs(correctedIdentifier); - - auto const & tag = scgElement->GetTag(); - if (tag == PAIR || tag == ARC) - scsElement->SetIdentifierForSCs(SCsWriter::MakeAlias(CONNECTOR, id)); -} - -void SCsWriter::Write( - SCgElements const & elements, - std::string const & filePath, - Buffer & buffer, - size_t depth, - std::unordered_set & writtenElements) -{ - std::list dependedConnectors; - - auto it = elements.cbegin(); - while (it != elements.cend()) - { - SCgElementPtr scgElement = it->second; - - bool isElementWritable = true; - std::string const & scgTag = scgElement->GetTag(); - if (scgTag == BUS) - isElementWritable = false; - else if (scgTag == PAIR || scgTag == ARC) - { - auto scgConnector = std::dynamic_pointer_cast(scgElement); - auto const & source = scgConnector->GetSource(); - auto const & target = scgConnector->GetTarget(); - - if (writtenElements.find(source) == writtenElements.cend() - || writtenElements.find(target) == writtenElements.cend()) - { - dependedConnectors.push_back(scgElement); - isElementWritable = false; - } - } - - if (isElementWritable) - { - auto const & scsElement = SCsElementFactory::CreateSCsElementForSCgElement(scgElement); - scsElement->ConvertFromSCgElement(scgElement); - scsElement->Dump(filePath, buffer, depth, writtenElements); - writtenElements.insert(scgElement); - } - - ++it; - } - if (!dependedConnectors.empty()) - { - size_t connectorsWrittenOnPreviousIteration = dependedConnectors.size(); - while (connectorsWrittenOnPreviousIteration != 0) - { - connectorsWrittenOnPreviousIteration = 0; - for (auto dependedConnector = dependedConnectors.cbegin(); dependedConnector != dependedConnectors.cend();) - { - auto scgConnector = std::dynamic_pointer_cast(*dependedConnector); - auto const & source = scgConnector->GetSource(); - auto const & target = scgConnector->GetTarget(); - bool isElementWritable = - (writtenElements.find(source) != writtenElements.cend() - && writtenElements.find(target) != writtenElements.cend()); - if (isElementWritable) - { - auto const & scsElement = SCsElementFactory::CreateSCsElementForSCgElement(*dependedConnector); - scsElement->ConvertFromSCgElement(*dependedConnector); - scsElement->Dump(filePath, buffer, depth, writtenElements); - writtenElements.insert(*dependedConnector); - ++connectorsWrittenOnPreviousIteration; - dependedConnector = dependedConnectors.erase(dependedConnector); - } - else - ++dependedConnector; - } - } - } - if (!dependedConnectors.empty()) - SC_THROW_EXCEPTION( - utils::ExceptionInvalidState, - "SCsWriter: unknown incident elements for " << dependedConnectors.size() << " connectors at `" << filePath - << "`."); -} - -void SCsWriter::WriteMainIdentifier( - Buffer & buffer, - size_t depth, - std::string const & systemIdentifier, - std::string const & mainIdentifier) -{ - buffer << NEWLINE; - buffer.AddTabs(depth) << systemIdentifier << NEWLINE; - buffer.AddTabs(depth) << SPACE << SC_CONNECTOR_DCOMMON_R << SPACE << NREL_MAIN_IDTF << COLON << SPACE << OPEN_BRACKET - << mainIdentifier << CLOSE_BRACKET << ELEMENT_END << NEWLINE; -} diff --git a/sc-tools/sc-builder/src/sc_scs_writer.hpp b/sc-tools/sc-builder/src/sc_scs_writer.hpp deleted file mode 100644 index 2e6b78f93..000000000 --- a/sc-tools/sc-builder/src/sc_scs_writer.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ - -#pragma once - -#include -#include -#include - -#include "buffer.hpp" -#include "gwf_translator_constants.hpp" -#include "sc_scs_element.hpp" - -class SCsWriter -{ -public: - static void Write( - SCgElements const & elements, - std::string const & filePath, - Buffer & buffer, - size_t depth, - std::unordered_set & writtenElements); - - class SCgIdentifierCorrector - { - public: - static void GenerateSCsIdentifier(SCgElementPtr const & scgElement, SCsElementPtr & scsElement); - - private: - static bool IsRussianIdentifier(std::string const & identifier); - static bool IsEnglishIdentifier(std::string const & identifier); - - static std::string GenerateIdentifierForUnresolvedCharacters( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar); - static std::string GenerateCorrectedIdentifier( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar); - static std::string GenerateSCsIdentifierForVariable(std::string & systemIdentifier); - }; - - static void WriteMainIdentifier( - Buffer & buffer, - size_t depth, - std::string const & systemIdentifier, - std::string const & mainIdentifier); - static std::string MakeAlias(std::string const & prefix, std::string const & elementId); - static bool IsVariable(std::string const & elementType); -}; From 8c6a68551f9fe7e0d9cc5a45ceb65cc04943004f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D1=80=D0=BE=D0=BB=D1=8C=20=D0=93=D0=BB=D0=B5?= =?UTF-8?q?=D0=B1?= Date: Wed, 14 May 2025 21:19:07 +0300 Subject: [PATCH 2/4] [builder][gwf][refactor]Changing the Write method --- sc-tools/sc-builder/src/sc_scs_writer.cpp | 339 ++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 sc-tools/sc-builder/src/sc_scs_writer.cpp diff --git a/sc-tools/sc-builder/src/sc_scs_writer.cpp b/sc-tools/sc-builder/src/sc_scs_writer.cpp new file mode 100644 index 000000000..d92d09edb --- /dev/null +++ b/sc-tools/sc-builder/src/sc_scs_writer.cpp @@ -0,0 +1,339 @@ +/* + * This source file is part of an OSTIS project. For the latest info, see http://ostis.net + * Distributed under the MIT License + * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) + */ + + #include "sc_scs_writer.hpp" + + #include + #include + #include + #include + + #include + + #include "sc_scg_element.hpp" + #include "sc_scs_element.hpp" + #include "sc_scg_to_scs_types_converter.hpp" + + using namespace Constants; + + std::string SCsWriter::MakeAlias(std::string const & prefix, std::string const & elementId) + { + return ALIAS_PREFIX + prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); + } + + bool SCsWriter::IsVariable(std::string const & elementType) + { + return elementType.find(VAR) != std::string::npos; + } + + bool SCsWriter::SCgIdentifierCorrector::IsRussianIdentifier(std::string const & identifier) + { + std::regex identifierPatternRus(R"(^[0-9a-zA-Z_\xD0\x80-\xD1\x8F\xD1\x90-\xD1\x8F\xD1\x91\xD0\x81*' ]*$)"); + return std::regex_match(identifier, identifierPatternRus); + } + + bool SCsWriter::SCgIdentifierCorrector::IsEnglishIdentifier(std::string const & identifier) + { + std::regex identifierPatternEng("^[0-9a-zA-Z_]*$"); + return std::regex_match(identifier, identifierPatternEng); + } + + std::string SCsWriter::SCgIdentifierCorrector::GenerateCorrectedIdentifier( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar) + { + if (systemIdentifier.empty()) + return GenerateIdentifierForUnresolvedCharacters(systemIdentifier, elementId, isVar); + + if (isVar && systemIdentifier[0] != UNDERSCORE[0]) + return GenerateSCsIdentifierForVariable(systemIdentifier); + + return systemIdentifier; + } + + std::string SCsWriter::SCgIdentifierCorrector::GenerateIdentifierForUnresolvedCharacters( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar) + { + std::string const & prefix = isVar ? EL_VAR_PREFIX : EL_PREFIX; + systemIdentifier = prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); + return systemIdentifier; + } + + std::string SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifierForVariable(std::string & systemIdentifier) + { + return UNDERSCORE + systemIdentifier; + } + + void SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifier( + SCgElementPtr const & scgElement, + SCsElementPtr & scsElement) + { + scsElement->SetIdentifierForSCs(scgElement->GetIdentifier()); + bool isVar = SCsWriter::IsVariable(scgElement->GetType()); + + std::string const & systemIdentifier = scsElement->GetIdentifierForSCs(); + if (!IsEnglishIdentifier(systemIdentifier)) + { + if (IsRussianIdentifier(systemIdentifier)) + scsElement->SetMainIdentifier(systemIdentifier); + + scsElement->SetIdentifierForSCs(""); + } + + std::string id = scgElement->GetId(); + std::string correctedIdentifier = scsElement->GetIdentifierForSCs(); + correctedIdentifier = GenerateCorrectedIdentifier(correctedIdentifier, id, isVar); + scsElement->SetIdentifierForSCs(correctedIdentifier); + + auto const & tag = scgElement->GetTag(); + if (tag == PAIR || tag == ARC) + scsElement->SetIdentifierForSCs(SCsWriter::MakeAlias(CONNECTOR, id)); + } + + // Вспомогательная функция для рекурсивного сбора узлов + void SCsWriter::CollectNodes( + SCgElements const & elements, + std::unordered_set & nodes, + std::unordered_set & visitedContours) + { + for (auto const & [id, element] : elements) + { + if (element->GetTag() == NODE) + { + nodes.insert(element); + } + else if (element->GetTag() == CONTOUR && !visitedContours.count(element)) + { + visitedContours.insert(element); + auto contour = std::dynamic_pointer_cast(element); + CollectNodes(contour->GetElements(), nodes, visitedContours); + } + } + } + + void SCsWriter::Write( + SCgElements const & elements, + std::string const & filePath, + Buffer & buffer, + size_t depth, + std::unordered_set & writtenElements) + { + // Шаг 1: Сбор всех узлов, включая вложенные в контуры + std::unordered_set allNodes; + std::unordered_set visitedContours; + CollectNodes(elements, allNodes, visitedContours); + + // Запись типов всех узлов + for (auto const & node : allNodes) + { + if (writtenElements.count(node)) continue; + writtenElements.insert(node); + std::string identifier = node->GetIdentifier(); + if (identifier.empty()) identifier = NODE + UNDERSCORE + node->GetId(); + buffer.AddTabs(depth) << identifier << NEWLINE; + + // Запись типа узла + std::string elementTypeStr; + SCgToSCsTypesConverter::ConvertSCgNodeTypeToSCsNodeType(node->GetType(), elementTypeStr); + if (elementTypeStr.empty()) elementTypeStr = NODE + UNDERSCORE; + buffer.AddTabs(depth + 1) << SC_CONNECTOR_MAIN_L << elementTypeStr << ELEMENT_END << NEWLINE; + + // Проверка, является ли узел SCgLink, и запись содержимого + auto link = std::dynamic_pointer_cast(node); + if (link && link->GetContentType() != NO_CONTENT) + { + std::string contentTypeStr = link->GetContentType(); + int contentType = std::stoi(contentTypeStr); + + if (contentType == 1 || contentType == 2 || contentType == 3) // STRING, INTEGER, FLOAT + { + std::string content = link->GetContentData(); + if (!content.empty()) + { + if (contentType == 2) // INTEGER + content = DOUBLE_QUOTE + INT64 + content + DOUBLE_QUOTE; + else if (contentType == 3) // FLOAT + content = DOUBLE_QUOTE + FLOAT + content + DOUBLE_QUOTE; + buffer.AddTabs(depth + 1) << SPACE << EQUAL << SPACE << OPEN_BRACKET << content << CLOSE_BRACKET + ELEMENT_END + NEWLINE; + } + } + else if (contentType == 4) // IMAGE + { + std::string fileName = link->GetFileName(); + std::string content = link->GetContentData(); + if (!fileName.empty() && !content.empty()) + { + // Формирование пути к файлу + std::filesystem::path basePath = std::filesystem::path(filePath).parent_path(); + std::filesystem::path fullPath = basePath / fileName; + + // Сохранение содержимого в файл + std::ofstream file(fullPath, std::ios::binary); + if (!file) + SC_THROW_EXCEPTION( + utils::ExceptionInvalidParams, + "SCsWriter::Write: Failed to open file for writing `" << fullPath.string() << "`."); + + file.write(content.data(), content.size()); + file.close(); + + // Определение формата изображения + std::string fileExtension = fullPath.extension().string(); + auto it = IMAGE_FORMATS.find(fileExtension); + if (it == IMAGE_FORMATS.end()) + SC_THROW_EXCEPTION( + utils::ExceptionItemNotFound, + "SCsWriter::Write: File extension `" << fileExtension << "` is not supported."); + + std::string imageFormat = it->second; + std::string fileContent = FILE_PREFIX + fullPath.filename().string(); + + // Вывод SCs-кода для изображения + buffer.AddTabs(depth + 1) << EQUAL + SPACE + DOUBLE_QUOTE << fileContent << DOUBLE_QUOTE + ELEMENT_END + NEWLINE; + buffer.AddTabs(depth + 1) << FORMAT_ARC + SPACE + EQUAL + SPACE + OPEN_PARENTHESIS << identifier << SPACE + SC_CONNECTOR_DCOMMON_R + SPACE << imageFormat << CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; + buffer.AddTabs(depth + 1) << NREL_FORMAT_ARC << SPACE + EQUAL + SPACE << OPEN_PARENTHESIS + NREL_FORMAT + SPACE << SC_CONNECTOR_MAIN_R + SPACE << FORMAT_ARC + CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; + } + } + } + buffer << NEWLINE; + } + + // Шаг 2: Предварительная обработка для дуг + std::unordered_set complexArcs; + std::unordered_set attributeArcs; + for (auto const & [id, element] : elements) + { + if (element->GetTag() == ARC || element->GetTag() == PAIR) + { + auto connector = std::dynamic_pointer_cast(element); + auto target = connector->GetTarget(); + if (target->GetTag() == ARC || target->GetTag() == PAIR) + { + complexArcs.insert(target); + attributeArcs.insert(element); + } + } + } + + // Шаг 3: Запись дуг и пар + for (auto const & [id, element] : elements) + { + if (element->GetTag() == ARC || element->GetTag() == PAIR) + { + if (writtenElements.count(element)) continue; + if (attributeArcs.count(element)) continue; + writtenElements.insert(element); + + auto connector = std::dynamic_pointer_cast(element); + std::string sourceId = connector->GetSource()->GetIdentifier(); + std::string targetId = connector->GetTarget()->GetIdentifier(); + if (sourceId.empty()) sourceId = NODE + UNDERSCORE + connector->GetSource()->GetId(); + if (targetId.empty()) targetId = NODE + UNDERSCORE + connector->GetTarget()->GetId(); + + if (complexArcs.count(element)) + { + std::string attrSourceId; + for (auto const & [incomingId, incomingElement] : elements) + { + if (incomingElement->GetTag() == ARC || incomingElement->GetTag() == PAIR) + { + auto incConnector = std::dynamic_pointer_cast(incomingElement); + if (incConnector->GetTarget() == element) + { + attrSourceId = incConnector->GetSource()->GetIdentifier(); + if (attrSourceId.empty()) attrSourceId = NODE + UNDERSCORE + incConnector->GetSource()->GetId(); + break; + } + } + } + + if (!attrSourceId.empty()) + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << attrSourceId << COLON << SPACE << targetId << ELEMENT_END << NEWLINE << NEWLINE; + } + else + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE << NEWLINE; + } + } + else + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE << NEWLINE; + } + } + } + + // Шаг 4: Запись контуров + for (auto const & [id, element] : elements) + { + if (element->GetTag() == CONTOUR) + { + if (writtenElements.count(element)) continue; + writtenElements.insert(element); + auto contour = std::dynamic_pointer_cast(element); + std::string identifier = element->GetIdentifier(); + if (identifier.empty()) identifier = CONTOUR + UNDERSCORE + element->GetId(); + buffer.AddTabs(depth) << identifier << SPACE + EQUAL << SPACE << OPEN_CONTOUR + NEWLINE; + + // Сбор узлов, принадлежащих текущему контуру + std::unordered_set contourNodes; + std::unordered_set contourVisitedContours; + CollectNodes(contour->GetElements(), contourNodes, contourVisitedContours); + + // Сбор узлов, участвующих в дугах внутри контура + std::unordered_set nodesInArcs; + for (auto const & [elemId, elem] : contour->GetElements()) + { + if (elem->GetTag() == ARC || elem->GetTag() == PAIR) + { + auto connector = std::dynamic_pointer_cast(elem); + if (connector->GetSource()->GetTag() == NODE) + nodesInArcs.insert(connector->GetSource()); + if (connector->GetTarget()->GetTag() == NODE) + nodesInArcs.insert(connector->GetTarget()); + } + } + + // Запись идентификаторов узлов, не участвующих в дугах + for (auto const & node : contourNodes) + { + if (nodesInArcs.count(node)) continue; // Пропускаем узлы, которые будут в дугах + std::string nodeIdentifier = node->GetIdentifier(); + if (nodeIdentifier.empty()) nodeIdentifier = NODE + UNDERSCORE + node->GetId(); + buffer.AddTabs(depth + 1) << nodeIdentifier << ELEMENT_END << NEWLINE; + } + + // Рекурсивная запись элементов контура (дуги, вложенные контуры) + Write(contour->GetElements(), filePath, buffer, depth + 1, writtenElements); + + buffer.AddTabs(depth) << CONTOUR_END << NEWLINE << NEWLINE; + } + } + } + + void SCsWriter::WriteMainIdentifier( + Buffer & buffer, + size_t depth, + std::string const & systemIdentifier, + std::string const & mainIdentifier) + { + buffer << NEWLINE; + buffer.AddTabs(depth) << systemIdentifier << NEWLINE; + buffer.AddTabs(depth) << SPACE << SC_CONNECTOR_DCOMMON_R << SPACE << NREL_MAIN_IDTF << COLON << SPACE << OPEN_BRACKET + << mainIdentifier << CLOSE_BRACKET << ELEMENT_END << NEWLINE; + } \ No newline at end of file From e7e24249089ae19fef9292b576bb0d9cabe7a099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D1=80=D0=BE=D0=BB=D1=8C=20=D0=93=D0=BB=D0=B5?= =?UTF-8?q?=D0=B1?= Date: Wed, 14 May 2025 21:19:57 +0300 Subject: [PATCH 3/4] [builder][gwf][refactor]Adding the CollectNode method --- sc-tools/sc-builder/src/sc_scs_writer.hpp | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 sc-tools/sc-builder/src/sc_scs_writer.hpp diff --git a/sc-tools/sc-builder/src/sc_scs_writer.hpp b/sc-tools/sc-builder/src/sc_scs_writer.hpp new file mode 100644 index 000000000..b18b14337 --- /dev/null +++ b/sc-tools/sc-builder/src/sc_scs_writer.hpp @@ -0,0 +1,61 @@ +/* + * This source file is part of an OSTIS project. For the latest info, see http://ostis.net + * Distributed under the MIT License + * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) + */ + + #pragma once + + #include + #include + #include + + #include "buffer.hpp" + #include "gwf_translator_constants.hpp" + #include "sc_scs_element.hpp" + + class SCsWriter + { + public: + static void Write( + SCgElements const & elements, + std::string const & filePath, + Buffer & buffer, + size_t depth, + std::unordered_set & writtenElements); + + static void WriteMainIdentifier( + Buffer & buffer, + size_t depth, + std::string const & systemIdentifier, + std::string const & mainIdentifier); + + static std::string MakeAlias(std::string const & prefix, std::string const & elementId); + static bool IsVariable(std::string const & elementType); + + class SCgIdentifierCorrector + { + public: + static void GenerateSCsIdentifier(SCgElementPtr const & scgElement, SCsElementPtr & scsElement); + + private: + static bool IsRussianIdentifier(std::string const & identifier); + static bool IsEnglishIdentifier(std::string const & identifier); + + static std::string GenerateIdentifierForUnresolvedCharacters( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar); + static std::string GenerateCorrectedIdentifier( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar); + static std::string GenerateSCsIdentifierForVariable(std::string & systemIdentifier); + }; + + private: + static void CollectNodes( + SCgElements const & elements, + std::unordered_set & nodes, + std::unordered_set & visitedContours); + }; \ No newline at end of file From a6ae2c639641af661c4e89f4f9545667e13f0c6f Mon Sep 17 00:00:00 2001 From: NikitaZotov Date: Mon, 14 Jul 2025 17:56:24 +0300 Subject: [PATCH 4/4] [refactor][builder] Format code --- sc-tools/sc-builder/src/sc_builder_runner.cpp | 7 +- sc-tools/sc-builder/src/sc_scs_writer.cpp | 692 +++++++++--------- sc-tools/sc-builder/src/sc_scs_writer.hpp | 110 +-- 3 files changed, 418 insertions(+), 391 deletions(-) diff --git a/sc-tools/sc-builder/src/sc_builder_runner.cpp b/sc-tools/sc-builder/src/sc_builder_runner.cpp index 63665da86..4196bda62 100644 --- a/sc-tools/sc-builder/src/sc_builder_runner.cpp +++ b/sc-tools/sc-builder/src/sc_builder_runner.cpp @@ -47,8 +47,10 @@ void PrintHelpMessage(std::string const & binaryName) "binaries.\n" << " --version Display the version of " << binaryName << ".\n" << " --help Display this help message.\n" - << " --gwf-to-scs Translate a GWF file to SCs and save the result to a specified file.\n" - << " --gwf-output Specify the output file for the GWF-to-SCs translation (default: .scs).\n" + << " --gwf-to-scs Translate a GWF file to SCs and save the result to a specified " + "file.\n" + << " --gwf-output Specify the output file for the GWF-to-SCs translation (default: " + ".scs).\n" << " --verbose|-v Enable verbose logging.\n"; } @@ -190,7 +192,6 @@ try "[sc-builder] group.\n" << "Make sure this path is a valid directory where you have write permissions and that it exists.\n" << "For more information, run with --help.\n"; - return EXIT_FAILURE; } diff --git a/sc-tools/sc-builder/src/sc_scs_writer.cpp b/sc-tools/sc-builder/src/sc_scs_writer.cpp index d92d09edb..e98bab2ff 100644 --- a/sc-tools/sc-builder/src/sc_scs_writer.cpp +++ b/sc-tools/sc-builder/src/sc_scs_writer.cpp @@ -4,336 +4,362 @@ * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) */ - #include "sc_scs_writer.hpp" - - #include - #include - #include - #include - - #include - - #include "sc_scg_element.hpp" - #include "sc_scs_element.hpp" - #include "sc_scg_to_scs_types_converter.hpp" - - using namespace Constants; - - std::string SCsWriter::MakeAlias(std::string const & prefix, std::string const & elementId) - { - return ALIAS_PREFIX + prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); - } - - bool SCsWriter::IsVariable(std::string const & elementType) - { - return elementType.find(VAR) != std::string::npos; - } - - bool SCsWriter::SCgIdentifierCorrector::IsRussianIdentifier(std::string const & identifier) - { - std::regex identifierPatternRus(R"(^[0-9a-zA-Z_\xD0\x80-\xD1\x8F\xD1\x90-\xD1\x8F\xD1\x91\xD0\x81*' ]*$)"); - return std::regex_match(identifier, identifierPatternRus); - } - - bool SCsWriter::SCgIdentifierCorrector::IsEnglishIdentifier(std::string const & identifier) - { - std::regex identifierPatternEng("^[0-9a-zA-Z_]*$"); - return std::regex_match(identifier, identifierPatternEng); - } - - std::string SCsWriter::SCgIdentifierCorrector::GenerateCorrectedIdentifier( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar) - { - if (systemIdentifier.empty()) - return GenerateIdentifierForUnresolvedCharacters(systemIdentifier, elementId, isVar); - - if (isVar && systemIdentifier[0] != UNDERSCORE[0]) - return GenerateSCsIdentifierForVariable(systemIdentifier); - - return systemIdentifier; - } - - std::string SCsWriter::SCgIdentifierCorrector::GenerateIdentifierForUnresolvedCharacters( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar) - { - std::string const & prefix = isVar ? EL_VAR_PREFIX : EL_PREFIX; - systemIdentifier = prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); - return systemIdentifier; - } - - std::string SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifierForVariable(std::string & systemIdentifier) - { - return UNDERSCORE + systemIdentifier; - } - - void SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifier( - SCgElementPtr const & scgElement, - SCsElementPtr & scsElement) - { - scsElement->SetIdentifierForSCs(scgElement->GetIdentifier()); - bool isVar = SCsWriter::IsVariable(scgElement->GetType()); - - std::string const & systemIdentifier = scsElement->GetIdentifierForSCs(); - if (!IsEnglishIdentifier(systemIdentifier)) - { - if (IsRussianIdentifier(systemIdentifier)) - scsElement->SetMainIdentifier(systemIdentifier); - - scsElement->SetIdentifierForSCs(""); - } - - std::string id = scgElement->GetId(); - std::string correctedIdentifier = scsElement->GetIdentifierForSCs(); - correctedIdentifier = GenerateCorrectedIdentifier(correctedIdentifier, id, isVar); - scsElement->SetIdentifierForSCs(correctedIdentifier); - - auto const & tag = scgElement->GetTag(); - if (tag == PAIR || tag == ARC) - scsElement->SetIdentifierForSCs(SCsWriter::MakeAlias(CONNECTOR, id)); - } - - // Вспомогательная функция для рекурсивного сбора узлов - void SCsWriter::CollectNodes( - SCgElements const & elements, - std::unordered_set & nodes, - std::unordered_set & visitedContours) - { - for (auto const & [id, element] : elements) - { - if (element->GetTag() == NODE) - { - nodes.insert(element); - } - else if (element->GetTag() == CONTOUR && !visitedContours.count(element)) - { - visitedContours.insert(element); - auto contour = std::dynamic_pointer_cast(element); - CollectNodes(contour->GetElements(), nodes, visitedContours); - } - } - } - - void SCsWriter::Write( - SCgElements const & elements, - std::string const & filePath, - Buffer & buffer, - size_t depth, - std::unordered_set & writtenElements) - { - // Шаг 1: Сбор всех узлов, включая вложенные в контуры - std::unordered_set allNodes; - std::unordered_set visitedContours; - CollectNodes(elements, allNodes, visitedContours); - - // Запись типов всех узлов - for (auto const & node : allNodes) - { - if (writtenElements.count(node)) continue; - writtenElements.insert(node); - std::string identifier = node->GetIdentifier(); - if (identifier.empty()) identifier = NODE + UNDERSCORE + node->GetId(); - buffer.AddTabs(depth) << identifier << NEWLINE; - - // Запись типа узла - std::string elementTypeStr; - SCgToSCsTypesConverter::ConvertSCgNodeTypeToSCsNodeType(node->GetType(), elementTypeStr); - if (elementTypeStr.empty()) elementTypeStr = NODE + UNDERSCORE; - buffer.AddTabs(depth + 1) << SC_CONNECTOR_MAIN_L << elementTypeStr << ELEMENT_END << NEWLINE; - - // Проверка, является ли узел SCgLink, и запись содержимого - auto link = std::dynamic_pointer_cast(node); - if (link && link->GetContentType() != NO_CONTENT) - { - std::string contentTypeStr = link->GetContentType(); - int contentType = std::stoi(contentTypeStr); - - if (contentType == 1 || contentType == 2 || contentType == 3) // STRING, INTEGER, FLOAT - { - std::string content = link->GetContentData(); - if (!content.empty()) - { - if (contentType == 2) // INTEGER - content = DOUBLE_QUOTE + INT64 + content + DOUBLE_QUOTE; - else if (contentType == 3) // FLOAT - content = DOUBLE_QUOTE + FLOAT + content + DOUBLE_QUOTE; - buffer.AddTabs(depth + 1) << SPACE << EQUAL << SPACE << OPEN_BRACKET << content << CLOSE_BRACKET + ELEMENT_END + NEWLINE; - } - } - else if (contentType == 4) // IMAGE - { - std::string fileName = link->GetFileName(); - std::string content = link->GetContentData(); - if (!fileName.empty() && !content.empty()) - { - // Формирование пути к файлу - std::filesystem::path basePath = std::filesystem::path(filePath).parent_path(); - std::filesystem::path fullPath = basePath / fileName; - - // Сохранение содержимого в файл - std::ofstream file(fullPath, std::ios::binary); - if (!file) - SC_THROW_EXCEPTION( - utils::ExceptionInvalidParams, - "SCsWriter::Write: Failed to open file for writing `" << fullPath.string() << "`."); - - file.write(content.data(), content.size()); - file.close(); - - // Определение формата изображения - std::string fileExtension = fullPath.extension().string(); - auto it = IMAGE_FORMATS.find(fileExtension); - if (it == IMAGE_FORMATS.end()) - SC_THROW_EXCEPTION( - utils::ExceptionItemNotFound, - "SCsWriter::Write: File extension `" << fileExtension << "` is not supported."); - - std::string imageFormat = it->second; - std::string fileContent = FILE_PREFIX + fullPath.filename().string(); - - // Вывод SCs-кода для изображения - buffer.AddTabs(depth + 1) << EQUAL + SPACE + DOUBLE_QUOTE << fileContent << DOUBLE_QUOTE + ELEMENT_END + NEWLINE; - buffer.AddTabs(depth + 1) << FORMAT_ARC + SPACE + EQUAL + SPACE + OPEN_PARENTHESIS << identifier << SPACE + SC_CONNECTOR_DCOMMON_R + SPACE << imageFormat << CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; - buffer.AddTabs(depth + 1) << NREL_FORMAT_ARC << SPACE + EQUAL + SPACE << OPEN_PARENTHESIS + NREL_FORMAT + SPACE << SC_CONNECTOR_MAIN_R + SPACE << FORMAT_ARC + CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; - } - } - } - buffer << NEWLINE; - } - - // Шаг 2: Предварительная обработка для дуг - std::unordered_set complexArcs; - std::unordered_set attributeArcs; - for (auto const & [id, element] : elements) - { - if (element->GetTag() == ARC || element->GetTag() == PAIR) - { - auto connector = std::dynamic_pointer_cast(element); - auto target = connector->GetTarget(); - if (target->GetTag() == ARC || target->GetTag() == PAIR) - { - complexArcs.insert(target); - attributeArcs.insert(element); - } - } - } - - // Шаг 3: Запись дуг и пар - for (auto const & [id, element] : elements) - { - if (element->GetTag() == ARC || element->GetTag() == PAIR) - { - if (writtenElements.count(element)) continue; - if (attributeArcs.count(element)) continue; - writtenElements.insert(element); - - auto connector = std::dynamic_pointer_cast(element); - std::string sourceId = connector->GetSource()->GetIdentifier(); - std::string targetId = connector->GetTarget()->GetIdentifier(); - if (sourceId.empty()) sourceId = NODE + UNDERSCORE + connector->GetSource()->GetId(); - if (targetId.empty()) targetId = NODE + UNDERSCORE + connector->GetTarget()->GetId(); - - if (complexArcs.count(element)) - { - std::string attrSourceId; - for (auto const & [incomingId, incomingElement] : elements) - { - if (incomingElement->GetTag() == ARC || incomingElement->GetTag() == PAIR) - { - auto incConnector = std::dynamic_pointer_cast(incomingElement); - if (incConnector->GetTarget() == element) - { - attrSourceId = incConnector->GetSource()->GetIdentifier(); - if (attrSourceId.empty()) attrSourceId = NODE + UNDERSCORE + incConnector->GetSource()->GetId(); - break; - } - } - } - - if (!attrSourceId.empty()) - { - std::string connectorSymbol; - SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); - if (connectorSymbol.empty()) connectorSymbol = SC_CONNECTOR_MAIN_R; - buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << attrSourceId << COLON << SPACE << targetId << ELEMENT_END << NEWLINE << NEWLINE; - } - else - { - std::string connectorSymbol; - SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); - if (connectorSymbol.empty()) connectorSymbol = SC_CONNECTOR_MAIN_R; - buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE << NEWLINE; - } - } - else - { - std::string connectorSymbol; - SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); - if (connectorSymbol.empty()) connectorSymbol = SC_CONNECTOR_MAIN_R; - buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE << NEWLINE; - } - } - } - - // Шаг 4: Запись контуров - for (auto const & [id, element] : elements) - { - if (element->GetTag() == CONTOUR) - { - if (writtenElements.count(element)) continue; - writtenElements.insert(element); - auto contour = std::dynamic_pointer_cast(element); - std::string identifier = element->GetIdentifier(); - if (identifier.empty()) identifier = CONTOUR + UNDERSCORE + element->GetId(); - buffer.AddTabs(depth) << identifier << SPACE + EQUAL << SPACE << OPEN_CONTOUR + NEWLINE; - - // Сбор узлов, принадлежащих текущему контуру - std::unordered_set contourNodes; - std::unordered_set contourVisitedContours; - CollectNodes(contour->GetElements(), contourNodes, contourVisitedContours); - - // Сбор узлов, участвующих в дугах внутри контура - std::unordered_set nodesInArcs; - for (auto const & [elemId, elem] : contour->GetElements()) - { - if (elem->GetTag() == ARC || elem->GetTag() == PAIR) - { - auto connector = std::dynamic_pointer_cast(elem); - if (connector->GetSource()->GetTag() == NODE) - nodesInArcs.insert(connector->GetSource()); - if (connector->GetTarget()->GetTag() == NODE) - nodesInArcs.insert(connector->GetTarget()); - } - } - - // Запись идентификаторов узлов, не участвующих в дугах - for (auto const & node : contourNodes) - { - if (nodesInArcs.count(node)) continue; // Пропускаем узлы, которые будут в дугах - std::string nodeIdentifier = node->GetIdentifier(); - if (nodeIdentifier.empty()) nodeIdentifier = NODE + UNDERSCORE + node->GetId(); - buffer.AddTabs(depth + 1) << nodeIdentifier << ELEMENT_END << NEWLINE; - } - - // Рекурсивная запись элементов контура (дуги, вложенные контуры) - Write(contour->GetElements(), filePath, buffer, depth + 1, writtenElements); - - buffer.AddTabs(depth) << CONTOUR_END << NEWLINE << NEWLINE; - } - } - } - - void SCsWriter::WriteMainIdentifier( - Buffer & buffer, - size_t depth, - std::string const & systemIdentifier, - std::string const & mainIdentifier) - { - buffer << NEWLINE; - buffer.AddTabs(depth) << systemIdentifier << NEWLINE; - buffer.AddTabs(depth) << SPACE << SC_CONNECTOR_DCOMMON_R << SPACE << NREL_MAIN_IDTF << COLON << SPACE << OPEN_BRACKET - << mainIdentifier << CLOSE_BRACKET << ELEMENT_END << NEWLINE; - } \ No newline at end of file +#include "sc_scs_writer.hpp" + +#include +#include +#include +#include + +#include + +#include "sc_scg_element.hpp" +#include "sc_scs_element.hpp" +#include "sc_scg_to_scs_types_converter.hpp" + +using namespace Constants; + +std::string SCsWriter::MakeAlias(std::string const & prefix, std::string const & elementId) +{ + return ALIAS_PREFIX + prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); +} + +bool SCsWriter::IsVariable(std::string const & elementType) +{ + return elementType.find(VAR) != std::string::npos; +} + +bool SCsWriter::SCgIdentifierCorrector::IsRussianIdentifier(std::string const & identifier) +{ + std::regex identifierPatternRus(R"(^[0-9a-zA-Z_\xD0\x80-\xD1\x8F\xD1\x90-\xD1\x8F\xD1\x91\xD0\x81*' ]*$)"); + return std::regex_match(identifier, identifierPatternRus); +} + +bool SCsWriter::SCgIdentifierCorrector::IsEnglishIdentifier(std::string const & identifier) +{ + std::regex identifierPatternEng("^[0-9a-zA-Z_]*$"); + return std::regex_match(identifier, identifierPatternEng); +} + +std::string SCsWriter::SCgIdentifierCorrector::GenerateCorrectedIdentifier( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar) +{ + if (systemIdentifier.empty()) + return GenerateIdentifierForUnresolvedCharacters(systemIdentifier, elementId, isVar); + + if (isVar && systemIdentifier[0] != UNDERSCORE[0]) + return GenerateSCsIdentifierForVariable(systemIdentifier); + + return systemIdentifier; +} + +std::string SCsWriter::SCgIdentifierCorrector::GenerateIdentifierForUnresolvedCharacters( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar) +{ + std::string const & prefix = isVar ? EL_VAR_PREFIX : EL_PREFIX; + systemIdentifier = prefix + UNDERSCORE + utils::StringUtils::ReplaceAll(elementId, DASH, UNDERSCORE); + return systemIdentifier; +} + +std::string SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifierForVariable(std::string & systemIdentifier) +{ + return UNDERSCORE + systemIdentifier; +} + +void SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifier( + SCgElementPtr const & scgElement, + SCsElementPtr & scsElement) +{ + scsElement->SetIdentifierForSCs(scgElement->GetIdentifier()); + bool isVar = SCsWriter::IsVariable(scgElement->GetType()); + + std::string const & systemIdentifier = scsElement->GetIdentifierForSCs(); + if (!IsEnglishIdentifier(systemIdentifier)) + { + if (IsRussianIdentifier(systemIdentifier)) + scsElement->SetMainIdentifier(systemIdentifier); + + scsElement->SetIdentifierForSCs(""); + } + + std::string id = scgElement->GetId(); + std::string correctedIdentifier = scsElement->GetIdentifierForSCs(); + correctedIdentifier = GenerateCorrectedIdentifier(correctedIdentifier, id, isVar); + scsElement->SetIdentifierForSCs(correctedIdentifier); + + auto const & tag = scgElement->GetTag(); + if (tag == PAIR || tag == ARC) + scsElement->SetIdentifierForSCs(SCsWriter::MakeAlias(CONNECTOR, id)); +} + +// Вспомогательная функция для рекурсивного сбора узлов +void SCsWriter::CollectNodes( + SCgElements const & elements, + std::unordered_set & nodes, + std::unordered_set & visitedContours) +{ + for (auto const & [id, element] : elements) + { + if (element->GetTag() == NODE) + { + nodes.insert(element); + } + else if (element->GetTag() == CONTOUR && !visitedContours.count(element)) + { + visitedContours.insert(element); + auto contour = std::dynamic_pointer_cast(element); + CollectNodes(contour->GetElements(), nodes, visitedContours); + } + } +} + +void SCsWriter::Write( + SCgElements const & elements, + std::string const & filePath, + Buffer & buffer, + size_t depth, + std::unordered_set & writtenElements) +{ + // Шаг 1: Сбор всех узлов, включая вложенные в контуры + std::unordered_set allNodes; + std::unordered_set visitedContours; + CollectNodes(elements, allNodes, visitedContours); + + // Запись типов всех узлов + for (auto const & node : allNodes) + { + if (writtenElements.count(node)) + continue; + writtenElements.insert(node); + std::string identifier = node->GetIdentifier(); + if (identifier.empty()) + identifier = NODE + UNDERSCORE + node->GetId(); + buffer.AddTabs(depth) << identifier << NEWLINE; + + // Запись типа узла + std::string elementTypeStr; + SCgToSCsTypesConverter::ConvertSCgNodeTypeToSCsNodeType(node->GetType(), elementTypeStr); + if (elementTypeStr.empty()) + elementTypeStr = NODE + UNDERSCORE; + buffer.AddTabs(depth + 1) << SC_CONNECTOR_MAIN_L << elementTypeStr << ELEMENT_END << NEWLINE; + + // Проверка, является ли узел SCgLink, и запись содержимого + auto link = std::dynamic_pointer_cast(node); + if (link && link->GetContentType() != NO_CONTENT) + { + std::string contentTypeStr = link->GetContentType(); + int contentType = std::stoi(contentTypeStr); + + if (contentType == 1 || contentType == 2 || contentType == 3) // STRING, INTEGER, FLOAT + { + std::string content = link->GetContentData(); + if (!content.empty()) + { + if (contentType == 2) // INTEGER + content = DOUBLE_QUOTE + INT64 + content + DOUBLE_QUOTE; + else if (contentType == 3) // FLOAT + content = DOUBLE_QUOTE + FLOAT + content + DOUBLE_QUOTE; + buffer.AddTabs(depth + 1) << SPACE << EQUAL << SPACE << OPEN_BRACKET << content + << CLOSE_BRACKET + ELEMENT_END + NEWLINE; + } + } + else if (contentType == 4) // IMAGE + { + std::string fileName = link->GetFileName(); + std::string content = link->GetContentData(); + if (!fileName.empty() && !content.empty()) + { + // Формирование пути к файлу + std::filesystem::path basePath = std::filesystem::path(filePath).parent_path(); + std::filesystem::path fullPath = basePath / fileName; + + // Сохранение содержимого в файл + std::ofstream file(fullPath, std::ios::binary); + if (!file) + SC_THROW_EXCEPTION( + utils::ExceptionInvalidParams, + "SCsWriter::Write: Failed to open file for writing `" << fullPath.string() << "`."); + + file.write(content.data(), content.size()); + file.close(); + + // Определение формата изображения + std::string fileExtension = fullPath.extension().string(); + auto it = IMAGE_FORMATS.find(fileExtension); + if (it == IMAGE_FORMATS.end()) + SC_THROW_EXCEPTION( + utils::ExceptionItemNotFound, + "SCsWriter::Write: File extension `" << fileExtension << "` is not supported."); + + std::string imageFormat = it->second; + std::string fileContent = FILE_PREFIX + fullPath.filename().string(); + + // Вывод SCs-кода для изображения + buffer.AddTabs(depth + 1) << EQUAL + SPACE + DOUBLE_QUOTE << fileContent + << DOUBLE_QUOTE + ELEMENT_END + NEWLINE; + buffer.AddTabs(depth + 1) << FORMAT_ARC + SPACE + EQUAL + SPACE + OPEN_PARENTHESIS << identifier + << SPACE + SC_CONNECTOR_DCOMMON_R + SPACE << imageFormat + << CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; + buffer.AddTabs(depth + 1) << NREL_FORMAT_ARC << SPACE + EQUAL + SPACE + << OPEN_PARENTHESIS + NREL_FORMAT + SPACE << SC_CONNECTOR_MAIN_R + SPACE + << FORMAT_ARC + CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; + } + } + } + buffer << NEWLINE; + } + + // Шаг 2: Предварительная обработка для дуг + std::unordered_set complexArcs; + std::unordered_set attributeArcs; + for (auto const & [id, element] : elements) + { + if (element->GetTag() == ARC || element->GetTag() == PAIR) + { + auto connector = std::dynamic_pointer_cast(element); + auto target = connector->GetTarget(); + if (target->GetTag() == ARC || target->GetTag() == PAIR) + { + complexArcs.insert(target); + attributeArcs.insert(element); + } + } + } + + // Шаг 3: Запись дуг и пар + for (auto const & [id, element] : elements) + { + if (element->GetTag() == ARC || element->GetTag() == PAIR) + { + if (writtenElements.count(element)) + continue; + if (attributeArcs.count(element)) + continue; + writtenElements.insert(element); + + auto connector = std::dynamic_pointer_cast(element); + std::string sourceId = connector->GetSource()->GetIdentifier(); + std::string targetId = connector->GetTarget()->GetIdentifier(); + if (sourceId.empty()) + sourceId = NODE + UNDERSCORE + connector->GetSource()->GetId(); + if (targetId.empty()) + targetId = NODE + UNDERSCORE + connector->GetTarget()->GetId(); + + if (complexArcs.count(element)) + { + std::string attrSourceId; + for (auto const & [incomingId, incomingElement] : elements) + { + if (incomingElement->GetTag() == ARC || incomingElement->GetTag() == PAIR) + { + auto incConnector = std::dynamic_pointer_cast(incomingElement); + if (incConnector->GetTarget() == element) + { + attrSourceId = incConnector->GetSource()->GetIdentifier(); + if (attrSourceId.empty()) + attrSourceId = NODE + UNDERSCORE + incConnector->GetSource()->GetId(); + break; + } + } + } + + if (!attrSourceId.empty()) + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation( + connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) + connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << attrSourceId << COLON << SPACE + << targetId << ELEMENT_END << NEWLINE << NEWLINE; + } + else + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation( + connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) + connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE + << NEWLINE; + } + } + else + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) + connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE + << NEWLINE; + } + } + } + + // Шаг 4: Запись контуров + for (auto const & [id, element] : elements) + { + if (element->GetTag() == CONTOUR) + { + if (writtenElements.count(element)) + continue; + writtenElements.insert(element); + auto contour = std::dynamic_pointer_cast(element); + std::string identifier = element->GetIdentifier(); + if (identifier.empty()) + identifier = CONTOUR + UNDERSCORE + element->GetId(); + buffer.AddTabs(depth) << identifier << SPACE + EQUAL << SPACE << OPEN_CONTOUR + NEWLINE; + + // Сбор узлов, принадлежащих текущему контуру + std::unordered_set contourNodes; + std::unordered_set contourVisitedContours; + CollectNodes(contour->GetElements(), contourNodes, contourVisitedContours); + + // Сбор узлов, участвующих в дугах внутри контура + std::unordered_set nodesInArcs; + for (auto const & [elemId, elem] : contour->GetElements()) + { + if (elem->GetTag() == ARC || elem->GetTag() == PAIR) + { + auto connector = std::dynamic_pointer_cast(elem); + if (connector->GetSource()->GetTag() == NODE) + nodesInArcs.insert(connector->GetSource()); + if (connector->GetTarget()->GetTag() == NODE) + nodesInArcs.insert(connector->GetTarget()); + } + } + + // Запись идентификаторов узлов, не участвующих в дугах + for (auto const & node : contourNodes) + { + if (nodesInArcs.count(node)) + continue; // Пропускаем узлы, которые будут в дугах + std::string nodeIdentifier = node->GetIdentifier(); + if (nodeIdentifier.empty()) + nodeIdentifier = NODE + UNDERSCORE + node->GetId(); + buffer.AddTabs(depth + 1) << nodeIdentifier << ELEMENT_END << NEWLINE; + } + + // Рекурсивная запись элементов контура (дуги, вложенные контуры) + Write(contour->GetElements(), filePath, buffer, depth + 1, writtenElements); + + buffer.AddTabs(depth) << CONTOUR_END << NEWLINE << NEWLINE; + } + } +} + +void SCsWriter::WriteMainIdentifier( + Buffer & buffer, + size_t depth, + std::string const & systemIdentifier, + std::string const & mainIdentifier) +{ + buffer << NEWLINE; + buffer.AddTabs(depth) << systemIdentifier << NEWLINE; + buffer.AddTabs(depth) << SPACE << SC_CONNECTOR_DCOMMON_R << SPACE << NREL_MAIN_IDTF << COLON << SPACE << OPEN_BRACKET + << mainIdentifier << CLOSE_BRACKET << ELEMENT_END << NEWLINE; +} \ No newline at end of file diff --git a/sc-tools/sc-builder/src/sc_scs_writer.hpp b/sc-tools/sc-builder/src/sc_scs_writer.hpp index b18b14337..b3b637235 100644 --- a/sc-tools/sc-builder/src/sc_scs_writer.hpp +++ b/sc-tools/sc-builder/src/sc_scs_writer.hpp @@ -4,58 +4,58 @@ * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) */ - #pragma once - - #include - #include - #include - - #include "buffer.hpp" - #include "gwf_translator_constants.hpp" - #include "sc_scs_element.hpp" - - class SCsWriter - { - public: - static void Write( - SCgElements const & elements, - std::string const & filePath, - Buffer & buffer, - size_t depth, - std::unordered_set & writtenElements); - - static void WriteMainIdentifier( - Buffer & buffer, - size_t depth, - std::string const & systemIdentifier, - std::string const & mainIdentifier); - - static std::string MakeAlias(std::string const & prefix, std::string const & elementId); - static bool IsVariable(std::string const & elementType); - - class SCgIdentifierCorrector - { - public: - static void GenerateSCsIdentifier(SCgElementPtr const & scgElement, SCsElementPtr & scsElement); - - private: - static bool IsRussianIdentifier(std::string const & identifier); - static bool IsEnglishIdentifier(std::string const & identifier); - - static std::string GenerateIdentifierForUnresolvedCharacters( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar); - static std::string GenerateCorrectedIdentifier( - std::string & systemIdentifier, - std::string const & elementId, - bool isVar); - static std::string GenerateSCsIdentifierForVariable(std::string & systemIdentifier); - }; - - private: - static void CollectNodes( - SCgElements const & elements, - std::unordered_set & nodes, - std::unordered_set & visitedContours); - }; \ No newline at end of file +#pragma once + +#include +#include +#include + +#include "buffer.hpp" +#include "gwf_translator_constants.hpp" +#include "sc_scs_element.hpp" + +class SCsWriter +{ +public: + static void Write( + SCgElements const & elements, + std::string const & filePath, + Buffer & buffer, + size_t depth, + std::unordered_set & writtenElements); + + static void WriteMainIdentifier( + Buffer & buffer, + size_t depth, + std::string const & systemIdentifier, + std::string const & mainIdentifier); + + static std::string MakeAlias(std::string const & prefix, std::string const & elementId); + static bool IsVariable(std::string const & elementType); + + class SCgIdentifierCorrector + { + public: + static void GenerateSCsIdentifier(SCgElementPtr const & scgElement, SCsElementPtr & scsElement); + + private: + static bool IsRussianIdentifier(std::string const & identifier); + static bool IsEnglishIdentifier(std::string const & identifier); + + static std::string GenerateIdentifierForUnresolvedCharacters( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar); + static std::string GenerateCorrectedIdentifier( + std::string & systemIdentifier, + std::string const & elementId, + bool isVar); + static std::string GenerateSCsIdentifierForVariable(std::string & systemIdentifier); + }; + +private: + static void CollectNodes( + SCgElements const & elements, + std::unordered_set & nodes, + std::unordered_set & visitedContours); +}; \ No newline at end of file