patterns: Added std::env and ability to pass parameters to patterns from the UI
This commit is contained in:
parent
6a0ad22774
commit
8f2e382c8a
@ -37,6 +37,21 @@ namespace hex::plugin::builtin {
|
||||
TextEditor m_textEditor;
|
||||
std::vector<std::pair<pl::LogConsole::Level, std::string>> m_console;
|
||||
|
||||
enum class EnvVarType {
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
Bool
|
||||
};
|
||||
|
||||
struct EnvVar {
|
||||
std::string name;
|
||||
pl::Token::Literal value;
|
||||
EnvVarType type;
|
||||
};
|
||||
|
||||
std::vector<EnvVar> m_envVarEntries;
|
||||
|
||||
void loadPatternFile(const std::string &path);
|
||||
void clearPatternData();
|
||||
void parsePattern(char *buffer);
|
||||
|
@ -51,7 +51,7 @@ namespace hex::plugin::builtin {
|
||||
/* assert(condition, message) */
|
||||
ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert", 2, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto condition = Token::literalToBoolean(params[0]);
|
||||
auto message = std::get<std::string>(params[1]);
|
||||
auto message = Token::literalToString(params[1], false);
|
||||
|
||||
if (!condition)
|
||||
LogConsole::abortEvaluation(hex::format("assertion failed \"{0}\"", message));
|
||||
@ -62,7 +62,7 @@ namespace hex::plugin::builtin {
|
||||
/* assert_warn(condition, message) */
|
||||
ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert_warn", 2, [](auto *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto condition = Token::literalToBoolean(params[0]);
|
||||
auto message = std::get<std::string>(params[1]);
|
||||
auto message = Token::literalToString(params[1], false);
|
||||
|
||||
if (!condition)
|
||||
ctx->getConsole().log(LogConsole::Level::Warning, hex::format("assertion failed \"{0}\"", message));
|
||||
@ -82,6 +82,13 @@ namespace hex::plugin::builtin {
|
||||
return format(ctx, params);
|
||||
});
|
||||
|
||||
/* env(name) */
|
||||
ContentRegistry::PatternLanguageFunctions::add(nsStd, "env", 1, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto name = Token::literalToString(params[0], false);
|
||||
|
||||
return ctx->getEnvVariable(name);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ContentRegistry::PatternLanguageFunctions::Namespace nsStdMem = { "std", "mem" };
|
||||
|
@ -88,6 +88,8 @@ namespace hex::plugin::builtin {
|
||||
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
|
||||
this->m_textEditor.SetShowWhitespaces(false);
|
||||
|
||||
this->m_envVarEntries = { { "", s128(0), EnvVarType::Integer } };
|
||||
|
||||
EventManager::subscribe<EventProjectFileStore>(this, [this]() {
|
||||
ProjectFile::setPattern(this->m_textEditor.GetText());
|
||||
});
|
||||
@ -284,6 +286,71 @@ namespace hex::plugin::builtin {
|
||||
this->m_patternLanguageRuntime->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("Environment Variables");
|
||||
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<int>(type)])) {
|
||||
for (auto i = 0; i < IM_ARRAYSIZE(Types); i++) {
|
||||
if (ImGui::Selectable(Types[i]))
|
||||
type = static_cast<EnvVarType>(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<s128>(value, 0);
|
||||
ImGui::InputScalar("###value", ImGuiDataType_S64, &displayValue);
|
||||
value = s128(displayValue);
|
||||
break;
|
||||
}
|
||||
case EnvVarType::Float: {
|
||||
auto displayValue = hex::get_or<double>(value, 0.0);
|
||||
ImGui::InputDouble("###value", &displayValue);
|
||||
value = displayValue;
|
||||
break;
|
||||
}
|
||||
case EnvVarType::Bool: {
|
||||
auto displayValue = hex::get_or<bool>(value, false);
|
||||
ImGui::Checkbox("###value", &displayValue);
|
||||
value = displayValue;
|
||||
break;
|
||||
}
|
||||
case EnvVarType::String: {
|
||||
auto displayValue = hex::get_or<std::string>(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()) {
|
||||
@ -418,7 +485,11 @@ namespace hex::plugin::builtin {
|
||||
EventManager::post<EventPatternChanged>();
|
||||
|
||||
std::thread([this, buffer = std::string(buffer)] {
|
||||
auto result = this->m_patternLanguageRuntime->executeString(ImHexApi::Provider::get(), buffer);
|
||||
std::map<std::string, pl::Token::Literal> envVars;
|
||||
for (const auto &[name, value, type] : this->m_envVarEntries)
|
||||
envVars.insert({ name, value });
|
||||
|
||||
auto result = this->m_patternLanguageRuntime->executeString(ImHexApi::Provider::get(), buffer, envVars);
|
||||
|
||||
auto error = this->m_patternLanguageRuntime->getError();
|
||||
if (error.has_value()) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <nfd.hpp>
|
||||
@ -227,6 +228,15 @@ namespace hex {
|
||||
return iter != a.end();
|
||||
}
|
||||
|
||||
template<typename T, typename ... VariantTypes>
|
||||
T get_or(const std::variant<VariantTypes...> &variant, T alt) {
|
||||
const T *value = std::get_if<T>(&variant);
|
||||
if (value == nullptr)
|
||||
return alt;
|
||||
else
|
||||
return *value;
|
||||
}
|
||||
|
||||
namespace scope_guard {
|
||||
|
||||
#define SCOPE_GUARD ::hex::scope_guard::ScopeGuardOnExit() + [&]()
|
||||
|
@ -149,6 +149,18 @@ namespace hex::pl {
|
||||
LogConsole::abortEvaluation("evaluation aborted by user");
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::optional<Token::Literal> getEnvVariable(const std::string &name) const {
|
||||
if (this->m_envVariables.contains(name))
|
||||
return this->m_envVariables.at(name);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void setEnvVariable(const std::string &name, const Token::Literal &value) {
|
||||
this->m_envVariables[name] = value;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void patternCreated();
|
||||
@ -173,6 +185,7 @@ namespace hex::pl {
|
||||
std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> m_customFunctions;
|
||||
std::vector<ASTNode*> m_customFunctionDefinitions;
|
||||
std::vector<Token::Literal> m_stack;
|
||||
std::map<std::string, Token::Literal> m_envVariables;
|
||||
|
||||
friend class PatternCreationLimiter;
|
||||
};
|
||||
|
@ -3,12 +3,14 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <bit>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
|
||||
namespace hex::prv { class Provider; }
|
||||
|
||||
@ -28,8 +30,8 @@ namespace hex::pl {
|
||||
PatternLanguage();
|
||||
~PatternLanguage();
|
||||
|
||||
std::optional<std::vector<PatternData*>> executeString(prv::Provider *provider, const std::string &string);
|
||||
std::optional<std::vector<PatternData*>> executeFile(prv::Provider *provider, const std::string &path);
|
||||
std::optional<std::vector<PatternData*>> executeString(prv::Provider *provider, const std::string &string, const std::map<std::string, Token::Literal> &envVars = { });
|
||||
std::optional<std::vector<PatternData*>> executeFile(prv::Provider *provider, const std::string &path, const std::map<std::string, Token::Literal> &envVars = { });
|
||||
|
||||
void abort();
|
||||
|
||||
|
@ -123,6 +123,10 @@ namespace hex::pl {
|
||||
this->m_scopes.clear();
|
||||
this->m_aborted = false;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
this->m_envVariables.clear();
|
||||
};
|
||||
|
||||
this->dataOffset() = 0x00;
|
||||
this->m_currPatternCount = 0;
|
||||
|
||||
|
@ -95,7 +95,7 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
|
||||
std::optional<std::vector<PatternData*>> PatternLanguage::executeString(prv::Provider *provider, const std::string &string) {
|
||||
std::optional<std::vector<PatternData*>> PatternLanguage::executeString(prv::Provider *provider, const std::string &string, const std::map<std::string, Token::Literal> &envVars) {
|
||||
this->m_currError.reset();
|
||||
this->m_evaluator->getConsole().clear();
|
||||
this->m_evaluator->setProvider(provider);
|
||||
@ -105,6 +105,9 @@ namespace hex::pl {
|
||||
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();
|
||||
@ -138,10 +141,10 @@ namespace hex::pl {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
std::optional<std::vector<PatternData*>> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path) {
|
||||
std::optional<std::vector<PatternData*>> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path, const std::map<std::string, Token::Literal> &envVars) {
|
||||
File file(path, File::Mode::Read);
|
||||
|
||||
return this->executeString(provider, file.readString());
|
||||
return this->executeString(provider, file.readString(), envVars);
|
||||
}
|
||||
|
||||
void PatternLanguage::abort() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user