diff --git a/lib/libimhex/include/hex/pattern_language/preprocessor.hpp b/lib/libimhex/include/hex/pattern_language/preprocessor.hpp index 2499777f8..ee8153bc9 100644 --- a/lib/libimhex/include/hex/pattern_language/preprocessor.hpp +++ b/lib/libimhex/include/hex/pattern_language/preprocessor.hpp @@ -9,6 +9,8 @@ #include #include +#include + namespace hex::pl { class Preprocessor { @@ -34,6 +36,8 @@ namespace hex::pl { std::set> m_defines; std::set> m_pragmas; + std::set m_onceIncludedFiles; + std::pair m_error; }; diff --git a/lib/libimhex/source/pattern_language/pattern_language.cpp b/lib/libimhex/source/pattern_language/pattern_language.cpp index 135894db1..0455bc6d6 100644 --- a/lib/libimhex/source/pattern_language/pattern_language.cpp +++ b/lib/libimhex/source/pattern_language/pattern_language.cpp @@ -23,6 +23,8 @@ namespace hex::pl { this->m_validator = new Validator(); this->m_evaluator = new Evaluator(); + this->m_preprocessor->addDefaultPragmaHandlers(); + this->m_preprocessor->addPragmaHandler("endian", [this](std::string value) { if (value == "big") { this->m_evaluator->setDefaultEndian(std::endian::big); @@ -83,8 +85,6 @@ namespace hex::pl { ImHexApi::Provider::get()->setBaseAddress(baseAddress); return true; }); - - this->m_preprocessor->addDefaultPragmaHandlers(); } PatternLanguage::~PatternLanguage() { diff --git a/lib/libimhex/source/pattern_language/preprocessor.cpp b/lib/libimhex/source/pattern_language/preprocessor.cpp index aa747f968..899590e38 100644 --- a/lib/libimhex/source/pattern_language/preprocessor.cpp +++ b/lib/libimhex/source/pattern_language/preprocessor.cpp @@ -62,11 +62,11 @@ namespace hex::pl { } offset += 1; - std::string includePath = includeFile; + fs::path includePath = includeFile; if (includeFile[0] != '/') { for (const auto &dir : hex::getPath(ImHexPath::PatternsInclude)) { - std::string tempPath = hex::format("{0}/{1}", dir.string().c_str(), includeFile.c_str()); + fs::path tempPath = dir / includePath; if (fs::exists(tempPath)) { includePath = tempPath; break; @@ -78,16 +78,25 @@ namespace hex::pl { if (!file.isValid()) throwPreprocessorError(hex::format("{0}: No such file or directory", includeFile.c_str()), lineNumber); + bool shouldInclude = true; + this->addPragmaHandler("once", [&, includePath, this](const std::string &value) { + auto [iter, added] = this->m_onceIncludedFiles.insert(includePath); + if (!added) shouldInclude = false; + return value.empty(); + }); + auto preprocessedInclude = this->preprocess(file.readString(), false); if (!preprocessedInclude.has_value()) throw this->m_error; - auto content = preprocessedInclude.value(); + if (shouldInclude) { + auto content = preprocessedInclude.value(); - std::replace(content.begin(), content.end(), '\n', ' '); - std::replace(content.begin(), content.end(), '\r', ' '); + std::replace(content.begin(), content.end(), '\n', ' '); + std::replace(content.begin(), content.end(), '\r', ' '); - output += content; + output += content; + } } else if (code.substr(offset, 6) == "define") { offset += 6; @@ -126,14 +135,18 @@ namespace hex::pl { } else if (code.substr(offset, 6) == "pragma") { offset += 6; - while (std::isblank(code[offset])) + while (std::isblank(code[offset])) { offset += 1; + if (code[offset] == '\n' || code[offset] == '\r') + throwPreprocessorError("no instruction given in #pragma directive", lineNumber); + } + std::string pragmaKey; - while (!std::isblank(code[offset])) { + while (!std::isblank(code[offset]) && code[offset] != '\n' && code[offset] != '\r') { pragmaKey += code[offset]; - if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r') + if (offset >= code.length()) throwPreprocessorError("no instruction given in #pragma directive", lineNumber); offset += 1; @@ -151,9 +164,6 @@ namespace hex::pl { offset += 1; } - if (pragmaValue.empty()) - throwPreprocessorError("missing value in #pragma directive", lineNumber); - this->m_pragmas.emplace(pragmaKey, pragmaValue, lineNumber); } else throwPreprocessorError("unknown preprocessor directive", lineNumber); @@ -219,8 +229,7 @@ namespace hex::pl { } void Preprocessor::addPragmaHandler(const std::string &pragmaType, const std::function &function) { - if (!this->m_pragmaHandlers.contains(pragmaType)) - this->m_pragmaHandlers.emplace(pragmaType, function); + this->m_pragmaHandlers[pragmaType] = function; } void Preprocessor::addDefaultPragmaHandlers() { @@ -230,6 +239,9 @@ namespace hex::pl { this->addPragmaHandler("endian", [](const std::string &value) { return value == "big" || value == "little" || value == "native"; }); + this->addPragmaHandler("once", [](const std::string &value) { + return value.empty(); + }); } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 7016f600d..a901c2fe7 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -105,6 +105,7 @@ namespace hex::plugin::builtin { return; pl::Preprocessor preprocessor; + preprocessor.addDefaultPragmaHandlers(); if (!ImHexApi::Provider::isValid()) return; @@ -119,7 +120,6 @@ namespace hex::plugin::builtin { } return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r'); }); - preprocessor.addDefaultPragmaHandlers(); this->m_possiblePatternFiles.clear();