From ea92e17ca0f4187cbf207551a9f1fa175b303238 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sat, 18 Dec 2021 22:56:36 +0100 Subject: [PATCH] patterns: Added basic support for in/out variables --- .../content/views/view_pattern_editor.hpp | 33 +- .../content/views/view_pattern_editor.cpp | 382 ++++++++++++------ .../include/hex/pattern_language/ast_node.hpp | 12 +- .../hex/pattern_language/evaluator.hpp | 25 +- .../hex/pattern_language/pattern_language.hpp | 16 +- .../include/hex/pattern_language/token.hpp | 6 +- .../source/pattern_language/evaluator.cpp | 13 +- .../source/pattern_language/lexer.cpp | 4 + .../source/pattern_language/parser.cpp | 14 +- .../pattern_language/pattern_language.cpp | 59 ++- 10 files changed, 411 insertions(+), 153 deletions(-) diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 2bb9ffb59..a2a8be879 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -25,11 +25,15 @@ namespace hex::plugin::builtin { void drawContent() override; private: - pl::PatternLanguage *m_patternLanguageRuntime; + pl::PatternLanguage *m_parserRuntime, *m_evaluatorRuntime; + std::vector m_possiblePatternFiles; u32 m_selectedPatternFile = 0; bool m_runAutomatically = false; + bool m_evaluatorRunning = false; + bool m_parserRunning = false; + bool m_hasUnevaluatedChanges = false; bool m_acceptPatternWindowOpen = false; @@ -37,6 +41,17 @@ namespace hex::plugin::builtin { TextEditor m_textEditor; std::vector> m_console; + struct PatternVariable { + bool inVariable; + bool outVariable; + + pl::Token::ValueType type; + pl::Token::Literal value; + }; + + std::map m_patternVariables; + std::vector m_patternTypes; + enum class EnvVarType { Integer, Float, @@ -45,16 +60,28 @@ namespace hex::plugin::builtin { }; struct EnvVar { + u64 id; std::string name; pl::Token::Literal value; EnvVarType type; + + bool operator==(const EnvVar &other) const { + return this->id == other.id; + } }; - std::vector m_envVarEntries; + u64 m_envVarIdCounter; + std::list m_envVarEntries; + + void drawConsole(ImVec2 size); + void drawEnvVars(ImVec2 size); + void drawVariableSettings(ImVec2 size); void loadPatternFile(const std::string &path); void clearPatternData(); - void parsePattern(char *buffer); + + void parsePattern(const std::string &code); + void evaluatePattern(const std::string &code); }; } \ 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 098db04eb..74247e148 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,7 @@ namespace hex::plugin::builtin { static TextEditor::LanguageDefinition langDef; if (!initialized) { static const char* const keywords[] = { - "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "this", "parent", "addressof", "sizeof", "$", "while", "for", "fn", "return", "namespace" + "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "this", "parent", "addressof", "sizeof", "$", "while", "for", "fn", "return", "namespace", "in", "out" }; for (auto& k : keywords) langDef.mKeywords.insert(k); @@ -83,12 +84,14 @@ namespace hex::plugin::builtin { ViewPatternEditor::ViewPatternEditor() : View("hex.builtin.view.pattern_editor.name") { - this->m_patternLanguageRuntime = new pl::PatternLanguage(); + this->m_evaluatorRuntime = new pl::PatternLanguage(); + this->m_parserRuntime = new pl::PatternLanguage(); this->m_textEditor.SetLanguageDefinition(PatternLanguage()); this->m_textEditor.SetShowWhitespaces(false); - this->m_envVarEntries = { { "", s128(0), EnvVarType::Integer } }; + this->m_envVarEntries.push_back({ 0, "", 0, EnvVarType::Integer }); + this->m_envVarIdCounter = 1; EventManager::subscribe(this, [this]() { ProjectFile::setPattern(this->m_textEditor.GetText()); @@ -96,7 +99,7 @@ namespace hex::plugin::builtin { EventManager::subscribe(this, [this]() { this->m_textEditor.SetText(ProjectFile::getPattern()); - this->parsePattern(this->m_textEditor.GetText().data()); + this->evaluatePattern(this->m_textEditor.GetText()); }); EventManager::subscribe(this, [this](std::string code) { @@ -156,7 +159,7 @@ namespace hex::plugin::builtin { EventManager::subscribe(this, [this]{ this->m_textEditor.SetText(""); - this->m_patternLanguageRuntime->abort(); + this->m_evaluatorRuntime->abort(); }); /* Settings */ @@ -181,7 +184,8 @@ namespace hex::plugin::builtin { } ViewPatternEditor::~ViewPatternEditor() { - delete this->m_patternLanguageRuntime; + delete this->m_evaluatorRuntime; + delete this->m_parserRuntime; EventManager::unsubscribe(this); EventManager::unsubscribe(this); @@ -232,49 +236,38 @@ namespace hex::plugin::builtin { if (ImHexApi::Provider::isValid() && provider->isAvailable()) { auto textEditorSize = ImGui::GetContentRegionAvail(); - textEditorSize.y *= 4.0/5.0; + textEditorSize.y *= 3.75/5.0; textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing(); this->m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true); - auto consoleSize = ImGui::GetContentRegionAvail(); - consoleSize.y -= ImGui::GetTextLineHeightWithSpacing(); + auto size = ImGui::GetContentRegionAvail(); + size.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5; - ImGui::PushStyleColor(ImGuiCol_ChildBg, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Background)]); - if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { - for (auto &[level, message] : this->m_console) { - switch (level) { - case pl::LogConsole::Level::Debug: - ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Comment)]); - break; - case pl::LogConsole::Level::Info: - ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Default)]); - break; - case pl::LogConsole::Level::Warning: - ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Preprocessor)]); - break; - case pl::LogConsole::Level::Error: - ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::ErrorMarker)]); - break; - default: continue; - } - - ImGui::TextUnformatted(message.c_str()); - - ImGui::PopStyleColor(); + if (ImGui::BeginTabBar("settings")) { + if (ImGui::BeginTabItem("Console")) { + this->drawConsole(size); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Environment Variables")) { + this->drawEnvVars(size); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Settings")) { + this->drawVariableSettings(size); + ImGui::EndTabItem(); } + ImGui::EndTabBar(); } - ImGui::EndChild(); - ImGui::PopStyleColor(1); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); if (this->m_evaluatorRunning) { if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) - this->m_patternLanguageRuntime->abort(); + this->m_evaluatorRuntime->abort(); } else { if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) - this->parsePattern(this->m_textEditor.GetText().data()); + this->evaluatePattern(this->m_textEditor.GetText()); } @@ -294,87 +287,25 @@ namespace hex::plugin::builtin { ImGui::SameLine(); ImGui::TextUnformatted(hex::format("{} / {}", - this->m_patternLanguageRuntime->getCreatedPatternCount(), - this->m_patternLanguageRuntime->getMaximumPatternCount() + this->m_evaluatorRuntime->getCreatedPatternCount(), + this->m_evaluatorRuntime->getMaximumPatternCount() ).c_str() ); - ImGui::SameLine(); - - if (ImGui::IconButton(ICON_VS_GLOBE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) - ImGui::OpenPopup("env_vars"); - - if (ImGui::BeginPopup("env_vars")) { - ImGui::TextUnformatted("hex.builtin.view.pattern_editor.env_vars"_lang); - ImGui::SameLine(); - if (ImGui::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) this->m_envVarEntries.push_back({ "", s128(0), EnvVarType::Integer }); - ImGui::Separator(); - - int index = 0; - for (auto &[name, value, type] : this->m_envVarEntries) { - ImGui::PushID(index++); - - ImGui::PushItemWidth(ImGui::GetTextLineHeightWithSpacing() * 2); - constexpr const char* Types[] = { "I", "F", "S", "B" }; - if (ImGui::BeginCombo("", Types[static_cast(type)])) { - for (auto i = 0; i < IM_ARRAYSIZE(Types); i++) { - if (ImGui::Selectable(Types[i])) - type = static_cast(i); - } - - ImGui::EndCombo(); - } - ImGui::PopItemWidth(); - - ImGui::SameLine(); - - ImGui::InputText("###name", name.data(), name.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &name); - ImGui::SameLine(); - - switch (type) { - case EnvVarType::Integer: { - s64 displayValue = hex::get_or(value, 0); - ImGui::InputScalar("###value", ImGuiDataType_S64, &displayValue); - value = s128(displayValue); - break; - } - case EnvVarType::Float: { - auto displayValue = hex::get_or(value, 0.0); - ImGui::InputDouble("###value", &displayValue); - value = displayValue; - break; - } - case EnvVarType::Bool: { - auto displayValue = hex::get_or(value, false); - ImGui::Checkbox("###value", &displayValue); - value = displayValue; - break; - } - case EnvVarType::String: { - auto displayValue = hex::get_or(value, ""); - ImGui::InputText("###value", displayValue.data(), displayValue.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &displayValue); - value = displayValue; - break; - } - } - - ImGui::PopID(); - } - - ImGui::EndPopup(); - } } if (this->m_textEditor.IsTextChanged()) { ProjectFile::markDirty(); - - if (this->m_runAutomatically) - this->m_hasUnevaluatedChanges = true; + this->m_hasUnevaluatedChanges = true; } - if (this->m_hasUnevaluatedChanges && !this->m_evaluatorRunning) { + if (this->m_hasUnevaluatedChanges && !this->m_evaluatorRunning && !this->m_parserRunning) { this->m_hasUnevaluatedChanges = false; - this->parsePattern(this->m_textEditor.GetText().data()); + + if (this->m_runAutomatically) + this->evaluatePattern(this->m_textEditor.GetText()); + else + this->parsePattern(this->m_textEditor.GetText()); } } @@ -383,6 +314,179 @@ namespace hex::plugin::builtin { ImGui::End(); } + void ViewPatternEditor::drawConsole(ImVec2 size) { + ImGui::PushStyleColor(ImGuiCol_ChildBg, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Background)]); + if (ImGui::BeginChild("##console", size, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + for (auto &[level, message] : this->m_console) { + switch (level) { + case pl::LogConsole::Level::Debug: + ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Comment)]); + break; + case pl::LogConsole::Level::Info: + ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Default)]); + break; + case pl::LogConsole::Level::Warning: + ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Preprocessor)]); + break; + case pl::LogConsole::Level::Error: + ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::ErrorMarker)]); + break; + default: continue; + } + + ImGui::TextUnformatted(message.c_str()); + + ImGui::PopStyleColor(); + } + + } + ImGui::EndChild(); + ImGui::PopStyleColor(1); + } + + void ViewPatternEditor::drawEnvVars(ImVec2 size) { + if (ImGui::BeginChild("##env_vars", size, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + int index = 0; + if (ImGui::BeginTable("##env_vars_table", 4, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersInnerH)) { + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthStretch, 0.1F); + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 0.4F); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.38F); + ImGui::TableSetupColumn("Remove", ImGuiTableColumnFlags_WidthStretch, 0.12F); + + for (auto iter = this->m_envVarEntries.begin(); iter != this->m_envVarEntries.end(); iter++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + auto &[id, name, value, type] = *iter; + + ImGui::PushID(index++); + ON_SCOPE_EXIT { ImGui::PopID(); }; + + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth()); + constexpr const char* Types[] = { "I", "F", "S", "B" }; + if (ImGui::BeginCombo("", Types[static_cast(type)])) { + for (auto i = 0; i < IM_ARRAYSIZE(Types); i++) { + if (ImGui::Selectable(Types[i])) + type = static_cast(i); + } + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + ImGui::TableNextColumn(); + + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth()); + ImGui::InputText("###name", name.data(), name.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &name); + ImGui::PopItemWidth(); + + ImGui::TableNextColumn(); + + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth()); + switch (type) { + case EnvVarType::Integer: { + s64 displayValue = hex::get_or(value, 0); + ImGui::InputScalar("###value", ImGuiDataType_S64, &displayValue); + value = s128(displayValue); + break; + } + case EnvVarType::Float: { + auto displayValue = hex::get_or(value, 0.0); + ImGui::InputDouble("###value", &displayValue); + value = displayValue; + break; + } + case EnvVarType::Bool: { + auto displayValue = hex::get_or(value, false); + ImGui::Checkbox("###value", &displayValue); + value = displayValue; + break; + } + case EnvVarType::String: { + auto displayValue = hex::get_or(value, ""); + ImGui::InputText("###value", displayValue.data(), displayValue.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &displayValue); + value = displayValue; + break; + } + } + ImGui::PopItemWidth(); + + ImGui::TableNextColumn(); + + if (ImGui::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + iter++; + this->m_envVarEntries.insert(iter, { this->m_envVarIdCounter++, "", s128(0), EnvVarType::Integer }); + iter--; + } + + ImGui::SameLine(); + + ImGui::BeginDisabled(this->m_envVarEntries.size() <= 1); + { + if (ImGui::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { + bool isFirst = iter == this->m_envVarEntries.begin(); + this->m_envVarEntries.erase(iter); + + if (isFirst) + iter = this->m_envVarEntries.begin(); + } + } + ImGui::EndDisabled(); + } + + ImGui::EndTable(); + } + } + ImGui::EndChild(); + } + + void ViewPatternEditor::drawVariableSettings(ImVec2 size) { + if (ImGui::BeginChild("##settings", size, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + if (ImGui::BeginTable("##env_vars_table", 2, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersInnerH)) { + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 0.4F); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.6F); + + for (auto &[name, variable] : this->m_patternVariables) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::TextUnformatted(name.c_str()); + + ImGui::TableNextColumn(); + + if (variable.outVariable) { + ImGui::TextUnformatted(pl::Token::literalToString(variable.value, true).c_str()); + } else if (variable.inVariable) { + if (pl::Token::isSigned(variable.type)) { + s64 value = hex::get_or(variable.value, 0); + ImGui::InputScalar("", ImGuiDataType_S64, &value); + variable.value = s128(value); + } else if (pl::Token::isUnsigned(variable.type)) { + u64 value = hex::get_or(variable.value, 0); + ImGui::InputScalar("", ImGuiDataType_U64, &value); + variable.value = u128(value); + } else if (pl::Token::isFloatingPoint(variable.type)) { + double value = hex::get_or(variable.value, 0.0); + ImGui::InputScalar("", ImGuiDataType_Double, &value); + variable.value = value; + } else if (variable.type == pl::Token::ValueType::Boolean) { + bool value = hex::get_or(variable.value, false); + ImGui::Checkbox("", &value); + variable.value = value; + } else if (variable.type == pl::Token::ValueType::Character) { + char buffer[2]; + ImGui::InputText("", buffer, 2); + variable.value = buffer[0]; + } + } + } + + ImGui::EndTable(); + } + } + ImGui::EndChild(); + } + void ViewPatternEditor::drawAlwaysVisible() { ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F)); @@ -476,7 +580,7 @@ namespace hex::plugin::builtin { fclose(file); - this->parsePattern(buffer); + this->evaluatePattern(buffer); this->m_textEditor.SetText(buffer); delete[] buffer; @@ -491,7 +595,43 @@ namespace hex::plugin::builtin { pl::PatternData::resetPalette(); } - void ViewPatternEditor::parsePattern(char *buffer) { + + void ViewPatternEditor::parsePattern(const std::string &code) { + this->m_parserRunning = true; + std::thread([this, code] { + auto ast = this->m_parserRuntime->parseString(code); + + this->m_patternVariables.clear(); + this->m_patternTypes.clear(); + + if (ast) { + for (auto node : *ast) { + if (auto variableDecl = dynamic_cast(node)) { + auto type = dynamic_cast(variableDecl->getType()); + if (type == nullptr) continue; + + auto builtinType = dynamic_cast(type->getType()); + if (builtinType == nullptr) continue; + + PatternVariable variable = { + .inVariable = variableDecl->isInVariable(), + .outVariable = variableDecl->isOutVariable(), + .type = builtinType->getType() + }; + + if (variable.inVariable || variable.outVariable) { + if (!this->m_patternVariables.contains(variableDecl->getName())) + this->m_patternVariables[variableDecl->getName()] = variable; + } + } + } + } + + this->m_parserRunning = false; + }).detach(); + } + + void ViewPatternEditor::evaluatePattern(const std::string &code) { this->m_evaluatorRunning = true; this->m_textEditor.SetErrorMarkers({ }); @@ -499,25 +639,35 @@ namespace hex::plugin::builtin { this->clearPatternData(); EventManager::post(); - std::thread([this, buffer = std::string(buffer)] { + std::thread([this, code] { std::map envVars; - for (const auto &[name, value, type] : this->m_envVarEntries) + for (const auto &[id, name, value, type] : this->m_envVarEntries) envVars.insert({ name, value }); - auto result = this->m_patternLanguageRuntime->executeString(ImHexApi::Provider::get(), buffer, envVars); + std::map inVariables; + for (auto &[name, variable] : this->m_patternVariables) { + if (variable.inVariable) + inVariables[name] = variable.value; + } - auto error = this->m_patternLanguageRuntime->getError(); + auto result = this->m_evaluatorRuntime->executeString(ImHexApi::Provider::get(), code, envVars, inVariables); + + auto error = this->m_evaluatorRuntime->getError(); if (error.has_value()) { this->m_textEditor.SetErrorMarkers({ error.value() }); } - this->m_console = this->m_patternLanguageRuntime->getConsoleLog(); + this->m_console = this->m_evaluatorRuntime->getConsoleLog(); + + auto outVariables = this->m_evaluatorRuntime->getOutVariables(); + for (auto &[name, variable] : this->m_patternVariables) { + if (variable.outVariable && outVariables.contains(name)) + variable.value = outVariables.at(name); + } if (result.has_value()) { SharedData::patternData = std::move(result.value()); - //View::doLater([]{ - EventManager::post(); - //}); + EventManager::post(); } this->m_evaluatorRunning = false; diff --git a/plugins/libimhex/include/hex/pattern_language/ast_node.hpp b/plugins/libimhex/include/hex/pattern_language/ast_node.hpp index 1b3de4c84..03f71a2ac 100644 --- a/plugins/libimhex/include/hex/pattern_language/ast_node.hpp +++ b/plugins/libimhex/include/hex/pattern_language/ast_node.hpp @@ -714,8 +714,8 @@ namespace hex::pl { class ASTNodeVariableDecl : public ASTNode, public Attributable { public: - ASTNodeVariableDecl(std::string name, ASTNode *type, ASTNode *placementOffset = nullptr) - : ASTNode(), m_name(std::move(name)), m_type(type), m_placementOffset(placementOffset) { } + ASTNodeVariableDecl(std::string name, ASTNode *type, ASTNode *placementOffset = nullptr, bool inVariable = false, bool outVariable = false) + : ASTNode(), m_name(std::move(name)), m_type(type), m_placementOffset(placementOffset), m_inVariable(inVariable), m_outVariable(outVariable) { } ASTNodeVariableDecl(const ASTNodeVariableDecl &other) : ASTNode(other), Attributable(other) { this->m_name = other.m_name; @@ -740,6 +740,9 @@ namespace hex::pl { [[nodiscard]] constexpr ASTNode* getType() const { return this->m_type; } [[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; } + [[nodiscard]] constexpr bool isInVariable() const { return this->m_inVariable; } + [[nodiscard]] constexpr bool isOutVariable() const { return this->m_outVariable; } + [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { if (this->m_placementOffset != nullptr) { auto offset = dynamic_cast(this->m_placementOffset->evaluate(evaluator)); @@ -770,6 +773,8 @@ namespace hex::pl { std::string m_name; ASTNode *m_type; ASTNode *m_placementOffset; + + bool m_inVariable, m_outVariable; }; class ASTNodeArrayVariableDecl : public ASTNode, public Attributable { @@ -1459,7 +1464,8 @@ namespace hex::pl { std::visit(overloaded { [&](std::string &assignmentValue) { }, - [&](auto &&assignmentValue) { std::memcpy(&value, &assignmentValue, pattern->getSize()); } + [&](PatternData *assignmentValue) { }, + [&](auto &&assignmentValue) { value = assignmentValue; } }, literal); } else diff --git a/plugins/libimhex/include/hex/pattern_language/evaluator.hpp b/plugins/libimhex/include/hex/pattern_language/evaluator.hpp index 3039200bc..17a617a4f 100644 --- a/plugins/libimhex/include/hex/pattern_language/evaluator.hpp +++ b/plugins/libimhex/include/hex/pattern_language/evaluator.hpp @@ -64,6 +64,21 @@ namespace hex::pl { this->m_provider = provider; } + void setInVariables(const std::map &inVariables) { + this->m_inVariables = inVariables; + } + + [[nodiscard]] + std::map getOutVariables() const { + std::map result; + + for (const auto &[name, offset] : this->m_outVariables) { + result.insert({ name, this->getStack()[offset] }); + } + + return result; + } + [[nodiscard]] prv::Provider *getProvider() const { return this->m_provider; @@ -137,7 +152,12 @@ namespace hex::pl { return this->m_stack; } - void createVariable(const std::string &name, ASTNode *type, const std::optional &value = std::nullopt); + [[nodiscard]] + const std::vector& getStack() const { + return this->m_stack; + } + + void createVariable(const std::string &name, ASTNode *type, const std::optional &value = std::nullopt, bool outVariable = false); void setVariable(const std::string &name, const Token::Literal& value); void abort() { @@ -185,7 +205,10 @@ namespace hex::pl { std::map m_customFunctions; std::vector m_customFunctionDefinitions; std::vector m_stack; + std::map m_envVariables; + std::map m_inVariables; + std::map m_outVariables; friend class PatternCreationLimiter; }; diff --git a/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp b/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp index 1795fc3e7..8c1b29efc 100644 --- a/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp +++ b/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp @@ -30,15 +30,27 @@ namespace hex::pl { PatternLanguage(); ~PatternLanguage(); - std::optional> executeString(prv::Provider *provider, const std::string &string, const std::map &envVars = { }); - std::optional> executeFile(prv::Provider *provider, const std::string &path, const std::map &envVars = { }); + [[nodiscard]] + std::optional> parseString(const std::string &code); + [[nodiscard]] + std::optional> executeString(prv::Provider *provider, const std::string &string, const std::map &envVars = { }, const std::map &inVariables = { }); + [[nodiscard]] + std::optional> executeFile(prv::Provider *provider, const std::string &path, const std::map &envVars = { }, const std::map &inVariables = { }); + [[nodiscard]] + const std::vector& getCurrentAST() const; void abort(); + [[nodiscard]] const std::vector>& getConsoleLog(); + [[nodiscard]] const std::optional>& getError(); + [[nodiscard]] + std::map getOutVariables() const; + [[nodiscard]] u32 getCreatedPatternCount(); + [[nodiscard]] u32 getMaximumPatternCount(); private: diff --git a/plugins/libimhex/include/hex/pattern_language/token.hpp b/plugins/libimhex/include/hex/pattern_language/token.hpp index c74d54688..dcfde4125 100644 --- a/plugins/libimhex/include/hex/pattern_language/token.hpp +++ b/plugins/libimhex/include/hex/pattern_language/token.hpp @@ -40,7 +40,9 @@ namespace hex::pl { For, Function, Return, - Namespace + Namespace, + In, + Out }; enum class Operator { @@ -285,6 +287,8 @@ namespace hex::pl { #define KEYWORD_FUNCTION COMPONENT(Keyword, Function) #define KEYWORD_RETURN COMPONENT(Keyword, Return) #define KEYWORD_NAMESPACE COMPONENT(Keyword, Namespace) +#define KEYWORD_IN COMPONENT(Keyword, In) +#define KEYWORD_OUT COMPONENT(Keyword, Out) #define INTEGER hex::pl::Token::Type::Integer, hex::pl::Token::Literal(u128(0)) #define IDENTIFIER hex::pl::Token::Type::Identifier, "" diff --git a/plugins/libimhex/source/pattern_language/evaluator.cpp b/plugins/libimhex/source/pattern_language/evaluator.cpp index 716678f01..5f994931c 100644 --- a/plugins/libimhex/source/pattern_language/evaluator.cpp +++ b/plugins/libimhex/source/pattern_language/evaluator.cpp @@ -3,7 +3,7 @@ namespace hex::pl { - void Evaluator::createVariable(const std::string &name, ASTNode *type, const std::optional &value) { + void Evaluator::createVariable(const std::string &name, ASTNode *type, const std::optional &value, bool outVariable) { auto &variables = *this->getScope(0).scope; for (auto &variable : variables) { if (variable->getVariableName() == name) { @@ -44,6 +44,9 @@ namespace hex::pl { this->getStack().emplace_back(); variables.push_back(pattern); + + if (outVariable) + this->m_outVariables[name] = pattern->getOffset(); } void Evaluator::setVariable(const std::string &name, const Token::Literal& value) { @@ -115,6 +118,7 @@ namespace hex::pl { }, value); this->getStack()[pattern->getOffset()] = castedLiteral; + } std::optional> Evaluator::evaluate(const std::vector &ast) { @@ -153,7 +157,12 @@ namespace hex::pl { auto type = varDeclNode->getType()->evaluate(this); ON_SCOPE_EXIT { delete type; }; - this->createVariable(pattern->getVariableName(), type); + auto &name = pattern->getVariableName(); + this->createVariable(name, type, std::nullopt, varDeclNode->isOutVariable()); + + if (varDeclNode->isInVariable() && this->m_inVariables.contains(name)) + this->setVariable(name, this->m_inVariables[name]); + delete pattern; } else { patterns.push_back(pattern); diff --git a/plugins/libimhex/source/pattern_language/lexer.cpp b/plugins/libimhex/source/pattern_language/lexer.cpp index fabdaeccc..d153837b5 100644 --- a/plugins/libimhex/source/pattern_language/lexer.cpp +++ b/plugins/libimhex/source/pattern_language/lexer.cpp @@ -416,6 +416,10 @@ namespace hex::pl { tokens.emplace_back(TOKEN(Keyword, Return)); else if (identifier == "namespace") tokens.emplace_back(TOKEN(Keyword, Namespace)); + else if (identifier == "in") + tokens.emplace_back(TOKEN(Keyword, In)); + else if (identifier == "out") + tokens.emplace_back(TOKEN(Keyword, Out)); // Check for built-in types else if (identifier == "u8") diff --git a/plugins/libimhex/source/pattern_language/parser.cpp b/plugins/libimhex/source/pattern_language/parser.cpp index 5ca9df90d..495c049a3 100644 --- a/plugins/libimhex/source/pattern_language/parser.cpp +++ b/plugins/libimhex/source/pattern_language/parser.cpp @@ -1024,16 +1024,22 @@ namespace hex::pl { // (parseType) Identifier @ Integer ASTNode* Parser::parseVariablePlacement(ASTNodeTypeDecl *type) { + bool inVariable = false; + bool outVariable = false; + auto name = getValue(-1).get(); - ASTNode *placementOffset; + ASTNode *placementOffset = nullptr; if (MATCHES(sequence(OPERATOR_AT))) { placementOffset = parseMathematicalExpression(); - } else { - placementOffset = nullptr; + } else if (MATCHES(sequence(KEYWORD_IN))) { + inVariable = true; + } + else if (MATCHES(sequence(KEYWORD_OUT))) { + outVariable = true; } - return create(new ASTNodeVariableDecl(name, type, placementOffset)); + return create(new ASTNodeVariableDecl(name, type, placementOffset, inVariable, outVariable)); } // (parseType) Identifier[[(parseMathematicalExpression)]] @ Integer diff --git a/plugins/libimhex/source/pattern_language/pattern_language.cpp b/plugins/libimhex/source/pattern_language/pattern_language.cpp index 7b367d052..e5f11dadd 100644 --- a/plugins/libimhex/source/pattern_language/pattern_language.cpp +++ b/plugins/libimhex/source/pattern_language/pattern_language.cpp @@ -94,25 +94,8 @@ namespace hex::pl { delete this->m_validator; } - - std::optional> PatternLanguage::executeString(prv::Provider *provider, const std::string &string, const std::map &envVars) { - this->m_currError.reset(); - this->m_evaluator->getConsole().clear(); - this->m_evaluator->setProvider(provider); - this->m_evaluator->setDefaultEndian(std::endian::native); - this->m_evaluator->setEvaluationDepth(32); - this->m_evaluator->setArrayLimit(0x1000); - this->m_evaluator->setPatternLimit(0x2000); - this->m_evaluator->setLoopLimit(0x1000); - - for (const auto &[name, value] : envVars) - this->m_evaluator->setEnvVariable(name, value); - - for (auto &node : this->m_currAST) - delete node; - this->m_currAST.clear(); - - auto preprocessedCode = this->m_preprocessor->preprocess(string); + std::optional> PatternLanguage::parseString(const std::string &code) { + auto preprocessedCode = this->m_preprocessor->preprocess(code); if (!preprocessedCode.has_value()) { this->m_currError = this->m_preprocessor->getError(); return { }; @@ -130,6 +113,31 @@ namespace hex::pl { return { }; } + return ast; + } + + std::optional> PatternLanguage::executeString(prv::Provider *provider, const std::string &code, const std::map &envVars, const std::map &inVariables) { + this->m_currError.reset(); + this->m_evaluator->getConsole().clear(); + this->m_evaluator->setProvider(provider); + this->m_evaluator->setDefaultEndian(std::endian::native); + this->m_evaluator->setEvaluationDepth(32); + this->m_evaluator->setArrayLimit(0x1000); + this->m_evaluator->setPatternLimit(0x2000); + this->m_evaluator->setLoopLimit(0x1000); + this->m_evaluator->setInVariables(inVariables); + + for (const auto &[name, value] : envVars) + this->m_evaluator->setEnvVariable(name, value); + + for (auto &node : this->m_currAST) + delete node; + this->m_currAST.clear(); + + auto ast = this->parseString(code); + if (!ast) + return { }; + this->m_currAST = ast.value(); auto patterns = this->m_evaluator->evaluate(ast.value()); @@ -141,16 +149,25 @@ namespace hex::pl { return patterns; } - std::optional> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path, const std::map &envVars) { + std::optional> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path, const std::map &envVars, const std::map &inVariables) { File file(path, File::Mode::Read); - return this->executeString(provider, file.readString(), envVars); + return this->executeString(provider, file.readString(), envVars, inVariables); } void PatternLanguage::abort() { this->m_evaluator->abort(); } + const std::vector &PatternLanguage::getCurrentAST() const { + return this->m_currAST; + } + + [[nodiscard]] + std::map PatternLanguage::getOutVariables() const { + return this->m_evaluator->getOutVariables(); + } + const std::vector>& PatternLanguage::getConsoleLog() { return this->m_evaluator->getConsole().getLog();