From 881daae15c800302ab00c8563ecdd048c7059445 Mon Sep 17 00:00:00 2001 From: Thomas Ubensee <34603111+tomuben@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:02:48 -0300 Subject: [PATCH] #983: Refactor CTPG script options Java parser code --- .../parser_ctpg_script_importer.cc | 50 +++++++++---------- .../parser_ctpg_script_importer.h | 17 ++++++- .../ctpg/script_option_lines_ctpg.cc | 50 ++++++++++++++----- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.cc b/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.cc index 226e2359..418de5cd 100644 --- a/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.cc +++ b/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.cc @@ -27,6 +27,30 @@ void ScriptImporter::importScript(std::string & scriptCode, importScript(scriptCode, options, 0); } +void ScriptImporter::replaceScripts(const ExecutionGraph::OptionsLineParser::CTPG::options_map_t::mapped_type & option_values, + const size_t recursionDepth, + std::vector &result) { + for (const auto & option: option_values) { + const char *importScriptCode = findImportScript(option.value); + std::string importScriptCodeStr; + if (m_importedScriptChecksums.addScript(importScriptCode) ) { + // Script has not been imported yet + // If this imported script contains %import statements + // they will be resolved in the next recursion. + ctpg_parser::options_map_t newOptions; + try { + ExecutionGraph::OptionsLineParser::CTPG::parseOptions(importScriptCode, newOptions); + } catch(const ExecutionGraph::OptionParserException & ex) { + Utils::rethrow(ex, "F-UDF-CL-SL-JAVA-1630"); + } + importScriptCodeStr.assign(importScriptCode); + importScript(importScriptCodeStr, newOptions, recursionDepth + 1); + } + ReplacedScripts replacedScript = {.script = std::move(importScriptCodeStr), .origPos = option.idx_in_source, .origLen = option.size }; + result.push_back(std::move(replacedScript)); + } +} + void ScriptImporter::importScript(std::string & scriptCode, ctpg_parser::options_map_t & options, const size_t recursionDepth) { @@ -43,35 +67,11 @@ void ScriptImporter::importScript(std::string & scriptCode, { return first.idx_in_source < second.idx_in_source; }); - struct ReplacedScripts { - ReplacedScripts(ReplacedScripts&&) = default; - std::string script; - size_t origPos; - size_t origLen; - }; std::vector replacedScripts; replacedScripts.reserve(optionIt->second.size()); //In order to continue compatibility with legacy implementation we must collect import scripts in forward direction //but then replace in reverse direction (in order to keep consistency of positions) - for (const auto & option: optionIt->second) { - const char *importScriptCode = findImportScript(option.value); - std::string importScriptCodeStr; - if (m_importedScriptChecksums.addScript(importScriptCode) ) { - // Script has not been imported yet - // If this imported script contains %import statements - // they will be resolved in the next recursion. - ctpg_parser::options_map_t newOptions; - try { - ExecutionGraph::OptionsLineParser::CTPG::parseOptions(importScriptCode, newOptions); - } catch(const ExecutionGraph::OptionParserException & ex) { - Utils::rethrow(ex, "F-UDF-CL-SL-JAVA-1630"); - } - importScriptCodeStr.assign(importScriptCode); - importScript(importScriptCodeStr, newOptions, recursionDepth + 1); - } - ReplacedScripts replacedScript = {.script = std::move(importScriptCodeStr), .origPos = option.idx_in_source, .origLen = option.size }; - replacedScripts.push_back(std::move(replacedScript)); - } + replaceScripts(optionIt->second, recursionDepth, replacedScripts); //Now replace the imported script bodies from end to start. Doing it in forward order would invalidate the offsets of later import scripts. for (auto optionIt = replacedScripts.rbegin(); optionIt != replacedScripts.rend(); optionIt++) { scriptCode.replace(optionIt->origPos, optionIt->origLen, optionIt->script); diff --git a/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.h b/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.h index f0492e34..07624254 100644 --- a/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.h +++ b/exaudfclient/base/javacontainer/script_options/parser_ctpg_script_importer.h @@ -16,6 +16,8 @@ namespace JavaScriptOptions { namespace CTPG { + + class ScriptImporter { public: @@ -23,17 +25,30 @@ class ScriptImporter { void importScript(std::string & scriptCode, ExecutionGraph::OptionsLineParser::CTPG::options_map_t & options); + private: + struct ReplacedScripts { + ReplacedScripts(ReplacedScripts&&) = default; + std::string script; + size_t origPos; + size_t origLen; + }; + private: void importScript(std::string & scriptCode, ExecutionGraph::OptionsLineParser::CTPG::options_map_t & options, const size_t recursionDepth); const char* findImportScript(const std::string & scriptKey); + + void replaceScripts(const ExecutionGraph::OptionsLineParser::CTPG::options_map_t::mapped_type & option_values, + const size_t recursionDepth, + std::vector &result); + private: Checksum m_importedScriptChecksums; SwigFactory & m_swigFactory; std::unique_ptr m_metaData; Keywords & m_keywords; - //The empirical maximal value for recursion depth is ~26000. So we choose 20000 to have a certain buffer. + //The empirical maximal value for recursion depth is ~18000. So we choose 10000 to have a certain buffer. const size_t cMaxRecursionDepth = 20000; }; diff --git a/exaudfclient/base/script_options_parser/ctpg/script_option_lines_ctpg.cc b/exaudfclient/base/script_options_parser/ctpg/script_option_lines_ctpg.cc index 74d799de..e89a948b 100644 --- a/exaudfclient/base/script_options_parser/ctpg/script_option_lines_ctpg.cc +++ b/exaudfclient/base/script_options_parser/ctpg/script_option_lines_ctpg.cc @@ -200,29 +200,53 @@ void parse(std::string&& code, options_type& result) { } //namespace ParserInternals +struct LinePositions { + size_t mStartPos; + size_t mEndPos; +}; + +inline std::optional getNextLine(const size_t current_pos, const std::string & scriptCode) { + /** + * Find first of occurence of '%', starting search from position 'current_pos'. + * If no '%' is found, return an empty result. + * If '%' is found, search backwards from '%' for '\n' or \r': + * 1. If not found, '%' was found in the first line. Then we can set 'new_option_start_pos'=0 + * 2. If found, set new_option_start_pos to position 1 char behind pos of found '\n' or '\r'. + * Then search forward for next occurence of '\n' or \r' and assign to var 'line_end_pos': + 1. If not found, 'line_end_pos' will get assigned std::string::npos (std::string::substr(...,npos), returns substring until end of string + 2. If found, 'line_end_pos' will assigned to position of line end of line where '%' was found + */ + std::optional retVal; + const size_t new_option_start_pos = scriptCode.find_first_of("%", current_pos); + if (new_option_start_pos == std::string::npos) + return retVal; + size_t line_start_pos = scriptCode.find_last_of("\r\n", new_option_start_pos); + if (std::string::npos == line_start_pos) + line_start_pos = 0; + else + line_start_pos++; + + const size_t line_end_pos = scriptCode.find_first_of("\r\n", line_start_pos); + retVal = LinePositions{ .mStartPos = line_start_pos, .mEndPos = line_end_pos}; + return retVal; +} + void parseOptions(const std::string& code, options_map_t & result) { size_t current_pos = 0; do { - const size_t new_option_start_pos = code.find_first_of("%", current_pos); - if (new_option_start_pos == std::string::npos) + const std::optional currentLinePositions = getNextLine(current_pos, code); + if (!currentLinePositions) break; - current_pos = code.find_last_of("\r\n", new_option_start_pos); - if (std::string::npos == current_pos) - current_pos = 0; - else - current_pos++; - - const size_t new_pos = code.find_first_of("\r\n", current_pos); - std::string line = code.substr(current_pos, new_pos); + std::string line = code.substr(currentLinePositions->mStartPos, currentLinePositions->mEndPos); options_type parser_result; ParserInternals::parse(std::move(line), parser_result); for (const auto & option: parser_result) { ScriptOption entry = { .value = option.value, - .idx_in_source = current_pos + option.start.column - 1, + .idx_in_source = currentLinePositions->mStartPos + option.start.column - 1, .size = option.end.column - option.start.column + 1 }; auto it_in_result = result.find(option.key); @@ -237,10 +261,10 @@ void parseOptions(const std::string& code, options_map_t & result) { it_in_result->second.push_back(entry); } } - if (new_pos == std::string::npos) { + if (currentLinePositions->mEndPos == std::string::npos) { break; } - current_pos = new_pos + 1; + current_pos = currentLinePositions->mEndPos + 1; } while(true); }